server.c | server.c | |||
---|---|---|---|---|
/* | /* | |||
This file is part of GNUnet. | This file is part of GNUnet. | |||
(C) 2009 Christian Grothoff (and other contributing authors) | (C) 2009, 2012 Christian Grothoff (and other contributing authors) | |||
GNUnet is free software; you can redistribute it and/or modify | GNUnet is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published | it under the terms of the GNU General Public License as published | |||
by the Free Software Foundation; either version 2, or (at your | by the Free Software Foundation; either version 2, or (at your | |||
option) any later version. | option) any later version. | |||
GNUnet is distributed in the hope that it will be useful, but | GNUnet is distributed in the hope that it will be useful, but | |||
WITHOUT ANY WARRANTY; without even the implied warranty of | WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
General Public License for more details. | General Public License for more details. | |||
skipping to change at line 29 | skipping to change at line 29 | |||
*/ | */ | |||
/** | /** | |||
* @file util/server.c | * @file util/server.c | |||
* @brief library for building GNUnet network servers | * @brief library for building GNUnet network servers | |||
* @author Christian Grothoff | * @author Christian Grothoff | |||
*/ | */ | |||
#include "platform.h" | #include "platform.h" | |||
#include "gnunet_common.h" | #include "gnunet_common.h" | |||
#include "gnunet_connection_lib.h" | #include "gnunet_util_lib.h" | |||
#include "gnunet_scheduler_lib.h" | ||||
#include "gnunet_server_lib.h" | ||||
#include "gnunet_time_lib.h" | ||||
#include "gnunet_disk_lib.h" | ||||
#include "gnunet_protocols.h" | #include "gnunet_protocols.h" | |||
#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) | #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) | |||
#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) | #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) | |||
#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_f ile (kind, "util", syscall, filename) | #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_f ile (kind, "util", syscall, filename) | |||
#define DEBUG_SERVER GNUNET_EXTRA_LOGGING | ||||
/** | /** | |||
* List of arrays of message handlers. | * List of arrays of message handlers. | |||
*/ | */ | |||
struct HandlerList | struct HandlerList | |||
{ | { | |||
/** | /** | |||
* This is a linked list. | * This is a linked list. | |||
*/ | */ | |||
struct HandlerList *next; | struct HandlerList *next; | |||
skipping to change at line 66 | skipping to change at line 60 | |||
*/ | */ | |||
const struct GNUNET_SERVER_MessageHandler *handlers; | const struct GNUNET_SERVER_MessageHandler *handlers; | |||
}; | }; | |||
/** | /** | |||
* List of arrays of message handlers. | * List of arrays of message handlers. | |||
*/ | */ | |||
struct NotifyList | struct NotifyList | |||
{ | { | |||
/** | /** | |||
* This is a linked list. | * This is a doubly linked list. | |||
*/ | */ | |||
struct NotifyList *next; | struct NotifyList *next; | |||
/** | /** | |||
* This is a doubly linked list. | ||||
*/ | ||||
struct NotifyList *prev; | ||||
/** | ||||
* Function to call. | * Function to call. | |||
*/ | */ | |||
GNUNET_SERVER_DisconnectCallback callback; | GNUNET_SERVER_DisconnectCallback callback; | |||
/** | /** | |||
* Closure for callback. | * Closure for callback. | |||
*/ | */ | |||
void *callback_cls; | void *callback_cls; | |||
}; | }; | |||
skipping to change at line 92 | skipping to change at line 91 | |||
* @brief handle for a server | * @brief handle for a server | |||
*/ | */ | |||
struct GNUNET_SERVER_Handle | struct GNUNET_SERVER_Handle | |||
{ | { | |||
/** | /** | |||
* List of handlers for incoming messages. | * List of handlers for incoming messages. | |||
*/ | */ | |||
struct HandlerList *handlers; | struct HandlerList *handlers; | |||
/** | /** | |||
* List of our current clients. | * 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; | struct GNUNET_SERVER_Client *clients_tail; | |||
/** | /** | |||
* Linked list of functions to call on disconnects by clients. | * Head of linked list of functions to call on disconnects by clients. | |||
*/ | */ | |||
struct NotifyList *disconnect_notify_list; | struct NotifyList *disconnect_notify_list_head; | |||
/** | ||||
* Tail of linked list of functions to call on disconnects by clients. | ||||
*/ | ||||
struct NotifyList *disconnect_notify_list_tail; | ||||
/** | /** | |||
* Function to call for access control. | * Function to call for access control. | |||
*/ | */ | |||
GNUNET_CONNECTION_AccessCheck access; | GNUNET_CONNECTION_AccessCheck access; | |||
/** | /** | |||
* Closure for access. | * Closure for access. | |||
*/ | */ | |||
void *access_cls; | void *access_cls; | |||
skipping to change at line 129 | skipping to change at line 138 | |||
* out (on write). | * out (on write). | |||
*/ | */ | |||
struct GNUNET_TIME_Relative idle_timeout; | struct GNUNET_TIME_Relative idle_timeout; | |||
/** | /** | |||
* Task scheduled to do the listening. | * Task scheduled to do the listening. | |||
*/ | */ | |||
GNUNET_SCHEDULER_TaskIdentifier listen_task; | GNUNET_SCHEDULER_TaskIdentifier 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 | * 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)? | * require that a handler is found (and if not kill the connection)? | |||
*/ | */ | |||
int require_found; | int require_found; | |||
/** | /** | |||
* Should all of the clients of this server continue to process | * Set to GNUNET_YES once we are in 'soft' shutdown where we wait for | |||
* connections as usual even if we get a shutdown request? (the | * all non-monitor clients to disconnect before we call | |||
* listen socket always ignores shutdown). | * 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 clients_ignore_shutdown; | 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 'callback' | ||||
*/ | ||||
void *callback_cls; | ||||
/** | ||||
* Active connection transmission handle. | ||||
*/ | ||||
struct GNUNET_CONNECTION_TransmitHandle *cth; | ||||
GNUNET_SERVER_MstCreateCallback mst_create; | ||||
GNUNET_SERVER_MstDestroyCallback mst_destroy; | ||||
GNUNET_SERVER_MstReceiveCallback mst_receive; | ||||
void *mst_cls; | ||||
}; | }; | |||
/** | /** | |||
* @brief handle for a client of the server | * @brief handle for a client of the server | |||
*/ | */ | |||
struct GNUNET_SERVER_Client | struct GNUNET_SERVER_Client | |||
{ | { | |||
/** | /** | |||
* This is a linked list. | * This is a doubly linked list. | |||
*/ | */ | |||
struct GNUNET_SERVER_Client *next; | struct GNUNET_SERVER_Client *next; | |||
/** | /** | |||
* This is a doubly linked list. | ||||
*/ | ||||
struct GNUNET_SERVER_Client *prev; | ||||
/** | ||||
* Processing of incoming data. | * Processing of incoming data. | |||
*/ | */ | |||
void *mst; | void *mst; | |||
/** | /** | |||
* Server that this client belongs to. | * Server that this client belongs to. | |||
*/ | */ | |||
struct GNUNET_SERVER_Handle *server; | struct GNUNET_SERVER_Handle *server; | |||
/** | /** | |||
skipping to change at line 195 | skipping to change at line 248 | |||
*/ | */ | |||
struct GNUNET_TIME_Absolute warn_start; | struct GNUNET_TIME_Absolute warn_start; | |||
/** | /** | |||
* Last activity on this socket (used to time it out | * Last activity on this socket (used to time it out | |||
* if reference_count == 0). | * if reference_count == 0). | |||
*/ | */ | |||
struct GNUNET_TIME_Absolute last_activity; | struct GNUNET_TIME_Absolute last_activity; | |||
/** | /** | |||
* | * Transmission handle we return for this client from | |||
*/ | * GNUNET_SERVER_notify_transmit_ready. | |||
GNUNET_CONNECTION_TransmitReadyNotify callback; | ||||
/** | ||||
* callback | ||||
*/ | */ | |||
void *callback_cls; | struct GNUNET_SERVER_TransmitHandle th; | |||
/** | /** | |||
* After how long should an idle connection time | * After how long should an idle connection time | |||
* out (on write). | * out (on write). | |||
*/ | */ | |||
struct GNUNET_TIME_Relative idle_timeout; | struct GNUNET_TIME_Relative idle_timeout; | |||
/** | /** | |||
* Number of external entities with a reference to | * Number of external entities with a reference to | |||
* this client object. | * this client object. | |||
skipping to change at line 235 | skipping to change at line 284 | |||
* Are we currently in the "process_client_buffer" function (and | * Are we currently in the "process_client_buffer" function (and | |||
* will hence restart the receive job on exit if suspended == 0 once | * will hence restart the receive job on exit if suspended == 0 once | |||
* we are done?). If this is set, then "receive_done" will | * we are done?). If this is set, then "receive_done" will | |||
* essentially only decrement suspended; if this is not set, then | * essentially only decrement suspended; if this is not set, then | |||
* "receive_done" may need to restart the receive process (either | * "receive_done" may need to restart the receive process (either | |||
* from the side-buffer or via select/recv). | * from the side-buffer or via select/recv). | |||
*/ | */ | |||
int in_process_client_buffer; | int in_process_client_buffer; | |||
/** | /** | |||
* We're about to close down this client due to some serious | * We're about to close down this client. | |||
* error. | ||||
*/ | */ | |||
int shutdown_now; | int shutdown_now; | |||
/** | /** | |||
* Are we currently trying to receive? (YES if we are, NO if we are not, | * Are we currently trying to receive? (YES if we are, NO if we are not, | |||
* SYSERR if data is already available in MST). | * SYSERR if data is already available in MST). | |||
*/ | */ | |||
int receive_pending; | int receive_pending; | |||
/** | /** | |||
skipping to change at line 259 | skipping to change at line 307 | |||
int finish_pending_write; | int finish_pending_write; | |||
/** | /** | |||
* Persist the file handle for this client no matter what happens, | * Persist the file handle for this client no matter what happens, | |||
* force the OS to close once the process actually dies. Should only | * force the OS to close once the process actually dies. Should only | |||
* be used in special cases! | * be used in special cases! | |||
*/ | */ | |||
int persist; | 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). | * Type of last message processed (for warn_no_receive_done). | |||
*/ | */ | |||
uint16_t warn_type; | uint16_t warn_type; | |||
}; | }; | |||
/** | /** | |||
* Scheduler says our listen socket is ready. Process it! | * Scheduler says our listen socket is ready. Process it! | |||
* | * | |||
* @param cls handle to our server for which we are processing the listen | * @param cls handle to our server for which we are processing the listen | |||
* socket | * socket | |||
* @param tc reason why we are running right now | * @param tc reason why we are running right now | |||
*/ | */ | |||
static void | static void | |||
process_listen_socket (void *cls, const struct GNUNET_SCHEDULER_TaskContext | process_listen_socket (void *cls, const struct GNUNET_SCHEDULER_TaskContext | |||
*tc) | *tc); | |||
/** | ||||
* Add a listen task with the scheduler for this server. | ||||
* | ||||
* @param server handle to our server for which we are adding the listen | ||||
* socket | ||||
*/ | ||||
static void | ||||
schedule_listen_task (struct GNUNET_SERVER_Handle *server) | ||||
{ | { | |||
struct GNUNET_SERVER_Handle *server = cls; | ||||
struct GNUNET_CONNECTION_Handle *sock; | ||||
struct GNUNET_SERVER_Client *client; | ||||
struct GNUNET_NETWORK_FDSet *r; | struct GNUNET_NETWORK_FDSet *r; | |||
unsigned int i; | unsigned int i; | |||
server->listen_task = GNUNET_SCHEDULER_NO_TASK; | 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, s | ||||
erver); | ||||
return; | ||||
} | ||||
r = GNUNET_NETWORK_fdset_create (); | r = GNUNET_NETWORK_fdset_create (); | |||
i = 0; | i = 0; | |||
while (NULL != server->listen_sockets[i]) | while (NULL != server->listen_sockets[i]) | |||
GNUNET_NETWORK_fdset_set (r, 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); | ||||
} | ||||
/** | ||||
* Scheduler says our listen socket is ready. Process it! | ||||
* | ||||
* @param cls handle to our server for which we are processing the listen | ||||
* socket | ||||
* @param tc reason why we are running right now | ||||
*/ | ||||
static void | ||||
process_listen_socket (void *cls, const struct GNUNET_SCHEDULER_TaskContext | ||||
*tc) | ||||
{ | ||||
struct GNUNET_SERVER_Handle *server = cls; | ||||
struct GNUNET_CONNECTION_Handle *sock; | ||||
struct GNUNET_SERVER_Client *client; | ||||
unsigned int i; | ||||
server->listen_task = GNUNET_SCHEDULER_NO_TASK; | ||||
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | |||
{ | { | |||
/* ignore shutdown, someone else will take care of it! */ | /* ignore shutdown, someone else will take care of it! */ | |||
server->listen_task = | schedule_listen_task (server); | |||
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, | ||||
GNUNET_SCHEDULER_NO_TASK, | ||||
GNUNET_TIME_UNIT_FOREVER_REL, r, NULL, | ||||
&process_listen_socket, server); | ||||
GNUNET_NETWORK_fdset_destroy (r); | ||||
return; | return; | |||
} | } | |||
i = 0; | i = 0; | |||
while (NULL != server->listen_sockets[i]) | while (NULL != server->listen_sockets[i]) | |||
{ | { | |||
if (GNUNET_NETWORK_fdset_isset (tc->read_ready, server->listen_sockets[ i])) | if (GNUNET_NETWORK_fdset_isset (tc->read_ready, server->listen_sockets[ i])) | |||
{ | { | |||
sock = | sock = | |||
GNUNET_CONNECTION_create_from_accept (server->access, | GNUNET_CONNECTION_create_from_accept (server->access, | |||
server->access_cls, | server->access_cls, | |||
server->listen_sockets[i]); | server->listen_sockets[i]); | |||
if (sock != NULL) | if (NULL != sock) | |||
{ | { | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, "Server accepted incoming connection. \n"); | LOG (GNUNET_ERROR_TYPE_DEBUG, "Server accepted incoming connection. \n"); | |||
#endif | ||||
client = GNUNET_SERVER_connect_socket (server, sock); | client = GNUNET_SERVER_connect_socket (server, sock); | |||
GNUNET_CONNECTION_ignore_shutdown (sock, | ||||
server->clients_ignore_shutdown) | ||||
; | ||||
/* decrement reference count, we don't keep "client" alive */ | /* decrement reference count, we don't keep "client" alive */ | |||
GNUNET_SERVER_client_drop (client); | GNUNET_SERVER_client_drop (client); | |||
} | } | |||
} | } | |||
i++; | i++; | |||
} | } | |||
/* listen for more! */ | /* listen for more! */ | |||
server->listen_task = | schedule_listen_task (server); | |||
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, | ||||
GNUNET_SCHEDULER_NO_TASK, | ||||
GNUNET_TIME_UNIT_FOREVER_REL, r, NULL, | ||||
&process_listen_socket, server); | ||||
GNUNET_NETWORK_fdset_destroy (r); | ||||
} | } | |||
/** | /** | |||
* Create and initialize a listen socket for the server. | * Create and initialize a listen socket for the server. | |||
* | * | |||
* @param serverAddr address to listen on | * @param serverAddr address to listen on | |||
* @param socklen length of address | * @param socklen length of address | |||
* @return NULL on error, otherwise the listen socket | * @return NULL on error, otherwise the listen socket | |||
*/ | */ | |||
static struct GNUNET_NETWORK_Handle * | static struct GNUNET_NETWORK_Handle * | |||
open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen) | open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen) | |||
{ | { | |||
const static int on = 1; | static int on = 1; | |||
struct GNUNET_NETWORK_Handle *sock; | struct GNUNET_NETWORK_Handle *sock; | |||
uint16_t port; | uint16_t port; | |||
int eno; | int eno; | |||
switch (serverAddr->sa_family) | switch (serverAddr->sa_family) | |||
{ | { | |||
case AF_INET: | case AF_INET: | |||
port = ntohs (((const struct sockaddr_in *) serverAddr)->sin_port); | port = ntohs (((const struct sockaddr_in *) serverAddr)->sin_port); | |||
break; | break; | |||
case AF_INET6: | case AF_INET6: | |||
skipping to change at line 366 | skipping to change at line 448 | |||
port = 0; | port = 0; | |||
break; | break; | |||
} | } | |||
sock = GNUNET_NETWORK_socket_create (serverAddr->sa_family, SOCK_STREAM, 0); | sock = GNUNET_NETWORK_socket_create (serverAddr->sa_family, SOCK_STREAM, 0); | |||
if (NULL == sock) | if (NULL == sock) | |||
{ | { | |||
LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); | |||
errno = 0; | errno = 0; | |||
return NULL; | return NULL; | |||
} | } | |||
if (port != 0) | if (0 != port) | |||
{ | { | |||
if (GNUNET_NETWORK_socket_setsockopt | if (GNUNET_NETWORK_socket_setsockopt | |||
(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) | (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) | |||
LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | |||
"setsockopt"); | "setsockopt"); | |||
#ifdef IPV6_V6ONLY | #ifdef IPV6_V6ONLY | |||
if ((serverAddr->sa_family == AF_INET6) && | if ((AF_INET6 == serverAddr->sa_family) && | |||
(GNUNET_NETWORK_socket_setsockopt | (GNUNET_NETWORK_socket_setsockopt | |||
(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) | (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) | |||
LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | |||
"setsockopt"); | "setsockopt"); | |||
#endif | #endif | |||
} | } | |||
/* bind the socket */ | /* bind the socket */ | |||
if (GNUNET_NETWORK_socket_bind (sock, serverAddr, socklen) != GNUNET_OK) | if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, serverAddr, socklen)) | |||
{ | { | |||
eno = errno; | eno = errno; | |||
if (errno != EADDRINUSE) | if (EADDRINUSE != errno) | |||
{ | { | |||
/* we don't log 'EADDRINUSE' here since an IPv4 bind may | /* we don't log 'EADDRINUSE' here since an IPv4 bind may | |||
* fail if we already took the port on IPv6; if both IPv4 and | * fail if we already took the port on IPv6; if both IPv4 and | |||
* IPv6 binds fail, then our caller will log using the | * IPv6 binds fail, then our caller will log using the | |||
* errno preserved in 'eno' */ | * errno preserved in 'eno' */ | |||
LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind"); | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind"); | |||
if (port != 0) | if (0 != port) | |||
LOG (GNUNET_ERROR_TYPE_ERROR, _("`%s' failed for port %d (%s).\n"), | LOG (GNUNET_ERROR_TYPE_ERROR, _("`%s' failed for port %d (%s).\n"), | |||
"bind", port, | "bind", port, | |||
(serverAddr->sa_family == AF_INET) ? "IPv4" : "IPv6"); | (AF_INET == serverAddr->sa_family) ? "IPv4" : "IPv6"); | |||
eno = 0; | eno = 0; | |||
} | } | |||
else | else | |||
{ | { | |||
if (port != 0) | if (0 != port) | |||
LOG (GNUNET_ERROR_TYPE_WARNING, | LOG (GNUNET_ERROR_TYPE_WARNING, | |||
_("`%s' failed for port %d (%s): address already in use\n"), | _("`%s' failed for port %d (%s): address already in use\n"), | |||
"bind", port, | "bind", port, | |||
(serverAddr->sa_family == AF_INET) ? "IPv4" : "IPv6"); | (AF_INET == serverAddr->sa_family) ? "IPv4" : "IPv6"); | |||
else if (serverAddr->sa_family == AF_UNIX) | else if (AF_UNIX == serverAddr->sa_family) | |||
LOG (GNUNET_ERROR_TYPE_WARNING, | LOG (GNUNET_ERROR_TYPE_WARNING, | |||
_("`%s' failed for `%s': address already in use\n"), "bind", | _("`%s' failed for `%s': address already in use\n"), "bind", | |||
((const struct sockaddr_un *) serverAddr)->sun_path); | ((const struct sockaddr_un *) serverAddr)->sun_path); | |||
} | } | |||
GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); | |||
errno = eno; | errno = eno; | |||
return NULL; | return NULL; | |||
} | } | |||
if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5)) | if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5)) | |||
{ | { | |||
LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "listen"); | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "listen"); | |||
GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); | |||
errno = 0; | errno = 0; | |||
return NULL; | return NULL; | |||
} | } | |||
#if DEBUG_SERVER | if (0 != port) | |||
if (port != 0) | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, "Server starts to listen on port %u.\n", | LOG (GNUNET_ERROR_TYPE_DEBUG, "Server starts to listen on port %u.\n", | |||
port); | port); | |||
#endif | ||||
return sock; | return sock; | |||
} | } | |||
/** | /** | |||
* Create a new server. | * Create a new server. | |||
* | * | |||
* @param access function for access control | * @param access function for access control | |||
* @param access_cls closure for access | * @param access_cls closure for access | |||
* @param lsocks NULL-terminated array of listen sockets | * @param lsocks NULL-terminated array of listen sockets | |||
* @param idle_timeout after how long should we timeout idle connections? | * @param idle_timeout after how long should we timeout idle connections? | |||
skipping to change at line 448 | skipping to change at line 528 | |||
* @return handle for the new server, NULL on error | * @return handle for the new server, NULL on error | |||
* (typically, "port" already in use) | * (typically, "port" already in use) | |||
*/ | */ | |||
struct GNUNET_SERVER_Handle * | struct GNUNET_SERVER_Handle * | |||
GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access, | GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access, | |||
void *access_cls, | void *access_cls, | |||
struct GNUNET_NETWORK_Handle **lsocks, | struct GNUNET_NETWORK_Handle **lsocks, | |||
struct GNUNET_TIME_Relative idle_timeout , | struct GNUNET_TIME_Relative idle_timeout , | |||
int require_found) | int require_found) | |||
{ | { | |||
struct GNUNET_SERVER_Handle *ret; | struct GNUNET_SERVER_Handle *server; | |||
struct GNUNET_NETWORK_FDSet *r; | ||||
int i; | ||||
ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle)); | server = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle)); | |||
ret->idle_timeout = idle_timeout; | server->idle_timeout = idle_timeout; | |||
ret->listen_sockets = lsocks; | server->listen_sockets = lsocks; | |||
ret->access = access; | server->access = access; | |||
ret->access_cls = access_cls; | server->access_cls = access_cls; | |||
ret->require_found = require_found; | server->require_found = require_found; | |||
if (lsocks != NULL) | if (NULL != lsocks) | |||
{ | schedule_listen_task (server); | |||
r = GNUNET_NETWORK_fdset_create (); | return server; | |||
i = 0; | ||||
while (NULL != ret->listen_sockets[i]) | ||||
GNUNET_NETWORK_fdset_set (r, ret->listen_sockets[i++]); | ||||
ret->listen_task = | ||||
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, | ||||
GNUNET_SCHEDULER_NO_TASK, | ||||
GNUNET_TIME_UNIT_FOREVER_REL, r, NULL, | ||||
&process_listen_socket, ret); | ||||
GNUNET_NETWORK_fdset_destroy (r); | ||||
} | ||||
return ret; | ||||
} | } | |||
/** | /** | |||
* Create a new server. | * Create a new server. | |||
* | * | |||
* @param access function for access control | * @param access function for access control | |||
* @param access_cls closure for access | * @param access_cls closure for access | |||
* @param serverAddr address to listen on (including port), NULL terminated array | * @param serverAddr address to listen on (including port), NULL terminated array | |||
* @param socklen length of serverAddr | * @param socklen length of serverAddr | |||
* @param idle_timeout after how long should we timeout idle connections? | * @param idle_timeout after how long should we timeout idle connections? | |||
skipping to change at line 497 | skipping to change at line 564 | |||
struct GNUNET_SERVER_Handle * | struct GNUNET_SERVER_Handle * | |||
GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access, void *access_cl s, | GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access, void *access_cl s, | |||
struct sockaddr *const *serverAddr, | struct sockaddr *const *serverAddr, | |||
const socklen_t * socklen, | const socklen_t * socklen, | |||
struct GNUNET_TIME_Relative idle_timeout, | struct GNUNET_TIME_Relative idle_timeout, | |||
int require_found) | int require_found) | |||
{ | { | |||
struct GNUNET_NETWORK_Handle **lsocks; | struct GNUNET_NETWORK_Handle **lsocks; | |||
unsigned int i; | unsigned int i; | |||
unsigned int j; | unsigned int j; | |||
unsigned int k; | ||||
int seen; | ||||
i = 0; | i = 0; | |||
while (serverAddr[i] != NULL) | while (NULL != serverAddr[i]) | |||
i++; | i++; | |||
if (i > 0) | if (i > 0) | |||
{ | { | |||
lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1)); | lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1)); | |||
i = 0; | i = 0; | |||
j = 0; | j = 0; | |||
while (serverAddr[i] != NULL) | while (NULL != serverAddr[i]) | |||
{ | { | |||
seen = 0; | ||||
for (k=0;k<i;k++) | ||||
if ( (socklen[k] == socklen[i]) && | ||||
(0 == memcmp (serverAddr[k], serverAddr[i], socklen[i])) ) | ||||
{ | ||||
seen = 1; | ||||
break; | ||||
} | ||||
if (0 != seen) | ||||
{ | ||||
/* duplicate address, skip */ | ||||
i++; | ||||
continue; | ||||
} | ||||
lsocks[j] = open_listen_socket (serverAddr[i], socklen[i]); | lsocks[j] = open_listen_socket (serverAddr[i], socklen[i]); | |||
if (lsocks[j] != NULL) | if (NULL != lsocks[j]) | |||
j++; | j++; | |||
i++; | i++; | |||
} | } | |||
if (j == 0) | if (0 == j) | |||
{ | { | |||
if (errno != 0) | if (0 != errno) | |||
LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind"); | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind"); | |||
GNUNET_free (lsocks); | GNUNET_free (lsocks); | |||
lsocks = NULL; | lsocks = NULL; | |||
} | } | |||
} | } | |||
else | else | |||
{ | { | |||
lsocks = NULL; | lsocks = NULL; | |||
} | } | |||
return GNUNET_SERVER_create_with_sockets (access, access_cls, lsocks, | return GNUNET_SERVER_create_with_sockets (access, access_cls, lsocks, | |||
idle_timeout, require_found); | 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) | ||||
{ | ||||
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 | ||||
* @param tc unused | ||||
*/ | ||||
static void | ||||
do_destroy (void *cls, | ||||
const struct GNUNET_SCHEDULER_TaskContext *tc) | ||||
{ | ||||
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; | ||||
GNUNET_SCHEDULER_add_continuation (&do_destroy, server, | ||||
GNUNET_SCHEDULER_REASON_PREREQ_DONE); | ||||
} | ||||
/** | ||||
* 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 (GNUNET_SCHEDULER_NO_TASK != server->listen_task) | ||||
{ | ||||
GNUNET_SCHEDULER_cancel (server->listen_task); | ||||
server->listen_task = GNUNET_SCHEDULER_NO_TASK; | ||||
} | ||||
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. | * Free resources held by this server. | |||
* | * | |||
* @param s server to destroy | * @param server server to destroy | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s) | GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server) | |||
{ | { | |||
struct HandlerList *hpos; | struct HandlerList *hpos; | |||
struct NotifyList *npos; | struct NotifyList *npos; | |||
unsigned int i; | unsigned int i; | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, "Server shutting down.\n"); | LOG (GNUNET_ERROR_TYPE_DEBUG, "Server shutting down.\n"); | |||
#endif | if (GNUNET_SCHEDULER_NO_TASK != server->listen_task) | |||
if (GNUNET_SCHEDULER_NO_TASK != s->listen_task) | ||||
{ | { | |||
GNUNET_SCHEDULER_cancel (s->listen_task); | GNUNET_SCHEDULER_cancel (server->listen_task); | |||
s->listen_task = GNUNET_SCHEDULER_NO_TASK; | server->listen_task = GNUNET_SCHEDULER_NO_TASK; | |||
} | } | |||
if (s->listen_sockets != NULL) | if (NULL != server->listen_sockets) | |||
{ | { | |||
i = 0; | i = 0; | |||
while (s->listen_sockets[i] != NULL) | while (NULL != server->listen_sockets[i]) | |||
GNUNET_break (GNUNET_OK == | GNUNET_break (GNUNET_OK == | |||
GNUNET_NETWORK_socket_close (s->listen_sockets[i++])); | GNUNET_NETWORK_socket_close (server->listen_sockets[i++ | |||
GNUNET_free (s->listen_sockets); | ])); | |||
s->listen_sockets = NULL; | GNUNET_free (server->listen_sockets); | |||
} | server->listen_sockets = NULL; | |||
while (s->clients != NULL) | } | |||
GNUNET_SERVER_client_disconnect (s->clients); | while (NULL != server->clients_head) | |||
while (NULL != (hpos = s->handlers)) | GNUNET_SERVER_client_disconnect (server->clients_head); | |||
while (NULL != (hpos = server->handlers)) | ||||
{ | { | |||
s->handlers = hpos->next; | server->handlers = hpos->next; | |||
GNUNET_free (hpos); | GNUNET_free (hpos); | |||
} | } | |||
while (NULL != (npos = s->disconnect_notify_list)) | while (NULL != (npos = server->disconnect_notify_list_head)) | |||
{ | { | |||
npos->callback (npos->callback_cls, NULL); | npos->callback (npos->callback_cls, NULL); | |||
s->disconnect_notify_list = npos->next; | GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head, | |||
server->disconnect_notify_list_tail, | ||||
npos); | ||||
GNUNET_free (npos); | GNUNET_free (npos); | |||
} | } | |||
GNUNET_free (s); | GNUNET_free (server); | |||
} | } | |||
/** | /** | |||
* Add additional handlers to an existing server. | * Add additional handlers to an existing server. | |||
* | * | |||
* @param server the server to add handlers to | * @param server the server to add handlers to | |||
* @param handlers array of message handlers for | * @param handlers array of message handlers for | |||
* incoming messages; the last entry must | * incoming messages; the last entry must | |||
* have "NULL" for the "callback"; multiple | * have "NULL" for the "callback"; multiple | |||
* entries for the same type are allowed, | * entries for the same type are allowed, | |||
skipping to change at line 599 | skipping to change at line 767 | |||
const struct GNUNET_SERVER_MessageHandler *hand lers) | const struct GNUNET_SERVER_MessageHandler *hand lers) | |||
{ | { | |||
struct HandlerList *p; | struct HandlerList *p; | |||
p = GNUNET_malloc (sizeof (struct HandlerList)); | p = GNUNET_malloc (sizeof (struct HandlerList)); | |||
p->handlers = handlers; | p->handlers = handlers; | |||
p->next = server->handlers; | p->next = server->handlers; | |||
server->handlers = p; | 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 'create', 'receive', 'destroy' | ||||
*/ | ||||
void | void | |||
GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server, | GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server, | |||
GNUNET_SERVER_MstCreateCallback create, | GNUNET_SERVER_MstCreateCallback create, | |||
GNUNET_SERVER_MstDestroyCallback destroy, | GNUNET_SERVER_MstDestroyCallback destroy, | |||
GNUNET_SERVER_MstReceiveCallback receive, | GNUNET_SERVER_MstReceiveCallback receive, | |||
void *cls) | void *cls) | |||
{ | { | |||
server->mst_create = create; | server->mst_create = create; | |||
server->mst_destroy = destroy; | server->mst_destroy = destroy; | |||
server->mst_receive = receive; | server->mst_receive = receive; | |||
skipping to change at line 623 | skipping to change at line 801 | |||
* Task run to warn about missing calls to 'GNUNET_SERVER_receive_done'. | * Task run to warn about missing calls to 'GNUNET_SERVER_receive_done'. | |||
* | * | |||
* @param cls our 'struct GNUNET_SERVER_Client*' to process more requests f rom | * @param cls our 'struct GNUNET_SERVER_Client*' to process more requests f rom | |||
* @param tc scheduler context (unused) | * @param tc scheduler context (unused) | |||
*/ | */ | |||
static void | static void | |||
warn_no_receive_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | warn_no_receive_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
{ | { | |||
struct GNUNET_SERVER_Client *client = 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 = | client->warn_task = | |||
GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | |||
&warn_no_receive_done, client); | &warn_no_receive_done, client); | |||
if (0 == (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | if (0 == (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | |||
LOG (GNUNET_ERROR_TYPE_WARNING, | LOG (GNUNET_ERROR_TYPE_WARNING, | |||
_ | _ | |||
("Processing code for message of type %u did not call GNUNET_SERVE R_receive_done after %llums\n"), | ("Processing code for message of type %u did not call GNUNET_SERVE R_receive_done after %llums\n"), | |||
(unsigned int) client->warn_type, | (unsigned int) client->warn_type, | |||
(unsigned long long) | (unsigned long long) | |||
GNUNET_TIME_absolute_get_duration (client->warn_start).rel_value); | GNUNET_TIME_absolute_get_duration (client->warn_start).rel_value); | |||
skipping to change at line 681 | skipping to change at line 860 | |||
{ | { | |||
struct HandlerList *pos; | struct HandlerList *pos; | |||
const struct GNUNET_SERVER_MessageHandler *mh; | const struct GNUNET_SERVER_MessageHandler *mh; | |||
unsigned int i; | unsigned int i; | |||
uint16_t type; | uint16_t type; | |||
uint16_t size; | uint16_t size; | |||
int found; | int found; | |||
type = ntohs (message->type); | type = ntohs (message->type); | |||
size = ntohs (message->size); | size = ntohs (message->size); | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Server schedules transmission of %u-byte message of type %u to clie nt.\n", | "Server schedules transmission of %u-byte message of type %u to clie nt.\n", | |||
size, type); | size, type); | |||
#endif | ||||
pos = server->handlers; | ||||
found = GNUNET_NO; | found = GNUNET_NO; | |||
while (pos != NULL) | for (pos = server->handlers; NULL != pos; pos = pos->next) | |||
{ | { | |||
i = 0; | i = 0; | |||
while (pos->handlers[i].callback != NULL) | while (pos->handlers[i].callback != NULL) | |||
{ | { | |||
mh = &pos->handlers[i]; | mh = &pos->handlers[i]; | |||
if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL)) | if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL)) | |||
{ | { | |||
if ((mh->expected_size != 0) && (mh->expected_size != size)) | if ((0 != mh->expected_size) && (mh->expected_size != size)) | |||
{ | { | |||
#if GNUNET8_NETWORK_IS_DEAD | #if GNUNET8_NETWORK_IS_DEAD | |||
LOG (GNUNET_ERROR_TYPE_WARNING, | LOG (GNUNET_ERROR_TYPE_WARNING, | |||
"Expected %u bytes for message of type %u, got %u\n", | "Expected %u bytes for message of type %u, got %u\n", | |||
mh->expected_size, mh->type, size); | mh->expected_size, mh->type, size); | |||
GNUNET_break_op (0); | GNUNET_break_op (0); | |||
#endif | #endif | |||
return GNUNET_SYSERR; | return GNUNET_SYSERR; | |||
} | } | |||
if (sender != NULL) | if (NULL != sender) | |||
{ | { | |||
if (0 == sender->suspended) | if ( (0 == sender->suspended) && | |||
(GNUNET_SCHEDULER_NO_TASK == 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_start = GNUNET_TIME_absolute_get (); | |||
sender->warn_task = | sender->warn_task = | |||
GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | |||
&warn_no_receive_done, sender ); | &warn_no_receive_done, sender ); | |||
sender->warn_type = type; | sender->warn_type = type; | |||
} | } | |||
sender->suspended++; | sender->suspended++; | |||
} | } | |||
mh->callback (mh->callback_cls, sender, message); | mh->callback (mh->callback_cls, sender, message); | |||
found = GNUNET_YES; | found = GNUNET_YES; | |||
} | } | |||
i++; | i++; | |||
} | } | |||
pos = pos->next; | ||||
} | } | |||
if (found == GNUNET_NO) | if (GNUNET_NO == found) | |||
{ | { | |||
LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, | LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, | |||
"Received message of unknown type %d\n", type); | "Received message of unknown type %d\n", type); | |||
if (server->require_found == GNUNET_YES) | if (GNUNET_YES == server->require_found) | |||
return GNUNET_SYSERR; | return GNUNET_SYSERR; | |||
} | } | |||
return GNUNET_OK; | return GNUNET_OK; | |||
} | } | |||
/** | /** | |||
* We are receiving an incoming message. Process it. | * We are receiving an incoming message. Process it. | |||
* | * | |||
* @param cls our closure (handle for the client) | * @param cls our closure (handle for the client) | |||
* @param buf buffer with data received from network | * @param buf buffer with data received from network | |||
skipping to change at line 766 | skipping to change at line 942 | |||
* @param client the client to process, RC must have already been increased | * @param client the client to process, RC must have already been increased | |||
* using GNUNET_SERVER_client_keep and will be decreased by one in t his | * using GNUNET_SERVER_client_keep and will be decreased by one in t his | |||
* function | * function | |||
* @param ret GNUNET_NO to start processing from the buffer, | * @param ret GNUNET_NO to start processing from the buffer, | |||
* GNUNET_OK if the mst buffer is drained and we should instantl y go back to receiving | * GNUNET_OK if the mst buffer is drained and we should instantl y go back to receiving | |||
* GNUNET_SYSERR if we should instantly abort due to error in a previous step | * GNUNET_SYSERR if we should instantly abort due to error in a previous step | |||
*/ | */ | |||
static void | static void | |||
process_mst (struct GNUNET_SERVER_Client *client, int ret) | process_mst (struct GNUNET_SERVER_Client *client, int ret) | |||
{ | { | |||
while ((ret != GNUNET_SYSERR) && (client->server != NULL) && | while ((GNUNET_SYSERR != ret) && (NULL != client->server) && | |||
(GNUNET_YES != client->shutdown_now) && (0 == client->suspended)) | (GNUNET_YES != client->shutdown_now) && (0 == client->suspended)) | |||
{ | { | |||
if (ret == GNUNET_OK) | if (GNUNET_OK == ret) | |||
{ | { | |||
client->receive_pending = GNUNET_YES; | ||||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Server re-enters receive loop, timeout: %llu.\n", | "Server re-enters receive loop, timeout: %llu.\n", | |||
client->idle_timeout.rel_value); | client->idle_timeout.rel_value); | |||
#endif | client->receive_pending = GNUNET_YES; | |||
GNUNET_CONNECTION_receive (client->connection, | GNUNET_CONNECTION_receive (client->connection, | |||
GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | |||
client->idle_timeout, &process_incoming, | client->idle_timeout, &process_incoming, | |||
client); | client); | |||
break; | break; | |||
} | } | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Server processes additional messages instantly.\n"); | "Server processes additional messages instantly.\n"); | |||
#endif | if (NULL != client->server->mst_receive) | |||
if (client->server->mst_receive != NULL) | ||||
ret = | ret = | |||
client->server->mst_receive (client->server->mst_cls, client->mst , | client->server->mst_receive (client->server->mst_cls, client->mst , | |||
client, NULL, 0, GNUNET_NO, GNUNET_Y ES); | client, NULL, 0, GNUNET_NO, GNUNET_Y ES); | |||
else | else | |||
ret = | ret = | |||
GNUNET_SERVER_mst_receive (client->mst, client, NULL, 0, GNUNET_N O, | GNUNET_SERVER_mst_receive (client->mst, client, NULL, 0, GNUNET_N O, | |||
GNUNET_YES); | GNUNET_YES); | |||
} | } | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Server leaves instant processing loop: ret = %d, server = %p, shutd own = %d, suspended = %u\n", | "Server leaves instant processing loop: ret = %d, server = %p, shutd own = %d, suspended = %u\n", | |||
ret, client->server, client->shutdown_now, client->suspended); | ret, client->server, client->shutdown_now, client->suspended); | |||
#endif | if (GNUNET_NO == ret) | |||
if (ret == GNUNET_NO) | ||||
{ | { | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Server has more data pending but is suspended.\n"); | "Server has more data pending but is suspended.\n"); | |||
#endif | ||||
client->receive_pending = GNUNET_SYSERR; /* data pending */ | client->receive_pending = GNUNET_SYSERR; /* data pending */ | |||
} | } | |||
if ((ret == GNUNET_SYSERR) || (GNUNET_YES == client->shutdown_now)) | if ((GNUNET_SYSERR == ret) || (GNUNET_YES == client->shutdown_now)) | |||
GNUNET_SERVER_client_disconnect (client); | GNUNET_SERVER_client_disconnect (client); | |||
GNUNET_SERVER_client_drop (client); | ||||
} | } | |||
/** | /** | |||
* We are receiving an incoming message. Process it. | * We are receiving an incoming message. Process it. | |||
* | * | |||
* @param cls our closure (handle for the client) | * @param cls our closure (handle for the client) | |||
* @param buf buffer with data received from network | * @param buf buffer with data received from network | |||
* @param available number of bytes available in buf | * @param available number of bytes available in buf | |||
* @param addr address of the sender | * @param addr address of the sender | |||
* @param addrlen length of addr | * @param addrlen length of addr | |||
skipping to change at line 835 | skipping to change at line 1001 | |||
static void | static void | |||
process_incoming (void *cls, const void *buf, size_t available, | process_incoming (void *cls, const void *buf, size_t available, | |||
const struct sockaddr *addr, socklen_t addrlen, int errCo de) | const struct sockaddr *addr, socklen_t addrlen, int errCo de) | |||
{ | { | |||
struct GNUNET_SERVER_Client *client = cls; | struct GNUNET_SERVER_Client *client = cls; | |||
struct GNUNET_SERVER_Handle *server = client->server; | struct GNUNET_SERVER_Handle *server = client->server; | |||
struct GNUNET_TIME_Absolute end; | struct GNUNET_TIME_Absolute end; | |||
struct GNUNET_TIME_Absolute now; | struct GNUNET_TIME_Absolute now; | |||
int ret; | int ret; | |||
GNUNET_assert (client->receive_pending == GNUNET_YES); | GNUNET_assert (GNUNET_YES == client->receive_pending); | |||
client->receive_pending = GNUNET_NO; | client->receive_pending = GNUNET_NO; | |||
now = GNUNET_TIME_absolute_get (); | now = GNUNET_TIME_absolute_get (); | |||
end = GNUNET_TIME_absolute_add (client->last_activity, client->idle_timeo ut); | end = GNUNET_TIME_absolute_add (client->last_activity, client->idle_timeo ut); | |||
if ((buf == NULL) && (available == 0) && (addr == NULL) && (errCode == 0) | if ((NULL == buf) && (0 == available) && (NULL == addr) && (0 == errCode) | |||
&& | && | |||
(client->shutdown_now != GNUNET_YES) && (server != NULL) && | (GNUNET_YES != client->shutdown_now) && (NULL != server) && | |||
(GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) && | (GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) && | |||
(end.abs_value > now.abs_value)) | (end.abs_value > now.abs_value)) | |||
{ | { | |||
/* wait longer, timeout changed (i.e. due to us sending) */ | /* wait longer, timeout changed (i.e. due to us sending) */ | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Receive time out, but no disconnect due to sending (%p)\n", | "Receive time out, but no disconnect due to sending (%p)\n", | |||
GNUNET_a2s (addr, addrlen)); | GNUNET_a2s (addr, addrlen)); | |||
#endif | ||||
client->receive_pending = GNUNET_YES; | client->receive_pending = GNUNET_YES; | |||
GNUNET_CONNECTION_receive (client->connection, | GNUNET_CONNECTION_receive (client->connection, | |||
GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | |||
GNUNET_TIME_absolute_get_remaining (end), | GNUNET_TIME_absolute_get_remaining (end), | |||
&process_incoming, client); | &process_incoming, client); | |||
return; | return; | |||
} | } | |||
if ((buf == NULL) || (available == 0) || (errCode != 0) || (server == NUL | if ((NULL == buf) || (0 == available) || (0 != errCode) || (NULL == serve | |||
L) || | r) || | |||
(client->shutdown_now == GNUNET_YES) || | (GNUNET_YES == client->shutdown_now) || | |||
(GNUNET_YES != GNUNET_CONNECTION_check (client->connection))) | (GNUNET_YES != GNUNET_CONNECTION_check (client->connection))) | |||
{ | { | |||
/* other side closed connection, error connecting, etc. */ | /* other side closed connection, error connecting, etc. */ | |||
GNUNET_SERVER_client_disconnect (client); | GNUNET_SERVER_client_disconnect (client); | |||
return; | return; | |||
} | } | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, "Server receives %u bytes from `%s'.\n", | LOG (GNUNET_ERROR_TYPE_DEBUG, "Server receives %u bytes from `%s'.\n", | |||
(unsigned int) available, GNUNET_a2s (addr, addrlen)); | (unsigned int) available, GNUNET_a2s (addr, addrlen)); | |||
#endif | ||||
GNUNET_SERVER_client_keep (client); | GNUNET_SERVER_client_keep (client); | |||
client->last_activity = now; | client->last_activity = now; | |||
if (server->mst_receive != NULL) | if (NULL != server->mst_receive) | |||
ret = | ret = | |||
client->server->mst_receive (client->server->mst_cls, client->mst, | client->server->mst_receive (client->server->mst_cls, client->mst, | |||
client, buf, available, GNUNET_NO, GNU NET_YES); | client, buf, available, GNUNET_NO, GNU NET_YES); | |||
else | else if (NULL != client->mst) | |||
{ | ||||
ret = | ret = | |||
GNUNET_SERVER_mst_receive (client->mst, client, buf, available, GNU NET_NO, | GNUNET_SERVER_mst_receive (client->mst, client, buf, available, GNU NET_NO, | |||
GNUNET_YES); | GNUNET_YES); | |||
} | ||||
else | ||||
{ | ||||
GNUNET_break (0); | ||||
return; | ||||
} | ||||
process_mst (client, ret); | process_mst (client, ret); | |||
GNUNET_SERVER_client_drop (client); | ||||
} | } | |||
/** | /** | |||
* Task run to start again receiving from the network | * Task run to start again receiving from the network | |||
* and process requests. | * and process requests. | |||
* | * | |||
* @param cls our 'struct GNUNET_SERVER_Client*' to process more requests f rom | * @param cls our 'struct GNUNET_SERVER_Client*' to process more requests f rom | |||
* @param tc scheduler context (unused) | * @param tc scheduler context (unused) | |||
*/ | */ | |||
static void | static void | |||
restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *t c) | restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *t c) | |||
{ | { | |||
struct GNUNET_SERVER_Client *client = cls; | struct GNUNET_SERVER_Client *client = cls; | |||
struct GNUNET_SERVER_Handle *server = client->server; | ||||
GNUNET_assert (GNUNET_YES != client->shutdown_now); | ||||
client->restart_task = GNUNET_SCHEDULER_NO_TASK; | client->restart_task = GNUNET_SCHEDULER_NO_TASK; | |||
if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) && | if (GNUNET_NO == client->receive_pending) | |||
(GNUNET_NO == server->clients_ignore_shutdown)) | ||||
{ | { | |||
GNUNET_SERVER_client_disconnect (client); | ||||
return; | ||||
} | ||||
if (client->receive_pending == GNUNET_NO) | ||||
{ | ||||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client. \n"); | LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client. \n"); | |||
#endif | ||||
client->receive_pending = GNUNET_YES; | client->receive_pending = GNUNET_YES; | |||
GNUNET_CONNECTION_receive (client->connection, | GNUNET_CONNECTION_receive (client->connection, | |||
GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | |||
client->idle_timeout, &process_incoming, cli ent); | client->idle_timeout, &process_incoming, cli ent); | |||
return; | return; | |||
} | } | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Server continues processing messages still in the buffer.\n"); | "Server continues processing messages still in the buffer.\n"); | |||
#endif | ||||
GNUNET_SERVER_client_keep (client); | GNUNET_SERVER_client_keep (client); | |||
client->receive_pending = GNUNET_NO; | client->receive_pending = GNUNET_NO; | |||
process_mst (client, GNUNET_NO); | process_mst (client, GNUNET_NO); | |||
GNUNET_SERVER_client_drop (client); | ||||
} | } | |||
/** | /** | |||
* This function is called whenever our inbound message tokenizer has | * This function is called whenever our inbound message tokenizer has | |||
* received a complete message. | * received a complete message. | |||
* | * | |||
* @param cls closure (struct GNUNET_SERVER_Handle) | * @param cls closure (struct GNUNET_SERVER_Handle) | |||
* @param client identification of the client (struct GNUNET_SERVER_Client* ) | * @param client identification of the client (struct GNUNET_SERVER_Client* ) | |||
* @param message the actual message | * @param message the actual message | |||
* | ||||
* @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing | ||||
*/ | */ | |||
static void | static int | |||
client_message_tokenizer_callback (void *cls, void *client, | client_message_tokenizer_callback (void *cls, void *client, | |||
const struct GNUNET_MessageHeader *messa ge) | const struct GNUNET_MessageHeader *messa ge) | |||
{ | { | |||
struct GNUNET_SERVER_Handle *server = cls; | struct GNUNET_SERVER_Handle *server = cls; | |||
struct GNUNET_SERVER_Client *sender = client; | struct GNUNET_SERVER_Client *sender = client; | |||
int ret; | int ret; | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Tokenizer gives server message of type %u from client\n", | "Tokenizer gives server message of type %u from client\n", | |||
ntohs (message->type)); | ntohs (message->type)); | |||
#endif | ||||
sender->in_process_client_buffer = GNUNET_YES; | sender->in_process_client_buffer = GNUNET_YES; | |||
ret = GNUNET_SERVER_inject (server, sender, message); | ret = GNUNET_SERVER_inject (server, sender, message); | |||
sender->in_process_client_buffer = GNUNET_NO; | sender->in_process_client_buffer = GNUNET_NO; | |||
if (GNUNET_OK != ret) | if ( (GNUNET_OK != ret) || (GNUNET_YES == sender->shutdown_now) ) | |||
{ | ||||
GNUNET_SERVER_client_disconnect (sender); | GNUNET_SERVER_client_disconnect (sender); | |||
return GNUNET_SYSERR; | ||||
} | ||||
return GNUNET_OK; | ||||
} | } | |||
/** | /** | |||
* Add a TCP socket-based connection to the set of handles managed by | * Add a TCP socket-based connection to the set of handles managed by | |||
* this server. Use this function for outgoing (P2P) connections that | * this server. Use this function for outgoing (P2P) connections that | |||
* we initiated (and where this server should process incoming | * we initiated (and where this server should process incoming | |||
* messages). | * messages). | |||
* | * | |||
* @param server the server to use | * @param server the server to use | |||
* @param connection the connection to manage (client must | * @param connection the connection to manage (client must | |||
skipping to change at line 974 | skipping to change at line 1138 | |||
* "client_drop" on the return value eventually) | * "client_drop" on the return value eventually) | |||
*/ | */ | |||
struct GNUNET_SERVER_Client * | struct GNUNET_SERVER_Client * | |||
GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, | GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, | |||
struct GNUNET_CONNECTION_Handle *connection) | struct GNUNET_CONNECTION_Handle *connection) | |||
{ | { | |||
struct GNUNET_SERVER_Client *client; | struct GNUNET_SERVER_Client *client; | |||
client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client)); | client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client)); | |||
client->connection = connection; | client->connection = connection; | |||
client->mst = | ||||
GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, server) | ||||
; | ||||
client->reference_count = 1; | client->reference_count = 1; | |||
client->server = server; | client->server = server; | |||
client->last_activity = GNUNET_TIME_absolute_get (); | client->last_activity = GNUNET_TIME_absolute_get (); | |||
client->next = server->clients; | ||||
client->idle_timeout = server->idle_timeout; | client->idle_timeout = server->idle_timeout; | |||
server->clients = client; | GNUNET_CONTAINER_DLL_insert (server->clients_head, | |||
client->receive_pending = GNUNET_YES; | server->clients_tail, | |||
client->callback = NULL; | client); | |||
client->callback_cls = NULL; | if (NULL != server->mst_create) | |||
if (server->mst_create != NULL) | ||||
client->mst = | client->mst = | |||
server->mst_create (server->mst_cls, client); | server->mst_create (server->mst_cls, client); | |||
else | else | |||
client->mst = | client->mst = | |||
GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, serve r); | GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, serve r); | |||
GNUNET_assert (NULL != client->mst); | ||||
client->receive_pending = GNUNET_YES; | ||||
GNUNET_CONNECTION_receive (client->connection, | GNUNET_CONNECTION_receive (client->connection, | |||
GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, | |||
client->idle_timeout, &process_incoming, clien t); | client->idle_timeout, &process_incoming, clien t); | |||
return client; | return client; | |||
} | } | |||
/** | /** | |||
* Change the timeout for a particular client. Decreasing the timeout | * Change the timeout for a particular client. Decreasing the timeout | |||
* may not go into effect immediately (only after the previous timeout | * may not go into effect immediately (only after the previous timeout | |||
* times out or activity happens on the socket). | * times out or activity happens on the socket). | |||
skipping to change at line 1014 | skipping to change at line 1174 | |||
* @param client the client to update | * @param client the client to update | |||
* @param timeout new timeout for activities on the socket | * @param timeout new timeout for activities on the socket | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_client_set_timeout (struct GNUNET_SERVER_Client *client, | GNUNET_SERVER_client_set_timeout (struct GNUNET_SERVER_Client *client, | |||
struct GNUNET_TIME_Relative timeout) | struct GNUNET_TIME_Relative timeout) | |||
{ | { | |||
client->idle_timeout = timeout; | client->idle_timeout = timeout; | |||
} | } | |||
void | ||||
GNUNET_SERVER_client_set_finish_pending_write (struct GNUNET_SERVER_Client | ||||
*client, | ||||
int finish) | ||||
{ | ||||
client->finish_pending_write = finish; | ||||
} | ||||
/** | /** | |||
* Notify the server that the given client handle should | * Notify the server that the given client handle should | |||
* be kept (keeps the connection up if possible, increments | * be kept (keeps the connection up if possible, increments | |||
* the internal reference counter). | * the internal reference counter). | |||
* | * | |||
* @param client the client to keep | * @param client the client to keep | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client) | GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client) | |||
{ | { | |||
skipping to change at line 1047 | skipping to change at line 1200 | |||
* that counter reaches zero an inactive connection maybe | * that counter reaches zero an inactive connection maybe | |||
* closed. | * closed. | |||
* | * | |||
* @param client the client to drop | * @param client the client to drop | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client) | GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client) | |||
{ | { | |||
GNUNET_assert (client->reference_count > 0); | GNUNET_assert (client->reference_count > 0); | |||
client->reference_count--; | client->reference_count--; | |||
if ((client->shutdown_now == GNUNET_YES) && (client->reference_count == 0 )) | if ((GNUNET_YES == client->shutdown_now) && (0 == client->reference_count )) | |||
GNUNET_SERVER_client_disconnect (client); | GNUNET_SERVER_client_disconnect (client); | |||
} | } | |||
/** | /** | |||
* Obtain the network address of the other party. | * Obtain the network address of the other party. | |||
* | * | |||
* @param client the client to get the address for | * @param client the client to get the address for | |||
* @param addr where to store the address | * @param addr where to store the address | |||
* @param addrlen where to store the length of the address | * @param addrlen where to store the length of the address | |||
* @return GNUNET_OK on success | * @return GNUNET_OK on success | |||
skipping to change at line 1086 | skipping to change at line 1239 | |||
void | void | |||
GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, | GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, | |||
GNUNET_SERVER_DisconnectCallback callback, | GNUNET_SERVER_DisconnectCallback callback, | |||
void *callback_cls) | void *callback_cls) | |||
{ | { | |||
struct NotifyList *n; | struct NotifyList *n; | |||
n = GNUNET_malloc (sizeof (struct NotifyList)); | n = GNUNET_malloc (sizeof (struct NotifyList)); | |||
n->callback = callback; | n->callback = callback; | |||
n->callback_cls = callback_cls; | n->callback_cls = callback_cls; | |||
n->next = server->disconnect_notify_list; | GNUNET_CONTAINER_DLL_insert (server->disconnect_notify_list_head, | |||
server->disconnect_notify_list = n; | server->disconnect_notify_list_tail, | |||
n); | ||||
} | } | |||
/** | /** | |||
* Ask the server to stop notifying us whenever a client disconnects. | * Ask the server to stop notifying us whenever a client disconnects. | |||
* | * | |||
* @param server the server manageing the clients | * @param server the server manageing the clients | |||
* @param callback function to call on disconnect | * @param callback function to call on disconnect | |||
* @param callback_cls closure for callback | * @param callback_cls closure for callback | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server , | GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server , | |||
GNUNET_SERVER_DisconnectCallback | GNUNET_SERVER_DisconnectCallback | |||
callback, void *callback_cls) | callback, void *callback_cls) | |||
{ | { | |||
struct NotifyList *pos; | struct NotifyList *pos; | |||
struct NotifyList *prev; | ||||
prev = NULL; | for (pos = server->disconnect_notify_list_head; NULL != pos; pos = pos->n | |||
pos = server->disconnect_notify_list; | ext) | |||
while (pos != NULL) | ||||
{ | ||||
if ((pos->callback == callback) && (pos->callback_cls == callback_cls)) | if ((pos->callback == callback) && (pos->callback_cls == callback_cls)) | |||
break; | break; | |||
prev = pos; | if (NULL == pos) | |||
pos = pos->next; | ||||
} | ||||
if (pos == NULL) | ||||
{ | { | |||
GNUNET_break (0); | GNUNET_break (0); | |||
return; | return; | |||
} | } | |||
if (prev == NULL) | GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head, | |||
server->disconnect_notify_list = pos->next; | server->disconnect_notify_list_tail, | |||
else | pos); | |||
prev->next = pos->next; | ||||
GNUNET_free (pos); | GNUNET_free (pos); | |||
} | } | |||
/** | /** | |||
* Destroy the connection that is passed in via 'cls'. Used | ||||
* as calling 'GNUNET_CONNECTION_destroy' from within a function | ||||
* that was itself called from within 'process_notify' of | ||||
* 'connection.c' is not allowed (see #2329). | ||||
* | ||||
* @param cls connection to destroy | ||||
* @param tc scheduler context (unused) | ||||
*/ | ||||
static void | ||||
destroy_connection (void *cls, | ||||
const struct GNUNET_SCHEDULER_TaskContext *tc) | ||||
{ | ||||
struct GNUNET_CONNECTION_Handle *connection = cls; | ||||
GNUNET_CONNECTION_destroy (connection); | ||||
} | ||||
/** | ||||
* Ask the server to disconnect from the given client. | * Ask the server to disconnect from the given client. | |||
* This is the same as returning GNUNET_SYSERR from a message | * This is the same as returning GNUNET_SYSERR from a message | |||
* handler, except that it allows dropping of a client even | * handler, except that it allows dropping of a client even | |||
* when not handling a message from that client. | * when not handling a message from that client. | |||
* | * | |||
* @param client the client to disconnect from | * @param client the client to disconnect from | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) | GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) | |||
{ | { | |||
struct GNUNET_SERVER_Client *prev; | struct GNUNET_SERVER_Handle *server = client->server; | |||
struct GNUNET_SERVER_Client *pos; | ||||
struct GNUNET_SERVER_Handle *server; | ||||
struct NotifyList *n; | struct NotifyList *n; | |||
unsigned int rc; | ||||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Client is being disconnected from the server.\n"); | "Client is being disconnected from the server.\n"); | |||
#endif | if (GNUNET_SCHEDULER_NO_TASK != client->restart_task) | |||
if (client->restart_task != GNUNET_SCHEDULER_NO_TASK) | ||||
{ | { | |||
GNUNET_SCHEDULER_cancel (client->restart_task); | GNUNET_SCHEDULER_cancel (client->restart_task); | |||
client->restart_task = GNUNET_SCHEDULER_NO_TASK; | client->restart_task = GNUNET_SCHEDULER_NO_TASK; | |||
} | } | |||
if (client->warn_task != GNUNET_SCHEDULER_NO_TASK) | if (GNUNET_SCHEDULER_NO_TASK != client->warn_task) | |||
{ | { | |||
GNUNET_SCHEDULER_cancel (client->warn_task); | GNUNET_SCHEDULER_cancel (client->warn_task); | |||
client->warn_task = GNUNET_SCHEDULER_NO_TASK; | client->warn_task = GNUNET_SCHEDULER_NO_TASK; | |||
} | } | |||
if (GNUNET_YES == client->receive_pending) | if (GNUNET_YES == client->receive_pending) | |||
{ | { | |||
GNUNET_CONNECTION_receive_cancel (client->connection); | GNUNET_CONNECTION_receive_cancel (client->connection); | |||
client->receive_pending = GNUNET_NO; | client->receive_pending = GNUNET_NO; | |||
} | } | |||
client->shutdown_now = GNUNET_YES; | ||||
rc = client->reference_count; | client->reference_count++; /* make sure nobody else clean up client... */ | |||
if (client->shutdown_now != GNUNET_YES) | if ( (NULL != client->mst) && | |||
{ | (NULL != server) ) | |||
server = client->server; | { | |||
client->shutdown_now = GNUNET_YES; | GNUNET_CONTAINER_DLL_remove (server->clients_head, | |||
prev = NULL; | server->clients_tail, | |||
pos = server->clients; | client); | |||
while ((pos != NULL) && (pos != client)) | if (NULL != server->mst_destroy) | |||
{ | server->mst_destroy (server->mst_cls, client->mst); | |||
prev = pos; | ||||
pos = pos->next; | ||||
} | ||||
GNUNET_assert (pos != NULL); | ||||
if (prev == NULL) | ||||
server->clients = pos->next; | ||||
else | else | |||
prev->next = pos->next; | GNUNET_SERVER_mst_destroy (client->mst); | |||
if (client->restart_task != GNUNET_SCHEDULER_NO_TASK) | client->mst = NULL; | |||
{ | for (n = server->disconnect_notify_list_head; NULL != n; n = n->next) | |||
GNUNET_SCHEDULER_cancel (client->restart_task); | ||||
client->restart_task = GNUNET_SCHEDULER_NO_TASK; | ||||
} | ||||
if (client->warn_task != GNUNET_SCHEDULER_NO_TASK) | ||||
{ | ||||
GNUNET_SCHEDULER_cancel (client->warn_task); | ||||
client->warn_task = GNUNET_SCHEDULER_NO_TASK; | ||||
} | ||||
n = server->disconnect_notify_list; | ||||
while (n != NULL) | ||||
{ | ||||
n->callback (n->callback_cls, client); | n->callback (n->callback_cls, client); | |||
n = n->next; | ||||
} | ||||
} | } | |||
if (rc > 0) | client->reference_count--; | |||
if (client->reference_count > 0) | ||||
{ | { | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"RC still positive, not destroying everything.\n"); | "RC still positive, not destroying everything.\n"); | |||
#endif | client->server = NULL; | |||
return; | return; | |||
} | } | |||
if (client->in_process_client_buffer == GNUNET_YES) | if (GNUNET_YES == client->in_process_client_buffer) | |||
{ | { | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"Still processing inputs, not destroying everything.\n"); | "Still processing inputs, not destroying everything.\n"); | |||
#endif | ||||
return; | return; | |||
} | } | |||
if (GNUNET_YES == client->persist) | ||||
if (client->persist == GNUNET_YES) | ||||
GNUNET_CONNECTION_persist_ (client->connection); | GNUNET_CONNECTION_persist_ (client->connection); | |||
GNUNET_CONNECTION_destroy (client->connection, client->finish_pending_wri | if (NULL != client->th.cth) | |||
te); | GNUNET_SERVER_notify_transmit_ready_cancel (&client->th); | |||
(void) GNUNET_SCHEDULER_add_now (&destroy_connection, | ||||
if (client->server->mst_destroy != NULL) | client->connection); | |||
client->server->mst_destroy (client->server->mst_cls, client->mst); | /* need to cancel again, as it might have been re-added | |||
else | in the meantime (i.e. during callbacks) */ | |||
GNUNET_SERVER_mst_destroy (client->mst); | if (GNUNET_SCHEDULER_NO_TASK != client->warn_task) | |||
{ | ||||
GNUNET_SCHEDULER_cancel (client->warn_task); | ||||
client->warn_task = GNUNET_SCHEDULER_NO_TASK; | ||||
} | ||||
if (GNUNET_YES == client->receive_pending) | ||||
{ | ||||
GNUNET_CONNECTION_receive_cancel (client->connection); | ||||
client->receive_pending = GNUNET_NO; | ||||
} | ||||
GNUNET_free (client); | 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, | * Disable the "CORK" feature for communication with the given client, | |||
* forcing the OS to immediately flush the buffer on transmission | * forcing the OS to immediately flush the buffer on transmission | |||
* instead of potentially buffering multiple messages. | * instead of potentially buffering multiple messages. | |||
* | * | |||
* @param client handle to the client | * @param client handle to the client | |||
* @return GNUNET_OK on success | * @return GNUNET_OK on success | |||
*/ | */ | |||
skipping to change at line 1253 | skipping to change at line 1402 | |||
* | * | |||
* @param cls the 'struct GNUNET_SERVER_Client' | * @param cls the 'struct GNUNET_SERVER_Client' | |||
* @param size number of bytes we can transmit | * @param size number of bytes we can transmit | |||
* @param buf where to copy the message | * @param buf where to copy the message | |||
* @return number of bytes actually transmitted | * @return number of bytes actually transmitted | |||
*/ | */ | |||
static size_t | static size_t | |||
transmit_ready_callback_wrapper (void *cls, size_t size, void *buf) | transmit_ready_callback_wrapper (void *cls, size_t size, void *buf) | |||
{ | { | |||
struct GNUNET_SERVER_Client *client = cls; | struct GNUNET_SERVER_Client *client = cls; | |||
size_t ret; | GNUNET_CONNECTION_TransmitReadyNotify callback; | |||
ret = client->callback (client->callback_cls, size, buf); | client->th.cth = NULL; | |||
if (ret > 0) | callback = client->th.callback; | |||
client->last_activity = GNUNET_TIME_absolute_get (); | client->th.callback = NULL; | |||
return ret; | 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 | * Notify us when the server has enough space to transmit | |||
* a message of the given size to the given client. | * a message of the given size to the given client. | |||
* | * | |||
* @param client client to transmit message to | * @param client client to transmit message to | |||
* @param size requested amount of buffer space | * @param size requested amount of buffer space | |||
* @param timeout after how long should we give up (and call | * @param timeout after how long should we give up (and call | |||
* notify with buf NULL and size 0)? | * notify with buf NULL and size 0)? | |||
* @param callback function to call when space is available | * @param callback function to call when space is available | |||
* @param callback_cls closure for callback | * @param callback_cls closure for callback | |||
* @return non-NULL if the notify callback was queued; can be used | * @return non-NULL if the notify callback was queued; can be used | |||
* to cancel the request using | * to cancel the request using | |||
* GNUNET_CONNECTION_notify_transmit_ready_cancel. | * GNUNET_SERVER_notify_transmit_ready_cancel. | |||
* NULL if we are already going to notify someone else (busy) | * NULL if we are already going to notify someone else (busy) | |||
*/ | */ | |||
struct GNUNET_CONNECTION_TransmitHandle * | struct GNUNET_SERVER_TransmitHandle * | |||
GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, | GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, | |||
size_t size, | size_t size, | |||
struct GNUNET_TIME_Relative timeout, | struct GNUNET_TIME_Relative timeout, | |||
GNUNET_CONNECTION_TransmitReadyNotify | GNUNET_CONNECTION_TransmitReadyNotify | |||
callback, void *callback_cls) | callback, void *callback_cls) | |||
{ | { | |||
client->callback_cls = callback_cls; | if (NULL != client->th.callback) | |||
client->callback = callback; | return NULL; | |||
return GNUNET_CONNECTION_notify_transmit_ready (client->connection, size, | client->th.callback_cls = callback_cls; | |||
timeout, | client->th.callback = callback; | |||
&transmit_ready_callback_ | client->th.cth = GNUNET_CONNECTION_notify_transmit_ready (client->connect | |||
wrapper, | ion, size, | |||
client); | 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_TransmitHa | ||||
ndle *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 | * 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. | * to only be killed when the service it's connected to is actually dead. | |||
* | * | |||
* @param client the client to set the persistent flag on | * @param client the client to set the persistent flag on | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client) | GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client) | |||
skipping to change at line 1318 | skipping to change at line 1484 | |||
* @param client client we were processing a message of | * @param client client we were processing a message of | |||
* @param success GNUNET_OK to keep the connection open and | * @param success GNUNET_OK to keep the connection open and | |||
* continue to receive | * continue to receive | |||
* GNUNET_NO to close the connection (normal behavior) | * GNUNET_NO to close the connection (normal behavior) | |||
* GNUNET_SYSERR to close the connection (signal | * GNUNET_SYSERR to close the connection (signal | |||
* serious error) | * serious error) | |||
*/ | */ | |||
void | void | |||
GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int succes s) | GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int succes s) | |||
{ | { | |||
if (client == NULL) | if (NULL == client) | |||
return; | return; | |||
GNUNET_assert (client->suspended > 0); | GNUNET_assert (client->suspended > 0); | |||
client->suspended--; | client->suspended--; | |||
if (success != GNUNET_OK) | if (GNUNET_OK != success) | |||
{ | { | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"GNUNET_SERVER_receive_done called with failure indication\n"); | "GNUNET_SERVER_receive_done called with failure indication\n"); | |||
#endif | if ( (client->reference_count > 0) || (client->suspended > 0) ) | |||
GNUNET_SERVER_client_disconnect (client); | client->shutdown_now = GNUNET_YES; | |||
else | ||||
GNUNET_SERVER_client_disconnect (client); | ||||
return; | return; | |||
} | } | |||
if (client->suspended > 0) | if (client->suspended > 0) | |||
{ | { | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"GNUNET_SERVER_receive_done called, but more clients pending\n"); | "GNUNET_SERVER_receive_done called, but more clients pending\n"); | |||
#endif | ||||
return; | return; | |||
} | } | |||
if (GNUNET_SCHEDULER_NO_TASK != client->warn_task) | if (GNUNET_SCHEDULER_NO_TASK != client->warn_task) | |||
{ | { | |||
GNUNET_SCHEDULER_cancel (client->warn_task); | GNUNET_SCHEDULER_cancel (client->warn_task); | |||
client->warn_task = GNUNET_SCHEDULER_NO_TASK; | client->warn_task = GNUNET_SCHEDULER_NO_TASK; | |||
} | } | |||
if (client->in_process_client_buffer == GNUNET_YES) | if (GNUNET_YES == client->in_process_client_buffer) | |||
{ | { | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"GNUNET_SERVER_receive_done called while still in processing loop\ n"); | "GNUNET_SERVER_receive_done called while still in processing loop\ n"); | |||
#endif | ||||
return; | return; | |||
} | } | |||
if ((client->server == NULL) || (GNUNET_YES == client->shutdown_now)) | if ((NULL == client->server) || (GNUNET_YES == client->shutdown_now)) | |||
{ | { | |||
GNUNET_SERVER_client_disconnect (client); | GNUNET_SERVER_client_disconnect (client); | |||
return; | return; | |||
} | } | |||
#if DEBUG_SERVER | ||||
LOG (GNUNET_ERROR_TYPE_DEBUG, | LOG (GNUNET_ERROR_TYPE_DEBUG, | |||
"GNUNET_SERVER_receive_done causes restart in reading from the socke t\n"); | "GNUNET_SERVER_receive_done causes restart in reading from the socke t\n"); | |||
#endif | ||||
GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == client->restart_task); | GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == client->restart_task); | |||
client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing, cli ent); | client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing, cli ent); | |||
} | } | |||
/** | ||||
* Configure this server's connections to continue handling client | ||||
* requests as usual even after we get a shutdown signal. The change | ||||
* only applies to clients that connect to the server from the outside | ||||
* using TCP after this call. Clients managed previously or those | ||||
* added using GNUNET_SERVER_connect_socket and | ||||
* GNUNET_SERVER_connect_callback are not affected by this option. | ||||
* | ||||
* @param h server handle | ||||
* @param do_ignore GNUNET_YES to ignore, GNUNET_NO to restore default | ||||
*/ | ||||
void | ||||
GNUNET_SERVER_ignore_shutdown (struct GNUNET_SERVER_Handle *h, int do_ignor | ||||
e) | ||||
{ | ||||
h->clients_ignore_shutdown = do_ignore; | ||||
} | ||||
/* end of server.c */ | /* end of server.c */ | |||
End of changes. 158 change blocks. | ||||
309 lines changed or deleted | 459 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |