summaryrefslogtreecommitdiff
path: root/src/transport
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/Makefile.am4
-rw-r--r--src/transport/gnunet-helper-transport-wlan-dummy.c30
-rw-r--r--src/transport/plugin_transport_http_client.c21
-rw-r--r--src/transport/plugin_transport_http_server.c23
-rw-r--r--src/transport/plugin_transport_tcp.c528
-rw-r--r--src/transport/plugin_transport_udp.c39
-rw-r--r--src/transport/plugin_transport_udp.h12
-rw-r--r--src/transport/plugin_transport_udp_broadcasting.c30
-rw-r--r--src/transport/plugin_transport_wlan.c29
-rw-r--r--src/transport/tcp_connection_legacy.c1647
-rw-r--r--src/transport/tcp_server_legacy.c1748
-rw-r--r--src/transport/tcp_server_mst_legacy.c311
-rw-r--r--src/transport/tcp_service_legacy.c1687
-rw-r--r--src/transport/test_plugin_transport.c2
14 files changed, 5979 insertions, 132 deletions
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am
index acc2557c6..7687f2348 100644
--- a/src/transport/Makefile.am
+++ b/src/transport/Makefile.am
@@ -191,6 +191,8 @@ libexec_PROGRAMS = \
$(BT_BIN) \
gnunet-service-transport
+
+
bin_PROGRAMS = \
gnunet-transport \
gnunet-transport-certificate-creation
@@ -561,7 +563,7 @@ TESTS = \
$(HTTP_API_TIMEOUT_TEST) \
$(HTTPS_API_TIMEOUT_TEST) \
$(WLAN_TIMEOUT_TEST) \
- $(BT_TIMEOUT_TEST)
+ $(BT_TIMEOUT_TEST)
if HAVE_GETOPT_BINARY
TESTS += \
test_transport_api_slow_ats
diff --git a/src/transport/gnunet-helper-transport-wlan-dummy.c b/src/transport/gnunet-helper-transport-wlan-dummy.c
index 684546314..63ed9c4b7 100644
--- a/src/transport/gnunet-helper-transport-wlan-dummy.c
+++ b/src/transport/gnunet-helper-transport-wlan-dummy.c
@@ -120,11 +120,11 @@ send_mac_to_plugin (char *buffer, struct GNUNET_TRANSPORT_WLAN_MacAddress *mac)
* type to the output forward and copy it to the buffer for stdout.
*
* @param cls the 'struct SendBuffer' to copy the converted message to
- * @param client unused
* @param hdr inbound message from the FIFO
*/
static int
-stdin_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr)
+stdin_send (void *cls,
+ const struct GNUNET_MessageHeader *hdr)
{
struct SendBuffer *write_pout = cls;
const struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage *in;
@@ -166,11 +166,11 @@ stdin_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr)
* We read a full message from stdin. Copy it to our send buffer.
*
* @param cls the 'struct SendBuffer' to copy to
- * @param client unused
* @param hdr the message we received to copy to the buffer
*/
static int
-file_in_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr)
+file_in_send (void *cls,
+ const struct GNUNET_MessageHeader *hdr)
{
struct SendBuffer *write_std = cls;
uint16_t sendsize;
@@ -213,8 +213,8 @@ main (int argc, char *argv[])
fd_set wfds;
struct timeval tv;
int retval;
- struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst = NULL;
- struct GNUNET_SERVER_MessageStreamTokenizer *file_in_mst = NULL;
+ struct GNUNET_MessageStreamTokenizer *stdin_mst = NULL;
+ struct GNUNET_MessageStreamTokenizer *file_in_mst = NULL;
struct GNUNET_TRANSPORT_WLAN_MacAddress macaddr;
int first;
@@ -340,8 +340,8 @@ main (int argc, char *argv[])
write_std.pos = 0;
write_pout.size = 0;
write_pout.pos = 0;
- stdin_mst = GNUNET_SERVER_mst_create (&stdin_send, &write_pout);
- file_in_mst = GNUNET_SERVER_mst_create (&file_in_send, &write_std);
+ stdin_mst = GNUNET_MST_create (&stdin_send, &write_pout);
+ file_in_mst = GNUNET_MST_create (&file_in_send, &write_std);
/* Send 'random' mac address */
macaddr.mac[0] = 0x13;
@@ -453,8 +453,9 @@ main (int argc, char *argv[])
}
else if (0 < readsize)
{
- GNUNET_SERVER_mst_receive (stdin_mst, NULL, readbuf, readsize,
- GNUNET_NO, GNUNET_NO);
+ GNUNET_MST_from_buffer (stdin_mst,
+ readbuf, readsize,
+ GNUNET_NO, GNUNET_NO);
}
else
@@ -475,8 +476,9 @@ main (int argc, char *argv[])
}
else if (0 < readsize)
{
- GNUNET_SERVER_mst_receive (file_in_mst, NULL, readbuf, readsize,
- GNUNET_NO, GNUNET_NO);
+ GNUNET_MST_from_buffer (file_in_mst,
+ readbuf, readsize,
+ GNUNET_NO, GNUNET_NO);
}
else
{
@@ -489,9 +491,9 @@ main (int argc, char *argv[])
end:
/* clean up */
if (NULL != stdin_mst)
- GNUNET_SERVER_mst_destroy (stdin_mst);
+ GNUNET_MST_destroy (stdin_mst);
if (NULL != file_in_mst)
- GNUNET_SERVER_mst_destroy (file_in_mst);
+ GNUNET_MST_destroy (file_in_mst);
if (NULL != fpout)
fclose (fpout);
diff --git a/src/transport/plugin_transport_http_client.c b/src/transport/plugin_transport_http_client.c
index ceed94af8..e91149289 100644
--- a/src/transport/plugin_transport_http_client.c
+++ b/src/transport/plugin_transport_http_client.c
@@ -221,7 +221,7 @@ struct GNUNET_ATS_Session
/**
* Message stream tokenizer for incoming data
*/
- struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
+ struct GNUNET_MessageStreamTokenizer *msg_tk;
/**
* Session timeout task
@@ -528,7 +528,7 @@ client_delete_session (struct GNUNET_ATS_Session *s)
GNUNET_TRANSPORT_SS_DONE);
if (NULL != s->msg_tk)
{
- GNUNET_SERVER_mst_destroy (s->msg_tk);
+ GNUNET_MST_destroy (s->msg_tk);
s->msg_tk = NULL;
}
GNUNET_HELLO_address_free (s->address);
@@ -1158,13 +1158,11 @@ client_wake_up (void *cls)
* Callback for message stream tokenizer
*
* @param cls the session
- * @param client not used
* @param message the message received
* @return always #GNUNET_OK
*/
static int
client_receive_mst_cb (void *cls,
- void *client,
const struct GNUNET_MessageHeader *message)
{
struct GNUNET_ATS_Session *s = cls;
@@ -1274,14 +1272,13 @@ client_receive (void *stream,
return CURL_WRITEFUNC_PAUSE;
}
if (NULL == s->msg_tk)
- s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb,
- s);
- GNUNET_SERVER_mst_receive (s->msg_tk,
- s,
- stream,
- len,
- GNUNET_NO,
- GNUNET_NO);
+ s->msg_tk = GNUNET_MST_create (&client_receive_mst_cb,
+ s);
+ GNUNET_MST_from_buffer (s->msg_tk,
+ stream,
+ len,
+ GNUNET_NO,
+ GNUNET_NO);
return len;
}
diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c
index 63c67b81c..2d6f40d58 100644
--- a/src/transport/plugin_transport_http_server.c
+++ b/src/transport/plugin_transport_http_server.c
@@ -201,7 +201,7 @@ struct GNUNET_ATS_Session
/**
* Message stream tokenizer for incoming data
*/
- struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
+ struct GNUNET_MessageStreamTokenizer *msg_tk;
/**
* Client recv handle
@@ -608,7 +608,7 @@ server_delete_session (struct GNUNET_ATS_Session *s)
}
if (NULL != s->msg_tk)
{
- GNUNET_SERVER_mst_destroy (s->msg_tk);
+ GNUNET_MST_destroy (s->msg_tk);
s->msg_tk = NULL;
}
GNUNET_HELLO_address_free (s->address);
@@ -1621,13 +1621,11 @@ server_send_callback (void *cls,
* Callback called by MessageStreamTokenizer when a message has arrived
*
* @param cls current session as closure
- * @param client client
* @param message the message to be forwarded to transport service
* @return #GNUNET_OK
*/
static int
server_receive_mst_cb (void *cls,
- void *client,
const struct GNUNET_MessageHeader *message)
{
struct GNUNET_ATS_Session *s = cls;
@@ -1847,13 +1845,16 @@ server_access_cb (void *cls,
*upload_data_size);
if (s->msg_tk == NULL)
{
- s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
+ s->msg_tk = GNUNET_MST_create (&server_receive_mst_cb,
+ s);
}
- GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, *upload_data_size,
- GNUNET_NO, GNUNET_NO);
+ GNUNET_MST_from_buffer (s->msg_tk,
+ upload_data,
+ *upload_data_size,
+ GNUNET_NO, GNUNET_NO);
server_mhd_connection_timeout (plugin, s,
- GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us / 1000LL
- / 1000LL);
+ GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us / 1000LL
+ / 1000LL);
(*upload_data_size) = 0;
}
else
@@ -1935,7 +1936,7 @@ server_disconnect_cb (void *cls,
sc->session->server_recv = NULL;
if (NULL != sc->session->msg_tk)
{
- GNUNET_SERVER_mst_destroy (sc->session->msg_tk);
+ GNUNET_MST_destroy (sc->session->msg_tk);
sc->session->msg_tk = NULL;
}
}
@@ -2757,7 +2758,7 @@ server_start_report_addresses (struct HTTP_Server_Plugin *plugin)
return;
}
- plugin->nat
+ plugin->nat
= GNUNET_NAT_register (plugin->env->cfg,
"transport-http_server",
IPPROTO_TCP,
diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c
index 34bbd00e0..10ea01cec 100644
--- a/src/transport/plugin_transport_tcp.c
+++ b/src/transport/plugin_transport_tcp.c
@@ -45,10 +45,495 @@
*/
#define NAT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
-GNUNET_NETWORK_STRUCT_BEGIN
+/**
+ * Opaque handle that can be used to cancel
+ * a transmit-ready notification.
+ */
+struct GNUNET_CONNECTION_TransmitHandle;
+
+/**
+ * @brief handle for a server
+ */
+struct GNUNET_SERVER_Handle;
+
+/**
+ * @brief opaque handle for a client of the server
+ */
+struct GNUNET_SERVER_Client;
+
+/**
+ * @brief opaque handle server returns for aborting transmission to a client.
+ */
+struct GNUNET_SERVER_TransmitHandle;
+
+/**
+ * @brief handle for a network connection
+ */
+struct GNUNET_CONNECTION_Handle;
+
+
+/**
+ * Function called to notify a client about the connection begin ready
+ * to queue more data. @a buf will be NULL and @a size zero if the
+ * connection was closed for writing in the meantime.
+ *
+ * @param cls closure
+ * @param size number of bytes available in @a buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to @a buf
+ */
+typedef size_t
+(*GNUNET_CONNECTION_TransmitReadyNotify) (void *cls,
+ size_t size,
+ void *buf);
+
+/**
+ * Credentials for UNIX domain sockets.
+ */
+struct GNUNET_CONNECTION_Credentials
+{
+ /**
+ * UID of the other end of the connection.
+ */
+ uid_t uid;
+
+ /**
+ * GID of the other end of the connection.
+ */
+ gid_t gid;
+};
+
+
+/**
+ * Functions with this signature are called whenever a client
+ * is disconnected on the network level.
+ *
+ * @param cls closure
+ * @param client identification of the client; NULL
+ * for the last call when the server is destroyed
+ */
+typedef void
+(*GNUNET_SERVER_DisconnectCallback) (void *cls,
+ struct GNUNET_SERVER_Client *client);
+
+
+/**
+ * Functions with this signature are called whenever a client
+ * is connected on the network level.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ */
+typedef void
+(*GNUNET_SERVER_ConnectCallback) (void *cls,
+ struct GNUNET_SERVER_Client *client);
+
+
/**
+ * Function to call for access control checks.
+ *
+ * @param cls closure
+ * @param ucred credentials, if available, otherwise NULL
+ * @param addr address
+ * @param addrlen length of address
+ * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR
+ * for unknown address family (will be denied).
+ */
+typedef int
+(*GNUNET_CONNECTION_AccessCheck) (void *cls,
+ const struct
+ GNUNET_CONNECTION_Credentials *
+ ucred,
+ const struct sockaddr * addr,
+ socklen_t addrlen);
+
+/**
+ * Callback function for data received from the network. Note that
+ * both "available" and "err" would be 0 if the read simply timed out.
+ *
+ * @param cls closure
+ * @param buf pointer to received data
+ * @param available number of bytes availabe in "buf",
+ * possibly 0 (on errors)
+ * @param addr address of the sender
+ * @param addrlen size of addr
+ * @param errCode value of errno (on errors receiving)
+ */
+typedef void
+(*GNUNET_CONNECTION_Receiver) (void *cls, const void *buf,
+ size_t available,
+ const struct sockaddr * addr,
+ socklen_t addrlen, int errCode);
+
+
+
+/**
+ * Close the connection and free associated resources. There must
+ * not be any pending requests for reading or writing to the
+ * connection at this time.
+ *
+ * @param connection connection to destroy
+ */
+void
+GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection);
+
+
+/**
+ * Signature of a function to create a custom tokenizer.
+ *
+ * @param cls closure from #GNUNET_SERVER_set_callbacks
+ * @param client handle to client the tokenzier will be used for
+ * @return handle to custom tokenizer ('mst')
+ */
+typedef void*
+(*GNUNET_SERVER_MstCreateCallback) (void *cls,
+ struct GNUNET_SERVER_Client *client);
+
+
+/**
+ * Signature of a function to destroy a custom tokenizer.
+ *
+ * @param cls closure from #GNUNET_SERVER_set_callbacks
+ * @param mst custom tokenizer handle
+ */
+typedef void
+(*GNUNET_SERVER_MstDestroyCallback) (void *cls,
+ void *mst);
+
+/**
+ * Signature of a function to receive data for a custom tokenizer.
+ *
+ * @param cls closure from #GNUNET_SERVER_set_callbacks
+ * @param mst custom tokenizer handle
+ * @param client_identity ID of client for which this is a buffer,
+ * can be NULL (will be passed back to 'cb')
+ * @param buf input data to add
+ * @param size number of bytes in @a buf
+ * @param purge should any excess bytes in the buffer be discarded
+ * (i.e. for packet-based services like UDP)
+ * @param one_shot only call callback once, keep rest of message in buffer
+ * @return #GNUNET_OK if we are done processing (need more data)
+ * #GNUNET_NO if one_shot was set and we have another message ready
+ * #GNUNET_SYSERR if the data stream is corrupt
+ */
+typedef int
+(*GNUNET_SERVER_MstReceiveCallback) (void *cls, void *mst,
+ struct GNUNET_SERVER_Client *client,
+ const char *buf,
+ size_t size,
+ int purge,
+ int one_shot);
+/**
+ * Functions with this signature are called whenever a message is
+ * received.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param message the actual message
+ */
+typedef void
+(*GNUNET_SERVER_MessageCallback) (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message);
+
+/**
+ * Message handler. Each struct specifies how to handle on particular
+ * type of message received.
+ */
+struct GNUNET_SERVER_MessageHandler
+{
+ /**
+ * Function to call for messages of "type".
+ */
+ GNUNET_SERVER_MessageCallback callback;
+
+ /**
+ * Closure argument for @e callback.
+ */
+ void *callback_cls;
+
+ /**
+ * Type of the message this handler covers.
+ */
+ uint16_t type;
+
+ /**
+ * Expected size of messages of this type. Use 0 for
+ * variable-size. If non-zero, messages of the given
+ * type will be discarded (and the connection closed)
+ * if they do not have the right size.
+ */
+ uint16_t expected_size;
+
+};
+
+/**
+ * Ask the server to disconnect from the given client. This is the
+ * same as passing #GNUNET_SYSERR to #GNUNET_SERVER_receive_done,
+ * 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);
+
+/**
+ * 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);
+
+
+/**
+ * Functions with this signature are called whenever a
+ * complete message is received by the tokenizer.
+ *
+ * Do not call #GNUNET_SERVER_mst_destroy from within
+ * the scope of this callback.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param message the actual message
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
+ */
+typedef int
+(*GNUNET_SERVER_MessageTokenizerCallback) (void *cls,
+ void *client,
+ const struct GNUNET_MessageHeader *message);
+
+
+/**
+ * Create a message stream tokenizer.
+ *
+ * @param cb function to call on completed messages
+ * @param cb_cls closure for @a cb
+ * @return handle to tokenizer
+ */
+struct GNUNET_SERVER_MessageStreamTokenizer *
+GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb,
+ void *cb_cls);
+
+/**
+ * Add incoming data to the receive buffer and call the
+ * callback for all complete messages.
+ *
+ * @param mst tokenizer to use
+ * @param client_identity ID of client for which this is a buffer,
+ * can be NULL (will be passed back to 'cb')
+ * @param buf input data to add
+ * @param size number of bytes in @a buf
+ * @param purge should any excess bytes in the buffer be discarded
+ * (i.e. for packet-based services like UDP)
+ * @param one_shot only call callback once, keep rest of message in buffer
+ * @return #GNUNET_OK if we are done processing (need more data)
+ * #GNUNET_NO if one_shot was set and we have another message ready
+ * #GNUNET_SYSERR if the data stream is corrupt
+ */
+int
+GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst,
+ void *client_identity,
+ const char *buf, size_t size,
+ int purge, int one_shot);
+
+
+
+/**
+ * Destroys a tokenizer.
+ *
+ * @param mst tokenizer to destroy
+ */
+void
+GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst);
+
+
+/**
+ * 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);
+/**
+ * Return user context associated with the given client.
+ *
+ * @param client client to query
+ * @param type expected return type (i.e. 'struct Foo')
+ * @return pointer to user context of type 'type *'.
+ */
+#define GNUNET_SERVER_client_get_user_context(client,type) \
+ (type *) GNUNET_SERVER_client_get_user_context_ (client, sizeof (type))
+
+/**
+ * Set user context to be associated with the given client.
+ *
+ * @param client client to query
+ * @param value pointer to user context
+ */
+#define GNUNET_SERVER_client_set_user_context(client,value) \
+ GNUNET_SERVER_client_set_user_context_ (client, value, sizeof (*value))
+
+
+
+/**
+ * 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);
+
+/**
+ * Abort transmission request.
+ *
+ * @param th request to abort
+ */
+void
+GNUNET_SERVER_notify_transmit_ready_cancel (struct GNUNET_SERVER_TransmitHandle *th);
+
+
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * Function called by the service's run
+ * method to run service-specific setup code.
+ *
+ * @param cls closure
+ * @param server the initialized server
+ * @param cfg configuration to use
+ */
+typedef void
+(*GNUNET_SERVICE_Main) (void *cls,
+ struct GNUNET_SERVER_Handle *server,
+ const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+
+/**
+ * Suspend accepting connections from the listen socket temporarily.
+ * Resume activity using #GNUNET_SERVER_resume.
+ *
+ * @param server server to stop accepting connections.
+ */
+void
+GNUNET_SERVER_suspend (struct GNUNET_SERVER_Handle *server);
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * Resume accepting connections from the listen socket.
+ *
+ * @param server server to resume accepting connections.
+ */
+void
+GNUNET_SERVER_resume (struct GNUNET_SERVER_Handle *server);
+
+/**
+ * Free resources held by this server.
+ *
+ * @param server server to destroy
+ */
+void
+GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server);
+
+
+
+
+#include "tcp_connection_legacy.c"
+#include "tcp_server_mst_legacy.c"
+#include "tcp_server_legacy.c"
+#include "tcp_service_legacy.c"
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
* Initial handshake message for a session.
*/
struct WelcomeMessage
@@ -521,47 +1006,6 @@ struct Plugin
};
-/* begin of ancient copy-and-pasted code that should be
- specialized for TCP ...*/
-/**
- * Add the given UNIX domain path as an address to the
- * list (as the first entry).
- *
- * @param saddrs array to update
- * @param saddrlens where to store the address length
- * @param unixpath path to add
- * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This
- * parameter is ignore on systems other than LINUX
- */
-static void
-add_unixpath (struct sockaddr **saddrs,
- socklen_t *saddrlens,
- const char *unixpath,
- int abstract)
-{
-#ifdef AF_UNIX
- struct sockaddr_un *un;
-
- un = GNUNET_new (struct sockaddr_un);
- un->sun_family = AF_UNIX;
- strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1);
-#ifdef LINUX
- if (GNUNET_YES == abstract)
- un->sun_path[0] = '\0';
-#endif
-#if HAVE_SOCKADDR_UN_SUN_LEN
- un->sun_len = (u_char) sizeof (struct sockaddr_un);
-#endif
- *saddrs = (struct sockaddr *) un;
- *saddrlens = sizeof (struct sockaddr_un);
-#else
- /* this function should never be called
- * unless AF_UNIX is defined! */
- GNUNET_assert (0);
-#endif
-}
-
-
/**
* Get the list of addresses that a server for the given service
* should bind to.
diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c
index eb48341b7..3a9013a5a 100644
--- a/src/transport/plugin_transport_udp.c
+++ b/src/transport/plugin_transport_udp.c
@@ -159,6 +159,11 @@ struct GNUNET_ATS_Session
struct GNUNET_PeerIdentity target;
/**
+ * Tokenizer for inbound messages.
+ */
+ struct GNUNET_MessageStreamTokenizer *mst;
+
+ /**
* Plugin this session belongs to.
*/
struct Plugin *plugin;
@@ -626,6 +631,11 @@ free_session (struct GNUNET_ATS_Session *s)
GNUNET_free (s->frag_ctx);
s->frag_ctx = NULL;
}
+ if (NULL != s->mst)
+ {
+ GNUNET_MST_destroy (s->mst);
+ s->mst = NULL;
+ }
GNUNET_free (s);
}
@@ -2499,18 +2509,16 @@ read_process_ack (struct Plugin *plugin,
* Message tokenizer has broken up an incomming message. Pass it on
* to the service.
*
- * @param cls the `struct Plugin *`
- * @param client the `struct GNUNET_ATS_Session *`
+ * @param cls the `struct GNUNET_ATS_Session *`
* @param hdr the actual message
* @return #GNUNET_OK (always)
*/
static int
process_inbound_tokenized_messages (void *cls,
- void *client,
const struct GNUNET_MessageHeader *hdr)
{
- struct Plugin *plugin = cls;
- struct GNUNET_ATS_Session *session = client;
+ struct GNUNET_ATS_Session *session = cls;
+ struct Plugin *plugin = session->plugin;
if (GNUNET_YES == session->in_destroy)
return GNUNET_OK;
@@ -2626,6 +2634,8 @@ udp_plugin_create_session (void *cls,
struct GNUNET_ATS_Session *s;
s = GNUNET_new (struct GNUNET_ATS_Session);
+ s->mst = GNUNET_MST_create (&process_inbound_tokenized_messages,
+ s);
s->plugin = plugin;
s->address = GNUNET_HELLO_address_copy (address);
s->target = address->peer;
@@ -2792,12 +2802,11 @@ process_udp_message (struct Plugin *plugin,
GNUNET_free (address);
s->rc++;
- GNUNET_SERVER_mst_receive (plugin->mst,
- s,
- (const char *) &msg[1],
- ntohs (msg->header.size) - sizeof(struct UDPMessage),
- GNUNET_YES,
- GNUNET_NO);
+ GNUNET_MST_from_buffer (s->mst,
+ (const char *) &msg[1],
+ ntohs (msg->header.size) - sizeof(struct UDPMessage),
+ GNUNET_YES,
+ GNUNET_NO);
s->rc--;
if ( (0 == s->rc) &&
(GNUNET_YES == s->in_destroy) )
@@ -3990,8 +3999,6 @@ libgnunet_plugin_transport_udp_init (void *cls)
p->sessions = GNUNET_CONTAINER_multipeermap_create (16,
GNUNET_NO);
p->defrag_ctxs = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
- p->mst = GNUNET_SERVER_mst_create (&process_inbound_tokenized_messages,
- p);
GNUNET_BANDWIDTH_tracker_init (&p->tracker,
NULL,
NULL,
@@ -4008,7 +4015,6 @@ libgnunet_plugin_transport_udp_init (void *cls)
_("Failed to create UDP network sockets\n"));
GNUNET_CONTAINER_multipeermap_destroy (p->sessions);
GNUNET_CONTAINER_heap_destroy (p->defrag_ctxs);
- GNUNET_SERVER_mst_destroy (p->mst);
if (NULL != p->nat)
GNUNET_NAT_unregister (p->nat);
GNUNET_free (p);
@@ -4120,11 +4126,6 @@ libgnunet_plugin_transport_udp_done (void *cls)
GNUNET_CONTAINER_heap_destroy (plugin->defrag_ctxs);
plugin->defrag_ctxs = NULL;
}
- if (NULL != plugin->mst)
- {
- GNUNET_SERVER_mst_destroy (plugin->mst);
- plugin->mst = NULL;
- }
while (NULL != (udpw = plugin->ipv4_queue_head))
{
dequeue (plugin,
diff --git a/src/transport/plugin_transport_udp.h b/src/transport/plugin_transport_udp.h
index 152b16099..48c7365c7 100644
--- a/src/transport/plugin_transport_udp.h
+++ b/src/transport/plugin_transport_udp.h
@@ -164,11 +164,6 @@ struct Plugin
struct GNUNET_SCHEDULER_Task *select_task_v6;
/**
- * Tokenizer for inbound messages.
- */
- struct GNUNET_SERVER_MessageStreamTokenizer *mst;
-
- /**
* Bandwidth tracker to limit global UDP traffic.
*/
struct GNUNET_BANDWIDTH_Tracker tracker;
@@ -192,7 +187,7 @@ struct Plugin
* Handle to NAT traversal support.
*/
struct GNUNET_NAT_STUN_Handle *stun;
-
+
/**
* The read socket for IPv4
*/
@@ -204,11 +199,6 @@ struct Plugin
struct GNUNET_NETWORK_Handle *sockv6;
/**
- * Tokenizer for inbound messages.
- */
- struct GNUNET_SERVER_MessageStreamTokenizer *broadcast_mst;
-
- /**
* Head of DLL of broadcast addresses
*/
struct BroadcastAddress *broadcast_tail;
diff --git a/src/transport/plugin_transport_udp_broadcasting.c b/src/transport/plugin_transport_udp_broadcasting.c
index a440830fd..c6ddbce9b 100644
--- a/src/transport/plugin_transport_udp_broadcasting.c
+++ b/src/transport/plugin_transport_udp_broadcasting.c
@@ -133,11 +133,10 @@ struct MstContext
*/
static int
broadcast_mst_cb (void *cls,
- void *client,
const struct GNUNET_MessageHeader *message)
{
- struct Plugin *plugin = cls;
- struct MstContext *mc = client;
+ struct MstContext *mc = cls;
+ struct Plugin *plugin = mc->plugin;
struct GNUNET_HELLO_Address *address;
const struct GNUNET_MessageHeader *hello;
const struct UDP_Beacon_Message *msg;
@@ -191,16 +190,20 @@ udp_broadcast_receive (struct Plugin *plugin,
size_t udp_addr_len,
enum GNUNET_ATS_Network_Type network_type)
{
+ struct GNUNET_MessageStreamTokenizer *broadcast_mst;
struct MstContext mc;
+ broadcast_mst = GNUNET_MST_create (&broadcast_mst_cb,
+ &mc);
+ mc.plugin = plugin;
mc.udp_addr = udp_addr;
mc.udp_addr_len = udp_addr_len;
mc.ats_address_network_type = network_type;
- GNUNET_SERVER_mst_receive (plugin->broadcast_mst,
- &mc,
- buf, size,
- GNUNET_NO,
- GNUNET_NO);
+ GNUNET_MST_from_buffer (broadcast_mst,
+ buf, size,
+ GNUNET_NO,
+ GNUNET_NO);
+ GNUNET_MST_destroy (broadcast_mst);
}
@@ -546,10 +549,6 @@ setup_broadcast (struct Plugin *plugin,
return;
}
- /* always create tokenizers */
- plugin->broadcast_mst =
- GNUNET_SERVER_mst_create (&broadcast_mst_cb, plugin);
-
if (GNUNET_YES != plugin->enable_broadcasting)
return; /* We do not send, just receive */
@@ -636,13 +635,6 @@ stop_broadcast (struct Plugin *plugin)
GNUNET_free (p);
}
}
-
- /* Destroy MSTs */
- if (NULL != plugin->broadcast_mst)
- {
- GNUNET_SERVER_mst_destroy (plugin->broadcast_mst);
- plugin->broadcast_mst = NULL;
- }
}
/* end of plugin_transport_udp_broadcasting.c */
diff --git a/src/transport/plugin_transport_wlan.c b/src/transport/plugin_transport_wlan.c
index 376065d24..3f5ada10b 100644
--- a/src/transport/plugin_transport_wlan.c
+++ b/src/transport/plugin_transport_wlan.c
@@ -38,6 +38,7 @@
#include "gnunet_fragmentation_lib.h"
#include "gnunet_constants.h"
+
#if BUILD_WLAN
/* begin case wlan */
#define PLUGIN_NAME "wlan"
@@ -48,6 +49,7 @@
#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_wlan_done
#define LOG(kind,...) GNUNET_log_from (kind, "transport-wlan",__VA_ARGS__)
+
/**
* time out of a mac endpoint
*/
@@ -92,6 +94,30 @@
#error need to build wlan or bluetooth
#endif
+
+
+/**
+ * Functions with this signature are called whenever a
+ * complete message is received by the tokenizer.
+ *
+ * Do not call #GNUNET_SERVER_mst_destroy from within
+ * the scope of this callback.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param message the actual message
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
+ */
+typedef int
+(*GNUNET_SERVER_MessageTokenizerCallback) (void *cls,
+ void *client,
+ const struct GNUNET_MessageHeader *message);
+
+
+/* Include legacy message stream tokenizer that was removed from util (for now) */
+#include "tcp_server_mst_legacy.c"
+
+
/**
* Max size of packet (that we give to the WLAN driver for transmission)
*/
@@ -1728,11 +1754,10 @@ send_hello_beacon (void *cls)
* Function used for to process the data from the suid process
*
* @param cls the plugin handle
- * @param client client that send the data (not used)
* @param hdr header of the GNUNET_MessageHeader
*/
static int
-handle_helper_message (void *cls, void *client,
+handle_helper_message (void *cls,
const struct GNUNET_MessageHeader *hdr)
{
struct Plugin *plugin = cls;
diff --git a/src/transport/tcp_connection_legacy.c b/src/transport/tcp_connection_legacy.c
new file mode 100644
index 000000000..f5253445d
--- /dev/null
+++ b/src/transport/tcp_connection_legacy.c
@@ -0,0 +1,1647 @@
+/*
+ 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 General Public License as published
+ by the Free Software Foundation; either version 3, 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file util/connection.c
+ * @brief TCP connection management
+ * @author Christian Grothoff
+ *
+ * This code is rather complex. Only modify it if you
+ * 1) Have a NEW testcase showing that the new code
+ * is needed and correct
+ * 2) All EXISTING testcases pass with the new code
+ * These rules should apply in general, but for this
+ * module they are VERY, VERY important.
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_resolver_service.h"
+
+
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-connection", syscall)
+
+
+/**
+ * Transmission handle. There can only be one for each connection.
+ */
+struct GNUNET_CONNECTION_TransmitHandle
+{
+
+ /**
+ * Function to call if the send buffer has notify_size
+ * bytes available.
+ */
+ GNUNET_CONNECTION_TransmitReadyNotify notify_ready;
+
+ /**
+ * Closure for notify_ready.
+ */
+ void *notify_ready_cls;
+
+ /**
+ * Our connection handle.
+ */
+ struct GNUNET_CONNECTION_Handle *connection;
+
+ /**
+ * Timeout for receiving (in absolute time).
+ */
+ struct GNUNET_TIME_Absolute transmit_timeout;
+
+ /**
+ * Task called on timeout.
+ */
+ struct GNUNET_SCHEDULER_Task * timeout_task;
+
+ /**
+ * At what number of bytes available in the
+ * write buffer should the notify method be called?
+ */
+ size_t notify_size;
+
+};
+
+
+/**
+ * During connect, we try multiple possible IP addresses
+ * to find out which one might work.
+ */
+struct AddressProbe
+{
+
+ /**
+ * This is a linked list.
+ */
+ struct AddressProbe *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct AddressProbe *prev;
+
+ /**
+ * The address; do not free (allocated at the end of this struct).
+ */
+ const struct sockaddr *addr;
+
+ /**
+ * Underlying OS's socket.
+ */
+ struct GNUNET_NETWORK_Handle *sock;
+
+ /**
+ * Connection for which we are probing.
+ */
+ struct GNUNET_CONNECTION_Handle *connection;
+
+ /**
+ * Lenth of addr.
+ */
+ socklen_t addrlen;
+
+ /**
+ * Task waiting for the connection to finish connecting.
+ */
+ struct GNUNET_SCHEDULER_Task * task;
+};
+
+
+/**
+ * @brief handle for a network connection
+ */
+struct GNUNET_CONNECTION_Handle
+{
+
+ /**
+ * Configuration to use.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Linked list of sockets we are currently trying out
+ * (during connect).
+ */
+ struct AddressProbe *ap_head;
+
+ /**
+ * Linked list of sockets we are currently trying out
+ * (during connect).
+ */
+ struct AddressProbe *ap_tail;
+
+ /**
+ * Network address of the other end-point, may be NULL.
+ */
+ struct sockaddr *addr;
+
+ /**
+ * Pointer to the hostname if connection was
+ * created using DNS lookup, otherwise NULL.
+ */
+ char *hostname;
+
+ /**
+ * Underlying OS's socket, set to NULL after fatal errors.
+ */
+ struct GNUNET_NETWORK_Handle *sock;
+
+ /**
+ * Function to call on data received, NULL if no receive is pending.
+ */
+ GNUNET_CONNECTION_Receiver receiver;
+
+ /**
+ * Closure for @e receiver.
+ */
+ void *receiver_cls;
+
+ /**
+ * Pointer to our write buffer.
+ */
+ char *write_buffer;
+
+ /**
+ * Current size of our @e write_buffer.
+ */
+ size_t write_buffer_size;
+
+ /**
+ * Current write-offset in @e write_buffer (where
+ * would we write next).
+ */
+ size_t write_buffer_off;
+
+ /**
+ * Current read-offset in @e write_buffer (how many
+ * bytes have already been sent).
+ */
+ size_t write_buffer_pos;
+
+ /**
+ * Length of @e addr.
+ */
+ socklen_t addrlen;
+
+ /**
+ * Read task that we may need to wait for.
+ */
+ struct GNUNET_SCHEDULER_Task *read_task;
+
+ /**
+ * Write task that we may need to wait for.
+ */
+ struct GNUNET_SCHEDULER_Task *write_task;
+
+ /**
+ * Handle to a pending DNS lookup request.
+ */
+ struct GNUNET_RESOLVER_RequestHandle *dns_active;
+
+ /**
+ * The handle we return for #GNUNET_CONNECTION_notify_transmit_ready().
+ */
+ struct GNUNET_CONNECTION_TransmitHandle nth;
+
+ /**
+ * Timeout for receiving (in absolute time).
+ */
+ struct GNUNET_TIME_Absolute receive_timeout;
+
+ /**
+ * Maximum number of bytes to read (for receiving).
+ */
+ size_t max;
+
+ /**
+ * Port to connect to.
+ */
+ uint16_t port;
+
+ /**
+ * When shutdown, do not ever actually close the socket, but
+ * free resources. Only should ever be set if using program
+ * termination as a signal (because only then will the leaked
+ * socket be freed!)
+ */
+ int8_t persist;
+
+ /**
+ * Usually 0. Set to 1 if this handle is in use, and should
+ * #GNUNET_CONNECTION_destroy() be called right now, the action needs
+ * to be deferred by setting it to -1.
+ */
+ int8_t destroy_later;
+
+ /**
+ * Handle to subsequent connection after proxy handshake completes,
+ */
+ struct GNUNET_CONNECTION_Handle *proxy_handshake;
+
+};
+
+
+/**
+ * Set the persist option on this connection handle. Indicates
+ * that the underlying socket or fd should never really be closed.
+ * Used for indicating process death.
+ *
+ * @param connection the connection to set persistent
+ */
+void
+GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *connection)
+{
+ connection->persist = GNUNET_YES;
+}
+
+
+/**
+ * Disable the "CORK" feature for communication with the given connection,
+ * forcing the OS to immediately flush the buffer on transmission
+ * instead of potentially buffering multiple messages. Essentially
+ * reduces the OS send buffers to zero.
+ * Used to make sure that the last messages sent through the connection
+ * reach the other side before the process is terminated.
+ *
+ * @param connection the connection to make flushing and blocking
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *connection)
+{
+ return GNUNET_NETWORK_socket_disable_corking (connection->sock);
+}
+
+
+/**
+ * Create a connection handle by boxing an existing OS socket. The OS
+ * socket should henceforth be no longer used directly.
+ * #GNUNET_connection_destroy() will close it.
+ *
+ * @param osSocket existing socket to box
+ * @return the boxed connection handle
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket)
+{
+ struct GNUNET_CONNECTION_Handle *connection;
+
+ connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
+ connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
+ connection->sock = osSocket;
+ return connection;
+}
+
+
+/**
+ * Create a connection handle by accepting on a listen socket. This
+ * function may block if the listen socket has no connection ready.
+ *
+ * @param access_cb function to use to check if access is allowed
+ * @param access_cb_cls closure for @a access_cb
+ * @param lsock listen socket
+ * @return the connection handle, NULL on error
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access_cb,
+ void *access_cb_cls,
+ struct GNUNET_NETWORK_Handle *lsock)
+{
+ struct GNUNET_CONNECTION_Handle *connection;
+ char addr[128];
+ socklen_t addrlen;
+ struct GNUNET_NETWORK_Handle *sock;
+ int aret;
+ struct sockaddr_in *v4;
+ struct sockaddr_in6 *v6;
+ struct sockaddr *sa;
+ void *uaddr;
+#ifdef SO_PEERCRED
+ struct ucred uc;
+ socklen_t olen;
+#endif
+ struct GNUNET_CONNECTION_Credentials *gcp;
+#if HAVE_GETPEEREID || defined(SO_PEERCRED) || HAVE_GETPEERUCRED
+ struct GNUNET_CONNECTION_Credentials gc;
+
+ gc.uid = 0;
+ gc.gid = 0;
+#endif
+
+ addrlen = sizeof (addr);
+ sock =
+ GNUNET_NETWORK_socket_accept (lsock,
+ (struct sockaddr *) &addr,
+ &addrlen);
+ if (NULL == sock)
+ {
+ if (EAGAIN != errno)
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "accept");
+ return NULL;
+ }
+ if ((addrlen > sizeof (addr)) || (addrlen < sizeof (sa_family_t)))
+ {
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+ return NULL;
+ }
+
+ sa = (struct sockaddr *) addr;
+ v6 = (struct sockaddr_in6 *) addr;
+ if ( (AF_INET6 == sa->sa_family) &&
+ (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr)) )
+ {
+ /* convert to V4 address */
+ v4 = GNUNET_new (struct sockaddr_in);
+ memset (v4, 0, sizeof (struct sockaddr_in));
+ v4->sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4->sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+ GNUNET_memcpy (&v4->sin_addr,
+ &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) -
+ sizeof (struct in_addr)],
+ sizeof (struct in_addr));
+ v4->sin_port = v6->sin6_port;
+ uaddr = v4;
+ addrlen = sizeof (struct sockaddr_in);
+ }
+ else
+ {
+ uaddr = GNUNET_malloc (addrlen);
+ GNUNET_memcpy (uaddr, addr, addrlen);
+ }
+ gcp = NULL;
+ if (AF_UNIX == sa->sa_family)
+ {
+#if HAVE_GETPEEREID
+ /* most BSDs */
+ if (0 == getpeereid (GNUNET_NETWORK_get_fd (sock),
+ &gc.uid,
+ &gc.gid))
+ gcp = &gc;
+#else
+#ifdef SO_PEERCRED
+ /* largely traditional GNU/Linux */
+ olen = sizeof (uc);
+ if ( (0 ==
+ getsockopt (GNUNET_NETWORK_get_fd (sock),
+ SOL_SOCKET,
+ SO_PEERCRED,
+ &uc,
+ &olen)) &&
+ (olen == sizeof (uc)) )
+ {
+ gc.uid = uc.uid;
+ gc.gid = uc.gid;
+ gcp = &gc;
+ }
+#else
+#if HAVE_GETPEERUCRED
+ /* this is for Solaris 10 */
+ ucred_t *uc;
+
+ uc = NULL;
+ if (0 == getpeerucred (GNUNET_NETWORK_get_fd (sock), &uc))
+ {
+ gc.uid = ucred_geteuid (uc);
+ gc.gid = ucred_getegid (uc);
+ gcp = &gc;
+ }
+ ucred_free (uc);
+#endif
+#endif
+#endif
+ }
+
+ if ( (NULL != access_cb) &&
+ (GNUNET_YES != (aret = access_cb (access_cb_cls,
+ gcp,
+ uaddr,
+ addrlen))) )
+ {
+ if (GNUNET_NO == aret)
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Access denied to `%s'\n"),
+ GNUNET_a2s (uaddr,
+ addrlen));
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_shutdown (sock,
+ SHUT_RDWR));
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+ GNUNET_free (uaddr);
+ return NULL;
+ }
+ connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
+ connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
+ connection->addr = uaddr;
+ connection->addrlen = addrlen;
+ connection->sock = sock;
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Accepting connection from `%s': %p\n"),
+ GNUNET_a2s (uaddr,
+ addrlen),
+ connection);
+ return connection;
+}
+
+
+/**
+ * Obtain the network address of the other party.
+ *
+ * @param connection 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_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *connection,
+ void **addr,
+ size_t *addrlen)
+{
+ if ((NULL == connection->addr) || (0 == connection->addrlen))
+ return GNUNET_NO;
+ *addr = GNUNET_malloc (connection->addrlen);
+ GNUNET_memcpy (*addr, connection->addr, connection->addrlen);
+ *addrlen = connection->addrlen;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Tell the receiver callback that we had an IO error.
+ *
+ * @param connection connection to signal error
+ * @param errcode error code to send
+ */
+static void
+signal_receive_error (struct GNUNET_CONNECTION_Handle *connection,
+ int errcode)
+{
+ GNUNET_CONNECTION_Receiver receiver;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receive encounters error (%s), connection closed (%p)\n",
+ STRERROR (errcode),
+ connection);
+ GNUNET_assert (NULL != (receiver = connection->receiver));
+ connection->receiver = NULL;
+ receiver (connection->receiver_cls,
+ NULL,
+ 0,
+ connection->addr,
+ connection->addrlen,
+ errcode);
+}
+
+
+/**
+ * Tell the receiver callback that a timeout was reached.
+ *
+ * @param connection connection to signal for
+ */
+static void
+signal_receive_timeout (struct GNUNET_CONNECTION_Handle *connection)
+{
+ GNUNET_CONNECTION_Receiver receiver;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Connection signals timeout to receiver (%p)!\n",
+ connection);
+ GNUNET_assert (NULL != (receiver = connection->receiver));
+ connection->receiver = NULL;
+ receiver (connection->receiver_cls, NULL, 0, NULL, 0, 0);
+}
+
+
+/**
+ * We failed to transmit data to the service, signal the error.
+ *
+ * @param connection handle that had trouble
+ * @param ecode error code (errno)
+ */
+static void
+signal_transmit_error (struct GNUNET_CONNECTION_Handle *connection,
+ int ecode)
+{
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmission encounterd error (%s), connection closed (%p)\n",
+ STRERROR (ecode),
+ connection);
+ if (NULL != connection->sock)
+ {
+ (void) GNUNET_NETWORK_socket_shutdown (connection->sock,
+ SHUT_RDWR);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_close (connection->sock));
+ connection->sock = NULL;
+ GNUNET_assert (NULL == connection->write_task);
+ }
+ if (NULL != connection->read_task)
+ {
+ /* send errors trigger read errors... */
+ GNUNET_SCHEDULER_cancel (connection->read_task);
+ connection->read_task = NULL;
+ signal_receive_timeout (connection);
+ return;
+ }
+ if (NULL == connection->nth.notify_ready)
+ return; /* nobody to tell about it */
+ notify = connection->nth.notify_ready;
+ connection->nth.notify_ready = NULL;
+ notify (connection->nth.notify_ready_cls,
+ 0,
+ NULL);
+}
+
+
+/**
+ * We've failed for good to establish a connection (timeout or
+ * no more addresses to try).
+ *
+ * @param connection the connection we tried to establish
+ */
+static void
+connect_fail_continuation (struct GNUNET_CONNECTION_Handle *connection)
+{
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Failed to establish TCP connection to `%s:%u', no further addresses to try.\n",
+ connection->hostname,
+ connection->port);
+ GNUNET_break (NULL == connection->ap_head);
+ GNUNET_break (NULL == connection->ap_tail);
+ GNUNET_break (GNUNET_NO == connection->dns_active);
+ GNUNET_break (NULL == connection->sock);
+ GNUNET_assert (NULL == connection->write_task);
+ GNUNET_assert (NULL == connection->proxy_handshake);
+
+ /* signal errors for jobs that used to wait on the connection */
+ connection->destroy_later = 1;
+ if (NULL != connection->receiver)
+ signal_receive_error (connection,
+ ECONNREFUSED);
+ if (NULL != connection->nth.notify_ready)
+ {
+ GNUNET_assert (NULL != connection->nth.timeout_task);
+ GNUNET_SCHEDULER_cancel (connection->nth.timeout_task);
+ connection->nth.timeout_task = NULL;
+ signal_transmit_error (connection,
+ ECONNREFUSED);
+ }
+ if (-1 == connection->destroy_later)
+ {
+ /* do it now */
+ connection->destroy_later = 0;
+ GNUNET_CONNECTION_destroy (connection);
+ return;
+ }
+ connection->destroy_later = 0;
+}
+
+
+/**
+ * We are ready to transmit (or got a timeout).
+ *
+ * @param cls our connection handle
+ */
+static void
+transmit_ready (void *cls);
+
+
+/**
+ * This function is called once we either timeout or have data ready
+ * to read.
+ *
+ * @param cls connection to read from
+ */
+static void
+receive_ready (void *cls);
+
+
+/**
+ * We've succeeded in establishing a connection.
+ *
+ * @param connection the connection we tried to establish
+ */
+static void
+connect_success_continuation (struct GNUNET_CONNECTION_Handle *connection)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Connection to `%s' succeeded! (%p)\n",
+ GNUNET_a2s (connection->addr,
+ connection->addrlen),
+ connection);
+ /* trigger jobs that waited for the connection */
+ if (NULL != connection->receiver)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Connection succeeded, starting with receiving data (%p)\n",
+ connection);
+ GNUNET_assert (NULL == connection->read_task);
+ connection->read_task =
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining
+ (connection->receive_timeout),
+ connection->sock,
+ &receive_ready, connection);
+ }
+ if (NULL != connection->nth.notify_ready)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Connection succeeded, starting with sending data (%p)\n",
+ connection);
+ GNUNET_assert (connection->nth.timeout_task != NULL);
+ GNUNET_SCHEDULER_cancel (connection->nth.timeout_task);
+ connection->nth.timeout_task = NULL;
+ GNUNET_assert (connection->write_task == NULL);
+ connection->write_task =
+ GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
+ (connection->nth.transmit_timeout), connection->sock,
+ &transmit_ready, connection);
+ }
+}
+
+
+/**
+ * Scheduler let us know that we're either ready to write on the
+ * socket OR connect timed out. Do the right thing.
+ *
+ * @param cls the `struct AddressProbe *` with the address that we are probing
+ */
+static void
+connect_probe_continuation (void *cls)
+{
+ struct AddressProbe *ap = cls;
+ struct GNUNET_CONNECTION_Handle *connection = ap->connection;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
+ struct AddressProbe *pos;
+ int error;
+ socklen_t len;
+
+ GNUNET_assert (NULL != ap->sock);
+ GNUNET_CONTAINER_DLL_remove (connection->ap_head,
+ connection->ap_tail,
+ ap);
+ len = sizeof (error);
+ errno = 0;
+ error = 0;
+ tc = GNUNET_SCHEDULER_get_task_context ();
+ if ( (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
+ (GNUNET_OK !=
+ GNUNET_NETWORK_socket_getsockopt (ap->sock,
+ SOL_SOCKET,
+ SO_ERROR,
+ &error,
+ &len)) ||
+ (0 != error) )
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_close (ap->sock));
+ GNUNET_free (ap);
+ if ( (NULL == connection->ap_head) &&
+ (GNUNET_NO == connection->dns_active) &&
+ (NULL == connection->proxy_handshake) )
+ connect_fail_continuation (connection);
+ return;
+ }
+ GNUNET_assert (NULL == connection->sock);
+ connection->sock = ap->sock;
+ GNUNET_assert (NULL == connection->addr);
+ connection->addr = GNUNET_malloc (ap->addrlen);
+ GNUNET_memcpy (connection->addr, ap->addr, ap->addrlen);
+ connection->addrlen = ap->addrlen;
+ GNUNET_free (ap);
+ /* cancel all other attempts */
+ while (NULL != (pos = connection->ap_head))
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock));
+ GNUNET_SCHEDULER_cancel (pos->task);
+ GNUNET_CONTAINER_DLL_remove (connection->ap_head,
+ connection->ap_tail,
+ pos);
+ GNUNET_free (pos);
+ }
+ connect_success_continuation (connection);
+}
+
+
+/**
+ * Try to establish a connection given the specified address.
+ * This function is called by the resolver once we have a DNS reply.
+ *
+ * @param cls our `struct GNUNET_CONNECTION_Handle *`
+ * @param addr address to try, NULL for "last call"
+ * @param addrlen length of @a addr
+ */
+static void
+try_connect_using_address (void *cls,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct GNUNET_CONNECTION_Handle *connection = cls;
+ struct AddressProbe *ap;
+ struct GNUNET_TIME_Relative delay;
+
+ if (NULL == addr)
+ {
+ connection->dns_active = NULL;
+ if ((NULL == connection->ap_head) &&
+ (NULL == connection->sock) &&
+ (NULL == connection->proxy_handshake))
+ connect_fail_continuation (connection);
+ return;
+ }
+ if (NULL != connection->sock)
+ return; /* already connected */
+ GNUNET_assert (NULL == connection->addr);
+ /* try to connect */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to connect using address `%s:%u/%s:%u'\n",
+ connection->hostname,
+ connection->port,
+ GNUNET_a2s (addr, addrlen),
+ connection->port);
+ ap = GNUNET_malloc (sizeof (struct AddressProbe) + addrlen);
+ ap->addr = (const struct sockaddr *) &ap[1];
+ GNUNET_memcpy (&ap[1], addr, addrlen);
+ ap->addrlen = addrlen;
+ ap->connection = connection;
+
+ switch (ap->addr->sa_family)
+ {
+ case AF_INET:
+ ((struct sockaddr_in *) ap->addr)->sin_port = htons (connection->port);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (connection->port);
+ break;
+ default:
+ GNUNET_break (0);
+ GNUNET_free (ap);
+ return; /* not supported by us */
+ }
+ ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family,
+ SOCK_STREAM, 0);
+ if (NULL == ap->sock)
+ {
+ GNUNET_free (ap);
+ return; /* not supported by OS */
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Trying to connect to `%s' (%p)\n",
+ GNUNET_a2s (ap->addr, ap->addrlen),
+ connection);
+ if ((GNUNET_OK !=
+ GNUNET_NETWORK_socket_connect (ap->sock,
+ ap->addr,
+ ap->addrlen)) &&
+ (EINPROGRESS != errno))
+ {
+ /* maybe refused / unsupported address, try next */
+ LOG_STRERROR (GNUNET_ERROR_TYPE_INFO, "connect");
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock));
+ GNUNET_free (ap);
+ return;
+ }
+ GNUNET_CONTAINER_DLL_insert (connection->ap_head, connection->ap_tail, ap);
+ delay = GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT;
+ if (NULL != connection->nth.notify_ready)
+ delay = GNUNET_TIME_relative_min (delay,
+ GNUNET_TIME_absolute_get_remaining (connection->nth.transmit_timeout));
+ if (NULL != connection->receiver)
+ delay = GNUNET_TIME_relative_min (delay,
+ GNUNET_TIME_absolute_get_remaining (connection->receive_timeout));
+ ap->task = GNUNET_SCHEDULER_add_write_net (delay,
+ ap->sock,
+ &connect_probe_continuation,
+ ap);
+}
+
+
+/**
+ * Create a connection handle by (asynchronously) connecting to a host.
+ * This function returns immediately, even if the connection has not
+ * yet been established. This function only creates TCP connections.
+ *
+ * @param cfg configuration to use
+ * @param hostname name of the host to connect to
+ * @param port port to connect to
+ * @return the connection handle
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *hostname,
+ uint16_t port)
+{
+ struct GNUNET_CONNECTION_Handle *connection;
+
+ GNUNET_assert (0 < strlen (hostname)); /* sanity check */
+ connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
+ connection->cfg = cfg;
+ connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
+ connection->port = port;
+ connection->hostname = GNUNET_strdup (hostname);
+ connection->dns_active =
+ GNUNET_RESOLVER_ip_get (connection->hostname,
+ AF_UNSPEC,
+ GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT,
+ &try_connect_using_address,
+ connection);
+ return connection;
+}
+
+
+/**
+ * Create a connection handle by connecting to a UNIX domain service.
+ * This function returns immediately, even if the connection has not
+ * yet been established. This function only creates UNIX connections.
+ *
+ * @param cfg configuration to use
+ * @param unixpath path to connect to
+ * @return the connection handle, NULL on systems without UNIX support
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *unixpath)
+{
+#ifdef AF_UNIX
+ struct GNUNET_CONNECTION_Handle *connection;
+ struct sockaddr_un *un;
+
+ GNUNET_assert (0 < strlen (unixpath)); /* sanity check */
+ un = GNUNET_new (struct sockaddr_un);
+ un->sun_family = AF_UNIX;
+ strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1);
+#ifdef LINUX
+ {
+ int abstract;
+
+ abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ "TESTING",
+ "USE_ABSTRACT_SOCKETS");
+ if (GNUNET_YES == abstract)
+ un->sun_path[0] = '\0';
+ }
+#endif
+#if HAVE_SOCKADDR_UN_SUN_LEN
+ un->sun_len = (u_char) sizeof (struct sockaddr_un);
+#endif
+ connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
+ connection->cfg = cfg;
+ connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
+ connection->port = 0;
+ connection->hostname = NULL;
+ connection->addr = (struct sockaddr *) un;
+ connection->addrlen = sizeof (struct sockaddr_un);
+ connection->sock = GNUNET_NETWORK_socket_create (AF_UNIX,
+ SOCK_STREAM,
+ 0);
+ if (NULL == connection->sock)
+ {
+ GNUNET_free (connection->addr);
+ GNUNET_free (connection->write_buffer);
+ GNUNET_free (connection);
+ return NULL;
+ }
+ if ( (GNUNET_OK !=
+ GNUNET_NETWORK_socket_connect (connection->sock,
+ connection->addr,
+ connection->addrlen)) &&
+ (EINPROGRESS != errno) )
+ {
+ /* Just return; we expect everything to work eventually so don't fail HARD */
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_close (connection->sock));
+ connection->sock = NULL;
+ return connection;
+ }
+ connect_success_continuation (connection);
+ return connection;
+#else
+ return NULL;
+#endif
+}
+
+
+/**
+ * Create a connection handle by (asynchronously) connecting to a host.
+ * This function returns immediately, even if the connection has not
+ * yet been established. This function only creates TCP connections.
+ *
+ * @param s socket to connect
+ * @param serv_addr server address
+ * @param addrlen length of @a serv_addr
+ * @return the connection handle
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_connect_socket (struct GNUNET_NETWORK_Handle *s,
+ const struct sockaddr *serv_addr,
+ socklen_t addrlen)
+{
+ struct GNUNET_CONNECTION_Handle *connection;
+
+ if ( (GNUNET_OK !=
+ GNUNET_NETWORK_socket_connect (s, serv_addr, addrlen)) &&
+ (EINPROGRESS != errno) )
+ {
+ /* maybe refused / unsupported address, try next */
+ LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG,
+ "connect");
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Attempt to connect to `%s' failed\n",
+ GNUNET_a2s (serv_addr,
+ addrlen));
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s));
+ return NULL;
+ }
+ connection = GNUNET_CONNECTION_create_from_existing (s);
+ connection->addr = GNUNET_malloc (addrlen);
+ GNUNET_memcpy (connection->addr, serv_addr, addrlen);
+ connection->addrlen = addrlen;
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Trying to connect to `%s' (%p)\n",
+ GNUNET_a2s (serv_addr, addrlen),
+ connection);
+ return connection;
+}
+
+
+/**
+ * Create a connection handle by creating a socket and
+ * (asynchronously) connecting to a host. This function returns
+ * immediately, even if the connection has not yet been established.
+ * This function only creates TCP connections.
+ *
+ * @param af_family address family to use
+ * @param serv_addr server address
+ * @param addrlen length of @a serv_addr
+ * @return the connection handle
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_sockaddr (int af_family,
+ const struct sockaddr *serv_addr,
+ socklen_t addrlen)
+{
+ struct GNUNET_NETWORK_Handle *s;
+
+ s = GNUNET_NETWORK_socket_create (af_family, SOCK_STREAM, 0);
+ if (NULL == s)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
+ "socket");
+ return NULL;
+ }
+ return GNUNET_CONNECTION_connect_socket (s,
+ serv_addr,
+ addrlen);
+}
+
+
+/**
+ * Check if connection is valid (no fatal errors have happened so far).
+ * Note that a connection that is still trying to connect is considered
+ * valid.
+ *
+ * @param connection connection to check
+ * @return #GNUNET_YES if valid, #GNUNET_NO otherwise
+ */
+int
+GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *connection)
+{
+ if ((NULL != connection->ap_head) ||
+ (NULL != connection->dns_active) ||
+ (NULL != connection->proxy_handshake))
+ return GNUNET_YES; /* still trying to connect */
+ if ( (0 != connection->destroy_later) ||
+ (NULL == connection->sock) )
+ return GNUNET_NO;
+ return GNUNET_YES;
+}
+
+
+/**
+ * Close the connection and free associated resources. There must
+ * not be any pending requests for reading or writing to the
+ * connection at this time.
+ *
+ * @param connection connection to destroy
+ */
+void
+GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection)
+{
+ struct AddressProbe *pos;
+
+ if (0 != connection->destroy_later)
+ {
+ connection->destroy_later = -1;
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Shutting down connection (%p)\n",
+ connection);
+ GNUNET_assert (NULL == connection->nth.notify_ready);
+ GNUNET_assert (NULL == connection->receiver);
+ if (NULL != connection->write_task)
+ {
+ GNUNET_SCHEDULER_cancel (connection->write_task);
+ connection->write_task = NULL;
+ connection->write_buffer_off = 0;
+ }
+ if (NULL != connection->read_task)
+ {
+ GNUNET_SCHEDULER_cancel (connection->read_task);
+ connection->read_task = NULL;
+ }
+ if (NULL != connection->nth.timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (connection->nth.timeout_task);
+ connection->nth.timeout_task = NULL;
+ }
+ connection->nth.notify_ready = NULL;
+ if (NULL != connection->dns_active)
+ {
+ GNUNET_RESOLVER_request_cancel (connection->dns_active);
+ connection->dns_active = NULL;
+ }
+ if (NULL != connection->proxy_handshake)
+ {
+ /* GNUNET_CONNECTION_destroy (connection->proxy_handshake); */
+ connection->proxy_handshake->destroy_later = -1;
+ connection->proxy_handshake = NULL; /* Not leaked ??? */
+ }
+ while (NULL != (pos = connection->ap_head))
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock));
+ GNUNET_SCHEDULER_cancel (pos->task);
+ GNUNET_CONTAINER_DLL_remove (connection->ap_head,
+ connection->ap_tail,
+ pos);
+ GNUNET_free (pos);
+ }
+ if ( (NULL != connection->sock) &&
+ (GNUNET_YES != connection->persist) )
+ {
+ if ((GNUNET_OK !=
+ GNUNET_NETWORK_socket_shutdown (connection->sock,
+ SHUT_RDWR)) &&
+ (ENOTCONN != errno) &&
+ (ECONNRESET != errno) )
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
+ "shutdown");
+ }
+ if (NULL != connection->sock)
+ {
+ if (GNUNET_YES != connection->persist)
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_close (connection->sock));
+ }
+ else
+ {
+ GNUNET_NETWORK_socket_free_memory_only_ (connection->sock); /* at least no memory leak (we deliberately
+ * leak the socket in this special case) ... */
+ }
+ }
+ GNUNET_free_non_null (connection->addr);
+ GNUNET_free_non_null (connection->hostname);
+ GNUNET_free (connection->write_buffer);
+ GNUNET_free (connection);
+}
+
+
+/**
+ * This function is called once we either timeout
+ * or have data ready to read.
+ *
+ * @param cls connection to read from
+ */
+static void
+receive_ready (void *cls)
+{
+ struct GNUNET_CONNECTION_Handle *connection = cls;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
+ char buffer[connection->max];
+ ssize_t ret;
+ GNUNET_CONNECTION_Receiver receiver;
+
+ connection->read_task = NULL;
+ tc = GNUNET_SCHEDULER_get_task_context ();
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receive from `%s' encounters error: timeout (%s, %p)\n",
+ GNUNET_a2s (connection->addr,
+ connection->addrlen),
+ GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (connection->receive_timeout),
+ GNUNET_YES),
+ connection);
+ signal_receive_timeout (connection);
+ return;
+ }
+ if (NULL == connection->sock)
+ {
+ /* connect failed for good */
+ signal_receive_error (connection, ECONNREFUSED);
+ return;
+ }
+ GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+ connection->sock));
+RETRY:
+ ret = GNUNET_NETWORK_socket_recv (connection->sock,
+ buffer,
+ connection->max);
+ if (-1 == ret)
+ {
+ if (EINTR == errno)
+ goto RETRY;
+ signal_receive_error (connection, errno);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "receive_ready read %u/%u bytes from `%s' (%p)!\n",
+ (unsigned int) ret,
+ connection->max,
+ GNUNET_a2s (connection->addr,
+ connection->addrlen),
+ connection);
+ GNUNET_assert (NULL != (receiver = connection->receiver));
+ connection->receiver = NULL;
+ receiver (connection->receiver_cls,
+ buffer,
+ ret,
+ connection->addr,
+ connection->addrlen,
+ 0);
+}
+
+
+/**
+ * Receive data from the given connection. Note that this function
+ * will call @a receiver asynchronously using the scheduler. It will
+ * "immediately" return. Note that there MUST only be one active
+ * receive call per connection at any given point in time (so do not
+ * call receive again until the receiver callback has been invoked).
+ *
+ * @param connection connection handle
+ * @param max maximum number of bytes to read
+ * @param timeout maximum amount of time to wait
+ * @param receiver function to call with received data
+ * @param receiver_cls closure for @a receiver
+ */
+void
+GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *connection,
+ size_t max,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_CONNECTION_Receiver receiver,
+ void *receiver_cls)
+{
+ GNUNET_assert ((NULL == connection->read_task) &&
+ (NULL == connection->receiver));
+ GNUNET_assert (NULL != receiver);
+ connection->receiver = receiver;
+ connection->receiver_cls = receiver_cls;
+ connection->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ connection->max = max;
+ if (NULL != connection->sock)
+ {
+ connection->read_task =
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining
+ (connection->receive_timeout),
+ connection->sock,
+ &receive_ready,
+ connection);
+ return;
+ }
+ if ((NULL == connection->dns_active) &&
+ (NULL == connection->ap_head) &&
+ (NULL == connection->proxy_handshake))
+ {
+ connection->receiver = NULL;
+ receiver (receiver_cls,
+ NULL, 0,
+ NULL, 0,
+ ETIMEDOUT);
+ return;
+ }
+}
+
+
+/**
+ * Cancel receive job on the given connection. Note that the
+ * receiver callback must not have been called yet in order
+ * for the cancellation to be valid.
+ *
+ * @param connection connection handle
+ * @return closure of the original receiver callback closure
+ */
+void *
+GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *connection)
+{
+ if (NULL != connection->read_task)
+ {
+ GNUNET_assert (connection ==
+ GNUNET_SCHEDULER_cancel (connection->read_task));
+ connection->read_task = NULL;
+ }
+ connection->receiver = NULL;
+ return connection->receiver_cls;
+}
+
+
+/**
+ * Try to call the transmit notify method (check if we do
+ * have enough space available first)!
+ *
+ * @param connection connection for which we should do this processing
+ * @return #GNUNET_YES if we were able to call notify
+ */
+static int
+process_notify (struct GNUNET_CONNECTION_Handle *connection)
+{
+ size_t used;
+ size_t avail;
+ size_t size;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "process_notify is running\n");
+ GNUNET_assert (NULL == connection->write_task);
+ if (NULL == (notify = connection->nth.notify_ready))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "No one to notify\n");
+ return GNUNET_NO;
+ }
+ used = connection->write_buffer_off - connection->write_buffer_pos;
+ avail = connection->write_buffer_size - used;
+ size = connection->nth.notify_size;
+ if (size > avail)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Not enough buffer\n");
+ return GNUNET_NO;
+ }
+ connection->nth.notify_ready = NULL;
+ if (connection->write_buffer_size - connection->write_buffer_off < size)
+ {
+ /* need to compact */
+ memmove (connection->write_buffer,
+ &connection->write_buffer[connection->write_buffer_pos],
+ used);
+ connection->write_buffer_off -= connection->write_buffer_pos;
+ connection->write_buffer_pos = 0;
+ }
+ avail = connection->write_buffer_size - connection->write_buffer_off;
+ GNUNET_assert (avail >= size);
+ size =
+ notify (connection->nth.notify_ready_cls, avail,
+ &connection->write_buffer[connection->write_buffer_off]);
+ GNUNET_assert (size <= avail);
+ if (0 != size)
+ connection->write_buffer_off += size;
+ return GNUNET_YES;
+}
+
+
+/**
+ * Task invoked by the scheduler when a call to transmit
+ * is timing out (we never got enough buffer space to call
+ * the callback function before the specified timeout
+ * expired).
+ *
+ * This task notifies the client about the timeout.
+ *
+ * @param cls the `struct GNUNET_CONNECTION_Handle`
+ */
+static void
+transmit_timeout (void *cls)
+{
+ struct GNUNET_CONNECTION_Handle *connection = cls;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ connection->nth.timeout_task = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmit to `%s:%u/%s' fails, time out reached (%p).\n",
+ connection->hostname,
+ connection->port,
+ GNUNET_a2s (connection->addr,
+ connection->addrlen),
+ connection);
+ notify = connection->nth.notify_ready;
+ GNUNET_assert (NULL != notify);
+ connection->nth.notify_ready = NULL;
+ notify (connection->nth.notify_ready_cls,
+ 0,
+ NULL);
+}
+
+
+/**
+ * Task invoked by the scheduler when we failed to connect
+ * at the time of being asked to transmit.
+ *
+ * This task notifies the client about the error.
+ *
+ * @param cls the `struct GNUNET_CONNECTION_Handle`
+ */
+static void
+connect_error (void *cls)
+{
+ struct GNUNET_CONNECTION_Handle *connection = cls;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmission request of size %u fails (%s/%u), connection failed (%p).\n",
+ connection->nth.notify_size,
+ connection->hostname,
+ connection->port,
+ connection);
+ connection->write_task = NULL;
+ notify = connection->nth.notify_ready;
+ connection->nth.notify_ready = NULL;
+ notify (connection->nth.notify_ready_cls,
+ 0,
+ NULL);
+}
+
+
+/**
+ * We are ready to transmit (or got a timeout).
+ *
+ * @param cls our connection handle
+ */
+static void
+transmit_ready (void *cls)
+{
+ struct GNUNET_CONNECTION_Handle *connection = cls;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
+ ssize_t ret;
+ size_t have;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "transmit_ready running (%p).\n",
+ connection);
+ GNUNET_assert (NULL != connection->write_task);
+ connection->write_task = NULL;
+ GNUNET_assert (NULL == connection->nth.timeout_task);
+ tc = GNUNET_SCHEDULER_get_task_context ();
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmit to `%s' fails, time out reached (%p).\n",
+ GNUNET_a2s (connection->addr,
+ connection->addrlen),
+ connection);
+ notify = connection->nth.notify_ready;
+ GNUNET_assert (NULL != notify);
+ connection->nth.notify_ready = NULL;
+ notify (connection->nth.notify_ready_cls, 0, NULL);
+ return;
+ }
+ GNUNET_assert (NULL != connection->sock);
+ if (NULL == tc->write_ready)
+ {
+ /* special circumstances (in particular, PREREQ_DONE after
+ * connect): not yet ready to write, but no "fatal" error either.
+ * Hence retry. */
+ goto SCHEDULE_WRITE;
+ }
+ if (! GNUNET_NETWORK_fdset_isset (tc->write_ready,
+ connection->sock))
+ {
+ GNUNET_assert (NULL == connection->write_task);
+ /* special circumstances (in particular, shutdown): not yet ready
+ * to write, but no "fatal" error either. Hence retry. */
+ goto SCHEDULE_WRITE;
+ }
+ GNUNET_assert (connection->write_buffer_off >= connection->write_buffer_pos);
+ if ((NULL != connection->nth.notify_ready) &&
+ (connection->write_buffer_size < connection->nth.notify_size))
+ {
+ connection->write_buffer =
+ GNUNET_realloc (connection->write_buffer, connection->nth.notify_size);
+ connection->write_buffer_size = connection->nth.notify_size;
+ }
+ process_notify (connection);
+ have = connection->write_buffer_off - connection->write_buffer_pos;
+ if (0 == have)
+ {
+ /* no data ready for writing, terminate write loop */
+ return;
+ }
+ GNUNET_assert (have <= connection->write_buffer_size);
+ GNUNET_assert (have + connection->write_buffer_pos <= connection->write_buffer_size);
+ GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size);
+RETRY:
+ ret =
+ GNUNET_NETWORK_socket_send (connection->sock,
+ &connection->write_buffer[connection->write_buffer_pos],
+ have);
+ if (-1 == ret)
+ {
+ if (EINTR == errno)
+ goto RETRY;
+ if (NULL != connection->write_task)
+ {
+ GNUNET_SCHEDULER_cancel (connection->write_task);
+ connection->write_task = NULL;
+ }
+ signal_transmit_error (connection, errno);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Connection transmitted %u/%u bytes to `%s' (%p)\n",
+ (unsigned int) ret,
+ have,
+ GNUNET_a2s (connection->addr,
+ connection->addrlen),
+ connection);
+ connection->write_buffer_pos += ret;
+ if (connection->write_buffer_pos == connection->write_buffer_off)
+ {
+ /* transmitted all pending data */
+ connection->write_buffer_pos = 0;
+ connection->write_buffer_off = 0;
+ }
+ if ( (0 == connection->write_buffer_off) &&
+ (NULL == connection->nth.notify_ready) )
+ return; /* all data sent! */
+ /* not done writing, schedule more */
+SCHEDULE_WRITE:
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Re-scheduling transmit_ready (more to do) (%p).\n",
+ connection);
+ have = connection->write_buffer_off - connection->write_buffer_pos;
+ GNUNET_assert ( (NULL != connection->nth.notify_ready) ||
+ (have > 0) );
+ if (NULL == connection->write_task)
+ connection->write_task =
+ GNUNET_SCHEDULER_add_write_net ((connection->nth.notify_ready ==
+ NULL) ? GNUNET_TIME_UNIT_FOREVER_REL :
+ GNUNET_TIME_absolute_get_remaining
+ (connection->nth.transmit_timeout),
+ connection->sock,
+ &transmit_ready, connection);
+}
+
+
+/**
+ * Ask the connection to call us once the specified number of bytes
+ * are free in the transmission buffer. Will never call the @a notify
+ * callback in this task, but always first go into the scheduler.
+ *
+ * @param connection connection
+ * @param size number of bytes to send
+ * @param timeout after how long should we give up (and call
+ * @a notify with buf NULL and size 0)?
+ * @param notify function to call
+ * @param notify_cls closure for @a notify
+ * @return non-NULL if the notify callback was queued,
+ * NULL if we are already going to notify someone else (busy)
+ */
+struct GNUNET_CONNECTION_TransmitHandle *
+GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle *connection,
+ size_t size,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_CONNECTION_TransmitReadyNotify notify,
+ void *notify_cls)
+{
+ if (NULL != connection->nth.notify_ready)
+ {
+ GNUNET_assert (0);
+ return NULL;
+ }
+ GNUNET_assert (NULL != notify);
+ GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
+ GNUNET_assert (connection->write_buffer_off <= connection->write_buffer_size);
+ GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size);
+ GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_off);
+ connection->nth.notify_ready = notify;
+ connection->nth.notify_ready_cls = notify_cls;
+ connection->nth.connection = connection;
+ connection->nth.notify_size = size;
+ connection->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ GNUNET_assert (NULL == connection->nth.timeout_task);
+ if ((NULL == connection->sock) &&
+ (NULL == connection->ap_head) &&
+ (NULL == connection->dns_active) &&
+ (NULL == connection->proxy_handshake))
+ {
+ if (NULL != connection->write_task)
+ GNUNET_SCHEDULER_cancel (connection->write_task);
+ connection->write_task = GNUNET_SCHEDULER_add_now (&connect_error,
+ connection);
+ return &connection->nth;
+ }
+ if (NULL != connection->write_task)
+ return &connection->nth; /* previous transmission still in progress */
+ if (NULL != connection->sock)
+ {
+ /* connected, try to transmit now */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Scheduling transmission (%p).\n",
+ connection);
+ connection->write_task =
+ GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
+ (connection->nth.transmit_timeout),
+ connection->sock,
+ &transmit_ready, connection);
+ return &connection->nth;
+ }
+ /* not yet connected, wait for connection */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Need to wait to schedule transmission for connection, adding timeout task (%p).\n",
+ connection);
+ connection->nth.timeout_task =
+ GNUNET_SCHEDULER_add_delayed (timeout,
+ &transmit_timeout,
+ connection);
+ return &connection->nth;
+}
+
+
+/**
+ * Cancel the specified transmission-ready notification.
+ *
+ * @param th notification to cancel
+ */
+void
+GNUNET_CONNECTION_notify_transmit_ready_cancel (struct GNUNET_CONNECTION_TransmitHandle *th)
+{
+ GNUNET_assert (NULL != th->notify_ready);
+ th->notify_ready = NULL;
+ if (NULL != th->timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (th->timeout_task);
+ th->timeout_task = NULL;
+ }
+ if (NULL != th->connection->write_task)
+ {
+ GNUNET_SCHEDULER_cancel (th->connection->write_task);
+ th->connection->write_task = NULL;
+ }
+}
+
+
+/**
+ * Create a connection to be proxied using a given connection.
+ *
+ * @param cph connection to proxy server
+ * @return connection to be proxied
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_proxied_from_handshake (struct GNUNET_CONNECTION_Handle *cph)
+{
+ struct GNUNET_CONNECTION_Handle *proxied = GNUNET_CONNECTION_create_from_existing (NULL);
+
+ proxied->proxy_handshake = cph;
+ return proxied;
+}
+
+
+/**
+ * Activate proxied connection and destroy initial proxy handshake connection.
+ * There must not be any pending requests for reading or writing to the
+ * proxy hadshake connection at this time.
+ *
+ * @param proxied connection connection to proxy server
+ */
+void
+GNUNET_CONNECTION_acivate_proxied (struct GNUNET_CONNECTION_Handle *proxied)
+{
+ struct GNUNET_CONNECTION_Handle *cph = proxied->proxy_handshake;
+
+ GNUNET_assert (NULL != cph);
+ GNUNET_assert (NULL == proxied->sock);
+ GNUNET_assert (NULL != cph->sock);
+ proxied->sock = cph->sock;
+ cph->sock = NULL;
+ GNUNET_CONNECTION_destroy (cph);
+ connect_success_continuation (proxied);
+}
+
+
+/* end of connection.c */
diff --git a/src/transport/tcp_server_legacy.c b/src/transport/tcp_server_legacy.c
new file mode 100644
index 000000000..c055285b1
--- /dev/null
+++ b/src/transport/tcp_server_legacy.c
@@ -0,0 +1,1748 @@
+/*
+ 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 General Public License as published
+ by the Free Software Foundation; either version 3, 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @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;
+ GNUNET_CONNECTION_receive (client->connection,
+ GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+ client->idle_timeout,
+ &process_incoming,
+ client);
+ 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_SERVER_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_SERVER_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;
+ GNUNET_CONNECTION_receive (client->connection,
+ GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+ client->idle_timeout,
+ &process_incoming,
+ client);
+ 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);
+}
+
+
+/**
+ * Destroy the connection that is passed in via @a 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
+ */
+static void
+destroy_connection (void *cls)
+{
+ struct GNUNET_CONNECTION_Handle *connection = cls;
+
+ GNUNET_CONNECTION_destroy (connection);
+}
+
+
+/**
+ * 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);
+ (void) GNUNET_SCHEDULER_add_now (&destroy_connection,
+ 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 */
diff --git a/src/transport/tcp_server_mst_legacy.c b/src/transport/tcp_server_mst_legacy.c
new file mode 100644
index 000000000..ba42b1837
--- /dev/null
+++ b/src/transport/tcp_server_mst_legacy.c
@@ -0,0 +1,311 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2010 GNUnet e.V.
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file util/server_mst.c
+ * @brief convenience functions for handling inbound message buffers
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+
+#if HAVE_UNALIGNED_64_ACCESS
+#define ALIGN_FACTOR 4
+#else
+#define ALIGN_FACTOR 8
+#endif
+
+
+/**
+ * Handle to a message stream tokenizer.
+ */
+struct GNUNET_SERVER_MessageStreamTokenizer
+{
+
+ /**
+ * Function to call on completed messages.
+ */
+ GNUNET_SERVER_MessageTokenizerCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Size of the buffer (starting at @e hdr).
+ */
+ size_t curr_buf;
+
+ /**
+ * How many bytes in buffer have we already processed?
+ */
+ size_t off;
+
+ /**
+ * How many bytes in buffer are valid right now?
+ */
+ size_t pos;
+
+ /**
+ * Beginning of the buffer. Typed like this to force alignment.
+ */
+ struct GNUNET_MessageHeader *hdr;
+
+};
+
+
+
+/**
+ * Create a message stream tokenizer.
+ *
+ * @param cb function to call on completed messages
+ * @param cb_cls closure for @a cb
+ * @return handle to tokenizer
+ */
+struct GNUNET_SERVER_MessageStreamTokenizer *
+GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_SERVER_MessageStreamTokenizer *ret;
+
+ ret = GNUNET_new (struct GNUNET_SERVER_MessageStreamTokenizer);
+ ret->hdr = GNUNET_malloc (GNUNET_SERVER_MIN_BUFFER_SIZE);
+ ret->curr_buf = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ ret->cb = cb;
+ ret->cb_cls = cb_cls;
+ return ret;
+}
+
+
+/**
+ * Add incoming data to the receive buffer and call the
+ * callback for all complete messages.
+ *
+ * @param mst tokenizer to use
+ * @param client_identity ID of client for which this is a buffer
+ * @param buf input data to add
+ * @param size number of bytes in @a buf
+ * @param purge should any excess bytes in the buffer be discarded
+ * (i.e. for packet-based services like UDP)
+ * @param one_shot only call callback once, keep rest of message in buffer
+ * @return #GNUNET_OK if we are done processing (need more data)
+ * #GNUNET_NO if @a one_shot was set and we have another message ready
+ * #GNUNET_SYSERR if the data stream is corrupt
+ */
+int
+GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst,
+ void *client_identity,
+ const char *buf, size_t size,
+ int purge, int one_shot)
+{
+ const struct GNUNET_MessageHeader *hdr;
+ size_t delta;
+ uint16_t want;
+ char *ibuf;
+ int need_align;
+ unsigned long offset;
+ int ret;
+
+ GNUNET_assert (mst->off <= mst->pos);
+ GNUNET_assert (mst->pos <= mst->curr_buf);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server-mst receives %u bytes with %u bytes already in private buffer\n",
+ (unsigned int) size, (unsigned int) (mst->pos - mst->off));
+ ret = GNUNET_OK;
+ ibuf = (char *) mst->hdr;
+ while (mst->pos > 0)
+ {
+do_align:
+ GNUNET_assert (mst->pos >= mst->off);
+ if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
+ (0 != (mst->off % ALIGN_FACTOR)))
+ {
+ /* need to align or need more space */
+ mst->pos -= mst->off;
+ memmove (ibuf, &ibuf[mst->off], mst->pos);
+ mst->off = 0;
+ }
+ if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
+ {
+ delta =
+ GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
+ (mst->pos - mst->off), size);
+ GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
+ mst->pos += delta;
+ buf += delta;
+ size -= delta;
+ }
+ if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
+ {
+ if (purge)
+ {
+ mst->off = 0;
+ mst->pos = 0;
+ }
+ return GNUNET_OK;
+ }
+ hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
+ want = ntohs (hdr->size);
+ if (want < sizeof (struct GNUNET_MessageHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if ( (mst->curr_buf - mst->off < want) &&
+ (mst->off > 0) )
+ {
+ /* can get more space by moving */
+ mst->pos -= mst->off;
+ memmove (ibuf, &ibuf[mst->off], mst->pos);
+ mst->off = 0;
+ }
+ if (mst->curr_buf < want)
+ {
+ /* need to get more space by growing buffer */
+ GNUNET_assert (0 == mst->off);
+ mst->hdr = GNUNET_realloc (mst->hdr, want);
+ ibuf = (char *) mst->hdr;
+ mst->curr_buf = want;
+ }
+ hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
+ if (mst->pos - mst->off < want)
+ {
+ delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
+ GNUNET_assert (mst->pos + delta <= mst->curr_buf);
+ GNUNET_memcpy (&ibuf[mst->pos], buf, delta);
+ mst->pos += delta;
+ buf += delta;
+ size -= delta;
+ }
+ if (mst->pos - mst->off < want)
+ {
+ if (purge)
+ {
+ mst->off = 0;
+ mst->pos = 0;
+ }
+ return GNUNET_OK;
+ }
+ if (one_shot == GNUNET_SYSERR)
+ {
+ /* cannot call callback again, but return value saying that
+ * we have another full message in the buffer */
+ ret = GNUNET_NO;
+ goto copy;
+ }
+ if (one_shot == GNUNET_YES)
+ one_shot = GNUNET_SYSERR;
+ mst->off += want;
+ if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
+ return GNUNET_SYSERR;
+ if (mst->off == mst->pos)
+ {
+ /* reset to beginning of buffer, it's free right now! */
+ mst->off = 0;
+ mst->pos = 0;
+ }
+ }
+ GNUNET_assert (0 == mst->pos);
+ while (size > 0)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server-mst has %u bytes left in inbound buffer\n",
+ (unsigned int) size);
+ if (size < sizeof (struct GNUNET_MessageHeader))
+ break;
+ offset = (unsigned long) buf;
+ need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO;
+ if (GNUNET_NO == need_align)
+ {
+ /* can try to do zero-copy and process directly from original buffer */
+ hdr = (const struct GNUNET_MessageHeader *) buf;
+ want = ntohs (hdr->size);
+ if (want < sizeof (struct GNUNET_MessageHeader))
+ {
+ GNUNET_break_op (0);
+ mst->off = 0;
+ return GNUNET_SYSERR;
+ }
+ if (size < want)
+ break; /* or not: buffer incomplete, so copy to private buffer... */
+ if (one_shot == GNUNET_SYSERR)
+ {
+ /* cannot call callback again, but return value saying that
+ * we have another full message in the buffer */
+ ret = GNUNET_NO;
+ goto copy;
+ }
+ if (one_shot == GNUNET_YES)
+ one_shot = GNUNET_SYSERR;
+ if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
+ return GNUNET_SYSERR;
+ buf += want;
+ size -= want;
+ }
+ else
+ {
+ /* need to copy to private buffer to align;
+ * yes, we go a bit more spagetti than usual here */
+ goto do_align;
+ }
+ }
+copy:
+ if ((size > 0) && (!purge))
+ {
+ if (size + mst->pos > mst->curr_buf)
+ {
+ mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos);
+ ibuf = (char *) mst->hdr;
+ mst->curr_buf = size + mst->pos;
+ }
+ GNUNET_assert (size + mst->pos <= mst->curr_buf);
+ GNUNET_memcpy (&ibuf[mst->pos], buf, size);
+ mst->pos += size;
+ }
+ if (purge)
+ {
+ mst->off = 0;
+ mst->pos = 0;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server-mst leaves %u bytes in private buffer\n",
+ (unsigned int) (mst->pos - mst->off));
+ return ret;
+}
+
+
+/**
+ * Destroys a tokenizer.
+ *
+ * @param mst tokenizer to destroy
+ */
+void
+GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst)
+{
+ GNUNET_free (mst->hdr);
+ GNUNET_free (mst);
+}
+
+
+
+/* end of server_mst.c */
diff --git a/src/transport/tcp_service_legacy.c b/src/transport/tcp_service_legacy.c
new file mode 100644
index 000000000..c55d586f3
--- /dev/null
+++ b/src/transport/tcp_service_legacy.c
@@ -0,0 +1,1687 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2009, 2012 GNUnet e.V.
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file util/service.c
+ * @brief functions related to starting services
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_constants.h"
+#include "gnunet_resolver_service.h"
+
+#if HAVE_MALLINFO
+#include <malloc.h>
+#include "gauger.h"
+#endif
+
+
+/* ******************* access control ******************** */
+
+/**
+ * Check if the given IP address is in the list of IP addresses.
+ *
+ * @param list a list of networks
+ * @param add the IP to check (in network byte order)
+ * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is
+ */
+static int
+check_ipv4_listed (const struct GNUNET_STRINGS_IPv4NetworkPolicy *list,
+ const struct in_addr *add)
+{
+ unsigned int i;
+
+ if (NULL == list)
+ return GNUNET_NO;
+ i = 0;
+ while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0))
+ {
+ if ((add->s_addr & list[i].netmask.s_addr) ==
+ (list[i].network.s_addr & list[i].netmask.s_addr))
+ return GNUNET_YES;
+ i++;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Check if the given IP address is in the list of IP addresses.
+ *
+ * @param list a list of networks
+ * @param ip the IP to check (in network byte order)
+ * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is
+ */
+static int
+check_ipv6_listed (const struct GNUNET_STRINGS_IPv6NetworkPolicy *list,
+ const struct in6_addr *ip)
+{
+ unsigned int i;
+ unsigned int j;
+ struct in6_addr zero;
+
+ if (NULL == list)
+ return GNUNET_NO;
+ memset (&zero, 0, sizeof (struct in6_addr));
+ i = 0;
+NEXT:
+ while (0 != memcmp (&zero, &list[i].network, sizeof (struct in6_addr)))
+ {
+ for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++)
+ if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) !=
+ (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j]))
+ {
+ i++;
+ goto NEXT;
+ }
+ return GNUNET_YES;
+ }
+ return GNUNET_NO;
+}
+
+
+/* ****************** service struct ****************** */
+
+
+/**
+ * Context for "service_task".
+ */
+struct GNUNET_SERVICE_Context
+{
+ /**
+ * Our configuration.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Handle for the server.
+ */
+ struct GNUNET_SERVER_Handle *server;
+
+ /**
+ * NULL-terminated array of addresses to bind to, NULL if we got pre-bound
+ * listen sockets.
+ */
+ struct sockaddr **addrs;
+
+ /**
+ * Name of our service.
+ */
+ const char *service_name;
+
+ /**
+ * Main service-specific task to run.
+ */
+ GNUNET_SERVICE_Main task;
+
+ /**
+ * Closure for @e task.
+ */
+ void *task_cls;
+
+ /**
+ * IPv4 addresses that are not allowed to connect.
+ */
+ struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_denied;
+
+ /**
+ * IPv6 addresses that are not allowed to connect.
+ */
+ struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_denied;
+
+ /**
+ * IPv4 addresses that are allowed to connect (if not
+ * set, all are allowed).
+ */
+ struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_allowed;
+
+ /**
+ * IPv6 addresses that are allowed to connect (if not
+ * set, all are allowed).
+ */
+ struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_allowed;
+
+ /**
+ * My (default) message handlers. Adjusted copy
+ * of "defhandlers".
+ */
+ struct GNUNET_SERVER_MessageHandler *my_handlers;
+
+ /**
+ * Array of the lengths of the entries in addrs.
+ */
+ socklen_t *addrlens;
+
+ /**
+ * NULL-terminated array of listen sockets we should take over.
+ */
+ struct GNUNET_NETWORK_Handle **lsocks;
+
+ /**
+ * Task ID of the shutdown task.
+ */
+ struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+ /**
+ * Idle timeout for server.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Overall success/failure of the service start.
+ */
+ int ret;
+
+ /**
+ * If we are daemonizing, this FD is set to the
+ * pipe to the parent. Send '.' if we started
+ * ok, '!' if not. -1 if we are not daemonizing.
+ */
+ int ready_confirm_fd;
+
+ /**
+ * Do we close connections if we receive messages
+ * for which we have no handler?
+ */
+ int require_found;
+
+ /**
+ * Do we require a matching UID for UNIX domain socket connections?
+ * #GNUNET_NO means that the UID does not have to match (however,
+ * @e match_gid may still impose other access control checks).
+ */
+ int match_uid;
+
+ /**
+ * Do we require a matching GID for UNIX domain socket connections?
+ * Ignored if @e match_uid is #GNUNET_YES. Note that this is about
+ * checking that the client's UID is in our group OR that the
+ * client's GID is our GID. If both "match_gid" and @e match_uid are
+ * #GNUNET_NO, all users on the local system have access.
+ */
+ int match_gid;
+
+ /**
+ * Our options.
+ */
+ enum GNUNET_SERVICE_Options options;
+
+};
+
+
+/* ****************** message handlers ****************** */
+
+/**
+ * Send a 'TEST' message back to the client.
+ *
+ * @param cls the 'struct GNUNET_SERVER_Client' to send TEST to
+ * @param size number of bytes available in 'buf'
+ * @param buf where to copy the message
+ * @return number of bytes written to 'buf'
+ */
+static size_t
+write_test (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ struct GNUNET_MessageHeader *msg;
+
+ if (size < sizeof (struct GNUNET_MessageHeader))
+ {
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return 0; /* client disconnected */
+ }
+ msg = (struct GNUNET_MessageHeader *) buf;
+ msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
+ msg->size = htons (sizeof (struct GNUNET_MessageHeader));
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+/**
+ * Handler for TEST message.
+ *
+ * @param cls closure (refers to service)
+ * @param client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_test (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ /* simply bounce message back to acknowledge */
+ if (NULL ==
+ GNUNET_SERVER_notify_transmit_ready (client,
+ sizeof (struct GNUNET_MessageHeader),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &write_test, client))
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+}
+
+
+/**
+ * Default handlers for all services. Will be copied and the
+ * "callback_cls" fields will be replaced with the specific service
+ * struct.
+ */
+static const struct GNUNET_SERVER_MessageHandler defhandlers[] = {
+ {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST,
+ sizeof (struct GNUNET_MessageHeader)},
+ {NULL, NULL, 0, 0}
+};
+
+
+/* ****************** service core routines ************** */
+
+
+/**
+ * Check if access to the service is allowed from the given address.
+ *
+ * @param cls closure
+ * @param uc credentials, if available, otherwise NULL
+ * @param addr address
+ * @param addrlen length of address
+ * @return #GNUNET_YES to allow, #GNUNET_NO to deny, #GNUNET_SYSERR
+ * for unknown address family (will be denied).
+ */
+static int
+check_access (void *cls, const struct GNUNET_CONNECTION_Credentials *uc,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ struct GNUNET_SERVICE_Context *sctx = cls;
+ const struct sockaddr_in *i4;
+ const struct sockaddr_in6 *i6;
+ int ret;
+
+ switch (addr->sa_family)
+ {
+ case AF_INET:
+ GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
+ i4 = (const struct sockaddr_in *) addr;
+ ret = ((NULL == sctx->v4_allowed) ||
+ (check_ipv4_listed (sctx->v4_allowed, &i4->sin_addr))) &&
+ ((NULL == sctx->v4_denied) ||
+ (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr)));
+ break;
+ case AF_INET6:
+ GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
+ i6 = (const struct sockaddr_in6 *) addr;
+ ret = ((NULL == sctx->v6_allowed) ||
+ (check_ipv6_listed (sctx->v6_allowed, &i6->sin6_addr))) &&
+ ((NULL == sctx->v6_denied) ||
+ (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr)));
+ break;
+#ifndef WINDOWS
+ case AF_UNIX:
+ ret = GNUNET_OK; /* controlled using file-system ACL now */
+ break;
+#endif
+ default:
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("Unknown address family %d\n"),
+ addr->sa_family);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != ret)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Access from `%s' denied to service `%s'\n"),
+ GNUNET_a2s (addr, addrlen),
+ sctx->service_name);
+ }
+ return ret;
+}
+
+
+/**
+ * Get the name of the file where we will
+ * write the PID of the service.
+ *
+ * @param sctx service context
+ * @return name of the file for the process ID
+ */
+static char *
+get_pid_file_name (struct GNUNET_SERVICE_Context *sctx)
+{
+ char *pif;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name,
+ "PIDFILE", &pif))
+ return NULL;
+ return pif;
+}
+
+
+/**
+ * Parse an IPv4 access control list.
+ *
+ * @param ret location where to write the ACL (set)
+ * @param sctx service context to use to get the configuration
+ * @param option name of the ACL option to parse
+ * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including
+ * no ACL configured)
+ */
+static int
+process_acl4 (struct GNUNET_STRINGS_IPv4NetworkPolicy **ret,
+ struct GNUNET_SERVICE_Context *sctx,
+ const char *option)
+{
+ char *opt;
+
+ if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option))
+ {
+ *ret = NULL;
+ return GNUNET_OK;
+ }
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
+ sctx->service_name,
+ option, &opt));
+ if (NULL == (*ret = GNUNET_STRINGS_parse_ipv4_policy (opt)))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Could not parse IPv4 network specification `%s' for `%s:%s'\n"),
+ opt, sctx->service_name, option);
+ GNUNET_free (opt);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (opt);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse an IPv6 access control list.
+ *
+ * @param ret location where to write the ACL (set)
+ * @param sctx service context to use to get the configuration
+ * @param option name of the ACL option to parse
+ * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including
+ * no ACL configured)
+ */
+static int
+process_acl6 (struct GNUNET_STRINGS_IPv6NetworkPolicy **ret,
+ struct GNUNET_SERVICE_Context *sctx,
+ const char *option)
+{
+ char *opt;
+
+ if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option))
+ {
+ *ret = NULL;
+ return GNUNET_OK;
+ }
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
+ sctx->service_name,
+ option, &opt));
+ if (NULL == (*ret = GNUNET_STRINGS_parse_ipv6_policy (opt)))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Could not parse IPv6 network specification `%s' for `%s:%s'\n"),
+ opt, sctx->service_name, option);
+ GNUNET_free (opt);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (opt);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Add the given UNIX domain path as an address to the
+ * list (as the first entry).
+ *
+ * @param saddrs array to update
+ * @param saddrlens where to store the address length
+ * @param unixpath path to add
+ * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This
+ * parameter is ignore on systems other than LINUX
+ */
+static void
+add_unixpath (struct sockaddr **saddrs,
+ socklen_t *saddrlens,
+ const char *unixpath,
+ int abstract)
+{
+#ifdef AF_UNIX
+ struct sockaddr_un *un;
+
+ un = GNUNET_new (struct sockaddr_un);
+ un->sun_family = AF_UNIX;
+ strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1);
+#ifdef LINUX
+ if (GNUNET_YES == abstract)
+ un->sun_path[0] = '\0';
+#endif
+#if HAVE_SOCKADDR_UN_SUN_LEN
+ un->sun_len = (u_char) sizeof (struct sockaddr_un);
+#endif
+ *saddrs = (struct sockaddr *) un;
+ *saddrlens = sizeof (struct sockaddr_un);
+#else
+ /* this function should never be called
+ * unless AF_UNIX is defined! */
+ GNUNET_assert (0);
+#endif
+}
+
+
+/**
+ * Get the list of addresses that a server for the given service
+ * should bind to.
+ *
+ * @param service_name name of the service
+ * @param cfg configuration (which specifies the addresses)
+ * @param addrs set (call by reference) to an array of pointers to the
+ * addresses the server should bind to and listen on; the
+ * array will be NULL-terminated (on success)
+ * @param addr_lens set (call by reference) to an array of the lengths
+ * of the respective `struct sockaddr` struct in the @a addrs
+ * array (on success)
+ * @return number of addresses found on success,
+ * #GNUNET_SYSERR if the configuration
+ * did not specify reasonable finding information or
+ * if it specified a hostname that could not be resolved;
+ * #GNUNET_NO if the number of addresses configured is
+ * zero (in this case, `*addrs` and `*addr_lens` will be
+ * set to NULL).
+ */
+int
+GNUNET_SERVICE_get_server_addresses (const char *service_name,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct sockaddr ***addrs,
+ socklen_t ** addr_lens)
+{
+ int disablev6;
+ struct GNUNET_NETWORK_Handle *desc;
+ unsigned long long port;
+ char *unixpath;
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct addrinfo *pos;
+ struct addrinfo *next;
+ unsigned int i;
+ int resi;
+ int ret;
+ int abstract;
+ struct sockaddr **saddrs;
+ socklen_t *saddrlens;
+ char *hostname;
+
+ *addrs = NULL;
+ *addr_lens = NULL;
+ desc = NULL;
+ if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6"))
+ {
+ if (GNUNET_SYSERR ==
+ (disablev6 =
+ GNUNET_CONFIGURATION_get_value_yesno (cfg, service_name, "DISABLEV6")))
+ return GNUNET_SYSERR;
+ }
+ else
+ disablev6 = GNUNET_NO;
+
+ if (! disablev6)
+ {
+ /* probe IPv6 support */
+ desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
+ if (NULL == desc)
+ {
+ if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) ||
+ (EACCES == errno))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
+ return GNUNET_SYSERR;
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"),
+ service_name, STRERROR (errno));
+ disablev6 = GNUNET_YES;
+ }
+ else
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
+ desc = NULL;
+ }
+ }
+
+ port = 0;
+ if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT"))
+ {
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg, service_name,
+ "PORT", &port))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Require valid port number for service `%s' in configuration!\n"),
+ service_name);
+ }
+ if (port > 65535)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Require valid port number for service `%s' in configuration!\n"),
+ service_name);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO"))
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, service_name,
+ "BINDTO", &hostname));
+ }
+ else
+ hostname = NULL;
+
+ unixpath = NULL;
+ abstract = GNUNET_NO;
+#ifdef AF_UNIX
+ if ((GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH",
+ &unixpath)) &&
+ (0 < strlen (unixpath)))
+ {
+ /* probe UNIX support */
+ struct sockaddr_un s_un;
+
+ if (strlen (unixpath) >= sizeof (s_un.sun_path))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath,
+ (unsigned long long) sizeof (s_un.sun_path));
+ unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Using `%s' instead\n"),
+ unixpath);
+ }
+#ifdef LINUX
+ abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ "TESTING",
+ "USE_ABSTRACT_SOCKETS");
+ if (GNUNET_SYSERR == abstract)
+ abstract = GNUNET_NO;
+#endif
+ if ((GNUNET_YES != abstract)
+ && (GNUNET_OK !=
+ GNUNET_DISK_directory_create_for_file (unixpath)))
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "mkdir",
+ unixpath);
+ }
+ if (NULL != unixpath)
+ {
+ desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0);
+ if (NULL == desc)
+ {
+ if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) ||
+ (EACCES == errno))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
+ GNUNET_free_non_null (hostname);
+ GNUNET_free (unixpath);
+ return GNUNET_SYSERR;
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"),
+ service_name,
+ STRERROR (errno));
+ GNUNET_free (unixpath);
+ unixpath = NULL;
+ }
+ else
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
+ desc = NULL;
+ }
+ }
+#endif
+
+ if ((0 == port) && (NULL == unixpath))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"),
+ service_name);
+ GNUNET_free_non_null (hostname);
+ return GNUNET_SYSERR;
+ }
+ if (0 == port)
+ {
+ saddrs = GNUNET_malloc (2 * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc (2 * sizeof (socklen_t));
+ add_unixpath (saddrs, saddrlens, unixpath, abstract);
+ GNUNET_free_non_null (unixpath);
+ GNUNET_free_non_null (hostname);
+ *addrs = saddrs;
+ *addr_lens = saddrlens;
+ return 1;
+ }
+
+ if (NULL != hostname)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Resolving `%s' since that is where `%s' will bind to.\n",
+ hostname,
+ service_name);
+ memset (&hints, 0, sizeof (struct addrinfo));
+ if (disablev6)
+ hints.ai_family = AF_INET;
+ hints.ai_protocol = IPPROTO_TCP;
+ if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
+ (NULL == res))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to resolve `%s': %s\n"),
+ hostname,
+ gai_strerror (ret));
+ GNUNET_free (hostname);
+ GNUNET_free_non_null (unixpath);
+ return GNUNET_SYSERR;
+ }
+ next = res;
+ i = 0;
+ while (NULL != (pos = next))
+ {
+ next = pos->ai_next;
+ if ((disablev6) && (pos->ai_family == AF_INET6))
+ continue;
+ i++;
+ }
+ if (0 == i)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to find %saddress for `%s'.\n"),
+ disablev6 ? "IPv4 " : "",
+ hostname);
+ freeaddrinfo (res);
+ GNUNET_free (hostname);
+ GNUNET_free_non_null (unixpath);
+ return GNUNET_SYSERR;
+ }
+ resi = i;
+ if (NULL != unixpath)
+ resi++;
+ saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
+ i = 0;
+ if (NULL != unixpath)
+ {
+ add_unixpath (saddrs, saddrlens, unixpath, abstract);
+ i++;
+ }
+ next = res;
+ while (NULL != (pos = next))
+ {
+ next = pos->ai_next;
+ if ((disablev6) && (AF_INET6 == pos->ai_family))
+ continue;
+ if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol))
+ continue; /* not TCP */
+ if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype))
+ continue; /* huh? */
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' will bind to `%s'\n",
+ service_name, GNUNET_a2s (pos->ai_addr, pos->ai_addrlen));
+ if (AF_INET == pos->ai_family)
+ {
+ GNUNET_assert (sizeof (struct sockaddr_in) == pos->ai_addrlen);
+ saddrlens[i] = pos->ai_addrlen;
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+ GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
+ ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
+ }
+ else
+ {
+ GNUNET_assert (AF_INET6 == pos->ai_family);
+ GNUNET_assert (sizeof (struct sockaddr_in6) == pos->ai_addrlen);
+ saddrlens[i] = pos->ai_addrlen;
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+ GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
+ }
+ i++;
+ }
+ GNUNET_free (hostname);
+ freeaddrinfo (res);
+ resi = i;
+ }
+ else
+ {
+ /* will bind against everything, just set port */
+ if (disablev6)
+ {
+ /* V4-only */
+ resi = 1;
+ if (NULL != unixpath)
+ resi++;
+ i = 0;
+ saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
+ if (NULL != unixpath)
+ {
+ add_unixpath (saddrs, saddrlens, unixpath, abstract);
+ i++;
+ }
+ saddrlens[i] = sizeof (struct sockaddr_in);
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
+#endif
+ ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
+ ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
+ }
+ else
+ {
+ /* dual stack */
+ resi = 2;
+ if (NULL != unixpath)
+ resi++;
+ saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
+ i = 0;
+ if (NULL != unixpath)
+ {
+ add_unixpath (saddrs, saddrlens, unixpath, abstract);
+ i++;
+ }
+ saddrlens[i] = sizeof (struct sockaddr_in6);
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
+#endif
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
+ i++;
+ saddrlens[i] = sizeof (struct sockaddr_in);
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
+#endif
+ ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
+ ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
+ }
+ }
+ GNUNET_free_non_null (unixpath);
+ *addrs = saddrs;
+ *addr_lens = saddrlens;
+ return resi;
+}
+
+
+#ifdef MINGW
+/**
+ * Read listen sockets from the parent process (ARM).
+ *
+ * @param sctx service context to initialize
+ * @return #GNUNET_YES if ok, #GNUNET_NO if not ok (must bind yourself),
+ * and #GNUNET_SYSERR on error.
+ */
+static int
+receive_sockets_from_parent (struct GNUNET_SERVICE_Context *sctx)
+{
+ const char *env_buf;
+ int fail;
+ uint64_t count;
+ uint64_t i;
+ HANDLE lsocks_pipe;
+
+ env_buf = getenv ("GNUNET_OS_READ_LSOCKS");
+ if ((NULL == env_buf) || (strlen (env_buf) <= 0))
+ return GNUNET_NO;
+ /* Using W32 API directly here, because this pipe will
+ * never be used outside of this function, and it's just too much of a bother
+ * to create a GNUnet API that boxes a HANDLE (the way it is done with socks)
+ */
+ lsocks_pipe = (HANDLE) strtoul (env_buf, NULL, 10);
+ if ( (0 == lsocks_pipe) || (INVALID_HANDLE_VALUE == lsocks_pipe))
+ return GNUNET_NO;
+ fail = 1;
+ do
+ {
+ int ret;
+ int fail2;
+ DWORD rd;
+
+ ret = ReadFile (lsocks_pipe, &count, sizeof (count), &rd, NULL);
+ if ((0 == ret) || (sizeof (count) != rd) || (0 == count))
+ break;
+ sctx->lsocks =
+ GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (count + 1));
+
+ fail2 = 1;
+ for (i = 0; i < count; i++)
+ {
+ WSAPROTOCOL_INFOA pi;
+ uint64_t size;
+ SOCKET s;
+
+ ret = ReadFile (lsocks_pipe, &size, sizeof (size), &rd, NULL);
+ if ( (0 == ret) || (sizeof (size) != rd) || (sizeof (pi) != size) )
+ break;
+ ret = ReadFile (lsocks_pipe, &pi, sizeof (pi), &rd, NULL);
+ if ( (0 == ret) || (sizeof (pi) != rd))
+ break;
+ s = WSASocketA (pi.iAddressFamily, pi.iSocketType, pi.iProtocol, &pi, 0, WSA_FLAG_OVERLAPPED);
+ sctx->lsocks[i] = GNUNET_NETWORK_socket_box_native (s);
+ if (NULL == sctx->lsocks[i])
+ break;
+ else if (i == count - 1)
+ fail2 = 0;
+ }
+ if (fail2)
+ break;
+ sctx->lsocks[count] = NULL;
+ fail = 0;
+ }
+ while (fail);
+
+ CloseHandle (lsocks_pipe);
+
+ if (fail)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not access a pre-bound socket, will try to bind myself\n"));
+ for (i = 0; (i < count) && (NULL != sctx->lsocks[i]); i++)
+ GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[i]));
+ GNUNET_free_non_null (sctx->lsocks);
+ sctx->lsocks = NULL;
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+#endif
+
+
+/**
+ * Setup addr, addrlen, idle_timeout
+ * based on configuration!
+ *
+ * Configuration may specify:
+ * - PORT (where to bind to for TCP)
+ * - UNIXPATH (where to bind to for UNIX domain sockets)
+ * - TIMEOUT (after how many ms does an inactive service timeout);
+ * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack)
+ * - BINDTO (hostname or IP address to bind to, otherwise we take everything)
+ * - ACCEPT_FROM (only allow connections from specified IPv4 subnets)
+ * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets)
+ * - REJECT_FROM (disallow allow connections from specified IPv4 subnets)
+ * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets)
+ *
+ * @param sctx service context to initialize
+ * @return #GNUNET_OK if configuration succeeded
+ */
+static int
+setup_service (struct GNUNET_SERVICE_Context *sctx)
+{
+ struct GNUNET_TIME_Relative idleout;
+ int tolerant;
+
+#ifndef MINGW
+ const char *nfds;
+ unsigned int cnt;
+ int flags;
+#endif
+
+ if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, "TIMEOUT"))
+ {
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (sctx->cfg, sctx->service_name,
+ "TIMEOUT", &idleout))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Specified value for `%s' of service `%s' is invalid\n"),
+ "TIMEOUT", sctx->service_name);
+ return GNUNET_SYSERR;
+ }
+ sctx->timeout = idleout;
+ }
+ else
+ sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+
+ if (GNUNET_CONFIGURATION_have_value
+ (sctx->cfg, sctx->service_name, "TOLERANT"))
+ {
+ if (GNUNET_SYSERR ==
+ (tolerant =
+ GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name,
+ "TOLERANT")))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Specified value for `%s' of service `%s' is invalid\n"),
+ "TOLERANT", sctx->service_name);
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ tolerant = GNUNET_NO;
+
+#ifndef MINGW
+ errno = 0;
+ if ((NULL != (nfds = getenv ("LISTEN_FDS"))) &&
+ (1 == SSCANF (nfds, "%u", &cnt)) && (cnt > 0) && (cnt < FD_SETSIZE) &&
+ (cnt + 4 < FD_SETSIZE))
+ {
+ sctx->lsocks =
+ GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (cnt + 1));
+ while (0 < cnt--)
+ {
+ flags = fcntl (3 + cnt, F_GETFD);
+ if ((flags < 0) || (0 != (flags & FD_CLOEXEC)) ||
+ (NULL ==
+ (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt))))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("Could not access pre-bound socket %u, will try to bind myself\n"),
+ (unsigned int) 3 + cnt);
+ cnt++;
+ while (sctx->lsocks[cnt] != NULL)
+ GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++]));
+ GNUNET_free (sctx->lsocks);
+ sctx->lsocks = NULL;
+ break;
+ }
+ }
+ unsetenv ("LISTEN_FDS");
+ }
+#else
+ if (getenv ("GNUNET_OS_READ_LSOCKS") != NULL)
+ {
+ receive_sockets_from_parent (sctx);
+ putenv ("GNUNET_OS_READ_LSOCKS=");
+ }
+#endif
+
+ if ((NULL == sctx->lsocks) &&
+ (GNUNET_SYSERR ==
+ GNUNET_SERVICE_get_server_addresses (sctx->service_name, sctx->cfg,
+ &sctx->addrs, &sctx->addrlens)))
+ return GNUNET_SYSERR;
+ sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES;
+ sctx->match_uid =
+ GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name,
+ "UNIX_MATCH_UID");
+ sctx->match_gid =
+ GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name,
+ "UNIX_MATCH_GID");
+ process_acl4 (&sctx->v4_denied, sctx, "REJECT_FROM");
+ process_acl4 (&sctx->v4_allowed, sctx, "ACCEPT_FROM");
+ process_acl6 (&sctx->v6_denied, sctx, "REJECT_FROM6");
+ process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6");
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get the name of the user that'll be used
+ * to provide the service.
+ *
+ * @param sctx service context
+ * @return value of the 'USERNAME' option
+ */
+static char *
+get_user_name (struct GNUNET_SERVICE_Context *sctx)
+{
+ char *un;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name,
+ "USERNAME", &un))
+ return NULL;
+ return un;
+}
+
+
+/**
+ * Write PID file.
+ *
+ * @param sctx service context
+ * @param pid PID to write (should be equal to 'getpid()'
+ * @return #GNUNET_OK on success (including no work to be done)
+ */
+static int
+write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid)
+{
+ FILE *pidfd;
+ char *pif;
+ char *user;
+ char *rdir;
+ int len;
+
+ if (NULL == (pif = get_pid_file_name (sctx)))
+ return GNUNET_OK; /* no file desired */
+ user = get_user_name (sctx);
+ rdir = GNUNET_strdup (pif);
+ len = strlen (rdir);
+ while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
+ len--;
+ rdir[len] = '\0';
+ if (0 != ACCESS (rdir, F_OK))
+ {
+ /* we get to create a directory -- and claim it
+ * as ours! */
+ (void) GNUNET_DISK_directory_create (rdir);
+ if ((NULL != user) && (0 < strlen (user)))
+ GNUNET_DISK_file_change_owner (rdir, user);
+ }
+ if (0 != ACCESS (rdir, W_OK | X_OK))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", rdir);
+ GNUNET_free (rdir);
+ GNUNET_free_non_null (user);
+ GNUNET_free (pif);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (rdir);
+ pidfd = FOPEN (pif, "w");
+ if (NULL == pidfd)
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "fopen", pif);
+ GNUNET_free (pif);
+ GNUNET_free_non_null (user);
+ return GNUNET_SYSERR;
+ }
+ if (0 > FPRINTF (pidfd, "%u", pid))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif);
+ GNUNET_break (0 == FCLOSE (pidfd));
+ if ((NULL != user) && (0 < strlen (user)))
+ GNUNET_DISK_file_change_owner (pif, user);
+ GNUNET_free_non_null (user);
+ GNUNET_free (pif);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Task run during shutdown. Stops the server/service.
+ *
+ * @param cls the `struct GNUNET_SERVICE_Context`
+ */
+static void
+shutdown_task (void *cls)
+{
+ struct GNUNET_SERVICE_Context *service = cls;
+ struct GNUNET_SERVER_Handle *server = service->server;
+
+ service->shutdown_task = NULL;
+ if (0 != (service->options & GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN))
+ GNUNET_SERVER_stop_listening (server);
+ else
+ GNUNET_SERVER_destroy (server);
+}
+
+
+/**
+ * Initial task for the service.
+ *
+ * @param cls service context
+ */
+static void
+service_task (void *cls)
+{
+ struct GNUNET_SERVICE_Context *sctx = cls;
+ unsigned int i;
+
+ GNUNET_RESOLVER_connect (sctx->cfg);
+ if (NULL != sctx->lsocks)
+ sctx->server
+ = GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks,
+ sctx->timeout, sctx->require_found);
+ else
+ sctx->server
+ = GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens,
+ sctx->timeout, sctx->require_found);
+ if (NULL == sctx->server)
+ {
+ if (NULL != sctx->addrs)
+ for (i = 0; NULL != sctx->addrs[i]; i++)
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Failed to start `%s' at `%s'\n"),
+ sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
+ sctx->ret = GNUNET_SYSERR;
+ return;
+ }
+#ifndef WINDOWS
+ if (NULL != sctx->addrs)
+ for (i = 0; NULL != sctx->addrs[i]; i++)
+ if ((AF_UNIX == sctx->addrs[i]->sa_family)
+ && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0]))
+ GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path,
+ sctx->match_uid,
+ sctx->match_gid);
+#endif
+
+
+ if (0 == (sctx->options & GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN))
+ {
+ /* install a task that will kill the server
+ * process if the scheduler ever gets a shutdown signal */
+ sctx->shutdown_task = GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+ sctx);
+ }
+ sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers));
+ GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers));
+ i = 0;
+ while (NULL != sctx->my_handlers[i].callback)
+ sctx->my_handlers[i++].callback_cls = sctx;
+ GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers);
+ if (-1 != sctx->ready_confirm_fd)
+ {
+ GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1));
+ GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd));
+ sctx->ready_confirm_fd = -1;
+ write_pid_file (sctx, getpid ());
+ }
+ if (NULL != sctx->addrs)
+ {
+ i = 0;
+ while (NULL != sctx->addrs[i])
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Service `%s' runs at %s\n"),
+ sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
+ i++;
+ }
+ }
+ sctx->task (sctx->task_cls, sctx->server, sctx->cfg);
+}
+
+
+/**
+ * Detach from terminal.
+ *
+ * @param sctx service context
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+detach_terminal (struct GNUNET_SERVICE_Context *sctx)
+{
+#ifndef MINGW
+ pid_t pid;
+ int nullfd;
+ int filedes[2];
+
+ if (0 != PIPE (filedes))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
+ return GNUNET_SYSERR;
+ }
+ pid = fork ();
+ if (pid < 0)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
+ return GNUNET_SYSERR;
+ }
+ if (0 != pid)
+ {
+ /* Parent */
+ char c;
+
+ GNUNET_break (0 == CLOSE (filedes[1]));
+ c = 'X';
+ if (1 != READ (filedes[0], &c, sizeof (char)))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "read");
+ fflush (stdout);
+ switch (c)
+ {
+ case '.':
+ exit (0);
+ case 'I':
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Service process failed to initialize\n"));
+ break;
+ case 'S':
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Service process could not initialize server function\n"));
+ break;
+ case 'X':
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Service process failed to report status\n"));
+ break;
+ }
+ exit (1); /* child reported error */
+ }
+ GNUNET_break (0 == CLOSE (0));
+ GNUNET_break (0 == CLOSE (1));
+ GNUNET_break (0 == CLOSE (filedes[0]));
+ nullfd = OPEN ("/dev/null", O_RDWR | O_APPEND);
+ if (nullfd < 0)
+ return GNUNET_SYSERR;
+ /* set stdin/stdout to /dev/null */
+ if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
+ (void) CLOSE (nullfd);
+ return GNUNET_SYSERR;
+ }
+ (void) CLOSE (nullfd);
+ /* Detach from controlling terminal */
+ pid = setsid ();
+ if (-1 == pid)
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "setsid");
+ sctx->ready_confirm_fd = filedes[1];
+#else
+ /* FIXME: we probably need to do something else
+ * elsewhere in order to fork the process itself... */
+ FreeConsole ();
+#endif
+ return GNUNET_OK;
+}
+
+
+/**
+ * Set user ID.
+ *
+ * @param sctx service context
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+set_user_id (struct GNUNET_SERVICE_Context *sctx)
+{
+ char *user;
+
+ if (NULL == (user = get_user_name (sctx)))
+ return GNUNET_OK; /* keep */
+#ifndef MINGW
+ struct passwd *pws;
+
+ errno = 0;
+ pws = getpwnam (user);
+ if (NULL == pws)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Cannot obtain information about user `%s': %s\n"), user,
+ errno == 0 ? _("No such user") : STRERROR (errno));
+ GNUNET_free (user);
+ return GNUNET_SYSERR;
+ }
+ if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) ||
+#if HAVE_INITGROUPS
+ (0 != initgroups (user, pws->pw_gid)) ||
+#endif
+ (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid)))
+ {
+ if ((0 != setregid (pws->pw_gid, pws->pw_gid)) ||
+ (0 != setreuid (pws->pw_uid, pws->pw_uid)))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot change user/group to `%s': %s\n"),
+ user, STRERROR (errno));
+ GNUNET_free (user);
+ return GNUNET_SYSERR;
+ }
+ }
+#endif
+ GNUNET_free (user);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Delete the PID file that was created by our parent.
+ *
+ * @param sctx service context
+ */
+static void
+pid_file_delete (struct GNUNET_SERVICE_Context *sctx)
+{
+ char *pif = get_pid_file_name (sctx);
+
+ if (NULL == pif)
+ return; /* no PID file */
+ if (0 != UNLINK (pif))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", pif);
+ GNUNET_free (pif);
+}
+
+
+/**
+ * Run a standard GNUnet service startup sequence (initialize loggers
+ * and configuration, parse options).
+ *
+ * @param argc number of command line arguments
+ * @param argv command line arguments
+ * @param service_name our service name
+ * @param options service options
+ * @param task main task of the service
+ * @param task_cls closure for @a task
+ * @return #GNUNET_SYSERR on error, #GNUNET_OK
+ * if we shutdown nicely
+ */
+int
+GNUNET_SERVICE_run (int argc, char *const *argv,
+ const char *service_name,
+ enum GNUNET_SERVICE_Options options,
+ GNUNET_SERVICE_Main task,
+ void *task_cls)
+{
+#define HANDLE_ERROR do { GNUNET_break (0); goto shutdown; } while (0)
+
+ int err;
+ int ret;
+ char *cfg_fn;
+ char *opt_cfg_fn;
+ char *loglev;
+ char *logfile;
+ int do_daemonize;
+ unsigned int i;
+ unsigned long long skew_offset;
+ unsigned long long skew_variance;
+ long long clock_offset;
+ struct GNUNET_SERVICE_Context sctx;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ const char *xdg;
+
+ struct GNUNET_GETOPT_CommandLineOption service_options[] = {
+ GNUNET_GETOPT_OPTION_CFG_FILE (&opt_cfg_fn),
+ {'d', "daemonize", NULL,
+ gettext_noop ("do daemonize (detach from terminal)"), 0,
+ GNUNET_GETOPT_set_one, &do_daemonize},
+ GNUNET_GETOPT_OPTION_HELP (NULL),
+ GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev),
+ GNUNET_GETOPT_OPTION_LOGFILE (&logfile),
+ GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION " " VCS_VERSION),
+ GNUNET_GETOPT_OPTION_END
+ };
+ err = 1;
+ do_daemonize = 0;
+ logfile = NULL;
+ loglev = NULL;
+ opt_cfg_fn = NULL;
+ xdg = getenv ("XDG_CONFIG_HOME");
+ if (NULL != xdg)
+ GNUNET_asprintf (&cfg_fn,
+ "%s%s%s",
+ xdg,
+ DIR_SEPARATOR_STR,
+ GNUNET_OS_project_data_get ()->config_file);
+ else
+ cfg_fn = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file);
+ memset (&sctx, 0, sizeof (sctx));
+ sctx.options = options;
+ sctx.ready_confirm_fd = -1;
+ sctx.ret = GNUNET_OK;
+ sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+ sctx.task = task;
+ sctx.task_cls = task_cls;
+ sctx.service_name = service_name;
+ sctx.cfg = cfg = GNUNET_CONFIGURATION_create ();
+
+ /* setup subsystems */
+ ret = GNUNET_GETOPT_run (service_name, service_options, argc, argv);
+ if (GNUNET_SYSERR == ret)
+ goto shutdown;
+ if (GNUNET_NO == ret)
+ {
+ err = 0;
+ goto shutdown;
+ }
+ if (GNUNET_OK != GNUNET_log_setup (service_name, loglev, logfile))
+ HANDLE_ERROR;
+ if (NULL == opt_cfg_fn)
+ opt_cfg_fn = GNUNET_strdup (cfg_fn);
+ if (GNUNET_YES == GNUNET_DISK_file_test (opt_cfg_fn))
+ {
+ if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, opt_cfg_fn))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Malformed configuration file `%s', exit ...\n"),
+ opt_cfg_fn);
+ goto shutdown;
+ }
+ }
+ else
+ {
+ if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Malformed configuration, exit ...\n"));
+ goto shutdown;
+ }
+ if (0 != strcmp (opt_cfg_fn, cfg_fn))
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not access configuration file `%s'\n"),
+ opt_cfg_fn);
+ }
+ if (GNUNET_OK != setup_service (&sctx))
+ goto shutdown;
+ if ((1 == do_daemonize) && (GNUNET_OK != detach_terminal (&sctx)))
+ HANDLE_ERROR;
+ if (GNUNET_OK != set_user_id (&sctx))
+ goto shutdown;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Service `%s' runs with configuration from `%s'\n",
+ service_name,
+ opt_cfg_fn);
+ if ((GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING",
+ "SKEW_OFFSET", &skew_offset)) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING",
+ "SKEW_VARIANCE", &skew_variance)))
+ {
+ clock_offset = skew_offset - skew_variance;
+ GNUNET_TIME_set_offset (clock_offset);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Skewing clock by %dll ms\n", clock_offset);
+ }
+ /* actually run service */
+ err = 0;
+ GNUNET_SCHEDULER_run (&service_task, &sctx);
+ /* shutdown */
+ if ((1 == do_daemonize) && (NULL != sctx.server))
+ pid_file_delete (&sctx);
+ GNUNET_free_non_null (sctx.my_handlers);
+
+shutdown:
+ if (-1 != sctx.ready_confirm_fd)
+ {
+ if (1 != WRITE (sctx.ready_confirm_fd, err ? "I" : "S", 1))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "write");
+ GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd));
+ }
+#if HAVE_MALLINFO
+ {
+ char *counter;
+
+ if ( (GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (sctx.cfg, service_name,
+ "GAUGER_HEAP")) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (sctx.cfg, service_name,
+ "GAUGER_HEAP",
+ &counter)) )
+ {
+ struct mallinfo mi;
+
+ mi = mallinfo ();
+ GAUGER (service_name, counter, mi.usmblks, "blocks");
+ GNUNET_free (counter);
+ }
+ }
+#endif
+ GNUNET_CONFIGURATION_destroy (cfg);
+ i = 0;
+ if (NULL != sctx.addrs)
+ while (NULL != sctx.addrs[i])
+ GNUNET_free (sctx.addrs[i++]);
+ GNUNET_free_non_null (sctx.addrs);
+ GNUNET_free_non_null (sctx.addrlens);
+ GNUNET_free_non_null (logfile);
+ GNUNET_free_non_null (loglev);
+ GNUNET_free (cfg_fn);
+ GNUNET_free_non_null (opt_cfg_fn);
+ GNUNET_free_non_null (sctx.v4_denied);
+ GNUNET_free_non_null (sctx.v6_denied);
+ GNUNET_free_non_null (sctx.v4_allowed);
+ GNUNET_free_non_null (sctx.v6_allowed);
+
+ return err ? GNUNET_SYSERR : sctx.ret;
+}
+
+
+/**
+ * Run a service startup sequence within an existing
+ * initialized system.
+ *
+ * @param service_name our service name
+ * @param cfg configuration to use
+ * @param options service options
+ * @return NULL on error, service handle
+ */
+struct GNUNET_SERVICE_Context *
+GNUNET_SERVICE_start (const char *service_name,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ enum GNUNET_SERVICE_Options options)
+{
+ int i;
+ struct GNUNET_SERVICE_Context *sctx;
+
+ sctx = GNUNET_new (struct GNUNET_SERVICE_Context);
+ sctx->ready_confirm_fd = -1; /* no daemonizing */
+ sctx->ret = GNUNET_OK;
+ sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+ sctx->service_name = service_name;
+ sctx->cfg = cfg;
+ sctx->options = options;
+
+ /* setup subsystems */
+ if (GNUNET_OK != setup_service (sctx))
+ {
+ GNUNET_SERVICE_stop (sctx);
+ return NULL;
+ }
+ if (NULL != sctx->lsocks)
+ sctx->server =
+ GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks,
+ sctx->timeout, sctx->require_found);
+ else
+ sctx->server =
+ GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens,
+ sctx->timeout, sctx->require_found);
+
+ if (NULL == sctx->server)
+ {
+ GNUNET_SERVICE_stop (sctx);
+ return NULL;
+ }
+#ifndef WINDOWS
+ if (NULL != sctx->addrs)
+ for (i = 0; NULL != sctx->addrs[i]; i++)
+ if ((AF_UNIX == sctx->addrs[i]->sa_family)
+ && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0]))
+ GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path,
+ sctx->match_uid,
+ sctx->match_gid);
+#endif
+ sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers));
+ GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers));
+ i = 0;
+ while ((sctx->my_handlers[i].callback != NULL))
+ sctx->my_handlers[i++].callback_cls = sctx;
+ GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers);
+ return sctx;
+}
+
+
+/**
+ * Obtain the server used by a service. Note that the server must NOT
+ * be destroyed by the caller.
+ *
+ * @param ctx the service context returned from the start function
+ * @return handle to the server for this service, NULL if there is none
+ */
+struct GNUNET_SERVER_Handle *
+GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx)
+{
+ return ctx->server;
+}
+
+
+/**
+ * Get the NULL-terminated array of listen sockets for this service.
+ *
+ * @param ctx service context to query
+ * @return NULL if there are no listen sockets, otherwise NULL-terminated
+ * array of listen sockets.
+ */
+struct GNUNET_NETWORK_Handle *const*
+GNUNET_SERVICE_get_listen_sockets (struct GNUNET_SERVICE_Context *ctx)
+{
+ return ctx->lsocks;
+}
+
+
+/**
+ * Stop a service that was started with "GNUNET_SERVICE_start".
+ *
+ * @param sctx the service context returned from the start function
+ */
+void
+GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx)
+{
+ unsigned int i;
+
+#if HAVE_MALLINFO
+ {
+ char *counter;
+
+ if ( (GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name,
+ "GAUGER_HEAP")) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (sctx->cfg, sctx->service_name,
+ "GAUGER_HEAP",
+ &counter)) )
+ {
+ struct mallinfo mi;
+
+ mi = mallinfo ();
+ GAUGER (sctx->service_name, counter, mi.usmblks, "blocks");
+ GNUNET_free (counter);
+ }
+ }
+#endif
+ if (NULL != sctx->shutdown_task)
+ {
+ GNUNET_SCHEDULER_cancel (sctx->shutdown_task);
+ sctx->shutdown_task = NULL;
+ }
+ if (NULL != sctx->server)
+ GNUNET_SERVER_destroy (sctx->server);
+ GNUNET_free_non_null (sctx->my_handlers);
+ if (NULL != sctx->addrs)
+ {
+ i = 0;
+ while (NULL != sctx->addrs[i])
+ GNUNET_free (sctx->addrs[i++]);
+ GNUNET_free (sctx->addrs);
+ }
+ GNUNET_free_non_null (sctx->addrlens);
+ GNUNET_free_non_null (sctx->v4_denied);
+ GNUNET_free_non_null (sctx->v6_denied);
+ GNUNET_free_non_null (sctx->v4_allowed);
+ GNUNET_free_non_null (sctx->v6_allowed);
+ GNUNET_free (sctx);
+}
+
+
+/* end of service.c */
diff --git a/src/transport/test_plugin_transport.c b/src/transport/test_plugin_transport.c
index be79d5499..1d92588ea 100644
--- a/src/transport/test_plugin_transport.c
+++ b/src/transport/test_plugin_transport.c
@@ -552,7 +552,7 @@ setup_plugin_environment ()
static int
-handle_helper_message (void *cls, void *client,
+handle_helper_message (void *cls,
const struct GNUNET_MessageHeader *hdr)
{
return GNUNET_OK;