aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/Makefile.am308
-rw-r--r--src/util/client.c526
-rw-r--r--src/util/common_allocation.c206
-rw-r--r--src/util/common_endian.c52
-rw-r--r--src/util/common_gettext.c42
-rw-r--r--src/util/common_logging.c401
-rw-r--r--src/util/configuration.c848
-rw-r--r--src/util/container_bloomfilter.c677
-rw-r--r--src/util/container_heap.c533
-rw-r--r--src/util/container_meta_data.c721
-rw-r--r--src/util/container_multihashmap.c334
-rw-r--r--src/util/crypto_aes.c148
-rw-r--r--src/util/crypto_crc.c106
-rw-r--r--src/util/crypto_hash.c791
-rw-r--r--src/util/crypto_ksk.c791
-rw-r--r--src/util/crypto_random.c136
-rw-r--r--src/util/crypto_rsa.c948
-rw-r--r--src/util/disk.c954
-rw-r--r--src/util/getopt.c1077
-rw-r--r--src/util/getopt_helpers.c200
-rw-r--r--src/util/network.c1239
-rw-r--r--src/util/os_installation.c443
-rw-r--r--src/util/os_load.c653
-rw-r--r--src/util/os_network.c286
-rw-r--r--src/util/os_priority.c186
-rw-r--r--src/util/perf_crypto_hash.c64
-rw-r--r--src/util/plugin.c243
-rw-r--r--src/util/program.c202
-rw-r--r--src/util/pseudonym.c545
-rw-r--r--src/util/scheduler.c886
-rw-r--r--src/util/server.c1091
-rw-r--r--src/util/server_tc.c198
-rw-r--r--src/util/service.c1451
-rw-r--r--src/util/signal.c76
-rw-r--r--src/util/strings.c396
-rw-r--r--src/util/test_client.c195
-rw-r--r--src/util/test_common_allocation.c112
-rw-r--r--src/util/test_common_endian.c42
-rw-r--r--src/util/test_common_logging.c100
-rw-r--r--src/util/test_configuration.c229
-rw-r--r--src/util/test_configuration_data.conf30
-rw-r--r--src/util/test_container_bloomfilter.c246
-rw-r--r--src/util/test_container_heap.c112
-rw-r--r--src/util/test_container_meta_data.c262
-rw-r--r--src/util/test_container_meta_data_image.jpgbin0 -> 136107 bytes
-rw-r--r--src/util/test_container_multihashmap.c113
-rw-r--r--src/util/test_crypto_aes.c180
-rw-r--r--src/util/test_crypto_aes_weak.c203
-rw-r--r--src/util/test_crypto_crc.c221
-rw-r--r--src/util/test_crypto_hash.c165
-rw-r--r--src/util/test_crypto_ksk.c220
-rw-r--r--src/util/test_crypto_random.c71
-rw-r--r--src/util/test_crypto_rsa.c335
-rw-r--r--src/util/test_disk.c274
-rw-r--r--src/util/test_getopt.c239
-rw-r--r--src/util/test_network.c209
-rw-r--r--src/util/test_network_addressing.c193
-rw-r--r--src/util/test_network_receive_cancel.c164
-rw-r--r--src/util/test_network_timeout.c144
-rw-r--r--src/util/test_network_timeout_no_connect.c95
-rw-r--r--src/util/test_network_transmit_cancel.c97
-rw-r--r--src/util/test_os_load.c182
-rw-r--r--src/util/test_os_network.c73
-rw-r--r--src/util/test_os_priority.c61
-rw-r--r--src/util/test_plugin.c64
-rw-r--r--src/util/test_plugin_plug.c42
-rw-r--r--src/util/test_program.c94
-rw-r--r--src/util/test_program_data.conf0
-rw-r--r--src/util/test_pseudonym.c138
-rw-r--r--src/util/test_pseudonym_data.conf8
-rw-r--r--src/util/test_scheduler.c263
-rw-r--r--src/util/test_scheduler_delay.c107
-rw-r--r--src/util/test_server.c287
-rw-r--r--src/util/test_server_disconnect.c241
-rw-r--r--src/util/test_server_with_client.c215
-rw-r--r--src/util/test_service.c337
-rw-r--r--src/util/test_service_data.conf26
-rw-r--r--src/util/test_strings.c111
-rw-r--r--src/util/test_time.c122
-rw-r--r--src/util/time.c286
80 files changed, 24666 insertions, 0 deletions
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
new file mode 100644
index 000000000..a90b2b793
--- /dev/null
+++ b/src/util/Makefile.am
@@ -0,0 +1,308 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3plugindir = $(libdir)/gnunet
4
5if MINGW
6 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -luuid -liconv -lstdc++ -lcomdlg32 -lgdi32
7endif
8
9if USE_COVERAGE
10 AM_CFLAGS = --coverage
11endif
12
13lib_LTLIBRARIES = libgnunetutil.la
14
15libgnunetutil_la_SOURCES = \
16 client.c \
17 common_allocation.c \
18 common_endian.c \
19 common_gettext.c \
20 common_logging.c \
21 configuration.c \
22 container_bloomfilter.c \
23 container_meta_data.c \
24 container_multihashmap.c \
25 crypto_aes.c \
26 crypto_crc.c \
27 crypto_hash.c \
28 crypto_ksk.c \
29 crypto_random.c \
30 crypto_rsa.c \
31 disk.c \
32 getopt.c \
33 getopt_helpers.c \
34 network.c \
35 os_installation.c \
36 os_load.c \
37 os_network.c \
38 os_priority.c \
39 plugin.c \
40 program.c \
41 pseudonym.c \
42 scheduler.c \
43 server.c \
44 server_tc.c \
45 service.c \
46 signal.c \
47 strings.c \
48 time.c
49
50
51libgnunetutil_la_LIBADD = \
52 $(GCLIBADD) \
53 $(LIBGCRYPT_LIBS) \
54 -lgmp -lltdl -lz -lextractor
55
56libgnunetutil_la_LDFLAGS = \
57 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
58 -version-info 4:0:0
59
60
61plugin_LTLIBRARIES = \
62 libgnunet_plugin_test.la
63
64libgnunet_plugin_test_la_SOURCES = \
65 test_plugin_plug.c
66libgnunet_plugin_test_la_LDFLAGS = \
67 $(GN_PLUGIN_LDFLAGS)
68
69
70check_PROGRAMS = \
71 test_client \
72 test_common_allocation \
73 test_common_endian \
74 test_common_logging \
75 test_configuration \
76 test_container_bloomfilter \
77 test_container_meta_data \
78 test_container_multihashmap \
79 test_crypto_aes \
80 test_crypto_aes_weak \
81 test_crypto_crc \
82 test_crypto_hash \
83 test_crypto_ksk \
84 test_crypto_random \
85 test_crypto_rsa \
86 test_disk \
87 test_getopt \
88 test_network \
89 test_network_addressing \
90 test_network_receive_cancel \
91 test_network_timeout \
92 test_network_timeout_no_connect \
93 test_network_transmit_cancel \
94 test_os_load \
95 test_os_network \
96 test_os_priority \
97 test_plugin \
98 test_program \
99 test_pseudonym \
100 test_scheduler \
101 test_scheduler_delay \
102 test_server \
103 test_server_disconnect \
104 test_server_with_client \
105 test_service \
106 test_strings \
107 test_time \
108 perf_crypto_hash
109
110TESTS = $(check_PROGRAMS)
111
112test_client_SOURCES = \
113 test_client.c
114test_client_LDADD = \
115 $(top_builddir)/src/util/libgnunetutil.la
116
117test_common_allocation_SOURCES = \
118 test_common_allocation.c
119test_common_allocation_LDADD = \
120 $(top_builddir)/src/util/libgnunetutil.la
121
122test_common_endian_SOURCES = \
123 test_common_endian.c
124test_common_endian_LDADD = \
125 $(top_builddir)/src/util/libgnunetutil.la
126
127test_common_logging_SOURCES = \
128 test_common_logging.c
129test_common_logging_LDADD = \
130 $(top_builddir)/src/util/libgnunetutil.la
131
132test_configuration_SOURCES = \
133 test_configuration.c
134test_configuration_LDADD = \
135 $(top_builddir)/src/util/libgnunetutil.la
136
137test_container_bloomfilter_SOURCES = \
138 test_container_bloomfilter.c
139test_container_bloomfilter_LDADD = \
140 $(top_builddir)/src/util/libgnunetutil.la
141
142test_container_meta_data_SOURCES = \
143 test_container_meta_data.c
144test_container_meta_data_LDADD = \
145 $(top_builddir)/src/util/libgnunetutil.la
146
147test_container_multihashmap_SOURCES = \
148 test_container_multihashmap.c
149test_container_multihashmap_LDADD = \
150 $(top_builddir)/src/util/libgnunetutil.la
151
152test_crypto_aes_SOURCES = \
153 test_crypto_aes.c
154test_crypto_aes_LDADD = \
155 $(top_builddir)/src/util/libgnunetutil.la
156
157test_crypto_aes_weak_SOURCES = \
158 test_crypto_aes_weak.c
159test_crypto_aes_weak_LDADD = \
160 $(top_builddir)/src/util/libgnunetutil.la
161
162test_crypto_crc_SOURCES = \
163 test_crypto_crc.c
164test_crypto_crc_LDADD = \
165 $(top_builddir)/src/util/libgnunetutil.la
166
167test_crypto_hash_SOURCES = \
168 test_crypto_hash.c
169test_crypto_hash_LDADD = \
170 $(top_builddir)/src/util/libgnunetutil.la
171
172test_crypto_ksk_SOURCES = \
173 test_crypto_ksk.c
174test_crypto_ksk_LDADD = \
175 $(top_builddir)/src/util/libgnunetutil.la
176
177test_crypto_random_SOURCES = \
178 test_crypto_random.c
179test_crypto_random_LDADD = \
180 $(top_builddir)/src/util/libgnunetutil.la
181
182test_crypto_rsa_SOURCES = \
183 test_crypto_rsa.c
184test_crypto_rsa_LDADD = \
185 $(top_builddir)/src/util/libgnunetutil.la
186
187test_disk_SOURCES = \
188 test_disk.c
189test_disk_LDADD = \
190 $(top_builddir)/src/util/libgnunetutil.la
191
192test_getopt_SOURCES = \
193 test_getopt.c
194test_getopt_LDADD = \
195 $(top_builddir)/src/util/libgnunetutil.la
196
197test_network_SOURCES = \
198 test_network.c
199test_network_LDADD = \
200 $(top_builddir)/src/util/libgnunetutil.la
201
202test_network_addressing_SOURCES = \
203 test_network_addressing.c
204test_network_addressing_LDADD = \
205 $(top_builddir)/src/util/libgnunetutil.la
206
207test_network_receive_cancel_SOURCES = \
208 test_network_receive_cancel.c
209test_network_receive_cancel_LDADD = \
210 $(top_builddir)/src/util/libgnunetutil.la
211
212test_network_timeout_SOURCES = \
213 test_network_timeout.c
214test_network_timeout_LDADD = \
215 $(top_builddir)/src/util/libgnunetutil.la
216
217test_network_timeout_no_connect_SOURCES = \
218 test_network_timeout_no_connect.c
219test_network_timeout_no_connect_LDADD = \
220 $(top_builddir)/src/util/libgnunetutil.la
221
222test_network_transmit_cancel_SOURCES = \
223 test_network_transmit_cancel.c
224test_network_transmit_cancel_LDADD = \
225 $(top_builddir)/src/util/libgnunetutil.la
226
227test_os_load_SOURCES = \
228 test_os_load.c
229test_os_load_LDADD = \
230 $(top_builddir)/src/util/libgnunetutil.la
231
232test_os_network_SOURCES = \
233 test_os_network.c
234test_os_network_LDADD = \
235 $(top_builddir)/src/util/libgnunetutil.la
236
237test_os_priority_SOURCES = \
238 test_os_priority.c
239test_os_priority_LDADD = \
240 $(top_builddir)/src/util/libgnunetutil.la
241
242test_plugin_SOURCES = \
243 test_plugin.c
244test_plugin_LDADD = \
245 $(top_builddir)/src/util/libgnunetutil.la
246
247test_program_SOURCES = \
248 test_program.c
249test_program_LDADD = \
250 $(top_builddir)/src/util/libgnunetutil.la
251
252test_pseudonym_SOURCES = \
253 test_pseudonym.c
254test_pseudonym_LDADD = \
255 $(top_builddir)/src/util/libgnunetutil.la
256
257test_scheduler_SOURCES = \
258 test_scheduler.c
259test_scheduler_LDADD = \
260 $(top_builddir)/src/util/libgnunetutil.la
261
262test_scheduler_delay_SOURCES = \
263 test_scheduler_delay.c
264test_scheduler_delay_LDADD = \
265 $(top_builddir)/src/util/libgnunetutil.la
266
267test_server_SOURCES = \
268 test_server.c
269test_server_LDADD = \
270 $(top_builddir)/src/util/libgnunetutil.la
271
272test_server_disconnect_SOURCES = \
273 test_server_disconnect.c
274test_server_disconnect_LDADD = \
275 $(top_builddir)/src/util/libgnunetutil.la
276
277test_server_with_client_SOURCES = \
278 test_server_with_client.c
279test_server_with_client_LDADD = \
280 $(top_builddir)/src/util/libgnunetutil.la
281
282test_service_SOURCES = \
283 test_service.c
284test_service_LDADD = \
285 $(top_builddir)/src/util/libgnunetutil.la
286
287test_strings_SOURCES = \
288 test_strings.c
289test_strings_LDADD = \
290 $(top_builddir)/src/util/libgnunetutil.la
291
292test_time_SOURCES = \
293 test_time.c
294test_time_LDADD = \
295 $(top_builddir)/src/util/libgnunetutil.la
296
297perf_crypto_hash_SOURCES = \
298 perf_crypto_hash.c
299perf_crypto_hash_LDADD = \
300 $(top_builddir)/src/util/libgnunetutil.la
301
302
303EXTRA_DIST = \
304 test_configuration_data.conf \
305 test_container_meta_data_image.jpg \
306 test_program_data.conf \
307 test_pseudonym_data.conf \
308 test_service_data.conf
diff --git a/src/util/client.c b/src/util/client.c
new file mode 100644
index 000000000..9dd70f266
--- /dev/null
+++ b/src/util/client.c
@@ -0,0 +1,526 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/client.c
23 * @brief code for access to services
24 * @author Christian Grothoff
25 *
26 * Generic TCP code for reliable, record-oriented TCP
27 * connections between clients and service providers.
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_client_lib.h"
33#include "gnunet_protocols.h"
34#include "gnunet_server_lib.h"
35#include "gnunet_scheduler_lib.h"
36
37#define DEBUG_CLIENT GNUNET_NO
38
39/**
40 * Struct to refer to a GNUnet TCP connection.
41 * This is more than just a socket because if the server
42 * drops the connection, the client automatically tries
43 * to reconnect (and for that needs connection information).
44 */
45struct GNUNET_CLIENT_Connection
46{
47
48 /**
49 * the socket handle, NULL if not live
50 */
51 struct GNUNET_NETWORK_SocketHandle *sock;
52
53 /**
54 * Our scheduler.
55 */
56 struct GNUNET_SCHEDULER_Handle *sched;
57
58 /**
59 * Name of the service we interact with.
60 */
61 char *service_name;
62
63 /**
64 * ID of task used for receiving.
65 */
66 GNUNET_SCHEDULER_TaskIdentifier receiver_task;
67
68 /**
69 * Handler for current receiver task.
70 */
71 GNUNET_CLIENT_MessageHandler receiver_handler;
72
73 /**
74 * Closure for receiver_handler.
75 */
76 void *receiver_handler_cls;
77
78 /**
79 * Handler for service test completion (NULL unless in service_test)
80 */
81 GNUNET_SCHEDULER_Task test_cb;
82
83 /**
84 * Closure for test_cb (NULL unless in service_test)
85 */
86 void *test_cb_cls;
87
88 /**
89 * Buffer for received message.
90 */
91 char *received_buf;
92
93 /**
94 * Timeout for receiving a response (absolute time).
95 */
96 struct GNUNET_TIME_Absolute receive_timeout;
97
98 /**
99 * Number of bytes in received_buf that are valid.
100 */
101 size_t received_pos;
102
103 /**
104 * Size of received_buf.
105 */
106 size_t received_size;
107
108 /**
109 * Do we have a complete response in received_buf?
110 */
111 int msg_complete;
112
113};
114
115
116/**
117 * Get a connection with a service.
118 *
119 * @param sched scheduler to use
120 * @param service_name name of the service
121 * @param cfg configuration to use
122 * @return NULL on error (service unknown to configuration)
123 */
124struct GNUNET_CLIENT_Connection *
125GNUNET_CLIENT_connect (struct GNUNET_SCHEDULER_Handle *sched,
126 const char *service_name,
127 struct GNUNET_CONFIGURATION_Handle *cfg)
128{
129 struct GNUNET_CLIENT_Connection *ret;
130 struct GNUNET_NETWORK_SocketHandle *sock;
131 char *hostname;
132 unsigned long long port;
133
134 if ((GNUNET_OK !=
135 GNUNET_CONFIGURATION_get_value_number (cfg,
136 service_name,
137 "PORT",
138 &port)) ||
139 (port > 65535) ||
140 (GNUNET_OK !=
141 GNUNET_CONFIGURATION_get_value_string (cfg,
142 service_name,
143 "HOSTNAME", &hostname)))
144 {
145 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
146 "Could not determine valid hostname and port for service `%s' from configuration.\n",
147 service_name);
148 return NULL;
149 }
150 sock = GNUNET_NETWORK_socket_create_from_connect (sched,
151 hostname,
152 port,
153 GNUNET_SERVER_MAX_MESSAGE_SIZE);
154 GNUNET_free (hostname);
155 if (sock == NULL)
156 return NULL;
157 ret = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_Connection));
158 ret->sock = sock;
159 ret->sched = sched;
160 ret->service_name = GNUNET_strdup (service_name);
161 return ret;
162}
163
164
165/**
166 * Receiver task has completed, free rest of client
167 * data structures.
168 */
169static void
170finish_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
171{
172 struct GNUNET_CLIENT_Connection *sock = cls;
173
174 GNUNET_array_grow (sock->received_buf, sock->received_size, 0);
175 GNUNET_free (sock->service_name);
176 GNUNET_free (sock);
177}
178
179
180/**
181 * Destroy connection with the service.
182 */
183void
184GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock)
185{
186 GNUNET_assert (sock->sock != NULL);
187 GNUNET_NETWORK_socket_destroy (sock->sock);
188 sock->sock = NULL;
189 sock->receiver_handler = NULL;
190 GNUNET_SCHEDULER_add_after (sock->sched,
191 GNUNET_YES,
192 GNUNET_SCHEDULER_PRIORITY_KEEP,
193 sock->receiver_task, &finish_cleanup, sock);
194}
195
196
197/**
198 * check if message is complete
199 */
200static void
201check_complete (struct GNUNET_CLIENT_Connection *conn)
202{
203 if ((conn->received_pos >= sizeof (struct GNUNET_MessageHeader)) &&
204 (conn->received_pos >=
205 ntohs (((const struct GNUNET_MessageHeader *) conn->received_buf)->
206 size)))
207 conn->msg_complete = GNUNET_YES;
208}
209
210
211/**
212 * Callback function for data received from the network. Note that
213 * both "available" and "err" would be 0 if the read simply timed out.
214 *
215 * @param cls closure
216 * @param buf pointer to received data
217 * @param available number of bytes availabe in "buf",
218 * possibly 0 (on errors)
219 * @param addr address of the sender
220 * @param addrlen size of addr
221 * @param errCode value of errno (on errors receiving)
222 */
223static void
224receive_helper (void *cls,
225 const void *buf,
226 size_t available,
227 const struct sockaddr *addr, socklen_t addrlen, int errCode)
228{
229 struct GNUNET_CLIENT_Connection *conn = cls;
230 struct GNUNET_TIME_Relative remaining;
231
232 GNUNET_assert (conn->msg_complete == GNUNET_NO);
233 conn->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
234
235 if ((available == 0) || (conn->sock == NULL) || (errCode != 0))
236 {
237 /* signal timeout! */
238 if (conn->receiver_handler != NULL)
239 {
240 conn->receiver_handler (conn->receiver_handler_cls, NULL);
241 conn->receiver_handler = NULL;
242 }
243 return;
244 }
245
246 /* FIXME: optimize for common fast case where buf contains the
247 entire message and we need no copying... */
248
249
250 /* slow path: append to array */
251 if (conn->received_size < conn->received_pos + available)
252 GNUNET_array_grow (conn->received_buf,
253 conn->received_size, conn->received_pos + available);
254 memcpy (&conn->received_buf[conn->received_pos], buf, available);
255 conn->received_pos += available;
256 check_complete (conn);
257 /* check for timeout */
258 remaining = GNUNET_TIME_absolute_get_remaining (conn->receive_timeout);
259 if (remaining.value == 0)
260 {
261 /* signal timeout! */
262 conn->receiver_handler (conn->receiver_handler_cls, NULL);
263 return;
264 }
265 /* back to receive -- either for more data or to call callback! */
266 GNUNET_CLIENT_receive (conn,
267 conn->receiver_handler,
268 conn->receiver_handler_cls, remaining);
269}
270
271
272/**
273 * Continuation to call the receive callback.
274 */
275static void
276receive_task (void *scls, const struct GNUNET_SCHEDULER_TaskContext *tc)
277{
278 struct GNUNET_CLIENT_Connection *sock = scls;
279 const struct GNUNET_MessageHeader *cmsg;
280 struct GNUNET_MessageHeader *msg;
281 GNUNET_CLIENT_MessageHandler handler = sock->receiver_handler;
282 void *cls = sock->receiver_handler_cls;
283 uint16_t msize;
284
285 GNUNET_assert (GNUNET_YES == sock->msg_complete);
286 sock->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
287 cmsg = (const struct GNUNET_MessageHeader *) sock->received_buf;
288 msize = ntohs (cmsg->size);
289 GNUNET_assert (sock->received_pos >= msize);
290 msg = GNUNET_malloc (msize);
291 memcpy (msg, cmsg, msize);
292 memmove (sock->received_buf,
293 &sock->received_buf[msize], sock->received_pos - msize);
294 sock->received_pos -= msize;
295 sock->msg_complete = GNUNET_NO;
296 sock->receiver_handler = NULL;
297 check_complete (sock);
298 handler (cls, msg);
299 GNUNET_free (msg);
300}
301
302
303/**
304 * Read from the service.
305 *
306 * @param sched scheduler to use
307 * @param sock the service
308 * @param handler function to call with the message
309 * @param cls closure for handler
310 * @param timeout how long to wait until timing out
311 */
312void
313GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock,
314 GNUNET_CLIENT_MessageHandler handler,
315 void *cls, struct GNUNET_TIME_Relative timeout)
316{
317 if (sock->sock == NULL)
318 {
319 /* already disconnected, fail instantly! */
320 GNUNET_break (0); /* this should not happen in well-written code! */
321 handler (cls, NULL);
322 return;
323 }
324 GNUNET_assert (sock->receiver_task ==
325 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
326 sock->receiver_handler = handler;
327 sock->receiver_handler_cls = cls;
328 sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
329 if (GNUNET_YES == sock->msg_complete)
330 sock->receiver_task = GNUNET_SCHEDULER_add_after (sock->sched,
331 GNUNET_YES,
332 GNUNET_SCHEDULER_PRIORITY_KEEP,
333 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
334 &receive_task, sock);
335 else
336 sock->receiver_task = GNUNET_NETWORK_receive (sock->sock,
337 GNUNET_SERVER_MAX_MESSAGE_SIZE,
338 timeout,
339 &receive_helper, sock);
340}
341
342
343static size_t
344write_shutdown (void *cls, size_t size, void *buf)
345{
346 struct GNUNET_MessageHeader *msg;
347
348 if (size < sizeof (struct GNUNET_MessageHeader))
349 return 0; /* client disconnected */
350 msg = (struct GNUNET_MessageHeader *) buf;
351 msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN);
352 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
353 return sizeof (struct GNUNET_MessageHeader);
354}
355
356
357/**
358 * Request that the service should shutdown.
359 * Afterwards, the connection should be disconnected.
360 *
361 * @param sched scheduler to use
362 * @param sock the socket connected to the service
363 */
364void
365GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock)
366{
367 GNUNET_NETWORK_notify_transmit_ready (sock->sock,
368 sizeof (struct GNUNET_MessageHeader),
369 GNUNET_TIME_UNIT_FOREVER_REL,
370 &write_shutdown, NULL);
371}
372
373
374/**
375 * Report service unavailable.
376 */
377static void
378service_test_error (struct GNUNET_SCHEDULER_Handle *s,
379 GNUNET_SCHEDULER_Task task, void *task_cls)
380{
381 GNUNET_SCHEDULER_add_continuation (s,
382 GNUNET_YES,
383 task,
384 task_cls,
385 GNUNET_SCHEDULER_REASON_TIMEOUT);
386}
387
388
389/**
390 * Receive confirmation from test, service is up.
391 *
392 * @param cls closure
393 * @param msg message received, NULL on timeout or fatal error
394 */
395static void
396confirm_handler (void *cls, const struct GNUNET_MessageHeader *msg)
397{
398 struct GNUNET_CLIENT_Connection *conn = cls;
399 /* We may want to consider looking at the reply in more
400 detail in the future, for example, is this the
401 correct service? FIXME! */
402 if (msg != NULL)
403 {
404#if DEBUG_CLIENT
405 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406 "Received confirmation that service is running.\n");
407#endif
408 GNUNET_SCHEDULER_add_continuation (conn->sched,
409 GNUNET_YES,
410 conn->test_cb,
411 conn->test_cb_cls,
412 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
413 }
414 else
415 {
416 service_test_error (conn->sched, conn->test_cb, conn->test_cb_cls);
417 }
418 GNUNET_CLIENT_disconnect (conn);
419}
420
421
422static size_t
423write_test (void *cls, size_t size, void *buf)
424{
425 struct GNUNET_MessageHeader *msg;
426
427 if (size < sizeof (struct GNUNET_MessageHeader))
428 {
429#if DEBUG_CLIENT
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 _("Failure to transmit TEST request.\n"));
432#endif
433 return 0; /* client disconnected */
434 }
435#if DEBUG_CLIENT
436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Transmitting TEST request.\n"));
437#endif
438 msg = (struct GNUNET_MessageHeader *) buf;
439 msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
440 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
441 return sizeof (struct GNUNET_MessageHeader);
442}
443
444
445/**
446 * Wait until the service is running.
447 *
448 * @param sched scheduler to use
449 * @param service name of the service to wait for
450 * @param cfg configuration to use
451 * @param timeout how long to wait at most in ms
452 * @param task task to run if service is running
453 * (reason will be "PREREQ_DONE" (service running)
454 * or "TIMEOUT" (service not known to be running))
455 * @param task_cls closure for task
456 */
457void
458GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched,
459 const char *service,
460 struct GNUNET_CONFIGURATION_Handle *cfg,
461 struct GNUNET_TIME_Relative timeout,
462 GNUNET_SCHEDULER_Task task, void *task_cls)
463{
464 struct GNUNET_CLIENT_Connection *conn;
465
466#if DEBUG_CLIENT
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468 "Testing if service `%s' is running.\n", service);
469#endif
470 conn = GNUNET_CLIENT_connect (sched, service, cfg);
471 if (conn == NULL)
472 {
473 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
474 _
475 ("Could not connect to service `%s', must not be running.\n"),
476 service);
477 service_test_error (sched, task, task_cls);
478 return;
479 }
480 conn->test_cb = task;
481 conn->test_cb_cls = task_cls;
482 if (NULL ==
483 GNUNET_NETWORK_notify_transmit_ready (conn->sock,
484 sizeof (struct
485 GNUNET_MessageHeader),
486 timeout, &write_test, NULL))
487 {
488 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
489 _("Failure to transmit request to service `%s'\n"),
490 service);
491 service_test_error (sched, task, task_cls);
492 GNUNET_CLIENT_disconnect (conn);
493 return;
494 }
495 GNUNET_CLIENT_receive (conn, &confirm_handler, conn, timeout);
496}
497
498
499/**
500 * Ask the client to call us once the specified number of bytes
501 * are free in the transmission buffer. May call the notify
502 * method immediately if enough space is available.
503 *
504 * @param client connection to the service
505 * @param size number of bytes to send
506 * @param timeout after how long should we give up (and call
507 * notify with buf NULL and size 0)?
508 * @param notify function to call
509 * @param notify_cls closure for notify
510 * @return NULL if our buffer will never hold size bytes,
511 * a handle if the notify callback was queued (can be used to cancel)
512 */
513struct GNUNET_NETWORK_TransmitHandle *
514GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock,
515 size_t size,
516 struct GNUNET_TIME_Relative timeout,
517 GNUNET_NETWORK_TransmitReadyNotify
518 notify, void *notify_cls)
519{
520 return GNUNET_NETWORK_notify_transmit_ready (sock->sock,
521 size,
522 timeout, notify, notify_cls);
523}
524
525
526/* end of client.c */
diff --git a/src/util/common_allocation.c b/src/util/common_allocation.c
new file mode 100644
index 000000000..9fabb3a32
--- /dev/null
+++ b/src/util/common_allocation.c
@@ -0,0 +1,206 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/memory/common_allocation.c
23 * @brief wrapper around malloc/free
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29
30#ifndef INT_MAX
31#define INT_MAX 0x7FFFFFFF
32#endif
33
34/**
35 * Allocate memory. Checks the return value, aborts if no more
36 * memory is available.
37 *
38 * @param size how many bytes of memory to allocate, do NOT use
39 * this function (or GNUNET_malloc) to allocate more than several MB
40 * of memory, if you are possibly needing a very large chunk use
41 * GNUNET_xmalloc_unchecked_ instead.
42 * @param filename where in the code was the call to GNUNET_array_grow
43 * @param linenumber where in the code was the call to GNUNET_array_grow
44 * @return pointer to size bytes of memory
45 */
46void *
47GNUNET_xmalloc_ (size_t size, const char *filename, int linenumber)
48{
49 /* As a security precaution, we generally do not allow very large
50 allocations using the default 'GNUNET_malloc' macro */
51 GNUNET_assert_at (size <= GNUNET_MAX_GNUNET_MALLOC_CHECKED, filename,
52 linenumber);
53 return GNUNET_xmalloc_unchecked_ (size, filename, linenumber);
54}
55
56void *
57GNUNET_xmalloc_unchecked_ (size_t size, const char *filename, int linenumber)
58{
59 void *result;
60
61 GNUNET_assert_at (size < INT_MAX, filename, linenumber);
62 result = malloc (size);
63 if (result == NULL)
64 {
65 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc");
66 abort ();
67 }
68 memset (result, 0, size);
69 return result;
70}
71
72/**
73 * Reallocate memory. Checks the return value, aborts if no more
74 * memory is available.
75 *
76 * @ptr the pointer to reallocate
77 * @param size how many bytes of memory to allocate, do NOT use
78 * this function (or GNUNET_malloc) to allocate more than several MB
79 * of memory
80 * @param filename where in the code was the call to GNUNET_realloc
81 * @param linenumber where in the code was the call to GNUNET_realloc
82 * @return pointer to size bytes of memory
83 */
84void *
85GNUNET_xrealloc_ (void *ptr,
86 const size_t n, const char *filename, int linenumber)
87{
88 ptr = realloc (ptr, n);
89 if (!ptr)
90 {
91 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "realloc");
92 abort ();
93 }
94 return ptr;
95}
96
97/**
98 * Free memory. Merely a wrapper for the case that we
99 * want to keep track of allocations.
100 *
101 * @param ptr the pointer to free
102 * @param filename where in the code was the call to GNUNET_array_grow
103 * @param linenumber where in the code was the call to GNUNET_array_grow
104 */
105void
106GNUNET_xfree_ (void *ptr, const char *filename, int linenumber)
107{
108 GNUNET_assert_at (ptr != NULL, filename, linenumber);
109 free (ptr);
110}
111
112/**
113 * Dup a string (same semantics as strdup).
114 *
115 * @param str the string to dup
116 * @param filename where in the code was the call to GNUNET_array_grow
117 * @param linenumber where in the code was the call to GNUNET_array_grow
118 * @return strdup(str)
119 */
120char *
121GNUNET_xstrdup_ (const char *str, const char *filename, int linenumber)
122{
123 char *res;
124
125 GNUNET_assert_at (str != NULL, filename, linenumber);
126 res = GNUNET_xmalloc_ (strlen (str) + 1, filename, linenumber);
127 memcpy (res, str, strlen (str) + 1);
128 return res;
129}
130
131/**
132 * Grow an array. Grows old by (*oldCount-newCount)*elementSize bytes
133 * and sets *oldCount to newCount.
134 *
135 * @param old address of the pointer to the array
136 * *old may be NULL
137 * @param elementSize the size of the elements of the array
138 * @param oldCount address of the number of elements in the *old array
139 * @param newCount number of elements in the new array, may be 0
140 * @param filename where in the code was the call to GNUNET_array_grow
141 * @param linenumber where in the code was the call to GNUNET_array_grow
142 */
143void
144GNUNET_xgrow_ (void **old,
145 size_t elementSize,
146 unsigned int *oldCount,
147 unsigned int newCount, const char *filename, int linenumber)
148{
149 void *tmp;
150 size_t size;
151
152 GNUNET_assert_at (INT_MAX / elementSize > newCount, filename, linenumber);
153 size = newCount * elementSize;
154 if (size == 0)
155 {
156 tmp = NULL;
157 }
158 else
159 {
160 tmp = GNUNET_xmalloc_ (size, filename, linenumber);
161 memset (tmp, 0, size); /* client code should not rely on this, though... */
162 if (*oldCount > newCount)
163 *oldCount = newCount; /* shrink is also allowed! */
164 memcpy (tmp, *old, elementSize * (*oldCount));
165 }
166
167 if (*old != NULL)
168 {
169 GNUNET_xfree_ (*old, filename, linenumber);
170 }
171 *old = tmp;
172 *oldCount = newCount;
173}
174
175
176int
177GNUNET_asprintf (char **buf, const char *format, ...)
178{
179 int ret;
180 va_list args;
181
182 va_start (args, format);
183 ret = VSNPRINTF (NULL, 0, format, args);
184 va_end (args);
185 *buf = GNUNET_malloc (ret + 1);
186 va_start (args, format);
187 ret = VSPRINTF (*buf, format, args);
188 va_end (args);
189 return ret;
190}
191
192int
193GNUNET_snprintf (char *buf, size_t size, const char *format, ...)
194{
195 int ret;
196 va_list args;
197
198 va_start (args, format);
199 ret = VSNPRINTF (buf, size, format, args);
200 va_end (args);
201 GNUNET_assert (ret <= size);
202 return ret;
203}
204
205
206/* end of common_allocation.c */
diff --git a/src/util/common_endian.c b/src/util/common_endian.c
new file mode 100644
index 000000000..223144c40
--- /dev/null
+++ b/src/util/common_endian.c
@@ -0,0 +1,52 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/common_endian.c
23 * @brief endian conversion helpers
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29
30unsigned long long
31GNUNET_ntohll (unsigned long long n)
32{
33#if __BYTE_ORDER == __BIG_ENDIAN
34 return n;
35#else
36 return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
37#endif
38}
39
40unsigned long long
41GNUNET_htonll (unsigned long long n)
42{
43#if __BYTE_ORDER == __BIG_ENDIAN
44 return n;
45#else
46 return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
47#endif
48}
49
50
51
52/* end of common_endian.c */
diff --git a/src/util/common_gettext.c b/src/util/common_gettext.c
new file mode 100644
index 000000000..ad347e160
--- /dev/null
+++ b/src/util/common_gettext.c
@@ -0,0 +1,42 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/common_gettext.c
23 * @brief gettext init routine
24 * @author Heikki Lindholm
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_os_lib.h"
29
30void __attribute__ ((constructor)) GNUNET_util_generic_ltdl_init ()
31{
32#if ENABLE_NLS
33 char *path;
34
35 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
36 if (path != NULL)
37 {
38 BINDTEXTDOMAIN ("GNUnet", path);
39 GNUNET_free (path);
40 }
41#endif
42}
diff --git a/src/util/common_logging.c b/src/util/common_logging.c
new file mode 100644
index 000000000..4068ed94b
--- /dev/null
+++ b/src/util/common_logging.c
@@ -0,0 +1,401 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/common_logging.c
23 * @brief error handling API
24 *
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_crypto_lib.h"
30#include "gnunet_strings_lib.h"
31#include "gnunet_time_lib.h"
32
33/**
34 * After how many seconds do we always print
35 * that "message X was repeated N times"? Use 12h.
36 */
37#define BULK_DELAY_THRESHOLD (12 * 60 * 60 * 1000)
38
39/**
40 * After how many repetitions do we always print
41 * that "message X was repeated N times"? (even if
42 * we have not yet reached the delay threshold)
43 */
44#define BULK_REPEAT_THRESHOLD 1000
45
46/**
47 * How many characters do we use for matching of
48 * bulk messages?
49 */
50#define BULK_TRACK_SIZE 256
51
52/**
53 * How many characters can a date/time string
54 * be at most?
55 */
56#define DATE_STR_SIZE 64
57
58/**
59 * Linked list of active loggers.
60 */
61struct CustomLogger
62{
63 /**
64 * This is a linked list.
65 */
66 struct CustomLogger *next;
67
68 /**
69 * Log function.
70 */
71 GNUNET_Logger logger;
72
73 /**
74 * Closure for logger.
75 */
76 void *logger_cls;
77};
78
79/**
80 * The last "bulk" error message that we have been logging.
81 * Note that this message maybe truncated to the first BULK_TRACK_SIZE
82 * characters, in which case it is NOT 0-terminated!
83 */
84static char last_bulk[BULK_TRACK_SIZE];
85
86/**
87 * Type of the last bulk message.
88 */
89static enum GNUNET_ErrorType last_bulk_kind;
90
91/**
92 * Time of the last bulk error message (0 for none)
93 */
94static struct GNUNET_TIME_Absolute last_bulk_time;
95
96/**
97 * Number of times that bulk message has been repeated since.
98 */
99static unsigned int last_bulk_repeat;
100
101/**
102 * Component when the last bulk was logged.
103 */
104static const char *last_bulk_comp;
105
106/**
107 * Running component.
108 */
109static const char *component;
110
111/**
112 * Minimum log level.
113 */
114static enum GNUNET_ErrorType min_level;
115
116/**
117 * Linked list of our custom loggres.
118 */
119static struct CustomLogger *loggers;
120
121/**
122 * Number of log calls to ignore.
123 */
124static unsigned int skip_log;
125
126/**
127 * Convert a textual description of a loglevel
128 * to the respective GNUNET_GE_KIND.
129 * @returns GNUNET_GE_INVALID if log does not parse
130 */
131static enum GNUNET_ErrorType
132get_type (const char *log)
133{
134 if (0 == strcasecmp (log, _("DEBUG")))
135 return GNUNET_ERROR_TYPE_DEBUG;
136 if (0 == strcasecmp (log, _("INFO")))
137 return GNUNET_ERROR_TYPE_INFO;
138 if (0 == strcasecmp (log, _("WARNING")))
139 return GNUNET_ERROR_TYPE_WARNING;
140 if (0 == strcasecmp (log, _("ERROR")))
141 return GNUNET_ERROR_TYPE_ERROR;
142 return GNUNET_ERROR_TYPE_INVALID;
143}
144
145/**
146 * Setup logging.
147 *
148 * @param comp default component to use
149 * @param loglevel what types of messages should be logged
150 */
151int
152GNUNET_log_setup (const char *comp, const char *loglevel, const char *logfile)
153{
154 FILE *altlog;
155
156 component = comp;
157 min_level = get_type (loglevel);
158 if (logfile == NULL)
159 return GNUNET_OK;
160 altlog = fopen (logfile, "a");
161 if (altlog == NULL)
162 {
163 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", logfile);
164 return GNUNET_SYSERR;
165 }
166 if (stderr != NULL)
167 fclose (stderr);
168 stderr = altlog;
169 return GNUNET_OK;
170}
171
172/**
173 * Add a custom logger.
174 *
175 * @param logger log function
176 * @param logger_cls closure for logger
177 */
178void
179GNUNET_logger_add (GNUNET_Logger logger, void *logger_cls)
180{
181 struct CustomLogger *entry;
182
183 entry = GNUNET_malloc (sizeof (struct CustomLogger));
184 entry->logger = logger;
185 entry->logger_cls = logger_cls;
186 entry->next = loggers;
187 loggers = entry;
188}
189
190/**
191 * Remove a custom logger.
192 *
193 * @param logger log function
194 * @param logger_cls closure for logger
195 */
196void
197GNUNET_logger_remove (GNUNET_Logger logger, void *logger_cls)
198{
199 struct CustomLogger *pos;
200 struct CustomLogger *prev;
201
202 prev = NULL;
203 pos = loggers;
204 while ((pos != NULL) &&
205 ((pos->logger != logger) || (pos->logger_cls != logger_cls)))
206 {
207 prev = pos;
208 pos = pos->next;
209 }
210 GNUNET_assert (pos != NULL);
211 if (prev == NULL)
212 loggers = pos->next;
213 else
214 prev->next = pos->next;
215 GNUNET_free (pos);
216}
217
218static void
219output_message (enum GNUNET_ErrorType kind,
220 const char *comp, const char *datestr, const char *msg)
221{
222 struct CustomLogger *pos;
223 if (stderr != NULL)
224 fprintf (stderr, "%s %s %s %s", datestr, comp,
225 GNUNET_error_type_to_string (kind), msg);
226 pos = loggers;
227 while (pos != NULL)
228 {
229 pos->logger (pos->logger_cls, kind, comp, datestr, msg);
230 pos = pos->next;
231 }
232}
233
234static void
235flush_bulk (const char *datestr)
236{
237 char msg[DATE_STR_SIZE + BULK_TRACK_SIZE + 256];
238 int rev;
239 char *last;
240 char *ft;
241
242 if ((last_bulk_time.value == 0) || (last_bulk_repeat == 0))
243 return;
244 rev = 0;
245 last = memchr (last_bulk, '\0', BULK_TRACK_SIZE);
246 if (last == NULL)
247 last = &last_bulk[BULK_TRACK_SIZE - 1];
248 else if (last != last_bulk)
249 last--;
250 if (last[0] == '\n')
251 {
252 rev = 1;
253 last[0] = '\0';
254 }
255 ft =
256 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration
257 (last_bulk_time));
258 snprintf (msg, sizeof (msg),
259 _("Message `%.*s' repeated %u times in the last %s\n"),
260 BULK_TRACK_SIZE, last_bulk, last_bulk_repeat, ft);
261 GNUNET_free (ft);
262 if (rev == 1)
263 last[0] = '\n';
264 output_message (last_bulk_kind, last_bulk_comp, datestr, msg);
265 last_bulk_time = GNUNET_TIME_absolute_get ();
266 last_bulk_repeat = 0;
267}
268
269
270/**
271 * Ignore the next n calls to the log function.
272 *
273 * @param n number of log calls to ignore
274 */
275void
276GNUNET_log_skip (unsigned int n)
277{
278 int ok;
279
280 if (n == 0)
281 {
282 ok = (0 == skip_log);
283 skip_log = 0;
284 GNUNET_assert (ok);
285 }
286 skip_log += n;
287}
288
289
290static void
291mylog (enum GNUNET_ErrorType kind,
292 const char *comp, const char *message, va_list va)
293{
294 char date[DATE_STR_SIZE];
295 time_t timetmp;
296 struct tm *tmptr;
297 size_t size;
298 char *buf;
299 va_list vacp;
300
301 if (skip_log > 0)
302 {
303 skip_log--;
304 return;
305 }
306 if ((kind & (~GNUNET_ERROR_TYPE_BULK)) > min_level)
307 return;
308 va_copy (vacp, va);
309 size = VSNPRINTF (NULL, 0, message, vacp) + 1;
310 va_end (vacp);
311 buf = malloc (size);
312 if (buf == NULL)
313 return; /* oops */
314 VSNPRINTF (buf, size, message, va);
315 time (&timetmp);
316 memset (date, 0, DATE_STR_SIZE);
317 tmptr = localtime (&timetmp);
318 strftime (date, DATE_STR_SIZE, "%b %d %H:%M:%S", tmptr);
319 if ((0 != (kind & GNUNET_ERROR_TYPE_BULK)) &&
320 (last_bulk_time.value != 0) &&
321 (0 == strncmp (buf, last_bulk, sizeof (last_bulk))))
322 {
323 last_bulk_repeat++;
324 if ((GNUNET_TIME_absolute_get_duration (last_bulk_time).value >
325 BULK_DELAY_THRESHOLD)
326 || (last_bulk_repeat > BULK_REPEAT_THRESHOLD))
327 flush_bulk (date);
328 free (buf);
329 return;
330 }
331 flush_bulk (date);
332 strncpy (last_bulk, buf, sizeof (last_bulk));
333 last_bulk_repeat = 0;
334 last_bulk_kind = kind;
335 last_bulk_time = GNUNET_TIME_absolute_get ();
336 last_bulk_comp = comp;
337 output_message (kind, comp, date, buf);
338 free (buf);
339}
340
341
342void
343GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...)
344{
345 va_list va;
346 va_start (va, message);
347 mylog (kind, component, message, va);
348 va_end (va);
349}
350
351
352void
353GNUNET_log_from (enum GNUNET_ErrorType kind,
354 const char *comp, const char *message, ...)
355{
356 va_list va;
357 va_start (va, message);
358 mylog (kind, comp, message, va);
359 va_end (va);
360}
361
362
363/**
364 * Convert KIND to String
365 */
366const char *
367GNUNET_error_type_to_string (enum GNUNET_ErrorType kind)
368{
369 if ((kind & GNUNET_ERROR_TYPE_ERROR) > 0)
370 return _("ERROR");
371 if ((kind & GNUNET_ERROR_TYPE_WARNING) > 0)
372 return _("WARNING");
373 if ((kind & GNUNET_ERROR_TYPE_INFO) > 0)
374 return _("INFO");
375 if ((kind & GNUNET_ERROR_TYPE_DEBUG) > 0)
376 return _("DEBUG");
377 return _("INVALID");
378}
379
380
381/**
382 * Convert a peer identity to a string (for printing debug messages).
383 * This is one of the very few calls in the entire API that is
384 * NOT reentrant!
385 *
386 * @param pid the peer identity
387 * @return string form of the pid; will be overwritten by next
388 * call to GNUNET_i2s.
389 */
390const char *
391GNUNET_i2s (const struct GNUNET_PeerIdentity *pid)
392{
393 static struct GNUNET_CRYPTO_HashAsciiEncoded ret;
394 GNUNET_CRYPTO_hash_to_enc (&pid->hashPubKey, &ret);
395 ret.encoding[4] = '\0';
396 return (const char *) ret.encoding;
397}
398
399
400
401/* end of common_logging.c */
diff --git a/src/util/configuration.c b/src/util/configuration.c
new file mode 100644
index 000000000..4413b0377
--- /dev/null
+++ b/src/util/configuration.c
@@ -0,0 +1,848 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/util/configuration.c
23 * @brief configuration management
24 *
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_configuration_lib.h"
31#include "gnunet_crypto_lib.h"
32#include "gnunet_disk_lib.h"
33#include "gnunet_os_lib.h"
34#include "gnunet_strings_lib.h"
35
36/**
37 * @brief configuration entry
38 */
39struct ConfigEntry
40{
41
42 /**
43 * This is a linked list.
44 */
45 struct ConfigEntry *next;
46
47 /**
48 * key for this entry
49 */
50 char *key;
51
52 /**
53 * current, commited value
54 */
55 char *val;
56};
57
58/**
59 * @brief configuration section
60 */
61struct ConfigSection
62{
63 /**
64 * This is a linked list.
65 */
66 struct ConfigSection *next;
67
68 /**
69 * entries in the section
70 */
71 struct ConfigEntry *entries;
72
73 /**
74 * name of the section
75 */
76 char *name;
77};
78
79/**
80 * @brief configuration data
81 */
82struct GNUNET_CONFIGURATION_Handle
83{
84 /**
85 * Configuration sections.
86 */
87 struct ConfigSection *sections;
88
89 /**
90 * Modification indication since last save
91 * GNUNET_NO if clean, GNUNET_YES if dirty,
92 * GNUNET_SYSERR on error (i.e. last save failed)
93 */
94 int dirty;
95
96};
97
98/**
99 * Create a GNUNET_CONFIGURATION_Configuration.
100 */
101struct GNUNET_CONFIGURATION_Handle *
102GNUNET_CONFIGURATION_create ()
103{
104 return GNUNET_malloc (sizeof (struct GNUNET_CONFIGURATION_Handle));
105}
106
107void
108GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg)
109{
110 struct ConfigSection *sec;
111 struct ConfigEntry *ent;
112
113 while (NULL != (sec = cfg->sections))
114 {
115 cfg->sections = sec->next;
116 while (NULL != (ent = sec->entries))
117 {
118 sec->entries = ent->next;
119 GNUNET_free (ent->key);
120 GNUNET_free_non_null (ent->val);
121 GNUNET_free (ent);
122 }
123 GNUNET_free (sec->name);
124 GNUNET_free (sec);
125 }
126 GNUNET_free (cfg);
127}
128
129int
130GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg,
131 const char *filename)
132{
133 int dirty;
134 char line[256];
135 char tag[64];
136 char value[192];
137 FILE *fp;
138 unsigned int nr;
139 int i;
140 int emptyline;
141 int ret;
142 char *section;
143 char *fn;
144
145 fn = GNUNET_STRINGS_filename_expand (filename);
146 dirty = cfg->dirty; /* back up value! */
147 if (NULL == (fp = FOPEN (fn, "r")))
148 {
149 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", fn);
150 GNUNET_free (fn);
151 return GNUNET_SYSERR;
152 }
153 GNUNET_free (fn);
154 ret = GNUNET_OK;
155 section = GNUNET_strdup ("");
156 memset (line, 0, 256);
157 nr = 0;
158 while (NULL != fgets (line, 255, fp))
159 {
160 nr++;
161 for (i = 0; i < 255; i++)
162 if (line[i] == '\t')
163 line[i] = ' ';
164 if (line[0] == '\n' || line[0] == '#' || line[0] == '%' ||
165 line[0] == '\r')
166 continue;
167 emptyline = 1;
168 for (i = 0; (i < 255 && line[i] != 0); i++)
169 if (line[i] != ' ' && line[i] != '\n' && line[i] != '\r')
170 emptyline = 0;
171 if (emptyline == 1)
172 continue;
173 /* remove tailing whitespace */
174 for (i = strlen (line) - 1; (i >= 0) && (isspace (line[i])); i--)
175 line[i] = '\0';
176 if (1 == sscanf (line, "@INLINE@ %191[^\n]", value))
177 {
178 /* @INLINE@ value */
179 if (0 != GNUNET_CONFIGURATION_parse (cfg, value))
180 ret = GNUNET_SYSERR; /* failed to parse included config */
181 }
182 else if (1 == sscanf (line, "[%99[^]]]", value))
183 {
184 /* [value] */
185 GNUNET_free (section);
186 section = GNUNET_strdup (value);
187 }
188 else if (2 == sscanf (line, " %63[^= ] = %191[^\n]", tag, value))
189 {
190 /* tag = value */
191 /* Strip LF */
192 i = strlen (value) - 1;
193 while ((i >= 0) && (isspace (value[i])))
194 value[i--] = '\0';
195 /* remove quotes */
196 i = 0;
197 if (value[0] == '"')
198 {
199 i = 1;
200 while ((value[i] != '\0') && (value[i] != '"'))
201 i++;
202 if (value[i] == '"')
203 {
204 value[i] = '\0';
205 i = 1;
206 }
207 else
208 i = 0;
209 }
210 GNUNET_CONFIGURATION_set_value_string (cfg,
211 section, tag, &value[i]);
212 }
213 else if (1 == sscanf (line, " %63[^= ] =[^\n]", tag))
214 {
215 /* tag = */
216 GNUNET_CONFIGURATION_set_value_string (cfg, section, tag, "");
217 }
218 else
219 {
220 /* parse error */
221 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
222 _
223 ("Syntax error in configuration file `%s' at line %u.\n"),
224 filename, nr);
225 ret = GNUNET_SYSERR;
226 break;
227 }
228 }
229 GNUNET_assert (0 == fclose (fp));
230 /* restore dirty flag - anything we set in the meantime
231 came from disk */
232 cfg->dirty = dirty;
233 GNUNET_free (section);
234 return ret;
235}
236
237int
238GNUNET_CONFIGURATION_test_dirty (struct GNUNET_CONFIGURATION_Handle *cfg)
239{
240 return cfg->dirty;
241}
242
243int
244GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *data,
245 const char *filename)
246{
247 struct ConfigSection *sec;
248 struct ConfigEntry *ent;
249 FILE *fp;
250 int error;
251 char *fn;
252 char *val;
253 char *pos;
254
255 fn = GNUNET_STRINGS_filename_expand (filename);
256 GNUNET_DISK_directory_create_for_file (fn);
257 if (NULL == (fp = FOPEN (fn, "w")))
258 {
259 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", fn);
260 GNUNET_free (fn);
261 return GNUNET_SYSERR;
262 }
263 GNUNET_free (fn);
264 error = 0;
265 sec = data->sections;
266 while (sec != NULL)
267 {
268 if (0 > fprintf (fp, "[%s]\n", sec->name))
269 {
270 error = 1;
271 break;
272 }
273 ent = sec->entries;
274 while (ent != NULL)
275 {
276 if (ent->val != NULL)
277 {
278 val = GNUNET_malloc (strlen (ent->val) * 2 + 1);
279 strcpy (val, ent->val);
280 while (NULL != (pos = strstr (val, "\n")))
281 {
282 memmove (&pos[2], &pos[1], strlen (&pos[1]));
283 pos[0] = '\\';
284 pos[1] = 'n';
285 }
286 if (0 > fprintf (fp, "%s = %s\n", ent->key, val))
287 {
288 error = 1;
289 GNUNET_free (val);
290 break;
291 }
292 GNUNET_free (val);
293 }
294 ent = ent->next;
295 }
296 if (error != 0)
297 break;
298 if (0 > fprintf (fp, "\n"))
299 {
300 error = 1;
301 break;
302 }
303 sec = sec->next;
304 }
305 if (error != 0)
306 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fprintf", filename);
307 GNUNET_assert (0 == fclose (fp));
308 if (error != 0)
309 {
310 data->dirty = GNUNET_SYSERR; /* last write failed */
311 return GNUNET_SYSERR;
312 }
313 data->dirty = GNUNET_NO; /* last write succeeded */
314 return GNUNET_OK;
315}
316
317
318static struct ConfigSection *
319findSection (struct GNUNET_CONFIGURATION_Handle *data, const char *section)
320{
321 struct ConfigSection *pos;
322
323 pos = data->sections;
324 while ((pos != NULL) && (0 != strcasecmp (section, pos->name)))
325 pos = pos->next;
326 return pos;
327}
328
329
330static struct ConfigEntry *
331findEntry (struct GNUNET_CONFIGURATION_Handle *data,
332 const char *section, const char *key)
333{
334 struct ConfigSection *sec;
335 struct ConfigEntry *pos;
336
337 sec = findSection (data, section);
338 if (sec == NULL)
339 return NULL;
340 pos = sec->entries;
341 while ((pos != NULL) && (0 != strcasecmp (key, pos->key)))
342 pos = pos->next;
343 return pos;
344}
345
346void
347GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle
348 *data,
349 const char *section,
350 const char *option, const char *value)
351{
352 struct ConfigSection *sec;
353 struct ConfigEntry *e;
354
355 e = findEntry (data, section, option);
356 if (e != NULL)
357 {
358 GNUNET_free_non_null (e->val);
359 e->val = GNUNET_strdup (value);
360 return;
361 }
362 sec = findSection (data, section);
363 if (sec == NULL)
364 {
365 sec = GNUNET_malloc (sizeof (struct ConfigSection));
366 sec->name = GNUNET_strdup (section);
367 sec->next = data->sections;
368 data->sections = sec;
369 }
370 e = GNUNET_malloc (sizeof (struct ConfigEntry));
371 e->key = GNUNET_strdup (option);
372 e->val = GNUNET_strdup (value);
373 e->next = sec->entries;
374 sec->entries = e;
375}
376
377void
378GNUNET_CONFIGURATION_set_value_number (struct GNUNET_CONFIGURATION_Handle
379 *cfg, const char *section,
380 const char *option,
381 unsigned long long number)
382{
383 char s[64];
384 GNUNET_snprintf (s, 64, "%llu", number);
385 GNUNET_CONFIGURATION_set_value_string (cfg, section, option, s);
386}
387
388int
389GNUNET_CONFIGURATION_get_value_number (struct GNUNET_CONFIGURATION_Handle
390 *cfg, const char *section,
391 const char *option,
392 unsigned long long *number)
393{
394 struct ConfigEntry *e;
395
396 e = findEntry (cfg, section, option);
397 if (e == NULL)
398 return GNUNET_SYSERR;
399 if (1 != SSCANF (e->val, "%llu", number))
400 return GNUNET_SYSERR;
401 return GNUNET_OK;
402}
403
404int
405GNUNET_CONFIGURATION_get_value_string (struct GNUNET_CONFIGURATION_Handle
406 *cfg, const char *section,
407 const char *option, char **value)
408{
409 struct ConfigEntry *e;
410
411 e = findEntry (cfg, section, option);
412 if ((e == NULL) || (e->val == NULL))
413 {
414 *value = NULL;
415 return GNUNET_SYSERR;
416 }
417 *value = GNUNET_strdup (e->val);
418 return GNUNET_OK;
419}
420
421int
422GNUNET_CONFIGURATION_get_value_choice (struct GNUNET_CONFIGURATION_Handle
423 *cfg, const char *section,
424 const char *option,
425 const char **choices,
426 const char **value)
427{
428 struct ConfigEntry *e;
429 int i;
430
431 e = findEntry (cfg, section, option);
432 if (e == NULL)
433 return GNUNET_SYSERR;
434 i = 0;
435 while (choices[i] != NULL)
436 {
437 if (0 == strcasecmp (choices[i], e->val))
438 break;
439 i++;
440 }
441 if (choices[i] == NULL)
442 {
443 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
444 _("Configuration value '%s' for '%s'"
445 " in section '%s' is not in set of legal choices\n"),
446 e->val, option, section);
447 return GNUNET_SYSERR;
448 }
449 *value = choices[i];
450 return GNUNET_OK;
451}
452
453/**
454 * Test if we have a value for a particular option
455 * @return GNUNET_YES if so, GNUNET_NO if not.
456 */
457int
458GNUNET_CONFIGURATION_have_value (struct GNUNET_CONFIGURATION_Handle *cfg,
459 const char *section, const char *option)
460{
461 struct ConfigEntry *e;
462 if ((NULL == (e = findEntry (cfg, section, option))) || (e->val == NULL))
463 return GNUNET_NO;
464 return GNUNET_YES;
465}
466
467/**
468 * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR"
469 * where either in the "PATHS" section or the environtment
470 * "FOO" is set to "DIRECTORY".
471 *
472 * @param old string to $-expand (will be freed!)
473 * @return $-expanded string
474 */
475char *
476GNUNET_CONFIGURATION_expand_dollar (struct GNUNET_CONFIGURATION_Handle *cfg,
477 char *orig)
478{
479 int i;
480 char *prefix;
481 char *result;
482 const char *post;
483 const char *env;
484
485 if (orig[0] != '$')
486 return orig;
487 i = 0;
488 while ((orig[i] != '/') && (orig[i] != '\\') && (orig[i] != '\0'))
489 i++;
490 if (orig[i] == '\0')
491 {
492 post = "";
493 }
494 else
495 {
496 orig[i] = '\0';
497 post = &orig[i + 1];
498 }
499 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
500 "PATHS",
501 &orig[1], &prefix))
502 {
503 if (NULL == (env = getenv (&orig[1])))
504 {
505 orig[i] = DIR_SEPARATOR;
506 return orig;
507 }
508 prefix = GNUNET_strdup (env);
509 }
510 result = GNUNET_malloc (strlen (prefix) + strlen (post) + 2);
511 strcpy (result, prefix);
512 if ((strlen (prefix) == 0) ||
513 ((prefix[strlen (prefix) - 1] != DIR_SEPARATOR) && (strlen (post) > 0)))
514 strcat (result, DIR_SEPARATOR_STR);
515 strcat (result, post);
516 GNUNET_free (prefix);
517 GNUNET_free (orig);
518 return result;
519}
520
521/**
522 * Get a configuration value that should be a string.
523 * @param value will be set to a freshly allocated configuration
524 * value, or NULL if option is not specified
525 * @return GNUNET_OK on success, GNUNET_SYSERR on error
526 */
527int
528GNUNET_CONFIGURATION_get_value_filename (struct GNUNET_CONFIGURATION_Handle
529 *data, const char *section,
530 const char *option, char **value)
531{
532 int ret;
533 char *tmp;
534
535 tmp = NULL;
536 ret = GNUNET_CONFIGURATION_get_value_string (data, section, option, &tmp);
537 if (ret == GNUNET_SYSERR)
538 return ret;
539 if (tmp != NULL)
540 {
541 tmp = GNUNET_CONFIGURATION_expand_dollar (data, tmp);
542 *value = GNUNET_STRINGS_filename_expand (tmp);
543 GNUNET_free (tmp);
544 }
545 else
546 {
547 *value = NULL;
548 }
549 return ret;
550}
551
552/**
553 * Get a configuration value that should be in a set of
554 * "GNUNET_YES" or "GNUNET_NO".
555 *
556 * @return GNUNET_YES, GNUNET_NO or GNUNET_SYSERR
557 */
558int
559GNUNET_CONFIGURATION_get_value_yesno (struct GNUNET_CONFIGURATION_Handle *cfg,
560 const char *section, const char *option)
561{
562 static const char *yesno[] = { "YES", "NO", NULL };
563 const char *val;
564 int ret;
565
566 ret = GNUNET_CONFIGURATION_get_value_choice (cfg,
567 section, option, yesno, &val);
568 if (ret == GNUNET_SYSERR)
569 return ret;
570 if (val == yesno[0])
571 return GNUNET_YES;
572 return GNUNET_NO;
573}
574
575
576/**
577 * Iterate over the set of filenames stored in a configuration value.
578 *
579 * @return number of filenames iterated over, -1 on error
580 */
581int
582GNUNET_CONFIGURATION_iterate_value_filenames (struct
583 GNUNET_CONFIGURATION_Handle
584 *cfg, const char *section,
585 const char *option,
586 GNUNET_FileNameCallback cb,
587 void *cls)
588{
589 char *list;
590 char *pos;
591 char *end;
592 char old;
593 int ret;
594
595 if (GNUNET_OK !=
596 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list))
597 return 0;
598 GNUNET_assert (list != NULL);
599 ret = 0;
600 pos = list;
601 while (1)
602 {
603 while (pos[0] == ' ')
604 pos++;
605 if (strlen (pos) == 0)
606 break;
607 end = pos + 1;
608 while ((end[0] != ' ') && (end[0] != '\0'))
609 {
610 if (end[0] == '\\')
611 {
612 switch (end[1])
613 {
614 case '\\':
615 case ' ':
616 memmove (end, &end[1], strlen (&end[1]) + 1);
617 case '\0':
618 /* illegal, but just keep it */
619 break;
620 default:
621 /* illegal, but just ignore that there was a '/' */
622 break;
623 }
624 }
625 end++;
626 }
627 old = end[0];
628 end[0] = '\0';
629 if (strlen (pos) > 0)
630 {
631 ret++;
632 if ((cb != NULL) && (GNUNET_OK != cb (cls, pos)))
633 {
634 ret = GNUNET_SYSERR;
635 break;
636 }
637 }
638 if (old == '\0')
639 break;
640 pos = end + 1;
641 }
642 GNUNET_free (list);
643 return ret;
644}
645
646static char *
647escape_name (const char *value)
648{
649 char *escaped;
650 const char *rpos;
651 char *wpos;
652
653 escaped = GNUNET_malloc (strlen (value) * 2 + 1);
654 memset (escaped, 0, strlen (value) * 2 + 1);
655 rpos = value;
656 wpos = escaped;
657 while (rpos[0] != '\0')
658 {
659 switch (rpos[0])
660 {
661 case '\\':
662 case ' ':
663 wpos[0] = '\\';
664 wpos[1] = rpos[0];
665 wpos += 2;
666 break;
667 default:
668 wpos[0] = rpos[0];
669 wpos++;
670 }
671 rpos++;
672 }
673 return escaped;
674}
675
676static int
677test_match (void *cls, const char *fn)
678{
679 const char *of = cls;
680 return (0 == strcmp (of, fn)) ? GNUNET_SYSERR : GNUNET_OK;
681}
682
683/**
684 * Append a filename to a configuration value that
685 * represents a list of filenames
686 *
687 * @param value filename to append
688 * @return GNUNET_OK on success,
689 * GNUNET_NO if the filename already in the list
690 * GNUNET_SYSERR on error
691 */
692int
693GNUNET_CONFIGURATION_append_value_filename (struct GNUNET_CONFIGURATION_Handle
694 *cfg,
695 const char *section,
696 const char *option,
697 const char *value)
698{
699 char *escaped;
700 char *old;
701 char *nw;
702
703 if (GNUNET_SYSERR
704 == GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
705 section,
706 option,
707 &test_match,
708 (void *) value))
709 return GNUNET_NO; /* already exists */
710 if (GNUNET_OK !=
711 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &old))
712 old = GNUNET_strdup ("");
713 escaped = escape_name (value);
714 nw = GNUNET_malloc (strlen (old) + strlen (escaped) + 2);
715 strcpy (nw, old);
716 strcat (nw, " ");
717 strcat (nw, escaped);
718 GNUNET_CONFIGURATION_set_value_string (cfg, section, option, nw);
719 GNUNET_free (old);
720 GNUNET_free (nw);
721 GNUNET_free (escaped);
722 return GNUNET_OK;
723}
724
725
726/**
727 * Remove a filename from a configuration value that
728 * represents a list of filenames
729 *
730 * @param value filename to remove
731 * @return GNUNET_OK on success,
732 * GNUNET_NO if the filename is not in the list,
733 * GNUNET_SYSERR on error
734 */
735int
736GNUNET_CONFIGURATION_remove_value_filename (struct GNUNET_CONFIGURATION_Handle
737 *cfg,
738 const char *section,
739 const char *option,
740 const char *value)
741{
742 char *list;
743 char *pos;
744 char *end;
745 char *match;
746 char old;
747 int ret;
748
749 if (GNUNET_OK !=
750 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list))
751 return GNUNET_NO;
752 match = escape_name (value);
753 ret = 0;
754 pos = list;
755 while (1)
756 {
757 while (pos[0] == ' ')
758 pos++;
759 if (strlen (pos) == 0)
760 break;
761 end = pos + 1;
762 while ((end[0] != ' ') && (end[0] != '\0'))
763 {
764 if (end[0] == '\\')
765 {
766 switch (end[1])
767 {
768 case '\\':
769 case ' ':
770 end++;
771 break;
772 case '\0':
773 /* illegal, but just keep it */
774 break;
775 default:
776 /* illegal, but just ignore that there was a '/' */
777 break;
778 }
779 }
780 end++;
781 }
782 old = end[0];
783 end[0] = '\0';
784 if (strlen (pos) > 0)
785 {
786 if (0 == strcmp (pos, match))
787 {
788 memmove (pos, &end[1], strlen (&end[1]) + 1);
789
790 if (pos != list)
791 pos[-1] = ' '; /* previously changed to "\0" */
792 GNUNET_CONFIGURATION_set_value_string (cfg,
793 section, option, list);
794 GNUNET_free (list);
795 GNUNET_free (match);
796 return GNUNET_OK;
797 }
798 }
799 if (old == '\0')
800 break;
801 pos = end + 1;
802 }
803 GNUNET_free (list);
804 GNUNET_free (match);
805 return GNUNET_NO;
806}
807
808
809/**
810 * Load configuration (starts with defaults, then loads
811 * system-specific configuration).
812 */
813int
814GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
815 const char *cfgfn)
816{
817 char *baseconfig;
818 char *ipath;
819
820 ipath = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
821 if (ipath == NULL)
822 return GNUNET_SYSERR;
823 baseconfig = NULL;
824 GNUNET_asprintf (&baseconfig,
825 "%s%s%s", ipath, DIR_SEPARATOR_STR, "defaults.conf");
826 GNUNET_free (ipath);
827 if ((GNUNET_OK !=
828 GNUNET_CONFIGURATION_parse (cfg, baseconfig)) ||
829 (!((cfgfn == NULL) ||
830 (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, cfgfn)))))
831 {
832 GNUNET_free (baseconfig);
833 return GNUNET_SYSERR;
834 }
835 GNUNET_free (baseconfig);
836 if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
837 "TESTING",
838 "WEAKRANDOM")) &&
839 (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg,
840 "TESTING",
841 "WEAKRANDOM")))
842 GNUNET_CRYPTO_random_disable_entropy_gathering ();
843 return GNUNET_OK;
844}
845
846
847
848/* end of configuration.c */
diff --git a/src/util/container_bloomfilter.c b/src/util/container_bloomfilter.c
new file mode 100644
index 000000000..8b76ef8dc
--- /dev/null
+++ b/src/util/container_bloomfilter.c
@@ -0,0 +1,677 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006, 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/container_bloomfilter.c
22 * @brief data structure used to reduce disk accesses.
23 *
24 * The idea basically: Create a signature for each element in the
25 * database. Add those signatures to a bit array. When doing a lookup,
26 * check if the bit array matches the signature of the requested
27 * element. If yes, address the disk, otherwise return 'not found'.
28 *
29 * A property of the bloom filter is that sometimes we will have
30 * a match even if the element is not on the disk (then we do
31 * an unnecessary disk access), but what's most important is that
32 * we never get a single "false negative".
33 *
34 * To be able to delete entries from the bloom filter, we maintain
35 * a 4 bit counter in the file on the drive (we still use only one
36 * bit in memory).
37 *
38 * @author Igor Wronsky
39 * @author Christian Grothoff
40 */
41
42#include "platform.h"
43#include "gnunet_common.h"
44#include "gnunet_container_lib.h"
45#include "gnunet_disk_lib.h"
46
47struct GNUNET_CONTAINER_BloomFilter
48{
49
50 /**
51 * The actual bloomfilter bit array
52 */
53 char *bitArray;
54
55 /**
56 * Filename of the filter
57 */
58 char *filename;
59
60 /**
61 * The bit counter file on disk
62 */
63 int fd;
64
65 /**
66 * How many bits we set for each stored element
67 */
68 unsigned int addressesPerElement;
69
70 /**
71 * Size of bitArray in bytes
72 */
73 unsigned int bitArraySize;
74
75};
76
77
78/**
79 * Sets a bit active in the bitArray. Increment bit-specific
80 * usage counter on disk only if below 4bit max (==15).
81 *
82 * @param bitArray memory area to set the bit in
83 * @param bitIdx which bit to set
84 */
85static void
86setBit (char *bitArray, unsigned int bitIdx)
87{
88 unsigned int arraySlot;
89 unsigned int targetBit;
90
91 arraySlot = bitIdx / 8;
92 targetBit = (1L << (bitIdx % 8));
93 bitArray[arraySlot] |= targetBit;
94}
95
96/**
97 * Clears a bit from bitArray. Bit is cleared from the array
98 * only if the respective usage counter on the disk hits/is zero.
99 *
100 * @param bitArray memory area to set the bit in
101 * @param bitIdx which bit to unset
102 */
103static void
104clearBit (char *bitArray, unsigned int bitIdx)
105{
106 unsigned int slot;
107 unsigned int targetBit;
108
109 slot = bitIdx / 8;
110 targetBit = (1L << (bitIdx % 8));
111 bitArray[slot] = bitArray[slot] & (~targetBit);
112}
113
114/**
115 * Checks if a bit is active in the bitArray
116 *
117 * @param bitArray memory area to set the bit in
118 * @param bitIdx which bit to test
119 * @return GNUNET_YES if the bit is set, GNUNET_NO if not.
120 */
121static int
122testBit (char *bitArray, unsigned int bitIdx)
123{
124 unsigned int slot;
125 unsigned int targetBit;
126
127 slot = bitIdx / 8;
128 targetBit = (1L << (bitIdx % 8));
129 if (bitArray[slot] & targetBit)
130 return GNUNET_YES;
131 else
132 return GNUNET_NO;
133}
134
135/**
136 * Sets a bit active in the bitArray and increments
137 * bit-specific usage counter on disk (but only if
138 * the counter was below 4 bit max (==15)).
139 *
140 * @param bitArray memory area to set the bit in
141 * @param bitIdx which bit to test
142 * @param fd A file to keep the 4 bit address usage counters in
143 */
144static void
145incrementBit (char *bitArray, unsigned int bitIdx, int fd)
146{
147 unsigned int fileSlot;
148 unsigned char value;
149 unsigned int high;
150 unsigned int low;
151 unsigned int targetLoc;
152
153 setBit (bitArray, bitIdx);
154 if (fd == -1)
155 return;
156 /* Update the counter file on disk */
157 fileSlot = bitIdx / 2;
158 targetLoc = bitIdx % 2;
159
160 GNUNET_assert (fileSlot == (unsigned int) LSEEK (fd, fileSlot, SEEK_SET));
161 if (1 != READ (fd, &value, 1))
162 value = 0;
163 low = value & 0xF;
164 high = (value & (~0xF)) >> 4;
165
166 if (targetLoc == 0)
167 {
168 if (low < 0xF)
169 low++;
170 }
171 else
172 {
173 if (high < 0xF)
174 high++;
175 }
176 value = ((high << 4) | low);
177 GNUNET_assert (fileSlot == (unsigned int) LSEEK (fd, fileSlot, SEEK_SET));
178 GNUNET_assert (1 == WRITE (fd, &value, 1));
179}
180
181/**
182 * Clears a bit from bitArray if the respective usage
183 * counter on the disk hits/is zero.
184 *
185 * @param bitArray memory area to set the bit in
186 * @param bitIdx which bit to test
187 * @param fd A file to keep the 4bit address usage counters in
188 */
189static void
190decrementBit (char *bitArray, unsigned int bitIdx, int fd)
191{
192 unsigned int fileSlot;
193 unsigned char value;
194 unsigned int high;
195 unsigned int low;
196 unsigned int targetLoc;
197
198 if (fd == -1)
199 return; /* cannot decrement! */
200 /* Each char slot in the counter file holds two 4 bit counters */
201 fileSlot = bitIdx / 2;
202 targetLoc = bitIdx % 2;
203 LSEEK (fd, fileSlot, SEEK_SET);
204 if (1 != READ (fd, &value, 1))
205 value = 0;
206 low = value & 0xF;
207 high = (value & 0xF0) >> 4;
208
209 /* decrement, but once we have reached the max, never go back! */
210 if (targetLoc == 0)
211 {
212 if ((low > 0) && (low < 0xF))
213 low--;
214 if (low == 0)
215 {
216 clearBit (bitArray, bitIdx);
217 }
218 }
219 else
220 {
221 if ((high > 0) && (high < 0xF))
222 high--;
223 if (high == 0)
224 {
225 clearBit (bitArray, bitIdx);
226 }
227 }
228 value = ((high << 4) | low);
229 LSEEK (fd, fileSlot, SEEK_SET);
230 GNUNET_assert (1 == WRITE (fd, &value, 1));
231}
232
233#define BUFFSIZE 65536
234
235/**
236 * Creates a file filled with zeroes
237 *
238 * @param fd the file handle
239 * @param size the size of the file
240 * @return GNUNET_OK if created ok, GNUNET_SYSERR otherwise
241 */
242static int
243makeEmptyFile (int fd, unsigned int size)
244{
245 char *buffer;
246 unsigned int bytesleft = size;
247 int res = 0;
248
249 if (fd == -1)
250 return GNUNET_SYSERR;
251 buffer = GNUNET_malloc (BUFFSIZE);
252 memset (buffer, 0, BUFFSIZE);
253 LSEEK (fd, 0, SEEK_SET);
254
255 while (bytesleft > 0)
256 {
257 if (bytesleft > BUFFSIZE)
258 {
259 res = WRITE (fd, buffer, BUFFSIZE);
260 bytesleft -= BUFFSIZE;
261 }
262 else
263 {
264 res = WRITE (fd, buffer, bytesleft);
265 bytesleft = 0;
266 }
267 GNUNET_assert (res != -1);
268 }
269 GNUNET_free (buffer);
270 return GNUNET_OK;
271}
272
273/* ************** GNUNET_CONTAINER_BloomFilter GNUNET_CRYPTO_hash iterator ********* */
274
275/**
276 * Iterator (callback) method to be called by the
277 * bloomfilter iterator on each bit that is to be
278 * set or tested for the key.
279 *
280 * @param bf the filter to manipulate
281 * @param bit the current bit
282 * @param additional context specific argument
283 */
284typedef void (*BitIterator) (struct GNUNET_CONTAINER_BloomFilter * bf,
285 unsigned int bit, void *arg);
286
287/**
288 * Call an iterator for each bit that the bloomfilter
289 * must test or set for this element.
290 *
291 * @param bf the filter
292 * @param callback the method to call
293 * @param arg extra argument to callback
294 * @param key the key for which we iterate over the BF bits
295 */
296static void
297iterateBits (struct GNUNET_CONTAINER_BloomFilter *bf,
298 BitIterator callback, void *arg, const GNUNET_HashCode * key)
299{
300 GNUNET_HashCode tmp[2];
301 int bitCount;
302 int round;
303 unsigned int slot = 0;
304
305 bitCount = bf->addressesPerElement;
306 memcpy (&tmp[0], key, sizeof (GNUNET_HashCode));
307 round = 0;
308 while (bitCount > 0)
309 {
310 while (slot < (sizeof (GNUNET_HashCode) / sizeof (unsigned int)))
311 {
312 callback (bf,
313 (((unsigned int *) &tmp[round & 1])[slot]) &
314 ((bf->bitArraySize * 8) - 1), arg);
315 slot++;
316 bitCount--;
317 if (bitCount == 0)
318 break;
319 }
320 if (bitCount > 0)
321 {
322 GNUNET_CRYPTO_hash (&tmp[round & 1], sizeof (GNUNET_HashCode),
323 &tmp[(round + 1) & 1]);
324 round++;
325 slot = 0;
326 }
327 }
328}
329
330/**
331 * Callback: increment bit
332 *
333 * @param bf the filter to manipulate
334 * @param bit the bit to increment
335 * @param arg not used
336 */
337static void
338incrementBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf,
339 unsigned int bit, void *arg)
340{
341 incrementBit (bf->bitArray, bit, bf->fd);
342}
343
344/**
345 * Callback: decrement bit
346 *
347 * @param bf the filter to manipulate
348 * @param bit the bit to decrement
349 * @param arg not used
350 */
351static void
352decrementBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf,
353 unsigned int bit, void *arg)
354{
355 decrementBit (bf->bitArray, bit, bf->fd);
356}
357
358/**
359 * Callback: test if all bits are set
360 *
361 * @param bf the filter
362 * @param bit the bit to test
363 * @param arg pointer set to GNUNET_NO if bit is not set
364 */
365static void
366testBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf, unsigned int bit,
367 void *cls)
368{
369 int *arg = cls;
370 if (GNUNET_NO == testBit (bf->bitArray, bit))
371 *arg = GNUNET_NO;
372}
373
374/* *********************** INTERFACE **************** */
375
376/**
377 * Load a bloom-filter from a file.
378 *
379 * @param filename the name of the file (or the prefix)
380 * @param size the size of the bloom-filter (number of
381 * bytes of storage space to use)
382 * @param k the number of GNUNET_CRYPTO_hash-functions to apply per
383 * element (number of bits set per element in the set)
384 * @return the bloomfilter
385 */
386struct GNUNET_CONTAINER_BloomFilter *
387GNUNET_CONTAINER_bloomfilter_load (const char *filename, unsigned int size,
388 unsigned int k)
389{
390 struct GNUNET_CONTAINER_BloomFilter *bf;
391 char *rbuff;
392 unsigned int pos;
393 int i;
394 unsigned int ui;
395
396 if ((k == 0) || (size == 0))
397 return NULL;
398 if (size < BUFFSIZE)
399 size = BUFFSIZE;
400 ui = 1;
401 while (ui < size)
402 ui *= 2;
403 size = ui; /* make sure it's a power of 2 */
404
405 bf = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_BloomFilter));
406 /* Try to open a bloomfilter file */
407 if (filename != NULL)
408 {
409#ifndef _MSC_VER
410 bf->fd = GNUNET_DISK_file_open (filename, O_RDWR | O_CREAT,
411 S_IRUSR | S_IWUSR);
412#else
413 bf->fd = GNUNET_DISK_file_open (filename,
414 O_WRONLY | O_CREAT, S_IREAD | S_IWRITE);
415#endif
416 if (-1 == bf->fd)
417 {
418 GNUNET_free (bf);
419 return NULL;
420 }
421 bf->filename = GNUNET_strdup (filename);
422 }
423 else
424 {
425 bf->fd = -1;
426 bf->filename = NULL;
427 }
428 /* Alloc block */
429 bf->bitArray = GNUNET_malloc_large (size);
430 bf->bitArraySize = size;
431 bf->addressesPerElement = k;
432 memset (bf->bitArray, 0, bf->bitArraySize);
433
434 if (bf->fd != -1)
435 {
436 /* Read from the file what bits we can */
437 rbuff = GNUNET_malloc (BUFFSIZE);
438 pos = 0;
439 while (pos < size * 8)
440 {
441 int res;
442
443 res = READ (bf->fd, rbuff, BUFFSIZE);
444 if (res == 0)
445 break; /* is ok! we just did not use that many bits yet */
446 for (i = 0; i < res; i++)
447 {
448 if ((rbuff[i] & 0x0F) != 0)
449 setBit (bf->bitArray, pos + i * 2);
450 if ((rbuff[i] & 0xF0) != 0)
451 setBit (bf->bitArray, pos + i * 2 + 1);
452 }
453 if (res < BUFFSIZE)
454 break;
455 pos += BUFFSIZE * 2; /* 2 bits per byte in the buffer */
456 }
457 GNUNET_free (rbuff);
458 }
459 return bf;
460}
461
462
463/**
464 * Create a bloom filter from raw bits.
465 *
466 * @param data the raw bits in memory (maybe NULL,
467 * in which case all bits should be considered
468 * to be zero).
469 * @param size the size of the bloom-filter (number of
470 * bytes of storage space to use); also size of data
471 * -- unless data is NULL
472 * @param k the number of GNUNET_CRYPTO_hash-functions to apply per
473 * element (number of bits set per element in the set)
474 * @return the bloomfilter
475 */
476struct GNUNET_CONTAINER_BloomFilter *
477GNUNET_CONTAINER_bloomfilter_init (const char *data, unsigned int size,
478 unsigned int k)
479{
480 struct GNUNET_CONTAINER_BloomFilter *bf;
481 unsigned int ui;
482
483 if ((k == 0) || (size == 0))
484 return NULL;
485 ui = 1;
486 while (ui < size)
487 ui *= 2;
488 if (size != ui)
489 {
490 GNUNET_break (0);
491 return NULL;
492 }
493 bf = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_BloomFilter));
494 bf->fd = -1;
495 bf->filename = NULL;
496 bf->bitArray = GNUNET_malloc_large (size);
497 bf->bitArraySize = size;
498 bf->addressesPerElement = k;
499 if (data != NULL)
500 memcpy (bf->bitArray, data, size);
501 else
502 memset (bf->bitArray, 0, bf->bitArraySize);
503 return bf;
504}
505
506
507/**
508 * Copy the raw data of this bloomfilter into
509 * the given data array.
510 *
511 * @param data where to write the data
512 * @param size the size of the given data array
513 * @return GNUNET_SYSERR if the data array is not big enough
514 */
515int
516GNUNET_CONTAINER_bloomfilter_get_raw_data (struct GNUNET_CONTAINER_BloomFilter
517 *bf, char *data, unsigned int size)
518{
519 if (NULL == bf)
520 return GNUNET_SYSERR;
521
522 if (bf->bitArraySize != size)
523 return GNUNET_SYSERR;
524 memcpy (data, bf->bitArray, size);
525 return GNUNET_OK;
526}
527
528/**
529 * Free the space associated with a filter
530 * in memory, flush to drive if needed (do not
531 * free the space on the drive)
532 *
533 * @param bf the filter
534 */
535void
536GNUNET_CONTAINER_bloomfilter_free (struct GNUNET_CONTAINER_BloomFilter *bf)
537{
538 if (NULL == bf)
539 return;
540 if (bf->fd != -1)
541 GNUNET_DISK_file_close (bf->filename, bf->fd);
542 GNUNET_free_non_null (bf->filename);
543 GNUNET_free (bf->bitArray);
544 GNUNET_free (bf);
545}
546
547/**
548 * Reset a bloom filter to empty. Clears the file on disk.
549 *
550 * @param bf the filter
551 */
552void
553GNUNET_CONTAINER_bloomfilter_clear (struct GNUNET_CONTAINER_BloomFilter *bf)
554{
555 if (NULL == bf)
556 return;
557
558 memset (bf->bitArray, 0, bf->bitArraySize);
559 if (bf->fd != -1)
560 makeEmptyFile (bf->fd, bf->bitArraySize * 4);
561}
562
563
564/**
565 * Test if an element is in the filter.
566 *
567 * @param e the element
568 * @param bf the filter
569 * @return GNUNET_YES if the element is in the filter, GNUNET_NO if not
570 */
571int
572GNUNET_CONTAINER_bloomfilter_test (struct GNUNET_CONTAINER_BloomFilter *bf,
573 const GNUNET_HashCode * e)
574{
575 int res;
576
577 if (NULL == bf)
578 return GNUNET_YES;
579 res = GNUNET_YES;
580 iterateBits (bf, &testBitCallback, &res, e);
581 return res;
582}
583
584/**
585 * Add an element to the filter
586 *
587 * @param bf the filter
588 * @param e the element
589 */
590void
591GNUNET_CONTAINER_bloomfilter_add (struct GNUNET_CONTAINER_BloomFilter *bf,
592 const GNUNET_HashCode * e)
593{
594
595 if (NULL == bf)
596 return;
597 iterateBits (bf, &incrementBitCallback, NULL, e);
598}
599
600
601/**
602 * Or the entries of the given raw data array with the
603 * data of the given bloom filter. Assumes that
604 * the size of the data array and the current filter
605 * match.
606 * @param bf the filter
607 */
608int
609GNUNET_CONTAINER_bloomfilter_or (struct GNUNET_CONTAINER_BloomFilter *bf,
610 const char *data, unsigned int size)
611{
612 unsigned int i;
613
614 if (NULL == bf)
615 return GNUNET_YES;
616 if (bf->bitArraySize != size)
617 return GNUNET_SYSERR;
618 /* FIXME: we could do this 4-8x faster by
619 going over int/long arrays */
620 for (i = 0; i < size; i++)
621 bf->bitArray[i] |= data[i];
622 return GNUNET_OK;
623}
624
625/**
626 * Remove an element from the filter.
627 *
628 * @param bf the filter
629 * @param e the element to remove
630 */
631void
632GNUNET_CONTAINER_bloomfilter_remove (struct GNUNET_CONTAINER_BloomFilter *bf,
633 const GNUNET_HashCode * e)
634{
635 if (NULL == bf)
636 return;
637 if (bf->fd == -1)
638 return;
639 iterateBits (bf, &decrementBitCallback, NULL, e);
640}
641
642/**
643 * Resize a bloom filter. Note that this operation
644 * is pretty costly. Essentially, the bloom filter
645 * needs to be completely re-build.
646 *
647 * @param bf the filter
648 * @param iterator an iterator over all elements stored in the BF
649 * @param iterator_arg argument to the iterator function
650 * @param size the new size for the filter
651 * @param k the new number of GNUNET_CRYPTO_hash-function to apply per element
652 */
653void
654GNUNET_CONTAINER_bloomfilter_resize (struct GNUNET_CONTAINER_BloomFilter *bf,
655 GNUNET_HashCodeIterator iterator,
656 void *iterator_arg, unsigned int size,
657 unsigned int k)
658{
659 GNUNET_HashCode hc;
660 unsigned int i;
661
662 GNUNET_free (bf->bitArray);
663 i = 1;
664 while (i < size)
665 i *= 2;
666 size = i; /* make sure it's a power of 2 */
667
668 bf->bitArraySize = size;
669 bf->bitArray = GNUNET_malloc (size);
670 memset (bf->bitArray, 0, bf->bitArraySize);
671 if (bf->fd != -1)
672 makeEmptyFile (bf->fd, bf->bitArraySize * 4);
673 while (GNUNET_YES == iterator (&hc, iterator_arg))
674 GNUNET_CONTAINER_bloomfilter_add (bf, &hc);
675}
676
677/* end of container_bloomfilter.c */
diff --git a/src/util/container_heap.c b/src/util/container_heap.c
new file mode 100644
index 000000000..1e7077c80
--- /dev/null
+++ b/src/util/container_heap.c
@@ -0,0 +1,533 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @author Nathan Evans
23 * @file util/container_heap.c
24 * @brief Implementation of heap operations
25 */
26
27#include "platform.h"
28#include "gnunet_protocols.h"
29#include "gnunet_util.h"
30#include "gnunet_util_containers.h"
31
32/*
33 * Struct that is stored in hashmap, pointers to
34 * locations in min_heap and max_heap.
35 */
36struct GNUNET_CONTAINER_heap_info
37{
38 struct GNUNET_CONTAINER_heap_node *min_loc;
39
40 struct GNUNET_CONTAINER_heap_node *max_loc;
41
42};
43
44/*
45 * Generic heap node structure, contains pointer to parent
46 * left child, right child, and actual neighbor.
47 */
48struct GNUNET_CONTAINER_heap_node
49{
50 struct GNUNET_CONTAINER_heap_node *parent;
51
52 struct GNUNET_CONTAINER_heap_node *left_child;
53
54 struct GNUNET_CONTAINER_heap_node *right_child;
55
56 GNUNET_CONTAINER_HeapCost cost;
57
58 void *element;
59
60};
61
62struct GNUNET_CONTAINER_Heap
63{
64 unsigned int size;
65
66 unsigned int max_size;
67
68 enum type;
69
70 struct GNUNET_CONTAINER_heap_node *root;
71
72 struct GNUNET_CONTAINER_heap_node *traversal_pos;
73
74};
75
76void
77internal_print (struct GNUNET_CONTAINER_heap_node *root)
78{
79 fprintf (stdout, "%d\n", root->cost);
80 if (root->left_child != NULL)
81 {
82 fprintf (stdout, "LEFT of %d\n", root->cost);
83 internal_print (root->left_child);
84 }
85 if (root->right_child != NULL)
86 {
87 fprintf (stdout, "RIGHT of %d\n", root->cost);
88 internal_print (root->right_child);
89 }
90}
91
92void
93printTree (struct GNUNET_CONTAINER_Heap *root)
94{
95 internal_print (root->root);
96}
97
98struct GNUNET_CONTAINER_Heap *
99GNUNET_CONTAINER_heap_create (enum type)
100{
101 struct GNUNET_CONTAINER_Heap *heap;
102 heap = malloc (sizeof (struct GNUNET_CONTAINER_Heap));
103 heap->max_size = -1;
104 heap->type = type;
105 heap->root = NULL;
106 heap->traversal_pos = NULL;
107 heap->size = 0;
108
109 return heap;
110}
111
112void
113GNUNET_CONTAINER_heap_destroy (struct GNUNET_CONTAINER_Heap *heap)
114{
115 void *unused;
116 while (heap->size > 0)
117 {
118 unused = GNUNET_CONTAINER_heap_remove_root (heap);
119 }
120
121 GNUNET_free (heap);
122 return;
123}
124
125struct GNUNET_CONTAINER_heap_node *
126find_element (struct GNUNET_CONTAINER_heap_node *node, void *element)
127{
128 struct GNUNET_CONTAINER_heap_node *ret;
129 ret = NULL;
130 if ((node != NULL) && (node->element == element))
131 {
132 ret = node;
133 }
134
135 if ((ret == NULL) && (node->left_child != NULL))
136 {
137 ret = find_element (node->left_child, element);
138 }
139
140 if ((ret == NULL) && (node->right_child != NULL))
141 {
142 ret = find_element (node->right_child, element);
143 }
144
145 return ret;
146}
147
148static struct GNUNET_CONTAINER_heap_node *
149getNextPos (struct GNUNET_CONTAINER_Heap *root)
150{
151 struct GNUNET_CONTAINER_heap_node *ret;
152 struct GNUNET_CONTAINER_heap_node *parent;
153 int pos;
154 int depth;
155 int i;
156
157 ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_heap_node));
158 pos = root->size + 1;
159 depth = (int) log2 (pos);
160 ret->left_child = NULL;
161 ret->right_child = NULL;
162
163 if (depth == 0)
164 {
165 ret->parent = NULL;
166 root->root = ret;
167 }
168 else
169 {
170 parent = root->root;
171 for (i = depth; i > 1; i--)
172 {
173 if (((pos / (1 << (i - 1))) % 2) == 0)
174 parent = parent->left_child;
175 else
176 parent = parent->right_child;
177 }
178
179 ret->parent = parent;
180 if ((pos % 2) == 0)
181 parent->left_child = ret;
182 else
183 parent->right_child = ret;
184
185 }
186
187 return ret;
188
189}
190
191static struct GNUNET_CONTAINER_heap_node *
192getPos (struct GNUNET_CONTAINER_Heap *root, unsigned int pos)
193{
194 struct GNUNET_CONTAINER_heap_node *ret;
195
196 int depth;
197 int i;
198
199 depth = (int) log2 (pos);
200 ret = NULL;
201 if (pos > root->size)
202 {
203 return ret;
204 }
205 else
206 {
207 ret = root->root;
208 for (i = depth; i > 0; i--)
209 {
210 if (((pos / (1 << (i - 1))) % 2) == 0)
211 ret = ret->left_child;
212 else
213 ret = ret->right_child;
214 }
215 }
216
217 return ret;
218
219}
220
221void
222swapNodes (struct GNUNET_CONTAINER_heap_node *first,
223 struct GNUNET_CONTAINER_heap_node *second,
224 struct GNUNET_CONTAINER_Heap *root)
225{
226 void *temp_element;
227 GNUNET_CONTAINER_HeapCost temp_cost;
228
229 temp_element = first->element;
230 temp_cost = first->cost;
231 first->element = second->element;
232 first->cost = second->cost;
233 second->element = temp_element;
234 second->cost = temp_cost;
235
236/*
237 * I still worry that there is some good reason for
238 * elements being location aware... but it eludes me
239 * for the moment...
240 if ((root->type == GNUNET_DV_MAX_HEAP))
241 {
242 first->neighbor->max_loc = first;
243 second->neighbor->max_loc = second;
244 }
245 else if ((root->type == GNUNET_DV_MIN_HEAP))
246 {
247 first->neighbor->min_loc = first;
248 second->neighbor->min_loc = second;
249 }
250*/
251 return;
252}
253
254void
255percolateHeap (struct GNUNET_CONTAINER_heap_node *pos,
256 struct GNUNET_CONTAINER_Heap *root)
257{
258
259 while ((pos->parent != NULL) &&
260 (((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
261 && (pos->parent->cost < pos->cost))
262 || ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
263 && (pos->parent->cost > pos->cost))))
264 {
265 swapNodes (pos, pos->parent, root);
266 pos = pos->parent;
267 }
268
269 return;
270}
271
272
273
274void
275percolateDownHeap (struct GNUNET_CONTAINER_heap_node *pos,
276 struct GNUNET_CONTAINER_Heap *root)
277{
278 struct GNUNET_CONTAINER_heap_node *switchNeighbor;
279
280 switchNeighbor = pos;
281
282 if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX))
283 {
284 if ((pos->left_child != NULL)
285 && (pos->left_child->cost > switchNeighbor->cost))
286 {
287 switchNeighbor = pos->left_child;
288 }
289
290 if ((pos->right_child != NULL)
291 && (pos->right_child->cost > switchNeighbor->cost))
292 {
293 switchNeighbor = pos->right_child;
294 }
295 }
296 else if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN))
297 {
298 if ((pos->left_child != NULL)
299 && (pos->left_child->cost < switchNeighbor->cost))
300 {
301 switchNeighbor = pos->left_child;
302 }
303
304 if ((pos->right_child != NULL)
305 && (pos->right_child->cost < switchNeighbor->cost))
306 {
307 switchNeighbor = pos->right_child;
308 }
309 }
310
311 if (switchNeighbor != pos)
312 {
313 swapNodes (switchNeighbor, pos, root);
314 percolateDownHeap (switchNeighbor, root);
315 }
316
317 return;
318}
319
320void *
321GNUNET_CONTAINER_heap_remove_node (struct GNUNET_CONTAINER_Heap *root,
322 void *element)
323{
324 void *ret;
325 struct GNUNET_CONTAINER_heap_node *del_node;
326 struct GNUNET_CONTAINER_heap_node *last;
327 GNUNET_CONTAINER_HeapCost old_cost;
328
329 del_node = NULL;
330 del_node = find_element (root->root, element);
331
332 if (del_node == NULL)
333 return NULL;
334
335 ret = del_node->element;
336 last = getPos (root, root->size);
337
338 old_cost = del_node->cost;
339 del_node->element = last->element;
340 del_node->cost = last->cost;
341
342 if (last->parent->left_child == last)
343 last->parent->left_child = NULL;
344 if (last->parent->right_child == last)
345 last->parent->right_child = NULL;
346
347 if (root->traversal_pos == last)
348 {
349 root->traversal_pos = root->root;
350 }
351 GNUNET_free (last);
352 root->size--;
353
354 if (del_node->cost > old_cost)
355 {
356 if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
357 percolateHeap (del_node, root);
358 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
359 percolateDownHeap (del_node, root);
360 }
361 else if (del_node->cost < old_cost)
362 {
363 if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
364 percolateDownHeap (del_node, root);
365 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
366 percolateHeap (del_node, root);
367 }
368
369 return ret;
370}
371
372int
373GNUNET_CONTAINER_heap_insert (struct GNUNET_CONTAINER_Heap *root,
374 void *element, GNUNET_CONTAINER_HeapCost cost)
375{
376 struct GNUNET_CONTAINER_heap_node *new_pos;
377 int ret;
378 ret = GNUNET_YES;
379
380 if (root->max_size > root->size)
381 {
382 new_pos = getNextPos (root);
383 new_pos->element = element;
384 new_pos->cost = cost;
385 root->size++;
386 /*We no longer can tolerate pointers between heaps :( */
387 /*if (root->type == GNUNET_DV_MIN_HEAP)
388 new_pos->neighbor->min_loc = new_pos;
389 else if (root->type == GNUNET_DV_MAX_HEAP)
390 new_pos->neighbor->max_loc = new_pos; */
391
392 percolateHeap (new_pos, root);
393 }
394 else
395 {
396 ret = GNUNET_NO;
397 }
398
399 return ret;
400}
401
402void *
403GNUNET_CONTAINER_heap_remove_root (struct GNUNET_CONTAINER_Heap *root)
404{
405 void *ret;
406 struct GNUNET_CONTAINER_heap_node *root_node;
407 struct GNUNET_CONTAINER_heap_node *last;
408
409 root_node = root->root;
410 ret = root_node->element;
411 last = getPos (root, root->size);
412
413 if (last->parent->left_child == last)
414 last->parent->left_child = NULL;
415 else if (last->parent->right_child == last)
416 last->parent->right_child = NULL;
417
418 root_node->element = last->element;
419 root_node->cost = last->cost;
420
421 if (root->traversal_pos == last)
422 {
423 root->traversal_pos = root->root;
424 }
425
426 GNUNET_free (last);
427 root->size--;
428 percolateDownHeap (root->root, root);
429 return ret;
430}
431
432static int
433updatedCost (struct GNUNET_CONTAINER_Heap *root,
434 struct GNUNET_CONTAINER_heap_node *node)
435{
436 struct GNUNET_CONTAINER_heap_node *parent;
437
438 if (node == NULL)
439 return GNUNET_SYSERR;
440
441 parent = node->parent;
442
443 if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX) && (parent != NULL)
444 && (node->cost > parent->cost))
445 percolateHeap (node, root);
446 else if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN) && (parent != NULL)
447 && (node->cost < parent->cost))
448 percolateHeap (node, root);
449 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
450 percolateDownHeap (node, root);
451 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
452 percolateDownHeap (node, root);
453
454 return GNUNET_YES;
455}
456
457
458int
459GNUNET_CONTAINER_heap_update_cost (struct GNUNET_CONTAINER_Heap *root,
460 void *element,
461 GNUNET_CONTAINER_HeapCost new_cost)
462{
463 struct GNUNET_CONTAINER_heap_node *node;
464 int ret = GNUNET_YES;
465 node = find_element (root->root, element);
466 if (node == NULL)
467 return GNUNET_NO;
468
469 node->cost = new_cost;
470 ret = updatedCost (root, node);
471 return ret;
472}
473
474void
475internal_iterator (struct GNUNET_CONTAINER_Heap *root,
476 struct GNUNET_CONTAINER_heap_node *node,
477 GNUNET_CONTAINER_HeapIterator iterator, void *cls)
478{
479 if (node == NULL)
480 return;
481 internal_iterator (root, node->left_child, iterator, cls);
482 internal_iterator (root, node->right_child, iterator, cls);
483 iterator (node->element, node->cost, root, cls);
484}
485
486int
487GNUNET_CONTAINER_heap_iterate (struct GNUNET_CONTAINER_Heap *heap,
488 GNUNET_CONTAINER_HeapIterator iterator,
489 void *cls)
490{
491 internal_iterator (heap, heap->root, iterator, cls);
492 return GNUNET_OK;
493}
494
495void *
496GNUNET_CONTAINER_heap_walk_get_next (struct GNUNET_CONTAINER_Heap *root)
497{
498 unsigned int choice;
499 void *element;
500
501 if ((root->traversal_pos == NULL) && (root->root != NULL))
502 {
503 root->traversal_pos = root->root;
504 }
505
506 if (root->traversal_pos == NULL)
507 return NULL;
508
509 element = root->traversal_pos->element;
510
511 choice = GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2);
512
513 switch (choice)
514 {
515 case 1:
516 root->traversal_pos = root->traversal_pos->right_child;
517 break;
518 case 0:
519 root->traversal_pos = root->traversal_pos->left_child;
520 break;
521 }
522
523 return element;
524
525}
526
527unsigned int
528GNUNET_CONTAINER_heap_get_size (struct GNUNET_CONTAINER_Heap *heap)
529{
530 return heap->size;
531}
532
533/* end of heap.c */
diff --git a/src/util/container_meta_data.c b/src/util/container_meta_data.c
new file mode 100644
index 000000000..b79de57d2
--- /dev/null
+++ b/src/util/container_meta_data.c
@@ -0,0 +1,721 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/container_meta_data.c
23 * @brief Storing of meta data
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30#include "gnunet_strings_lib.h"
31#include "gnunet_time_lib.h"
32#include <extractor.h>
33#include <zlib.h>
34
35#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS
36
37struct Item
38{
39 EXTRACTOR_KeywordType type;
40 char *data;
41};
42
43/**
44 * Meta data to associate with a file, directory or namespace.
45 */
46struct GNUNET_CONTAINER_MetaData
47{
48 uint32_t itemCount;
49 struct Item *items;
50};
51
52/**
53 * Create a fresh struct CONTAINER_MetaData token.
54 */
55struct GNUNET_CONTAINER_MetaData *
56GNUNET_CONTAINER_meta_data_create ()
57{
58 struct GNUNET_CONTAINER_MetaData *ret;
59 ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MetaData));
60 ret->items = NULL;
61 ret->itemCount = 0;
62 return ret;
63}
64
65/**
66 * Free meta data.
67 */
68void
69GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
70{
71 int i;
72
73 if (md == NULL)
74 return;
75 for (i = 0; i < md->itemCount; i++)
76 GNUNET_free (md->items[i].data);
77 GNUNET_array_grow (md->items, md->itemCount, 0);
78 GNUNET_free (md);
79}
80
81/**
82 * Add the current time as the publication date
83 * to the meta-data.
84 */
85void
86GNUNET_CONTAINER_meta_data_add_publication_date (struct
87 GNUNET_CONTAINER_MetaData
88 *md)
89{
90 char *dat;
91 struct GNUNET_TIME_Absolute t;
92
93 t = GNUNET_TIME_absolute_get ();
94 GNUNET_CONTAINER_meta_data_delete (md, EXTRACTOR_PUBLICATION_DATE, NULL);
95 dat = GNUNET_STRINGS_absolute_time_to_string (t);
96 GNUNET_CONTAINER_meta_data_insert (md, EXTRACTOR_PUBLICATION_DATE, dat);
97 GNUNET_free (dat);
98}
99
100/**
101 * Extend metadata.
102 * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists
103 */
104int
105GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
106 EXTRACTOR_KeywordType type,
107 const char *data)
108{
109 uint32_t idx;
110 char *p;
111
112 GNUNET_assert (data != NULL);
113 for (idx = 0; idx < md->itemCount; idx++)
114 {
115 if ((md->items[idx].type == type) &&
116 (0 == strcmp (md->items[idx].data, data)))
117 return GNUNET_SYSERR;
118 }
119 idx = md->itemCount;
120 GNUNET_array_grow (md->items, md->itemCount, md->itemCount + 1);
121 md->items[idx].type = type;
122 md->items[idx].data = p = GNUNET_strdup (data);
123
124 /* change OS native dir separators to unix '/' and others to '_' */
125 if (type == EXTRACTOR_FILENAME)
126 {
127 while (*p != '\0')
128 {
129 if (*p == DIR_SEPARATOR)
130 *p = '/';
131 else if (*p == '\\')
132 *p = '_';
133 p++;
134 }
135 }
136
137 return GNUNET_OK;
138}
139
140/**
141 * Remove an item.
142 * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md
143 */
144int
145GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
146 EXTRACTOR_KeywordType type,
147 const char *data)
148{
149 uint32_t idx;
150 int ret = GNUNET_SYSERR;
151 for (idx = 0; idx < md->itemCount; idx++)
152 {
153 if ((md->items[idx].type == type) &&
154 ((data == NULL) || (0 == strcmp (md->items[idx].data, data))))
155 {
156 GNUNET_free (md->items[idx].data);
157 md->items[idx] = md->items[md->itemCount - 1];
158 GNUNET_array_grow (md->items, md->itemCount, md->itemCount - 1);
159 if (data == NULL)
160 {
161 ret = GNUNET_OK;
162 continue;
163 }
164 return GNUNET_OK;
165 }
166 }
167 return ret;
168}
169
170/**
171 * Iterate over MD entries, excluding thumbnails.
172 *
173 * @return number of entries
174 */
175int
176GNUNET_CONTAINER_meta_data_get_contents (const struct
177 GNUNET_CONTAINER_MetaData *md,
178 GNUNET_CONTAINER_MetaDataProcessor
179 iterator, void *closure)
180{
181 uint32_t i;
182 uint32_t sub;
183
184 sub = 0;
185 for (i = 0; i < md->itemCount; i++)
186 {
187 if (!EXTRACTOR_isBinaryType (md->items[i].type))
188 {
189 if ((iterator != NULL) &&
190 (GNUNET_OK != iterator (md->items[i].type,
191 md->items[i].data, closure)))
192 return GNUNET_SYSERR;
193 }
194 else
195 sub++;
196 }
197 return (int) (md->itemCount - sub);
198}
199
200/**
201 * Iterate over MD entries
202 *
203 * @return number of entries
204 */
205char *
206GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
207 *md, EXTRACTOR_KeywordType type)
208{
209 uint32_t i;
210
211 for (i = 0; i < md->itemCount; i++)
212 if (type == md->items[i].type)
213 return GNUNET_strdup (md->items[i].data);
214 return NULL;
215}
216
217/**
218 * Iterate over MD entries
219 *
220 * @return number of entries
221 */
222char *
223GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
224 GNUNET_CONTAINER_MetaData *md,
225 ...)
226{
227 char *ret;
228 va_list args;
229 EXTRACTOR_KeywordType type;
230
231 ret = NULL;
232 va_start (args, md);
233 while (1)
234 {
235 type = va_arg (args, EXTRACTOR_KeywordType);
236 if (type == -1)
237 break;
238 ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type);
239 if (ret != NULL)
240 break;
241 }
242 va_end (args);
243 return ret;
244}
245
246/**
247 * Get a thumbnail from the meta-data (if present).
248 *
249 * @param thumb will be set to the thumbnail data. Must be
250 * freed by the caller!
251 * @return number of bytes in thumbnail, 0 if not available
252 */
253size_t
254GNUNET_CONTAINER_meta_data_get_thumbnail (const struct
255 GNUNET_CONTAINER_MetaData * md,
256 unsigned char **thumb)
257{
258 char *encoded;
259 int ret;
260 size_t size;
261
262 encoded =
263 GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_THUMBNAIL_DATA);
264 if (encoded == NULL)
265 return 0;
266 if (strlen (encoded) == 0)
267 {
268 GNUNET_free (encoded);
269 return 0; /* invalid */
270 }
271 *thumb = NULL;
272 ret = EXTRACTOR_binaryDecode (encoded, thumb, &size);
273 GNUNET_free (encoded);
274 if (ret != 0)
275 return 0;
276 return size;
277}
278
279/**
280 * Duplicate struct GNUNET_CONTAINER_MetaData.
281 */
282struct GNUNET_CONTAINER_MetaData *
283GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
284 *md)
285{
286 uint32_t i;
287 struct GNUNET_CONTAINER_MetaData *ret;
288
289 if (md == NULL)
290 return NULL;
291 ret = GNUNET_CONTAINER_meta_data_create ();
292 for (i = 0; i < md->itemCount; i++)
293 GNUNET_CONTAINER_meta_data_insert (ret, md->items[i].type,
294 md->items[i].data);
295 return ret;
296}
297
298/**
299 * Extract meta-data from a file.
300 *
301 * @return GNUNET_SYSERR on error, otherwise the number
302 * of meta-data items obtained
303 */
304int
305GNUNET_CONTAINER_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData
306 *md, const char *filename,
307 EXTRACTOR_ExtractorList *
308 extractors)
309{
310 EXTRACTOR_KeywordList *head;
311 EXTRACTOR_KeywordList *pos;
312 int ret;
313
314 if (filename == NULL)
315 return GNUNET_SYSERR;
316 if (extractors == NULL)
317 return 0;
318 head = EXTRACTOR_getKeywords (extractors, filename);
319 head = EXTRACTOR_removeDuplicateKeywords (head,
320 EXTRACTOR_DUPLICATES_REMOVE_UNKNOWN);
321 pos = head;
322 ret = 0;
323 while (pos != NULL)
324 {
325 if (GNUNET_OK ==
326 GNUNET_CONTAINER_meta_data_insert (md, pos->keywordType,
327 pos->keyword))
328 ret++;
329 pos = pos->next;
330 }
331 EXTRACTOR_freeKeywords (head);
332 return ret;
333}
334
335static unsigned int
336tryCompression (char *data, unsigned int oldSize)
337{
338 char *tmp;
339 uLongf dlen;
340
341#ifdef compressBound
342 dlen = compressBound (oldSize);
343#else
344 dlen = oldSize + (oldSize / 100) + 20;
345 /* documentation says 100.1% oldSize + 12 bytes, but we
346 should be able to overshoot by more to be safe */
347#endif
348 tmp = GNUNET_malloc (dlen);
349 if (Z_OK == compress2 ((Bytef *) tmp,
350 &dlen, (const Bytef *) data, oldSize, 9))
351 {
352 if (dlen < oldSize)
353 {
354 memcpy (data, tmp, dlen);
355 GNUNET_free (tmp);
356 return dlen;
357 }
358 }
359 GNUNET_free (tmp);
360 return oldSize;
361}
362
363/**
364 * Decompress input, return the decompressed data
365 * as output, set outputSize to the number of bytes
366 * that were found.
367 *
368 * @return NULL on error
369 */
370static char *
371decompress (const char *input,
372 unsigned int inputSize, unsigned int outputSize)
373{
374 char *output;
375 uLongf olen;
376
377 olen = outputSize;
378 output = GNUNET_malloc (olen);
379 if (Z_OK == uncompress ((Bytef *) output,
380 &olen, (const Bytef *) input, inputSize))
381 {
382 return output;
383 }
384 else
385 {
386 GNUNET_free (output);
387 return NULL;
388 }
389}
390
391/**
392 * Flag in 'version' that indicates compressed meta-data.
393 */
394#define HEADER_COMPRESSED 0x80000000
395
396/**
397 * Bits in 'version' that give the version number.
398 */
399#define HEADER_VERSION_MASK 0x7FFFFFFF
400
401struct MetaDataHeader
402{
403 /**
404 * The version of the MD serialization.
405 * The highest bit is used to indicate
406 * compression.
407 *
408 * Version 0 is the current version;
409 * Version is 1 for a NULL pointer.
410 * Other version numbers are not yet defined.
411 */
412 uint32_t version;
413
414 /**
415 * How many MD entries are there?
416 */
417 uint32_t entries;
418
419 /**
420 * Size of the MD (decompressed)
421 */
422 uint32_t size;
423
424 /**
425 * This is followed by 'entries' values of type 'unsigned int' that
426 * correspond to EXTRACTOR_KeywordTypes. After that, the meta-data
427 * keywords follow (0-terminated). The MD block always ends with
428 * 0-termination, padding with 0 until a multiple of 8 bytes.
429 */
430
431};
432
433/**
434 * Serialize meta-data to target.
435 *
436 * @param size maximum number of bytes available
437 * @param part is it ok to just write SOME of the
438 * meta-data to match the size constraint,
439 * possibly discarding some data?
440 * @return number of bytes written on success,
441 * GNUNET_SYSERR on error (typically: not enough
442 * space)
443 */
444int
445GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
446 *md, char *target, unsigned int max,
447 enum
448 GNUNET_CONTAINER_MetaDataSerializationOptions
449 part)
450{
451 struct MetaDataHeader *hdr;
452 size_t size;
453 size_t pos;
454 uint32_t i;
455 size_t len;
456 uint32_t ic;
457
458 if (max < sizeof (struct MetaDataHeader))
459 return GNUNET_SYSERR; /* far too small */
460 ic = md ? md->itemCount : 0;
461 hdr = NULL;
462 while (1)
463 {
464 size = sizeof (struct MetaDataHeader);
465 size += sizeof (unsigned int) * ic;
466 for (i = 0; i < ic; i++)
467 size += 1 + strlen (md->items[i].data);
468 while (size % 8 != 0)
469 size++;
470 hdr = GNUNET_malloc (size);
471 hdr->version = htonl (md == NULL ? 1 : 0);
472 hdr->entries = htonl (ic);
473 for (i = 0; i < ic; i++)
474 ((unsigned int *) &hdr[1])[i] =
475 htonl ((unsigned int) md->items[i].type);
476 pos = sizeof (struct MetaDataHeader);
477 pos += sizeof (unsigned int) * ic;
478 for (i = 0; i < ic; i++)
479 {
480 len = strlen (md->items[i].data) + 1;
481 memcpy (&((char *) hdr)[pos], md->items[i].data, len);
482 pos += len;
483 }
484
485 hdr->size = htonl (size);
486 if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
487 {
488 pos = tryCompression ((char *) &hdr[1],
489 size - sizeof (struct MetaDataHeader));
490 }
491 else
492 {
493 pos = size - sizeof (struct MetaDataHeader);
494 }
495 if (pos < size - sizeof (struct MetaDataHeader))
496 {
497 hdr->version = htonl (HEADER_COMPRESSED);
498 size = pos + sizeof (struct MetaDataHeader);
499 }
500 if (size <= max)
501 break;
502 GNUNET_free (hdr);
503 hdr = NULL;
504
505 if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART) == 0)
506 {
507 return GNUNET_SYSERR; /* does not fit! */
508 }
509 /* partial serialization ok, try again with less meta-data */
510 if (size > 2 * max)
511 ic = ic * 2 / 3; /* still far too big, make big reductions */
512 else
513 ic--; /* small steps, we're close */
514 }
515 GNUNET_assert (size <= max);
516 memcpy (target, hdr, size);
517 GNUNET_free (hdr);
518 /* extra check: deserialize! */
519#if EXTRA_CHECKS
520 {
521 struct GNUNET_CONTAINER_MetaData *mdx;
522 mdx = GNUNET_CONTAINER_meta_data_deserialize (target, size);
523 GNUNET_assert (NULL != mdx);
524 GNUNET_CONTAINER_meta_data_destroy (mdx);
525 }
526#endif
527 return size;
528}
529
530/**
531 * Estimate (!) the size of the meta-data in
532 * serialized form. The estimate MAY be higher
533 * than what is strictly needed.
534 */
535unsigned int
536GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
537 GNUNET_CONTAINER_MetaData *md,
538 enum
539 GNUNET_CONTAINER_MetaDataSerializationOptions
540 part)
541{
542 struct MetaDataHeader *hdr;
543 size_t size;
544 size_t pos;
545 uint32_t i;
546 size_t len;
547 uint32_t ic;
548
549 ic = md ? md->itemCount : 0;
550 size = sizeof (struct MetaDataHeader);
551 size += sizeof (unsigned int) * ic;
552 for (i = 0; i < ic; i++)
553 size += 1 + strlen (md->items[i].data);
554 while (size % 8 != 0)
555 size++;
556 hdr = GNUNET_malloc (size);
557 hdr->version = htonl (md == NULL ? 1 : 0);
558 hdr->entries = htonl (ic);
559 for (i = 0; i < ic; i++)
560 ((unsigned int *) &hdr[1])[i] = htonl ((unsigned int) md->items[i].type);
561 pos = sizeof (struct MetaDataHeader);
562 pos += sizeof (unsigned int) * ic;
563 for (i = 0; i < ic; i++)
564 {
565 len = strlen (md->items[i].data) + 1;
566 memcpy (&((char *) hdr)[pos], md->items[i].data, len);
567 pos += len;
568 }
569 if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
570 {
571 pos =
572 tryCompression ((char *) &hdr[1],
573 size - sizeof (struct MetaDataHeader));
574 }
575 else
576 {
577 pos = size - sizeof (struct MetaDataHeader);
578 }
579 if (pos < size - sizeof (struct MetaDataHeader))
580 size = pos + sizeof (struct MetaDataHeader);
581 GNUNET_free (hdr);
582 return size;
583}
584
585/**
586 * Deserialize meta-data. Initializes md.
587 * @param size number of bytes available
588 * @return MD on success, NULL on error (i.e.
589 * bad format)
590 */
591struct GNUNET_CONTAINER_MetaData *
592GNUNET_CONTAINER_meta_data_deserialize (const char *input, unsigned int size)
593{
594 struct GNUNET_CONTAINER_MetaData *md;
595 const struct MetaDataHeader *hdr;
596 uint32_t ic;
597 char *data;
598 const char *cdata;
599 uint32_t dataSize;
600 int compressed;
601 int i;
602 unsigned int pos;
603 int len;
604 uint32_t version;
605
606 if (size < sizeof (struct MetaDataHeader))
607 return NULL;
608 hdr = (const struct MetaDataHeader *) input;
609 version = ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_VERSION_MASK;
610 if (version == 1)
611 return NULL; /* null pointer */
612 if (version != 0)
613 {
614 GNUNET_break_op (0); /* unsupported version */
615 return NULL;
616 }
617 ic = ntohl (MAKE_UNALIGNED (hdr->entries));
618 compressed =
619 (ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_COMPRESSED) != 0;
620 if (compressed)
621 {
622 dataSize =
623 ntohl (MAKE_UNALIGNED (hdr->size)) - sizeof (struct MetaDataHeader);
624 if (dataSize > 2 * 1042 * 1024)
625 {
626 GNUNET_break (0);
627 return NULL; /* only 2 MB allowed [to make sure we don't blow
628 our memory limit because of a mal-formed
629 message... ] */
630 }
631 data =
632 decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
633 size - sizeof (struct MetaDataHeader), dataSize);
634 if (data == NULL)
635 {
636 GNUNET_break_op (0);
637 return NULL;
638 }
639 cdata = data;
640 }
641 else
642 {
643 data = NULL;
644 cdata = (const char *) &hdr[1];
645 dataSize = size - sizeof (struct MetaDataHeader);
646 if (size != ntohl (MAKE_UNALIGNED (hdr->size)))
647 {
648 GNUNET_break (0);
649 return NULL;
650 }
651 }
652
653 if ((sizeof (unsigned int) * ic + ic) > dataSize)
654 {
655 GNUNET_break (0);
656 goto FAILURE;
657 }
658 if ((ic > 0) && (cdata[dataSize - 1] != '\0'))
659 {
660 GNUNET_break (0);
661 goto FAILURE;
662 }
663
664 md = GNUNET_CONTAINER_meta_data_create ();
665 GNUNET_array_grow (md->items, md->itemCount, ic);
666 i = 0;
667 pos = sizeof (unsigned int) * ic;
668 while ((pos < dataSize) && (i < ic))
669 {
670 len = strlen (&cdata[pos]) + 1;
671 md->items[i].type = (EXTRACTOR_KeywordType)
672 ntohl (MAKE_UNALIGNED (((const unsigned int *) cdata)[i]));
673 md->items[i].data = GNUNET_strdup (&cdata[pos]);
674 pos += len;
675 i++;
676 }
677 if (i < ic)
678 { /* oops */
679 GNUNET_CONTAINER_meta_data_destroy (md);
680 goto FAILURE;
681 }
682 GNUNET_free_non_null (data);
683 return md;
684FAILURE:
685 GNUNET_free_non_null (data);
686 return NULL; /* size too small */
687}
688
689/**
690 * Test if two MDs are equal.
691 */
692int
693GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
694 *md1,
695 const struct GNUNET_CONTAINER_MetaData
696 *md2)
697{
698 uint32_t i;
699 uint32_t j;
700 int found;
701
702 if (md1->itemCount != md2->itemCount)
703 return GNUNET_NO;
704 for (i = 0; i < md1->itemCount; i++)
705 {
706 found = GNUNET_NO;
707 for (j = 0; j < md2->itemCount; j++)
708 if ((md1->items[i].type == md2->items[j].type) &&
709 (0 == strcmp (md1->items[i].data, md2->items[j].data)))
710 {
711 found = GNUNET_YES;
712 break;
713 }
714 if (found == GNUNET_NO)
715 return GNUNET_NO;
716 }
717 return GNUNET_YES;
718}
719
720
721/* end of container_meta_data.c */
diff --git a/src/util/container_multihashmap.c b/src/util/container_multihashmap.c
new file mode 100644
index 000000000..a84955eb3
--- /dev/null
+++ b/src/util/container_multihashmap.c
@@ -0,0 +1,334 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/container_multihashmap.c
22 * @brief hash map where the same key maybe present multiple times
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_container_lib.h"
29#include "gnunet_crypto_lib.h"
30
31struct MapEntry
32{
33 GNUNET_HashCode key;
34 void *value;
35 struct MapEntry *next;
36};
37
38struct GNUNET_CONTAINER_MultiHashMap
39{
40
41 struct MapEntry **map;
42
43 unsigned int size;
44
45 unsigned int map_length;
46};
47
48struct GNUNET_CONTAINER_MultiHashMap *
49GNUNET_CONTAINER_multihashmap_create (unsigned int len)
50{
51 struct GNUNET_CONTAINER_MultiHashMap *ret;
52
53 ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MultiHashMap));
54 ret->size = 0;
55 ret->map = GNUNET_malloc (len * sizeof (struct MapEntry *));
56 memset (ret->map, 0, len * sizeof (struct MapEntry *));
57 ret->map_length = len;
58 return ret;
59}
60
61void
62GNUNET_CONTAINER_multihashmap_destroy (struct GNUNET_CONTAINER_MultiHashMap
63 *map)
64{
65 unsigned int i;
66 struct MapEntry *e;
67
68 for (i = 0; i < map->map_length; i++)
69 {
70 while (NULL != (e = map->map[i]))
71 {
72 map->map[i] = e->next;
73 GNUNET_free (e);
74 }
75 }
76 GNUNET_free (map->map);
77 GNUNET_free (map);
78}
79
80static unsigned int
81idx_of (const struct GNUNET_CONTAINER_MultiHashMap *m,
82 const GNUNET_HashCode * key)
83{
84 return (*(unsigned int *) key) % m->map_length;
85}
86
87unsigned int
88GNUNET_CONTAINER_multihashmap_size (const struct GNUNET_CONTAINER_MultiHashMap
89 *map)
90{
91 return map->size;
92}
93
94void *
95GNUNET_CONTAINER_multihashmap_get (const struct GNUNET_CONTAINER_MultiHashMap
96 *map, const GNUNET_HashCode * key)
97{
98 struct MapEntry *e;
99
100 e = map->map[idx_of (map, key)];
101 while (e != NULL)
102 {
103 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
104 return e->value;
105 e = e->next;
106 }
107 return NULL;
108}
109
110int
111GNUNET_CONTAINER_multihashmap_iterate (const struct
112 GNUNET_CONTAINER_MultiHashMap *map,
113 GNUNET_CONTAINER_HashMapIterator it,
114 void *cls)
115{
116 int count;
117 unsigned int i;
118 struct MapEntry *e;
119
120 count = 0;
121 for (i = 0; i < map->map_length; i++)
122 {
123 e = map->map[i];
124 while (e != NULL)
125 {
126 if ((NULL != it) && (GNUNET_OK != it (&e->key, e->value, cls)))
127 return GNUNET_SYSERR;
128 count++;
129 e = e->next;
130 }
131 }
132 return count;
133}
134
135int
136GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap
137 *map, const GNUNET_HashCode * key,
138 void *value)
139{
140 struct MapEntry *e;
141 struct MapEntry *p;
142 unsigned int i;
143
144 i = idx_of (map, key);
145 p = NULL;
146 e = map->map[i];
147 while (e != NULL)
148 {
149 if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
150 (value == e->value))
151 {
152 if (p == NULL)
153 map->map[i] = e->next;
154 else
155 p->next = e->next;
156 GNUNET_free (e);
157 map->size--;
158 return GNUNET_YES;
159 }
160 p = e;
161 e = e->next;
162 }
163 return GNUNET_NO;
164}
165
166int
167GNUNET_CONTAINER_multihashmap_remove_all (struct GNUNET_CONTAINER_MultiHashMap
168 *map, const GNUNET_HashCode * key)
169{
170 struct MapEntry *e;
171 struct MapEntry *p;
172 unsigned int i;
173 int ret;
174
175 ret = 0;
176 i = idx_of (map, key);
177 p = NULL;
178 e = map->map[i];
179 while (e != NULL)
180 {
181 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
182 {
183 if (p == NULL)
184 map->map[i] = e->next;
185 else
186 p->next = e->next;
187 GNUNET_free (e);
188 map->size--;
189 if (p == NULL)
190 e = map->map[i];
191 else
192 e = p->next;
193 ret++;
194 }
195 else
196 {
197 p = e;
198 e = e->next;
199 }
200 }
201 return ret;
202}
203
204int
205GNUNET_CONTAINER_multihashmap_contains (const struct
206 GNUNET_CONTAINER_MultiHashMap *map,
207 const GNUNET_HashCode * key)
208{
209 struct MapEntry *e;
210 unsigned int i;
211
212 i = idx_of (map, key);
213 e = map->map[i];
214 while (e != NULL)
215 {
216 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
217 return GNUNET_YES;
218 e = e->next;
219 }
220 return GNUNET_NO;
221}
222
223static void
224grow (struct GNUNET_CONTAINER_MultiHashMap *map)
225{
226 struct MapEntry **old;
227 struct MapEntry *e;
228 unsigned int i;
229 unsigned int l;
230
231 old = map->map;
232 l = map->map_length;
233 map->map_length *= 2;
234 map->map = GNUNET_malloc (sizeof (struct MapEntry *) * map->map_length);
235 memset (map->map, 0, sizeof (struct MapEntry *) * map->map_length);
236 for (i = 0; i < l; i++)
237 {
238 while (NULL != (e = old[i]))
239 {
240 old[i] = e->next;
241 e->next = map->map[idx_of (map, &e->key)];
242 map->map[idx_of (map, &e->key)] = e;
243 }
244 }
245 GNUNET_free (old);
246}
247
248int
249GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap *map,
250 const GNUNET_HashCode * key,
251 void *value,
252 enum GNUNET_CONTAINER_MultiHashMapOption
253 opt)
254{
255 struct MapEntry *e;
256 unsigned int i;
257
258 i = idx_of (map, key);
259 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
260 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
261 {
262 e = map->map[i];
263 while (e != NULL)
264 {
265 if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
266 (value == e->value))
267 {
268 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
269 return GNUNET_SYSERR;
270 e->value = value;
271 return GNUNET_NO;
272 }
273 e = e->next;
274 }
275 }
276 if (map->size / 3 > map->map_length / 4)
277 grow (map);
278 e = GNUNET_malloc (sizeof (struct MapEntry));
279 e->key = *key;
280 e->value = value;
281 e->next = map->map[i];
282 map->map[i] = e;
283 map->size++;
284 return GNUNET_OK;
285}
286
287int
288GNUNET_CONTAINER_multihashmap_get_multiple (const struct
289 GNUNET_CONTAINER_MultiHashMap
290 *map, const GNUNET_HashCode * key,
291 GNUNET_CONTAINER_HashMapIterator
292 it, void *cls)
293{
294 int count;
295 struct MapEntry *e;
296
297 count = 0;
298 e = map->map[idx_of (map, key)];
299 while (e != NULL)
300 {
301 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
302 {
303 if ((it != NULL) && (GNUNET_OK != it (&e->key, e->value, cls)))
304 return GNUNET_SYSERR;
305 count++;
306 }
307 e = e->next;
308 }
309 return count;
310}
311
312void *
313GNUNET_CONTAINER_multihashmap_get_random (const struct
314 GNUNET_CONTAINER_MultiHashMap *map)
315{
316 unsigned int rand;
317 struct MapEntry *e;
318 e = NULL;
319
320 if (map->size == 0)
321 return NULL;
322
323 while (e == NULL)
324 {
325 rand =
326 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
327 map->map_length);
328 e = map->map[rand];
329 }
330
331 return e->value;
332}
333
334/* end of multihashmap.c */
diff --git a/src/util/crypto_aes.c b/src/util/crypto_aes.c
new file mode 100644
index 000000000..28a65dfca
--- /dev/null
+++ b/src/util/crypto_aes.c
@@ -0,0 +1,148 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/crypto_aes.c
23 * @brief Symmetric encryption services.
24 * @author Christian Grothoff
25 * @author Ioana Patrascu
26 */
27
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_crypto_lib.h"
31#include <gcrypt.h>
32
33/**
34 * Create a new SessionKey (for AES-256).
35 */
36void
37GNUNET_CRYPTO_aes_create_session_key (struct GNUNET_CRYPTO_AesSessionKey *key)
38{
39 gcry_randomize (&key->key[0], GNUNET_CRYPTO_AES_KEY_LENGTH,
40 GCRY_STRONG_RANDOM);
41 key->crc32 =
42 htonl (GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH));
43}
44
45/**
46 * Check that a new session key is well-formed.
47 *
48 * @return GNUNET_OK if the key is valid
49 */
50int
51GNUNET_CRYPTO_aes_check_session_key (const struct GNUNET_CRYPTO_AesSessionKey
52 *key)
53{
54 uint32_t crc;
55
56 crc = GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH);
57 if (ntohl (key->crc32) == crc)
58 return GNUNET_OK;
59 GNUNET_break (0);
60 return GNUNET_SYSERR;
61}
62
63
64/**
65 * Encrypt a block with the public key of another
66 * host that uses the same cyper.
67 * @param block the block to encrypt
68 * @param len the size of the block
69 * @param sessionkey the key used to encrypt
70 * @param iv the initialization vector to use, use INITVALUE
71 * for streams.
72 * @param result the output parameter in which to store the encrypted result
73 * @returns the size of the encrypted block, -1 for errors
74 */
75int
76GNUNET_CRYPTO_aes_encrypt (const void *block,
77 uint16_t len,
78 const struct GNUNET_CRYPTO_AesSessionKey
79 *sessionkey,
80 const struct GNUNET_CRYPTO_AesInitializationVector
81 *iv, void *result)
82{
83 gcry_cipher_hd_t handle;
84 int rc;
85
86 if (sessionkey->crc32 !=
87 htonl (GNUNET_CRYPTO_crc32_n
88 (sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH)))
89 {
90 GNUNET_break (0);
91 return GNUNET_SYSERR;
92 }
93 GNUNET_assert (0 == gcry_cipher_open (&handle,
94 GCRY_CIPHER_AES256,
95 GCRY_CIPHER_MODE_CFB, 0));
96 rc = gcry_cipher_setkey (handle, sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH);
97 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
98 rc =
99 gcry_cipher_setiv (handle, iv,
100 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
101 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
102 GNUNET_assert (0 == gcry_cipher_encrypt (handle, result, len, block, len));
103 gcry_cipher_close (handle);
104 return len;
105}
106
107/**
108 * Decrypt a given block with the sessionkey.
109 * @param sessionkey the key used to decrypt
110 * @param block the data to decrypt, encoded as returned by encrypt
111 * @param size the size of the block to decrypt
112 * @param iv the initialization vector to use, use INITVALUE
113 * for streams.
114 * @param result address to store the result at
115 * @return -1 on failure, size of decrypted block on success
116 */
117int
118GNUNET_CRYPTO_aes_decrypt (const struct GNUNET_CRYPTO_AesSessionKey
119 *sessionkey, const void *block, uint16_t size,
120 const struct GNUNET_CRYPTO_AesInitializationVector
121 *iv, void *result)
122{
123 gcry_cipher_hd_t handle;
124 int rc;
125
126 if (sessionkey->crc32 !=
127 htonl (GNUNET_CRYPTO_crc32_n
128 (sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH)))
129 {
130 GNUNET_break (0);
131 return GNUNET_SYSERR;
132 }
133 GNUNET_assert (0 == gcry_cipher_open (&handle,
134 GCRY_CIPHER_AES256,
135 GCRY_CIPHER_MODE_CFB, 0));
136 rc = gcry_cipher_setkey (handle, sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH);
137 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
138 rc =
139 gcry_cipher_setiv (handle, iv,
140 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
141 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
142 GNUNET_assert (0 ==
143 gcry_cipher_decrypt (handle, result, size, block, size));
144 gcry_cipher_close (handle);
145 return size;
146}
147
148/* end of crypto_aes.c */
diff --git a/src/util/crypto_crc.c b/src/util/crypto_crc.c
new file mode 100644
index 000000000..35d1e2576
--- /dev/null
+++ b/src/util/crypto_crc.c
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20 For the actual CRC code:
21 Copyright abandoned; this code is in the public domain.
22 Provided to GNUnet by peter@horizon.com
23*/
24
25/**
26 * @file util/crypto_crc.c
27 * @brief implementation of CRC32
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_common.h"
33#include "gnunet_crypto_lib.h"
34
35/* Avoid wasting space on 8-byte longs. */
36#if UINT_MAX >= 0xffffffff
37typedef unsigned int uLong;
38#elif ULONG_MAX >= 0xffffffff
39typedef unsigned long uLong;
40#else
41#error This compiler is not ANSI-compliant!
42#endif
43
44#define Z_NULL 0
45
46
47#define POLYNOMIAL (uLong)0xedb88320
48static uLong crc_table[256];
49
50/*
51 * This routine writes each crc_table entry exactly once,
52 * with the ccorrect final value. Thus, it is safe to call
53 * even on a table that someone else is using concurrently.
54 */
55void __attribute__ ((constructor)) GNUNET_CRYPTO_crc_init ()
56{
57 unsigned int i, j;
58 uLong h = 1;
59 crc_table[0] = 0;
60 for (i = 128; i; i >>= 1)
61 {
62 h = (h >> 1) ^ ((h & 1) ? POLYNOMIAL : 0);
63 /* h is now crc_table[i] */
64 for (j = 0; j < 256; j += 2 * i)
65 crc_table[i + j] = crc_table[j] ^ h;
66 }
67}
68
69/*
70 * This computes the standard preset and inverted CRC, as used
71 * by most networking standards. Start by passing in an initial
72 * chaining value of 0, and then pass in the return value from the
73 * previous crc32() call. The final return value is the CRC.
74 * Note that this is a little-endian CRC, which is best used with
75 * data transmitted lsbit-first, and it should, itself, be appended
76 * to data in little-endian byte and bit order to preserve the
77 * property of detecting all burst errors of length 32 bits or less.
78 */
79static uLong
80crc32 (uLong crc, const char *buf, size_t len)
81{
82 GNUNET_assert (crc_table[255] != 0);
83 crc ^= 0xffffffff;
84 while (len--)
85 crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff];
86 return crc ^ 0xffffffff;
87}
88
89
90/**
91 * Compute the CRC32 checksum for the first len bytes of the buffer.
92 *
93 * @param buf the data over which we're taking the CRC
94 * @param len the length of the buffer
95 * @return the resulting CRC32 checksum
96 */
97int
98GNUNET_CRYPTO_crc32_n (const void *buf, unsigned int len)
99{
100 uLong crc;
101 crc = crc32 (0L, Z_NULL, 0);
102 crc = crc32 (crc, (char *) buf, len);
103 return crc;
104}
105
106/* end of crc32.c */
diff --git a/src/util/crypto_hash.c b/src/util/crypto_hash.c
new file mode 100644
index 000000000..139496eac
--- /dev/null
+++ b/src/util/crypto_hash.c
@@ -0,0 +1,791 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20 SHA-512 code by Jean-Luc Cooke <jlcooke@certainkey.com>
21
22 Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com>
23 Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
24 Copyright (c) 2003 Kyle McMartin <kyle@debian.org>
25*/
26
27/**
28 * @file util/crypto_hash.c
29 * @brief SHA-512 GNUNET_CRYPTO_hash related functions
30 * @author Christian Grothoff
31 */
32
33#include "platform.h"
34#include "gnunet_common.h"
35#include "gnunet_crypto_lib.h"
36#include "gnunet_disk_lib.h"
37
38#define SHA512_DIGEST_SIZE 64
39#define SHA512_HMAC_BLOCK_SIZE 128
40
41struct sha512_ctx
42{
43 unsigned long long state[8];
44 unsigned int count[4];
45 unsigned char buf[128];
46};
47
48static unsigned long long
49Ch (unsigned long long x, unsigned long long y, unsigned long long z)
50{
51 return z ^ (x & (y ^ z));
52}
53
54static unsigned long long
55Maj (unsigned long long x, unsigned long long y, unsigned long long z)
56{
57 return (x & y) | (z & (x | y));
58}
59
60static unsigned long long
61RORu64 (unsigned long long x, unsigned long long y)
62{
63 return (x >> y) | (x << (64 - y));
64}
65
66const unsigned long long sha512_K[80] = {
67 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,
68 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
69 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,
70 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
71 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
72 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
73 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,
74 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
75 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,
76 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
77 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,
78 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
79 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,
80 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
81 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
82 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
83 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,
84 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
85 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,
86 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
87 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,
88 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
89 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,
90 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
91 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
92 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
93 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL,
94};
95
96#define e0(x) (RORu64(x,28) ^ RORu64(x,34) ^ RORu64(x,39))
97#define e1(x) (RORu64(x,14) ^ RORu64(x,18) ^ RORu64(x,41))
98#define s0(x) (RORu64(x, 1) ^ RORu64(x, 8) ^ (x >> 7))
99#define s1(x) (RORu64(x,19) ^ RORu64(x,61) ^ (x >> 6))
100
101/* H* initial state for SHA-512 */
102#define H0 0x6a09e667f3bcc908ULL
103#define H1 0xbb67ae8584caa73bULL
104#define H2 0x3c6ef372fe94f82bULL
105#define H3 0xa54ff53a5f1d36f1ULL
106#define H4 0x510e527fade682d1ULL
107#define H5 0x9b05688c2b3e6c1fULL
108#define H6 0x1f83d9abfb41bd6bULL
109#define H7 0x5be0cd19137e2179ULL
110
111/* H'* initial state for SHA-384 */
112#define HP0 0xcbbb9d5dc1059ed8ULL
113#define HP1 0x629a292a367cd507ULL
114#define HP2 0x9159015a3070dd17ULL
115#define HP3 0x152fecd8f70e5939ULL
116#define HP4 0x67332667ffc00b31ULL
117#define HP5 0x8eb44a8768581511ULL
118#define HP6 0xdb0c2e0d64f98fa7ULL
119#define HP7 0x47b5481dbefa4fa4ULL
120
121#define LOAD_OP(t1, I, W, input) \
122 t1 = input[(8*I) ] & 0xff;\
123 t1 <<= 8;\
124 t1 |= input[(8*I)+1] & 0xff;\
125 t1 <<= 8;\
126 t1 |= input[(8*I)+2] & 0xff;\
127 t1 <<= 8;\
128 t1 |= input[(8*I)+3] & 0xff;\
129 t1 <<= 8;\
130 t1 |= input[(8*I)+4] & 0xff;\
131 t1 <<= 8;\
132 t1 |= input[(8*I)+5] & 0xff;\
133 t1 <<= 8;\
134 t1 |= input[(8*I)+6] & 0xff;\
135 t1 <<= 8;\
136 t1 |= input[(8*I)+7] & 0xff;\
137 W[I] = t1;
138
139
140#define BLEND_OP(I, W) \
141 W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16];
142
143static void
144sha512_transform (unsigned long long *state, const unsigned char *input)
145{
146 unsigned long long a, b, c, d, e, f, g, h, t1, t2;
147 unsigned long long W[80];
148 unsigned long long t0;
149 int i;
150
151 /* load the input */
152 for (i = 0; i < 16; i++)
153 {
154 LOAD_OP (t0, i, W, input);
155 }
156
157 for (i = 16; i < 80; i++)
158 {
159 BLEND_OP (i, W);
160 }
161
162 /* load the state into our registers */
163 a = state[0];
164 b = state[1];
165 c = state[2];
166 d = state[3];
167 e = state[4];
168 f = state[5];
169 g = state[6];
170 h = state[7];
171
172 /* now iterate */
173 for (i = 0; i < 80; i += 8)
174 {
175 t1 = h + e1 (e) + Ch (e, f, g) + sha512_K[i] + W[i];
176 t2 = e0 (a) + Maj (a, b, c);
177 d += t1;
178 h = t1 + t2;
179 t1 = g + e1 (d) + Ch (d, e, f) + sha512_K[i + 1] + W[i + 1];
180 t2 = e0 (h) + Maj (h, a, b);
181 c += t1;
182 g = t1 + t2;
183 t1 = f + e1 (c) + Ch (c, d, e) + sha512_K[i + 2] + W[i + 2];
184 t2 = e0 (g) + Maj (g, h, a);
185 b += t1;
186 f = t1 + t2;
187 t1 = e + e1 (b) + Ch (b, c, d) + sha512_K[i + 3] + W[i + 3];
188 t2 = e0 (f) + Maj (f, g, h);
189 a += t1;
190 e = t1 + t2;
191 t1 = d + e1 (a) + Ch (a, b, c) + sha512_K[i + 4] + W[i + 4];
192 t2 = e0 (e) + Maj (e, f, g);
193 h += t1;
194 d = t1 + t2;
195 t1 = c + e1 (h) + Ch (h, a, b) + sha512_K[i + 5] + W[i + 5];
196 t2 = e0 (d) + Maj (d, e, f);
197 g += t1;
198 c = t1 + t2;
199 t1 = b + e1 (g) + Ch (g, h, a) + sha512_K[i + 6] + W[i + 6];
200 t2 = e0 (c) + Maj (c, d, e);
201 f += t1;
202 b = t1 + t2;
203 t1 = a + e1 (f) + Ch (f, g, h) + sha512_K[i + 7] + W[i + 7];
204 t2 = e0 (b) + Maj (b, c, d);
205 e += t1;
206 a = t1 + t2;
207 }
208
209 state[0] += a;
210 state[1] += b;
211 state[2] += c;
212 state[3] += d;
213 state[4] += e;
214 state[5] += f;
215 state[6] += g;
216 state[7] += h;
217
218 /* erase our data */
219 a = b = c = d = e = f = g = h = t1 = t2 = 0;
220 memset (W, 0, 80 * sizeof (unsigned long long));
221}
222
223static void
224sha512_init (struct sha512_ctx *sctx)
225{
226 sctx->state[0] = H0;
227 sctx->state[1] = H1;
228 sctx->state[2] = H2;
229 sctx->state[3] = H3;
230 sctx->state[4] = H4;
231 sctx->state[5] = H5;
232 sctx->state[6] = H6;
233 sctx->state[7] = H7;
234 sctx->count[0] = sctx->count[1] = sctx->count[2] = sctx->count[3] = 0;
235 memset (sctx->buf, 0, sizeof (sctx->buf));
236}
237
238static void
239sha512_update (struct sha512_ctx *sctx,
240 const unsigned char *data, unsigned int len)
241{
242 unsigned int i, index, part_len;
243
244 /* Compute number of bytes mod 128 */
245 index = (unsigned int) ((sctx->count[0] >> 3) & 0x7F);
246
247 /* Update number of bits */
248 if ((sctx->count[0] += (len << 3)) < (len << 3))
249 {
250 if ((sctx->count[1] += 1) < 1)
251 if ((sctx->count[2] += 1) < 1)
252 sctx->count[3]++;
253 sctx->count[1] += (len >> 29);
254 }
255
256 part_len = 128 - index;
257
258 /* Transform as many times as possible. */
259 if (len >= part_len)
260 {
261 memcpy (&sctx->buf[index], data, part_len);
262 sha512_transform (sctx->state, sctx->buf);
263
264 for (i = part_len; i + 127 < len; i += 128)
265 sha512_transform (sctx->state, &data[i]);
266
267 index = 0;
268 }
269 else
270 {
271 i = 0;
272 }
273
274 /* Buffer remaining input */
275 memcpy (&sctx->buf[index], &data[i], len - i);
276}
277
278static void
279sha512_final (struct sha512_ctx *sctx, unsigned char *hash)
280{
281 static unsigned char padding[128] = { 0x80, };
282
283 unsigned int t;
284 unsigned long long t2;
285 unsigned char bits[128];
286 unsigned int index, pad_len;
287 int i, j;
288
289 index = pad_len = t = i = j = 0;
290 t2 = 0;
291
292 /* Save number of bits */
293 t = sctx->count[0];
294 bits[15] = t;
295 t >>= 8;
296 bits[14] = t;
297 t >>= 8;
298 bits[13] = t;
299 t >>= 8;
300 bits[12] = t;
301 t = sctx->count[1];
302 bits[11] = t;
303 t >>= 8;
304 bits[10] = t;
305 t >>= 8;
306 bits[9] = t;
307 t >>= 8;
308 bits[8] = t;
309 t = sctx->count[2];
310 bits[7] = t;
311 t >>= 8;
312 bits[6] = t;
313 t >>= 8;
314 bits[5] = t;
315 t >>= 8;
316 bits[4] = t;
317 t = sctx->count[3];
318 bits[3] = t;
319 t >>= 8;
320 bits[2] = t;
321 t >>= 8;
322 bits[1] = t;
323 t >>= 8;
324 bits[0] = t;
325
326 /* Pad out to 112 mod 128. */
327 index = (sctx->count[0] >> 3) & 0x7f;
328 pad_len = (index < 112) ? (112 - index) : ((128 + 112) - index);
329 sha512_update (sctx, padding, pad_len);
330
331 /* Append length (before padding) */
332 sha512_update (sctx, bits, 16);
333
334 /* Store state in digest */
335 for (i = j = 0; i < 8; i++, j += 8)
336 {
337 t2 = sctx->state[i];
338 hash[j + 7] = (char) t2 & 0xff;
339 t2 >>= 8;
340 hash[j + 6] = (char) t2 & 0xff;
341 t2 >>= 8;
342 hash[j + 5] = (char) t2 & 0xff;
343 t2 >>= 8;
344 hash[j + 4] = (char) t2 & 0xff;
345 t2 >>= 8;
346 hash[j + 3] = (char) t2 & 0xff;
347 t2 >>= 8;
348 hash[j + 2] = (char) t2 & 0xff;
349 t2 >>= 8;
350 hash[j + 1] = (char) t2 & 0xff;
351 t2 >>= 8;
352 hash[j] = (char) t2 & 0xff;
353 }
354
355 /* Zeroize sensitive information. */
356 memset (sctx, 0, sizeof (struct sha512_ctx));
357}
358
359/**
360 * Hash block of given size.
361 *
362 * @param block the data to GNUNET_CRYPTO_hash, length is given as a second argument
363 * @param size the length of the data to GNUNET_CRYPTO_hash
364 * @param ret pointer to where to write the hashcode
365 */
366void
367GNUNET_CRYPTO_hash (const void *block, unsigned int size,
368 GNUNET_HashCode * ret)
369{
370 struct sha512_ctx ctx;
371
372 sha512_init (&ctx);
373 sha512_update (&ctx, block, size);
374 sha512_final (&ctx, (unsigned char *) ret);
375}
376
377
378/**
379 * Context used when hashing a file.
380 */
381struct FileHashContext
382{
383
384 /**
385 * Function to call upon completion.
386 */
387 GNUNET_CRYPTO_HashCompletedCallback callback;
388
389 /**
390 * Closure for callback.
391 */
392 void *callback_cls;
393
394 /**
395 * IO buffer.
396 */
397 unsigned char *buffer;
398
399 /**
400 * Name of the file we are hashing.
401 */
402 char *filename;
403
404 /**
405 * Cummulated hash.
406 */
407 struct sha512_ctx hctx;
408
409 /**
410 * Blocksize.
411 */
412 size_t bsize;
413
414 /**
415 * Size of the file.
416 */
417 unsigned long long fsize;
418
419 /**
420 * Current offset.
421 */
422 unsigned long long offset;
423
424 /**
425 * Run on shutdown?
426 */
427 int run_on_shutdown;
428
429 /**
430 * File descriptor.
431 */
432 int fd;
433
434};
435
436
437/**
438 * Report result of hash computation to callback
439 * and free associated resources.
440 */
441static void
442file_hash_finish (struct FileHashContext *fhc, const GNUNET_HashCode * res)
443{
444 fhc->callback (fhc->callback_cls, res);
445 GNUNET_free (fhc->filename);
446 if (fhc->fd != -1)
447 GNUNET_break (0 == CLOSE (fhc->fd));
448 GNUNET_free (fhc); /* also frees fhc->buffer */
449}
450
451
452/**
453 * File hashing task.
454 *
455 * @param cls closure
456 * @param tc context
457 */
458static void
459file_hash_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
460{
461 struct FileHashContext *fhc = cls;
462 GNUNET_HashCode res;
463 size_t delta;
464
465 GNUNET_assert (fhc->offset < fhc->fsize);
466 delta = fhc->bsize;
467 if (fhc->fsize - fhc->offset < delta)
468 delta = fhc->fsize - fhc->offset;
469 if (delta != READ (fhc->fd, fhc->buffer, delta))
470 {
471 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
472 "read", fhc->filename);
473 file_hash_finish (fhc, NULL);
474 return;
475 }
476 sha512_update (&fhc->hctx, fhc->buffer, delta);
477 fhc->offset += delta;
478 if (fhc->offset == fhc->fsize)
479 {
480 sha512_final (&fhc->hctx, (unsigned char *) &res);
481 file_hash_finish (fhc, &res);
482 return;
483 }
484 GNUNET_SCHEDULER_add_after (tc->sched,
485 fhc->run_on_shutdown,
486 GNUNET_SCHEDULER_PRIORITY_KEEP,
487 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
488 &file_hash_task, fhc);
489}
490
491
492/**
493 * Compute the hash of an entire file.
494 *
495 * @param sched scheduler to use
496 * @param priority scheduling priority to use
497 * @param run_on_shutdown should we complete even on shutdown?
498 * @param filename name of file to hash
499 * @param blocksize number of bytes to process in one task
500 * @param callback function to call upon completion
501 * @param callback_cls closure for callback
502 */
503void
504GNUNET_CRYPTO_hash_file (struct GNUNET_SCHEDULER_Handle *sched,
505 enum GNUNET_SCHEDULER_Priority priority,
506 int run_on_shutdown,
507 const char *filename,
508 size_t blocksize,
509 GNUNET_CRYPTO_HashCompletedCallback callback,
510 void *callback_cls)
511{
512 struct FileHashContext *fhc;
513
514 GNUNET_assert (blocksize > 0);
515 fhc = GNUNET_malloc (sizeof (struct FileHashContext) + blocksize);
516 fhc->callback = callback;
517 fhc->callback_cls = callback_cls;
518 fhc->buffer = (unsigned char *) &fhc[1];
519 fhc->filename = GNUNET_strdup (filename);
520 fhc->fd = -1;
521 sha512_init (&fhc->hctx);
522 fhc->bsize = blocksize;
523 if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fhc->fsize, GNUNET_NO))
524 {
525 file_hash_finish (fhc, NULL);
526 return;
527 }
528 fhc->run_on_shutdown = run_on_shutdown;
529 fhc->fd = GNUNET_DISK_file_open (filename, O_RDONLY | O_LARGEFILE);
530 if (fhc->fd == -1)
531 {
532 file_hash_finish (fhc, NULL);
533 return;
534 }
535 GNUNET_SCHEDULER_add_after (sched,
536 run_on_shutdown,
537 priority,
538 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
539 &file_hash_task, fhc);
540}
541
542
543/* ***************** binary-ASCII encoding *************** */
544
545/**
546 * 32 characters for encoding (GNUNET_CRYPTO_hash => 32 characters)
547 */
548static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
549
550static unsigned int
551getValue__ (unsigned char a)
552{
553 if ((a >= '0') && (a <= '9'))
554 return a - '0';
555 if ((a >= 'A') && (a <= 'V'))
556 return (a - 'A' + 10);
557 return -1;
558}
559
560/**
561 * Convert GNUNET_CRYPTO_hash to ASCII encoding. The ASCII encoding is rather
562 * GNUnet specific. It was chosen such that it only uses characters
563 * in [0-9A-V], can be produced without complex arithmetics and uses a
564 * small number of characters. The GNUnet encoding uses 102
565 * characters plus a null terminator.
566 *
567 * @param block the GNUNET_CRYPTO_hash code
568 * @param result where to store the encoding (struct GNUNET_CRYPTO_HashAsciiEncoded can be
569 * safely cast to char*, a '\0' termination is set).
570 */
571void
572GNUNET_CRYPTO_hash_to_enc (const GNUNET_HashCode * block,
573 struct GNUNET_CRYPTO_HashAsciiEncoded *result)
574{
575 unsigned int wpos;
576 unsigned int rpos;
577 unsigned int bits;
578 unsigned int vbit;
579
580 GNUNET_assert (block != NULL);
581 GNUNET_assert (result != NULL);
582 vbit = 0;
583 wpos = 0;
584 rpos = 0;
585 bits = 0;
586 while ((rpos < sizeof (GNUNET_HashCode)) || (vbit > 0))
587 {
588 if ((rpos < sizeof (GNUNET_HashCode)) && (vbit < 5))
589 {
590 bits = (bits << 8) | ((unsigned char *) block)[rpos++]; /* eat 8 more bits */
591 vbit += 8;
592 }
593 if (vbit < 5)
594 {
595 bits <<= (5 - vbit); /* zero-padding */
596 GNUNET_assert (vbit == 2); /* padding by 3: 512+3 mod 5 == 0 */
597 vbit = 5;
598 }
599 GNUNET_assert (wpos <
600 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1);
601 result->encoding[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
602 vbit -= 5;
603 }
604 GNUNET_assert (wpos == sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1);
605 GNUNET_assert (vbit == 0);
606 result->encoding[wpos] = '\0';
607}
608
609/**
610 * Convert ASCII encoding back to GNUNET_CRYPTO_hash
611 *
612 * @param enc the encoding
613 * @param result where to store the GNUNET_CRYPTO_hash code
614 * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding
615 */
616int
617GNUNET_CRYPTO_hash_from_string (const char *enc, GNUNET_HashCode * result)
618{
619 unsigned int rpos;
620 unsigned int wpos;
621 unsigned int bits;
622 unsigned int vbit;
623
624 if (strlen (enc) != sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1)
625 return GNUNET_SYSERR;
626
627 vbit = 2; /* padding! */
628 wpos = sizeof (GNUNET_HashCode);
629 rpos = sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1;
630 bits = getValue__ (enc[--rpos]) >> 3;
631 while (wpos > 0)
632 {
633 GNUNET_assert (rpos > 0);
634 bits = (getValue__ (enc[--rpos]) << vbit) | bits;
635 vbit += 5;
636 if (vbit >= 8)
637 {
638 ((unsigned char *) result)[--wpos] = (unsigned char) bits;
639 bits >>= 8;
640 vbit -= 8;
641 }
642 }
643 GNUNET_assert (rpos == 0);
644 GNUNET_assert (vbit == 0);
645 return GNUNET_OK;
646}
647
648/**
649 * Compute the distance between 2 hashcodes. The computation must be
650 * fast, not involve bits[0] or bits[4] (they're used elsewhere), and be
651 * somewhat consistent. And of course, the result should be a positive
652 * number.
653 *
654 * @returns a positive number which is a measure for
655 * hashcode proximity.
656 */
657unsigned int
658GNUNET_CRYPTO_hash_distance_u32 (const GNUNET_HashCode * a,
659 const GNUNET_HashCode * b)
660{
661 unsigned int x1 = (a->bits[1] - b->bits[1]) >> 16;
662 unsigned int x2 = (b->bits[1] - a->bits[1]) >> 16;
663 return (x1 * x2);
664}
665
666void
667GNUNET_CRYPTO_hash_create_random (GNUNET_HashCode * result)
668{
669 int i;
670 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
671 i--)
672 result->bits[i] = rand ();
673}
674
675void
676GNUNET_CRYPTO_hash_difference (const GNUNET_HashCode * a,
677 const GNUNET_HashCode * b,
678 GNUNET_HashCode * result)
679{
680 int i;
681 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
682 i--)
683 result->bits[i] = b->bits[i] - a->bits[i];
684}
685
686void
687GNUNET_CRYPTO_hash_sum (const GNUNET_HashCode * a,
688 const GNUNET_HashCode * delta,
689 GNUNET_HashCode * result)
690{
691 int i;
692 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
693 i--)
694 result->bits[i] = delta->bits[i] + a->bits[i];
695}
696
697void
698GNUNET_CRYPTO_hash_xor (const GNUNET_HashCode * a,
699 const GNUNET_HashCode * b, GNUNET_HashCode * result)
700{
701 int i;
702 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
703 i--)
704 result->bits[i] = a->bits[i] ^ b->bits[i];
705}
706
707/**
708 * Convert a hashcode into a key.
709 */
710void
711GNUNET_CRYPTO_hash_to_AES_key (const GNUNET_HashCode * hc,
712 struct GNUNET_CRYPTO_AesSessionKey *skey,
713 struct GNUNET_CRYPTO_AesInitializationVector
714 *iv)
715{
716 GNUNET_assert (sizeof (GNUNET_HashCode) >=
717 GNUNET_CRYPTO_AES_KEY_LENGTH +
718 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
719 memcpy (skey, hc, GNUNET_CRYPTO_AES_KEY_LENGTH);
720 skey->crc32 =
721 htonl (GNUNET_CRYPTO_crc32_n (skey, GNUNET_CRYPTO_AES_KEY_LENGTH));
722 memcpy (iv, &((char *) hc)[GNUNET_CRYPTO_AES_KEY_LENGTH],
723 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
724}
725
726/**
727 * Obtain a bit from a hashcode.
728 * @param code the GNUNET_CRYPTO_hash to index bit-wise
729 * @param bit index into the hashcode, [0...511]
730 * @return Bit \a bit from hashcode \a code, -1 for invalid index
731 */
732int
733GNUNET_CRYPTO_hash_get_bit (const GNUNET_HashCode * code, unsigned int bit)
734{
735 GNUNET_assert (bit < 8 * sizeof (GNUNET_HashCode));
736 return (((unsigned char *) code)[bit >> 3] & (1 << (bit & 7))) > 0;
737}
738
739/**
740 * Compare function for HashCodes, producing a total ordering
741 * of all hashcodes.
742 * @return 1 if h1 > h2, -1 if h1 < h2 and 0 if h1 == h2.
743 */
744int
745GNUNET_CRYPTO_hash_cmp (const GNUNET_HashCode * h1,
746 const GNUNET_HashCode * h2)
747{
748 unsigned int *i1;
749 unsigned int *i2;
750 int i;
751
752 i1 = (unsigned int *) h1;
753 i2 = (unsigned int *) h2;
754 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
755 i--)
756 {
757 if (i1[i] > i2[i])
758 return 1;
759 if (i1[i] < i2[i])
760 return -1;
761 }
762 return 0;
763}
764
765/**
766 * Find out which of the two GNUNET_CRYPTO_hash codes is closer to target
767 * in the XOR metric (Kademlia).
768 * @return -1 if h1 is closer, 1 if h2 is closer and 0 if h1==h2.
769 */
770int
771GNUNET_CRYPTO_hash_xorcmp (const GNUNET_HashCode * h1,
772 const GNUNET_HashCode * h2,
773 const GNUNET_HashCode * target)
774{
775 int i;
776 unsigned int d1;
777 unsigned int d2;
778
779 for (i = sizeof (GNUNET_HashCode) / sizeof (unsigned int) - 1; i >= 0; i--)
780 {
781 d1 = ((unsigned int *) h1)[i] ^ ((unsigned int *) target)[i];
782 d2 = ((unsigned int *) h2)[i] ^ ((unsigned int *) target)[i];
783 if (d1 > d2)
784 return 1;
785 else if (d1 < d2)
786 return -1;
787 }
788 return 0;
789}
790
791/* end of hashing.c */
diff --git a/src/util/crypto_ksk.c b/src/util/crypto_ksk.c
new file mode 100644
index 000000000..c3461ae61
--- /dev/null
+++ b/src/util/crypto_ksk.c
@@ -0,0 +1,791 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 1994, 1996, 1998, 2001, 2002, 2003 Free Software Foundation, Inc.
4 Copyright (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
5
6 GNUnet is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2, or (at your
9 option) any later version.
10
11 GNUnet is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNUnet; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20
21 Note: This code is based on code from libgcrypt
22 The code was adapted for GNUnet to support RSA-key generation
23 based on weak, pseudo-random keys. Do NOT use to generate
24 ordinary RSA keys!
25*/
26
27
28/**
29 * @file util/crypto_ksk.c
30 * @brief implementation of RSA-Key generation for KBlocks
31 * (do NOT use for pseudonyms or hostkeys!)
32 * @author Christian Grothoff
33 */
34
35#include "platform.h"
36#include "gnunet_common.h"
37#include "gnunet_crypto_lib.h"
38#include <gmp.h>
39#include <gcrypt.h>
40
41/**
42 * Log an error message at log-level 'level' that indicates
43 * a failure of the command 'cmd' with the message given
44 * by gcry_strerror(rc).
45 */
46#define LOG_GCRY(level, cmd, rc) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0);
47
48
49typedef struct
50{
51 mpz_t n; /* public modulus */
52 mpz_t e; /* public exponent */
53 mpz_t d; /* exponent */
54 mpz_t p; /* prime p. */
55 mpz_t q; /* prime q. */
56 mpz_t u; /* inverse of p mod q. */
57} KBlock_secret_key;
58
59/**
60 * The private information of an RSA key pair.
61 * NOTE: this must match the definition in crypto_rsa.c
62 */
63struct GNUNET_CRYPTO_RsaPrivateKey
64{
65 gcry_sexp_t sexp;
66};
67
68
69/* Note: 2 is not included because it can be tested more easily by
70 looking at bit 0. The last entry in this list is marked by a zero */
71static uint16_t small_prime_numbers[] = {
72 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
73 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
74 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
75 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
76 211, 223, 227, 229, 233, 239, 241, 251, 257, 263,
77 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
78 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
79 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
80 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
81 509, 521, 523, 541, 547, 557, 563, 569, 571, 577,
82 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
83 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
84 709, 719, 727, 733, 739, 743, 751, 757, 761, 769,
85 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
86 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
87 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
88 991, 997, 1009, 1013, 1019, 1021, 1031, 1033,
89 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091,
90 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
91 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
92 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277,
93 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307,
94 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399,
95 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
96 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493,
97 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559,
98 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609,
99 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
100 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
101 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789,
102 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871,
103 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931,
104 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997,
105 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
106 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111,
107 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161,
108 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243,
109 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
110 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
111 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411,
112 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473,
113 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551,
114 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633,
115 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
116 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729,
117 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791,
118 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851,
119 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917,
120 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
121 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061,
122 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137,
123 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209,
124 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271,
125 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
126 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391,
127 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467,
128 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533,
129 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583,
130 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
131 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709,
132 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779,
133 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851,
134 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917,
135 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
136 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049,
137 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111,
138 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177,
139 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243,
140 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
141 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391,
142 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457,
143 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519,
144 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597,
145 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
146 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729,
147 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799,
148 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889,
149 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951,
150 4957, 4967, 4969, 4973, 4987, 4993, 4999,
151 0
152};
153
154#define DIM(v) (sizeof(v)/sizeof((v)[0]))
155static int no_of_small_prime_numbers = DIM (small_prime_numbers) - 1;
156
157
158 static unsigned int
159 get_nbits (mpz_t a)
160{
161 return mpz_sizeinbase (a, 2);
162}
163
164/**
165 * Count the number of zerobits at the low end of A
166 */
167static unsigned int
168get_trailing_zeros (mpz_t a)
169{
170 unsigned int count = 0;
171 unsigned int nbits = get_nbits (a);
172
173 while ((mpz_tstbit (a, count)) && (count < nbits))
174 count++;
175 return count;
176}
177
178/**
179 * Set bit N of A. and clear all bits above
180 */
181static void
182set_highbit (mpz_t a, unsigned int n)
183{
184 unsigned int nbits;
185
186 nbits = get_nbits (a);
187 while (nbits > n)
188 mpz_clrbit (a, nbits--);
189 mpz_setbit (a, n);
190}
191
192static void
193mpz_randomize (mpz_t n, unsigned int nbits, GNUNET_HashCode * rnd)
194{
195 GNUNET_HashCode *tmp;
196 int cnt;
197 int i;
198
199 cnt = (nbits / sizeof (GNUNET_HashCode) / 8) + 1;
200 tmp = GNUNET_malloc (sizeof (GNUNET_HashCode) * cnt);
201
202 tmp[0] = *rnd;
203 for (i = 0; i < cnt - 1; i++)
204 {
205 GNUNET_CRYPTO_hash (&tmp[i], sizeof (GNUNET_HashCode), &tmp[i + 1]);
206 }
207 *rnd = tmp[cnt - 1];
208 mpz_import (n, cnt * sizeof (GNUNET_HashCode) / sizeof (unsigned int),
209 1, sizeof (unsigned int), 1, 0, tmp);
210 GNUNET_free (tmp);
211 i = get_nbits (n);
212 while (i > nbits)
213 mpz_clrbit (n, i--);
214}
215
216/**
217 * Return true if n is probably a prime
218 */
219static int
220is_prime (mpz_t n, int steps, GNUNET_HashCode * hc)
221{
222 mpz_t x;
223 mpz_t y;
224 mpz_t z;
225 mpz_t nminus1;
226 mpz_t a2;
227 mpz_t q;
228 unsigned int i, j, k;
229 int rc = 0;
230 unsigned int nbits;
231
232 mpz_init (x);
233 mpz_init (y);
234 mpz_init (z);
235 mpz_init (nminus1);
236 mpz_init_set_ui (a2, 2);
237 nbits = get_nbits (n);
238 mpz_sub_ui (nminus1, n, 1);
239
240 /* Find q and k, so that n = 1 + 2^k * q . */
241 mpz_init_set (q, nminus1);
242 k = get_trailing_zeros (q);
243 mpz_tdiv_q_2exp (q, q, k);
244
245 for (i = 0; i < steps; i++)
246 {
247 if (!i)
248 {
249 mpz_set_ui (x, 2);
250 }
251 else
252 {
253 mpz_randomize (x, nbits, hc);
254
255 /* Make sure that the number is smaller than the prime and
256 keep the randomness of the high bit. */
257 if (mpz_tstbit (x, nbits - 2))
258 {
259 set_highbit (x, nbits - 2); /* Clear all higher bits. */
260 }
261 else
262 {
263 set_highbit (x, nbits - 2);
264 mpz_clrbit (x, nbits - 2);
265 }
266 GNUNET_assert (mpz_cmp (x, nminus1) < 0 && mpz_cmp_ui (x, 1) > 0);
267 }
268 mpz_powm (y, x, q, n);
269 if (mpz_cmp_ui (y, 1) && mpz_cmp (y, nminus1))
270 {
271 for (j = 1; j < k && mpz_cmp (y, nminus1); j++)
272 {
273 mpz_powm (y, y, a2, n);
274 if (!mpz_cmp_ui (y, 1))
275 goto leave; /* Not a prime. */
276 }
277 if (mpz_cmp (y, nminus1))
278 goto leave; /* Not a prime. */
279 }
280 }
281 rc = 1; /* May be a prime. */
282
283leave:
284 mpz_clear (x);
285 mpz_clear (y);
286 mpz_clear (z);
287 mpz_clear (nminus1);
288 mpz_clear (q);
289 mpz_clear (a2);
290
291 return rc;
292}
293
294static void
295gen_prime (mpz_t ptest, unsigned int nbits, GNUNET_HashCode * hc)
296{
297 mpz_t prime, pminus1, val_2, val_3, result;
298 int i;
299 unsigned x, step;
300 int *mods;
301 mpz_t tmp;
302
303 GNUNET_assert (nbits >= 16);
304
305 mods = GNUNET_malloc (no_of_small_prime_numbers * sizeof (*mods));
306 /* Make nbits fit into mpz_t implementation. */
307 mpz_init_set_ui (val_2, 2);
308 mpz_init_set_ui (val_3, 3);
309 mpz_init (prime);
310 mpz_init (result);
311 mpz_init (pminus1);
312 mpz_init (ptest);
313 while (1)
314 {
315 /* generate a random number */
316 mpz_randomize (prime, nbits, hc);
317 /* Set high order bit to 1, set low order bit to 1. If we are
318 generating a secret prime we are most probably doing that
319 for RSA, to make sure that the modulus does have the
320 requested key size we set the 2 high order bits. */
321 set_highbit (prime, nbits - 1);
322 mpz_setbit (prime, nbits - 2);
323 mpz_setbit (prime, 0);
324
325 /* Calculate all remainders. */
326 mpz_init (tmp);
327 for (i = 0; (x = small_prime_numbers[i]); i++)
328 mods[i] = mpz_fdiv_r_ui (tmp, prime, x);
329 mpz_clear (tmp);
330 /* Now try some primes starting with prime. */
331 for (step = 0; step < 20000; step += 2)
332 {
333 /* Check against all the small primes we have in mods. */
334 for (i = 0; (x = small_prime_numbers[i]); i++)
335 {
336 while (mods[i] + step >= x)
337 mods[i] -= x;
338 if (!(mods[i] + step))
339 break;
340 }
341 if (x)
342 continue; /* Found a multiple of an already known prime. */
343
344 mpz_add_ui (ptest, prime, step);
345 if (!mpz_tstbit (ptest, nbits - 2))
346 break;
347
348 /* Do a fast Fermat test now. */
349 mpz_sub_ui (pminus1, ptest, 1);
350 mpz_powm (result, val_2, pminus1, ptest);
351 if ((!mpz_cmp_ui (result, 1)) && (is_prime (ptest, 5, hc)))
352 {
353 /* Got it. */
354 mpz_clear (val_2);
355 mpz_clear (val_3);
356 mpz_clear (result);
357 mpz_clear (pminus1);
358 mpz_clear (prime);
359 GNUNET_free (mods);
360 return;
361 }
362 }
363 }
364}
365
366/**
367 * Find the greatest common divisor G of A and B.
368 * Return: 1 if this 1, 0 in all other cases
369 */
370static int
371test_gcd (mpz_t g, mpz_t xa, mpz_t xb)
372{
373 mpz_t a, b;
374
375 mpz_init_set (a, xa);
376 mpz_init_set (b, xb);
377
378 /* TAOCP Vol II, 4.5.2, Algorithm A */
379 while (mpz_cmp_ui (b, 0))
380 {
381 mpz_fdiv_r (g, a, b); /* g used as temorary variable */
382 mpz_set (a, b);
383 mpz_set (b, g);
384 }
385 mpz_set (g, a);
386
387 mpz_clear (a);
388 mpz_clear (b);
389 return (0 == mpz_cmp_ui (g, 1));
390}
391
392/**
393 * Generate a key pair with a key of size NBITS.
394 * @param sk where to store the key
395 * @param nbits the number of bits to use
396 * @param hc the HC to use for PRNG (modified!)
397 */
398static void
399generate_kblock_key (KBlock_secret_key * sk,
400 unsigned int nbits, GNUNET_HashCode * hc)
401{
402 mpz_t t1, t2;
403 mpz_t phi; /* helper: (p-1)(q-1) */
404 mpz_t g;
405 mpz_t f;
406
407 /* make sure that nbits is even so that we generate p, q of equal size */
408 if ((nbits & 1))
409 nbits++;
410
411 mpz_init_set_ui (sk->e, 257);
412 mpz_init (sk->n);
413 mpz_init (sk->p);
414 mpz_init (sk->q);
415 mpz_init (sk->d);
416 mpz_init (sk->u);
417
418 mpz_init (t1);
419 mpz_init (t2);
420 mpz_init (phi);
421 mpz_init (g);
422 mpz_init (f);
423
424 do
425 {
426 do
427 {
428 mpz_clear (sk->p);
429 mpz_clear (sk->q);
430 gen_prime (sk->p, nbits / 2, hc);
431 gen_prime (sk->q, nbits / 2, hc);
432
433 if (mpz_cmp (sk->p, sk->q) > 0) /* p shall be smaller than q (for calc of u) */
434 mpz_swap (sk->p, sk->q);
435 /* calculate the modulus */
436 mpz_mul (sk->n, sk->p, sk->q);
437 }
438 while (get_nbits (sk->n) != nbits);
439
440 /* calculate Euler totient: phi = (p-1)(q-1) */
441 mpz_sub_ui (t1, sk->p, 1);
442 mpz_sub_ui (t2, sk->q, 1);
443 mpz_mul (phi, t1, t2);
444 mpz_gcd (g, t1, t2);
445 mpz_fdiv_q (f, phi, g);
446
447 while (0 == test_gcd (t1, sk->e, phi))
448 { /* (while gcd is not 1) */
449 mpz_add_ui (sk->e, sk->e, 2);
450 }
451
452 /* calculate the secret key d = e^1 mod phi */
453 }
454 while ((0 == mpz_invert (sk->d, sk->e, f)) ||
455 (0 == mpz_invert (sk->u, sk->p, sk->q)));
456
457 mpz_clear (t1);
458 mpz_clear (t2);
459 mpz_clear (phi);
460 mpz_clear (f);
461 mpz_clear (g);
462}
463
464
465/**
466 * Internal representation of the private key.
467 */
468struct KskRsaPrivateKeyBinaryEncoded
469{
470 /**
471 * Total size of the structure, in bytes, in big-endian!
472 */
473 uint16_t len GNUNET_PACKED;
474 uint16_t sizen GNUNET_PACKED; /* in big-endian! */
475 uint16_t sizee GNUNET_PACKED; /* in big-endian! */
476 uint16_t sized GNUNET_PACKED; /* in big-endian! */
477 uint16_t sizep GNUNET_PACKED; /* in big-endian! */
478 uint16_t sizeq GNUNET_PACKED; /* in big-endian! */
479 uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */
480 uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */
481 /* followed by the actual values */
482};
483
484
485/**
486 * Deterministically (!) create a hostkey using only the
487 * given HashCode as input to the PRNG.
488 */
489static struct KskRsaPrivateKeyBinaryEncoded *
490makeKblockKeyInternal (const GNUNET_HashCode * hc)
491{
492 KBlock_secret_key sk;
493 GNUNET_HashCode hx;
494 void *pbu[6];
495 mpz_t *pkv[6];
496 size_t sizes[6];
497 struct KskRsaPrivateKeyBinaryEncoded *retval;
498 int i;
499 size_t size;
500
501 hx = *hc;
502 generate_kblock_key (&sk, 1024, /* at least 10x as fast than 2048 bits
503 -- we simply cannot afford 2048 bits
504 even on modern hardware, and especially
505 not since clearly a dictionary attack
506 will still be much cheaper
507 than breaking a 1024 bit RSA key.
508 If an adversary can spend the time to
509 break a 1024 bit RSA key just to forge
510 a signature -- SO BE IT. [ CG, 6/2005 ] */
511 &hx);
512 pkv[0] = &sk.n;
513 pkv[1] = &sk.e;
514 pkv[2] = &sk.d;
515 pkv[3] = &sk.p;
516 pkv[4] = &sk.q;
517 pkv[5] = &sk.u;
518 size = sizeof (struct KskRsaPrivateKeyBinaryEncoded);
519 for (i = 0; i < 6; i++)
520 {
521 pbu[i] = mpz_export (NULL, &sizes[i], 1, /* most significant word first */
522 1, /* unit is bytes */
523 1, /* big endian */
524 0, /* nails */
525 *pkv[i]);
526 size += sizes[i];
527 }
528 GNUNET_assert (size < 65536);
529 retval = GNUNET_malloc (size);
530 retval->len = htons (size);
531 i = 0;
532 retval->sizen = htons (sizes[0]);
533 memcpy (&((char *) &retval[1])[i], pbu[0], sizes[0]);
534 i += sizes[0];
535 retval->sizee = htons (sizes[1]);
536 memcpy (&((char *) &retval[1])[i], pbu[1], sizes[1]);
537 i += sizes[1];
538 retval->sized = htons (sizes[2]);
539 memcpy (&((char *) &retval[1])[i], pbu[2], sizes[2]);
540 i += sizes[2];
541 /* swap p and q! */
542 retval->sizep = htons (sizes[4]);
543 memcpy (&((char *) &retval[1])[i], pbu[4], sizes[4]);
544 i += sizes[4];
545 retval->sizeq = htons (sizes[3]);
546 memcpy (&((char *) &retval[1])[i], pbu[3], sizes[3]);
547 i += sizes[3];
548 retval->sizedmp1 = htons (0);
549 retval->sizedmq1 = htons (0);
550 memcpy (&((char *) &retval[1])[i], pbu[5], sizes[5]);
551 for (i = 0; i < 6; i++)
552 {
553 mpz_clear (*pkv[i]);
554 free (pbu[i]);
555 }
556 return retval;
557}
558
559
560/**
561 * Decode the internal format into the format used
562 * by libgcrypt.
563 */
564static struct GNUNET_CRYPTO_RsaPrivateKey *
565ksk_decode_key (const struct KskRsaPrivateKeyBinaryEncoded *encoding)
566{
567 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
568 gcry_sexp_t res;
569 gcry_mpi_t n, e, d, p, q, u;
570 int rc;
571 size_t size;
572 int pos;
573
574 pos = 0;
575 size = ntohs (encoding->sizen);
576 rc = gcry_mpi_scan (&n,
577 GCRYMPI_FMT_USG,
578 &((const unsigned char *) (&encoding[1]))[pos],
579 size, &size);
580 pos += ntohs (encoding->sizen);
581 if (rc)
582 {
583 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
584 return NULL;
585 }
586 size = ntohs (encoding->sizee);
587 rc = gcry_mpi_scan (&e,
588 GCRYMPI_FMT_USG,
589 &((const unsigned char *) (&encoding[1]))[pos],
590 size, &size);
591 pos += ntohs (encoding->sizee);
592 if (rc)
593 {
594 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
595 gcry_mpi_release (n);
596 return NULL;
597 }
598 size = ntohs (encoding->sized);
599 rc = gcry_mpi_scan (&d,
600 GCRYMPI_FMT_USG,
601 &((const unsigned char *) (&encoding[1]))[pos],
602 size, &size);
603 pos += ntohs (encoding->sized);
604 if (rc)
605 {
606 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
607 gcry_mpi_release (n);
608 gcry_mpi_release (e);
609 return NULL;
610 }
611 /* swap p and q! */
612 size = ntohs (encoding->sizep);
613 if (size > 0)
614 {
615 rc = gcry_mpi_scan (&q,
616 GCRYMPI_FMT_USG,
617 &((const unsigned char *) (&encoding[1]))[pos],
618 size, &size);
619 pos += ntohs (encoding->sizep);
620 if (rc)
621 {
622 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
623 gcry_mpi_release (n);
624 gcry_mpi_release (e);
625 gcry_mpi_release (d);
626 return NULL;
627 }
628 }
629 else
630 q = NULL;
631 size = ntohs (encoding->sizeq);
632 if (size > 0)
633 {
634 rc = gcry_mpi_scan (&p,
635 GCRYMPI_FMT_USG,
636 &((const unsigned char *) (&encoding[1]))[pos],
637 size, &size);
638 pos += ntohs (encoding->sizeq);
639 if (rc)
640 {
641 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
642 gcry_mpi_release (n);
643 gcry_mpi_release (e);
644 gcry_mpi_release (d);
645 if (q != NULL)
646 gcry_mpi_release (q);
647 return NULL;
648 }
649 }
650 else
651 p = NULL;
652 pos += ntohs (encoding->sizedmp1);
653 pos += ntohs (encoding->sizedmq1);
654 size =
655 ntohs (encoding->len) - sizeof (struct KskRsaPrivateKeyBinaryEncoded) -
656 pos;
657 if (size > 0)
658 {
659 rc = gcry_mpi_scan (&u,
660 GCRYMPI_FMT_USG,
661 &((const unsigned char *) (&encoding[1]))[pos],
662 size, &size);
663 if (rc)
664 {
665 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
666 gcry_mpi_release (n);
667 gcry_mpi_release (e);
668 gcry_mpi_release (d);
669 if (p != NULL)
670 gcry_mpi_release (p);
671 if (q != NULL)
672 gcry_mpi_release (q);
673 return NULL;
674 }
675 }
676 else
677 u = NULL;
678
679 if ((p != NULL) && (q != NULL) && (u != NULL))
680 {
681 rc = gcry_sexp_build (&res, &size, /* erroff */
682 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))",
683 n, e, d, p, q, u);
684 }
685 else
686 {
687 if ((p != NULL) && (q != NULL))
688 {
689 rc = gcry_sexp_build (&res, &size, /* erroff */
690 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))",
691 n, e, d, p, q);
692 }
693 else
694 {
695 rc = gcry_sexp_build (&res, &size, /* erroff */
696 "(private-key(rsa(n %m)(e %m)(d %m)))",
697 n, e, d);
698 }
699 }
700 gcry_mpi_release (n);
701 gcry_mpi_release (e);
702 gcry_mpi_release (d);
703 if (p != NULL)
704 gcry_mpi_release (p);
705 if (q != NULL)
706 gcry_mpi_release (q);
707 if (u != NULL)
708 gcry_mpi_release (u);
709
710 if (rc)
711 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
712#if EXTRA_CHECKS
713 if (gcry_pk_testkey (res))
714 {
715 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
716 return NULL;
717 }
718#endif
719 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
720 ret->sexp = res;
721 return ret;
722}
723
724
725
726
727typedef struct
728{
729 GNUNET_HashCode hc;
730 struct KskRsaPrivateKeyBinaryEncoded *pke;
731} KBlockKeyCacheLine;
732
733static KBlockKeyCacheLine **cache;
734static unsigned int cacheSize;
735
736/**
737 * Deterministically (!) create a hostkey using only the
738 * given HashCode as input to the PRNG.
739 */
740struct GNUNET_CRYPTO_RsaPrivateKey *
741GNUNET_CRYPTO_rsa_key_create_from_hash (const GNUNET_HashCode * hc)
742{
743 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
744 KBlockKeyCacheLine *line;
745 int i;
746
747 for (i = 0; i < cacheSize; i++)
748 {
749 if (0 == memcmp (hc, &cache[i]->hc, sizeof (GNUNET_HashCode)))
750 {
751 ret = ksk_decode_key (cache[i]->pke);
752 return ret;
753 }
754 }
755
756 line = GNUNET_malloc (sizeof (KBlockKeyCacheLine));
757 line->hc = *hc;
758 line->pke = makeKblockKeyInternal (hc);
759 GNUNET_array_grow (cache, cacheSize, cacheSize + 1);
760 cache[cacheSize - 1] = line;
761 return ksk_decode_key (line->pke);
762}
763
764void __attribute__ ((constructor)) GNUNET_CRYPTO_ksk_init ()
765{
766 gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
767 if (!gcry_check_version (GCRYPT_VERSION))
768 {
769 fprintf (stderr,
770 _
771 ("libgcrypt has not the expected version (version %s is required).\n"),
772 GCRYPT_VERSION);
773 abort ();
774 }
775#ifdef gcry_fast_random_poll
776 gcry_fast_random_poll ();
777#endif
778}
779
780void __attribute__ ((destructor)) GNUNET_CRYPTO_ksk_fini ()
781{
782 int i;
783 for (i = 0; i < cacheSize; i++)
784 {
785 GNUNET_free (cache[i]->pke);
786 GNUNET_free (cache[i]);
787 }
788 GNUNET_array_grow (cache, cacheSize, 0);
789}
790
791/* end of kblockkey.c */
diff --git a/src/util/crypto_random.c b/src/util/crypto_random.c
new file mode 100644
index 000000000..3f7ac4dd3
--- /dev/null
+++ b/src/util/crypto_random.c
@@ -0,0 +1,136 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @file util/crypto_random.c
24 * @brief functions to gather random numbers
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_crypto_lib.h"
30#include <gcrypt.h>
31
32/**
33 * @return a random value in the interval [0,i[.
34 */
35unsigned int
36GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality mode, unsigned int i)
37{
38#ifdef gcry_fast_random_poll
39 static unsigned int invokeCount;
40#endif
41 unsigned int ret;
42
43 GNUNET_assert (i > 0);
44
45 if (mode == GNUNET_CRYPTO_QUALITY_STRONG)
46 {
47 /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
48#ifdef gcry_fast_random_poll
49 if ((invokeCount++ % 256) == 0)
50 gcry_fast_random_poll ();
51#endif
52 ret = rand (); /* in case gcry_randomize fails,
53 we at least get a pseudo-
54 random number this way */
55 gcry_randomize ((unsigned char *) &ret,
56 sizeof (unsigned int), GCRY_STRONG_RANDOM);
57 return ret % i;
58 }
59 else
60 {
61 ret = i * ((double) RANDOM () / RAND_MAX);
62 if (ret >= i)
63 ret = i - 1;
64 return ret;
65 }
66}
67
68
69/**
70 * Get an array with a random permutation of the
71 * numbers 0...n-1.
72 * @param mode GNUNET_RANDOM_QUALITY_STRONG if the strong (but expensive)
73 * PRNG should be used, GNUNET_RANDOM_QUALITY_WEAK otherwise
74 * @param n the size of the array
75 * @return the permutation array (allocated from heap)
76 */
77unsigned int *
78GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode, unsigned int n)
79{
80 unsigned int *ret;
81 unsigned int i;
82 unsigned int tmp;
83 unsigned int x;
84
85 GNUNET_assert (n > 0);
86 ret = GNUNET_malloc (n * sizeof (int));
87 for (i = 0; i < n; i++)
88 ret[i] = i;
89 for (i = 0; i < n; i++)
90 {
91 x = GNUNET_CRYPTO_random_u32 (mode, n);
92 tmp = ret[x];
93 ret[x] = ret[i];
94 ret[i] = tmp;
95 }
96 return ret;
97}
98
99/**
100 * Random on unsigned 64-bit values.
101 */
102unsigned long long
103GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode,
104 unsigned long long u)
105{
106 unsigned long long ret;
107
108 GNUNET_assert (u > 0);
109 if (mode == GNUNET_CRYPTO_QUALITY_STRONG)
110 {
111 gcry_randomize ((unsigned char *) &ret,
112 sizeof (unsigned long long), GCRY_STRONG_RANDOM);
113 return ret % u;
114 }
115 else
116 {
117 ret = u * ((double) RANDOM () / RAND_MAX);
118 if (ret >= u)
119 ret = u - 1;
120 return ret;
121 }
122}
123
124/**
125 * This function should only be called in testcases
126 * where strong entropy gathering is not desired
127 * (for example, for hostkey generation).
128 */
129void
130GNUNET_CRYPTO_random_disable_entropy_gathering ()
131{
132 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
133}
134
135
136/* end of crypto_random.c */
diff --git a/src/util/crypto_rsa.c b/src/util/crypto_rsa.c
new file mode 100644
index 000000000..b61729e06
--- /dev/null
+++ b/src/util/crypto_rsa.c
@@ -0,0 +1,948 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/crypto_rsa.c
23 * @brief public key cryptography (RSA) with libgcrypt
24 * @author Christian Grothoff
25 *
26 * Note that the code locks often needlessly on the gcrypt-locking api.
27 * One would think that simple MPI operations should not require locking
28 * (since only global operations on the random pool must be locked,
29 * strictly speaking). But libgcrypt does sometimes require locking in
30 * unexpected places, so the safe solution is to always lock even if it
31 * is not required. The performance impact is minimal anyway.
32 */
33
34#include "platform.h"
35#include <gcrypt.h>
36#include "gnunet_common.h"
37#include "gnunet_crypto_lib.h"
38#include "gnunet_disk_lib.h"
39
40/**
41 * The private information of an RSA key pair.
42 * NOTE: this must match the definition in crypto_ksk.c
43 */
44struct GNUNET_CRYPTO_RsaPrivateKey
45{
46 gcry_sexp_t sexp;
47};
48
49
50/**
51 * GNUnet mandates a certain format for the encoding
52 * of private RSA key information that is provided
53 * by the RSA implementations. This format is used
54 * to serialize a private RSA key (typically when
55 * writing it to disk).
56 */
57struct RsaPrivateKeyBinaryEncoded
58{
59 /**
60 * Total size of the structure, in bytes, in big-endian!
61 */
62 uint16_t len GNUNET_PACKED;
63 uint16_t sizen GNUNET_PACKED; /* in big-endian! */
64 uint16_t sizee GNUNET_PACKED; /* in big-endian! */
65 uint16_t sized GNUNET_PACKED; /* in big-endian! */
66 uint16_t sizep GNUNET_PACKED; /* in big-endian! */
67 uint16_t sizeq GNUNET_PACKED; /* in big-endian! */
68 uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */
69 uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */
70 /* followed by the actual values */
71};
72
73
74#define HOSTKEY_LEN 2048
75
76#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS
77
78
79/**
80 * Log an error message at log-level 'level' that indicates
81 * a failure of the command 'cmd' with the message given
82 * by gcry_strerror(rc).
83 */
84#define LOG_GCRY(level, cmd, rc) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0);
85
86/**
87 * If target != size, move target bytes to the
88 * end of the size-sized buffer and zero out the
89 * first target-size bytes.
90 */
91static void
92adjust (unsigned char *buf, size_t size, size_t target)
93{
94 if (size < target)
95 {
96 memmove (&buf[target - size], buf, size);
97 memset (buf, 0, target - size);
98 }
99}
100
101/**
102 * This HostKey implementation uses RSA.
103 */
104struct GNUNET_CRYPTO_RsaPrivateKey *
105GNUNET_CRYPTO_rsa_key_create ()
106{
107 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
108 gcry_sexp_t s_key;
109 gcry_sexp_t s_keyparam;
110
111 GNUNET_assert (0 == gcry_sexp_build (&s_keyparam,
112 NULL,
113 "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))",
114 HOSTKEY_LEN));
115 GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam));
116 gcry_sexp_release (s_keyparam);
117#if EXTRA_CHECKS
118 GNUNET_assert (0 == gcry_pk_testkey (s_key));
119#endif
120 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
121 ret->sexp = s_key;
122 return ret;
123}
124
125/**
126 * Free memory occupied by hostkey
127 */
128void
129GNUNET_CRYPTO_rsa_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
130{
131 gcry_sexp_release (hostkey->sexp);
132 GNUNET_free (hostkey);
133}
134
135static int
136key_from_sexp (gcry_mpi_t * array,
137 gcry_sexp_t sexp, const char *topname, const char *elems)
138{
139 gcry_sexp_t list, l2;
140 const char *s;
141 int i, idx;
142
143 list = gcry_sexp_find_token (sexp, topname, 0);
144 if (!list)
145 {
146 return 1;
147 }
148 l2 = gcry_sexp_cadr (list);
149 gcry_sexp_release (list);
150 list = l2;
151 if (!list)
152 {
153 return 2;
154 }
155
156 idx = 0;
157 for (s = elems; *s; s++, idx++)
158 {
159 l2 = gcry_sexp_find_token (list, s, 1);
160 if (!l2)
161 {
162 for (i = 0; i < idx; i++)
163 {
164 gcry_free (array[i]);
165 array[i] = NULL;
166 }
167 gcry_sexp_release (list);
168 return 3; /* required parameter not found */
169 }
170 array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
171 gcry_sexp_release (l2);
172 if (!array[idx])
173 {
174 for (i = 0; i < idx; i++)
175 {
176 gcry_free (array[i]);
177 array[i] = NULL;
178 }
179 gcry_sexp_release (list);
180 return 4; /* required parameter is invalid */
181 }
182 }
183 gcry_sexp_release (list);
184 return 0;
185}
186
187/**
188 * Extract the public key of the host.
189 * @param hostkey the hostkey to extract into the result.
190 * @param result where to write the result.
191 */
192void
193GNUNET_CRYPTO_rsa_key_get_public (const struct GNUNET_CRYPTO_RsaPrivateKey
194 *hostkey,
195 struct
196 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
197 *result)
198{
199 gcry_mpi_t skey[2];
200 size_t size;
201 int rc;
202
203 rc = key_from_sexp (skey, hostkey->sexp, "public-key", "ne");
204 if (rc)
205 rc = key_from_sexp (skey, hostkey->sexp, "private-key", "ne");
206 if (rc)
207 rc = key_from_sexp (skey, hostkey->sexp, "rsa", "ne");
208 GNUNET_assert (0 == rc);
209 result->len =
210 htons (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) -
211 sizeof (result->padding));
212 result->sizen = htons (GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH);
213 result->padding = 0;
214 size = GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
215 GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
216 &result->key[0], size, &size, skey[0]));
217 adjust (&result->key[0], size, GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH);
218 size =
219 GNUNET_CRYPTO_RSA_KEY_LENGTH - GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
220 GNUNET_assert (0 ==
221 gcry_mpi_print (GCRYMPI_FMT_USG,
222 &result->
223 key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH],
224 size, &size, skey[1]));
225 adjust (&result->key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH], size,
226 GNUNET_CRYPTO_RSA_KEY_LENGTH -
227 GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH);
228 gcry_mpi_release (skey[0]);
229 gcry_mpi_release (skey[1]);
230}
231
232
233/**
234 * Internal: publicKey => RSA-Key.
235 *
236 * Note that the return type is not actually a private
237 * key but rather an sexpression for the public key!
238 */
239static struct GNUNET_CRYPTO_RsaPrivateKey *
240public2PrivateKey (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
241 *publicKey)
242{
243 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
244 gcry_sexp_t result;
245 gcry_mpi_t n;
246 gcry_mpi_t e;
247 size_t size;
248 size_t erroff;
249 int rc;
250
251 if ((ntohs (publicKey->sizen) != GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH) ||
252 (ntohs (publicKey->len) !=
253 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) -
254 sizeof (publicKey->padding)))
255 {
256 GNUNET_break (0);
257 return NULL;
258 }
259 size = GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
260 rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &publicKey->key[0], size, &size);
261 if (rc)
262 {
263 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
264 return NULL;
265 }
266 size =
267 GNUNET_CRYPTO_RSA_KEY_LENGTH - GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
268 rc =
269 gcry_mpi_scan (&e, GCRYMPI_FMT_USG,
270 &publicKey->key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH],
271 size, &size);
272 if (rc)
273 {
274 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
275 gcry_mpi_release (n);
276 return NULL;
277 }
278 rc = gcry_sexp_build (&result,
279 &erroff, "(public-key(rsa(n %m)(e %m)))", n, e);
280 gcry_mpi_release (n);
281 gcry_mpi_release (e);
282 if (rc)
283 {
284 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */
285 return NULL;
286 }
287 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
288 ret->sexp = result;
289 return ret;
290}
291
292
293/**
294 * Encode the private key in a format suitable for
295 * storing it into a file.
296 * @returns encoding of the private key.
297 * The first 4 bytes give the size of the array, as usual.
298 */
299static struct RsaPrivateKeyBinaryEncoded *
300rsa_encode_key (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
301{
302 struct RsaPrivateKeyBinaryEncoded *retval;
303 gcry_mpi_t pkv[6];
304 void *pbu[6];
305 size_t sizes[6];
306 int rc;
307 int i;
308 int size;
309
310#if EXTRA_CHECKS
311 if (gcry_pk_testkey (hostkey->sexp))
312 {
313 GNUNET_break (0);
314 return NULL;
315 }
316#endif
317
318 memset (pkv, 0, sizeof (gcry_mpi_t) * 6);
319 rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpqu");
320 if (rc)
321 rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpqu");
322 if (rc)
323 rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpq");
324 if (rc)
325 rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpq");
326 if (rc)
327 rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "ned");
328 if (rc)
329 rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "ned");
330 GNUNET_assert (0 == rc);
331 size = sizeof (struct RsaPrivateKeyBinaryEncoded);
332 for (i = 0; i < 6; i++)
333 {
334 if (pkv[i] != NULL)
335 {
336 GNUNET_assert (0 == gcry_mpi_aprint (GCRYMPI_FMT_USG,
337 (unsigned char **) &pbu[i],
338 &sizes[i], pkv[i]));
339 size += sizes[i];
340 }
341 else
342 {
343 pbu[i] = NULL;
344 sizes[i] = 0;
345 }
346 }
347 GNUNET_assert (size < 65536);
348 retval = GNUNET_malloc (size);
349 retval->len = htons (size);
350 i = 0;
351 retval->sizen = htons (sizes[0]);
352 memcpy (&((char *) (&retval[1]))[i], pbu[0], sizes[0]);
353 i += sizes[0];
354 retval->sizee = htons (sizes[1]);
355 memcpy (&((char *) (&retval[1]))[i], pbu[1], sizes[1]);
356 i += sizes[1];
357 retval->sized = htons (sizes[2]);
358 memcpy (&((char *) (&retval[1]))[i], pbu[2], sizes[2]);
359 i += sizes[2];
360 /* swap p and q! */
361 retval->sizep = htons (sizes[4]);
362 memcpy (&((char *) (&retval[1]))[i], pbu[4], sizes[4]);
363 i += sizes[4];
364 retval->sizeq = htons (sizes[3]);
365 memcpy (&((char *) (&retval[1]))[i], pbu[3], sizes[3]);
366 i += sizes[3];
367 retval->sizedmp1 = htons (0);
368 retval->sizedmq1 = htons (0);
369 memcpy (&((char *) (&retval[1]))[i], pbu[5], sizes[5]);
370 for (i = 0; i < 6; i++)
371 {
372 if (pkv[i] != NULL)
373 gcry_mpi_release (pkv[i]);
374 if (pbu[i] != NULL)
375 free (pbu[i]);
376 }
377 return retval;
378}
379
380/**
381 * Decode the private key from the file-format back
382 * to the "normal", internal format.
383 */
384static struct GNUNET_CRYPTO_RsaPrivateKey *
385rsa_decode_key (const struct RsaPrivateKeyBinaryEncoded *encoding)
386{
387 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
388 gcry_sexp_t res;
389 gcry_mpi_t n, e, d, p, q, u;
390 int rc;
391 size_t size;
392 int pos;
393
394 pos = 0;
395 size = ntohs (encoding->sizen);
396 rc = gcry_mpi_scan (&n,
397 GCRYMPI_FMT_USG,
398 &((const unsigned char *) (&encoding[1]))[pos],
399 size, &size);
400 pos += ntohs (encoding->sizen);
401 if (rc)
402 {
403 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
404 return NULL;
405 }
406 size = ntohs (encoding->sizee);
407 rc = gcry_mpi_scan (&e,
408 GCRYMPI_FMT_USG,
409 &((const unsigned char *) (&encoding[1]))[pos],
410 size, &size);
411 pos += ntohs (encoding->sizee);
412 if (rc)
413 {
414 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
415 gcry_mpi_release (n);
416 return NULL;
417 }
418 size = ntohs (encoding->sized);
419 rc = gcry_mpi_scan (&d,
420 GCRYMPI_FMT_USG,
421 &((const unsigned char *) (&encoding[1]))[pos],
422 size, &size);
423 pos += ntohs (encoding->sized);
424 if (rc)
425 {
426 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
427 gcry_mpi_release (n);
428 gcry_mpi_release (e);
429 return NULL;
430 }
431 /* swap p and q! */
432 size = ntohs (encoding->sizep);
433 if (size > 0)
434 {
435 rc = gcry_mpi_scan (&q,
436 GCRYMPI_FMT_USG,
437 &((const unsigned char *) (&encoding[1]))[pos],
438 size, &size);
439 pos += ntohs (encoding->sizep);
440 if (rc)
441 {
442 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
443 gcry_mpi_release (n);
444 gcry_mpi_release (e);
445 gcry_mpi_release (d);
446 return NULL;
447 }
448 }
449 else
450 q = NULL;
451 size = ntohs (encoding->sizeq);
452 if (size > 0)
453 {
454 rc = gcry_mpi_scan (&p,
455 GCRYMPI_FMT_USG,
456 &((const unsigned char *) (&encoding[1]))[pos],
457 size, &size);
458 pos += ntohs (encoding->sizeq);
459 if (rc)
460 {
461 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
462 gcry_mpi_release (n);
463 gcry_mpi_release (e);
464 gcry_mpi_release (d);
465 if (q != NULL)
466 gcry_mpi_release (q);
467 return NULL;
468 }
469 }
470 else
471 p = NULL;
472 pos += ntohs (encoding->sizedmp1);
473 pos += ntohs (encoding->sizedmq1);
474 size =
475 ntohs (encoding->len) - sizeof (struct RsaPrivateKeyBinaryEncoded) - pos;
476 if (size > 0)
477 {
478 rc = gcry_mpi_scan (&u,
479 GCRYMPI_FMT_USG,
480 &((const unsigned char *) (&encoding[1]))[pos],
481 size, &size);
482 if (rc)
483 {
484 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
485 gcry_mpi_release (n);
486 gcry_mpi_release (e);
487 gcry_mpi_release (d);
488 if (p != NULL)
489 gcry_mpi_release (p);
490 if (q != NULL)
491 gcry_mpi_release (q);
492 return NULL;
493 }
494 }
495 else
496 u = NULL;
497
498 if ((p != NULL) && (q != NULL) && (u != NULL))
499 {
500 rc = gcry_sexp_build (&res, &size, /* erroff */
501 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))",
502 n, e, d, p, q, u);
503 }
504 else
505 {
506 if ((p != NULL) && (q != NULL))
507 {
508 rc = gcry_sexp_build (&res, &size, /* erroff */
509 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))",
510 n, e, d, p, q);
511 }
512 else
513 {
514 rc = gcry_sexp_build (&res, &size, /* erroff */
515 "(private-key(rsa(n %m)(e %m)(d %m)))",
516 n, e, d);
517 }
518 }
519 gcry_mpi_release (n);
520 gcry_mpi_release (e);
521 gcry_mpi_release (d);
522 if (p != NULL)
523 gcry_mpi_release (p);
524 if (q != NULL)
525 gcry_mpi_release (q);
526 if (u != NULL)
527 gcry_mpi_release (u);
528
529 if (rc)
530 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
531#if EXTRA_CHECKS
532 if (gcry_pk_testkey (res))
533 {
534 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
535 return NULL;
536 }
537#endif
538 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
539 ret->sexp = res;
540 return ret;
541}
542
543
544/**
545 * Create a new private key by reading it from a file. If the
546 * files does not exist, create a new key and write it to the
547 * file. Caller must free return value. Note that this function
548 * can not guarantee that another process might not be trying
549 * the same operation on the same file at the same time. The
550 * caller must somehow know that the file either already exists
551 * with a valid key OR be sure that no other process is calling
552 * this function at the same time. If the contents of the file
553 * are invalid the old file is deleted and a fresh key is
554 * created.
555 *
556 * @return new private key, NULL on error (for example,
557 * permission denied)
558 */
559struct GNUNET_CRYPTO_RsaPrivateKey *
560GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename)
561{
562 struct flock fl;
563 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
564 struct RsaPrivateKeyBinaryEncoded *enc;
565 struct stat sbuf;
566 uint16_t len;
567 int fd;
568 unsigned int cnt;
569 int ec;
570
571 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
572 return NULL;
573 while (0 != STAT (filename, &sbuf))
574 {
575 fd = open (filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
576 if (-1 == fd)
577 {
578 if (errno == EEXIST)
579 continue;
580 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
581 "open", filename);
582 return NULL;
583 }
584 memset (&fl, 0, sizeof (struct flock));
585 fl.l_type = F_WRLCK;
586 fl.l_whence = SEEK_SET;
587 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
588 cnt = 0;
589 while (0 != fcntl (fd, F_SETLK, &fl))
590 {
591 sleep (1);
592 if (0 == ++cnt % 10)
593 {
594 ec = errno;
595 fl.l_type = F_GETLK;
596 fcntl (fd, F_GETLK, &fl);
597 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598 _
599 ("Could not aquire lock on file `%s' due to process %u: %s...\n"),
600 filename, fl.l_pid, STRERROR (errno));
601 }
602 memset (&fl, 0, sizeof (struct flock));
603 fl.l_type = F_WRLCK;
604 fl.l_whence = SEEK_SET;
605 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
606 }
607 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
608 _("Creating a new private key. This may take a while.\n"));
609 ret = GNUNET_CRYPTO_rsa_key_create ();
610 GNUNET_assert (ret != NULL);
611 enc = rsa_encode_key (ret);
612 GNUNET_assert (enc != NULL);
613 GNUNET_assert (ntohs (enc->len) == WRITE (fd, enc, ntohs (enc->len)));
614 GNUNET_free (enc);
615 fdatasync (fd);
616 memset (&fl, 0, sizeof (struct flock));
617 fl.l_type = F_UNLCK;
618 fl.l_whence = SEEK_SET;
619 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
620 cnt = 0;
621 if (0 != fcntl (fd, F_SETLK, &fl))
622 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
623 "fcntl", filename);
624 GNUNET_assert (0 == CLOSE (fd));
625 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
626 _("Stored new private key in `%s'.\n"), filename);
627 return ret;
628 }
629 /* hostkey file exists already, read it! */
630 fd = open (filename, O_RDONLY);
631 if (-1 == fd)
632 {
633 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
634 return NULL;
635 }
636 cnt = 0;
637 while (1)
638 {
639 memset (&fl, 0, sizeof (struct flock));
640 fl.l_type = F_RDLCK;
641 fl.l_whence = SEEK_SET;
642 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
643 if (0 != fcntl (fd, F_SETLK, &fl))
644 {
645 if (0 == ++cnt % 10)
646 {
647 ec = errno;
648 fl.l_type = F_GETLK;
649 fcntl (fd, F_GETLK, &fl);
650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651 _
652 ("Could not aquire lock on file `%s' due to process %u: %s...\n"),
653 filename, fl.l_pid, STRERROR (errno));
654 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
655 _
656 ("This may be ok if someone is currently generating a hostkey.\n"));
657 }
658 sleep (1);
659 continue;
660 }
661 if (0 != STAT (filename, &sbuf))
662 {
663 /* eh, what!? File we opened is now gone!? */
664 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
665 "stat", filename);
666 memset (&fl, 0, sizeof (struct flock));
667 fl.l_type = F_UNLCK;
668 fl.l_whence = SEEK_SET;
669 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
670 if (0 != fcntl (fd, F_SETLK, &fl))
671 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
672 "fcntl", filename);
673 GNUNET_assert (0 == CLOSE (fd));
674 return NULL;
675 }
676 if (sbuf.st_size < sizeof (struct RsaPrivateKeyBinaryEncoded))
677 {
678 /* maybe we got the read lock before the hostkey generating
679 process had a chance to get the write lock; give it up! */
680 memset (&fl, 0, sizeof (struct flock));
681 fl.l_type = F_UNLCK;
682 fl.l_whence = SEEK_SET;
683 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
684 if (0 != fcntl (fd, F_SETLK, &fl))
685 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
686 "fcntl", filename);
687 if (0 == ++cnt % 10)
688 {
689 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
690 _
691 ("When trying to read hostkey file `%s' I found %u bytes but I need at least %u.\n"),
692 filename, (unsigned int) sbuf.st_size,
693 (unsigned int) sizeof (struct
694 RsaPrivateKeyBinaryEncoded));
695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
696 _
697 ("This may be ok if someone is currently generating a hostkey.\n"));
698 }
699 sleep (2); /* wait a bit longer! */
700 continue;
701 }
702 break;
703 }
704 enc = GNUNET_malloc (sbuf.st_size);
705 GNUNET_assert (sbuf.st_size == READ (fd, enc, sbuf.st_size));
706 len = ntohs (enc->len);
707 if ((len != sbuf.st_size) || (NULL == (ret = rsa_decode_key (enc))))
708 {
709 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
710 _
711 ("File `%s' does not contain a valid private key. You should delete it.\n"),
712 filename);
713 GNUNET_free (enc);
714 }
715 memset (&fl, 0, sizeof (struct flock));
716 fl.l_type = F_UNLCK;
717 fl.l_whence = SEEK_SET;
718 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
719 if (0 != fcntl (fd, F_SETLK, &fl))
720 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
721 GNUNET_assert (0 == CLOSE (fd));
722 return ret;
723}
724
725
726/**
727 * Encrypt a block with the public key of another host that uses the
728 * same cyper.
729 *
730 * @param block the block to encrypt
731 * @param size the size of block
732 * @param publicKey the encoded public key used to encrypt
733 * @param target where to store the encrypted block
734 * @returns GNUNET_SYSERR on error, GNUNET_OK if ok
735 */
736int
737GNUNET_CRYPTO_rsa_encrypt (const void *block,
738 uint16_t size,
739 const struct
740 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey,
741 struct GNUNET_CRYPTO_RsaEncryptedData *target)
742{
743 gcry_sexp_t result;
744 gcry_sexp_t data;
745 struct GNUNET_CRYPTO_RsaPrivateKey *pubkey;
746 gcry_mpi_t val;
747 gcry_mpi_t rval;
748 size_t isize;
749 size_t erroff;
750
751 GNUNET_assert (size <= sizeof (GNUNET_HashCode));
752 pubkey = public2PrivateKey (publicKey);
753 isize = size;
754 GNUNET_assert (0 ==
755 gcry_mpi_scan (&val, GCRYMPI_FMT_USG, block, isize, &isize));
756 GNUNET_assert (0 ==
757 gcry_sexp_build (&data, &erroff,
758 "(data (flags pkcs1)(value %m))", val));
759 gcry_mpi_release (val);
760 GNUNET_assert (0 == gcry_pk_encrypt (&result, data, pubkey->sexp));
761 gcry_sexp_release (data);
762 GNUNET_CRYPTO_rsa_key_free (pubkey);
763
764 GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "a"));
765 gcry_sexp_release (result);
766 isize = sizeof (struct GNUNET_CRYPTO_RsaEncryptedData);
767 GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
768 (unsigned char *) target, isize, &isize,
769 rval));
770 gcry_mpi_release (rval);
771 adjust (&target->encoding[0], isize,
772 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
773 return GNUNET_OK;
774}
775
776/**
777 * Decrypt a given block with the hostkey.
778 *
779 * @param hostkey the hostkey with which to decrypt this block
780 * @param block the data to decrypt, encoded as returned by encrypt
781 * @param result pointer to a location where the result can be stored
782 * @param max the maximum number of bits to store for the result, if
783 * the decrypted block is bigger, an error is returned
784 * @returns the size of the decrypted block, -1 on error
785 */
786int
787GNUNET_CRYPTO_rsa_decrypt (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey,
788 const struct GNUNET_CRYPTO_RsaEncryptedData *block,
789 void *result, uint16_t max)
790{
791 gcry_sexp_t resultsexp;
792 gcry_sexp_t data;
793 size_t erroff;
794 size_t size;
795 gcry_mpi_t val;
796 unsigned char *endp;
797 unsigned char *tmp;
798
799#if EXTRA_CHECKS
800 GNUNET_assert (0 == gcry_pk_testkey (hostkey->sexp));
801#endif
802 size = sizeof (struct GNUNET_CRYPTO_RsaEncryptedData);
803 GNUNET_assert (0 == gcry_mpi_scan (&val,
804 GCRYMPI_FMT_USG, &block->encoding[0],
805 size, &size));
806 GNUNET_assert (0 ==
807 gcry_sexp_build (&data, &erroff,
808 "(enc-val(flags)(rsa(a %m)))", val));
809 gcry_mpi_release (val);
810 GNUNET_assert (0 == gcry_pk_decrypt (&resultsexp, data, hostkey->sexp));
811 gcry_sexp_release (data);
812 /* resultsexp has format "(value %m)" */
813 GNUNET_assert (NULL !=
814 (val = gcry_sexp_nth_mpi (resultsexp, 1, GCRYMPI_FMT_USG)));
815 gcry_sexp_release (resultsexp);
816 tmp = GNUNET_malloc (max + HOSTKEY_LEN / 8);
817 size = max + HOSTKEY_LEN / 8;
818 GNUNET_assert (0 ==
819 gcry_mpi_print (GCRYMPI_FMT_USG, tmp, size, &size, val));
820 gcry_mpi_release (val);
821 endp = tmp;
822 endp += (size - max);
823 size = max;
824 memcpy (result, endp, size);
825 GNUNET_free (tmp);
826 return size;
827}
828
829
830/**
831 * Sign a given block.
832 *
833 * @param hostkey private key to use for the signing
834 * @param purpose what to sign (size, purpose)
835 * @param result where to write the signature
836 * @return GNUNET_SYSERR on error, GNUNET_OK on success
837 */
838int
839GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey,
840 const struct GNUNET_CRYPTO_RsaSignaturePurpose
841 *purpose, struct GNUNET_CRYPTO_RsaSignature *sig)
842{
843 gcry_sexp_t result;
844 gcry_sexp_t data;
845 size_t ssize;
846 gcry_mpi_t rval;
847 GNUNET_HashCode hc;
848 char *buff;
849 int bufSize;
850
851 GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc);
852#define FORMATSTRING "(4:data(5:flags5:pkcs1)(4:hash6:sha51264:0123456789012345678901234567890123456789012345678901234567890123))"
853 bufSize = strlen (FORMATSTRING) + 1;
854 buff = GNUNET_malloc (bufSize);
855 memcpy (buff, FORMATSTRING, bufSize);
856 memcpy (&buff
857 [bufSize -
858 strlen
859 ("0123456789012345678901234567890123456789012345678901234567890123))")
860 - 1], &hc, sizeof (GNUNET_HashCode));
861 GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0));
862 GNUNET_free (buff);
863 GNUNET_assert (0 == gcry_pk_sign (&result, data, hostkey->sexp));
864 gcry_sexp_release (data);
865 GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "s"));
866 gcry_sexp_release (result);
867 ssize = sizeof (struct GNUNET_CRYPTO_RsaSignature);
868 GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
869 (unsigned char *) sig, ssize, &ssize,
870 rval));
871 gcry_mpi_release (rval);
872 adjust (sig->sig, ssize, sizeof (struct GNUNET_CRYPTO_RsaSignature));
873 return GNUNET_OK;
874}
875
876
877/**
878 * Verify signature.
879 *
880 * @param purpose what is the purpose that the signature should have?
881 * @param validate block to validate (size, purpose, data)
882 * @param sig signature that is being validated
883 * @param publicKey public key of the signer
884 * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
885 */
886int
887GNUNET_CRYPTO_rsa_verify (uint32_t purpose,
888 const struct GNUNET_CRYPTO_RsaSignaturePurpose
889 *validate,
890 const struct GNUNET_CRYPTO_RsaSignature *sig,
891 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
892 *publicKey)
893{
894 gcry_sexp_t data;
895 gcry_sexp_t sigdata;
896 size_t size;
897 gcry_mpi_t val;
898 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
899 GNUNET_HashCode hc;
900 char *buff;
901 int bufSize;
902 size_t erroff;
903 int rc;
904
905 if (purpose != ntohl (validate->purpose))
906 return GNUNET_SYSERR; /* purpose mismatch */
907 GNUNET_CRYPTO_hash (validate, ntohl (validate->size), &hc);
908 size = sizeof (struct GNUNET_CRYPTO_RsaSignature);
909 GNUNET_assert (0 == gcry_mpi_scan (&val,
910 GCRYMPI_FMT_USG,
911 (const unsigned char *) sig, size,
912 &size));
913 GNUNET_assert (0 ==
914 gcry_sexp_build (&sigdata, &erroff, "(sig-val(rsa(s %m)))",
915 val));
916 gcry_mpi_release (val);
917 bufSize = strlen (FORMATSTRING) + 1;
918 buff = GNUNET_malloc (bufSize);
919 memcpy (buff, FORMATSTRING, bufSize);
920 memcpy (&buff[strlen (FORMATSTRING) -
921 strlen
922 ("0123456789012345678901234567890123456789012345678901234567890123))")],
923 &hc, sizeof (GNUNET_HashCode));
924 GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0));
925 GNUNET_free (buff);
926 hostkey = public2PrivateKey (publicKey);
927 if (hostkey == NULL)
928 {
929 gcry_sexp_release (data);
930 gcry_sexp_release (sigdata);
931 return GNUNET_SYSERR;
932 }
933 rc = gcry_pk_verify (sigdata, data, hostkey->sexp);
934 GNUNET_CRYPTO_rsa_key_free (hostkey);
935 gcry_sexp_release (data);
936 gcry_sexp_release (sigdata);
937 if (rc)
938 {
939 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
940 _("RSA signature verification failed at %s:%d: %s\n"),
941 __FILE__, __LINE__, gcry_strerror (rc));
942 return GNUNET_SYSERR;
943 }
944 return GNUNET_OK;
945}
946
947
948/* end of crypto_rsa.c */
diff --git a/src/util/disk.c b/src/util/disk.c
new file mode 100644
index 000000000..f0fe9341b
--- /dev/null
+++ b/src/util/disk.c
@@ -0,0 +1,954 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/disk.c
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_directories.h"
30#include "gnunet_disk_lib.h"
31#include "gnunet_scheduler_lib.h"
32#include "gnunet_strings_lib.h"
33
34
35#if LINUX || CYGWIN
36#include <sys/vfs.h>
37#else
38#ifdef SOMEBSD
39#include <sys/param.h>
40#include <sys/mount.h>
41#else
42#ifdef OSX
43#include <sys/param.h>
44#include <sys/mount.h>
45#else
46#ifdef SOLARIS
47#include <sys/types.h>
48#include <sys/statvfs.h>
49#else
50#ifdef MINGW
51#define _IFMT 0170000 /* type of file */
52#define _IFLNK 0120000 /* symbolic link */
53#define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
54#else
55#error PORT-ME: need to port statfs (how much space is left on the drive?)
56#endif
57#endif
58#endif
59#endif
60#endif
61
62#ifndef SOMEBSD
63#ifndef WINDOWS
64#ifndef OSX
65#include <wordexp.h>
66#endif
67#endif
68#endif
69
70typedef struct
71{
72 unsigned long long total;
73 int include_sym_links;
74} GetFileSizeData;
75
76static int
77getSizeRec (void *ptr, const char *fn)
78{
79 GetFileSizeData *gfsd = ptr;
80#ifdef HAVE_STAT64
81 struct stat64 buf;
82#else
83 struct stat buf;
84#endif
85
86#ifdef HAVE_STAT64
87 if (0 != STAT64 (fn, &buf))
88 {
89 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
90 return GNUNET_SYSERR;
91 }
92#else
93 if (0 != STAT (fn, &buf))
94 {
95 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
96 return GNUNET_SYSERR;
97 }
98#endif
99 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
100 gfsd->total += buf.st_size;
101 if ((S_ISDIR (buf.st_mode)) &&
102 (0 == ACCESS (fn, X_OK)) &&
103 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
104 {
105 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
106 return GNUNET_SYSERR;
107 }
108 return GNUNET_OK;
109}
110
111/**
112 * Get the size of the file (or directory)
113 * of the given file (in bytes).
114 *
115 * @return GNUNET_SYSERR on error, GNUNET_OK on success
116 */
117int
118GNUNET_DISK_file_size (const char *filename,
119 unsigned long long *size, int includeSymLinks)
120{
121 GetFileSizeData gfsd;
122 int ret;
123
124 GNUNET_assert (size != NULL);
125 gfsd.total = 0;
126 gfsd.include_sym_links = includeSymLinks;
127 ret = getSizeRec (&gfsd, filename);
128 *size = gfsd.total;
129 return ret;
130}
131
132/**
133 * Get the number of blocks that are left on the partition that
134 * contains the given file (for normal users).
135 *
136 * @param part a file on the partition to check
137 * @return -1 on errors, otherwise the number of free blocks
138 */
139long
140GNUNET_DISK_get_blocks_available (const char *part)
141{
142#ifdef SOLARIS
143 struct statvfs buf;
144
145 if (0 != statvfs (part, &buf))
146 {
147 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
148 return -1;
149 }
150 return buf.f_bavail;
151#elif MINGW
152 DWORD dwDummy;
153 DWORD dwBlocks;
154 char szDrive[4];
155
156 memcpy (szDrive, part, 3);
157 szDrive[3] = 0;
158 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
159 {
160 GNUNET_GE_LOG (GNUNET_ERROR_TYPE_WARNING,
161 _("`%s' failed for drive `%s': %u\n"),
162 "GetDiskFreeSpace", szDrive, GetLastError ());
163
164 return -1;
165 }
166 return dwBlocks;
167#else
168 struct statfs s;
169 if (0 != statfs (part, &s))
170 {
171 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
172 return -1;
173 }
174 return s.f_bavail;
175#endif
176}
177
178/**
179 * Test if fil is a directory.
180 *
181 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
182 * does not exist
183 */
184int
185GNUNET_DISK_directory_test (const char *fil)
186{
187 struct stat filestat;
188 int ret;
189
190 ret = STAT (fil, &filestat);
191 if (ret != 0)
192 {
193 if (errno != ENOENT)
194 {
195 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
196 return GNUNET_SYSERR;
197 }
198 return GNUNET_NO;
199 }
200 if (!S_ISDIR (filestat.st_mode))
201 return GNUNET_NO;
202 if (ACCESS (fil, R_OK | X_OK) < 0)
203 {
204 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
205 return GNUNET_SYSERR;
206 }
207 return GNUNET_YES;
208}
209
210/**
211 * Check that fil corresponds to a filename
212 * (of a file that exists and that is not a directory).
213 * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
214 * else (will print an error message in that case, too).
215 */
216int
217GNUNET_DISK_file_test (const char *fil)
218{
219 struct stat filestat;
220 int ret;
221 char *rdir;
222
223 rdir = GNUNET_STRINGS_filename_expand (fil);
224 if (rdir == NULL)
225 return GNUNET_SYSERR;
226
227 ret = STAT (rdir, &filestat);
228 if (ret != 0)
229 {
230 if (errno != ENOENT)
231 {
232 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
233 GNUNET_free (rdir);
234 return GNUNET_SYSERR;
235 }
236 GNUNET_free (rdir);
237 return GNUNET_NO;
238 }
239 if (!S_ISREG (filestat.st_mode))
240 {
241 GNUNET_free (rdir);
242 return GNUNET_NO;
243 }
244 if (ACCESS (rdir, R_OK) < 0)
245 {
246 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
247 GNUNET_free (rdir);
248 return GNUNET_SYSERR;
249 }
250 GNUNET_free (rdir);
251 return GNUNET_YES;
252}
253
254/**
255 * Implementation of "mkdir -p"
256 * @param dir the directory to create
257 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
258 */
259int
260GNUNET_DISK_directory_create (const char *dir)
261{
262 char *rdir;
263 int len;
264 int pos;
265 int ret = GNUNET_OK;
266
267 rdir = GNUNET_STRINGS_filename_expand (dir);
268 if (rdir == NULL)
269 return GNUNET_SYSERR;
270
271 len = strlen (rdir);
272#ifndef MINGW
273 pos = 1; /* skip heading '/' */
274#else
275 /* Local or Network path? */
276 if (strncmp (rdir, "\\\\", 2) == 0)
277 {
278 pos = 2;
279 while (rdir[pos])
280 {
281 if (rdir[pos] == '\\')
282 {
283 pos++;
284 break;
285 }
286 pos++;
287 }
288 }
289 else
290 {
291 pos = 3; /* strlen("C:\\") */
292 }
293#endif
294 while (pos <= len)
295 {
296 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
297 {
298 rdir[pos] = '\0';
299 ret = GNUNET_DISK_directory_test (rdir);
300 if (ret == GNUNET_SYSERR)
301 {
302 GNUNET_free (rdir);
303 return GNUNET_SYSERR;
304 }
305 if (ret == GNUNET_NO)
306 {
307#ifndef MINGW
308 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
309#else
310 ret = mkdir (rdir);
311#endif
312 if ((ret != 0) && (errno != EEXIST))
313 {
314 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
315 rdir);
316 GNUNET_free (rdir);
317 return GNUNET_SYSERR;
318 }
319 }
320 rdir[pos] = DIR_SEPARATOR;
321 }
322 pos++;
323 }
324 GNUNET_free (rdir);
325 return GNUNET_OK;
326}
327
328
329/**
330 * Create the directory structure for storing
331 * a file.
332 *
333 * @param filename name of a file in the directory
334 * @returns GNUNET_OK on success,
335 * GNUNET_SYSERR on failure,
336 * GNUNET_NO if the directory
337 * exists but is not writeable for us
338 */
339int
340GNUNET_DISK_directory_create_for_file (const char *dir)
341{
342 char *rdir;
343 int len;
344 int ret;
345
346 rdir = GNUNET_STRINGS_filename_expand (dir);
347 if (rdir == NULL)
348 return GNUNET_SYSERR;
349 len = strlen (rdir);
350 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
351 len--;
352 rdir[len] = '\0';
353 ret = GNUNET_DISK_directory_create (rdir);
354 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
355 ret = GNUNET_NO;
356 GNUNET_free (rdir);
357 return ret;
358}
359
360/**
361 * Read the contents of a binary file into a buffer.
362 * @param fileName the name of the file, not freed,
363 * must already be expanded!
364 * @param len the maximum number of bytes to read
365 * @param result the buffer to write the result to
366 * @return the number of bytes read on success, -1 on failure
367 */
368int
369GNUNET_DISK_file_read (const char *fileName, int len, void *result)
370{
371 /* open file, must exist, open read only */
372 int handle;
373 int size;
374
375 GNUNET_assert (fileName != NULL);
376 GNUNET_assert (len > 0);
377 if (len == 0)
378 return 0;
379 GNUNET_assert (result != NULL);
380 handle = GNUNET_DISK_file_open (fileName, O_RDONLY, S_IRUSR);
381 if (handle < 0)
382 return -1;
383 size = READ (handle, result, len);
384 GNUNET_DISK_file_close (fileName, handle);
385 return size;
386}
387
388
389/**
390 * Convert string to value ('755' for chmod-call)
391 */
392static int
393atoo (const char *s)
394{
395 int n = 0;
396
397 while (('0' <= *s) && (*s < '8'))
398 {
399 n <<= 3;
400 n += *s++ - '0';
401 }
402 return n;
403}
404
405/**
406 * Write a buffer to a file.
407 * @param fileName the name of the file, NOT freed!
408 * @param buffer the data to write
409 * @param n number of bytes to write
410 * @param mode permissions to set on the file
411 * @return GNUNET_OK on success, GNUNET_SYSERR on error
412 */
413int
414GNUNET_DISK_file_write (const char *fileName,
415 const void *buffer, unsigned int n, const char *mode)
416{
417 int handle;
418 char *fn;
419
420 /* open file, open with 600, create if not
421 present, otherwise overwrite */
422 GNUNET_assert (fileName != NULL);
423 fn = GNUNET_STRINGS_filename_expand (fileName);
424 handle = GNUNET_DISK_file_open (fn, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
425 if (handle == -1)
426 {
427 GNUNET_free (fn);
428 return GNUNET_SYSERR;
429 }
430 GNUNET_assert ((n == 0) || (buffer != NULL));
431 /* write the buffer take length from the beginning */
432 if (n != WRITE (handle, buffer, n))
433 {
434 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
435 GNUNET_DISK_file_close (fn, handle);
436 GNUNET_free (fn);
437 return GNUNET_SYSERR;
438 }
439 GNUNET_DISK_file_close (fn, handle);
440 if (0 != CHMOD (fn, atoo (mode)))
441 {
442 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
443 }
444 GNUNET_free (fn);
445 return GNUNET_OK;
446}
447
448/**
449 * Scan a directory for files. The name of the directory
450 * must be expanded first (!).
451 * @param dirName the name of the directory
452 * @param callback the method to call for each file,
453 * can be NULL, in that case, we only count
454 * @param data argument to pass to callback
455 * @return the number of files found, GNUNET_SYSERR on error or
456 * ieration aborted by callback returning GNUNET_SYSERR
457 */
458int
459GNUNET_DISK_directory_scan (const char *dirName,
460 GNUNET_FileNameCallback callback, void *data)
461{
462 DIR *dinfo;
463 struct dirent *finfo;
464 struct stat istat;
465 int count = 0;
466 char *name;
467 char *dname;
468 unsigned int name_len;
469 unsigned int n_size;
470
471 GNUNET_assert (dirName != NULL);
472 dname = GNUNET_STRINGS_filename_expand (dirName);
473 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
474 dname[strlen (dname) - 1] = '\0';
475 if (0 != STAT (dname, &istat))
476 {
477 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
478 GNUNET_free (dname);
479 return GNUNET_SYSERR;
480 }
481 if (!S_ISDIR (istat.st_mode))
482 {
483 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
484 _("Expected `%s' to be a directory!\n"), dirName);
485 GNUNET_free (dname);
486 return GNUNET_SYSERR;
487 }
488 errno = 0;
489 dinfo = OPENDIR (dname);
490 if ((errno == EACCES) || (dinfo == NULL))
491 {
492 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
493 if (dinfo != NULL)
494 closedir (dinfo);
495 GNUNET_free (dname);
496 return GNUNET_SYSERR;
497 }
498 name_len = 256;
499 n_size = strlen (dname) + name_len + 2;
500 name = GNUNET_malloc (n_size);
501 while ((finfo = readdir (dinfo)) != NULL)
502 {
503 if ((0 == strcmp (finfo->d_name, ".")) ||
504 (0 == strcmp (finfo->d_name, "..")))
505 continue;
506 if (callback != NULL)
507 {
508 if (name_len < strlen (finfo->d_name))
509 {
510 GNUNET_free (name);
511 name_len = strlen (finfo->d_name);
512 n_size = strlen (dname) + name_len + 2;
513 name = GNUNET_malloc (n_size);
514 }
515 /* dname can end in "/" only if dname == "/";
516 if dname does not end in "/", we need to add
517 a "/" (otherwise, we must not!) */
518 GNUNET_snprintf (name,
519 n_size,
520 "%s%s%s",
521 dname,
522 (strcmp (dname, DIR_SEPARATOR_STR) ==
523 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
524 if (GNUNET_OK != callback (data, name))
525 {
526 closedir (dinfo);
527 GNUNET_free (name);
528 GNUNET_free (dname);
529 return GNUNET_SYSERR;
530 }
531 }
532 count++;
533 }
534 closedir (dinfo);
535 GNUNET_free (name);
536 GNUNET_free (dname);
537 return count;
538}
539
540
541/**
542 * Opaque handle used for iterating over a directory.
543 */
544struct GNUNET_DISK_DirectoryIterator
545{
546 /**
547 * Our scheduler.
548 */
549 struct GNUNET_SCHEDULER_Handle *sched;
550
551 /**
552 * Function to call on directory entries.
553 */
554 GNUNET_DISK_DirectoryIteratorCallback callback;
555
556 /**
557 * Closure for callback.
558 */
559 void *callback_cls;
560
561 /**
562 * Reference to directory.
563 */
564 DIR *directory;
565
566 /**
567 * Directory name.
568 */
569 char *dirname;
570
571 /**
572 * Next filename to process.
573 */
574 char *next_name;
575
576 /**
577 * Our priority.
578 */
579 enum GNUNET_SCHEDULER_Priority priority;
580
581};
582
583
584/**
585 * Task used by the directory iterator.
586 */
587static void
588directory_iterator_task (void *cls,
589 const struct GNUNET_SCHEDULER_TaskContext *tc)
590{
591 struct GNUNET_DISK_DirectoryIterator *iter = cls;
592 char *name;
593
594 name = iter->next_name;
595 GNUNET_assert (name != NULL);
596 iter->next_name = NULL;
597 iter->callback (iter->callback_cls, iter, name, iter->dirname);
598 GNUNET_free (name);
599}
600
601
602/**
603 * This function must be called during the DiskIteratorCallback
604 * (exactly once) to schedule the task to process the next
605 * filename in the directory (if there is one).
606 *
607 * @param iter opaque handle for the iterator
608 * @param can set to GNUNET_YES to terminate the iteration early
609 * @return GNUNET_YES if iteration will continue,
610 * GNUNET_NO if this was the last entry (and iteration is complete),
611 * GNUNET_SYSERR if abort was YES
612 */
613int
614GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
615 *iter, int can)
616{
617 struct dirent *finfo;
618
619 GNUNET_assert (iter->next_name == NULL);
620 if (can == GNUNET_YES)
621 {
622 closedir (iter->directory);
623 GNUNET_free (iter->dirname);
624 GNUNET_free (iter);
625 return GNUNET_SYSERR;
626 }
627 while (NULL != (finfo = readdir (iter->directory)))
628 {
629 if ((0 == strcmp (finfo->d_name, ".")) ||
630 (0 == strcmp (finfo->d_name, "..")))
631 continue;
632 GNUNET_asprintf (&iter->next_name,
633 "%s%s%s",
634 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
635 break;
636 }
637 if (finfo == NULL)
638 {
639 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
640 return GNUNET_NO;
641 }
642 GNUNET_SCHEDULER_add_after (iter->sched,
643 GNUNET_YES,
644 iter->priority,
645 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
646 &directory_iterator_task, iter);
647 return GNUNET_YES;
648}
649
650
651/**
652 * Scan a directory for files using the scheduler to run a task for
653 * each entry. The name of the directory must be expanded first (!).
654 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
655 * may provide a simpler API.
656 *
657 * @param sched scheduler to use
658 * @param prio priority to use
659 * @param dirName the name of the directory
660 * @param callback the method to call for each file
661 * @param callback_cls closure for callback
662 */
663void
664GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
665 enum GNUNET_SCHEDULER_Priority prio,
666 const char *dirName,
667 GNUNET_DISK_DirectoryIteratorCallback
668 callback, void *callback_cls)
669{
670 struct GNUNET_DISK_DirectoryIterator *di;
671
672 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
673 di->sched = sched;
674 di->callback = callback;
675 di->callback_cls = callback_cls;
676 di->directory = OPENDIR (dirName);
677 di->dirname = GNUNET_strdup (dirName);
678 di->priority = prio;
679 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
680}
681
682
683static int
684remove_helper (void *unused, const char *fn)
685{
686 GNUNET_DISK_directory_remove (fn);
687 return GNUNET_OK;
688}
689
690/**
691 * Remove all files in a directory (rm -rf). Call with
692 * caution.
693 *
694 *
695 * @param fileName the file to remove
696 * @return GNUNET_OK on success, GNUNET_SYSERR on error
697 */
698int
699GNUNET_DISK_directory_remove (const char *fileName)
700{
701 struct stat istat;
702
703 if (0 != LSTAT (fileName, &istat))
704 return GNUNET_NO; /* file may not exist... */
705 if (UNLINK (fileName) == 0)
706 return GNUNET_OK;
707 if ((errno != EISDIR) &&
708 /* EISDIR is not sufficient in all cases, e.g.
709 sticky /tmp directory may result in EPERM on BSD.
710 So we also explicitly check "isDirectory" */
711 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
712 {
713 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
714 return GNUNET_SYSERR;
715 }
716 if (GNUNET_SYSERR ==
717 GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
718 return GNUNET_SYSERR;
719 if (0 != RMDIR (fileName))
720 {
721 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
722 return GNUNET_SYSERR;
723 }
724 return GNUNET_OK;
725}
726
727void
728GNUNET_DISK_file_close (const char *filename, int fd)
729{
730 if (0 != CLOSE (fd))
731 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", filename);
732}
733
734int
735GNUNET_DISK_file_open (const char *filename, int oflag, ...)
736{
737 char *fn;
738 int mode;
739 int ret;
740#ifdef MINGW
741 char szFile[_MAX_PATH + 1];
742 long lRet;
743
744 if ((lRet = plibc_conv_to_win_path (filename, szFile)) != ERROR_SUCCESS)
745 {
746 errno = ENOENT;
747 SetLastError (lRet);
748 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
749 "plibc_conv_to_win_path", filename);
750 return -1;
751 }
752 fn = GNUNET_strdup (szFile);
753#else
754 fn = GNUNET_STRINGS_filename_expand (filename);
755#endif
756 if (oflag & O_CREAT)
757 {
758 va_list arg;
759 va_start (arg, oflag);
760 mode = va_arg (arg, int);
761 va_end (arg);
762 }
763 else
764 {
765 mode = 0;
766 }
767#ifdef MINGW
768 /* set binary mode */
769 oflag |= O_BINARY;
770#endif
771 ret = OPEN (fn, oflag, mode);
772 if (ret == -1)
773 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", fn);
774 GNUNET_free (fn);
775 return ret;
776}
777
778#define COPY_BLK_SIZE 65536
779
780/**
781 * Copy a file.
782 * @return GNUNET_OK on success, GNUNET_SYSERR on error
783 */
784int
785GNUNET_DISK_file_copy (const char *src, const char *dst)
786{
787 char *buf;
788 unsigned long long pos;
789 unsigned long long size;
790 unsigned long long len;
791 int in;
792 int out;
793
794 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
795 return GNUNET_SYSERR;
796 pos = 0;
797 in = GNUNET_DISK_file_open (src, O_RDONLY | O_LARGEFILE);
798 if (in == -1)
799 return GNUNET_SYSERR;
800 out = GNUNET_DISK_file_open (dst,
801 O_LARGEFILE | O_WRONLY | O_CREAT | O_EXCL,
802 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
803 if (out == -1)
804 {
805 GNUNET_DISK_file_close (src, in);
806 return GNUNET_SYSERR;
807 }
808 buf = GNUNET_malloc (COPY_BLK_SIZE);
809 while (pos < size)
810 {
811 len = COPY_BLK_SIZE;
812 if (len > size - pos)
813 len = size - pos;
814 if (len != READ (in, buf, len))
815 goto FAIL;
816 if (len != WRITE (out, buf, len))
817 goto FAIL;
818 pos += len;
819 }
820 GNUNET_free (buf);
821 GNUNET_DISK_file_close (src, in);
822 GNUNET_DISK_file_close (dst, out);
823 return GNUNET_OK;
824FAIL:
825 GNUNET_free (buf);
826 GNUNET_DISK_file_close (src, in);
827 GNUNET_DISK_file_close (dst, out);
828 return GNUNET_SYSERR;
829}
830
831
832/**
833 * @brief Removes special characters as ':' from a filename.
834 * @param fn the filename to canonicalize
835 */
836void
837GNUNET_DISK_filename_canonicalize (char *fn)
838{
839 char *idx;
840 char c;
841
842 idx = fn;
843 while (*idx)
844 {
845 c = *idx;
846
847 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
848 c == '"' || c == '<' || c == '>' || c == '|')
849 {
850 *idx = '_';
851 }
852
853 idx++;
854 }
855}
856
857
858
859/**
860 * @brief Change owner of a file
861 */
862int
863GNUNET_DISK_file_change_owner (const char *filename, const char *user)
864{
865#ifndef MINGW
866 struct passwd *pws;
867
868 pws = getpwnam (user);
869 if (pws == NULL)
870 {
871 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
872 _("Cannot obtain information about user `%s': %s\n"),
873 user, STRERROR (errno));
874 return GNUNET_SYSERR;
875 }
876 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
877 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
878#endif
879 return GNUNET_OK;
880}
881
882
883/**
884 * Construct full path to a file inside of the private
885 * directory used by GNUnet. Also creates the corresponding
886 * directory. If the resulting name is supposed to be
887 * a directory, end the last argument in '/' (or pass
888 * DIR_SEPARATOR_STR as the last argument before NULL).
889 *
890 * @param serviceName name of the service
891 * @param varargs is NULL-terminated list of
892 * path components to append to the
893 * private directory name.
894 * @return the constructed filename
895 */
896char *
897GNUNET_DISK_get_home_filename (struct GNUNET_CONFIGURATION_Handle *cfg,
898 const char *serviceName, ...)
899{
900 const char *c;
901 char *pfx;
902 char *ret;
903 va_list ap;
904 unsigned int needed;
905
906 if (GNUNET_OK !=
907 GNUNET_CONFIGURATION_get_value_filename (cfg,
908 serviceName, "HOME", &pfx))
909 return NULL;
910 if (pfx == NULL)
911 {
912 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
913 _("No `%s' specified for service `%s' in configuration.\n"),
914 "HOME", serviceName);
915 return NULL;
916 }
917 needed = strlen (pfx) + 2;
918 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
919 needed++;
920 va_start (ap, serviceName);
921 while (1)
922 {
923 c = va_arg (ap, const char *);
924 if (c == NULL)
925 break;
926 needed += strlen (c);
927 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
928 needed++;
929 }
930 va_end (ap);
931 ret = GNUNET_malloc (needed);
932 strcpy (ret, pfx);
933 GNUNET_free (pfx);
934 va_start (ap, serviceName);
935 while (1)
936 {
937 c = va_arg (ap, const char *);
938 if (c == NULL)
939 break;
940 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
941 strcat (ret, DIR_SEPARATOR_STR);
942 strcat (ret, c);
943 }
944 va_end (ap);
945 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
946 GNUNET_DISK_directory_create_for_file (ret);
947 else
948 GNUNET_DISK_directory_create (ret);
949 return ret;
950}
951
952
953
954/* end of disk.c */
diff --git a/src/util/getopt.c b/src/util/getopt.c
new file mode 100644
index 000000000..e069e76f7
--- /dev/null
+++ b/src/util/getopt.c
@@ -0,0 +1,1077 @@
1/* Getopt for GNU.
2 NOTE: getopt is now part of the C library, so if you don't know what
3 "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
4 before changing it!
5
6 Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
7 Free Software Foundation, Inc.
8
9NOTE: The canonical source of this file is maintained with the GNU C Library.
10Bugs can be reported to bug-glibc@prep.ai.mit.edu.
11
12This program is free software; you can redistribute it and/or modify it
13under the terms of the GNU General Public License as published by the
14Free Software Foundation; either version 2, or (at your option) any
15later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25USA.
26
27
28This code was heavily modified for GNUnet.
29Copyright (C) 2006 Christian Grothoff
30*/
31
32/**
33 * @file util/getopt.c
34 * @brief GNU style option parsing
35 *
36 * TODO: get rid of statics (make reentrant) and
37 * replace main GNU getopt parser with one that
38 * actually fits our API.
39 */
40
41#include "platform.h"
42#include "gnunet_common.h"
43#include "gnunet_getopt_lib.h"
44
45#ifdef VMS
46# include <unixlib.h>
47# if HAVE_STRING_H - 0
48# include <string.h>
49# endif
50#endif
51
52#if defined (WIN32) && !defined (__CYGWIN32__)
53/* It's not Unix, really. See? Capital letters. */
54# include <windows.h>
55# define getpid() GetCurrentProcessId()
56#endif
57
58#ifndef _
59/* This is for other GNU distributions with internationalized messages.
60 When compiling libc, the _ macro is predefined. */
61# ifdef HAVE_LIBINTL_H
62# include <libintl.h>
63# define _(msgid) gettext (msgid)
64# else
65# define _(msgid) (msgid)
66# endif
67#endif
68
69/* Describe the long-named options requested by the application.
70 The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
71 of `struct GNoption' terminated by an element containing a name which is
72 zero.
73
74 The field `has_arg' is:
75 no_argument (or 0) if the option does not take an argument,
76 required_argument (or 1) if the option requires an argument,
77 optional_argument (or 2) if the option takes an optional argument.
78
79 If the field `flag' is not NULL, it points to a variable that is set
80 to the value given in the field `val' when the option is found, but
81 left unchanged if the option is not found.
82
83 To have a long-named option do something other than set an `int' to
84 a compiled-in constant, such as set a value from `GNoptarg', set the
85 option's `flag' field to zero and its `val' field to a nonzero
86 value (the equivalent single-letter option character, if there is
87 one). For long options that have a zero `flag' field, `getopt'
88 returns the contents of the `val' field. */
89
90struct GNoption
91{
92 const char *name;
93 /* has_arg can't be an enum because some compilers complain about
94 type mismatches in all the code that assumes it is an int. */
95 int has_arg;
96 int *flag;
97 int val;
98};
99
100
101/* This version of `getopt' appears to the caller like standard Unix `getopt'
102 but it behaves differently for the user, since it allows the user
103 to intersperse the options with the other arguments.
104
105 As `getopt' works, it permutes the elements of ARGV so that,
106 when it is done, all the options precede everything else. Thus
107 all application programs are extended to handle flexible argument order.
108
109 Setting the environment variable POSIXLY_CORRECT disables permutation.
110 Then the behavior is completely standard.
111
112 GNU application programs can use a third alternative mode in which
113 they can distinguish the relative order of options and other arguments. */
114
115/* For communication from `getopt' to the caller.
116 When `getopt' finds an option that takes an argument,
117 the argument value is returned here.
118 Also, when `ordering' is RETURN_IN_ORDER,
119 each non-option ARGV-element is returned here. */
120
121static char *GNoptarg = NULL;
122
123/* Index in ARGV of the next element to be scanned.
124 This is used for communication to and from the caller
125 and for communication between successive calls to `getopt'.
126
127 On entry to `getopt', zero means this is the first call; initialize.
128
129 When `getopt' returns -1, this is the index of the first of the
130 non-option elements that the caller should itself scan.
131
132 Otherwise, `GNoptind' communicates from one call to the next
133 how much of ARGV has been scanned so far. */
134
135/* 1003.2 says this must be 1 before any call. */
136static int GNoptind = 1;
137
138/* Formerly, initialization of getopt depended on GNoptind==0, which
139 causes problems with re-calling getopt as programs generally don't
140 know that. */
141
142static int __getopt_initialized = 0;
143
144/* The next char to be scanned in the option-element
145 in which the last option character we returned was found.
146 This allows us to pick up the scan where we left off.
147
148 If this is zero, or a null string, it means resume the scan
149 by advancing to the next ARGV-element. */
150
151static char *nextchar;
152
153/* Callers store zero here to inhibit the error message
154 for unrecognized options. */
155
156static int GNopterr = 1;
157
158/* Set to an option character which was unrecognized.
159 This must be initialized on some systems to avoid linking in the
160 system's own getopt implementation. */
161
162static int GNoptopt = '?';
163
164/* Describe how to deal with options that follow non-option ARGV-elements.
165
166 If the caller did not specify anything,
167 the default is REQUIRE_ORDER if the environment variable
168 POSIXLY_CORRECT is defined, PERMUTE otherwise.
169
170 REQUIRE_ORDER means don't recognize them as options;
171 stop option processing when the first non-option is seen.
172 This is what Unix does.
173 This mode of operation is selected by either setting the environment
174 variable POSIXLY_CORRECT, or using `+' as the first character
175 of the list of option characters.
176
177 PERMUTE is the default. We GNUNET_CRYPTO_random_permute the contents of ARGV as we scan,
178 so that eventually all the non-options are at the end. This allows options
179 to be given in any order, even with programs that were not written to
180 expect this.
181
182 RETURN_IN_ORDER is an option available to programs that were written
183 to expect GNoptions and other ARGV-elements in any order and that care about
184 the ordering of the two. We describe each non-option ARGV-element
185 as if it were the argument of an option with character code 1.
186 Using `-' as the first character of the list of option characters
187 selects this mode of operation.
188
189 The special argument `--' forces an end of option-scanning regardless
190 of the value of `ordering'. In the case of RETURN_IN_ORDER, only
191 `--' can cause `getopt' to return -1 with `GNoptind' != ARGC. */
192
193static enum
194{
195 REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
196} ordering;
197
198/* Value of POSIXLY_CORRECT environment variable. */
199static char *posixly_correct;
200
201#ifdef __GNU_LIBRARY__
202/* We want to avoid inclusion of string.h with non-GNU libraries
203 because there are many ways it can cause trouble.
204 On some systems, it contains special magic macros that don't work
205 in GCC. */
206#include <string.h>
207#define my_index strchr
208#else
209
210/* Avoid depending on library functions or files
211 whose names are inconsistent. */
212
213char *getenv ();
214
215static char *
216my_index (str, chr)
217 const char *str;
218 int chr;
219{
220 while (*str)
221 {
222 if (*str == chr)
223 return (char *) str;
224 str++;
225 }
226 return 0;
227}
228
229/* If using GCC, we can safely declare strlen this way.
230 If not using GCC, it is ok not to declare it. */
231#ifdef __GNUC__
232/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
233 That was relevant to code that was here before. */
234#if !defined (__STDC__) || !__STDC__
235/* gcc with -traditional declares the built-in strlen to return int,
236 and has done so at least since version 2.4.5. -- rms. */
237extern int strlen (const char *);
238#endif /* not __STDC__ */
239#endif /* __GNUC__ */
240
241#endif /* not __GNU_LIBRARY__ */
242
243/* Handle permutation of arguments. */
244
245/* Describe the part of ARGV that contains non-options that have
246 been skipped. `first_nonopt' is the index in ARGV of the first of them;
247 `last_nonopt' is the index after the last of them. */
248
249static int first_nonopt;
250static int last_nonopt;
251
252#ifdef _LIBC
253/* Bash 2.0 gives us an environment variable containing flags
254 indicating ARGV elements that should not be considered arguments. */
255
256/* Defined in getopt_init.c */
257extern char *__getopt_nonoption_flags;
258
259static int nonoption_flags_max_len;
260static int nonoption_flags_len;
261
262static int original_argc;
263static char *const *original_argv;
264
265extern pid_t __libc_pid;
266
267/* Make sure the environment variable bash 2.0 puts in the environment
268 is valid for the getopt call we must make sure that the ARGV passed
269 to getopt is that one passed to the process. */
270static void
271 __attribute__ ((unused)) store_args_and_env (int argc, char *const *argv)
272{
273 /* XXX This is no good solution. We should rather copy the args so
274 that we can compare them later. But we must not use malloc(3). */
275 original_argc = argc;
276 original_argv = argv;
277}
278
279text_set_element (__libc_subinit, store_args_and_env);
280
281# define SWAP_FLAGS(ch1, ch2) \
282 if (nonoption_flags_len > 0) \
283 { \
284 char __tmp = __getopt_nonoption_flags[ch1]; \
285 __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
286 __getopt_nonoption_flags[ch2] = __tmp; \
287 }
288#else /* !_LIBC */
289# define SWAP_FLAGS(ch1, ch2)
290#endif /* _LIBC */
291
292/* Exchange two adjacent subsequences of ARGV.
293 One subsequence is elements [first_nonopt,last_nonopt)
294 which contains all the non-options that have been skipped so far.
295 The other is elements [last_nonopt,GNoptind), which contains all
296 the options processed since those non-options were skipped.
297
298 `first_nonopt' and `last_nonopt' are relocated so that they describe
299 the new indices of the non-options in ARGV after they are moved. */
300
301#if defined (__STDC__) && __STDC__
302static void exchange (char **);
303#endif
304
305static void
306exchange (argv)
307 char **argv;
308{
309 int bottom = first_nonopt;
310 int middle = last_nonopt;
311 int top = GNoptind;
312 char *tem;
313
314 /* Exchange the shorter segment with the far end of the longer segment.
315 That puts the shorter segment into the right place.
316 It leaves the longer segment in the right place overall,
317 but it consists of two parts that need to be swapped next. */
318
319#ifdef _LIBC
320 /* First make sure the handling of the `__getopt_nonoption_flags'
321 string can work normally. Our top argument must be in the range
322 of the string. */
323 if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
324 {
325 /* We must extend the array. The user plays games with us and
326 presents new arguments. */
327 char *new_str = malloc (top + 1);
328 if (new_str == NULL)
329 nonoption_flags_len = nonoption_flags_max_len = 0;
330 else
331 {
332 memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
333 memset (&new_str[nonoption_flags_max_len], '\0',
334 top + 1 - nonoption_flags_max_len);
335 nonoption_flags_max_len = top + 1;
336 __getopt_nonoption_flags = new_str;
337 }
338 }
339#endif
340
341 while (top > middle && middle > bottom)
342 {
343 if (top - middle > middle - bottom)
344 {
345 /* Bottom segment is the short one. */
346 int len = middle - bottom;
347 register int i;
348
349 /* Swap it with the top part of the top segment. */
350 for (i = 0; i < len; i++)
351 {
352 tem = argv[bottom + i];
353 argv[bottom + i] = argv[top - (middle - bottom) + i];
354 argv[top - (middle - bottom) + i] = tem;
355 SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
356 }
357 /* Exclude the moved bottom segment from further swapping. */
358 top -= len;
359 }
360 else
361 {
362 /* Top segment is the short one. */
363 int len = top - middle;
364 register int i;
365
366 /* Swap it with the bottom part of the bottom segment. */
367 for (i = 0; i < len; i++)
368 {
369 tem = argv[bottom + i];
370 argv[bottom + i] = argv[middle + i];
371 argv[middle + i] = tem;
372 SWAP_FLAGS (bottom + i, middle + i);
373 }
374 /* Exclude the moved top segment from further swapping. */
375 bottom += len;
376 }
377 }
378
379 /* Update records for the slots the non-options now occupy. */
380
381 first_nonopt += (GNoptind - last_nonopt);
382 last_nonopt = GNoptind;
383}
384
385/* Initialize the internal data when the first call is made. */
386
387#if defined (__STDC__) && __STDC__
388static const char *_getopt_initialize (int, char *const *, const char *);
389#endif
390static const char *
391_getopt_initialize (argc, argv, optstring)
392 int argc;
393 char *const *argv;
394 const char *optstring;
395{
396 /* Start processing options with ARGV-element 1 (since ARGV-element 0
397 is the program name); the sequence of previously skipped
398 non-option ARGV-elements is empty. */
399
400 first_nonopt = last_nonopt = GNoptind;
401
402 nextchar = NULL;
403
404 posixly_correct = getenv ("POSIXLY_CORRECT");
405
406 /* Determine how to handle the ordering of options and nonoptions. */
407
408 if (optstring[0] == '-')
409 {
410 ordering = RETURN_IN_ORDER;
411 ++optstring;
412 }
413 else if (optstring[0] == '+')
414 {
415 ordering = REQUIRE_ORDER;
416 ++optstring;
417 }
418 else if (posixly_correct != NULL)
419 ordering = REQUIRE_ORDER;
420 else
421 ordering = PERMUTE;
422
423#ifdef _LIBC
424 if (posixly_correct == NULL
425 && argc == original_argc && argv == original_argv)
426 {
427 if (nonoption_flags_max_len == 0)
428 {
429 if (__getopt_nonoption_flags == NULL
430 || __getopt_nonoption_flags[0] == '\0')
431 nonoption_flags_max_len = -1;
432 else
433 {
434 const char *orig_str = __getopt_nonoption_flags;
435 int len = nonoption_flags_max_len = strlen (orig_str);
436 if (nonoption_flags_max_len < argc)
437 nonoption_flags_max_len = argc;
438 __getopt_nonoption_flags =
439 (char *) malloc (nonoption_flags_max_len);
440 if (__getopt_nonoption_flags == NULL)
441 nonoption_flags_max_len = -1;
442 else
443 {
444 memcpy (__getopt_nonoption_flags, orig_str, len);
445 memset (&__getopt_nonoption_flags[len], '\0',
446 nonoption_flags_max_len - len);
447 }
448 }
449 }
450 nonoption_flags_len = nonoption_flags_max_len;
451 }
452 else
453 nonoption_flags_len = 0;
454#endif
455
456 return optstring;
457}
458
459/* Scan elements of ARGV (whose length is ARGC) for option characters
460 given in OPTSTRING.
461
462 If an element of ARGV starts with '-', and is not exactly "-" or "--",
463 then it is an option element. The characters of this element
464 (aside from the initial '-') are option characters. If `getopt'
465 is called repeatedly, it returns successively each of the option characters
466 from each of the option elements.
467
468 If `getopt' finds another option character, it returns that character,
469 updating `GNoptind' and `nextchar' so that the next call to `getopt' can
470 resume the scan with the following option character or ARGV-element.
471
472 If there are no more option characters, `getopt' returns -1.
473 Then `GNoptind' is the index in ARGV of the first ARGV-element
474 that is not an option. (The ARGV-elements have been permuted
475 so that those that are not options now come last.)
476
477 OPTSTRING is a string containing the legitimate option characters.
478 If an option character is seen that is not listed in OPTSTRING,
479 return '?' after printing an error message. If you set `GNopterr' to
480 zero, the error message is suppressed but we still return '?'.
481
482 If a char in OPTSTRING is followed by a colon, that means it wants an arg,
483 so the following text in the same ARGV-element, or the text of the following
484 ARGV-element, is returned in `GNoptarg'. Two colons mean an option that
485 wants an optional arg; if there is text in the current ARGV-element,
486 it is returned in `GNoptarg', otherwise `GNoptarg' is set to zero.
487
488 If OPTSTRING starts with `-' or `+', it requests different methods of
489 handling the non-option ARGV-elements.
490 See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
491
492 Long-named options begin with `--' instead of `-'.
493 Their names may be abbreviated as long as the abbreviation is unique
494 or is an exact match for some defined option. If they have an
495 argument, it follows the option name in the same ARGV-element, separated
496 from the option name by a `=', or else the in next ARGV-element.
497 When `getopt' finds a long-named option, it returns 0 if that option's
498 `flag' field is nonzero, the value of the option's `val' field
499 if the `flag' field is zero.
500
501 The elements of ARGV aren't really const, because we GNUNET_CRYPTO_random_permute them.
502 But we pretend they're const in the prototype to be compatible
503 with other systems.
504
505 LONGOPTS is a vector of `struct GNoption' terminated by an
506 element containing a name which is zero.
507
508 LONGIND returns the index in LONGOPT of the long-named option found.
509 It is only valid when a long-named option has been found by the most
510 recent call.
511
512 If LONG_ONLY is nonzero, '-' as well as '--' can introduce
513 long-named options. */
514
515static int
516GN_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
517 int argc;
518 char *const *argv;
519 const char *optstring;
520 const struct GNoption *longopts;
521 int *longind;
522 int long_only;
523{
524 GNoptarg = NULL;
525
526 if (GNoptind == 0 || !__getopt_initialized)
527 {
528 if (GNoptind == 0)
529 GNoptind = 1; /* Don't scan ARGV[0], the program name. */
530 optstring = _getopt_initialize (argc, argv, optstring);
531 __getopt_initialized = 1;
532 }
533
534 /* Test whether ARGV[GNoptind] points to a non-option argument.
535 Either it does not have option syntax, or there is an environment flag
536 from the shell indicating it is not an option. The later information
537 is only used when the used in the GNU libc. */
538#ifdef _LIBC
539#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0' \
540 || (GNoptind < nonoption_flags_len \
541 && __getopt_nonoption_flags[GNoptind] == '1'))
542#else
543#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0')
544#endif
545
546 if (nextchar == NULL || *nextchar == '\0')
547 {
548 /* Advance to the next ARGV-element. */
549
550 /* Give FIRST_NONOPT & LAST_NONOPT rational values if GNoptind has been
551 moved back by the user (who may also have changed the arguments). */
552 if (last_nonopt > GNoptind)
553 last_nonopt = GNoptind;
554 if (first_nonopt > GNoptind)
555 first_nonopt = GNoptind;
556
557 if (ordering == PERMUTE)
558 {
559 /* If we have just processed some options following some non-options,
560 exchange them so that the options come first. */
561
562 if (first_nonopt != last_nonopt && last_nonopt != GNoptind)
563 exchange ((char **) argv);
564 else if (last_nonopt != GNoptind)
565 first_nonopt = GNoptind;
566
567 /* Skip any additional non-options
568 and extend the range of non-options previously skipped. */
569
570 while (GNoptind < argc && NONOPTION_P)
571 GNoptind++;
572 last_nonopt = GNoptind;
573 }
574
575 /* The special ARGV-element `--' means premature end of options.
576 Skip it like a null option,
577 then exchange with previous non-options as if it were an option,
578 then skip everything else like a non-option. */
579 if (GNoptind != argc && !strcmp (argv[GNoptind], "--"))
580 {
581 GNoptind++;
582
583 if (first_nonopt != last_nonopt && last_nonopt != GNoptind)
584 exchange ((char **) argv);
585 else if (first_nonopt == last_nonopt)
586 first_nonopt = GNoptind;
587 last_nonopt = argc;
588
589 GNoptind = argc;
590 }
591
592 /* If we have done all the ARGV-elements, stop the scan
593 and back over any non-options that we skipped and permuted. */
594
595 if (GNoptind == argc)
596 {
597 /* Set the next-arg-index to point at the non-options
598 that we previously skipped, so the caller will digest them. */
599 if (first_nonopt != last_nonopt)
600 GNoptind = first_nonopt;
601 return -1;
602 }
603
604 /* If we have come to a non-option and did not permute it,
605 either stop the scan or describe it to the caller and pass it by. */
606
607 if (NONOPTION_P)
608 {
609 if (ordering == REQUIRE_ORDER)
610 return -1;
611 GNoptarg = argv[GNoptind++];
612 return 1;
613 }
614
615 /* We have found another option-ARGV-element.
616 Skip the initial punctuation. */
617
618 nextchar = (argv[GNoptind] + 1
619 + (longopts != NULL && argv[GNoptind][1] == '-'));
620 }
621
622 /* Decode the current option-ARGV-element. */
623
624 /* Check whether the ARGV-element is a long option.
625
626 If long_only and the ARGV-element has the form "-f", where f is
627 a valid short option, don't consider it an abbreviated form of
628 a long option that starts with f. Otherwise there would be no
629 way to give the -f short option.
630
631 On the other hand, if there's a long option "fubar" and
632 the ARGV-element is "-fu", do consider that an abbreviation of
633 the long option, just like "--fu", and not "-f" with arg "u".
634
635 This distinction seems to be the most useful approach. */
636
637 if (longopts != NULL
638 && (argv[GNoptind][1] == '-'
639 || (long_only
640 && (argv[GNoptind][2]
641 || !my_index (optstring, argv[GNoptind][1])))))
642 {
643 char *nameend;
644 const struct GNoption *p;
645 const struct GNoption *pfound = NULL;
646 int exact = 0;
647 int ambig = 0;
648 int indfound = -1;
649 int option_index;
650
651 for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
652 /* Do nothing. */ ;
653
654 /* Test all long options for either exact match
655 or abbreviated matches. */
656 for (p = longopts, option_index = 0; p->name; p++, option_index++)
657 if (!strncmp (p->name, nextchar, nameend - nextchar))
658 {
659 if ((unsigned int) (nameend - nextchar)
660 == (unsigned int) strlen (p->name))
661 {
662 /* Exact match found. */
663 pfound = p;
664 indfound = option_index;
665 exact = 1;
666 break;
667 }
668 else if (pfound == NULL)
669 {
670 /* First nonexact match found. */
671 pfound = p;
672 indfound = option_index;
673 }
674 else
675 /* Second or later nonexact match found. */
676 ambig = 1;
677 }
678
679 if (ambig && !exact)
680 {
681 if (GNopterr)
682 fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
683 argv[0], argv[GNoptind]);
684 nextchar += strlen (nextchar);
685 GNoptind++;
686 GNoptopt = 0;
687 return '?';
688 }
689
690 if (pfound != NULL)
691 {
692 option_index = indfound;
693 GNoptind++;
694 if (*nameend)
695 {
696 /* Don't test has_arg with >, because some C compilers don't
697 allow it to be used on enums. */
698 if (pfound->has_arg)
699 GNoptarg = nameend + 1;
700 else
701 {
702 if (GNopterr)
703 {
704 if (argv[GNoptind - 1][1] == '-')
705 /* --option */
706 fprintf (stderr,
707 _
708 ("%s: option `--%s' does not allow an argument\n"),
709 argv[0], pfound->name);
710 else
711 /* +option or -option */
712 fprintf (stderr,
713 _
714 ("%s: option `%c%s' does not allow an argument\n"),
715 argv[0], argv[GNoptind - 1][0],
716 pfound->name);
717 }
718 nextchar += strlen (nextchar);
719
720 GNoptopt = pfound->val;
721 return '?';
722 }
723 }
724 else if (pfound->has_arg == 1)
725 {
726 if (GNoptind < argc)
727 {
728 GNoptarg = argv[GNoptind++];
729 }
730 else
731 {
732 if (GNopterr)
733 {
734 fprintf (stderr,
735 _("%s: option `%s' requires an argument\n"),
736 argv[0], argv[GNoptind - 1]);
737 }
738 nextchar += strlen (nextchar);
739 GNoptopt = pfound->val;
740 return (optstring[0] == ':') ? ':' : '?';
741 }
742 }
743 nextchar += strlen (nextchar);
744 if (longind != NULL)
745 *longind = option_index;
746 if (pfound->flag)
747 {
748 *(pfound->flag) = pfound->val;
749 return 0;
750 }
751 return pfound->val;
752 }
753
754 /* Can't find it as a long option. If this is not getopt_long_only,
755 or the option starts with '--' or is not a valid short
756 option, then it's an error.
757 Otherwise interpret it as a short option. */
758 if (!long_only || argv[GNoptind][1] == '-'
759 || my_index (optstring, *nextchar) == NULL)
760 {
761 if (GNopterr)
762 {
763 if (argv[GNoptind][1] == '-')
764 /* --option */
765 fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
766 argv[0], nextchar);
767 else
768 /* +option or -option */
769 fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
770 argv[0], argv[GNoptind][0], nextchar);
771 }
772 nextchar = (char *) "";
773 GNoptind++;
774 GNoptopt = 0;
775 return '?';
776 }
777 }
778
779 /* Look at and handle the next short option-character. */
780
781 {
782 char c = *nextchar++;
783 char *temp = my_index (optstring, c);
784
785 /* Increment `GNoptind' when we start to process its last character. */
786 if (*nextchar == '\0')
787 ++GNoptind;
788
789 if (temp == NULL || c == ':')
790 {
791 if (GNopterr)
792 {
793 if (posixly_correct)
794 /* 1003.2 specifies the format of this message. */
795 fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
796 else
797 fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
798 }
799 GNoptopt = c;
800 return '?';
801 }
802 /* Convenience. Treat POSIX -W foo same as long option --foo */
803 if (temp[0] == 'W' && temp[1] == ';')
804 {
805 char *nameend;
806 const struct GNoption *p;
807 const struct GNoption *pfound = NULL;
808 int exact = 0;
809 int ambig = 0;
810 int indfound = 0;
811 int option_index;
812
813 /* This is an option that requires an argument. */
814 if (*nextchar != '\0')
815 {
816 GNoptarg = nextchar;
817 /* If we end this ARGV-element by taking the rest as an arg,
818 we must advance to the next element now. */
819 GNoptind++;
820 }
821 else if (GNoptind == argc)
822 {
823 if (GNopterr)
824 {
825 /* 1003.2 specifies the format of this message. */
826 fprintf (stderr, _("%s: option requires an argument -- %c\n"),
827 argv[0], c);
828 }
829 GNoptopt = c;
830 if (optstring[0] == ':')
831 c = ':';
832 else
833 c = '?';
834 return c;
835 }
836 else
837 /* We already incremented `GNoptind' once;
838 increment it again when taking next ARGV-elt as argument. */
839 GNoptarg = argv[GNoptind++];
840
841 /* GNoptarg is now the argument, see if it's in the
842 table of longopts. */
843
844 for (nextchar = nameend = GNoptarg; *nameend && *nameend != '=';
845 nameend++)
846 /* Do nothing. */ ;
847
848 /* Test all long options for either exact match
849 or abbreviated matches. */
850 for (p = longopts, option_index = 0; p->name; p++, option_index++)
851 if (!strncmp (p->name, nextchar, nameend - nextchar))
852 {
853 if ((unsigned int) (nameend - nextchar) == strlen (p->name))
854 {
855 /* Exact match found. */
856 pfound = p;
857 indfound = option_index;
858 exact = 1;
859 break;
860 }
861 else if (pfound == NULL)
862 {
863 /* First nonexact match found. */
864 pfound = p;
865 indfound = option_index;
866 }
867 else
868 /* Second or later nonexact match found. */
869 ambig = 1;
870 }
871 if (ambig && !exact)
872 {
873 if (GNopterr)
874 fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
875 argv[0], argv[GNoptind]);
876 nextchar += strlen (nextchar);
877 GNoptind++;
878 return '?';
879 }
880 if (pfound != NULL)
881 {
882 option_index = indfound;
883 if (*nameend)
884 {
885 /* Don't test has_arg with >, because some C compilers don't
886 allow it to be used on enums. */
887 if (pfound->has_arg)
888 GNoptarg = nameend + 1;
889 else
890 {
891 if (GNopterr)
892 fprintf (stderr, _("\
893%s: option `-W %s' does not allow an argument\n"), argv[0], pfound->name);
894
895 nextchar += strlen (nextchar);
896 return '?';
897 }
898 }
899 else if (pfound->has_arg == 1)
900 {
901 if (GNoptind < argc)
902 GNoptarg = argv[GNoptind++];
903 else
904 {
905 if (GNopterr)
906 fprintf (stderr,
907 _("%s: option `%s' requires an argument\n"),
908 argv[0], argv[GNoptind - 1]);
909 nextchar += strlen (nextchar);
910 return optstring[0] == ':' ? ':' : '?';
911 }
912 }
913 nextchar += strlen (nextchar);
914 if (longind != NULL)
915 *longind = option_index;
916 if (pfound->flag)
917 {
918 *(pfound->flag) = pfound->val;
919 return 0;
920 }
921 return pfound->val;
922 }
923 nextchar = NULL;
924 return 'W'; /* Let the application handle it. */
925 }
926 if (temp[1] == ':')
927 {
928 if (temp[2] == ':')
929 {
930 /* This is an option that accepts an argument optionally. */
931 if (*nextchar != '\0')
932 {
933 GNoptarg = nextchar;
934 GNoptind++;
935 }
936 else
937 GNoptarg = NULL;
938 nextchar = NULL;
939 }
940 else
941 {
942 /* This is an option that requires an argument. */
943 if (*nextchar != '\0')
944 {
945 GNoptarg = nextchar;
946 /* If we end this ARGV-element by taking the rest as an arg,
947 we must advance to the next element now. */
948 GNoptind++;
949 }
950 else if (GNoptind == argc)
951 {
952 if (GNopterr)
953 {
954 /* 1003.2 specifies the format of this message. */
955 fprintf (stderr,
956 _("%s: option requires an argument -- %c\n"),
957 argv[0], c);
958 }
959 GNoptopt = c;
960 if (optstring[0] == ':')
961 c = ':';
962 else
963 c = '?';
964 }
965 else
966 /* We already incremented `GNoptind' once;
967 increment it again when taking next ARGV-elt as argument. */
968 GNoptarg = argv[GNoptind++];
969 nextchar = NULL;
970 }
971 }
972 return c;
973 }
974}
975
976static int
977GNgetopt_long (int argc,
978 char *const *argv,
979 const char *options,
980 const struct GNoption *long_options, int *opt_index)
981{
982 return GN_getopt_internal (argc, argv, options, long_options, opt_index, 0);
983}
984
985/* ******************** now the GNUnet specific modifications... ********************* */
986
987/**
988 * Parse the command line.
989 *
990 * @param binaryName name of the binary / application with options
991 * @param cfg for storing/accessing configuration data
992 * @param allOptions defined options and handlers
993 * @param argc number of arguments
994 * @param argv actual arguments
995 * @return index into argv with first non-option
996 * argument, or -1 on error
997 */
998int
999GNUNET_GETOPT_run (const char *binaryOptions,
1000 struct GNUNET_CONFIGURATION_Handle *cfg,
1001 const struct GNUNET_GETOPT_CommandLineOption *allOptions,
1002 unsigned int argc, char *const *argv)
1003{
1004 struct GNoption *long_options;
1005 struct GNUNET_GETOPT_CommandLineProcessorContext clpc;
1006 int count;
1007 int i;
1008 char *shorts;
1009 int spos;
1010 int cont;
1011 int c;
1012
1013 GNUNET_assert (argc > 0);
1014 GNoptind = 0;
1015 clpc.binaryName = argv[0];
1016 clpc.binaryOptions = binaryOptions;
1017 clpc.allOptions = allOptions;
1018 clpc.argv = argv;
1019 clpc.argc = argc;
1020 clpc.cfg = cfg;
1021 count = 0;
1022 while (allOptions[count].name != NULL)
1023 count++;
1024 long_options = GNUNET_malloc (sizeof (struct GNoption) * (count + 1));
1025 shorts = GNUNET_malloc (count * 2 + 1);
1026 spos = 0;
1027 for (i = 0; i < count; i++)
1028 {
1029 long_options[i].name = allOptions[i].name;
1030 long_options[i].has_arg = allOptions[i].require_argument;
1031 long_options[i].flag = NULL;
1032 long_options[i].val = allOptions[i].shortName;
1033 shorts[spos++] = allOptions[i].shortName;
1034 if (allOptions[i].require_argument != 0)
1035 shorts[spos++] = ':';
1036 }
1037 long_options[count].name = NULL;
1038 long_options[count].has_arg = 0;
1039 long_options[count].flag = NULL;
1040 long_options[count].val = '\0';
1041 shorts[spos++] = '\0';
1042 cont = GNUNET_OK;
1043 /* main getopt loop */
1044 while (cont == GNUNET_OK)
1045 {
1046 int option_index = 0;
1047 c = GNgetopt_long (argc, argv, shorts, long_options, &option_index);
1048
1049 if (c == GNUNET_SYSERR)
1050 break; /* No more flags to process */
1051
1052 for (i = 0; i < count; i++)
1053 {
1054 clpc.currentArgument = GNoptind - 1;
1055 if ((char) c == allOptions[i].shortName)
1056 {
1057 cont = allOptions[i].processor (&clpc,
1058 allOptions[i].scls,
1059 allOptions[i].name, GNoptarg);
1060 break;
1061 }
1062 }
1063 if (i == count)
1064 {
1065 fprintf (stderr, _("Use --help to get a list of options.\n"));
1066 cont = GNUNET_SYSERR;
1067 }
1068 }
1069
1070 GNUNET_free (shorts);
1071 GNUNET_free (long_options);
1072 if (cont == GNUNET_SYSERR)
1073 return GNUNET_SYSERR;
1074 return GNoptind;
1075}
1076
1077/* end of getopt.c */
diff --git a/src/util/getopt_helpers.c b/src/util/getopt_helpers.c
new file mode 100644
index 000000000..3aea5d102
--- /dev/null
+++ b/src/util/getopt_helpers.c
@@ -0,0 +1,200 @@
1/*
2 This file is part of GNUnet
3 (C) 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/util/getopt_helpers.c
23 * @brief implements command line that sets option
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_getopt_lib.h"
30
31
32int
33GNUNET_GETOPT_print_version_ (struct GNUNET_GETOPT_CommandLineProcessorContext
34 *ctx, void *scls, const char *option,
35 const char *value)
36{
37 const char *version = scls;
38
39 printf ("%s v%s\n", ctx->binaryName, version);
40 return GNUNET_SYSERR;
41}
42
43
44
45#define BORDER 29
46
47int
48GNUNET_GETOPT_format_help_ (struct GNUNET_GETOPT_CommandLineProcessorContext
49 *ctx, void *scls, const char *option,
50 const char *value)
51{
52 const char *about = scls;
53 int slen;
54 int i;
55 int j;
56 int ml;
57 int p;
58 char *scp;
59 const char *trans;
60 const struct GNUNET_GETOPT_CommandLineOption *opt;
61
62 printf ("%s\n%s\n", ctx->binaryOptions, gettext (about));
63 printf (_
64 ("Arguments mandatory for long options are also mandatory for short options.\n"));
65 slen = 0;
66 i = 0;
67 opt = ctx->allOptions;
68 while (opt[i].description != NULL)
69 {
70 if (opt[i].shortName == '\0')
71 printf (" ");
72 else
73 printf (" -%c, ", opt[i].shortName);
74 printf ("--%s", opt[i].name);
75 slen = 8 + strlen (opt[i].name);
76 if (opt[i].argumentHelp != NULL)
77 {
78 printf ("=%s", opt[i].argumentHelp);
79 slen += 1 + strlen (opt[i].argumentHelp);
80 }
81 if (slen > BORDER)
82 {
83 printf ("\n%*s", BORDER, "");
84 slen = BORDER;
85 }
86 if (slen < BORDER)
87 {
88 printf ("%*s", BORDER - slen, "");
89 slen = BORDER;
90 }
91 trans = gettext (opt[i].description);
92 ml = strlen (trans);
93 p = 0;
94 OUTER:
95 while (ml - p > 78 - slen)
96 {
97 for (j = p + 78 - slen; j > p; j--)
98 {
99 if (isspace (trans[j]))
100 {
101 scp = GNUNET_malloc (j - p + 1);
102 memcpy (scp, &trans[p], j - p);
103 scp[j - p] = '\0';
104 printf ("%s\n%*s", scp, BORDER + 2, "");
105 GNUNET_free (scp);
106 p = j + 1;
107 slen = BORDER + 2;
108 goto OUTER;
109 }
110 }
111 /* could not find space to break line */
112 scp = GNUNET_malloc (78 - slen + 1);
113 memcpy (scp, &trans[p], 78 - slen);
114 scp[78 - slen] = '\0';
115 printf ("%s\n%*s", scp, BORDER + 2, "");
116 GNUNET_free (scp);
117 slen = BORDER + 2;
118 p = p + 78 - slen;
119 }
120 /* print rest */
121 if (p < ml)
122 printf ("%s\n", &trans[p]);
123 if (strlen (trans) == 0)
124 printf ("\n");
125 i++;
126 }
127 printf ("Report bugs to gnunet-developers@gnu.org.\n"
128 "GNUnet home page: http://www.gnu.org/software/gnunet/\n"
129 "General help using GNU software: http://www.gnu.org/gethelp/\n");
130 return GNUNET_SYSERR;
131}
132
133
134int
135GNUNET_GETOPT_increment_value (struct
136 GNUNET_GETOPT_CommandLineProcessorContext *ctx,
137 void *scls, const char *cmdLineOption,
138 const char *value)
139{
140 int *val = scls;
141 (*val)++;
142 return GNUNET_OK;
143}
144
145int
146GNUNET_GETOPT_set_one (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
147 void *scls, const char *option, const char *value)
148{
149 int *val = scls;
150 *val = 1;
151 return GNUNET_OK;
152}
153
154int
155GNUNET_GETOPT_set_string (struct GNUNET_GETOPT_CommandLineProcessorContext
156 *ctx, void *scls, const char *option,
157 const char *value)
158{
159 char **val = scls;
160
161 GNUNET_assert (value != NULL);
162 if (NULL != *val)
163 GNUNET_free (*val);
164 *val = GNUNET_strdup (value);
165 return GNUNET_OK;
166}
167
168int
169GNUNET_GETOPT_set_ulong (struct GNUNET_GETOPT_CommandLineProcessorContext
170 *ctx, void *scls, const char *option,
171 const char *value)
172{
173 unsigned long long *val = scls;
174 if (1 != SSCANF (value, "%llu", val))
175 {
176 fprintf (stderr,
177 _("You must pass a number to the `%s' option.\n"), "-X");
178 return GNUNET_SYSERR;
179 }
180 return GNUNET_OK;
181}
182
183
184int
185GNUNET_GETOPT_set_uint (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
186 void *scls, const char *option, const char *value)
187{
188 unsigned int *val = scls;
189
190 if (1 != SSCANF (value, "%u", val))
191 {
192 fprintf (stderr,
193 _("You must pass a number to the `%s' option.\n"), "-X");
194 return GNUNET_SYSERR;
195 }
196 return GNUNET_OK;
197}
198
199
200/* end of getopt_helpers.c */
diff --git a/src/util/network.c b/src/util/network.c
new file mode 100644
index 000000000..22c1ca632
--- /dev/null
+++ b/src/util/network.c
@@ -0,0 +1,1239 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/network/network.c
23 * @brief basic, low-level TCP networking interface
24 * @author Christian Grothoff
25 *
26 * This code is rather complex. Only modify it if you
27 * 1) Have a NEW testcase showing that the new code
28 * is needed and correct
29 * 2) All EXISTING testcases pass with the new code
30 * These rules should apply in general, but for this
31 * module they are VERY, VERY important.
32 *
33 * TODO:
34 * - can we merge receive_ready and receive_again?
35 * - can we integrate the nth.timeout_task with the write_task's timeout?
36 */
37
38#include "platform.h"
39#include "gnunet_common.h"
40#include "gnunet_network_lib.h"
41#include "gnunet_scheduler_lib.h"
42
43#define DEBUG_NETWORK GNUNET_NO
44
45struct GNUNET_NETWORK_TransmitHandle
46{
47
48 /**
49 * Function to call if the send buffer has notify_size
50 * bytes available.
51 */
52 GNUNET_NETWORK_TransmitReadyNotify notify_ready;
53
54 /**
55 * Closure for notify_ready.
56 */
57 void *notify_ready_cls;
58
59 /**
60 * Our socket handle.
61 */
62 struct GNUNET_NETWORK_SocketHandle *sh;
63
64 /**
65 * Timeout for receiving (in absolute time).
66 */
67 struct GNUNET_TIME_Absolute transmit_timeout;
68
69 /**
70 * Task called on timeout.
71 */
72 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
73
74 /**
75 * At what number of bytes available in the
76 * write buffer should the notify method be called?
77 */
78 size_t notify_size;
79
80};
81
82/**
83 * @brief handle for a network socket
84 */
85struct GNUNET_NETWORK_SocketHandle
86{
87
88 /**
89 * Scheduler that was used for the connect task.
90 */
91 struct GNUNET_SCHEDULER_Handle *sched;
92
93 /**
94 * Address information for connect (may be NULL).
95 */
96 struct addrinfo *ai;
97
98 /**
99 * Index for the next struct addrinfo for connect attempts (may be NULL)
100 */
101 struct addrinfo *ai_pos;
102
103 /**
104 * Network address of the other end-point, may be NULL.
105 */
106 struct sockaddr *addr;
107
108 /**
109 * Pointer to our write buffer.
110 */
111 char *write_buffer;
112
113 /**
114 * Size of our write buffer.
115 */
116 size_t write_buffer_size;
117
118 /**
119 * Current write-offset in write buffer (where
120 * would we write next).
121 */
122 size_t write_buffer_off;
123
124 /**
125 * Current read-offset in write buffer (how many
126 * bytes have already been send).
127 */
128 size_t write_buffer_pos;
129
130 /**
131 * Length of addr.
132 */
133 socklen_t addrlen;
134
135 /**
136 * Connect task that we may need to wait for.
137 */
138 GNUNET_SCHEDULER_TaskIdentifier connect_task;
139
140 /**
141 * Read task that we may need to wait for.
142 */
143 GNUNET_SCHEDULER_TaskIdentifier read_task;
144
145 /**
146 * Write task that we may need to wait for.
147 */
148 GNUNET_SCHEDULER_TaskIdentifier write_task;
149
150 /**
151 * The handle we return for GNUNET_NETWORK_notify_transmit_ready.
152 */
153 struct GNUNET_NETWORK_TransmitHandle nth;
154
155 /**
156 * Underlying OS's socket, set to -1 after fatal errors.
157 */
158 int sock;
159
160 /**
161 * Port to connect to.
162 */
163 uint16_t port;
164
165 /**
166 * Function to call on data received, NULL
167 * if no receive is pending.
168 */
169 GNUNET_NETWORK_Receiver receiver;
170
171 /**
172 * Closure for receiver.
173 */
174 void *receiver_cls;
175
176 /**
177 * Timeout for receiving (in absolute time).
178 */
179 struct GNUNET_TIME_Absolute receive_timeout;
180
181 /**
182 * Maximum number of bytes to read
183 * (for receiving).
184 */
185 size_t max;
186
187};
188
189
190/**
191 * Create a socket handle by boxing an existing OS socket. The OS
192 * socket should henceforth be no longer used directly.
193 * GNUNET_socket_destroy will close it.
194 *
195 * @param sched scheduler to use
196 * @param osSocket existing socket to box
197 * @param maxbuf maximum write buffer size for the socket (use
198 * 0 for sockets that need no write buffers, such as listen sockets)
199 * @return the boxed socket handle
200 */
201struct GNUNET_NETWORK_SocketHandle *
202GNUNET_NETWORK_socket_create_from_existing (struct GNUNET_SCHEDULER_Handle
203 *sched, int osSocket,
204 size_t maxbuf)
205{
206 struct GNUNET_NETWORK_SocketHandle *ret;
207 ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf);
208 ret->write_buffer = (char *) &ret[1];
209 ret->write_buffer_size = maxbuf;
210 ret->sock = osSocket;
211 ret->sched = sched;
212 return ret;
213}
214
215
216/**
217 * Create a socket handle by accepting on a listen socket. This
218 * function may block if the listen socket has no connection ready.
219 *
220 * @param sched scheduler to use
221 * @param access function to use to check if access is allowed
222 * @param access_cls closure for access
223 * @param lsock listen socket
224 * @param maxbuf maximum write buffer size for the socket (use
225 * 0 for sockets that need no write buffers, such as listen sockets)
226 * @return the socket handle, NULL on error
227 */
228struct GNUNET_NETWORK_SocketHandle *
229GNUNET_NETWORK_socket_create_from_accept (struct GNUNET_SCHEDULER_Handle
230 *sched,
231 GNUNET_NETWORK_AccessCheck access,
232 void *access_cls, int lsock,
233 size_t maxbuf)
234{
235 struct GNUNET_NETWORK_SocketHandle *ret;
236 char addr[32];
237 char msg[INET6_ADDRSTRLEN];
238 socklen_t addrlen;
239 int fam;
240 int fd;
241 int aret;
242 struct sockaddr_in *v4;
243 struct sockaddr_in6 *v6;
244 struct sockaddr *sa;
245 void *uaddr;
246
247 addrlen = sizeof (addr);
248 fd = accept (lsock, (struct sockaddr *) &addr, &addrlen);
249 if (fd == -1)
250 {
251 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "accept");
252 return NULL;
253 }
254 if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC))
255 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
256 "fcntl");
257 if (addrlen > sizeof (addr))
258 {
259 GNUNET_break (0);
260 GNUNET_break (0 == CLOSE (fd));
261 return NULL;
262 }
263
264 sa = (struct sockaddr *) addr;
265 v6 = (struct sockaddr_in6 *) addr;
266 if ((sa->sa_family == AF_INET6) && (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr)))
267 {
268 /* convert to V4 address */
269 v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
270 memset (v4, 0, sizeof (struct sockaddr_in));
271 v4->sin_family = AF_INET;
272 memcpy (&v4->sin_addr,
273 &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) -
274 sizeof (struct in_addr)],
275 sizeof (struct in_addr));
276 v4->sin_port = v6->sin6_port;
277 uaddr = v4;
278 addrlen = sizeof (struct sockaddr_in);
279 }
280 else
281 {
282 uaddr = GNUNET_malloc (addrlen);
283 memcpy (uaddr, addr, addrlen);
284 }
285
286 if ((access != NULL) &&
287 (GNUNET_YES != (aret = access (access_cls, uaddr, addrlen))))
288 {
289 if (aret == GNUNET_NO)
290 {
291 fam = ((struct sockaddr *) addr)->sa_family;
292 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
293 _("Access denied to `%s'\n"),
294 inet_ntop (fam,
295 (fam == AF_INET6)
296 ? (const void *) &((struct sockaddr_in6 *)
297 &addr)->
298 sin6_addr : (const void *)
299 &((struct sockaddr_in *) &addr)->sin_addr,
300 msg, sizeof (msg)));
301 }
302 GNUNET_break (0 == SHUTDOWN (fd, SHUT_RDWR));
303 GNUNET_break (0 == CLOSE (fd));
304 GNUNET_free (uaddr);
305 return NULL;
306 }
307 ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf);
308 ret->write_buffer = (char *) &ret[1];
309 ret->write_buffer_size = maxbuf;
310 ret->addr = uaddr;
311 ret->addrlen = addrlen;
312 ret->sock = fd;
313 ret->sched = sched;
314 return ret;
315}
316
317/**
318 * Obtain the network address of the other party.
319 *
320 * @param sock the client to get the address for
321 * @param addr where to store the address
322 * @param addrlen where to store the length of the address
323 * @return GNUNET_OK on success
324 */
325int
326GNUNET_NETWORK_socket_get_address (struct GNUNET_NETWORK_SocketHandle *sock,
327 void **addr, size_t * addrlen)
328{
329 if ((sock->addr == NULL) || (sock->addrlen == 0))
330 return GNUNET_NO;
331 *addr = GNUNET_malloc (sock->addrlen);
332 memcpy (*addr, sock->addr, sock->addrlen);
333 *addrlen = sock->addrlen;
334 return GNUNET_OK;
335}
336
337
338/**
339 * Set if a socket should use blocking or non-blocking IO.
340 *
341 * @return GNUNET_OK on success, GNUNET_SYSERR on error
342 */
343static int
344socket_set_blocking (int handle, int doBlock)
345{
346#if MINGW
347 u_long mode;
348 mode = !doBlock;
349#if HAVE_PLIBC_FD
350 if (ioctlsocket (plibc_fd_get_handle (handle), FIONBIO, &mode) ==
351 SOCKET_ERROR)
352 {
353 SetErrnoFromWinsockError (WSAGetLastError ());
354 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket");
355 return GNUNET_SYSERR;
356 }
357#else
358 if (ioctlsocket (handle, FIONBIO, &mode) == SOCKET_ERROR)
359 {
360 SetErrnoFromWinsockError (WSAGetLastError ());
361 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket");
362 return GNUNET_SYSERR;
363 }
364#endif
365 /* store the blocking mode */
366#if HAVE_PLIBC_FD
367 plibc_fd_set_blocking (handle, doBlock);
368#else
369 __win_SetHandleBlockingMode (handle, doBlock);
370#endif
371 return GNUNET_OK;
372
373#else
374 /* not MINGW */
375 int flags = fcntl (handle, F_GETFL);
376 if (flags == -1)
377 {
378 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");
379 return GNUNET_SYSERR;
380 }
381 if (doBlock)
382 flags &= ~O_NONBLOCK;
383 else
384 flags |= O_NONBLOCK;
385 if (0 != fcntl (handle, F_SETFL, flags))
386 {
387 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");
388 return GNUNET_SYSERR;
389 }
390 return GNUNET_OK;
391#endif
392}
393
394
395/**
396 * Initiate asynchronous TCP connect request.
397 *
398 * @param sock what socket to connect
399 * @return GNUNET_SYSERR error (no more addresses to try)
400 */
401static int
402try_connect (struct GNUNET_NETWORK_SocketHandle *sock)
403{
404 int s;
405
406 if (sock->addr != NULL)
407 {
408 GNUNET_free (sock->addr);
409 sock->addr = NULL;
410 sock->addrlen = 0;
411 }
412 while (1)
413 {
414 if (sock->ai_pos == NULL)
415 {
416 /* no more addresses to try, fatal! */
417 return GNUNET_SYSERR;
418 }
419 switch (sock->ai_pos->ai_family)
420 {
421 case AF_INET:
422 ((struct sockaddr_in *) sock->ai_pos->ai_addr)->sin_port =
423 htons (sock->port);
424 break;
425 case AF_INET6:
426 ((struct sockaddr_in6 *) sock->ai_pos->ai_addr)->sin6_port =
427 htons (sock->port);
428 break;
429 default:
430 sock->ai_pos = sock->ai_pos->ai_next;
431 continue;
432 }
433 s = SOCKET (sock->ai_pos->ai_family, SOCK_STREAM, 0);
434 if (s == -1)
435 {
436 /* maybe unsupported address family, try next */
437 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "socket");
438 sock->ai_pos = sock->ai_pos->ai_next;
439 continue;
440 }
441 if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC))
442 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
443 "fcntl");
444 if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO))
445 {
446 /* we'll treat this one as fatal */
447 GNUNET_break (0 == CLOSE (s));
448 return GNUNET_SYSERR;
449 }
450 if ((0 != CONNECT (s,
451 sock->ai_pos->ai_addr,
452 sock->ai_pos->ai_addrlen)) && (errno != EINPROGRESS))
453 {
454 /* maybe refused / unsupported address, try next */
455 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect");
456 GNUNET_break (0 == CLOSE (s));
457 continue;
458 }
459 break;
460 }
461 /* got one! copy address information! */
462 sock->addrlen = sock->ai_pos->ai_addrlen;
463 sock->addr = GNUNET_malloc (sock->addrlen);
464 memcpy (sock->addr, sock->ai_pos->ai_addr, sock->addrlen);
465 sock->ai_pos = sock->ai_pos->ai_next;
466 sock->sock = s;
467 return GNUNET_OK;
468}
469
470
471/**
472 * Scheduler let us know that we're either ready to
473 * write on the socket OR connect timed out. Do the
474 * right thing.
475 */
476static void
477connect_continuation (void *cls,
478 const struct GNUNET_SCHEDULER_TaskContext *tc)
479{
480 struct GNUNET_NETWORK_SocketHandle *sock = cls;
481 unsigned int len;
482 int error;
483
484 /* nobody needs to wait for us anymore... */
485 sock->connect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
486 /* Note: write-ready does NOT mean connect succeeded,
487 we need to use getsockopt to be sure */
488 len = sizeof (error);
489 errno = 0;
490 error = 0;
491 if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
492 (0 != getsockopt (sock->sock, SOL_SOCKET, SO_ERROR, &error, &len)) ||
493 (error != 0) || (errno != 0))
494 {
495 /* connect failed / timed out */
496 GNUNET_break (0 == CLOSE (sock->sock));
497 sock->sock = -1;
498 if (GNUNET_SYSERR == try_connect (sock))
499 {
500 /* failed for good */
501 GNUNET_break (sock->ai_pos == NULL);
502 freeaddrinfo (sock->ai);
503 sock->ai = NULL;
504 return;
505 }
506 sock->connect_task = GNUNET_SCHEDULER_add_write (tc->sched, GNUNET_NO, /* abort on shutdown */
507 GNUNET_SCHEDULER_PRIORITY_KEEP,
508 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
509 GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT,
510 sock->sock,
511 &connect_continuation,
512 sock);
513 return;
514 }
515 /* connect succeeded! clean up "ai" */
516#if DEBUG_NETWORK
517 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection succeeded!\n");
518#endif
519 freeaddrinfo (sock->ai);
520 sock->ai_pos = NULL;
521 sock->ai = NULL;
522}
523
524
525/**
526 * Create a socket handle by (asynchronously) connecting to a host.
527 * This function returns immediately, even if the connection has not
528 * yet been established. This function only creates TCP connections.
529 *
530 * @param sched scheduler to use
531 * @param hostname name of the host to connect to
532 * @param port port to connect to
533 * @param maxbuf maximum write buffer size for the socket (use
534 * 0 for sockets that need no write buffers, such as listen sockets)
535 * @return the socket handle
536 */
537struct GNUNET_NETWORK_SocketHandle *
538GNUNET_NETWORK_socket_create_from_connect (struct GNUNET_SCHEDULER_Handle
539 *sched, const char *hostname,
540 uint16_t port, size_t maxbuf)
541{
542 struct GNUNET_NETWORK_SocketHandle *ret;
543 struct addrinfo hints;
544 int ec;
545
546 ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf);
547 ret->sock = -1;
548 ret->sched = sched;
549 ret->write_buffer = (char *) &ret[1];
550 ret->write_buffer_size = maxbuf;
551 ret->port = port;
552 memset (&hints, 0, sizeof (hints));
553 hints.ai_family = AF_UNSPEC;
554 hints.ai_socktype = SOCK_STREAM;
555 if (0 != (ec = getaddrinfo (hostname, NULL, &hints, &ret->ai)))
556 {
557 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
558 "`%s' failed for hostname `%s': %s\n",
559 "getaddrinfo", hostname, gai_strerror (ec));
560 GNUNET_free (ret);
561 return NULL;
562 }
563 ret->ai_pos = ret->ai;
564 if (GNUNET_SYSERR == try_connect (ret))
565 {
566 freeaddrinfo (ret->ai);
567 GNUNET_free (ret);
568 return NULL;
569 }
570 ret->connect_task = GNUNET_SCHEDULER_add_write (sched, GNUNET_NO, /* abort on shutdown */
571 GNUNET_SCHEDULER_PRIORITY_KEEP,
572 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
573 GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT,
574 ret->sock,
575 &connect_continuation, ret);
576 return ret;
577
578}
579
580
581/**
582 * Create a socket handle by (asynchronously) connecting to a host.
583 * This function returns immediately, even if the connection has not
584 * yet been established. This function only creates TCP connections.
585 *
586 * @param sched scheduler to use
587 * @param af_family address family to use
588 * @param serv_addr server address
589 * @param addrlen length of server address
590 * @param maxbuf maximum write buffer size for the socket (use
591 * 0 for sockets that need no write buffers, such as listen sockets)
592 * @return the socket handle
593 */
594struct GNUNET_NETWORK_SocketHandle *
595GNUNET_NETWORK_socket_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle
596 *sched, int af_family,
597 const struct sockaddr *serv_addr,
598 socklen_t addrlen, size_t maxbuf)
599{
600 int s;
601
602 s = SOCKET (af_family, SOCK_STREAM, 0);
603 if (s == -1)
604 {
605 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
606 GNUNET_ERROR_TYPE_BULK, "socket");
607 return NULL;
608 }
609 if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC))
610 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
611 "fcntl");
612 if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO))
613 {
614 /* we'll treat this one as fatal */
615 GNUNET_break (0 == CLOSE (s));
616 return NULL;
617 }
618 if ((0 != CONNECT (s, serv_addr, addrlen)) && (errno != EINPROGRESS))
619 {
620 /* maybe refused / unsupported address, try next */
621 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect");
622 GNUNET_break (0 == CLOSE (s));
623 return NULL;
624 }
625 return GNUNET_NETWORK_socket_create_from_existing (sched, s, maxbuf);
626}
627
628
629/**
630 * Check if socket is valid (no fatal errors have happened so far).
631 * Note that a socket that is still trying to connect is considered
632 * valid.
633 *
634 * @param sock socket to check
635 * @return GNUNET_YES if valid, GNUNET_NO otherwise
636 */
637int
638GNUNET_NETWORK_socket_check (struct GNUNET_NETWORK_SocketHandle *sock)
639{
640 if (sock->ai != NULL)
641 return GNUNET_YES; /* still trying to connect */
642 return (sock->sock == -1) ? GNUNET_NO : GNUNET_YES;
643}
644
645
646/**
647 * Scheduler let us know that the connect task is finished (or was
648 * cancelled due to shutdown). Now really clean up.
649 */
650static void
651destroy_continuation (void *cls,
652 const struct GNUNET_SCHEDULER_TaskContext *tc)
653{
654 struct GNUNET_NETWORK_SocketHandle *sock = cls;
655 GNUNET_NETWORK_TransmitReadyNotify notify;
656
657 if (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
658 {
659#if DEBUG_NETWORK
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "Destroy code waiting for writes to complete.\n");
662#endif
663 GNUNET_SCHEDULER_add_after (sock->sched,
664 GNUNET_YES,
665 GNUNET_SCHEDULER_PRIORITY_KEEP,
666 sock->write_task,
667 &destroy_continuation, sock);
668 return;
669 }
670 if (sock->sock != -1)
671 {
672#if DEBUG_NETWORK
673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket.\n");
674#endif
675 SHUTDOWN (sock->sock, SHUT_RDWR);
676 }
677 if (sock->read_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
678 {
679#if DEBUG_NETWORK
680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681 "Destroy code waiting for receive to complete.\n");
682#endif
683 GNUNET_SCHEDULER_add_after (sock->sched,
684 GNUNET_YES,
685 GNUNET_SCHEDULER_PRIORITY_KEEP,
686 sock->read_task,
687 &destroy_continuation, sock);
688 return;
689 }
690 if (NULL != (notify = sock->nth.notify_ready))
691 {
692 sock->nth.notify_ready = NULL;
693 notify (sock->nth.notify_ready_cls, 0, NULL);
694 if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
695 {
696 GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task);
697 sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
698 }
699 }
700 if (sock->sock != -1)
701 {
702#if DEBUG_NETWORK
703 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Closing socket.\n");
704#endif
705 GNUNET_break (0 == CLOSE (sock->sock));
706 }
707 GNUNET_free_non_null (sock->addr);
708 if (sock->ai != NULL)
709 freeaddrinfo (sock->ai);
710 GNUNET_free (sock);
711}
712
713
714/**
715 * Close the socket and free associated resources. Pending
716 * transmissions are simply dropped. A pending receive call will be
717 * called with an error code of "EPIPE".
718 *
719 * @param sock socket to destroy
720 */
721void
722GNUNET_NETWORK_socket_destroy (struct GNUNET_NETWORK_SocketHandle *sock)
723{
724#if DEBUG_NETWORK
725 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
726 "Network asked to destroy socket %p\n", sock);
727#endif
728 if (sock->write_buffer_off == 0)
729 sock->ai_pos = NULL; /* if we're still trying to connect and have
730 no message pending, stop trying! */
731 GNUNET_assert (sock->sched != NULL);
732 GNUNET_SCHEDULER_add_after (sock->sched,
733 GNUNET_YES,
734 GNUNET_SCHEDULER_PRIORITY_KEEP,
735 sock->connect_task,
736 &destroy_continuation, sock);
737}
738
739/**
740 * Tell the receiver callback that a timeout was reached.
741 */
742static void
743signal_timeout (struct GNUNET_NETWORK_SocketHandle *sh)
744{
745 GNUNET_NETWORK_Receiver receiver;
746
747#if DEBUG_NETWORK
748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
749 "Network signals timeout to receiver!\n");
750#endif
751 GNUNET_assert (NULL != (receiver = sh->receiver));
752 sh->receiver = NULL;
753 receiver (sh->receiver_cls, NULL, 0, NULL, 0, 0);
754}
755
756
757/**
758 * Tell the receiver callback that we had an IO error.
759 */
760static void
761signal_error (struct GNUNET_NETWORK_SocketHandle *sh, int errcode)
762{
763 GNUNET_NETWORK_Receiver receiver;
764 GNUNET_assert (NULL != (receiver = sh->receiver));
765 sh->receiver = NULL;
766 receiver (sh->receiver_cls, NULL, 0, sh->addr, sh->addrlen, errcode);
767}
768
769
770/**
771 * This function is called once we either timeout
772 * or have data ready to read.
773 */
774static void
775receive_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
776{
777 struct GNUNET_NETWORK_SocketHandle *sh = cls;
778 struct GNUNET_TIME_Absolute now;
779 char buffer[sh->max];
780 ssize_t ret;
781 GNUNET_NETWORK_Receiver receiver;
782
783 sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
784 now = GNUNET_TIME_absolute_get ();
785 if ((now.value > sh->receive_timeout.value) ||
786 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) ||
787 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
788 {
789#if DEBUG_NETWORK
790 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
791 "Receive encounters error: timeout...\n");
792#endif
793 signal_timeout (sh);
794 return;
795 }
796 if (sh->sock == -1)
797 {
798 /* connect failed for good */
799#if DEBUG_NETWORK
800 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801 "Receive encounters error, socket closed...\n");
802#endif
803 signal_error (sh, ECONNREFUSED);
804 return;
805 }
806 GNUNET_assert (FD_ISSET (sh->sock, tc->read_ready));
807RETRY:
808 ret = RECV (sh->sock, buffer, sh->max, MSG_DONTWAIT);
809 if (ret == -1)
810 {
811 if (errno == EINTR)
812 goto RETRY;
813#if DEBUG_NETWORK
814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815 "Error receiving: %s\n", STRERROR (errno));
816#endif
817 signal_error (sh, errno);
818 return;
819 }
820#if DEBUG_NETWORK
821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
822 "Receive got %d bytes from OS!\n", ret);
823#endif
824 GNUNET_assert (NULL != (receiver = sh->receiver));
825 sh->receiver = NULL;
826 receiver (sh->receiver_cls, buffer, ret, sh->addr, sh->addrlen, 0);
827}
828
829
830/**
831 * This function is called after establishing a connection either has
832 * succeeded or timed out. Note that it is possible that the attempt
833 * timed out and that we're immediately retrying. If we are retrying,
834 * we need to wait again (or timeout); if we succeeded, we need to
835 * wait for data (or timeout).
836 */
837static void
838receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
839{
840 struct GNUNET_NETWORK_SocketHandle *sh = cls;
841 struct GNUNET_TIME_Absolute now;
842
843 sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
844 if ((sh->sock == -1) &&
845 (sh->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK))
846 {
847 /* not connected and no longer trying */
848#if DEBUG_NETWORK
849 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
850 "Receive encounters error, socket closed...\n");
851#endif
852 signal_error (sh, ECONNREFUSED);
853 return;
854 }
855 now = GNUNET_TIME_absolute_get ();
856 if ((now.value > sh->receive_timeout.value) ||
857 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
858 {
859#if DEBUG_NETWORK
860 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
861 "Receive encounters error: timeout...\n");
862#endif
863 signal_timeout (sh);
864 return;
865 }
866 if (sh->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
867 {
868 /* connect was retried */
869#if DEBUG_NETWORK
870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
871 "Receive still waits on connect...\n");
872#endif
873 sh->read_task = GNUNET_SCHEDULER_add_after (tc->sched,
874 GNUNET_YES,
875 GNUNET_SCHEDULER_PRIORITY_KEEP,
876 sh->connect_task,
877 &receive_again, sh);
878 }
879 else
880 {
881 /* connect succeeded, wait for data! */
882#if DEBUG_NETWORK
883 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884 "Receive now waits for socket...\n");
885#endif
886 sh->read_task = GNUNET_SCHEDULER_add_read (tc->sched,
887 GNUNET_YES,
888 GNUNET_SCHEDULER_PRIORITY_KEEP,
889 sh->connect_task,
890 GNUNET_TIME_absolute_get_remaining
891 (sh->receive_timeout),
892 sh->sock, &receive_ready,
893 sh);
894 }
895}
896
897
898/**
899 * Receive data from the given socket. Note that this function will
900 * call "receiver" asynchronously using the scheduler. It will
901 * "immediately" return. Note that there MUST only be one active
902 * receive call per socket at any given point in time (so do not
903 * call receive again until the receiver callback has been invoked).
904 *
905 * @param sched scheduler to use
906 * @param sock socket handle
907 * @param max maximum number of bytes to read
908 * @param timeout maximum amount of time to wait (use -1 for "forever")
909 * @param receiver function to call with received data
910 * @param receiver_cls closure for receiver
911 * @return scheduler task ID used for receiving, GNUNET_SCHEDULER_NO_PREREQUISITE_TASK on error
912 */
913GNUNET_SCHEDULER_TaskIdentifier
914GNUNET_NETWORK_receive (struct GNUNET_NETWORK_SocketHandle *sock,
915 size_t max,
916 struct GNUNET_TIME_Relative timeout,
917 GNUNET_NETWORK_Receiver receiver, void *receiver_cls)
918{
919 struct GNUNET_SCHEDULER_TaskContext tc;
920#if DEBUG_NETWORK
921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
922 "Network asked to receive from socket...\n");
923#endif
924 GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) &&
925 (sock->receiver == NULL));
926 sock->receiver = receiver;
927 sock->receiver_cls = receiver_cls;
928 sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
929 sock->max = max;
930 memset (&tc, 0, sizeof (tc));
931 tc.sched = sock->sched;
932 tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE;
933 receive_again (sock, &tc);
934 return sock->read_task;
935}
936
937
938/**
939 * Cancel receive job on the given socket. Note that the
940 * receiver callback must not have been called yet in order
941 * for the cancellation to be valid.
942 *
943 * @param sock socket handle
944 * @param task task identifier returned from the receive call
945 * @return closure of the original receiver callback
946 */
947void *
948GNUNET_NETWORK_receive_cancel (struct GNUNET_NETWORK_SocketHandle *sock,
949 GNUNET_SCHEDULER_TaskIdentifier task)
950{
951 GNUNET_assert (sock->read_task == task);
952 GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->sched, task));
953 sock->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
954 sock->receiver = NULL;
955 return sock->receiver_cls;
956}
957
958
959/**
960 * Try to call the transmit notify method (check if we do
961 * have enough space available first)!
962 *
963 * @param sock socket for which we should do this processing
964 * @return GNUNET_YES if we were able to call notify
965 */
966static int
967process_notify (struct GNUNET_NETWORK_SocketHandle *sock)
968{
969 size_t used;
970 size_t avail;
971 size_t size;
972 GNUNET_NETWORK_TransmitReadyNotify notify;
973
974 GNUNET_assert (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
975 if (NULL == (notify = sock->nth.notify_ready))
976 return GNUNET_NO;
977 used = sock->write_buffer_off - sock->write_buffer_pos;
978 avail = sock->write_buffer_size - used;
979 size = sock->nth.notify_size;
980 if (sock->nth.notify_size > avail)
981 return GNUNET_NO;
982 sock->nth.notify_ready = NULL;
983 if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
984 {
985 GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task);
986 sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
987 }
988 if (sock->write_buffer_size - sock->write_buffer_off < size)
989 {
990 /* need to compact */
991 memmove (sock->write_buffer,
992 &sock->write_buffer[sock->write_buffer_pos], used);
993 sock->write_buffer_off -= sock->write_buffer_pos;
994 sock->write_buffer_pos = 0;
995 }
996 GNUNET_assert (sock->write_buffer_size - sock->write_buffer_off >= size);
997 size = notify (sock->nth.notify_ready_cls,
998 sock->write_buffer_size - sock->write_buffer_off,
999 &sock->write_buffer[sock->write_buffer_off]);
1000 sock->write_buffer_off += size;
1001 return GNUNET_YES;
1002}
1003
1004
1005/**
1006 * Task invoked by the scheduler when a call to transmit
1007 * is timing out (we never got enough buffer space to call
1008 * the callback function before the specified timeout
1009 * expired).
1010 *
1011 * This task notifies the client about the timeout.
1012 */
1013static void
1014transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1015{
1016 struct GNUNET_NETWORK_SocketHandle *sock = cls;
1017 GNUNET_NETWORK_TransmitReadyNotify notify;
1018
1019#if DEBUG_NETWORK
1020 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmit fails, time out reached.\n");
1021#endif
1022 notify = sock->nth.notify_ready;
1023 sock->nth.notify_ready = NULL;
1024 notify (sock->nth.notify_ready_cls, 0, NULL);
1025}
1026
1027
1028static void
1029transmit_error (struct GNUNET_NETWORK_SocketHandle *sock)
1030{
1031 if (sock->nth.notify_ready == NULL)
1032 return; /* nobody to tell about it */
1033 if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1034 {
1035 GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task);
1036 sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1037 }
1038 transmit_timeout (sock, NULL);
1039}
1040
1041
1042/**
1043 * See if we are now connected. If not, wait longer for
1044 * connect to succeed. If connected, we should be able
1045 * to write now as well, unless we timed out.
1046 */
1047static void
1048transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1049{
1050 struct GNUNET_NETWORK_SocketHandle *sock = cls;
1051 ssize_t ret;
1052 size_t have;
1053
1054#if DEBUG_NETWORK
1055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1056 "Transmit ready called --- will try to send\n");
1057#endif
1058 GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1059 sock->write_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1060 if (sock->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1061 {
1062 /* still waiting for connect */
1063#if DEBUG_NETWORK
1064 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1065 "Transmission still waiting for connect...\n");
1066#endif
1067 GNUNET_assert (sock->write_task ==
1068 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1069 sock->write_task =
1070 GNUNET_SCHEDULER_add_delayed (tc->sched, GNUNET_NO,
1071 GNUNET_SCHEDULER_PRIORITY_KEEP,
1072 sock->connect_task,
1073 GNUNET_TIME_UNIT_ZERO, &transmit_ready,
1074 sock);
1075 return;
1076 }
1077 if (sock->sock == -1)
1078 {
1079 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1080 _
1081 ("Did not transmit request, socket closed or connect failed.\n"));
1082 transmit_error (sock);
1083 return; /* connect failed for good, we're finished */
1084 }
1085 if ((tc->write_ready == NULL) || (!FD_ISSET (sock->sock, tc->write_ready)))
1086 {
1087#if DEBUG_NETWORK
1088 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1089 "Socket not yet ready for writing, will wait for that.\n");
1090#endif
1091 goto SCHEDULE_WRITE;
1092 }
1093 GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos);
1094 process_notify (sock);
1095 have = sock->write_buffer_off - sock->write_buffer_pos;
1096 if (have == 0)
1097 {
1098#if DEBUG_NETWORK
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No data ready for writing.\n");
1100#endif
1101 return;
1102 }
1103RETRY:
1104 ret = SEND (sock->sock,
1105 &sock->write_buffer[sock->write_buffer_pos],
1106 have, MSG_DONTWAIT | MSG_NOSIGNAL);
1107 if (ret == -1)
1108 {
1109 if (errno == EINTR)
1110 goto RETRY;
1111#if DEBUG_NETWORK
1112 GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send");
1113#endif
1114 SHUTDOWN (sock->sock, SHUT_RDWR);
1115 GNUNET_break (0 == CLOSE (sock->sock));
1116 sock->sock = -1;
1117 transmit_error (sock);
1118 return;
1119 }
1120#if DEBUG_NETWORK
1121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitted %d bytes to OS\n", ret);
1122#endif
1123 sock->write_buffer_pos += ret;
1124 if (sock->write_buffer_pos == sock->write_buffer_off)
1125 {
1126 /* transmitted all pending data */
1127 sock->write_buffer_pos = 0;
1128 sock->write_buffer_off = 0;
1129#if DEBUG_NETWORK
1130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1131 "Transmission buffer now empty.\n", ret);
1132#endif
1133 }
1134 if ((sock->write_buffer_off == 0) && (NULL == sock->nth.notify_ready))
1135 return; /* all data sent! */
1136 /* not done writing, schedule more */
1137#if DEBUG_NETWORK
1138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139 "More data ready for transmission, scheduling task again!\n");
1140#endif
1141SCHEDULE_WRITE:
1142 if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1143 sock->write_task =
1144 GNUNET_SCHEDULER_add_write (tc->sched,
1145 GNUNET_NO,
1146 GNUNET_SCHEDULER_PRIORITY_KEEP,
1147 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1148 GNUNET_TIME_UNIT_FOREVER_REL,
1149 sock->sock, &transmit_ready, sock);
1150}
1151
1152
1153/**
1154 * Ask the socket to call us once the specified number of bytes
1155 * are free in the transmission buffer. May call the notify
1156 * method immediately if enough space is available.
1157 *
1158 * @param sock socket
1159 * @param size number of bytes to send
1160 * @param timeout after how long should we give up (and call
1161 * notify with buf NULL and size 0)?
1162 * @param notify function to call
1163 * @param notify_cls closure for notify
1164 * @return non-NULL if the notify callback was queued,
1165 * NULL if we are already going to notify someone else (busy)
1166 */
1167struct GNUNET_NETWORK_TransmitHandle *
1168GNUNET_NETWORK_notify_transmit_ready (struct GNUNET_NETWORK_SocketHandle
1169 *sock, size_t size,
1170 struct GNUNET_TIME_Relative timeout,
1171 GNUNET_NETWORK_TransmitReadyNotify
1172 notify, void *notify_cls)
1173{
1174 if (sock->nth.notify_ready != NULL)
1175 return NULL;
1176 GNUNET_assert (notify != NULL);
1177 GNUNET_assert (sock->write_buffer_size >= size);
1178
1179 if ((sock->sock == -1) &&
1180 (sock->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK))
1181 {
1182#if DEBUG_NETWORK
1183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1184 "Transmit fails, connection failed.\n");
1185#endif
1186 notify (notify_cls, 0, NULL);
1187 return &sock->nth;
1188 }
1189 GNUNET_assert (sock->write_buffer_off <= sock->write_buffer_size);
1190 GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_size);
1191 GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_off);
1192 sock->nth.notify_ready = notify;
1193 sock->nth.notify_ready_cls = notify_cls;
1194 sock->nth.sh = sock;
1195 sock->nth.notify_size = size;
1196 sock->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1197 sock->nth.timeout_task = GNUNET_SCHEDULER_add_delayed (sock->sched,
1198 GNUNET_NO,
1199 GNUNET_SCHEDULER_PRIORITY_KEEP,
1200 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1201 timeout,
1202 &transmit_timeout,
1203 sock);
1204#if DEBUG_NETWORK
1205 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1206 "Scheduling asynchronous transmission once connect is done...\n");
1207#endif
1208 if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1209 sock->write_task = GNUNET_SCHEDULER_add_delayed (sock->sched,
1210 GNUNET_NO,
1211 GNUNET_SCHEDULER_PRIORITY_KEEP,
1212 sock->connect_task,
1213 GNUNET_TIME_UNIT_ZERO,
1214 &transmit_ready, sock);
1215 return &sock->nth;
1216}
1217
1218
1219/**
1220 * Cancel the specified transmission-ready
1221 * notification.
1222 */
1223void
1224GNUNET_NETWORK_notify_transmit_ready_cancel (struct
1225 GNUNET_NETWORK_TransmitHandle *h)
1226{
1227 GNUNET_assert (h->notify_ready != NULL);
1228 GNUNET_SCHEDULER_cancel (h->sh->sched, h->timeout_task);
1229 h->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1230 h->notify_ready = NULL;
1231}
1232
1233
1234#if 0 /* keep Emacsens' auto-indent happy */
1235{
1236#endif
1237#ifdef __cplusplus
1238}
1239#endif
diff --git a/src/util/os_installation.c b/src/util/os_installation.c
new file mode 100644
index 000000000..201095544
--- /dev/null
+++ b/src/util/os_installation.c
@@ -0,0 +1,443 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/util/os_installation.c
23 * @brief get paths used by the program
24 * @author Milan
25 */
26
27#ifdef __cplusplus
28extern "C"
29{
30#if 0 /* keep Emacsens' auto-indent happy */
31}
32#endif
33#endif
34
35#include <sys/stat.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39
40#include "platform.h"
41#include "gnunet_common.h"
42#include "gnunet_configuration_lib.h"
43#include "gnunet_disk_lib.h"
44#include "gnunet_os_lib.h"
45#if OSX
46#include <mach-o/ldsyms.h>
47#include <mach-o/dyld.h>
48#endif
49
50#if LINUX
51/**
52 * Try to determine path by reading /proc/PID/exe
53 */
54static char *
55get_path_from_proc_maps ()
56{
57 char fn[64];
58 char *line;
59 char *dir;
60 FILE *f;
61
62 GNUNET_snprintf (fn, 64, "/proc/%u/maps", getpid ());
63 line = GNUNET_malloc (1024);
64 dir = GNUNET_malloc (1024);
65 f = fopen (fn, "r");
66 if (f != NULL)
67 {
68 while (NULL != fgets (line, 1024, f))
69 {
70 if ((1 == sscanf (line,
71 "%*x-%*x %*c%*c%*c%*c %*x %*2u:%*2u %*u%*[ ]%s",
72 dir)) && (NULL != strstr (dir, "libgnunetutil")))
73 {
74 strstr (dir, "libgnunetutil")[0] = '\0';
75 fclose (f);
76 GNUNET_free (line);
77 return dir;
78 }
79 }
80 fclose (f);
81 }
82 GNUNET_free (dir);
83 GNUNET_free (line);
84 return NULL;
85}
86
87/**
88 * Try to determine path by reading /proc/PID/exe
89 */
90static char *
91get_path_from_proc_exe ()
92{
93 char fn[64];
94 char *lnk;
95 size_t size;
96
97 GNUNET_snprintf (fn, 64, "/proc/%u/exe", getpid ());
98 lnk = GNUNET_malloc (1024);
99 size = readlink (fn, lnk, 1023);
100 if ((size == 0) || (size >= 1024))
101 {
102 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
103 GNUNET_free (lnk);
104 return NULL;
105 }
106 lnk[size] = '\0';
107 while ((lnk[size] != '/') && (size > 0))
108 size--;
109 if ((size < 4) || (lnk[size - 4] != '/'))
110 {
111 /* not installed in "/bin/" -- binary path probably useless */
112 GNUNET_free (lnk);
113 return NULL;
114 }
115 lnk[size] = '\0';
116 return lnk;
117}
118#endif
119
120#if WINDOWS
121/**
122 * Try to determine path with win32-specific function
123 */
124static char *
125get_path_from_module_filename ()
126{
127 char *path;
128 char *idx;
129
130 path = GNUNET_malloc (4097);
131 GetModuleFileName (NULL, path, 4096);
132 idx = path + strlen (path);
133 while ((idx > path) && (*idx != '\\') && (*idx != '/'))
134 idx--;
135 *idx = '\0';
136 return path;
137}
138#endif
139
140#if OSX
141typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize);
142
143static char *
144get_path_from_NSGetExecutablePath ()
145{
146 static char zero = '\0';
147 char *path;
148 size_t len;
149 MyNSGetExecutablePathProto func;
150 int ret;
151
152 path = NULL;
153 func =
154 (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath");
155 if (!func)
156 return NULL;
157 path = &zero;
158 len = 0;
159 /* get the path len, including the trailing \0 */
160 func (path, &len);
161 if (len == 0)
162 return NULL;
163 path = GNUNET_malloc (len);
164 ret = func (path, &len);
165 if (ret != 0)
166 {
167 GNUNET_free (path);
168 return NULL;
169 }
170 len = strlen (path);
171 while ((path[len] != '/') && (len > 0))
172 len--;
173 path[len] = '\0';
174 return path;
175}
176
177static char *
178get_path_from_dyld_image ()
179{
180 const char *path;
181 char *p, *s;
182 int i;
183 int c;
184
185 p = NULL;
186 c = _dyld_image_count ();
187 for (i = 0; i < c; i++)
188 {
189 if (_dyld_get_image_header (i) == &_mh_dylib_header)
190 {
191 path = _dyld_get_image_name (i);
192 if (path != NULL && strlen (path) > 0)
193 {
194 p = strdup (path);
195 s = p + strlen (p);
196 while ((s > p) && (*s != '/'))
197 s--;
198 s++;
199 *s = '\0';
200 }
201 break;
202 }
203 }
204 return p;
205}
206#endif
207
208static char *
209get_path_from_PATH ()
210{
211 char *path;
212 char *pos;
213 char *end;
214 char *buf;
215 const char *p;
216 size_t size;
217
218 p = getenv ("PATH");
219 if (p == NULL)
220 return NULL;
221 path = GNUNET_strdup (p); /* because we write on it */
222 buf = GNUNET_malloc (strlen (path) + 20);
223 size = strlen (path);
224 pos = path;
225
226 while (NULL != (end = strchr (pos, ':')))
227 {
228 *end = '\0';
229 sprintf (buf, "%s/%s", pos, "gnunetd");
230 if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
231 {
232 pos = GNUNET_strdup (pos);
233 GNUNET_free (buf);
234 GNUNET_free (path);
235 return pos;
236 }
237 pos = end + 1;
238 }
239 sprintf (buf, "%s/%s", pos, "gnunetd");
240 if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
241 {
242 pos = GNUNET_strdup (pos);
243 GNUNET_free (buf);
244 GNUNET_free (path);
245 return pos;
246 }
247 GNUNET_free (buf);
248 GNUNET_free (path);
249 return NULL;
250}
251
252static char *
253get_path_from_GNUNET_PREFIX ()
254{
255 const char *p;
256
257 p = getenv ("GNUNET_PREFIX");
258 if (p != NULL)
259 return GNUNET_strdup (p);
260 return NULL;
261}
262
263/*
264 * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path
265 * @author Milan
266 *
267 * @return a pointer to the executable path, or NULL on error
268 */
269static char *
270os_get_gnunet_path ()
271{
272 char *ret;
273
274 ret = get_path_from_GNUNET_PREFIX ();
275 if (ret != NULL)
276 return ret;
277#if LINUX
278 ret = get_path_from_proc_maps ();
279 if (ret != NULL)
280 return ret;
281 ret = get_path_from_proc_exe ();
282 if (ret != NULL)
283 return ret;
284#endif
285#if WINDOWS
286 ret = get_path_from_module_filename ();
287 if (ret != NULL)
288 return ret;
289#endif
290#if OSX
291 ret = get_path_from_dyld_image ();
292 if (ret != NULL)
293 return ret;
294 ret = get_path_from_NSGetExecutablePath ();
295 if (ret != NULL)
296 return ret;
297#endif
298 ret = get_path_from_PATH ();
299 if (ret != NULL)
300 return ret;
301 /* other attempts here */
302 return NULL;
303}
304
305/*
306 * @brief get the path to current app's bin/
307 * @author Milan
308 *
309 * @return a pointer to the executable path, or NULL on error
310 */
311static char *
312os_get_exec_path ()
313{
314 char *ret;
315
316#if LINUX
317 ret = get_path_from_proc_exe ();
318 if (ret != NULL)
319 return ret;
320#endif
321#if WINDOWS
322 ret = get_path_from_module_filename ();
323 if (ret != NULL)
324 return ret;
325#endif
326#if OSX
327 ret = get_path_from_NSGetExecutablePath ();
328 if (ret != NULL)
329 return ret;
330#endif
331 /* other attempts here */
332 return NULL;
333}
334
335
336
337/**
338 * @brief get the path to a specific GNUnet installation directory or,
339 * with GNUNET_IPK_SELF_PREFIX, the current running apps installation directory
340 * @author Milan
341 * @return a pointer to the dir path (to be freed by the caller)
342 */
343char *
344GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
345{
346 size_t n;
347 const char *dirname;
348 char *execpath = NULL;
349 char *tmp;
350 int isbasedir;
351
352 /* if wanted, try to get the current app's bin/ */
353 if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
354 execpath = os_get_exec_path ();
355
356 /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some
357 * guess for the current app */
358 if (execpath == NULL)
359 execpath = os_get_gnunet_path ();
360
361 if (execpath == NULL)
362 return NULL;
363
364 n = strlen (execpath);
365 if (n == 0)
366 {
367 /* should never happen, but better safe than sorry */
368 GNUNET_free (execpath);
369 return NULL;
370 }
371 /* remove filename itself */
372 while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
373 execpath[--n] = '\0';
374
375 isbasedir = 1;
376 if ((n > 5) &&
377 ((0 == strcasecmp (&execpath[n - 5], "lib32")) ||
378 (0 == strcasecmp (&execpath[n - 5], "lib64"))))
379 {
380 if (dirkind != GNUNET_OS_IPK_LIBDIR)
381 {
382 /* strip '/lib32' or '/lib64' */
383 execpath[n - 5] = '\0';
384 n -= 5;
385 }
386 else
387 isbasedir = 0;
388 }
389 else if ((n > 3) &&
390 ((0 == strcasecmp (&execpath[n - 3], "bin")) ||
391 (0 == strcasecmp (&execpath[n - 3], "lib"))))
392 {
393 /* strip '/bin' or '/lib' */
394 execpath[n - 3] = '\0';
395 n -= 3;
396 }
397 /* in case this was a directory named foo-bin, remove "foo-" */
398 while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
399 execpath[--n] = '\0';
400 switch (dirkind)
401 {
402 case GNUNET_OS_IPK_PREFIX:
403 case GNUNET_OS_IPK_SELF_PREFIX:
404 dirname = DIR_SEPARATOR_STR;
405 break;
406 case GNUNET_OS_IPK_BINDIR:
407 dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR;
408 break;
409 case GNUNET_OS_IPK_LIBDIR:
410 if (isbasedir)
411 dirname =
412 DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet"
413 DIR_SEPARATOR_STR;
414 else
415 dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
416 break;
417 case GNUNET_OS_IPK_DATADIR:
418 dirname =
419 DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "gnunet"
420 DIR_SEPARATOR_STR;
421 break;
422 case GNUNET_OS_IPK_LOCALEDIR:
423 dirname =
424 DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale"
425 DIR_SEPARATOR_STR;
426 break;
427 default:
428 GNUNET_free (execpath);
429 return NULL;
430 }
431 tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
432 sprintf (tmp, "%s%s", execpath, dirname);
433 GNUNET_free (execpath);
434 return tmp;
435}
436
437#if 0 /* keep Emacsens' auto-indent happy */
438{
439#endif
440#ifdef __cplusplus
441}
442#endif
443/* end of installpath.c */
diff --git a/src/util/os_load.c b/src/util/os_load.c
new file mode 100644
index 000000000..d1115e33f
--- /dev/null
+++ b/src/util/os_load.c
@@ -0,0 +1,653 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/os_load_cpu.c
23 * @brief calls to determine current CPU load
24 * @author Tzvetan Horozov
25 * @author Christian Grothoff
26 * @author Igor Wronsky
27 * @author Alex Harper (OS X portion)
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_os_lib.h"
33#include "gnunet_strings_lib.h"
34
35#if SOLARIS
36#if HAVE_KSTAT_H
37#include <kstat.h>
38#endif
39#if HAVE_SYS_SYSINFO_H
40#include <sys/sysinfo.h>
41#endif
42#if HAVE_KVM_H
43#include <kvm.h>
44#endif
45#endif
46#if SOMEBSD
47#if HAVE_KVM_H
48#include <kvm.h>
49#endif
50#endif
51
52#ifdef OSX
53#include <mach/mach.h>
54
55static processor_cpu_load_info_t prev_cpu_load;
56#endif
57
58#define DEBUG_STATUSCALLS GNUNET_NO
59
60#ifdef LINUX
61static FILE *proc_stat;
62#endif
63
64/**
65 * Current CPU load, as percentage of CPU cycles not idle or
66 * blocked on IO.
67 */
68static int currentCPULoad;
69
70static double agedCPULoad = -1;
71
72/**
73 * Current IO load, as percentage of CPU cycles blocked on IO.
74 */
75static int currentIOLoad;
76
77static double agedIOLoad = -1;
78
79#ifdef OSX
80static int
81initMachCpuStats ()
82{
83 unsigned int cpu_count;
84 processor_cpu_load_info_t cpu_load;
85 mach_msg_type_number_t cpu_msg_count;
86 kern_return_t kret;
87 int i, j;
88
89 kret = host_processor_info (mach_host_self (),
90 PROCESSOR_CPU_LOAD_INFO,
91 &cpu_count,
92 (processor_info_array_t *) & cpu_load,
93 &cpu_msg_count);
94 if (kret != KERN_SUCCESS)
95 {
96 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
97 "host_processor_info failed.");
98 return GNUNET_SYSERR;
99 }
100 prev_cpu_load = GNUNET_malloc (cpu_count * sizeof (*prev_cpu_load));
101 for (i = 0; i < cpu_count; i++)
102 {
103 for (j = 0; j < CPU_STATE_MAX; j++)
104 {
105 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
106 }
107 }
108 vm_deallocate (mach_task_self (),
109 (vm_address_t) cpu_load,
110 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
111 return GNUNET_OK;
112}
113#endif
114
115/**
116 * Update the currentCPU and currentIO load values.
117 *
118 * Before its first invocation the method initStatusCalls() must be called.
119 * If there is an error the method returns -1.
120 */
121static int
122updateUsage ()
123{
124 currentIOLoad = -1;
125 currentCPULoad = -1;
126#ifdef LINUX
127 /* under linux, first try %idle/usage using /proc/stat;
128 if that does not work, disable /proc/stat for the future
129 by closing the file and use the next-best method. */
130 if (proc_stat != NULL)
131 {
132 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
133 static int have_last_cpu = GNUNET_NO;
134 int ret;
135 char line[256];
136 unsigned long long user_read, system_read, nice_read, idle_read,
137 iowait_read;
138 unsigned long long user, system, nice, idle, iowait;
139 unsigned long long usage_time = 0, total_time = 1;
140
141 /* Get the first line with the data */
142 rewind (proc_stat);
143 fflush (proc_stat);
144 if (NULL == fgets (line, 256, proc_stat))
145 {
146 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
147 GNUNET_ERROR_TYPE_BULK,
148 "fgets", "/proc/stat");
149 fclose (proc_stat);
150 proc_stat = NULL; /* don't try again */
151 }
152 else
153 {
154 iowait_read = 0;
155 ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
156 &user_read,
157 &system_read, &nice_read, &idle_read, &iowait_read);
158 if (ret < 4)
159 {
160 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
161 GNUNET_ERROR_TYPE_BULK,
162 "fgets-sscanf", "/proc/stat");
163 fclose (proc_stat);
164 proc_stat = NULL; /* don't try again */
165 have_last_cpu = GNUNET_NO;
166 }
167 else
168 {
169 /* Store the current usage */
170 user = user_read - last_cpu_results[0];
171 system = system_read - last_cpu_results[1];
172 nice = nice_read - last_cpu_results[2];
173 idle = idle_read - last_cpu_results[3];
174 iowait = iowait_read - last_cpu_results[4];
175 /* Calculate the % usage */
176 usage_time = user + system + nice;
177 total_time = usage_time + idle + iowait;
178 if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
179 {
180 currentCPULoad = (int) (100L * usage_time / total_time);
181 if (ret > 4)
182 currentIOLoad = (int) (100L * iowait / total_time);
183 else
184 currentIOLoad = -1; /* 2.4 kernel */
185 }
186 /* Store the values for the next calculation */
187 last_cpu_results[0] = user_read;
188 last_cpu_results[1] = system_read;
189 last_cpu_results[2] = nice_read;
190 last_cpu_results[3] = idle_read;
191 last_cpu_results[4] = iowait_read;
192 have_last_cpu = GNUNET_YES;
193 return GNUNET_OK;
194 }
195 }
196 }
197#endif
198
199#ifdef OSX
200 {
201 unsigned int cpu_count;
202 processor_cpu_load_info_t cpu_load;
203 mach_msg_type_number_t cpu_msg_count;
204 unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
205 unsigned long long t_idle_all, t_total_all;
206 kern_return_t kret;
207 int i, j;
208
209 t_idle_all = t_total_all = 0;
210 kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
211 &cpu_count,
212 (processor_info_array_t *) & cpu_load,
213 &cpu_msg_count);
214 if (kret == KERN_SUCCESS)
215 {
216 for (i = 0; i < cpu_count; i++)
217 {
218 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
219 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
220 {
221 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
222 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
223 }
224 else
225 {
226 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
227 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
228 1);
229 }
230
231 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
232 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
233 {
234 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
235 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
236 }
237 else
238 {
239 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
240 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
241 1);
242 }
243
244 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
245 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
246 {
247 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
248 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
249 }
250 else
251 {
252 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
253 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
254 1);
255 }
256
257 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
258 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
259 {
260 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
261 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
262 }
263 else
264 {
265 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
266 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
267 1);
268 }
269 t_total = t_sys + t_user + t_nice + t_idle;
270 t_idle_all += t_idle;
271 t_total_all += t_total;
272 }
273 for (i = 0; i < cpu_count; i++)
274 {
275 for (j = 0; j < CPU_STATE_MAX; j++)
276 {
277 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
278 }
279 }
280 if (t_total_all > 0)
281 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
282 else
283 currentCPULoad = -1;
284 vm_deallocate (mach_task_self (),
285 (vm_address_t) cpu_load,
286 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
287 currentIOLoad = -1; /* FIXME-OSX! */
288 return GNUNET_OK;
289 }
290 else
291 {
292 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
293 "host_processor_info failed.");
294 return GNUNET_SYSERR;
295 }
296 }
297#endif
298 /* try kstat (Solaris only) */
299#if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
300 {
301 static long long last_idlecount;
302 static long long last_totalcount;
303 static int kstat_once; /* if open fails, don't keep
304 trying */
305 kstat_ctl_t *kc;
306 kstat_t *khelper;
307 long long idlecount;
308 long long totalcount;
309 long long deltaidle;
310 long long deltatotal;
311
312 if (kstat_once == 1)
313 goto ABORT_KSTAT;
314 kc = kstat_open ();
315 if (kc == NULL)
316 {
317 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
318 "kstat_open");
319 goto ABORT_KSTAT;
320 }
321
322 idlecount = 0;
323 totalcount = 0;
324 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
325 {
326 cpu_stat_t stats;
327
328 if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
329 continue;
330 if (khelper->ks_data_size > sizeof (cpu_stat_t))
331 continue; /* better save then sorry! */
332 if (-1 != kstat_read (kc, khelper, &stats))
333 {
334 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
335 totalcount
336 += stats.cpu_sysinfo.cpu[CPU_IDLE] +
337 stats.cpu_sysinfo.cpu[CPU_USER] +
338 stats.cpu_sysinfo.cpu[CPU_KERNEL] +
339 stats.cpu_sysinfo.cpu[CPU_WAIT];
340 }
341 }
342 if (0 != kstat_close (kc))
343 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
344 "kstat_close");
345 if ((idlecount == 0) && (totalcount == 0))
346 goto ABORT_KSTAT; /* no stats found => abort */
347 deltaidle = idlecount - last_idlecount;
348 deltatotal = totalcount - last_totalcount;
349 if ((deltatotal > 0) && (last_totalcount > 0))
350 {
351 currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal);
352 if (currentCPULoad > 100)
353 currentCPULoad = 100; /* odd */
354 if (currentCPULoad < 0)
355 currentCPULoad = 0; /* odd */
356 currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
357 }
358 else
359 currentCPULoad = -1;
360 currentIOLoad = -1; /* FIXME-SOLARIS! */
361 last_idlecount = idlecount;
362 last_totalcount = totalcount;
363 return GNUNET_OK;
364 ABORT_KSTAT:
365 kstat_once = 1; /* failed, don't try again */
366 return GNUNET_SYSERR;
367 }
368#endif
369
370 /* insert methods better than getloadavg for
371 other platforms HERE! */
372
373 /* ok, maybe we have getloadavg on this platform */
374#if HAVE_GETLOADAVG
375 {
376 static int warnOnce = 0;
377 double loadavg;
378 if (1 != getloadavg (&loadavg, 1))
379 {
380 /* only warn once, if there is a problem with
381 getloadavg, we're going to hit it frequently... */
382 if (warnOnce == 0)
383 {
384 warnOnce = 1;
385 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "getloadavg");
386 }
387 return GNUNET_SYSERR;
388 }
389 else
390 {
391 /* success with getloadavg */
392 currentCPULoad = (int) (100 * loadavg);
393 currentIOLoad = -1; /* FIXME */
394 return GNUNET_OK;
395 }
396 }
397#endif
398
399#if MINGW
400 /* Win NT? */
401 if (GNNtQuerySystemInformation)
402 {
403 static double dLastKernel;
404 static double dLastIdle;
405 static double dLastUser;
406 double dKernel;
407 double dIdle;
408 double dUser;
409 double dDiffKernel;
410 double dDiffIdle;
411 double dDiffUser;
412 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo;
413
414 if (GNNtQuerySystemInformation (SystemProcessorPerformanceInformation,
415 &theInfo,
416 sizeof (theInfo), NULL) == NO_ERROR)
417 {
418 /* PORT-ME MINGW: Multi-processor? */
419 dKernel = Li2Double (theInfo.KernelTime);
420 dIdle = Li2Double (theInfo.IdleTime);
421 dUser = Li2Double (theInfo.UserTime);
422 dDiffKernel = dKernel - dLastKernel;
423 dDiffIdle = dIdle - dLastIdle;
424 dDiffUser = dUser - dLastUser;
425
426 if (((dDiffKernel + dDiffUser) > 0) &&
427 (dLastIdle + dLastKernel + dLastUser > 0))
428 currentCPULoad =
429 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0;
430 else
431 currentCPULoad = -1; /* don't know (yet) */
432
433 dLastKernel = dKernel;
434 dLastIdle = dIdle;
435 dLastUser = dUser;
436
437 currentIOLoad = -1; /* FIXME-MINGW */
438 return GNUNET_OK;
439 }
440 else
441 {
442 /* only warn once, if there is a problem with
443 NtQuery..., we're going to hit it frequently... */
444 static int once;
445 if (once == 0)
446 {
447 once = 1;
448 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
449 _("Cannot query the CPU usage (Windows NT).\n"));
450 }
451 return GNUNET_SYSERR;
452 }
453 }
454 else
455 { /* Win 9x */
456 HKEY hKey;
457 DWORD dwDataSize, dwType, dwDummy;
458
459 /* Start query */
460 if (RegOpenKeyEx (HKEY_DYN_DATA,
461 "PerfStats\\StartSrv",
462 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
463 {
464 /* only warn once */
465 static int once = 0;
466 if (once == 0)
467 {
468 once = 1;
469 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
470 _("Cannot query the CPU usage (Win 9x)\n"));
471 }
472 }
473
474 RegOpenKeyEx (HKEY_DYN_DATA,
475 "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey);
476 dwDataSize = sizeof (dwDummy);
477 RegQueryValueEx (hKey,
478 "KERNEL\\CPUUsage",
479 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
480 RegCloseKey (hKey);
481
482 /* Get CPU usage */
483 RegOpenKeyEx (HKEY_DYN_DATA,
484 "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey);
485 dwDataSize = sizeof (currentCPULoad);
486 RegQueryValueEx (hKey,
487 "KERNEL\\CPUUsage",
488 NULL, &dwType, (LPBYTE) & currentCPULoad, &dwDataSize);
489 RegCloseKey (hKey);
490 currentIOLoad = -1; /* FIXME-MINGW! */
491
492 /* Stop query */
493 RegOpenKeyEx (HKEY_DYN_DATA,
494 "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey);
495 RegOpenKeyEx (HKEY_DYN_DATA,
496 "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey);
497 dwDataSize = sizeof (dwDummy);
498 RegQueryValueEx (hKey,
499 "KERNEL\\CPUUsage",
500 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
501 RegCloseKey (hKey);
502
503 return GNUNET_OK;
504 }
505#endif
506
507 /* loadaverage not defined and no platform
508 specific alternative defined
509 => default: error
510 */
511 return GNUNET_SYSERR;
512}
513
514/**
515 * Update load values (if enough time has expired),
516 * including computation of averages. Code assumes
517 * that lock has already been obtained.
518 */
519static void
520updateAgedLoad (struct GNUNET_CONFIGURATION_Handle *cfg)
521{
522 static struct GNUNET_TIME_Absolute lastCall;
523
524 if ((agedCPULoad == -1)
525 || (GNUNET_TIME_absolute_get_duration (lastCall).value > 500))
526 {
527 /* use smoothing, but do NOT update lastRet at frequencies higher
528 than 500ms; this makes the smoothing (mostly) independent from
529 the frequency at which getCPULoad is called (and we don't spend
530 more time measuring CPU than actually computing something). */
531 lastCall = GNUNET_TIME_absolute_get ();
532 updateUsage ();
533 if (currentCPULoad == -1)
534 {
535 agedCPULoad = -1;
536 }
537 else
538 {
539 if (agedCPULoad == -1)
540 {
541 agedCPULoad = currentCPULoad;
542 }
543 else
544 {
545 /* for CPU, we don't do the 'fast increase' since CPU is much
546 more jitterish to begin with */
547 agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
548 }
549 }
550 if (currentIOLoad == -1)
551 {
552 agedIOLoad = -1;
553 }
554 else
555 {
556 if (agedIOLoad == -1)
557 {
558 agedIOLoad = currentIOLoad;
559 }
560 else
561 {
562 /* for IO, we don't do the 'fast increase' since IO is much
563 more jitterish to begin with */
564 agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
565 }
566 }
567 }
568}
569
570/**
571 * Get the load of the CPU relative to what is allowed.
572 * @return the CPU load as a percentage of allowed
573 * (100 is equivalent to full load)
574 */
575int
576GNUNET_OS_load_cpu_get (struct GNUNET_CONFIGURATION_Handle *cfg)
577{
578 unsigned long long maxCPULoad;
579 int ret;
580
581 updateAgedLoad (cfg);
582 ret = agedCPULoad;
583 if (ret == -1)
584 return -1;
585 if (GNUNET_OK !=
586 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXCPULOAD",
587 &maxCPULoad))
588 return GNUNET_SYSERR;
589 return (100 * ret) / maxCPULoad;
590}
591
592
593/**
594 * Get the load of the CPU relative to what is allowed.
595 * @return the CPU load as a percentage of allowed
596 * (100 is equivalent to full load)
597 */
598int
599GNUNET_OS_load_disk_get (struct GNUNET_CONFIGURATION_Handle *cfg)
600{
601 unsigned long long maxIOLoad;
602 int ret;
603
604 updateAgedLoad (cfg);
605 ret = agedIOLoad;
606 if (ret == -1)
607 return -1;
608 if (-1 ==
609 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXIOLOAD",
610 &maxIOLoad))
611 return GNUNET_SYSERR;
612 return (100 * ret) / maxIOLoad;
613}
614
615/**
616 * The following method is called in order to initialize the status calls
617 * routines. After that it is safe to call each of the status calls separately
618 * @return GNUNET_OK on success and GNUNET_SYSERR on error (or calls errexit).
619 */
620void __attribute__ ((constructor)) GNUNET_cpustats_ltdl_init ()
621{
622#ifdef LINUX
623 proc_stat = fopen ("/proc/stat", "r");
624 if (NULL == proc_stat)
625 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", "/proc/stat");
626#elif OSX
627 initMachCpuStats ();
628#elif MINGW
629 InitWinEnv (NULL);
630#endif
631 updateUsage (); /* initialize */
632}
633
634/**
635 * Shutdown the status calls module.
636 */
637void __attribute__ ((destructor)) GNUNET_cpustats_ltdl_fini ()
638{
639#ifdef LINUX
640 if (proc_stat != NULL)
641 {
642 fclose (proc_stat);
643 proc_stat = NULL;
644 }
645#elif OSX
646 GNUNET_free_non_null (prev_cpu_load);
647#elif MINGW
648 ShutdownWinEnv ();
649#endif
650}
651
652
653/* end of os_load_cpu.c */
diff --git a/src/util/os_network.c b/src/util/os_network.c
new file mode 100644
index 000000000..cb5ccb12a
--- /dev/null
+++ b/src/util/os_network.c
@@ -0,0 +1,286 @@
1/*
2 This file is part of GNUnet.
3 (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @file util/os_network.c
24 * @brief function to determine available network interfaces
25 * @author Nils Durner
26 * @author Heikki Lindholm
27 * @author Jake Dust
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_os_lib.h"
33
34/**
35 * @brief Enumerate all network interfaces
36 * @param callback the callback function
37 */
38void
39GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor proc,
40 void *cls)
41{
42#ifdef MINGW
43 PMIB_IFTABLE pTable;
44 PMIB_IPADDRTABLE pAddrTable;
45 DWORD dwIfIdx, dwExternalNIC;
46 IPAddr theIP;
47
48 /* Determine our external NIC */
49 theIP = inet_addr ("192.0.34.166"); /* www.example.com */
50 if ((!GNGetBestInterface) ||
51 (GNGetBestInterface (theIP, &dwExternalNIC) != NO_ERROR))
52 {
53 dwExternalNIC = 0;
54 }
55
56 /* Enumerate NICs */
57 EnumNICs (&pTable, &pAddrTable);
58
59 if (pTable)
60 {
61 for (dwIfIdx = 0; dwIfIdx <= pTable->dwNumEntries; dwIfIdx++)
62 {
63 char szEntry[1001];
64 DWORD dwIP = 0;
65 int iItm;
66 PIP_ADAPTER_INFO pAdapterInfo;
67 PIP_ADAPTER_INFO pAdapter = NULL;
68 DWORD dwRetVal = 0;
69
70 /* Get IP-Address */
71 int i;
72 for (i = 0; i < pAddrTable->dwNumEntries; i++)
73 {
74 if (pAddrTable->table[i].dwIndex ==
75 pTable->table[dwIfIdx].dwIndex)
76 {
77 dwIP = pAddrTable->table[i].dwAddr;
78 break;
79 }
80 }
81
82 if (dwIP)
83 {
84 BYTE bPhysAddr[MAXLEN_PHYSADDR];
85 char *pszIfName = NULL;
86 char dst[INET_ADDRSTRLEN];
87
88 /* Get friendly interface name */
89 pAdapterInfo =
90 (IP_ADAPTER_INFO *) malloc (sizeof (IP_ADAPTER_INFO));
91 ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
92
93 /* Make an initial call to GetAdaptersInfo to get
94 the necessary size into the ulOutBufLen variable */
95 if (GGetAdaptersInfo (pAdapterInfo, &ulOutBufLen) ==
96 ERROR_BUFFER_OVERFLOW)
97 {
98 free (pAdapterInfo);
99 pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
100 }
101
102 if ((dwRetVal =
103 GGetAdaptersInfo (pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
104 {
105 pAdapter = pAdapterInfo;
106 while (pAdapter)
107 {
108 if (pTable->table[dwIfIdx].dwIndex == pAdapter->Index)
109 {
110 char szKey[251];
111 long lLen = 250;
112
113 sprintf (szKey,
114 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
115 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s\\Connection",
116 pAdapter->AdapterName);
117 pszIfName = (char *) malloc (251);
118 if (QueryRegistry
119 (HKEY_LOCAL_MACHINE, szKey, "Name", pszIfName,
120 &lLen) != ERROR_SUCCESS)
121 {
122 free (pszIfName);
123 pszIfName = NULL;
124 }
125 }
126 pAdapter = pAdapter->Next;
127 }
128 }
129 free (pAdapterInfo);
130
131 /* Set entry */
132 memset (bPhysAddr, 0, MAXLEN_PHYSADDR);
133 memcpy (bPhysAddr,
134 pTable->table[dwIfIdx].bPhysAddr,
135 pTable->table[dwIfIdx].dwPhysAddrLen);
136
137 snprintf (szEntry, 1000, "%s (%s - %I64u)",
138 pszIfName ? pszIfName : (char *) pTable->
139 table[dwIfIdx].bDescr, inet_ntop (AF_INET, &dwIP, dst,
140 INET_ADDRSTRLEN),
141 *((unsigned long long *) bPhysAddr));
142 szEntry[1000] = 0;
143
144 if (pszIfName)
145 free (pszIfName);
146
147 if (GNUNET_OK !=
148 proc (cls,
149 szEntry,
150 pAddrTable->table[dwIfIdx].dwIndex == dwExternalNIC,
151 NULL /* FIXME: pass actual IP address! */ ,
152 0))
153 break;
154 }
155 }
156 GlobalFree (pAddrTable);
157 GlobalFree (pTable);
158 }
159
160 return GNUNET_YES;
161
162#elif HAVE_GETIFADDRS && HAVE_FREEIFADDRS
163
164 struct ifaddrs *ifa_first;
165 socklen_t alen;
166
167 if (getifaddrs (&ifa_first) == 0)
168 {
169 struct ifaddrs *ifa_ptr;
170
171 ifa_ptr = ifa_first;
172 for (ifa_ptr = ifa_first; ifa_ptr != NULL; ifa_ptr = ifa_ptr->ifa_next)
173 {
174 if (ifa_ptr->ifa_name != NULL &&
175 ifa_ptr->ifa_addr != NULL && (ifa_ptr->ifa_flags & IFF_UP) != 0)
176 {
177 if ((ifa_ptr->ifa_addr->sa_family != AF_INET) &&
178 (ifa_ptr->ifa_addr->sa_family != AF_INET6))
179 continue;
180 if (ifa_ptr->ifa_addr->sa_family == AF_INET)
181 alen = sizeof (struct sockaddr_in);
182 else
183 alen = sizeof (struct sockaddr_in6);
184 if (GNUNET_OK != proc (cls,
185 ifa_ptr->ifa_name,
186 0 == strcmp (ifa_ptr->ifa_name,
187 GNUNET_DEFAULT_INTERFACE),
188 ifa_ptr->ifa_addr, alen))
189 break;
190 }
191 }
192 freeifaddrs (ifa_first);
193 }
194#else
195 char line[1024];
196 const char *start;
197 char ifc[12];
198 char addrstr[128];
199 FILE *f;
200 int have_ifc;
201 struct sockaddr_in a4;
202 struct sockaddr_in6 a6;
203 struct in_addr v4;
204 struct in6_addr v6;
205
206 if (system ("ifconfig -a > /dev/null 2> /dev/null"))
207 if (system ("/sbin/ifconfig -a > /dev/null 2> /dev/null") == 0)
208 f = popen ("/sbin/ifconfig -a 2> /dev/null", "r");
209 else
210 f = NULL;
211 else
212 f = popen ("ifconfig -a 2> /dev/null", "r");
213 if (!f)
214 {
215 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
216 GNUNET_ERROR_TYPE_BULK, "popen", "ifconfig");
217 return;
218 }
219
220 have_ifc = GNUNET_NO;
221 ifc[11] = '\0';
222 while (NULL != fgets (line, sizeof (line), f))
223 {
224 if (strlen (line) == 0)
225 {
226 have_ifc = GNUNET_NO;
227 continue;
228 }
229 if (!isspace (line[0]))
230 {
231 have_ifc =
232 (1 == SSCANF (line, "%11s", ifc)) ? GNUNET_YES : GNUNET_NO;
233 /* would end with ':' on OSX, fix it! */
234 if (ifc[strlen (ifc) - 1] == ':')
235 ifc[strlen (ifc) - 1] = '\0';
236 continue;
237 }
238 if (!have_ifc)
239 continue; /* strange input, hope for the best */
240 start = line;
241 while (('\0' != *start) && (isspace (*start)))
242 start++;
243 if ( /* Linux */
244 (1 == SSCANF (start, "inet addr:%127s", addrstr)) ||
245 (1 == SSCANF (start, "inet6 addr:%127s", addrstr)) ||
246 /* Solaris, OS X */
247 (1 == SSCANF (start, "inet %127s", addrstr)) ||
248 (1 == SSCANF (start, "inet6 %127s", addrstr)))
249 {
250 /* IPv4 */
251 if (1 == inet_pton (AF_INET, addrstr, &v4))
252 {
253 memset (&a4, 0, sizeof (a4));
254 a4.sin_family = AF_INET;
255 a4.sin_addr = v4;
256 if (GNUNET_OK !=
257 proc (cls,
258 ifc,
259 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE),
260 (const struct sockaddr *) &a4, sizeof (a4)))
261 break;
262 continue;
263 }
264 /* IPv6 */
265 if (1 == inet_pton (AF_INET6, addrstr, &v6))
266 {
267 memset (&a6, 0, sizeof (a6));
268 a6.sin6_family = AF_INET6;
269 a6.sin6_addr = v6;
270 fprintf (stderr, "procing %s\n", addrstr);
271 if (GNUNET_OK !=
272 proc (cls,
273 ifc,
274 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE),
275 (const struct sockaddr *) &a6, sizeof (a6)))
276 break;
277 continue;
278 }
279 }
280 }
281 pclose (f);
282#endif
283}
284
285
286/* end of os_network.c */
diff --git a/src/util/os_priority.c b/src/util/os_priority.c
new file mode 100644
index 000000000..7862d68e9
--- /dev/null
+++ b/src/util/os_priority.c
@@ -0,0 +1,186 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/os/priority.c
23 * @brief Methods to set process priority
24 * @author Nils Durner
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_os_lib.h"
30
31/**
32 * Set our process priority
33 */
34int
35GNUNET_OS_set_process_priority (pid_t proc,
36 enum GNUNET_SCHEDULER_Priority eprio)
37{
38 int prio = 0;
39
40 GNUNET_assert (eprio < GNUNET_SCHEDULER_PRIORITY_COUNT);
41 if (eprio == GNUNET_SCHEDULER_PRIORITY_KEEP)
42 return GNUNET_OK;
43 /* convert to MINGW/Unix values */
44 switch (eprio)
45 {
46 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
47#ifdef MINGW
48 prio = NORMAL_PRIORITY_CLASS;
49#else
50 prio = 0;
51#endif
52 break;
53 case GNUNET_SCHEDULER_PRIORITY_HIGH:
54#ifdef MINGW
55 prio = ABOVE_NORMAL_PRIORITY_CLASS;
56#else
57 prio = -5;
58#endif
59 break;
60 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
61#ifdef MINGW
62 prio = BELOW_NORMAL_PRIORITY_CLASS;
63#else
64 prio = 10;
65#endif
66 break;
67 case GNUNET_SCHEDULER_PRIORITY_UI:
68 case GNUNET_SCHEDULER_PRIORITY_URGENT:
69#ifdef MINGW
70 prio = HIGH_PRIORITY_CLASS;
71#else
72 prio = -10;
73#endif
74 break;
75 case GNUNET_SCHEDULER_PRIORITY_IDLE:
76#ifdef MINGW
77 prio = IDLE_PRIORITY_CLASS;
78#else
79 prio = 19;
80#endif
81 break;
82 default:
83 GNUNET_assert (0);
84 return GNUNET_SYSERR;
85 }
86 /* Set process priority */
87#ifdef MINGW
88 SetPriorityClass (GetCurrentProcess (), prio);
89#else
90 if (proc == getpid ())
91 {
92 errno = 0;
93 if ((-1 == nice (prio)) && (errno != 0))
94 {
95 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
96 GNUNET_ERROR_TYPE_BULK, "nice");
97 return GNUNET_SYSERR;
98 }
99 }
100 else
101 {
102 if (0 != setpriority (PRIO_PROCESS, proc, prio))
103
104 {
105 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
106 GNUNET_ERROR_TYPE_BULK, "setpriority");
107 return GNUNET_SYSERR;
108 }
109 }
110#endif
111 return GNUNET_OK;
112}
113
114
115
116/**
117 * Start a process.
118 *
119 * @param filename name of the binary
120 * @param ... NULL-terminated list of arguments to the process
121 * @return process ID of the new process, -1 on error
122 */
123pid_t
124GNUNET_OS_start_process (const char *filename, ...)
125{
126 pid_t ret;
127 char **argv;
128 va_list ap;
129 int argc;
130
131 ret = fork ();
132 if (ret != 0)
133 {
134 if (ret == -1)
135 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
136 return ret;
137 }
138 argc = 0;
139 va_start (ap, filename);
140 while (NULL != va_arg (ap, char *))
141 argc++;
142 va_end (ap);
143 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
144 argc = 0;
145 va_start (ap, filename);
146 while (NULL != (argv[argc] = va_arg (ap, char *)))
147 argc++;
148 va_end (ap);
149 execvp (filename, argv);
150 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
151 exit (1);
152}
153
154
155
156
157/**
158 * Start a process.
159 *
160 * @param filename name of the binary
161 * @param argv NULL-terminated list of arguments to the process
162 * @return process ID of the new process, -1 on error
163 */
164pid_t
165GNUNET_OS_start_process_v (const char *filename, char *const argv[])
166{
167 pid_t ret;
168
169 ret = fork ();
170 if (ret != 0)
171 {
172 if (ret == -1)
173 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
174 return ret;
175 }
176 execvp (filename, argv);
177 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
178 exit (1);
179}
180
181
182
183
184
185
186/* end of os_priority.c */
diff --git a/src/util/perf_crypto_hash.c b/src/util/perf_crypto_hash.c
new file mode 100644
index 000000000..a6f6ba340
--- /dev/null
+++ b/src/util/perf_crypto_hash.c
@@ -0,0 +1,64 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @author Christian Grothoff
23 * @file util/perf_crypto_hash.c
24 * @brief measure performance of hash function
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_time_lib.h"
30
31static void
32perfHash ()
33{
34 GNUNET_HashCode hc1;
35 GNUNET_HashCode hc2;
36 GNUNET_HashCode hc3;
37 int i;
38 char *buf;
39
40 buf = GNUNET_malloc (1024 * 64);
41 memset (buf, 1, 1024 * 64);
42 GNUNET_CRYPTO_hash ("foo", 3, &hc1);
43 for (i = 0; i < 1024; i++)
44 {
45 GNUNET_CRYPTO_hash (&hc1, sizeof (GNUNET_HashCode), &hc2);
46 GNUNET_CRYPTO_hash (&hc2, sizeof (GNUNET_HashCode), &hc1);
47 GNUNET_CRYPTO_hash (buf, 1024 * 64, &hc3);
48 }
49 GNUNET_free (buf);
50}
51
52int
53main (int argc, char *argv[])
54{
55 struct GNUNET_TIME_Absolute start;
56
57 start = GNUNET_TIME_absolute_get ();
58 perfHash ();
59 printf ("Hash perf took %llu ms\n",
60 GNUNET_TIME_absolute_get_duration (start).value);
61 return 0;
62}
63
64/* end of hashperf.c */
diff --git a/src/util/plugin.c b/src/util/plugin.c
new file mode 100644
index 000000000..d26a343c7
--- /dev/null
+++ b/src/util/plugin.c
@@ -0,0 +1,243 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/plugin.c
23 * @brief Methods to access plugins
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include <libltdl/ltdl.h>
29#include "gnunet_common.h"
30#include "gnunet_os_lib.h"
31#include "gnunet_plugin_lib.h"
32
33/**
34 * Linked list of active plugins.
35 */
36struct PluginList
37{
38 /**
39 * This is a linked list.
40 */
41 struct PluginList *next;
42
43 /**
44 * Name of the library.
45 */
46 char *name;
47
48 /**
49 * System handle.
50 */
51 void *handle;
52};
53
54
55/**
56 * Libtool search path before we started.
57 */
58static char *old_dlsearchpath;
59
60
61/**
62 * List of plugins we have loaded.
63 */
64static struct PluginList *plugins;
65
66
67/**
68 * Setup libtool paths.
69 */
70void __attribute__ ((constructor)) GNUNET_PLUGIN_init ()
71{
72 int err;
73 const char *opath;
74 char *path;
75 char *cpath;
76
77#ifdef MINGW
78 InitWinEnv (NULL);
79#endif
80
81 err = lt_dlinit ();
82 if (err > 0)
83 {
84 fprintf (stderr,
85 _("Initialization of plugin mechanism failed: %s!\n"),
86 lt_dlerror ());
87 return;
88 }
89 opath = lt_dlgetsearchpath ();
90 if (opath != NULL)
91 old_dlsearchpath = GNUNET_strdup (opath);
92 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
93 if (path != NULL)
94 {
95 if (opath != NULL)
96 {
97 cpath = GNUNET_malloc (strlen (path) + strlen (opath) + 4);
98 strcpy (cpath, opath);
99 strcat (cpath, ":");
100 strcat (cpath, path);
101 lt_dlsetsearchpath (cpath);
102 GNUNET_free (path);
103 GNUNET_free (cpath);
104 }
105 else
106 {
107 lt_dlsetsearchpath (path);
108 GNUNET_free (path);
109 }
110 }
111}
112
113
114/**
115 * Shutdown libtool.
116 */
117void __attribute__ ((destructor)) GNUNET_PLUGIN_fini ()
118{
119 lt_dlsetsearchpath (old_dlsearchpath);
120 if (old_dlsearchpath != NULL)
121 {
122 GNUNET_free (old_dlsearchpath);
123 old_dlsearchpath = NULL;
124 }
125
126#ifdef MINGW
127 ShutdownWinEnv ();
128#endif
129
130 lt_dlexit ();
131}
132
133
134/**
135 * Lookup a function in the plugin.
136 */
137static GNUNET_PLUGIN_Callback
138resolve_function (struct PluginList *plug, const char *name)
139{
140 char *initName;
141 void *mptr;
142
143 GNUNET_asprintf (&initName, "_%s_%s", plug->name, name);
144 mptr = lt_dlsym (plug->handle, &initName[1]);
145 if (mptr == NULL)
146 mptr = lt_dlsym (plug->handle, initName);
147 if (mptr == NULL)
148 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149 _("`%s' failed to resolve method '%s' with error: %s\n"),
150 "lt_dlsym", &initName[1], lt_dlerror ());
151 GNUNET_free (initName);
152 return mptr;
153}
154
155
156/**
157 * Setup plugin (runs the "init" callback and returns whatever "init"
158 * returned). If "init" returns NULL, the plugin is unloaded.
159 *
160 * Note that the library must export symbols called
161 * "library_name_init" and "library_name_done". These will be called
162 * when the library is loaded and unloaded respectively.
163 *
164 * @param library_name name of the plugin to load
165 * @param arg argument to the plugin initialization function
166 * @return whatever the initialization function returned
167 */
168void *
169GNUNET_PLUGIN_load (const char *library_name, void *arg)
170{
171 void *libhandle;
172 struct PluginList *plug;
173 GNUNET_PLUGIN_Callback init;
174 void *ret;
175
176 libhandle = lt_dlopenext (library_name);
177 if (libhandle == NULL)
178 {
179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180 _("`%s' failed for library `%s' with error: %s\n"),
181 "lt_dlopenext", library_name, lt_dlerror ());
182 return NULL;
183 }
184 plug = GNUNET_malloc (sizeof (struct PluginList));
185 plug->handle = libhandle;
186 plug->name = GNUNET_strdup (library_name);
187 plug->next = plugins;
188 plugins = plug;
189 init = resolve_function (plug, "init");
190 if ((init == NULL) || (NULL == (ret = init (arg))))
191 {
192 GNUNET_free (plug->name);
193 plugins = plug->next;
194 GNUNET_free (plug);
195 return NULL;
196 }
197 return ret;
198}
199
200
201/**
202 * Unload plugin (runs the "done" callback and returns whatever "done"
203 * returned). The plugin is then unloaded.
204 *
205 * @param library_name name of the plugin to unload
206 * @param arg argument to the plugin shutdown function
207 * @return whatever the shutdown function returned
208 */
209void *
210GNUNET_PLUGIN_unload (const char *library_name, void *arg)
211{
212 struct PluginList *pos;
213 struct PluginList *prev;
214 GNUNET_PLUGIN_Callback done;
215 void *ret;
216
217 prev = NULL;
218 pos = plugins;
219 while ((pos != NULL) && (0 != strcmp (pos->name, library_name)))
220 {
221 prev = pos;
222 pos = pos->next;
223 }
224 if (pos == NULL)
225 return NULL;
226
227 done = resolve_function (pos, "done");
228 ret = NULL;
229 if (done != NULL)
230 ret = done (arg);
231 if (prev == NULL)
232 plugins = pos->next;
233 else
234 prev->next = pos->next;
235 // lt_dlclose (pos->handle);
236 GNUNET_free (pos->name);
237 GNUNET_free (pos);
238 return ret;
239}
240
241
242
243/* end of plugin.c */
diff --git a/src/util/program.c b/src/util/program.c
new file mode 100644
index 000000000..c8ebfc4eb
--- /dev/null
+++ b/src/util/program.c
@@ -0,0 +1,202 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/program.c
23 * @brief standard code for GNUnet startup and shutdown
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_configuration_lib.h"
30#include "gnunet_crypto_lib.h"
31#include "gnunet_directories.h"
32#include "gnunet_getopt_lib.h"
33#include "gnunet_os_lib.h"
34#include "gnunet_program_lib.h"
35#include "gnunet_scheduler_lib.h"
36#include <gcrypt.h>
37
38/**
39 * Context for the command.
40 */
41struct CommandContext
42{
43 /**
44 * Argv argument.
45 */
46 char *const *args;
47
48 /**
49 * Name of the configuration file used, can be NULL!
50 */
51 char *cfgfile;
52
53 /**
54 * Main function to run.
55 */
56 GNUNET_PROGRAM_Main task;
57
58 /**
59 * Closure for task.
60 */
61 void *task_cls;
62
63 /**
64 * Configuration to use.
65 */
66 struct GNUNET_CONFIGURATION_Handle *cfg;
67
68};
69
70
71/**
72 * Initial task called by the scheduler for each
73 * program. Runs the program-specific main task.
74 */
75static void
76program_main (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
77{
78 struct CommandContext *cc = cls;
79
80 cc->task (cc->task_cls, tc->sched, cc->args, cc->cfgfile, cc->cfg);
81}
82
83
84/**
85 * Compare function for 'qsort' to sort command-line arguments by the
86 * short option.
87 */
88static int
89cmd_sorter (const void *a1, const void *a2)
90{
91 const struct GNUNET_GETOPT_CommandLineOption *c1 = a1;
92 const struct GNUNET_GETOPT_CommandLineOption *c2 = a2;
93 if (toupper (c1->shortName) > toupper (c2->shortName))
94 return 1;
95 if (toupper (c1->shortName) < toupper (c2->shortName))
96 return -1;
97 if (c1->shortName > c2->shortName)
98 return 1;
99 if (c1->shortName < c2->shortName)
100 return -1;
101 return 0;
102}
103
104
105/**
106 * Run a standard GNUnet command startup sequence (initialize loggers
107 * and configuration, parse options).
108 *
109 * @param argc number of command line arguments
110 * @param argv command line arguments
111 * @param binaryName our expected name
112 * @param options command line options
113 * @param task main function to run
114 * @param task_cls closure for task
115 * @return GNUNET_SYSERR on error, GNUNET_OK on success
116 */
117int
118GNUNET_PROGRAM_run (int argc,
119 char *const *argv,
120 const char *binaryName,
121 const char *binaryHelp,
122 const struct GNUNET_GETOPT_CommandLineOption *options,
123 GNUNET_PROGRAM_Main task, void *task_cls)
124{
125 struct CommandContext cc;
126 char *path;
127 char *loglev;
128 int ret;
129 unsigned int cnt;
130 struct GNUNET_GETOPT_CommandLineOption defoptions[] = {
131 GNUNET_GETOPT_OPTION_CFG_FILE (&cc.cfgfile),
132 GNUNET_GETOPT_OPTION_HELP (binaryHelp),
133 GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev),
134 GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION)
135 };
136 struct GNUNET_GETOPT_CommandLineOption *allopts;
137
138 memset (&cc, 0, sizeof (cc));
139 loglev = NULL;
140 cc.task = task;
141 cc.task_cls = task_cls;
142 cc.cfg = GNUNET_CONFIGURATION_create ();
143
144 /* prepare */
145#if ENABLE_NLS
146 setlocale (LC_ALL, "");
147 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
148 if (path != NULL)
149 {
150 BINDTEXTDOMAIN ("GNUnet", path);
151 GNUNET_free (path);
152 }
153 textdomain ("GNUnet");
154#endif
155 cnt = 0;
156 while (options[cnt].name != NULL)
157 cnt++;
158 allopts =
159 GNUNET_malloc ((cnt +
160 1) * sizeof (struct GNUNET_GETOPT_CommandLineOption) +
161 sizeof (defoptions));
162 memcpy (allopts, defoptions, sizeof (defoptions));
163 memcpy (&allopts
164 [sizeof (defoptions) /
165 sizeof (struct GNUNET_GETOPT_CommandLineOption)], options,
166 (cnt + 1) * sizeof (struct GNUNET_GETOPT_CommandLineOption));
167 cnt +=
168 sizeof (defoptions) / sizeof (struct GNUNET_GETOPT_CommandLineOption);
169 qsort (allopts, cnt, sizeof (struct GNUNET_GETOPT_CommandLineOption),
170 &cmd_sorter);
171 loglev = GNUNET_strdup ("WARNING");
172 if ((-1 == (ret = GNUNET_GETOPT_run (binaryName,
173 cc.cfg,
174 allopts,
175 (unsigned int) argc, argv))) ||
176 ((GNUNET_OK !=
177 GNUNET_log_setup (binaryName,
178 loglev,
179 NULL)) ||
180 (GNUNET_OK != GNUNET_CONFIGURATION_load (cc.cfg, cc.cfgfile))))
181
182 {
183 GNUNET_free_non_null (cc.cfgfile);
184 GNUNET_free (loglev);
185 GNUNET_free (allopts);
186 return GNUNET_SYSERR;
187 }
188 GNUNET_free (allopts);
189
190 /* run */
191 cc.args = &argv[ret];
192 GNUNET_SCHEDULER_run (&program_main, &cc);
193
194 /* clean up */
195 GNUNET_CONFIGURATION_destroy (cc.cfg);
196 GNUNET_free_non_null (cc.cfgfile);
197 GNUNET_free (loglev);
198 return GNUNET_OK;
199}
200
201
202/* end of program.c */
diff --git a/src/util/pseudonym.c b/src/util/pseudonym.c
new file mode 100644
index 000000000..6d9146613
--- /dev/null
+++ b/src/util/pseudonym.c
@@ -0,0 +1,545 @@
1/*
2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/pseudonym.c
23 * @brief helper functions
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30#include "gnunet_disk_lib.h"
31#include "gnunet_pseudonym_lib.h"
32
33#define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/metadata" DIR_SEPARATOR_STR
34#define PS_NAMES_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/names" DIR_SEPARATOR_STR
35
36struct DiscoveryCallback
37{
38 struct DiscoveryCallback *next;
39 GNUNET_PSEUDONYM_Iterator callback;
40 void *closure;
41};
42
43static struct DiscoveryCallback *head;
44
45/**
46 * Internal notification about new tracked URI.
47 */
48static void
49internal_notify (const GNUNET_HashCode * id,
50 const struct GNUNET_CONTAINER_MetaData *md, int rating)
51{
52 struct DiscoveryCallback *pos;
53
54 pos = head;
55 while (pos != NULL)
56 {
57 pos->callback (pos->closure, id, md, rating);
58 pos = pos->next;
59 }
60}
61
62/**
63 * Register callback to be invoked whenever we discover
64 * a new pseudonym.
65 */
66int
67GNUNET_PSEUDONYM_discovery_callback_register (struct
68 GNUNET_CONFIGURATION_Handle
69 *cfg,
70 GNUNET_PSEUDONYM_Iterator
71 iterator, void *closure)
72{
73 struct DiscoveryCallback *list;
74
75 list = GNUNET_malloc (sizeof (struct DiscoveryCallback));
76 list->callback = iterator;
77 list->closure = closure;
78 list->next = head;
79 head = list;
80 GNUNET_PSEUDONYM_list_all (cfg, iterator, closure);
81 return GNUNET_OK;
82}
83
84/**
85 * Unregister pseudonym discovery callback.
86 */
87int
88GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
89 iterator, void *closure)
90{
91 struct DiscoveryCallback *prev;
92 struct DiscoveryCallback *pos;
93
94 prev = NULL;
95 pos = head;
96 while ((pos != NULL) &&
97 ((pos->callback != iterator) || (pos->closure != closure)))
98 {
99 prev = pos;
100 pos = pos->next;
101 }
102 if (pos == NULL)
103 return GNUNET_SYSERR;
104 if (prev == NULL)
105 head = pos->next;
106 else
107 prev->next = pos->next;
108 GNUNET_free (pos);
109 return GNUNET_OK;
110}
111
112
113/**
114 * Get the filename (or directory name) for the given
115 * pseudonym identifier and directory prefix.
116 */
117static char *
118get_data_filename (struct GNUNET_CONFIGURATION_Handle
119 *cfg, const char *prefix, const GNUNET_HashCode * psid)
120{
121 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
122
123 if (psid != NULL)
124 GNUNET_CRYPTO_hash_to_enc (psid, &enc);
125 return GNUNET_DISK_get_home_filename (cfg,
126 GNUNET_CLIENT_SERVICE_NAME,
127 prefix,
128 (psid ==
129 NULL) ? NULL : (const char *) &enc,
130 NULL);
131}
132
133static void
134write_pseudonym_info (struct GNUNET_CONFIGURATION_Handle *cfg,
135 const GNUNET_HashCode * nsid,
136 const struct GNUNET_CONTAINER_MetaData *meta,
137 int32_t ranking, const char *ns_name)
138{
139 unsigned int size;
140 unsigned int tag;
141 unsigned int off;
142 char *buf;
143 char *fn;
144
145 fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
146 GNUNET_assert (fn != NULL);
147 size = GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
148 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
149 tag = size + sizeof (int) + 1;
150 off = 0;
151 if (ns_name != NULL)
152 {
153 off = strlen (ns_name);
154 tag += off;
155 }
156 buf = GNUNET_malloc (tag);
157 ((int *) buf)[0] = htonl (ranking); /* ranking */
158 if (ns_name != NULL)
159 {
160 memcpy (&buf[sizeof (int)], ns_name, off + 1);
161 }
162 else
163 {
164 buf[sizeof (int)] = '\0';
165 }
166 GNUNET_assert
167 (size == GNUNET_CONTAINER_meta_data_serialize (meta,
168 &buf[sizeof
169 (int) +
170 off + 1],
171 size,
172 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL));
173 GNUNET_DISK_file_write (fn, buf, tag, "660");
174 GNUNET_free (fn);
175 GNUNET_free (buf);
176 /* create entry for pseudonym name in names */
177 GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
178}
179
180static int
181read_info (struct GNUNET_CONFIGURATION_Handle *cfg,
182 const GNUNET_HashCode * nsid,
183 struct GNUNET_CONTAINER_MetaData **meta,
184 int32_t * ranking, char **ns_name)
185{
186 unsigned long long len;
187 unsigned int size;
188 unsigned int zend;
189 struct stat sbuf;
190 char *buf;
191 char *fn;
192
193 if (meta != NULL)
194 *meta = NULL;
195 if (ns_name != NULL)
196 *ns_name = NULL;
197 fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
198 GNUNET_assert (fn != NULL);
199
200 if ((0 != STAT (fn, &sbuf))
201 || (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES)))
202 {
203 GNUNET_free (fn);
204 return GNUNET_SYSERR;
205 }
206 if (len <= sizeof (int) + 1)
207 {
208 GNUNET_free (fn);
209 return GNUNET_SYSERR;
210 }
211 if (len > 16 * 1024 * 1024)
212 {
213 /* too big, must be invalid! remove! */
214 GNUNET_break (0);
215 if (0 != UNLINK (fn))
216 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
217 GNUNET_free (fn);
218 return GNUNET_SYSERR;
219 }
220 buf = GNUNET_malloc (len);
221 if (len != GNUNET_DISK_file_read (fn, len, buf))
222 {
223 GNUNET_free (buf);
224 GNUNET_free (fn);
225 return GNUNET_SYSERR;
226 }
227 if (ranking != NULL)
228 *ranking = ntohl (((int *) buf)[0]);
229 zend = sizeof (int);
230 while ((zend < len) && (buf[zend] != '\0'))
231 zend++;
232 if (zend == len)
233 {
234 GNUNET_free (buf);
235 GNUNET_free (fn);
236 return GNUNET_SYSERR;
237 }
238 if (ns_name != NULL)
239 {
240 if (zend != sizeof (int))
241 *ns_name = GNUNET_strdup (&buf[sizeof (int)]);
242 else
243 *ns_name = NULL;
244 }
245 zend++;
246 size = len - zend;
247 if (meta != NULL)
248 {
249 *meta = GNUNET_CONTAINER_meta_data_deserialize (&buf[zend], size);
250 if ((*meta) == NULL)
251 {
252 /* invalid data! remove! */
253 GNUNET_break (0);
254 if (0 != UNLINK (fn))
255 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
256 "unlink", fn);
257 GNUNET_free (buf);
258 GNUNET_free (fn);
259 return GNUNET_SYSERR;
260 }
261 }
262 GNUNET_free (fn);
263 GNUNET_free (buf);
264 return GNUNET_OK;
265}
266
267
268
269/**
270 * Return the unique, human readable name for the given namespace.
271 *
272 * @return NULL on failure (should never happen)
273 */
274char *
275GNUNET_PSEUDONYM_id_to_name (struct GNUNET_CONFIGURATION_Handle *cfg,
276 const GNUNET_HashCode * nsid)
277{
278 struct GNUNET_CONTAINER_MetaData *meta;
279 char *name;
280 GNUNET_HashCode nh;
281 char *fn;
282 unsigned long long len;
283 int fd;
284 unsigned int i;
285 unsigned int idx;
286 char *ret;
287 struct stat sbuf;
288
289 meta = NULL;
290 name = NULL;
291 if (GNUNET_OK == read_info (cfg, nsid, &meta, NULL, &name))
292 {
293 if ((meta != NULL) && (name == NULL))
294 name = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
295 EXTRACTOR_TITLE,
296 EXTRACTOR_FILENAME,
297 EXTRACTOR_DESCRIPTION,
298 EXTRACTOR_SUBJECT,
299 EXTRACTOR_PUBLISHER,
300 EXTRACTOR_AUTHOR,
301 EXTRACTOR_COMMENT,
302 EXTRACTOR_SUMMARY,
303 EXTRACTOR_OWNER,
304 -1);
305 if (meta != NULL)
306 {
307 GNUNET_CONTAINER_meta_data_destroy (meta);
308 meta = NULL;
309 }
310 }
311 if (name == NULL)
312 name = GNUNET_strdup (_("no-name"));
313 GNUNET_CRYPTO_hash (name, strlen (name), &nh);
314 fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
315 GNUNET_assert (fn != NULL);
316
317 len = 0;
318 if (0 == STAT (fn, &sbuf))
319 GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
320 fd = GNUNET_DISK_file_open (fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
321 i = 0;
322 idx = -1;
323 while ((len >= sizeof (GNUNET_HashCode)) &&
324 (sizeof (GNUNET_HashCode)
325 == READ (fd, &nh, sizeof (GNUNET_HashCode))))
326 {
327 if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
328 {
329 idx = i;
330 break;
331 }
332 i++;
333 len -= sizeof (GNUNET_HashCode);
334 }
335 if (idx == -1)
336 {
337 idx = i;
338 if (sizeof (GNUNET_HashCode) !=
339 WRITE (fd, nsid, sizeof (GNUNET_HashCode)))
340 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
341 }
342 CLOSE (fd);
343 ret = GNUNET_malloc (strlen (name) + 32);
344 GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
345 GNUNET_free (name);
346 GNUNET_free (fn);
347 return ret;
348}
349
350/**
351 * Get the namespace ID belonging to the given namespace name.
352 *
353 * @return GNUNET_OK on success
354 */
355int
356GNUNET_PSEUDONYM_name_to_id (struct GNUNET_CONFIGURATION_Handle *cfg,
357 const char *ns_uname, GNUNET_HashCode * nsid)
358{
359 size_t slen;
360 unsigned long long len;
361 unsigned int idx;
362 char *name;
363 GNUNET_HashCode nh;
364 char *fn;
365 int fd;
366
367 idx = -1;
368 slen = strlen (ns_uname);
369 while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
370 slen--;
371 if (slen == 0)
372 return GNUNET_SYSERR;
373 name = GNUNET_strdup (ns_uname);
374 name[slen - 1] = '\0';
375 GNUNET_CRYPTO_hash (name, strlen (name), &nh);
376 GNUNET_free (name);
377 fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
378 GNUNET_assert (fn != NULL);
379
380 if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
381 (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
382 ((idx + 1) * sizeof (GNUNET_HashCode) > len))
383 {
384 GNUNET_free (fn);
385 return GNUNET_SYSERR;
386 }
387 fd = GNUNET_DISK_file_open (fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
388 GNUNET_free (fn);
389 LSEEK (fd, idx * sizeof (GNUNET_HashCode), SEEK_SET);
390 if (sizeof (GNUNET_HashCode) != READ (fd, nsid, sizeof (GNUNET_HashCode)))
391 {
392 CLOSE (fd);
393 return GNUNET_SYSERR;
394 }
395 CLOSE (fd);
396 return GNUNET_OK;
397}
398
399
400
401
402struct ListPseudonymClosure
403{
404 GNUNET_PSEUDONYM_Iterator iterator;
405 void *closure;
406 struct GNUNET_CONFIGURATION_Handle *cfg;
407};
408
409static int
410list_pseudonym_helper (void *cls, const char *fullname)
411{
412 struct ListPseudonymClosure *c = cls;
413 int ret;
414 GNUNET_HashCode id;
415 int rating;
416 struct GNUNET_CONTAINER_MetaData *meta;
417 const char *fn;
418
419 if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
420 return GNUNET_OK;
421 fn =
422 &fullname[strlen (fullname) + 1 -
423 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
424 if (fn[-1] != DIR_SEPARATOR)
425 return GNUNET_OK;
426 ret = GNUNET_OK;
427 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
428 return GNUNET_OK; /* invalid name */
429 if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, NULL))
430 return GNUNET_OK; /* ignore entry */
431 if (c->iterator != NULL)
432 ret = c->iterator (c->closure, &id, meta, rating);
433 GNUNET_CONTAINER_meta_data_destroy (meta);
434 return ret;
435}
436
437/**
438 * List all available pseudonyms.
439 */
440int
441GNUNET_PSEUDONYM_list_all (struct GNUNET_CONFIGURATION_Handle *cfg,
442 GNUNET_PSEUDONYM_Iterator iterator, void *closure)
443{
444 struct ListPseudonymClosure cls;
445 char *fn;
446 int ret;
447
448 cls.iterator = iterator;
449 cls.closure = closure;
450 cls.cfg = cfg;
451 fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
452 GNUNET_assert (fn != NULL);
453 GNUNET_DISK_directory_create (fn);
454 ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
455 GNUNET_free (fn);
456 return ret;
457}
458
459/**
460 * Change the ranking of a pseudonym.
461 *
462 * @param nsid id of the pseudonym
463 * @param delta by how much should the rating be
464 * changed?
465 * @return new rating of the pseudonym
466 */
467int
468GNUNET_PSEUDONYM_rank (struct GNUNET_CONFIGURATION_Handle *cfg,
469 const GNUNET_HashCode * nsid, int delta)
470{
471 struct GNUNET_CONTAINER_MetaData *meta;
472 int ret;
473 int32_t ranking;
474 char *name;
475
476 name = NULL;
477 ret = read_info (cfg, nsid, &meta, &ranking, &name);
478 if (ret == GNUNET_SYSERR)
479 {
480 ranking = 0;
481 meta = GNUNET_CONTAINER_meta_data_create ();
482 }
483 ranking += delta;
484 write_pseudonym_info (cfg, nsid, meta, ranking, name);
485 GNUNET_CONTAINER_meta_data_destroy (meta);
486 GNUNET_free_non_null (name);
487 return ranking;
488}
489
490/**
491 * Insert metadata into existing MD record (passed as cls).
492 */
493static int
494merge_meta_helper (EXTRACTOR_KeywordType type, const char *data, void *cls)
495{
496 struct GNUNET_CONTAINER_MetaData *meta = cls;
497 GNUNET_CONTAINER_meta_data_insert (meta, type, data);
498 return GNUNET_OK;
499}
500
501
502
503/**
504 * Add a pseudonym to the set of known pseudonyms.
505 * For all pseudonym advertisements that we discover
506 * FSUI should automatically call this function.
507 *
508 * @param id the pseudonym identifier
509 */
510void
511GNUNET_PSEUDONYM_add (struct GNUNET_CONFIGURATION_Handle *cfg,
512 const GNUNET_HashCode * id,
513 const struct GNUNET_CONTAINER_MetaData *meta)
514{
515 char *name;
516 int32_t ranking;
517 struct GNUNET_CONTAINER_MetaData *old;
518 char *fn;
519 struct stat sbuf;
520
521 ranking = 0;
522 fn = get_data_filename (cfg, PS_METADATA_DIR, id);
523 GNUNET_assert (fn != NULL);
524
525 if ((0 == STAT (fn, &sbuf)) &&
526 (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
527 {
528 GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old);
529 write_pseudonym_info (cfg, id, old, ranking, name);
530 GNUNET_CONTAINER_meta_data_destroy (old);
531 GNUNET_free_non_null (name);
532 }
533 else
534 {
535 write_pseudonym_info (cfg, id, meta, ranking, NULL);
536 }
537 GNUNET_free (fn);
538 internal_notify (id, meta, ranking);
539}
540
541
542
543
544
545/* end of pseudonym.c */
diff --git a/src/util/scheduler.c b/src/util/scheduler.c
new file mode 100644
index 000000000..0d30910e0
--- /dev/null
+++ b/src/util/scheduler.c
@@ -0,0 +1,886 @@
1/*
2 This file is part of GNUnet
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @file util/scheduler/scheduler.c
23 * @brief schedule computations using continuation passing style
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_signal_lib.h"
30#include "gnunet_time_lib.h"
31
32/**
33 * Linked list of pending tasks.
34 */
35struct Task
36{
37 /**
38 * This is a linked list.
39 */
40 struct Task *next;
41
42 /**
43 * Function to run when ready.
44 */
45 GNUNET_SCHEDULER_Task callback;
46
47 /**
48 * Closure for the callback.
49 */
50 void *callback_cls;
51
52 /**
53 * Set of file descriptors this task is waiting
54 * for for reading. Once ready, this is updated
55 * to reflect the set of file descriptors ready
56 * for operation.
57 */
58 fd_set read_set;
59
60 /**
61 * Set of file descriptors this task is waiting
62 * for for writing. Once ready, this is updated
63 * to reflect the set of file descriptors ready
64 * for operation.
65 */
66 fd_set write_set;
67
68 /**
69 * Unique task identifier.
70 */
71 GNUNET_SCHEDULER_TaskIdentifier id;
72
73 /**
74 * Identifier of a prerequisite task.
75 */
76 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
77
78 /**
79 * Absolute timeout value for the task, or
80 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
81 */
82 struct GNUNET_TIME_Absolute timeout;
83
84 /**
85 * Why is the task ready? Set after task is added to ready queue.
86 * Initially set to zero. All reasons that have already been
87 * satisfied (i.e. read or write ready) will be set over time.
88 */
89 enum GNUNET_SCHEDULER_Reason reason;
90
91 /**
92 * Task priority.
93 */
94 enum GNUNET_SCHEDULER_Priority priority;
95
96 /**
97 * highest-numbered file descriptor in read_set or write_set plus one
98 */
99 int nfds;
100
101 /**
102 * Should this task be run on shutdown?
103 */
104 int run_on_shutdown;
105
106};
107
108
109/**
110 * Handle for the scheduling service.
111 */
112struct GNUNET_SCHEDULER_Handle
113{
114
115 /**
116 * List of tasks waiting for an event.
117 */
118 struct Task *pending;
119
120 /**
121 * List of tasks ready to run right now,
122 * grouped by importance.
123 */
124 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
125
126 /**
127 * Identity of the last task queued. Incremented for each task to
128 * generate a unique task ID (it is virtually impossible to start
129 * more than 2^64 tasks during the lifetime of a process).
130 */
131 GNUNET_SCHEDULER_TaskIdentifier last_id;
132
133 /**
134 * Highest number so that all tasks with smaller identifiers
135 * have already completed. Also the lowest number of a task
136 * still waiting to be executed.
137 */
138 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
139
140 /**
141 * GNUNET_NO if we are running normally,
142 * GNUNET_YES if we are in shutdown mode.
143 */
144 int shutdown;
145
146 /**
147 * Number of tasks on the ready list.
148 */
149 unsigned int ready_count;
150
151 /**
152 * Priority of the task running right now. Only
153 * valid while a task is running.
154 */
155 enum GNUNET_SCHEDULER_Priority current_priority;
156
157};
158
159
160/**
161 * Check that the given priority is legal (and return it).
162 */
163static enum GNUNET_SCHEDULER_Priority
164check_priority (enum GNUNET_SCHEDULER_Priority p)
165{
166 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
167 return p;
168 GNUNET_assert (0);
169 return 0; /* make compiler happy */
170}
171
172
173/**
174 * Update the timeout value so that it is smaller than min.
175 */
176static void
177update_timeout (struct timeval *tv, struct GNUNET_TIME_Relative min)
178{
179 if (((tv->tv_sec * 1000) + (tv->tv_usec / 1000)) > min.value)
180 {
181 tv->tv_sec = min.value / 1000;
182 tv->tv_usec = (min.value - tv->tv_sec * 1000) * 1000;
183 }
184}
185
186
187/**
188 * Set the given file descriptor bit in the given set and update max
189 * to the maximum of the existing max and fd+1.
190 */
191static void
192set_fd (int fd, int *max, fd_set * set)
193{
194 if (*max <= fd)
195 *max = fd + 1;
196 FD_SET (fd, set);
197}
198
199
200/**
201 * Is a task with this identifier still pending? Also updates
202 * "lowest_pending_id" as a side-effect (for faster checks in the
203 * future), but only if the return value is "GNUNET_NO" (and
204 * the "lowest_pending_id" check failed).
205 *
206 * @return GNUNET_YES if so, GNUNET_NO if not
207 */
208static int
209is_pending (struct GNUNET_SCHEDULER_Handle *sched,
210 GNUNET_SCHEDULER_TaskIdentifier id)
211{
212 struct Task *pos;
213 enum GNUNET_SCHEDULER_Priority p;
214 GNUNET_SCHEDULER_TaskIdentifier min;
215
216 if (id < sched->lowest_pending_id)
217 return GNUNET_NO;
218 min = -1; /* maximum value */
219 pos = sched->pending;
220 while (pos != NULL)
221 {
222 if (pos->id == id)
223 return GNUNET_YES;
224 if (pos->id < min)
225 min = pos->id;
226 pos = pos->next;
227 }
228 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
229 {
230 pos = sched->ready[p];
231 while (pos != NULL)
232 {
233 if (pos->id == id)
234 return GNUNET_YES;
235 if (pos->id < min)
236 min = pos->id;
237 pos = pos->next;
238 }
239 }
240 sched->lowest_pending_id = min;
241 return GNUNET_NO;
242}
243
244
245/**
246 * Update all sets and timeout for select.
247 */
248static void
249update_sets (struct GNUNET_SCHEDULER_Handle *sched,
250 int *max, fd_set * rs, fd_set * ws, struct timeval *tv)
251{
252 int i;
253 struct Task *pos;
254
255 pos = sched->pending;
256 while (pos != NULL)
257 {
258 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) &&
259 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
260 {
261 pos = pos->next;
262 continue;
263 }
264
265 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
266 update_timeout (tv,
267 GNUNET_TIME_absolute_get_remaining (pos->timeout));
268 for (i = 0; i < pos->nfds; i++)
269 {
270 if (FD_ISSET (i, &pos->read_set))
271 set_fd (i, max, rs);
272 if (FD_ISSET (i, &pos->write_set))
273 set_fd (i, max, ws);
274 }
275 pos = pos->next;
276 }
277}
278
279
280/**
281 * Check if the ready set overlaps with the set we want to have ready.
282 * If so, update the want set (set all FDs that are ready). If not,
283 * return GNUNET_NO.
284 *
285 * @param maxfd highest FD that needs to be checked.
286 * @return GNUNET_YES if there was some overlap
287 */
288static int
289set_overlaps (const fd_set * ready, fd_set * want, int maxfd)
290{
291 int i;
292
293 for (i = 0; i < maxfd; i++)
294 if (FD_ISSET (i, want) && FD_ISSET (i, ready))
295 {
296 /* copy all over (yes, there maybe unrelated bits,
297 but this should not hurt well-written clients) */
298 memcpy (want, ready, sizeof (fd_set));
299 return GNUNET_YES;
300 }
301 return GNUNET_NO;
302}
303
304
305/**
306 * Check if the given task is eligible to run now.
307 * Also set the reason why it is eligible.
308 *
309 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
310 */
311static int
312is_ready (struct GNUNET_SCHEDULER_Handle *sched,
313 struct Task *task,
314 struct GNUNET_TIME_Absolute now,
315 const fd_set * rs, const fd_set * ws)
316{
317 if ((GNUNET_NO == task->run_on_shutdown) && (GNUNET_YES == sched->shutdown))
318 return GNUNET_NO;
319 if ((GNUNET_YES == task->run_on_shutdown) &&
320 (GNUNET_YES == sched->shutdown))
321 task->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
322 if (now.value >= task->timeout.value)
323 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
324 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
325 (rs != NULL) && (set_overlaps (rs, &task->read_set, task->nfds)))
326 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
327 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
328 (ws != NULL) && (set_overlaps (ws, &task->write_set, task->nfds)))
329 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
330 if (task->reason == 0)
331 return GNUNET_NO; /* not ready */
332 if (task->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
333 {
334 if (GNUNET_YES == is_pending (sched, task->prereq_id))
335 return GNUNET_NO; /* prereq waiting */
336 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
337 }
338 return GNUNET_YES;
339}
340
341
342/**
343 * Put a task that is ready for execution into the ready queue.
344 */
345static void
346queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
347{
348 task->next = handle->ready[check_priority (task->priority)];
349 handle->ready[check_priority (task->priority)] = task;
350 handle->ready_count++;
351}
352
353
354/**
355 * Check which tasks are ready and move them
356 * to the respective ready queue.
357 */
358static void
359check_ready (struct GNUNET_SCHEDULER_Handle *handle,
360 const fd_set * rs, const fd_set * ws)
361{
362 struct Task *pos;
363 struct Task *prev;
364 struct Task *next;
365 struct GNUNET_TIME_Absolute now;
366
367 now = GNUNET_TIME_absolute_get ();
368 prev = NULL;
369 pos = handle->pending;
370 while (pos != NULL)
371 {
372 next = pos->next;
373 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
374 {
375 if (prev == NULL)
376 handle->pending = next;
377 else
378 prev->next = next;
379 queue_ready_task (handle, pos);
380 pos = next;
381 continue;
382 }
383 prev = pos;
384 pos = next;
385 }
386}
387
388
389/**
390 * Run at least one task in the highest-priority queue that is not
391 * empty. Keep running tasks until we are either no longer running
392 * "URGENT" tasks or until we have at least one "pending" task (which
393 * may become ready, hence we should select on it). Naturally, if
394 * there are no more ready tasks, we also return.
395 */
396static void
397run_ready (struct GNUNET_SCHEDULER_Handle *sched)
398{
399 enum GNUNET_SCHEDULER_Priority p;
400 struct Task *pos;
401 struct GNUNET_SCHEDULER_TaskContext tc;
402
403 do
404 {
405 if (sched->ready_count == 0)
406 return;
407 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
408 /* yes, p>0 is correct, 0 is "KEEP" which should
409 always be an empty queue (see assertion)! */
410 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
411 {
412 pos = sched->ready[p];
413 if (pos != NULL)
414 break;
415 }
416 GNUNET_assert (pos != NULL); /* ready_count wrong? */
417 sched->ready[p] = pos->next;
418 sched->ready_count--;
419 sched->current_priority = p;
420 GNUNET_assert (pos->priority == p);
421 tc.sched = sched;
422 tc.reason = pos->reason;
423 tc.read_ready = &pos->read_set;
424 tc.write_ready = &pos->write_set;
425 pos->callback (pos->callback_cls, &tc);
426 GNUNET_free (pos);
427 }
428 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
429}
430
431
432/**
433 * Have we (ever) received a SIGINT/TERM/QUIT/HUP?
434 */
435static volatile int sig_shutdown;
436
437
438/**
439 * Signal handler called for signals that should cause us to shutdown.
440 */
441static void
442sighandler_shutdown ()
443{
444 sig_shutdown = 1;
445}
446
447
448/**
449 * Initialize a scheduler using this thread. This function will
450 * return when either a shutdown was initiated (via signal) and all
451 * tasks marked to "run_on_shutdown" have been completed or when all
452 * tasks in general have been completed.
453 *
454 * @param task task to run immediately
455 * @param cls closure of task
456 */
457void
458GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls)
459{
460 struct GNUNET_SCHEDULER_Handle sched;
461 fd_set rs;
462 fd_set ws;
463 int max;
464 struct timeval tv;
465 int ret;
466 struct GNUNET_SIGNAL_Context *shc_int;
467 struct GNUNET_SIGNAL_Context *shc_term;
468 struct GNUNET_SIGNAL_Context *shc_quit;
469 struct GNUNET_SIGNAL_Context *shc_hup;
470 struct Task *tpos;
471
472 sig_shutdown = 0;
473 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
474 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
475 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
476 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
477 memset (&sched, 0, sizeof (sched));
478 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
479 GNUNET_SCHEDULER_add_continuation (&sched,
480 GNUNET_YES,
481 task,
482 cls, GNUNET_SCHEDULER_REASON_STARTUP);
483 while ((GNUNET_NO == sched.shutdown) &&
484 (!sig_shutdown) &&
485 ((sched.pending != NULL) || (sched.ready_count > 0)))
486 {
487 FD_ZERO (&rs);
488 FD_ZERO (&ws);
489 max = 0;
490 tv.tv_sec = 0x7FFFFFFF;
491 tv.tv_usec = 0;
492 if (sched.ready_count > 0)
493 {
494 /* no blocking, more work already ready! */
495 tv.tv_sec = 0;
496 tv.tv_usec = 0;
497 }
498 update_sets (&sched, &max, &rs, &ws, &tv);
499 ret = SELECT (max, &rs, &ws, NULL, &tv);
500 if (ret == -1)
501 {
502 if (errno == EINTR)
503 continue;
504 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
505 break;
506 }
507 check_ready (&sched, &rs, &ws);
508 run_ready (&sched);
509 }
510 if (sig_shutdown)
511 sched.shutdown = GNUNET_YES;
512 GNUNET_SIGNAL_handler_uninstall (shc_int);
513 GNUNET_SIGNAL_handler_uninstall (shc_term);
514 GNUNET_SIGNAL_handler_uninstall (shc_quit);
515 GNUNET_SIGNAL_handler_uninstall (shc_hup);
516 do
517 {
518 run_ready (&sched);
519 check_ready (&sched, NULL, NULL);
520 }
521 while (sched.ready_count > 0);
522 while (NULL != (tpos = sched.pending))
523 {
524 sched.pending = tpos->next;
525 GNUNET_free (tpos);
526 }
527}
528
529
530/**
531 * Request the shutdown of a scheduler. This function can be used to
532 * stop a scheduling thread when created with the
533 * "GNUNET_SCHEDULER_init_thread" function or from within the signal
534 * handler for signals causing shutdowns.
535 */
536void
537GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
538{
539 sched->shutdown = GNUNET_YES;
540}
541
542
543/**
544 * Get information about the current load of this scheduler. Use this
545 * function to determine if an elective task should be added or simply
546 * dropped (if the decision should be made based on the number of
547 * tasks ready to run).
548 *
549 * @param sched scheduler to query
550 * @return number of tasks pending right now
551 */
552unsigned int
553GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
554 enum GNUNET_SCHEDULER_Priority p)
555{
556 struct Task *pos;
557 unsigned int ret;
558
559 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
560 return sched->ready_count;
561 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
562 p = sched->current_priority;
563 ret = 0;
564 pos = sched->ready[p];
565 while (pos != NULL)
566 {
567 pos = pos->next;
568 ret++;
569 }
570 return ret;
571}
572
573
574/**
575 * Cancel the task with the specified identifier.
576 * The task must not yet have run.
577 *
578 * @param sched scheduler to use
579 * @param task id of the task to cancel
580 */
581void *
582GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
583 GNUNET_SCHEDULER_TaskIdentifier task)
584{
585 struct Task *t;
586 struct Task *prev;
587 enum GNUNET_SCHEDULER_Priority p;
588 void *ret;
589
590 prev = NULL;
591 t = sched->pending;
592 while (t != NULL)
593 {
594 if (t->id == task)
595 break;
596 prev = t;
597 t = t->next;
598 }
599 p = 0;
600 while (t == NULL)
601 {
602 p++;
603 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
604 prev = NULL;
605 t = sched->ready[p];
606 while (t != NULL)
607 {
608 if (t->id == task)
609 {
610 sched->ready_count--;
611 break;
612 }
613 prev = t;
614 t = t->next;
615 }
616 }
617 if (prev == NULL)
618 {
619 if (p == 0)
620 sched->pending = t->next;
621 else
622 sched->ready[p] = t->next;
623 }
624 else
625 prev->next = t->next;
626 ret = t->callback_cls;
627 GNUNET_free (t);
628 return ret;
629}
630
631
632/**
633 * Continue the current execution with the given function. This is
634 * similar to the other "add" functions except that there is no delay
635 * and the reason code can be specified.
636 *
637 * @param sched scheduler to use
638 * @param main main function of the task
639 * @param cls closure of task
640 * @param reason reason for task invocation
641 */
642void
643GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
644 int run_on_shutdown,
645 GNUNET_SCHEDULER_Task main,
646 void *cls,
647 enum GNUNET_SCHEDULER_Reason reason)
648{
649 struct Task *task;
650
651 task = GNUNET_malloc (sizeof (struct Task));
652 task->callback = main;
653 task->callback_cls = cls;
654 task->id = ++sched->last_id;
655 task->reason = reason;
656 task->priority = sched->current_priority;
657 task->run_on_shutdown = run_on_shutdown;
658 queue_ready_task (sched, task);
659}
660
661
662/**
663 * Schedule a new task to be run after the specified
664 * prerequisite task has completed.
665 *
666 * @param sched scheduler to use
667 * @param run_on_shutdown run on shutdown?
668 * @param prio how important is this task?
669 * @param prerequisite_task run this task after the task with the given
670 * task identifier completes (and any of our other
671 * conditions, such as delay, read or write-readyness
672 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
673 * on completion of other tasks.
674 * @param main main function of the task
675 * @param cls closure of task
676 * @return unique task identifier for the job
677 * only valid until "main" is started!
678 */
679GNUNET_SCHEDULER_TaskIdentifier
680GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
681 int run_on_shutdown,
682 enum GNUNET_SCHEDULER_Priority prio,
683 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
684 GNUNET_SCHEDULER_Task main, void *cls)
685{
686 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
687 prerequisite_task,
688 GNUNET_TIME_UNIT_ZERO,
689 0, NULL, NULL, main, cls);
690}
691
692
693/**
694 * Schedule a new task to be run with a specified delay. The task
695 * will be scheduled for execution once the delay has expired and the
696 * prerequisite task has completed.
697 *
698 * @param sched scheduler to use
699 * @param run_on_shutdown run on shutdown? You can use this
700 * argument to run a function only during shutdown
701 * by setting delay to -1. Set this
702 * argument to GNUNET_NO to skip this task if
703 * the user requested process termination.
704 * @param prio how important is this task?
705 * @param prerequisite_task run this task after the task with the given
706 * task identifier completes (and any of our other
707 * conditions, such as delay, read or write-readyness
708 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
709 * on completion of other tasks.
710 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
711 * @param main main function of the task
712 * @param cls closure of task
713 * @return unique task identifier for the job
714 * only valid until "main" is started!
715 */
716GNUNET_SCHEDULER_TaskIdentifier
717GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
718 int run_on_shutdown,
719 enum GNUNET_SCHEDULER_Priority prio,
720 GNUNET_SCHEDULER_TaskIdentifier
721 prerequisite_task,
722 struct GNUNET_TIME_Relative delay,
723 GNUNET_SCHEDULER_Task main, void *cls)
724{
725 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
726 prerequisite_task, delay,
727 0, NULL, NULL, main, cls);
728}
729
730
731/**
732 * Schedule a new task to be run with a specified delay or when the
733 * specified file descriptor is ready for reading. The delay can be
734 * used as a timeout on the socket being ready. The task will be
735 * scheduled for execution once either the delay has expired or the
736 * socket operation is ready.
737 *
738 * @param sched scheduler to use
739 * @param run_on_shutdown run on shutdown? Set this
740 * argument to GNUNET_NO to skip this task if
741 * the user requested process termination.
742 * @param prio how important is this task?
743 * @param prerequisite_task run this task after the task with the given
744 * task identifier completes (and any of our other
745 * conditions, such as delay, read or write-readyness
746 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
747 * on completion of other tasks.
748 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
749 * @param rfd read file-descriptor
750 * @param main main function of the task
751 * @param cls closure of task
752 * @return unique task identifier for the job
753 * only valid until "main" is started!
754 */
755GNUNET_SCHEDULER_TaskIdentifier
756GNUNET_SCHEDULER_add_read (struct GNUNET_SCHEDULER_Handle * sched,
757 int run_on_shutdown,
758 enum GNUNET_SCHEDULER_Priority prio,
759 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
760 struct GNUNET_TIME_Relative delay,
761 int rfd, GNUNET_SCHEDULER_Task main, void *cls)
762{
763 fd_set rs;
764
765 GNUNET_assert (rfd >= 0);
766 FD_ZERO (&rs);
767 FD_SET (rfd, &rs);
768 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
769 prerequisite_task, delay,
770 rfd + 1, &rs, NULL, main, cls);
771}
772
773
774/**
775 * Schedule a new task to be run with a specified delay or when the
776 * specified file descriptor is ready for writing. The delay can be
777 * used as a timeout on the socket being ready. The task will be
778 * scheduled for execution once either the delay has expired or the
779 * socket operation is ready.
780 *
781 * @param sched scheduler to use
782 * @param run_on_shutdown run on shutdown? Set this
783 * argument to GNUNET_NO to skip this task if
784 * the user requested process termination.
785 * @param prio how important is this task?
786 * @param prerequisite_task run this task after the task with the given
787 * task identifier completes (and any of our other
788 * conditions, such as delay, read or write-readyness
789 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
790 * on completion of other tasks.
791 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
792 * @param wfd write file-descriptor
793 * @param main main function of the task
794 * @param cls closure of task
795 * @return unique task identifier for the job
796 * only valid until "main" is started!
797 */
798GNUNET_SCHEDULER_TaskIdentifier
799GNUNET_SCHEDULER_add_write (struct GNUNET_SCHEDULER_Handle * sched,
800 int run_on_shutdown,
801 enum GNUNET_SCHEDULER_Priority prio,
802 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
803 struct GNUNET_TIME_Relative delay,
804 int wfd, GNUNET_SCHEDULER_Task main, void *cls)
805{
806 fd_set ws;
807
808 GNUNET_assert (wfd >= 0);
809 FD_ZERO (&ws);
810 FD_SET (wfd, &ws);
811 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
812 prerequisite_task, delay,
813 wfd + 1, NULL, &ws, main, cls);
814}
815
816
817/**
818 * Schedule a new task to be run with a specified delay or when any of
819 * the specified file descriptor sets is ready. The delay can be used
820 * as a timeout on the socket(s) being ready. The task will be
821 * scheduled for execution once either the delay has expired or any of
822 * the socket operations is ready. This is the most general
823 * function of the "add" family. Note that the "prerequisite_task"
824 * must be satisfied in addition to any of the other conditions. In
825 * other words, the task will be started when
826 * <code>
827 * (prerequisite-run)
828 * && (delay-ready
829 * || any-rs-ready
830 * || any-ws-ready
831 * || (shutdown-active && run-on-shutdown) )
832 * </code>
833 *
834 * @param sched scheduler to use
835 * @param run_on_shutdown run on shutdown? Set this
836 * argument to GNUNET_NO to skip this task if
837 * the user requested process termination.
838 * @param prio how important is this task?
839 * @param prerequisite_task run this task after the task with the given
840 * task identifier completes (and any of our other
841 * conditions, such as delay, read or write-readyness
842 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
843 * on completion of other tasks.
844 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
845 * @param nfds highest-numbered file descriptor in any of the two sets plus one
846 * @param rs set of file descriptors we want to read (can be NULL)
847 * @param ws set of file descriptors we want to write (can be NULL)
848 * @param main main function of the task
849 * @param cls closure of task
850 * @return unique task identifier for the job
851 * only valid until "main" is started!
852 */
853GNUNET_SCHEDULER_TaskIdentifier
854GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
855 int run_on_shutdown,
856 enum GNUNET_SCHEDULER_Priority prio,
857 GNUNET_SCHEDULER_TaskIdentifier
858 prerequisite_task,
859 struct GNUNET_TIME_Relative delay,
860 int nfds, const fd_set * rs, const fd_set * ws,
861 GNUNET_SCHEDULER_Task main, void *cls)
862{
863 struct Task *task;
864
865 task = GNUNET_malloc (sizeof (struct Task));
866 task->callback = main;
867 task->callback_cls = cls;
868 if ((rs != NULL) && (nfds > 0))
869 memcpy (&task->read_set, rs, sizeof (fd_set));
870 if ((ws != NULL) && (nfds > 0))
871 memcpy (&task->write_set, ws, sizeof (fd_set));
872 task->id = ++sched->last_id;
873 task->prereq_id = prerequisite_task;
874 task->timeout = GNUNET_TIME_relative_to_absolute (delay);
875 task->priority =
876 check_priority ((prio ==
877 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->
878 current_priority : prio);
879 task->nfds = nfds;
880 task->run_on_shutdown = run_on_shutdown;
881 task->next = sched->pending;
882 sched->pending = task;
883 return task->id;
884}
885
886/* end of scheduler.c */
diff --git a/src/util/server.c b/src/util/server.c
new file mode 100644
index 000000000..91bc8cc7a
--- /dev/null
+++ b/src/util/server.c
@@ -0,0 +1,1091 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/server.c
23 * @brief library for building GNUnet network servers
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - fix inefficient memmove in message processing
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_network_lib.h"
33#include "gnunet_scheduler_lib.h"
34#include "gnunet_server_lib.h"
35#include "gnunet_time_lib.h"
36
37/**
38 * List of arrays of message handlers.
39 */
40struct HandlerList
41{
42 /**
43 * This is a linked list.
44 */
45 struct HandlerList *next;
46
47 /**
48 * NULL-terminated array of handlers.
49 */
50 const struct GNUNET_SERVER_MessageHandler *handlers;
51};
52
53
54/**
55 * List of arrays of message handlers.
56 */
57struct NotifyList
58{
59 /**
60 * This is a linked list.
61 */
62 struct NotifyList *next;
63
64 /**
65 * Function to call.
66 */
67 GNUNET_SERVER_DisconnectCallback callback;
68
69 /**
70 * Closure for callback.
71 */
72 void *callback_cls;
73};
74
75
76/**
77 * @brief handle for a server
78 */
79struct GNUNET_SERVER_Handle
80{
81 /**
82 * My scheduler.
83 */
84 struct GNUNET_SCHEDULER_Handle *sched;
85
86 /**
87 * List of handlers for incoming messages.
88 */
89 struct HandlerList *handlers;
90
91 /**
92 * List of our current clients.
93 */
94 struct GNUNET_SERVER_Client *clients;
95
96 /**
97 * Linked list of functions to call on disconnects by clients.
98 */
99 struct NotifyList *disconnect_notify_list;
100
101 /**
102 * Function to call for access control.
103 */
104 GNUNET_NETWORK_AccessCheck access;
105
106 /**
107 * Closure for access.
108 */
109 void *access_cls;
110
111 /**
112 * After how long should an idle connection time
113 * out (on write).
114 */
115 struct GNUNET_TIME_Relative idle_timeout;
116
117 /**
118 * maximum write buffer size for accepted sockets
119 */
120 size_t maxbuf;
121
122 /**
123 * Pipe used to signal shutdown of the server.
124 */
125 int shutpipe[2];
126
127 /**
128 * Socket used to listen for new connections. Set to
129 * "-1" by GNUNET_SERVER_destroy to initiate shutdown.
130 */
131 int listen_socket;
132
133 /**
134 * Set to GNUNET_YES if we are shutting down.
135 */
136 int do_shutdown;
137
138 /**
139 * Do we ignore messages of types that we do not
140 * understand or do we require that a handler
141 * is found (and if not kill the connection)?
142 */
143 int require_found;
144
145};
146
147
148/**
149 * @brief handle for a client of the server
150 */
151struct GNUNET_SERVER_Client
152{
153
154 /**
155 * Size of the buffer for incoming data. Should be
156 * first so we get nice alignment.
157 */
158 char incoming_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
159
160 /**
161 * This is a linked list.
162 */
163 struct GNUNET_SERVER_Client *next;
164
165 /**
166 * Server that this client belongs to.
167 */
168 struct GNUNET_SERVER_Handle *server;
169
170 /**
171 * Client closure for callbacks.
172 */
173 void *client_closure;
174
175 /**
176 * Callback to receive from client.
177 */
178 GNUNET_SERVER_ReceiveCallback receive;
179
180 /**
181 * Callback to cancel receive from client.
182 */
183 GNUNET_SERVER_ReceiveCancelCallback receive_cancel;
184
185 /**
186 * Callback to ask about transmit-ready notification.
187 */
188 GNUNET_SERVER_TransmitReadyCallback notify_transmit_ready;
189
190 /**
191 * Callback to ask about transmit-ready notification.
192 */
193 GNUNET_SERVER_TransmitReadyCancelCallback notify_transmit_ready_cancel;
194
195 /**
196 * Callback to check if client is still valid.
197 */
198 GNUNET_SERVER_CheckCallback check;
199
200 /**
201 * Callback to destroy client.
202 */
203 GNUNET_SERVER_DestroyCallback destroy;
204
205 /**
206 * Side-buffer for incoming data used when processing
207 * is suspended.
208 */
209 char *side_buf;
210
211 /**
212 * Number of bytes in the side buffer.
213 */
214 size_t side_buf_size;
215
216 /**
217 * Last activity on this socket (used to time it out
218 * if reference_count == 0).
219 */
220 struct GNUNET_TIME_Absolute last_activity;
221
222 /**
223 * Current task identifier for the receive call
224 * (or GNUNET_SCHEDULER_NO_PREREQUISITE_TASK for none).
225 */
226 GNUNET_SCHEDULER_TaskIdentifier my_receive;
227
228 /**
229 * How many bytes in the "incoming_buffer" are currently
230 * valid? (starting at offset 0).
231 */
232 size_t receive_pos;
233
234 /**
235 * Number of external entities with a reference to
236 * this client object.
237 */
238 unsigned int reference_count;
239
240 /**
241 * Was processing if incoming messages suspended while
242 * we were still processing data already received?
243 * This is a counter saying how often processing was
244 * suspended (once per handler invoked).
245 */
246 unsigned int suspended;
247
248 /**
249 * Are we currently in the "process_client_buffer" function (and
250 * will hence restart the receive job on exit if suspended == 0 once
251 * we are done?). If this is set, then "receive_done" will
252 * essentially only decrement suspended; if this is not set, then
253 * "receive_done" may need to restart the receive process (either
254 * from the side-buffer or via select/recv).
255 */
256 int in_process_client_buffer;
257
258 /**
259 * We're about to close down this client due to some serious
260 * error.
261 */
262 int shutdown_now;
263
264};
265
266
267/**
268 * Server has been asked to shutdown, free resources.
269 */
270static void
271destroy_server (struct GNUNET_SERVER_Handle *server)
272{
273 struct GNUNET_SERVER_Client *pos;
274 struct HandlerList *hpos;
275 struct NotifyList *npos;
276
277 GNUNET_assert (server->listen_socket == -1);
278 GNUNET_break (0 == CLOSE (server->shutpipe[0]));
279 GNUNET_break (0 == CLOSE (server->shutpipe[1]));
280 while (server->clients != NULL)
281 {
282 pos = server->clients;
283 server->clients = pos->next;
284 pos->server = NULL;
285 }
286 while (NULL != (hpos = server->handlers))
287 {
288 server->handlers = hpos->next;
289 GNUNET_free (hpos);
290 }
291 while (NULL != (npos = server->disconnect_notify_list))
292 {
293 server->disconnect_notify_list = npos->next;
294 GNUNET_free (npos);
295 }
296 GNUNET_free (server);
297}
298
299
300/**
301 * Scheduler says our listen socket is ready.
302 * Process it!
303 */
304static void
305process_listen_socket (void *cls,
306 const struct GNUNET_SCHEDULER_TaskContext *tc)
307{
308 struct GNUNET_SERVER_Handle *server = cls;
309 struct GNUNET_NETWORK_SocketHandle *sock;
310 struct GNUNET_SERVER_Client *client;
311 fd_set r;
312
313 if ((server->do_shutdown) ||
314 ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0))
315 {
316 /* shutdown was initiated */
317 GNUNET_assert (server->listen_socket != -1);
318 GNUNET_break (0 == CLOSE (server->listen_socket));
319 server->listen_socket = -1;
320 if (server->do_shutdown)
321 destroy_server (server);
322 return;
323 }
324 GNUNET_assert (FD_ISSET (server->listen_socket, tc->read_ready));
325 GNUNET_assert (!FD_ISSET (server->shutpipe[0], tc->read_ready));
326 sock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
327 server->access,
328 server->access_cls,
329 server->listen_socket,
330 server->maxbuf);
331 if (sock != NULL)
332 {
333 client = GNUNET_SERVER_connect_socket (server, sock);
334 /* decrement reference count, we don't keep "client" alive */
335 GNUNET_SERVER_client_drop (client);
336 }
337 /* listen for more! */
338 FD_ZERO (&r);
339 FD_SET (server->listen_socket, &r);
340 FD_SET (server->shutpipe[0], &r);
341 GNUNET_SCHEDULER_add_select (server->sched,
342 GNUNET_YES,
343 GNUNET_SCHEDULER_PRIORITY_HIGH,
344 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
345 GNUNET_TIME_UNIT_FOREVER_REL,
346 GNUNET_MAX (server->listen_socket,
347 server->shutpipe[0]) + 1, &r, NULL,
348 &process_listen_socket, server);
349}
350
351
352/**
353 * Create and initialize a listen socket for the server.
354 *
355 * @return -1 on error, otherwise the listen socket
356 */
357static int
358open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
359{
360 const static int on = 1;
361 int fd;
362 uint16_t port;
363
364 switch (serverAddr->sa_family)
365 {
366 case AF_INET:
367 port = ntohs (((const struct sockaddr_in *) serverAddr)->sin_port);
368 break;
369 case AF_INET6:
370 port = ntohs (((const struct sockaddr_in6 *) serverAddr)->sin6_port);
371 break;
372 default:
373 GNUNET_break (0);
374 return -1;
375 }
376 fd = SOCKET (serverAddr->sa_family, SOCK_STREAM, 0);
377 if (fd < 0)
378 {
379 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
380 return -1;
381 }
382 if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC))
383 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
384 "fcntl");
385 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
386 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
387 "setsockopt");
388 /* bind the socket */
389 if (BIND (fd, serverAddr, socklen) < 0)
390 {
391 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
392 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393 _
394 ("`%s' failed for port %d. Is the service already running?\n"),
395 "bind", port);
396 GNUNET_break (0 == CLOSE (fd));
397 return -1;
398 }
399 if (0 != LISTEN (fd, 5))
400 {
401 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
402 GNUNET_break (0 == CLOSE (fd));
403 return -1;
404 }
405 return fd;
406}
407
408
409/**
410 * Create a new server.
411 *
412 * @param sched scheduler to use
413 * @param access function for access control
414 * @param access_cls closure for access
415 * @param serverAddr address to listen on (including port), use NULL
416 * for internal server (no listening)
417 * @param socklen length of serverAddr
418 * @param maxbuf maximum write buffer size for accepted sockets
419 * @param idle_timeout after how long should we timeout idle connections?
420 * @param require_found if YES, connections sending messages of unknown type
421 * will be closed
422 * @return handle for the new server, NULL on error
423 * (typically, "port" already in use)
424 */
425struct GNUNET_SERVER_Handle *
426GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched,
427 GNUNET_NETWORK_AccessCheck access,
428 void *access_cls,
429 const struct sockaddr *serverAddr,
430 socklen_t socklen,
431 size_t maxbuf,
432 struct GNUNET_TIME_Relative
433 idle_timeout, int require_found)
434{
435 struct GNUNET_SERVER_Handle *ret;
436 int lsock;
437 fd_set r;
438
439 lsock = -2;
440 if (serverAddr != NULL)
441 {
442 lsock = open_listen_socket (serverAddr, socklen);
443 if (lsock == -1)
444 return NULL;
445 }
446 ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle));
447 if (0 != PIPE (ret->shutpipe))
448 {
449 GNUNET_break (0 == CLOSE (lsock));
450 GNUNET_free (ret);
451 return NULL;
452 }
453 ret->sched = sched;
454 ret->maxbuf = maxbuf;
455 ret->idle_timeout = idle_timeout;
456 ret->listen_socket = lsock;
457 ret->access = access;
458 ret->access_cls = access_cls;
459 ret->require_found = require_found;
460 if (lsock >= 0)
461 {
462 FD_ZERO (&r);
463 FD_SET (ret->listen_socket, &r);
464 FD_SET (ret->shutpipe[0], &r);
465 GNUNET_SCHEDULER_add_select (sched,
466 GNUNET_YES,
467 GNUNET_SCHEDULER_PRIORITY_HIGH,
468 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
469 GNUNET_TIME_UNIT_FOREVER_REL,
470 GNUNET_MAX (ret->listen_socket,
471 ret->shutpipe[0]) + 1, &r,
472 NULL, &process_listen_socket, ret);
473 }
474 return ret;
475}
476
477
478/**
479 * Free resources held by this server.
480 */
481void
482GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s)
483{
484 static char c;
485
486 GNUNET_assert (s->do_shutdown == GNUNET_NO);
487 s->do_shutdown = GNUNET_YES;
488 if (s->listen_socket == -1)
489 destroy_server (s);
490 else
491 GNUNET_break (1 == WRITE (s->shutpipe[1], &c, 1));
492}
493
494
495/**
496 * Add additional handlers to an existing server.
497 *
498 * @param server the server to add handlers to
499 * @param handlers array of message handlers for
500 * incoming messages; the last entry must
501 * have "NULL" for the "callback"; multiple
502 * entries for the same type are allowed,
503 * they will be called in order of occurence.
504 * These handlers can be removed later;
505 * the handlers array must exist until removed
506 * (or server is destroyed).
507 */
508void
509GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server,
510 const struct GNUNET_SERVER_MessageHandler
511 *handlers)
512{
513 struct HandlerList *p;
514
515 p = GNUNET_malloc (sizeof (struct HandlerList));
516 p->handlers = handlers;
517 p->next = server->handlers;
518 server->handlers = p;
519}
520
521
522/**
523 * Inject a message into the server, pretend it came
524 * from the specified client. Delivery of the message
525 * will happen instantly (if a handler is installed;
526 * otherwise the call does nothing).
527 *
528 * @param server the server receiving the message
529 * @param sender the "pretended" sender of the message
530 * can be NULL!
531 * @param message message to transmit
532 * @return GNUNET_OK if the message was OK and the
533 * connection can stay open
534 * GNUNET_SYSERR if the connection to the
535 * client should be shut down
536 */
537int
538GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
539 struct GNUNET_SERVER_Client *sender,
540 const struct GNUNET_MessageHeader *message)
541{
542 struct HandlerList *pos;
543 const struct GNUNET_SERVER_MessageHandler *mh;
544 unsigned int i;
545 uint16_t type;
546 uint16_t size;
547 int found;
548
549 type = ntohs (message->type);
550 size = ntohs (message->size);
551 pos = server->handlers;
552 found = GNUNET_NO;
553 while (pos != NULL)
554 {
555 i = 0;
556 while (pos->handlers[i].callback != NULL)
557 {
558 mh = &pos->handlers[i];
559 if (mh->type == type)
560 {
561 if ((mh->expected_size != 0) && (mh->expected_size != size))
562 {
563 GNUNET_break_op (0);
564 return GNUNET_SYSERR;
565 }
566 if (sender != NULL)
567 sender->suspended++;
568 mh->callback (mh->callback_cls, server, sender, message);
569 found = GNUNET_YES;
570 }
571 i++;
572 }
573 pos = pos->next;
574 }
575 if (found == GNUNET_NO)
576 {
577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
578 _("Received message of unknown type %d\n"), type);
579 if (server->require_found == GNUNET_YES)
580 return GNUNET_SYSERR;
581 }
582 return GNUNET_OK;
583}
584
585
586/**
587 * We're finished with this client and especially its input
588 * processing. If the RC is zero, free all resources otherwise wait
589 * until RC hits zero to do so.
590 */
591static void
592shutdown_incoming_processing (struct GNUNET_SERVER_Client *client)
593{
594 struct GNUNET_SERVER_Client *prev;
595 struct GNUNET_SERVER_Client *pos;
596 struct GNUNET_SERVER_Handle *server;
597 struct NotifyList *n;
598 unsigned int rc;
599
600 GNUNET_assert (client->my_receive == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
601 rc = client->reference_count;
602 if (client->server != NULL)
603 {
604 server = client->server;
605 client->server = NULL;
606 prev = NULL;
607 pos = server->clients;
608 while ((pos != NULL) && (pos != client))
609 {
610 prev = pos;
611 pos = pos->next;
612 }
613 GNUNET_assert (pos != NULL);
614 if (prev == NULL)
615 server->clients = pos->next;
616 else
617 prev->next = pos->next;
618 n = server->disconnect_notify_list;
619 while (n != NULL)
620 {
621 n->callback (n->callback_cls, client);
622 n = n->next;
623 }
624 }
625 /* wait for RC to hit zero, then free */
626 if (rc > 0)
627 return;
628 client->destroy (client->client_closure);
629 GNUNET_free (client);
630}
631
632
633static void
634process_client_buffer (struct GNUNET_SERVER_Client *client)
635{
636 struct GNUNET_SERVER_Handle *server;
637 const struct GNUNET_MessageHeader *hdr;
638 size_t msize;
639
640 client->in_process_client_buffer = GNUNET_YES;
641 server = client->server;
642 while ((client->receive_pos >= sizeof (struct GNUNET_MessageHeader)) &&
643 (0 == client->suspended) && (GNUNET_YES != client->shutdown_now))
644 {
645 hdr = (const struct GNUNET_MessageHeader *) &client->incoming_buffer;
646 msize = ntohs (hdr->size);
647 if (msize > client->receive_pos)
648 break;
649 if ((msize < sizeof (struct GNUNET_MessageHeader)) ||
650 (GNUNET_OK != GNUNET_SERVER_inject (server, client, hdr)))
651 {
652 client->in_process_client_buffer = GNUNET_NO;
653 shutdown_incoming_processing (client);
654 return;
655 }
656 /* FIXME: this is highly inefficient; we should
657 try to avoid this if the new base address is
658 already nicely aligned. See old handler code... */
659 memmove (client->incoming_buffer,
660 &client->incoming_buffer[msize], client->receive_pos - msize);
661 client->receive_pos -= msize;
662 }
663 client->in_process_client_buffer = GNUNET_NO;
664 if (GNUNET_YES == client->shutdown_now)
665 shutdown_incoming_processing (client);
666}
667
668
669/**
670 * We are receiving an incoming message. Process it.
671 */
672static void
673process_incoming (void *cls,
674 const void *buf,
675 size_t available,
676 const struct sockaddr *addr, socklen_t addrlen, int errCode)
677{
678 struct GNUNET_SERVER_Client *client = cls;
679 struct GNUNET_SERVER_Handle *server = client->server;
680 const char *cbuf = buf;
681 size_t maxcpy;
682
683 client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
684 if ((buf == NULL) ||
685 (available == 0) ||
686 (errCode != 0) ||
687 (server == NULL) ||
688 (client->shutdown_now == GNUNET_YES) ||
689 (GNUNET_YES != client->check (client->client_closure)))
690 {
691 /* other side closed connection, error connecting, etc. */
692 shutdown_incoming_processing (client);
693 return;
694 }
695 GNUNET_SERVER_client_keep (client);
696 client->last_activity = GNUNET_TIME_absolute_get ();
697 /* process data (if available) */
698 while (available > 0)
699 {
700 maxcpy = available;
701 if (maxcpy > sizeof (client->incoming_buffer) - client->receive_pos)
702 maxcpy = sizeof (client->incoming_buffer) - client->receive_pos;
703 memcpy (&client->incoming_buffer[client->receive_pos], cbuf, maxcpy);
704 client->receive_pos += maxcpy;
705 cbuf += maxcpy;
706 available -= maxcpy;
707 if (0 < client->suspended)
708 {
709 if (available > 0)
710 {
711 client->side_buf_size = available;
712 client->side_buf = GNUNET_malloc (available);
713 memcpy (client->side_buf, cbuf, available);
714 available = 0;
715 }
716 break; /* do not run next client iteration! */
717 }
718 process_client_buffer (client);
719 }
720 GNUNET_assert (available == 0);
721 if ((client->suspended == 0) &&
722 (GNUNET_YES != client->shutdown_now) && (client->server != NULL))
723 {
724 /* Finally, keep receiving! */
725 client->my_receive = client->receive (client->client_closure,
726 GNUNET_SERVER_MAX_MESSAGE_SIZE,
727 server->idle_timeout,
728 &process_incoming, client);
729 }
730 if (GNUNET_YES == client->shutdown_now)
731 shutdown_incoming_processing (client);
732 GNUNET_SERVER_client_drop (client);
733}
734
735
736static void
737restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
738{
739 struct GNUNET_SERVER_Client *client = cls;
740
741 process_client_buffer (client);
742 if (0 == client->suspended)
743 client->my_receive = client->receive (client->client_closure,
744 GNUNET_SERVER_MAX_MESSAGE_SIZE,
745 client->server->idle_timeout,
746 &process_incoming, client);
747}
748
749
750/**
751 * Add a client to the set of our clients and
752 * start receiving.
753 */
754static void
755add_client (struct GNUNET_SERVER_Handle *server,
756 struct GNUNET_SERVER_Client *client)
757{
758 client->server = server;
759 client->last_activity = GNUNET_TIME_absolute_get ();
760 client->next = server->clients;
761 server->clients = client;
762 client->my_receive = client->receive (client->client_closure,
763 GNUNET_SERVER_MAX_MESSAGE_SIZE,
764 server->idle_timeout,
765 &process_incoming, client);
766}
767
768static GNUNET_SCHEDULER_TaskIdentifier
769sock_receive (void *cls,
770 size_t max,
771 struct GNUNET_TIME_Relative timeout,
772 GNUNET_NETWORK_Receiver receiver, void *receiver_cls)
773{
774 return GNUNET_NETWORK_receive (cls, max, timeout, receiver, receiver_cls);
775}
776
777static void
778sock_receive_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti)
779{
780 GNUNET_NETWORK_receive_cancel (cls, ti);
781}
782
783
784static void *
785sock_notify_transmit_ready (void *cls,
786 size_t size,
787 struct GNUNET_TIME_Relative timeout,
788 GNUNET_NETWORK_TransmitReadyNotify notify,
789 void *notify_cls)
790{
791 return GNUNET_NETWORK_notify_transmit_ready (cls, size, timeout, notify,
792 notify_cls);
793}
794
795
796static void
797sock_notify_transmit_ready_cancel (void *cls, void *h)
798{
799 GNUNET_NETWORK_notify_transmit_ready_cancel (h);
800}
801
802
803/**
804 * Check if socket is still valid (no fatal errors have happened so far).
805 *
806 * @param cls the socket
807 * @return GNUNET_YES if valid, GNUNET_NO otherwise
808 */
809static int
810sock_check (void *cls)
811{
812 return GNUNET_NETWORK_socket_check (cls);
813}
814
815
816/**
817 * Destroy this socket (free resources).
818 *
819 * @param cls the socket
820 */
821static void
822sock_destroy (void *cls)
823{
824 GNUNET_NETWORK_socket_destroy (cls);
825}
826
827
828/**
829 * Add a TCP socket-based connection to the set of handles managed by
830 * this server. Use this function for outgoing (P2P) connections that
831 * we initiated (and where this server should process incoming
832 * messages).
833 *
834 * @param server the server to use
835 * @param connection the connection to manage (client must
836 * stop using this connection from now on)
837 * @return the client handle (client should call
838 * "client_drop" on the return value eventually)
839 */
840struct GNUNET_SERVER_Client *
841GNUNET_SERVER_connect_socket (struct
842 GNUNET_SERVER_Handle
843 *server,
844 struct GNUNET_NETWORK_SocketHandle *connection)
845{
846 struct GNUNET_SERVER_Client *client;
847
848 client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client));
849 client->client_closure = connection;
850 client->receive = &sock_receive;
851 client->receive_cancel = &sock_receive_cancel;
852 client->notify_transmit_ready = &sock_notify_transmit_ready;
853 client->notify_transmit_ready_cancel = &sock_notify_transmit_ready_cancel;
854 client->check = &sock_check;
855 client->destroy = &sock_destroy;
856 client->reference_count = 1;
857 add_client (server, client);
858 return client;
859}
860
861
862/**
863 * Add an arbitrary connection to the set of handles managed by this
864 * server. This can be used if a sending and receiving does not
865 * really go over the network (internal transmission) or for servers
866 * using UDP.
867 *
868 * @param server the server to use
869 * @param chandle opaque handle for the connection
870 * @param creceive receive function for the connection
871 * @param ccancel cancel receive function for the connection
872 * @param cnotify transmit notification function for the connection
873 * @param cnotify_cancel transmit notification cancellation function for the connection
874 * @param ccheck function to test if the connection is still up
875 * @param cdestroy function to close and free the connection
876 * @return the client handle (client should call
877 * "client_drop" on the return value eventually)
878 */
879struct GNUNET_SERVER_Client *
880GNUNET_SERVER_connect_callback (struct
881 GNUNET_SERVER_Handle
882 *server,
883 void *chandle,
884 GNUNET_SERVER_ReceiveCallback
885 creceive,
886 GNUNET_SERVER_ReceiveCancelCallback
887 ccancel,
888 GNUNET_SERVER_TransmitReadyCallback
889 cnotify,
890 GNUNET_SERVER_TransmitReadyCancelCallback
891 cnotify_cancel,
892 GNUNET_SERVER_CheckCallback
893 ccheck,
894 GNUNET_SERVER_DestroyCallback cdestroy)
895{
896 struct GNUNET_SERVER_Client *client;
897
898 client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client));
899 client->client_closure = chandle;
900 client->receive = creceive;
901 client->receive_cancel = ccancel;
902 client->notify_transmit_ready = cnotify;
903 client->notify_transmit_ready_cancel = cnotify_cancel;
904 client->check = ccheck;
905 client->destroy = cdestroy;
906 client->reference_count = 1;
907 add_client (server, client);
908 return client;
909}
910
911
912/**
913 * Notify the server that the given client handle should
914 * be kept (keeps the connection up if possible, increments
915 * the internal reference counter).
916 *
917 * @param client the client to keep
918 */
919void
920GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client)
921{
922 client->reference_count++;
923}
924
925
926/**
927 * Notify the server that the given client handle is no
928 * longer required. Decrements the reference counter. If
929 * that counter reaches zero an inactive connection maybe
930 * closed.
931 *
932 * @param client the client to drop
933 */
934void
935GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client)
936{
937 GNUNET_assert (client->reference_count > 0);
938 client->reference_count--;
939 if ((client->server == NULL) && (client->reference_count == 0))
940 shutdown_incoming_processing (client);
941}
942
943
944/**
945 * Obtain the network address of the other party.
946 *
947 * @param client the client to get the address for
948 * @param addr where to store the address
949 * @param addrlen where to store the length of the address
950 * @return GNUNET_OK on success
951 */
952int
953GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client,
954 void **addr, size_t * addrlen)
955{
956 if (client->receive != &sock_receive)
957 return GNUNET_SYSERR; /* not a network client */
958 return GNUNET_NETWORK_socket_get_address (client->client_closure,
959 addr, addrlen);
960}
961
962
963/**
964 * Ask the server to notify us whenever a client disconnects.
965 * This function is called whenever the actual network connection
966 * is closed; the reference count may be zero or larger than zero
967 * at this point.
968 *
969 * @param server the server manageing the clients
970 * @param callback function to call on disconnect
971 * @param callback_cls closure for callback
972 */
973void
974GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server,
975 GNUNET_SERVER_DisconnectCallback callback,
976 void *callback_cls)
977{
978 struct NotifyList *n;
979
980 n = GNUNET_malloc (sizeof (struct NotifyList));
981 n->callback = callback;
982 n->callback_cls = callback_cls;
983 n->next = server->disconnect_notify_list;
984 server->disconnect_notify_list = n;
985}
986
987
988/**
989 * Ask the server to disconnect from the given client.
990 * This is the same as returning GNUNET_SYSERR from a message
991 * handler, except that it allows dropping of a client even
992 * when not handling a message from that client.
993 *
994 * @param client the client to disconnect from
995 */
996void
997GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
998{
999 if (client->server == NULL)
1000 return; /* already disconnected */
1001 GNUNET_assert (client->my_receive != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1002 client->receive_cancel (client->client_closure, client->my_receive);
1003 client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1004 shutdown_incoming_processing (client);
1005}
1006
1007
1008/**
1009 * Notify us when the server has enough space to transmit
1010 * a message of the given size to the given client.
1011 *
1012 * @param server the server to use
1013 * @param client client to transmit message to
1014 * @param size requested amount of buffer space
1015 * @param timeout after how long should we give up (and call
1016 * notify with buf NULL and size 0)?
1017 * @param callback function to call when space is available
1018 * @param callback_cls closure for callback
1019 * @return non-NULL if the notify callback was queued; can be used
1020 * to cancel the request using
1021 * GNUNET_NETWORK_notify_transmit_ready_cancel.
1022 * NULL if we are already going to notify someone else (busy)
1023 */
1024struct GNUNET_NETWORK_TransmitHandle *
1025GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
1026 size_t size,
1027 struct GNUNET_TIME_Relative timeout,
1028 GNUNET_NETWORK_TransmitReadyNotify
1029 callback, void *callback_cls)
1030{
1031 return client->notify_transmit_ready (client->client_closure,
1032 size,
1033 timeout, callback, callback_cls);
1034}
1035
1036
1037/**
1038 * Resume receiving from this client, we are done processing the
1039 * current request. This function must be called from within each
1040 * GNUNET_SERVER_MessageCallback (or its respective continuations).
1041 *
1042 * @param client client we were processing a message of
1043 * @param success GNUNET_OK to keep the connection open and
1044 * continue to receive
1045 * GNUNET_SYSERR to close the connection (signal
1046 * serious error)
1047 */
1048void
1049GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success)
1050{
1051 char *sb;
1052
1053 if (client == NULL)
1054 return;
1055 GNUNET_assert (client->suspended > 0);
1056 client->suspended--;
1057 if (success != GNUNET_OK)
1058 client->shutdown_now = GNUNET_YES;
1059 if (client->suspended > 0)
1060 return;
1061 if (client->in_process_client_buffer == GNUNET_YES)
1062 return;
1063 if (client->side_buf_size > 0)
1064 {
1065 /* resume processing from side-buf */
1066 sb = client->side_buf;
1067 client->side_buf = NULL;
1068 /* this will also resume the receive job */
1069 if (GNUNET_YES != client->shutdown_now)
1070 process_incoming (client, sb, client->side_buf_size, NULL, 0, 0);
1071 else
1072 shutdown_incoming_processing (client);
1073 /* finally, free the side-buf */
1074 GNUNET_free (sb);
1075 return;
1076 }
1077 /* resume receive job */
1078 if (GNUNET_YES != client->shutdown_now)
1079 {
1080 GNUNET_SCHEDULER_add_continuation (client->server->sched,
1081 GNUNET_NO,
1082 &restart_processing,
1083 client,
1084 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1085 return;
1086 }
1087 shutdown_incoming_processing (client);
1088}
1089
1090
1091/* end of server.c */
diff --git a/src/util/server_tc.c b/src/util/server_tc.c
new file mode 100644
index 000000000..dc51e1433
--- /dev/null
+++ b/src/util/server_tc.c
@@ -0,0 +1,198 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/server_tc.c
23 * @brief convenience functions for transmission of
24 * complex responses as a server
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_network_lib.h"
31#include "gnunet_scheduler_lib.h"
32#include "gnunet_server_lib.h"
33#include "gnunet_time_lib.h"
34
35
36
37/**
38 * How much buffer space do we want to have at least
39 * before transmitting another increment?
40 */
41#define MIN_BLOCK_SIZE 128
42
43
44
45struct GNUNET_SERVER_TransmitContext
46{
47 /**
48 * Which client are we transmitting to?
49 */
50 struct GNUNET_SERVER_Client *client;
51
52 /**
53 * Transmission buffer. (current offset for writing).
54 */
55 char *buf;
56
57 /**
58 * Number of bytes in buf.
59 */
60 size_t total;
61
62 /**
63 * Offset for writing in buf.
64 */
65 size_t off;
66
67 /**
68 * Timeout for this request.
69 */
70 struct GNUNET_TIME_Absolute timeout;
71};
72
73
74/**
75 * Helper function for incremental transmission of the response.
76 */
77static size_t
78transmit_response (void *cls, size_t size, void *buf)
79{
80 struct GNUNET_SERVER_TransmitContext *tc = cls;
81 size_t msize;
82 if (buf == NULL)
83 {
84 GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR);
85 GNUNET_free_non_null (tc->buf);
86 GNUNET_free (tc);
87 return 0;
88 }
89 if (tc->total - tc->off > size)
90 msize = size;
91 else
92 msize = tc->total - tc->off;
93 memcpy (buf, &tc->buf[tc->off], msize);
94 tc->off += msize;
95 if (tc->total == tc->off)
96 {
97 GNUNET_SERVER_receive_done (tc->client, GNUNET_OK);
98 GNUNET_free_non_null (tc->buf);
99 GNUNET_free (tc);
100 }
101 else
102 {
103 if (NULL == GNUNET_SERVER_notify_transmit_ready (tc->client,
104 GNUNET_MIN
105 (MIN_BLOCK_SIZE,
106 tc->total - tc->off),
107 GNUNET_TIME_absolute_get_remaining
108 (tc->timeout),
109 &transmit_response,
110 tc))
111 {
112 GNUNET_break (0);
113 GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR);
114 GNUNET_free_non_null (tc->buf);
115 GNUNET_free (tc);
116 }
117 }
118 return msize;
119}
120
121
122/**
123 * Create a new transmission context for the
124 * given client.
125 *
126 * @param client client to create the context for.
127 * @return NULL on error
128 */
129struct GNUNET_SERVER_TransmitContext *
130GNUNET_SERVER_transmit_context_create (struct GNUNET_SERVER_Client *client)
131{
132 struct GNUNET_SERVER_TransmitContext *tc;
133
134 GNUNET_assert (client != NULL);
135 tc = GNUNET_malloc (sizeof (struct GNUNET_SERVER_TransmitContext));
136 tc->client = client;
137 return tc;
138}
139
140
141/**
142 * Append a message to the transmission context.
143 * All messages in the context will be sent by
144 * the transmit_context_run method.
145 *
146 * @param tc context to use
147 * @param data what to append to the result message
148 * @param length length of data
149 * @param type type of the message
150 */
151void
152GNUNET_SERVER_transmit_context_append (struct GNUNET_SERVER_TransmitContext
153 *tc, const void *data, size_t length,
154 uint16_t type)
155{
156 struct GNUNET_MessageHeader *msg;
157 size_t size;
158
159 GNUNET_assert (length < GNUNET_SERVER_MAX_MESSAGE_SIZE);
160 size = length + sizeof (struct GNUNET_MessageHeader);
161 GNUNET_assert (size > length);
162 tc->buf = GNUNET_realloc (tc->buf, tc->total + size);
163 msg = (struct GNUNET_MessageHeader *) &tc->buf[tc->total];
164 tc->total += size;
165 msg->size = htons (size);
166 msg->type = htons (type);
167 memcpy (&msg[1], data, length);
168}
169
170
171/**
172 * Execute a transmission context. If there is
173 * an error in the transmission, the receive_done
174 * method will be called with an error code (GNUNET_SYSERR),
175 * otherwise with GNUNET_OK.
176 *
177 * @param tc transmission context to use
178 * @param timeout when to time out and abort the transmission
179 */
180void
181GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc,
182 struct GNUNET_TIME_Relative timeout)
183{
184 tc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
185 if (NULL ==
186 GNUNET_SERVER_notify_transmit_ready (tc->client,
187 GNUNET_MIN (MIN_BLOCK_SIZE,
188 tc->total), timeout,
189 &transmit_response, tc))
190 {
191 GNUNET_break (0);
192 GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR);
193 GNUNET_free_non_null (tc->buf);
194 GNUNET_free (tc);
195 }
196}
197
198/* end of server_tc.c */
diff --git a/src/util/service.c b/src/util/service.c
new file mode 100644
index 000000000..af71db692
--- /dev/null
+++ b/src/util/service.c
@@ -0,0 +1,1451 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/service.c
23 * @brief functions related to starting services
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_configuration_lib.h"
29#include "gnunet_crypto_lib.h"
30#include "gnunet_directories.h"
31#include "gnunet_disk_lib.h"
32#include "gnunet_getopt_lib.h"
33#include "gnunet_os_lib.h"
34#include "gnunet_protocols.h"
35#include "gnunet_server_lib.h"
36#include "gnunet_service_lib.h"
37
38/* ******************* access control ******************** */
39
40/**
41 * @brief IPV4 network in CIDR notation.
42 */
43struct IPv4NetworkSet
44{
45 struct in_addr network;
46 struct in_addr netmask;
47};
48
49/**
50 * @brief network in CIDR notation for IPV6.
51 */
52struct IPv6NetworkSet
53{
54 struct in6_addr network;
55 struct in6_addr netmask;
56};
57
58
59/**
60 * Parse a network specification. The argument specifies
61 * a list of networks. The format is
62 * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated
63 * with a semicolon). The network must be given in dotted-decimal
64 * notation. The netmask can be given in CIDR notation (/16) or
65 * in dotted-decimal (/255.255.0.0).
66 * <p>
67 * @param routeList a string specifying the forbidden networks
68 * @return the converted list, NULL if the synatx is flawed
69 */
70static struct IPv4NetworkSet *
71parse_ipv4_specification (const char *routeList)
72{
73 unsigned int count;
74 unsigned int i;
75 unsigned int j;
76 unsigned int len;
77 int cnt;
78 unsigned int pos;
79 unsigned int temps[8];
80 int slash;
81 struct IPv4NetworkSet *result;
82
83 if (routeList == NULL)
84 return NULL;
85 len = strlen (routeList);
86 if (len == 0)
87 return NULL;
88 count = 0;
89 for (i = 0; i < len; i++)
90 if (routeList[i] == ';')
91 count++;
92 result = GNUNET_malloc (sizeof (struct IPv4NetworkSet) * (count + 1));
93 /* add termination */
94 memset (result, 0, sizeof (struct IPv4NetworkSet) * (count + 1));
95 i = 0;
96 pos = 0;
97 while (i < count)
98 {
99 cnt = sscanf (&routeList[pos],
100 "%u.%u.%u.%u/%u.%u.%u.%u;",
101 &temps[0],
102 &temps[1],
103 &temps[2],
104 &temps[3], &temps[4], &temps[5], &temps[6], &temps[7]);
105 if (cnt == 8)
106 {
107 for (j = 0; j < 8; j++)
108 if (temps[j] > 0xFF)
109 {
110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
111 _("Invalid format for IP: `%s'\n"),
112 &routeList[pos]);
113 GNUNET_free (result);
114 return NULL;
115 }
116 result[i].network.s_addr
117 =
118 htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
119 temps[3]);
120 result[i].netmask.s_addr =
121 htonl ((temps[4] << 24) + (temps[5] << 16) + (temps[6] << 8) +
122 temps[7]);
123 while (routeList[pos] != ';')
124 pos++;
125 pos++;
126 i++;
127 continue;
128 }
129 /* try second notation */
130 cnt = sscanf (&routeList[pos],
131 "%u.%u.%u.%u/%u;",
132 &temps[0], &temps[1], &temps[2], &temps[3], &slash);
133 if (cnt == 5)
134 {
135 for (j = 0; j < 4; j++)
136 if (temps[j] > 0xFF)
137 {
138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
139 _("Invalid format for IP: `%s'\n"),
140 &routeList[pos]);
141 GNUNET_free (result);
142 return NULL;
143 }
144 result[i].network.s_addr
145 =
146 htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
147 temps[3]);
148 if ((slash <= 32) && (slash >= 0))
149 {
150 result[i].netmask.s_addr = 0;
151 while (slash > 0)
152 {
153 result[i].netmask.s_addr
154 = (result[i].netmask.s_addr >> 1) + 0x80000000;
155 slash--;
156 }
157 result[i].netmask.s_addr = htonl (result[i].netmask.s_addr);
158 while (routeList[pos] != ';')
159 pos++;
160 pos++;
161 i++;
162 continue;
163 }
164 else
165 {
166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
167 _
168 ("Invalid network notation ('/%d' is not legal in IPv4 CIDR)."),
169 slash);
170 GNUNET_free (result);
171 return NULL; /* error */
172 }
173 }
174 /* try third notation */
175 slash = 32;
176 cnt = sscanf (&routeList[pos],
177 "%u.%u.%u.%u;",
178 &temps[0], &temps[1], &temps[2], &temps[3]);
179 if (cnt == 4)
180 {
181 for (j = 0; j < 4; j++)
182 if (temps[j] > 0xFF)
183 {
184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
185 _("Invalid format for IP: `%s'\n"),
186 &routeList[pos]);
187 GNUNET_free (result);
188 return NULL;
189 }
190 result[i].network.s_addr
191 =
192 htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
193 temps[3]);
194 result[i].netmask.s_addr = 0;
195 while (slash > 0)
196 {
197 result[i].netmask.s_addr
198 = (result[i].netmask.s_addr >> 1) + 0x80000000;
199 slash--;
200 }
201 result[i].netmask.s_addr = htonl (result[i].netmask.s_addr);
202 while (routeList[pos] != ';')
203 pos++;
204 pos++;
205 i++;
206 continue;
207 }
208 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
209 _("Invalid format for IP: `%s'\n"), &routeList[pos]);
210 GNUNET_free (result);
211 return NULL; /* error */
212 }
213 if (pos < strlen (routeList))
214 {
215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216 _("Invalid format for IP: `%s'\n"), &routeList[pos]);
217 GNUNET_free (result);
218 return NULL; /* oops */
219 }
220 return result; /* ok */
221}
222
223
224/**
225 * Parse a network specification. The argument specifies
226 * a list of networks. The format is
227 * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated
228 * with a semicolon). The network must be given in colon-hex
229 * notation. The netmask must be given in CIDR notation (/16) or
230 * can be omitted to specify a single host.
231 * <p>
232 * @param routeList a string specifying the forbidden networks
233 * @return the converted list, NULL if the synatx is flawed
234 */
235static struct IPv6NetworkSet *
236parse_ipv6_specification (const char *routeListX)
237{
238 unsigned int count;
239 unsigned int i;
240 unsigned int len;
241 unsigned int pos;
242 int start;
243 int slash;
244 int ret;
245 char *routeList;
246 struct IPv6NetworkSet *result;
247 unsigned int bits;
248 unsigned int off;
249 int save;
250
251 if (routeListX == NULL)
252 return NULL;
253 len = strlen (routeListX);
254 if (len == 0)
255 return NULL;
256 routeList = GNUNET_strdup (routeListX);
257 count = 0;
258 for (i = 0; i < len; i++)
259 if (routeList[i] == ';')
260 count++;
261 if (routeList[len - 1] != ';')
262 {
263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
264 _
265 ("Invalid network notation (does not end with ';': `%s')\n"),
266 routeList);
267 GNUNET_free (routeList);
268 return NULL;
269 }
270
271 result = GNUNET_malloc (sizeof (struct IPv6NetworkSet) * (count + 1));
272 memset (result, 0, sizeof (struct IPv6NetworkSet) * (count + 1));
273 i = 0;
274 pos = 0;
275 while (i < count)
276 {
277 start = pos;
278 while (routeList[pos] != ';')
279 pos++;
280 slash = pos;
281 while ((slash >= start) && (routeList[slash] != '/'))
282 slash--;
283 if (slash < start)
284 {
285 memset (&result[i].netmask, 0xFF, sizeof (struct in6_addr));
286 slash = pos;
287 }
288 else
289 {
290 routeList[pos] = '\0';
291 ret = inet_pton (AF_INET6,
292 &routeList[slash + 1], &result[i].netmask);
293 if (ret <= 0)
294 {
295 save = errno;
296 if ((1 != SSCANF (&routeList[slash + 1],
297 "%u", &bits)) || (bits >= 128))
298 {
299 if (ret == 0)
300 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
301 _("Wrong format `%s' for netmask\n"),
302 &routeList[slash + 1]);
303 else
304 {
305 errno = save;
306 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
307 "inet_pton");
308 }
309 GNUNET_free (result);
310 GNUNET_free (routeList);
311 return NULL;
312 }
313 off = 0;
314 while (bits > 8)
315 {
316 result[i].netmask.s6_addr[off++] = 0xFF;
317 bits -= 8;
318 }
319 while (bits > 0)
320 {
321 result[i].netmask.s6_addr[off]
322 = (result[i].netmask.s6_addr[off] >> 1) + 0x80;
323 bits--;
324 }
325 }
326 }
327 routeList[slash] = '\0';
328 ret = inet_pton (AF_INET6, &routeList[start], &result[i].network);
329 if (ret <= 0)
330 {
331 if (ret == 0)
332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333 _("Wrong format `%s' for network\n"),
334 &routeList[slash + 1]);
335 else
336 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "inet_pton");
337 GNUNET_free (result);
338 GNUNET_free (routeList);
339 return NULL;
340 }
341 pos++;
342 i++;
343 }
344 GNUNET_free (routeList);
345 return result;
346}
347
348
349/**
350 * Check if the given IP address is in the list of IP addresses.
351 *
352 * @param list a list of networks
353 * @param ip the IP to check (in network byte order)
354 * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is
355 */
356static int
357check_ipv4_listed (const struct IPv4NetworkSet *list,
358 const struct in_addr *add)
359{
360 int i;
361
362 i = 0;
363 if (list == NULL)
364 return GNUNET_NO;
365
366 while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0))
367 {
368 if ((add->s_addr & list[i].netmask.s_addr) ==
369 (list[i].network.s_addr & list[i].netmask.s_addr))
370 return GNUNET_YES;
371 i++;
372 }
373 return GNUNET_NO;
374}
375
376/**
377 * Check if the given IP address is in the list of IP addresses.
378 *
379 * @param list a list of networks
380 * @param ip the IP to check (in network byte order)
381 * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is
382 */
383static int
384check_ipv6_listed (const struct IPv6NetworkSet *list,
385 const struct in6_addr *ip)
386{
387 unsigned int i;
388 unsigned int j;
389 struct in6_addr zero;
390
391 if (list == NULL)
392 return GNUNET_NO;
393
394 memset (&zero, 0, sizeof (struct in6_addr));
395 i = 0;
396NEXT:
397 while (memcmp (&zero, &list[i].network, sizeof (struct in6_addr)) != 0)
398 {
399 for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++)
400 if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) !=
401 (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j]))
402 {
403 i++;
404 goto NEXT;
405 }
406 return GNUNET_YES;
407 }
408 return GNUNET_NO;
409}
410
411
412/* ****************** service struct ****************** */
413
414
415/**
416 * Context for "service_task".
417 */
418struct GNUNET_SERVICE_Context
419{
420 /**
421 * Our configuration.
422 */
423 struct GNUNET_CONFIGURATION_Handle *cfg;
424
425 /**
426 * Handle for the server.
427 */
428 struct GNUNET_SERVER_Handle *server;
429
430 /**
431 * Scheduler for the server.
432 */
433 struct GNUNET_SCHEDULER_Handle *sched;
434
435 /**
436 * Address to bind to.
437 */
438 struct sockaddr *addr;
439
440 /**
441 * Name of our service.
442 */
443 const char *serviceName;
444
445 /**
446 * Main service-specific task to run.
447 */
448 GNUNET_SERVICE_Main task;
449
450 /**
451 * Closure for task.
452 */
453 void *task_cls;
454
455 /**
456 * IPv4 addresses that are not allowed to connect.
457 */
458 struct IPv4NetworkSet *v4_denied;
459
460 /**
461 * IPv6 addresses that are not allowed to connect.
462 */
463 struct IPv6NetworkSet *v6_denied;
464
465 /**
466 * IPv4 addresses that are allowed to connect (if not
467 * set, all are allowed).
468 */
469 struct IPv4NetworkSet *v4_allowed;
470
471 /**
472 * IPv6 addresses that are allowed to connect (if not
473 * set, all are allowed).
474 */
475 struct IPv6NetworkSet *v6_allowed;
476
477 /**
478 * My (default) message handlers. Adjusted copy
479 * of "defhandlers".
480 */
481 struct GNUNET_SERVER_MessageHandler *my_handlers;
482
483 /**
484 * Idle timeout for server.
485 */
486 struct GNUNET_TIME_Relative timeout;
487
488 /**
489 * Maximum buffer size for the server.
490 */
491 size_t maxbuf;
492
493 /**
494 * Overall success/failure of the service start.
495 */
496 int ret;
497
498 /**
499 * If we are daemonizing, this FD is set to the
500 * pipe to the parent. Send '.' if we started
501 * ok, '!' if not. -1 if we are not daemonizing.
502 */
503 int ready_confirm_fd;
504
505 /**
506 * Do we close connections if we receive messages
507 * for which we have no handler?
508 */
509 int require_found;
510
511 /**
512 * Can clients ask us to initiate a shutdown?
513 */
514 int allow_shutdown;
515
516 /**
517 * Length of addr.
518 */
519 socklen_t addrlen;
520
521};
522
523
524/* ****************** message handlers ****************** */
525
526static size_t
527write_test (void *cls, size_t size, void *buf)
528{
529 struct GNUNET_SERVER_Client *client = cls;
530 struct GNUNET_MessageHeader *msg;
531
532 if (size < sizeof (struct GNUNET_MessageHeader))
533 {
534 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
535 return 0; /* client disconnected */
536 }
537 msg = (struct GNUNET_MessageHeader *) buf;
538 msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
539 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
540 GNUNET_SERVER_receive_done (client, GNUNET_OK);
541 return sizeof (struct GNUNET_MessageHeader);
542}
543
544/**
545 * Handler for TEST message.
546 *
547 * @param cls closure (refers to service)
548 * @param server the server handling the message
549 * @param client identification of the client
550 * @param message the actual message
551 */
552static void
553handle_test (void *cls,
554 struct GNUNET_SERVER_Handle *server,
555 struct GNUNET_SERVER_Client *client,
556 const struct GNUNET_MessageHeader *message)
557{
558 /* simply bounce message back to acknowledge */
559 if (NULL == GNUNET_SERVER_notify_transmit_ready (client,
560 sizeof (struct
561 GNUNET_MessageHeader),
562 GNUNET_TIME_UNIT_FOREVER_REL,
563 &write_test, client))
564 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
565}
566
567
568/**
569 * Handler for SHUTDOWN message.
570 *
571 * @param cls closure (refers to service)
572 * @param server the server handling the message
573 * @param client identification of the client
574 * @param message the actual message
575 */
576static void
577handle_shutdown (void *cls,
578 struct GNUNET_SERVER_Handle *server,
579 struct GNUNET_SERVER_Client *client,
580 const struct GNUNET_MessageHeader *message)
581{
582 struct GNUNET_SERVICE_Context *service = cls;
583 if (!service->allow_shutdown)
584 {
585 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
586 _
587 ("Received shutdown request, but configured to ignore!\n"));
588 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
589 return;
590 }
591 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
592 _("Initiating shutdown as requested by client.\n"));
593 GNUNET_assert (service->sched != NULL);
594 GNUNET_SCHEDULER_shutdown (service->sched);
595 GNUNET_SERVER_receive_done (client, GNUNET_OK);
596}
597
598
599/**
600 * Default handlers for all services. Will be copied and the
601 * "callback_cls" fields will be replaced with the specific service
602 * struct.
603 */
604static const struct GNUNET_SERVER_MessageHandler defhandlers[] = {
605 {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST,
606 sizeof (struct GNUNET_MessageHeader)},
607 {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_SHUTDOWN,
608 sizeof (struct GNUNET_MessageHeader)},
609 {NULL, NULL, 0, 0}
610};
611
612
613
614/* ****************** service core routines ************** */
615
616
617/**
618 * Check if access to the service is allowed from the given address.
619 */
620static int
621check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen)
622{
623 struct GNUNET_SERVICE_Context *sctx = cls;
624 const struct sockaddr_in *i4;
625 const struct sockaddr_in6 *i6;
626 int ret;
627 char buf[INET6_ADDRSTRLEN];
628 uint16_t port;
629
630 switch (addr->sa_family)
631 {
632 case AF_INET:
633 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
634 i4 = (const struct sockaddr_in *) addr;
635 port = ntohs (i4->sin_port);
636 ret = ((sctx->v4_allowed == NULL) ||
637 (check_ipv4_listed (sctx->v4_allowed,
638 &i4->sin_addr)))
639 && ((sctx->v4_denied == NULL) ||
640 (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr)));
641 if (ret != GNUNET_OK)
642 inet_ntop (AF_INET, &i4->sin_addr, buf, sizeof (buf));
643 break;
644 case AF_INET6:
645 GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
646 i6 = (const struct sockaddr_in6 *) addr;
647 port = ntohs (i6->sin6_port);
648 ret = ((sctx->v6_allowed == NULL) ||
649 (check_ipv6_listed (sctx->v6_allowed,
650 &i6->sin6_addr)))
651 && ((sctx->v6_denied == NULL) ||
652 (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr)));
653 if (ret != GNUNET_OK)
654 inet_ntop (AF_INET6, &i6->sin6_addr, buf, sizeof (buf));
655 break;
656 default:
657 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
658 _("Unknown address family %d\n"), addr->sa_family);
659 return GNUNET_SYSERR;
660 }
661 if (ret != GNUNET_OK)
662 {
663 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
664 _("Access from `%s:%u' denied to service `%s'\n"),
665 buf, port, sctx->serviceName);
666 }
667 return ret;
668}
669
670
671/**
672 * Get the name of the file where we will
673 * write the PID of the service.
674 */
675static char *
676get_pid_file_name (struct GNUNET_SERVICE_Context *sctx)
677{
678
679 char *pif;
680
681 if (GNUNET_OK !=
682 GNUNET_CONFIGURATION_get_value_filename (sctx->cfg,
683 sctx->serviceName,
684 "PIDFILE", &pif))
685 return NULL;
686 return pif;
687}
688
689
690/**
691 * Parse an IPv4 access control list.
692 */
693static int
694process_acl4 (struct IPv4NetworkSet **ret,
695 struct GNUNET_SERVICE_Context *sctx, const char *option)
696{
697 char *opt;
698
699 if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option))
700 return GNUNET_OK;
701 GNUNET_break (GNUNET_OK ==
702 GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
703 sctx->serviceName,
704 option, &opt));
705 if (NULL == (*ret = parse_ipv4_specification (opt)))
706 {
707 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
708 _
709 ("Could not parse IPv4 network specification `%s' for `%s:%s'\n"),
710 opt, sctx->serviceName, option);
711 GNUNET_free (opt);
712 return GNUNET_SYSERR;
713 }
714 GNUNET_free (opt);
715 return GNUNET_OK;
716}
717
718
719/**
720 * Parse an IPv4 access control list.
721 */
722static int
723process_acl6 (struct IPv6NetworkSet **ret,
724 struct GNUNET_SERVICE_Context *sctx, const char *option)
725{
726 char *opt;
727 if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option))
728 return GNUNET_OK;
729 GNUNET_break (GNUNET_OK ==
730 GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
731 sctx->serviceName,
732 option, &opt));
733 if (NULL == (*ret = parse_ipv6_specification (opt)))
734 {
735 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
736 _
737 ("Could not parse IPv6 network specification `%s' for `%s:%s'\n"),
738 opt, sctx->serviceName, option);
739 GNUNET_free (opt);
740 return GNUNET_SYSERR;
741 }
742 GNUNET_free (opt);
743 return GNUNET_OK;
744}
745
746
747/**
748 * Setup addr, addrlen, maxbuf, idle_timeout
749 * based on configuration!
750 *
751 * Configuration must specify a "PORT". It may
752 * specify:
753 * - TIMEOUT (after how many ms does an inactive service timeout);
754 * - MAXBUF (maximum incoming message size supported)
755 * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack)
756 * - ALLOW_SHUTDOWN (allow clients to shutdown this service)
757 * - BINDTO (hostname or IP address to bind to, otherwise we take everything)
758 * - ACCEPT_FROM (only allow connections from specified IPv4 subnets)
759 * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets)
760 * - REJECT_FROM (disallow allow connections from specified IPv4 subnets)
761 * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets)
762 *
763 * @return GNUNET_OK if configuration succeeded
764 */
765static int
766setup_service (struct GNUNET_SERVICE_Context *sctx)
767{
768 unsigned long long maxbuf;
769 unsigned long long idleout;
770 char *hostname;
771 unsigned long long port;
772 int disablev6;
773 struct addrinfo hints;
774 struct addrinfo *res;
775 struct addrinfo *pos;
776 int ret;
777 int tolerant;
778
779 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
780 sctx->serviceName, "TIMEOUT"))
781 {
782 if (GNUNET_OK !=
783 GNUNET_CONFIGURATION_get_value_number (sctx->cfg,
784 sctx->serviceName,
785 "TIMEOUT", &idleout))
786 return GNUNET_SYSERR;
787
788 sctx->timeout.value = idleout;
789 }
790 else
791 sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
792 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
793 sctx->serviceName, "MAXBUF"))
794 {
795 if (GNUNET_OK !=
796 GNUNET_CONFIGURATION_get_value_number (sctx->cfg,
797 sctx->serviceName,
798 "MAXBUF", &maxbuf))
799 return GNUNET_SYSERR;
800 }
801 else
802 maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE;
803 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
804 sctx->serviceName, "DISABLEV6"))
805 {
806 if (GNUNET_SYSERR ==
807 (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg,
808 sctx->
809 serviceName,
810 "DISABLEV6")))
811 return GNUNET_SYSERR;
812 }
813 else
814 disablev6 = GNUNET_NO;
815 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
816 sctx->serviceName, "ALLOW_SHUTDOWN"))
817 {
818 if (GNUNET_SYSERR ==
819 (sctx->allow_shutdown =
820 GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName,
821 "ALLOW_SHUTDOWN")))
822 return GNUNET_SYSERR;
823 }
824 else
825 sctx->allow_shutdown = GNUNET_NO;
826
827 if (!disablev6)
828 {
829 /* probe IPv6 support */
830 ret = SOCKET (PF_INET6, SOCK_STREAM, 0);
831 if (ret == -1)
832 {
833 if ((errno == ENOBUFS) ||
834 (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES))
835 {
836 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
837 return GNUNET_SYSERR;
838 }
839 ret = SOCKET (PF_INET, SOCK_STREAM, 0);
840 if (ret != -1)
841 {
842 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
843 _
844 ("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"),
845 sctx->serviceName, strerror (errno));
846 disablev6 = GNUNET_YES;
847 }
848 }
849 if (ret != -1)
850 GNUNET_break (0 == CLOSE (ret));
851 }
852
853
854
855 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
856 sctx->serviceName, "TOLERANT"))
857 {
858 if (GNUNET_SYSERR ==
859 (tolerant = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg,
860 sctx->serviceName,
861 "TOLERANT")))
862 return GNUNET_SYSERR;
863 }
864 else
865 tolerant = GNUNET_NO;
866 sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES;
867
868
869 if ((GNUNET_OK !=
870 GNUNET_CONFIGURATION_get_value_number (sctx->cfg,
871 sctx->serviceName,
872 "PORT",
873 &port)) || (port > 65535))
874 {
875 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
876 _
877 ("Require valid port number for service `%s' in configuration!\n"),
878 sctx->serviceName);
879 return GNUNET_SYSERR;
880 }
881 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
882 sctx->serviceName, "BINDTO"))
883 {
884 GNUNET_break (GNUNET_OK ==
885 GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
886 sctx->serviceName,
887 "BINDTO",
888 &hostname));
889 }
890 else
891 hostname = NULL;
892
893 if (hostname != NULL)
894 {
895 memset (&hints, 0, sizeof (struct addrinfo));
896 if (disablev6)
897 hints.ai_family = AF_INET;
898 if ((0 != (ret = getaddrinfo (hostname,
899 NULL, &hints, &res))) || (res == NULL))
900 {
901 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
902 _("Failed to resolve `%s': %s\n"),
903 hostname, gai_strerror (ret));
904 GNUNET_free (hostname);
905 return GNUNET_SYSERR;
906 }
907 pos = res;
908 while ((NULL != pos) &&
909 (((disablev6) &&
910 (pos->ai_family != AF_INET)) ||
911 ((pos->ai_family != AF_INET) && (pos->ai_family != AF_INET6))))
912 pos = pos->ai_next;
913 if (pos == NULL)
914 {
915 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
916 _("Failed to find IPv4 address for `%s'.\n"), hostname);
917 freeaddrinfo (res);
918 GNUNET_free (hostname);
919 return GNUNET_SYSERR;
920 }
921 GNUNET_free (hostname);
922 if (pos->ai_family == AF_INET)
923 {
924 GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in));
925 sctx->addrlen = pos->ai_addrlen;
926 sctx->addr = GNUNET_malloc (sctx->addrlen);
927 memcpy (sctx->addr, res->ai_addr, sctx->addrlen);
928 ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port);
929 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
930 _
931 ("Configured to bind to %s address; %s connections to this service will fail!\n"),
932 "IPv4", "IPv6");
933 }
934 else
935 {
936 GNUNET_assert (pos->ai_family == AF_INET6);
937 GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6));
938 sctx->addrlen = pos->ai_addrlen;
939 sctx->addr = GNUNET_malloc (sctx->addrlen);
940 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
941 _
942 ("Configured to bind to %s address; %s connections to this service will fail!\n"),
943 "IPv6", "IPv4");
944 ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port);
945 }
946 freeaddrinfo (res);
947 }
948 else
949 {
950 /* will bind against everything, just set port */
951 if (disablev6)
952 {
953 /* V4-only */
954 sctx->addrlen = sizeof (struct sockaddr_in6);
955 sctx->addr = GNUNET_malloc (sctx->addrlen);
956 ((struct sockaddr_in *) sctx->addr)->sin_family = AF_INET;
957 ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port);
958 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
959 _
960 ("Configured to bind to %s address; %s connections to this service will fail!\n"),
961 "IPv4", "IPv6");
962 }
963 else
964 {
965 /* dual stack */
966 sctx->addrlen = sizeof (struct sockaddr_in6);
967 sctx->addr = GNUNET_malloc (sctx->addrlen);
968 ((struct sockaddr_in6 *) sctx->addr)->sin6_family = AF_INET6;
969 ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port);
970 }
971 }
972 sctx->maxbuf = (size_t) maxbuf;
973 if (sctx->maxbuf != maxbuf)
974 {
975 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
976 _
977 ("Value in configuration for `%s' and service `%s' too large!\n"),
978 "MAXBUF", sctx->serviceName);
979 return GNUNET_SYSERR;
980 }
981
982
983 if ((GNUNET_OK !=
984 process_acl4 (&sctx->v4_denied,
985 sctx,
986 "REJECT_FROM")) ||
987 (GNUNET_OK !=
988 process_acl4 (&sctx->v4_allowed,
989 sctx,
990 "ACCEPT_FROM")) ||
991 (GNUNET_OK !=
992 process_acl6 (&sctx->v6_denied,
993 sctx,
994 "REJECT_FROM6")) ||
995 (GNUNET_OK != process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6")))
996 return GNUNET_SYSERR;
997 return GNUNET_OK;
998}
999
1000
1001/**
1002 * Get the name of the user that'll be used
1003 * to provide the service.
1004 */
1005static char *
1006get_user_name (struct GNUNET_SERVICE_Context *sctx)
1007{
1008
1009 char *un;
1010
1011 if (GNUNET_OK !=
1012 GNUNET_CONFIGURATION_get_value_filename (sctx->cfg,
1013 sctx->serviceName,
1014 "USERNAME", &un))
1015 return NULL;
1016 return un;
1017}
1018
1019/**
1020 * Write PID file.
1021 */
1022static int
1023write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid)
1024{
1025 FILE *pidfd;
1026 char *pif;
1027 char *user;
1028 char *rdir;
1029 int len;
1030
1031 if (NULL == (pif = get_pid_file_name (sctx)))
1032 return GNUNET_OK; /* no file desired */
1033 user = get_user_name (sctx);
1034 rdir = GNUNET_strdup (pif);
1035 len = strlen (rdir);
1036 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
1037 len--;
1038 rdir[len] = '\0';
1039 if (0 != ACCESS (rdir, F_OK))
1040 {
1041 /* we get to create a directory -- and claim it
1042 as ours! */
1043 GNUNET_DISK_directory_create (rdir);
1044 if ((user != NULL) && (0 < strlen (user)))
1045 GNUNET_DISK_file_change_owner (rdir, user);
1046 }
1047 if (0 != ACCESS (rdir, W_OK | X_OK))
1048 {
1049 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "access", rdir);
1050 GNUNET_free (rdir);
1051 GNUNET_free_non_null (user);
1052 GNUNET_free (pif);
1053 return GNUNET_SYSERR;
1054 }
1055 GNUNET_free (rdir);
1056 pidfd = FOPEN (pif, "w");
1057 if (pidfd == NULL)
1058 {
1059 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", pif);
1060 GNUNET_free (pif);
1061 GNUNET_free_non_null (user);
1062 return GNUNET_SYSERR;
1063 }
1064 if (0 > FPRINTF (pidfd, "%u", pid))
1065 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif);
1066 GNUNET_break (0 == fclose (pidfd));
1067 if ((user != NULL) && (0 < strlen (user)))
1068 GNUNET_DISK_file_change_owner (pif, user);
1069 GNUNET_free_non_null (user);
1070 GNUNET_free (pif);
1071 return GNUNET_OK;
1072}
1073
1074
1075/**
1076 * Initial task for the service.
1077 */
1078static void
1079service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1080{
1081 struct GNUNET_SERVICE_Context *sctx = cls;
1082 unsigned int i;
1083
1084 sctx->sched = tc->sched;
1085 sctx->server = GNUNET_SERVER_create (tc->sched,
1086 &check_access,
1087 sctx,
1088 sctx->addr,
1089 sctx->addrlen,
1090 sctx->maxbuf,
1091 sctx->timeout, sctx->require_found);
1092 if (sctx->server == NULL)
1093 {
1094 sctx->ret = GNUNET_SYSERR;
1095 return;
1096 }
1097 sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers));
1098 memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers));
1099 i = 0;
1100 while ((sctx->my_handlers[i].callback != NULL))
1101 sctx->my_handlers[i++].callback_cls = sctx;
1102 GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers);
1103 if (sctx->ready_confirm_fd != -1)
1104 {
1105 GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1));
1106 GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd));
1107 sctx->ready_confirm_fd = -1;
1108 write_pid_file (sctx, getpid ());
1109 }
1110
1111 sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg);
1112}
1113
1114
1115/**
1116 * Detach from terminal.
1117 */
1118static int
1119detach_terminal (struct GNUNET_SERVICE_Context *sctx)
1120{
1121 pid_t pid;
1122 int nullfd;
1123 int filedes[2];
1124
1125#ifndef MINGW
1126 if (0 != PIPE (filedes))
1127 {
1128 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1129 return GNUNET_SYSERR;
1130 }
1131 pid = fork ();
1132 if (pid < 0)
1133 {
1134 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
1135 return GNUNET_SYSERR;
1136 }
1137 if (pid != 0)
1138 {
1139 /* Parent */
1140 char c;
1141
1142 GNUNET_break (0 == CLOSE (filedes[1]));
1143 c = 'X';
1144 if (1 != READ (filedes[0], &c, sizeof (char)))
1145 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
1146 fflush (stdout);
1147 switch (c)
1148 {
1149 case '.':
1150 exit (0);
1151 case 'I':
1152 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1153 _("Service process failed to initialize\n"));
1154 break;
1155 case 'S':
1156 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1157 _
1158 ("Service process could not initialize server function\n"));
1159 break;
1160 case 'X':
1161 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1162 _("Service process failed to report status\n"));
1163 break;
1164 }
1165 exit (1); /* child reported error */
1166 }
1167 GNUNET_break (0 == CLOSE (0));
1168 GNUNET_break (0 == CLOSE (1));
1169 GNUNET_break (0 == CLOSE (filedes[0]));
1170 nullfd = GNUNET_DISK_file_open ("/dev/null", O_RDWR | O_APPEND);
1171 if (nullfd < 0)
1172 return GNUNET_SYSERR;
1173 /* set stdin/stdout to /dev/null */
1174 if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0))
1175 {
1176 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
1177 return GNUNET_SYSERR;
1178 }
1179 /* Detach from controlling terminal */
1180 pid = setsid ();
1181 if (pid == -1)
1182 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsid");
1183 sctx->ready_confirm_fd = filedes[1];
1184#else
1185 /* FIXME: we probably need to do something else
1186 elsewhere in order to fork the process itself... */
1187 FreeConsole ();
1188#endif
1189 return GNUNET_OK;
1190}
1191
1192
1193/**
1194 * Set user ID.
1195 */
1196static int
1197set_user_id (struct GNUNET_SERVICE_Context *sctx)
1198{
1199 char *user;
1200
1201 if (NULL == (user = get_user_name (sctx)))
1202 return GNUNET_OK; /* keep */
1203#ifndef MINGW
1204 struct passwd *pws;
1205
1206 errno = 0;
1207 pws = getpwnam (user);
1208 if (pws == NULL)
1209 {
1210 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1211 _("Cannot obtain information about user `%s': %s\n"),
1212 user, errno == 0 ? _("No such user") : STRERROR (errno));
1213 GNUNET_free (user);
1214 return GNUNET_SYSERR;
1215 }
1216 if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) ||
1217#if HAVE_INITGROUPS
1218 (0 != initgroups (user, pws->pw_gid)) ||
1219#endif
1220 (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid)))
1221 {
1222 if ((0 != setregid (pws->pw_gid, pws->pw_gid)) ||
1223 (0 != setreuid (pws->pw_uid, pws->pw_uid)))
1224 {
1225 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1226 _("Cannot change user/group to `%s': %s\n"), user,
1227 STRERROR (errno));
1228 GNUNET_free (user);
1229 return GNUNET_SYSERR;
1230 }
1231 }
1232#endif
1233 GNUNET_free (user);
1234 return GNUNET_OK;
1235}
1236
1237
1238/**
1239 * Delete the PID file that was created by our parent.
1240 */
1241static void
1242pid_file_delete (struct GNUNET_SERVICE_Context *sctx)
1243{
1244 char *pif = get_pid_file_name (sctx);
1245 if (pif == NULL)
1246 return; /* no PID file */
1247 if (0 != UNLINK (pif))
1248 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", pif);
1249 GNUNET_free (pif);
1250}
1251
1252/**
1253 * Run a standard GNUnet service startup sequence (initialize loggers
1254 * and configuration, parse options).
1255 *
1256 * @param argc number of command line arguments
1257 * @param argv command line arguments
1258 * @param serviceName our service name
1259 * @param task main task of the service
1260 * @param task_cls closure for task
1261 * @param term termination task of the service
1262 * @param term_cls closure for term
1263 * @return GNUNET_SYSERR on error, GNUNET_OK
1264 * if we shutdown nicely
1265 */
1266int
1267GNUNET_SERVICE_run (int argc,
1268 char *const *argv,
1269 const char *serviceName,
1270 GNUNET_SERVICE_Main task,
1271 void *task_cls, GNUNET_SERVICE_Term term, void *term_cls)
1272{
1273 char *cfg_fn;
1274 char *loglev;
1275 char *logfile;
1276 int do_daemonize;
1277 struct GNUNET_SERVICE_Context sctx;
1278 struct GNUNET_GETOPT_CommandLineOption service_options[] = {
1279 GNUNET_GETOPT_OPTION_CFG_FILE (&cfg_fn),
1280 {'d', "daemonize", NULL,
1281 gettext_noop ("do daemonize (detach from terminal)"), 0,
1282 GNUNET_GETOPT_set_one, &do_daemonize},
1283 GNUNET_GETOPT_OPTION_HELP (serviceName),
1284 GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev),
1285 GNUNET_GETOPT_OPTION_LOGFILE (&logfile),
1286 GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION),
1287 GNUNET_GETOPT_OPTION_END
1288 };
1289 do_daemonize = 0;
1290 logfile = NULL;
1291 loglev = GNUNET_strdup ("WARNING");
1292 cfg_fn = GNUNET_strdup (GNUNET_DEFAULT_DAEMON_CONFIG_FILE);
1293 memset (&sctx, 0, sizeof (sctx));
1294 sctx.ready_confirm_fd = -1;
1295 sctx.ret = GNUNET_OK;
1296 sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1297 sctx.maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE;
1298 sctx.task = task;
1299 sctx.serviceName = serviceName;
1300 sctx.cfg = GNUNET_CONFIGURATION_create ();
1301 /* setup subsystems */
1302 if ((GNUNET_SYSERR ==
1303 GNUNET_GETOPT_run (serviceName,
1304 sctx.cfg,
1305 service_options,
1306 argc,
1307 argv)) ||
1308 (GNUNET_OK !=
1309 GNUNET_log_setup (serviceName, loglev, logfile)) ||
1310 (GNUNET_OK !=
1311 GNUNET_CONFIGURATION_load (sctx.cfg, cfg_fn)) ||
1312 (GNUNET_OK !=
1313 setup_service (&sctx)) ||
1314 ((do_daemonize == 1) &&
1315 (GNUNET_OK != detach_terminal (&sctx))) ||
1316 (GNUNET_OK != set_user_id (&sctx)))
1317 {
1318 if (sctx.ready_confirm_fd != -1)
1319 {
1320 if (1 != WRITE (sctx.ready_confirm_fd, "I", 1))
1321 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1322 GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd));
1323 }
1324 GNUNET_CONFIGURATION_destroy (sctx.cfg);
1325 GNUNET_free_non_null (sctx.addr);
1326 GNUNET_free_non_null (logfile);
1327 GNUNET_free (loglev);
1328 GNUNET_free (cfg_fn);
1329 GNUNET_free_non_null (sctx.v4_denied);
1330 GNUNET_free_non_null (sctx.v6_denied);
1331 GNUNET_free_non_null (sctx.v4_allowed);
1332 GNUNET_free_non_null (sctx.v6_allowed);
1333 return GNUNET_SYSERR;
1334 }
1335
1336 /* actually run service */
1337 GNUNET_SCHEDULER_run (&service_task, &sctx);
1338 if (sctx.ready_confirm_fd != -1)
1339 {
1340 if (1 != WRITE (sctx.ready_confirm_fd, "S", 1))
1341 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1342 GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd));
1343 }
1344
1345 /* shutdown */
1346 if (term != NULL)
1347 term (term_cls, sctx.cfg);
1348 if ((do_daemonize == 1) && (sctx.server != NULL))
1349 pid_file_delete (&sctx);
1350 if (sctx.server != NULL)
1351 GNUNET_SERVER_destroy (sctx.server);
1352 GNUNET_free_non_null (sctx.my_handlers);
1353 GNUNET_CONFIGURATION_destroy (sctx.cfg);
1354 GNUNET_free_non_null (sctx.addr);
1355 GNUNET_free_non_null (logfile);
1356 GNUNET_free (loglev);
1357 GNUNET_free (cfg_fn);
1358 GNUNET_free_non_null (sctx.v4_denied);
1359 GNUNET_free_non_null (sctx.v6_denied);
1360 GNUNET_free_non_null (sctx.v4_allowed);
1361 GNUNET_free_non_null (sctx.v6_allowed);
1362 return sctx.ret;
1363}
1364
1365
1366/**
1367 * Run a service startup sequence within an existing
1368 * initialized system.
1369 *
1370 * @param serviceName our service name
1371 * @param sched scheduler to use
1372 * @param cfg configuration to use
1373 * @return NULL on error, service handle
1374 */
1375struct GNUNET_SERVICE_Context *
1376GNUNET_SERVICE_start (const char *serviceName,
1377 struct GNUNET_SCHEDULER_Handle *sched,
1378 struct GNUNET_CONFIGURATION_Handle *cfg)
1379{
1380 int i;
1381 struct GNUNET_SERVICE_Context *sctx;
1382
1383 sctx = GNUNET_malloc (sizeof (struct GNUNET_SERVICE_Context));
1384 sctx->ready_confirm_fd = -1; /* no daemonizing */
1385 sctx->ret = GNUNET_OK;
1386 sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1387 sctx->maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE;
1388 sctx->serviceName = serviceName;
1389 sctx->cfg = cfg;
1390 sctx->sched = sched;
1391
1392 /* setup subsystems */
1393 if ((GNUNET_OK != setup_service (sctx)) ||
1394 (NULL == (sctx->server = GNUNET_SERVER_create (sched,
1395 &check_access,
1396 sctx,
1397 sctx->addr,
1398 sctx->addrlen,
1399 sctx->maxbuf,
1400 sctx->timeout,
1401 sctx->require_found))))
1402 {
1403 GNUNET_SERVICE_stop (sctx);
1404 return NULL;
1405 }
1406 sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers));
1407 memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers));
1408 i = 0;
1409 while ((sctx->my_handlers[i].callback != NULL))
1410 sctx->my_handlers[i++].callback_cls = sctx;
1411 GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers);
1412
1413
1414 return sctx;
1415}
1416
1417/**
1418 * Obtain the server used by a service. Note that the server must NOT
1419 * be destroyed by the caller.
1420 *
1421 * @param ctx the service context returned from the start function
1422 * @return handle to the server for this service, NULL if there is none
1423 */
1424struct GNUNET_SERVER_Handle *
1425GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx)
1426{
1427 return ctx->server;
1428}
1429
1430
1431/**
1432 * Stop a service that was started with "GNUNET_SERVICE_start".
1433 *
1434 * @param ctx the service context returned from the start function
1435 */
1436void
1437GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx)
1438{
1439 if (NULL != sctx->server)
1440 GNUNET_SERVER_destroy (sctx->server);
1441 GNUNET_free_non_null (sctx->my_handlers);
1442 GNUNET_free_non_null (sctx->addr);
1443 GNUNET_free_non_null (sctx->v4_denied);
1444 GNUNET_free_non_null (sctx->v6_denied);
1445 GNUNET_free_non_null (sctx->v4_allowed);
1446 GNUNET_free_non_null (sctx->v6_allowed);
1447 GNUNET_free (sctx);
1448}
1449
1450
1451/* end of service.c */
diff --git a/src/util/signal.c b/src/util/signal.c
new file mode 100644
index 000000000..478551ca2
--- /dev/null
+++ b/src/util/signal.c
@@ -0,0 +1,76 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/signal.c
23 * @brief code for installing and uninstalling signal handlers
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_signal_lib.h"
30
31struct GNUNET_SIGNAL_Context
32{
33 int sig;
34
35 GNUNET_SIGNAL_Handler method;
36
37#ifndef MINGW
38 struct sigaction oldsig;
39#endif
40};
41
42struct GNUNET_SIGNAL_Context *
43GNUNET_SIGNAL_handler_install (int signal, GNUNET_SIGNAL_Handler handler)
44{
45 struct GNUNET_SIGNAL_Context *ret;
46#ifndef MINGW
47 struct sigaction sig;
48#endif
49
50 ret = GNUNET_malloc (sizeof (struct GNUNET_SIGNAL_Context));
51 ret->sig = signal;
52 ret->method = handler;
53#ifndef MINGW
54 sig.sa_handler = (void *) handler;
55 sigemptyset (&sig.sa_mask);
56#ifdef SA_INTERRUPT
57 sig.sa_flags = SA_INTERRUPT; /* SunOS */
58#else
59 sig.sa_flags = SA_RESTART;
60#endif
61 sigaction (signal, &sig, &ret->oldsig);
62#endif
63 return ret;
64}
65
66void
67GNUNET_SIGNAL_handler_uninstall (struct GNUNET_SIGNAL_Context *ctx)
68{
69#ifndef MINGW
70 struct sigaction sig;
71
72 sigemptyset (&sig.sa_mask);
73 sigaction (ctx->sig, &ctx->oldsig, &sig);
74#endif
75 GNUNET_free (ctx);
76}
diff --git a/src/util/strings.c b/src/util/strings.c
new file mode 100644
index 000000000..18f6582e8
--- /dev/null
+++ b/src/util/strings.c
@@ -0,0 +1,396 @@
1/*
2 This file is part of GNUnet.
3 (C) 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/strings.c
23 * @brief string functions
24 * @author Nils Durner
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#if HAVE_ICONV_H
30#include <iconv.h>
31#endif
32#include "gnunet_common.h"
33#include "gnunet_strings_lib.h"
34
35
36/**
37 * Fill a buffer of the given size with
38 * count 0-terminated strings (given as varargs).
39 * If "buffer" is NULL, only compute the amount of
40 * space required (sum of "strlen(arg)+1").
41 *
42 * Unlike using "snprintf" with "%s", this function
43 * will add 0-terminators after each string. The
44 * "GNUNET_string_buffer_tokenize" function can be
45 * used to parse the buffer back into individual
46 * strings.
47 *
48 * @return number of bytes written to the buffer
49 * (or number of bytes that would have been written)
50 */
51unsigned int
52GNUNET_STRINGS_buffer_fill (char *buffer,
53 unsigned int size, unsigned int count, ...)
54{
55 unsigned int needed;
56 unsigned int slen;
57 const char *s;
58 va_list ap;
59
60 needed = 0;
61 va_start (ap, count);
62 while (count > 0)
63 {
64 s = va_arg (ap, const char *);
65 slen = strlen (s) + 1;
66 if (buffer != NULL)
67 {
68 GNUNET_assert (needed + slen <= size);
69 memcpy (&buffer[needed], s, slen);
70 }
71 needed += slen;
72 count--;
73 }
74 va_end (ap);
75 return needed;
76}
77
78
79/**
80 * Given a buffer of a given size, find "count"
81 * 0-terminated strings in the buffer and assign
82 * the count (varargs) of type "const char**" to the
83 * locations of the respective strings in the
84 * buffer.
85 *
86 * @param buffer the buffer to parse
87 * @param size size of the buffer
88 * @param count number of strings to locate
89 * @return offset of the character after the last 0-termination
90 * in the buffer, or 0 on error.
91 */
92unsigned int
93GNUNET_STRINGS_buffer_tokenize (const char *buffer,
94 unsigned int size, unsigned int count, ...)
95{
96 unsigned int start;
97 unsigned int needed;
98 const char **r;
99 va_list ap;
100
101 needed = 0;
102 va_start (ap, count);
103 while (count > 0)
104 {
105 r = va_arg (ap, const char **);
106 start = needed;
107 while ((needed < size) && (buffer[needed] != '\0'))
108 needed++;
109 if (needed == size)
110 {
111 va_end (ap);
112 return 0; /* error */
113 }
114 *r = &buffer[start];
115 needed++; /* skip 0-termination */
116 count--;
117 }
118 va_end (ap);
119 return needed;
120}
121
122
123/**
124 * Convert a given filesize into a fancy human-readable format.
125 */
126char *
127GNUNET_STRINGS_byte_size_fancy (unsigned long long size)
128{
129 const char *unit = _( /* size unit */ "b");
130 char *ret;
131
132 if (size > 5 * 1024)
133 {
134 size = size / 1024;
135 unit = _( /* size unit */ "KiB");
136 if (size > 5 * 1024)
137 {
138 size = size / 1024;
139 unit = _( /* size unit */ "MiB");
140 if (size > 5 * 1024)
141 {
142 size = size / 1024;
143 unit = _( /* size unit */ "GiB");
144 if (size > 5 * 1024)
145 {
146 size = size / 1024;
147 unit = _( /* size unit */ "TiB");
148 }
149 }
150 }
151 }
152 ret = GNUNET_malloc (32);
153 GNUNET_snprintf (ret, 32, "%llu%s", size, unit);
154 return ret;
155}
156
157
158/**
159 * Convert the len characters long character sequence
160 * given in input that is in the given charset
161 * to UTF-8.
162 * @return the converted string (0-terminated),
163 * if conversion fails, a copy of the orignal
164 * string is returned.
165 */
166char *
167GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset)
168{
169 char *ret;
170#if ENABLE_NLS && HAVE_ICONV
171 size_t tmpSize;
172 size_t finSize;
173 char *tmp;
174 char *itmp;
175 iconv_t cd;
176
177 cd = iconv_open ("UTF-8", charset);
178 if (cd == (iconv_t) - 1)
179 {
180 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
181 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
182 _("Character set requested was `%s'\n"), charset);
183 ret = GNUNET_malloc (len + 1);
184 memcpy (ret, input, len);
185 ret[len] = '\0';
186 return ret;
187 }
188 tmpSize = 3 * len + 4;
189 tmp = GNUNET_malloc (tmpSize);
190 itmp = tmp;
191 finSize = tmpSize;
192 if (iconv (cd, (char **) &input, &len, &itmp, &finSize) == (size_t) - 1)
193 {
194 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv");
195 iconv_close (cd);
196 GNUNET_free (tmp);
197 ret = GNUNET_malloc (len + 1);
198 memcpy (ret, input, len);
199 ret[len] = '\0';
200 return ret;
201 }
202 ret = GNUNET_malloc (tmpSize - finSize + 1);
203 memcpy (ret, tmp, tmpSize - finSize);
204 ret[tmpSize - finSize] = '\0';
205 GNUNET_free (tmp);
206 if (0 != iconv_close (cd))
207 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_close");
208 return ret;
209#else
210 ret = GNUNET_malloc (len + 1);
211 memcpy (ret, input, len);
212 ret[len] = '\0';
213 return ret;
214#endif
215}
216
217
218/**
219 * Complete filename (a la shell) from abbrevition.
220 * @param fil the name of the file, may contain ~/ or
221 * be relative to the current directory
222 * @returns the full file name,
223 * NULL is returned on error
224 */
225char *
226GNUNET_STRINGS_filename_expand (const char *fil)
227{
228 char *buffer;
229#ifndef MINGW
230 size_t len;
231 size_t n;
232 char *fm;
233 const char *fil_ptr;
234#else
235 char *fn;
236 long lRet;
237#endif
238
239 if (fil == NULL)
240 return NULL;
241
242#ifndef MINGW
243 if (fil[0] == DIR_SEPARATOR)
244 /* absolute path, just copy */
245 return GNUNET_strdup (fil);
246 if (fil[0] == '~')
247 {
248 fm = getenv ("HOME");
249 if (fm == NULL)
250 {
251 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
252 _
253 ("Failed to expand `$HOME': environment variable `HOME' not set"));
254 return NULL;
255 }
256 fm = GNUNET_strdup (fm);
257 /* do not copy '~' */
258 fil_ptr = fil + 1;
259
260 /* skip over dir seperator to be consistent */
261 if (fil_ptr[0] == DIR_SEPARATOR)
262 fil_ptr++;
263 }
264 else
265 {
266 /* relative path */
267 fil_ptr = fil;
268 len = 512;
269 fm = NULL;
270 while (1)
271 {
272 buffer = GNUNET_malloc (len);
273 if (getcwd (buffer, len) != NULL)
274 {
275 fm = buffer;
276 break;
277 }
278 if ((errno == ERANGE) && (len < 1024 * 1024 * 4))
279 {
280 len *= 2;
281 GNUNET_free (buffer);
282 continue;
283 }
284 GNUNET_free (buffer);
285 break;
286 }
287 if (fm == NULL)
288 {
289 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getcwd");
290 buffer = getenv ("PWD"); /* alternative */
291 if (buffer != NULL)
292 fm = GNUNET_strdup (buffer);
293 }
294 if (fm == NULL)
295 fm = GNUNET_strdup ("./"); /* give up */
296 }
297 n = strlen (fm) + 1 + strlen (fil_ptr) + 1;
298 buffer = GNUNET_malloc (n);
299 GNUNET_snprintf (buffer, n, "%s%s%s",
300 fm,
301 (fm[strlen (fm) - 1] ==
302 DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
303 GNUNET_free (fm);
304 return buffer;
305#else
306 fn = GNUNET_malloc (MAX_PATH + 1);
307
308 if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
309 {
310 SetErrnoFromWinError (lRet);
311 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
312 "plibc_conv_to_win_path");
313 return NULL;
314 }
315 /* is the path relative? */
316 if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0))
317 {
318 char szCurDir[MAX_PATH + 1];
319 lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir);
320 if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
321 {
322 SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
323 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
324 "GetCurrentDirectory");
325 return NULL;
326 }
327 buffer = GNUNET_malloc (MAX_PATH + 1);
328 GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn);
329 GNUNET_free (fn);
330 fn = buffer;
331 }
332
333 return fn;
334#endif
335}
336
337
338/**
339 * Give relative time in human-readable fancy format.
340 * @param delta time in milli seconds
341 */
342char *
343GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative del)
344{
345 const char *unit = _( /* time unit */ "ms");
346 char *ret;
347 uint64_t delta = del.value;
348
349 if (delta > 5 * 1000)
350 {
351 delta = delta / 1000;
352 unit = _( /* time unit */ "s");
353 if (delta > 5 * 60)
354 {
355 delta = delta / 60;
356 unit = _( /* time unit */ "m");
357 if (delta > 5 * 60)
358 {
359 delta = delta / 60;
360 unit = _( /* time unit */ "h");
361 if (delta > 5 * 24)
362 {
363 delta = delta / 24;
364 unit = _( /* time unit */ " days");
365 }
366 }
367 }
368 }
369 GNUNET_asprintf (&ret, "%llu%s", delta, unit);
370 return ret;
371}
372
373
374/**
375 * "man ctime_r", except for GNUnet time; also, unlike ctime, the
376 * return value does not include the newline character.
377 */
378char *
379GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
380{
381 time_t tt;
382 char *ret;
383
384 tt = t.value / 1000;
385#ifdef ctime_r
386 ret = ctime_r (&tt, GNUNET_malloc (32));
387#else
388 ret = GNUNET_strdup (ctime (&tt));
389#endif
390 ret[strlen (ret) - 1] = '\0';
391 return ret;
392}
393
394
395
396/* end of strings.c */
diff --git a/src/util/test_client.c b/src/util/test_client.c
new file mode 100644
index 000000000..5c2e552e9
--- /dev/null
+++ b/src/util/test_client.c
@@ -0,0 +1,195 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_client.c
22 * @brief tests for client.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_client_lib.h"
27#include "gnunet_configuration_lib.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_server_lib.h"
30#include "gnunet_time_lib.h"
31
32#define VERBOSE GNUNET_NO
33
34#define PORT 14325
35
36#define MYNAME "test_client"
37
38static struct GNUNET_CLIENT_Connection *client;
39
40static struct GNUNET_SERVER_Handle *server;
41
42static struct GNUNET_CONFIGURATION_Handle *cfg;
43
44#define MY_TYPE 130
45
46struct CopyContext
47{
48 struct GNUNET_SERVER_Client *client;
49 struct GNUNET_MessageHeader *cpy;
50};
51
52static size_t
53copy_msg (void *cls, size_t size, void *buf)
54{
55 struct CopyContext *ctx = cls;
56 struct GNUNET_MessageHeader *cpy = ctx->cpy;
57
58 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (cpy->size));
59 GNUNET_assert (size >= ntohs (cpy->size));
60 memcpy (buf, cpy, ntohs (cpy->size));
61 GNUNET_SERVER_receive_done (ctx->client, GNUNET_OK);
62 GNUNET_free (cpy);
63 GNUNET_free (ctx);
64 return sizeof (struct GNUNET_MessageHeader);
65}
66
67
68/**
69 * Callback that just bounces the message back to the sender.
70 */
71static void
72echo_cb (void *cls,
73 struct GNUNET_SERVER_Handle *server,
74 struct GNUNET_SERVER_Client *client,
75 const struct GNUNET_MessageHeader *message)
76{
77 struct CopyContext *cc;
78 struct GNUNET_MessageHeader *cpy;
79
80 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) ==
81 ntohs (message->size));
82 cc = GNUNET_malloc (sizeof (struct CopyContext));
83 cc->client = client;
84 cpy = GNUNET_malloc (ntohs (message->size));
85 memcpy (cpy, message, ntohs (message->size));
86 cc->cpy = cpy;
87 GNUNET_assert (NULL !=
88 GNUNET_SERVER_notify_transmit_ready (client,
89 ntohs (message->size),
90 GNUNET_TIME_UNIT_SECONDS,
91 &copy_msg, cc));
92}
93
94
95static struct GNUNET_SERVER_MessageHandler handlers[] = {
96 {&echo_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
97 {NULL, NULL, 0, 0}
98};
99
100
101static void
102recv_bounce (void *cls, const struct GNUNET_MessageHeader *got)
103{
104 int *ok = cls;
105 struct GNUNET_MessageHeader msg;
106
107 GNUNET_assert (got != NULL); /* timeout */
108 msg.type = htons (MY_TYPE);
109 msg.size = htons (sizeof (msg));
110 GNUNET_assert (0 == memcmp (got, &msg, sizeof (msg)));
111 GNUNET_CLIENT_disconnect (client);
112 client = NULL;
113 GNUNET_SERVER_destroy (server);
114 server = NULL;
115 *ok = 0;
116}
117
118
119static size_t
120make_msg (void *cls, size_t size, void *buf)
121{
122 struct GNUNET_MessageHeader *msg = buf;
123 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
124 msg->type = htons (MY_TYPE);
125 msg->size = htons (sizeof (msg));
126 return sizeof (struct GNUNET_MessageHeader);
127}
128
129
130static void
131task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
132{
133 struct sockaddr_in sa;
134
135 memset (&sa, 0, sizeof (sa));
136 sa.sin_family = AF_INET;
137 sa.sin_port = htons (PORT);
138 server = GNUNET_SERVER_create (tc->sched,
139 NULL,
140 NULL,
141 (const struct sockaddr *) &sa,
142 sizeof (sa),
143 1024,
144 GNUNET_TIME_relative_multiply
145 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
146 GNUNET_NO);
147 GNUNET_assert (server != NULL);
148 handlers[0].callback_cls = cls;
149 handlers[1].callback_cls = cls;
150 GNUNET_SERVER_add_handlers (server, handlers);
151 client = GNUNET_CLIENT_connect (tc->sched, MYNAME, cfg);
152 GNUNET_assert (client != NULL);
153 GNUNET_assert (NULL !=
154 GNUNET_CLIENT_notify_transmit_ready (client,
155 sizeof (struct
156 GNUNET_MessageHeader),
157 GNUNET_TIME_UNIT_SECONDS,
158 &make_msg, NULL));
159 GNUNET_CLIENT_receive (client, &recv_bounce, cls,
160 GNUNET_TIME_relative_multiply
161 (GNUNET_TIME_UNIT_MILLISECONDS, 250));
162}
163
164
165/**
166 * Main method, starts scheduler with task1,
167 * checks that "ok" is correct at the end.
168 */
169static int
170check ()
171{
172 int ok;
173
174 cfg = GNUNET_CONFIGURATION_create ();
175 GNUNET_CONFIGURATION_set_value_number (cfg, MYNAME, "PORT", PORT);
176 GNUNET_CONFIGURATION_set_value_string (cfg,
177 MYNAME, "HOSTNAME", "localhost");
178 ok = 1;
179 GNUNET_SCHEDULER_run (&task, &ok);
180 GNUNET_CONFIGURATION_destroy (cfg);
181 return ok;
182}
183
184int
185main (int argc, char *argv[])
186{
187 int ret = 0;
188
189 GNUNET_log_setup ("test_client", "WARNING", NULL);
190 ret += check ();
191
192 return ret;
193}
194
195/* end of test_client.c */
diff --git a/src/util/test_common_allocation.c b/src/util/test_common_allocation.c
new file mode 100644
index 000000000..76c7dc8a3
--- /dev/null
+++ b/src/util/test_common_allocation.c
@@ -0,0 +1,112 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_common_allocation.c
23 * @brief testcase for common_allocation.c
24 */
25#include "platform.h"
26#include "gnunet_common.h"
27
28static int
29check ()
30{
31#define MAX_TESTVAL 1024
32 char *ptrs[MAX_TESTVAL];
33 int i;
34 int j;
35 int k;
36 unsigned int ui;
37
38 /* GNUNET_malloc/GNUNET_free test */
39 k = 352; /* random start value */
40 for (i = 1; i < MAX_TESTVAL; i++)
41 {
42 ptrs[i] = GNUNET_malloc (i);
43 for (j = 0; j < i; j++)
44 ptrs[i][j] = k++;
45 }
46
47 for (i = MAX_TESTVAL - 1; i >= 1; i--)
48 {
49 for (j = i - 1; j >= 0; j--)
50 if (ptrs[i][j] != (char) --k)
51 return 1;
52 GNUNET_free (ptrs[i]);
53 }
54
55 /* GNUNET_free_non_null test */
56 GNUNET_free_non_null (NULL);
57 GNUNET_free_non_null (GNUNET_malloc (4));
58
59 /* GNUNET_strdup tests */
60 ptrs[0] = GNUNET_strdup ("bar");
61 if (0 != strcmp (ptrs[0], "bar"))
62 return 3;
63 /* now realloc */
64 ptrs[0] = GNUNET_realloc (ptrs[0], 12);
65 strcpy (ptrs[0], "Hello World");
66
67 GNUNET_free (ptrs[0]);
68 GNUNET_asprintf (&ptrs[0], "%s %s", "Hello", "World");
69 GNUNET_assert (strlen (ptrs[0]) == 11);
70 GNUNET_free (ptrs[0]);
71
72 /* GNUNET_array_grow tests */
73 ptrs[0] = NULL;
74 ui = 0;
75 GNUNET_array_grow (ptrs[0], ui, 42);
76 if (ui != 42)
77 return 4;
78 GNUNET_array_grow (ptrs[0], ui, 22);
79 if (ui != 22)
80 return 5;
81 for (j = 0; j < 22; j++)
82 ptrs[0][j] = j;
83 GNUNET_array_grow (ptrs[0], ui, 32);
84 for (j = 0; j < 22; j++)
85 if (ptrs[0][j] != j)
86 return 6;
87 for (j = 22; j < 32; j++)
88 if (ptrs[0][j] != 0)
89 return 7;
90 GNUNET_array_grow (ptrs[0], ui, 0);
91 if (i != 0)
92 return 8;
93 if (ptrs[0] != NULL)
94 return 9;
95
96
97 return 0;
98}
99
100int
101main (int argc, char *argv[])
102{
103 int ret;
104
105 GNUNET_log_setup ("test-common-allocation", "WARNING", NULL);
106 ret = check ();
107 if (ret != 0)
108 fprintf (stderr, "ERROR %d.\n", ret);
109 return ret;
110}
111
112/* end of test_common_allocation.c */
diff --git a/src/util/test_common_endian.c b/src/util/test_common_endian.c
new file mode 100644
index 000000000..43f163902
--- /dev/null
+++ b/src/util/test_common_endian.c
@@ -0,0 +1,42 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_common_endian.c
22 * @brief testcase for common_endian.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26
27#define CHECK(n) if (n != GNUNET_htonll(GNUNET_ntohll(n))) return 1;
28
29int
30main (int argc, char *argv[])
31{
32 GNUNET_log_setup ("test-common-endian", "WARNING", NULL);
33 CHECK (1);
34 CHECK (0x12345678);
35 CHECK (123456789012345LL);
36 if ((0x1234567890ABCDEFLL !=
37 GNUNET_htonll (0xEFCDAB9078563412LL)) && 42 != htonl (42))
38 return 1;
39 return 0;
40}
41
42/* end of test_common_endian.c */
diff --git a/src/util/test_common_logging.c b/src/util/test_common_logging.c
new file mode 100644
index 000000000..eb337aac4
--- /dev/null
+++ b/src/util/test_common_logging.c
@@ -0,0 +1,100 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_common_logging.c
23 * @brief testcase for the logging module
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28
29static void
30my_log (void *ctx, enum GNUNET_ErrorType kind,
31 const char *component, const char *date, const char *msg)
32{
33 unsigned int *c = ctx;
34 (*c)++;
35}
36
37
38
39int
40main (int argc, char *argv[])
41{
42 unsigned int failureCount = 0;
43 unsigned int logs = 0;
44
45 fclose (stderr);
46 stderr = NULL;
47 GNUNET_logger_add (&my_log, &logs);
48 GNUNET_logger_add (&my_log, &logs);
49 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
50 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
51 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
52 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
53 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
54 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
55 GNUNET_logger_remove (&my_log, &logs);
56 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Flusher...\n");
57 /* the last 6 calls should be merged (repated bulk messages!) */
58 GNUNET_logger_remove (&my_log, &logs);
59 if (logs != 4)
60 {
61 fprintf (stdout, "Expected 4 log calls, got %u\n", logs);
62 failureCount++;
63 }
64 GNUNET_break (0 ==
65 strcmp (_("ERROR"),
66 GNUNET_error_type_to_string
67 (GNUNET_ERROR_TYPE_ERROR)));
68 GNUNET_break (0 ==
69 strcmp (_("WARNING"),
70 GNUNET_error_type_to_string
71 (GNUNET_ERROR_TYPE_WARNING)));
72 GNUNET_break (0 ==
73 strcmp (_("INFO"),
74 GNUNET_error_type_to_string
75 (GNUNET_ERROR_TYPE_INFO)));
76 GNUNET_break (0 ==
77 strcmp (_("DEBUG"),
78 GNUNET_error_type_to_string
79 (GNUNET_ERROR_TYPE_DEBUG)));
80 GNUNET_log_setup ("test_common_logging", "WARNING", "/dev/null");
81 logs = 0;
82 GNUNET_logger_add (&my_log, &logs);
83 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Checker...\n");
84 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Drop me...\n");
85 GNUNET_logger_remove (&my_log, &logs);
86 if (logs != 1)
87 {
88 fprintf (stdout, "Expected 1 log call, got %u\n", logs);
89 failureCount++;
90 }
91
92 if (failureCount != 0)
93 {
94 fprintf (stdout, "%u TESTS FAILED!\n", failureCount);
95 return -1;
96 }
97 return 0;
98} /* end of main */
99
100/* end of test_common_logging.c */
diff --git a/src/util/test_configuration.c b/src/util/test_configuration.c
new file mode 100644
index 000000000..2f37c684c
--- /dev/null
+++ b/src/util/test_configuration.c
@@ -0,0 +1,229 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_configuration.c
22 * @brief Test that the configuration module works.
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_configuration_lib.h"
29
30static struct GNUNET_CONFIGURATION_Handle *cfg;
31
32static int
33testConfig ()
34{
35 char *c;
36 unsigned long long l;
37
38 if (GNUNET_OK !=
39 GNUNET_CONFIGURATION_get_value_string (cfg, "test", "b", &c))
40 return 1;
41 if (0 != strcmp ("b", c))
42 {
43 fprintf (stderr, "Got `%s'\n", c);
44 GNUNET_free (c);
45 return 2;
46 }
47 GNUNET_free (c);
48 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
49 "test", "five", &l))
50 return 3;
51 if (5 != l)
52 return 4;
53 GNUNET_CONFIGURATION_set_value_string (cfg, "more", "c", "YES");
54 if (GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno (cfg, "more", "c"))
55 return 5;
56 GNUNET_CONFIGURATION_set_value_number (cfg, "NUMBERS", "TEN", 10);
57 if (GNUNET_OK !=
58 GNUNET_CONFIGURATION_get_value_string (cfg, "NUMBERS", "TEN", &c))
59 return 6;
60 if (0 != strcmp (c, "10"))
61 {
62 GNUNET_free (c);
63 return 7;
64 }
65 GNUNET_free (c);
66
67 if (GNUNET_OK !=
68 GNUNET_CONFIGURATION_get_value_filename (cfg, "last", "test", &c))
69 return 8;
70 if (0 != strcmp (c, "/hello/world"))
71 {
72 GNUNET_free (c);
73 return 9;
74 }
75 GNUNET_free (c);
76
77 return 0;
78}
79
80static const char *want[] = {
81 "/Hello",
82 "/File Name",
83 "/World",
84 NULL,
85 NULL,
86};
87
88static int
89check (void *data, const char *fn)
90{
91 int *idx = data;
92
93 if (0 == strcmp (want[*idx], fn))
94 {
95 (*idx)++;
96 return GNUNET_OK;
97 }
98 return GNUNET_SYSERR;
99}
100
101static int
102testConfigFilenames ()
103{
104 int idx;
105
106 idx = 0;
107 if (3 != GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
108 "FILENAMES",
109 "test",
110 &check, &idx))
111 return 8;
112 if (idx != 3)
113 return 16;
114 if (GNUNET_OK !=
115 GNUNET_CONFIGURATION_remove_value_filename (cfg,
116 "FILENAMES",
117 "test", "/File Name"))
118 return 24;
119
120 if (GNUNET_NO !=
121 GNUNET_CONFIGURATION_remove_value_filename (cfg,
122 "FILENAMES",
123 "test", "/File Name"))
124 return 32;
125 if (GNUNET_NO !=
126 GNUNET_CONFIGURATION_remove_value_filename (cfg,
127 "FILENAMES",
128 "test", "Stuff"))
129 return 40;
130
131 if (GNUNET_NO !=
132 GNUNET_CONFIGURATION_append_value_filename (cfg,
133 "FILENAMES",
134 "test", "/Hello"))
135 return 48;
136 if (GNUNET_NO !=
137 GNUNET_CONFIGURATION_append_value_filename (cfg,
138 "FILENAMES",
139 "test", "/World"))
140 return 56;
141
142 if (GNUNET_YES !=
143 GNUNET_CONFIGURATION_append_value_filename (cfg,
144 "FILENAMES",
145 "test", "/File 1"))
146 return 64;
147
148 if (GNUNET_YES !=
149 GNUNET_CONFIGURATION_append_value_filename (cfg,
150 "FILENAMES",
151 "test", "/File 2"))
152 return 72;
153
154 idx = 0;
155 want[1] = "/World";
156 want[2] = "/File 1";
157 want[3] = "/File 2";
158 if (4 != GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
159 "FILENAMES",
160 "test",
161 &check, &idx))
162 return 80;
163 if (idx != 4)
164 return 88;
165 return 0;
166}
167
168int
169main (int argc, char *argv[])
170{
171 int failureCount = 0;
172 char *c;
173
174 GNUNET_log_setup ("test_configuration", "WARNING", NULL);
175 cfg = GNUNET_CONFIGURATION_create ();
176 GNUNET_assert (cfg != NULL);
177 if (GNUNET_OK !=
178 GNUNET_CONFIGURATION_parse (cfg, "test_configuration_data.conf"))
179 {
180 fprintf (stderr, "Failed to parse configuration file\n");
181 GNUNET_CONFIGURATION_destroy (cfg);
182 return 1;
183 }
184 failureCount += testConfig ();
185 failureCount += 2 * testConfigFilenames ();
186
187 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, "/tmp/gnunet-test.conf"))
188 {
189 fprintf (stderr, "Failed to write configuration file\n");
190 GNUNET_CONFIGURATION_destroy (cfg);
191 return 1;
192 }
193 GNUNET_CONFIGURATION_destroy (cfg);
194 GNUNET_assert (0 == UNLINK ("/tmp/gnunet-test.conf"));
195
196 cfg = GNUNET_CONFIGURATION_create ();
197 if (GNUNET_OK !=
198 GNUNET_CONFIGURATION_load (cfg, "test_configuration_data.conf"))
199 {
200 GNUNET_break (0);
201 GNUNET_CONFIGURATION_destroy (cfg);
202 return 1;
203 }
204 if ((GNUNET_OK !=
205 GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "WEAKRANDOM",
206 &c))
207 || (0 != strcmp (c, "YES")))
208 {
209 GNUNET_CONFIGURATION_destroy (cfg);
210 return 1;
211 }
212 GNUNET_free (c);
213 if ((GNUNET_OK !=
214 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME",
215 &c))
216 || (0 != strcmp (c, "/var/lib/gnunet/")))
217 {
218 GNUNET_CONFIGURATION_destroy (cfg);
219 return 1;
220 }
221 GNUNET_free (c);
222 GNUNET_CONFIGURATION_destroy (cfg);
223 if (failureCount != 0)
224 {
225 fprintf (stderr, "Test failed: %u\n", failureCount);
226 return 1;
227 }
228 return 0;
229}
diff --git a/src/util/test_configuration_data.conf b/src/util/test_configuration_data.conf
new file mode 100644
index 000000000..52b6b8220
--- /dev/null
+++ b/src/util/test_configuration_data.conf
@@ -0,0 +1,30 @@
1[PATHS]
2SUBST=/hello
3
4[GNUNET]
5SUBST=hello
6GNUNET_HOME=/tmp
7
8[test]
9a=a
10b=b
11five=5
12
13[more]
14c=c
15five=42
16
17
18[last]
19test = $SUBST/world
20boom = "1 2 3 testing"
21trailing = YES
22
23[FILENAMES]
24test = "/Hello /File\ Name /World"
25
26
27[TESTING]
28WEAKRANDOM = YES
29
30
diff --git a/src/util/test_container_bloomfilter.c b/src/util/test_container_bloomfilter.c
new file mode 100644
index 000000000..9beb11298
--- /dev/null
+++ b/src/util/test_container_bloomfilter.c
@@ -0,0 +1,246 @@
1/*
2 This file is part of GNUnet.
3 (C) 2004, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_container_bloomfilter.c
22 * @brief Testcase for the bloomfilter.
23 * @author Christian Grothoff
24 * @author Igor Wronsky
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30
31#define K 4
32#define SIZE 65536
33#define TESTFILE "/tmp/bloomtest.dat"
34
35/**
36 * Generate a random hashcode.
37 */
38static void
39nextHC (GNUNET_HashCode * hc)
40{
41 GNUNET_CRYPTO_hash_create_random (hc);
42}
43
44static int
45add_iterator (GNUNET_HashCode * next, void *arg)
46{
47 int *ret = arg;
48 GNUNET_HashCode pos;
49
50 if (0 == (*ret)--)
51 return GNUNET_NO;
52 nextHC (&pos);
53 *next = pos;
54 return GNUNET_YES;
55}
56
57int
58main (int argc, char *argv[])
59{
60 struct GNUNET_CONTAINER_BloomFilter *bf;
61 struct GNUNET_CONTAINER_BloomFilter *bfi;
62 GNUNET_HashCode tmp;
63 int i;
64 int ok1;
65 int ok2;
66 int falseok;
67 char buf[SIZE];
68 struct stat sbuf;
69
70 GNUNET_log_setup ("test-container-bloomfilter", "WARNING", NULL);
71 srand (1);
72 if (0 == stat (TESTFILE, &sbuf))
73 if (0 != UNLINK (TESTFILE))
74 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink", TESTFILE);
75 bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K);
76
77 for (i = 0; i < 200; i++)
78 {
79 nextHC (&tmp);
80 GNUNET_CONTAINER_bloomfilter_add (bf, &tmp);
81 }
82 srand (1);
83 ok1 = 0;
84 for (i = 0; i < 200; i++)
85 {
86 nextHC (&tmp);
87 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
88 ok1++;
89 }
90 if (ok1 != 200)
91 {
92 printf ("Got %d elements out of"
93 "200 expected after insertion.\n", ok1);
94 GNUNET_CONTAINER_bloomfilter_free (bf);
95 return -1;
96 }
97 if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_get_raw_data (bf, buf, SIZE))
98 {
99 GNUNET_CONTAINER_bloomfilter_free (bf);
100 return -1;
101 }
102
103 GNUNET_CONTAINER_bloomfilter_free (bf);
104
105 bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K);
106 GNUNET_assert (bf != NULL);
107 bfi = GNUNET_CONTAINER_bloomfilter_init (buf, SIZE, K);
108 GNUNET_assert (bfi != NULL);
109
110 srand (1);
111 ok1 = 0;
112 ok2 = 0;
113 for (i = 0; i < 200; i++)
114 {
115 nextHC (&tmp);
116 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
117 ok1++;
118 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
119 ok2++;
120 }
121 if (ok1 != 200)
122 {
123 printf ("Got %d elements out of 200 "
124 "expected after reloading.\n", ok1);
125 GNUNET_CONTAINER_bloomfilter_free (bf);
126 GNUNET_CONTAINER_bloomfilter_free (bfi);
127 return -1;
128 }
129
130 if (ok2 != 200)
131 {
132 printf ("Got %d elements out of 200 "
133 "expected after initialization.\n", ok2);
134 GNUNET_CONTAINER_bloomfilter_free (bf);
135 GNUNET_CONTAINER_bloomfilter_free (bfi);
136 return -1;
137 }
138
139 srand (1);
140 for (i = 0; i < 100; i++)
141 {
142 nextHC (&tmp);
143 GNUNET_CONTAINER_bloomfilter_remove (bf, &tmp);
144 GNUNET_CONTAINER_bloomfilter_remove (bfi, &tmp);
145 }
146
147 srand (1);
148
149 ok1 = 0;
150 ok2 = 0;
151 for (i = 0; i < 200; i++)
152 {
153 nextHC (&tmp);
154 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
155 ok1++;
156 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
157 ok2++;
158 }
159
160 if (ok1 != 100)
161 {
162 printf ("Expected 100 elements in loaded filter"
163 " after adding 200 and deleting 100, got %d\n", ok1);
164 GNUNET_CONTAINER_bloomfilter_free (bf);
165 GNUNET_CONTAINER_bloomfilter_free (bfi);
166 return -1;
167 }
168 if (ok2 != 200)
169 {
170 printf ("Expected 200 elements in initialized filter"
171 " after adding 200 and deleting 100 "
172 "(which should do nothing for a filter not backed by a file), got %d\n",
173 ok2);
174 GNUNET_CONTAINER_bloomfilter_free (bf);
175 GNUNET_CONTAINER_bloomfilter_free (bfi);
176 return -1;
177 }
178
179 srand (3);
180
181 GNUNET_CONTAINER_bloomfilter_clear (bf);
182 falseok = 0;
183 for (i = 0; i < 1000; i++)
184 {
185 nextHC (&tmp);
186 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
187 falseok++;
188 }
189 if (falseok > 0)
190 {
191 GNUNET_CONTAINER_bloomfilter_free (bf);
192 GNUNET_CONTAINER_bloomfilter_free (bfi);
193 return -1;
194 }
195
196 if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_or (bf, buf, SIZE))
197 {
198 GNUNET_CONTAINER_bloomfilter_free (bf);
199 GNUNET_CONTAINER_bloomfilter_free (bfi);
200 return -1;
201 }
202
203 srand (2);
204 i = 20;
205 GNUNET_CONTAINER_bloomfilter_resize (bfi, &add_iterator, &i, SIZE * 2, K);
206
207 srand (2);
208 i = 20;
209 GNUNET_CONTAINER_bloomfilter_resize (bf, &add_iterator, &i, SIZE * 2, K);
210 srand (2);
211
212 ok1 = 0;
213 ok2 = 0;
214 for (i = 0; i < 20; i++)
215 {
216 nextHC (&tmp);
217 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
218 ok1++;
219 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
220 ok2++;
221 }
222
223 if (ok1 != 20)
224 {
225 printf ("Expected 20 elements in resized file-backed filter"
226 " after adding 20, got %d\n", ok1);
227 GNUNET_CONTAINER_bloomfilter_free (bf);
228 GNUNET_CONTAINER_bloomfilter_free (bfi);
229 return -1;
230 }
231 if (ok2 != 20)
232 {
233 printf ("Expected 20 elements in resized filter"
234 " after adding 20, got %d\n", ok2);
235 GNUNET_CONTAINER_bloomfilter_free (bf);
236 GNUNET_CONTAINER_bloomfilter_free (bfi);
237 return -1;
238 }
239
240
241 GNUNET_CONTAINER_bloomfilter_free (bf);
242 GNUNET_CONTAINER_bloomfilter_free (bfi);
243
244 GNUNET_break (0 == UNLINK (TESTFILE));
245 return 0;
246}
diff --git a/src/util/test_container_heap.c b/src/util/test_container_heap.c
new file mode 100644
index 000000000..511589af5
--- /dev/null
+++ b/src/util/test_container_heap.c
@@ -0,0 +1,112 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @author Nathan Evans
23 * @file util/containers/heaptest.c
24 * @brief Test of heap operations
25 */
26
27#include "gnunet_util.h"
28#include "gnunet_util_containers.h"
29#include "dv.h"
30
31static int
32iterator_callback (void *element, GNUNET_CONTAINER_HeapCost cost,
33 struct GNUNET_CONTAINER_Heap *root, void *cls)
34{
35 struct GNUNET_dv_neighbor *node;
36 node = (struct GNUNET_dv_neighbor *) element;
37 fprintf (stdout, "%d\n", node->cost);
38 //fprintf (stdout, "%d\n", ((struct GNUNET_dv_neighbor *)element)->cost);
39
40 return GNUNET_OK;
41}
42
43
44int
45main (int argc, char **argv)
46{
47 struct GNUNET_CONTAINER_Heap *myHeap;
48 struct GNUNET_dv_neighbor *neighbor1;
49 struct GNUNET_dv_neighbor *neighbor2;
50 struct GNUNET_dv_neighbor *neighbor3;
51 struct GNUNET_dv_neighbor *neighbor4;
52 struct GNUNET_dv_neighbor *neighbor5;
53 struct GNUNET_dv_neighbor *neighbor6;
54
55 GNUNET_log_setup ("test-container-heap", "WARNING", NULL);
56
57 myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
58
59 neighbor1 = malloc (sizeof (struct GNUNET_dv_neighbor));
60 neighbor2 = malloc (sizeof (struct GNUNET_dv_neighbor));
61 neighbor3 = malloc (sizeof (struct GNUNET_dv_neighbor));
62 neighbor4 = malloc (sizeof (struct GNUNET_dv_neighbor));
63 neighbor5 = malloc (sizeof (struct GNUNET_dv_neighbor));
64 neighbor6 = malloc (sizeof (struct GNUNET_dv_neighbor));
65
66 neighbor1->cost = 60;
67 neighbor2->cost = 50;
68 neighbor3->cost = 70;
69 neighbor4->cost = 120;
70 neighbor5->cost = 100;
71 neighbor6->cost = 30;
72
73 GNUNET_CONTAINER_heap_insert (myHeap, neighbor1, neighbor1->cost);
74 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
75
76 fprintf (stdout, "\n");
77 GNUNET_CONTAINER_heap_insert (myHeap, neighbor2, neighbor2->cost);
78
79 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
80 fprintf (stdout, "\n");
81 GNUNET_CONTAINER_heap_insert (myHeap, neighbor3, neighbor3->cost);
82
83 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
84 fprintf (stdout, "\n");
85 GNUNET_CONTAINER_heap_insert (myHeap, neighbor4, neighbor4->cost);
86
87 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
88 fprintf (stdout, "\n");
89 GNUNET_CONTAINER_heap_insert (myHeap, neighbor5, neighbor5->cost);
90
91 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
92 fprintf (stdout, "\n");
93 GNUNET_CONTAINER_heap_insert (myHeap, neighbor6, neighbor6->cost);
94
95 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
96 fprintf (stdout, "\n");
97 GNUNET_CONTAINER_heap_remove_node (myHeap, neighbor5);
98
99 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
100 fprintf (stdout, "\n");
101 GNUNET_CONTAINER_heap_remove_root (myHeap);
102
103 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
104 fprintf (stdout, "\n");
105 GNUNET_CONTAINER_heap_update_cost (myHeap, neighbor6, 200);
106
107 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
108 fprintf (stdout, "\n");
109 return 0;
110}
111
112/* end of heaptest.c */
diff --git a/src/util/test_container_meta_data.c b/src/util/test_container_meta_data.c
new file mode 100644
index 000000000..17ef79161
--- /dev/null
+++ b/src/util/test_container_meta_data.c
@@ -0,0 +1,262 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_container_meta_data.c
23 * @brief Test for container_meta_data.c
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30
31#define ABORT(m) { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); if (m != NULL) GNUNET_CONTAINER_meta_data_destroy(m); return 1; }
32
33static int
34testMeta (int i)
35{
36 struct GNUNET_CONTAINER_MetaData *m;
37 char *val;
38 int j;
39 unsigned int size;
40
41 m = GNUNET_CONTAINER_meta_data_create ();
42 if (GNUNET_OK !=
43 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_TITLE, "TestTitle"))
44 ABORT (m);
45 if (GNUNET_OK !=
46 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_AUTHOR, "TestTitle"))
47 ABORT (m);
48 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_TITLE, "TestTitle")) /* dup! */
49 ABORT (m);
50 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_AUTHOR, "TestTitle")) /* dup! */
51 ABORT (m);
52 if (2 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
53 ABORT (m);
54 if (GNUNET_OK !=
55 GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_AUTHOR, "TestTitle"))
56 ABORT (m);
57 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_AUTHOR, "TestTitle")) /* already gone */
58 ABORT (m);
59 if (1 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
60 ABORT (m);
61 if (GNUNET_OK !=
62 GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_TITLE, "TestTitle"))
63 ABORT (m);
64 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_TITLE, "TestTitle")) /* already gone */
65 ABORT (m);
66 if (0 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
67 ABORT (m);
68 val = GNUNET_malloc (256);
69 for (j = 0; j < i; j++)
70 {
71 GNUNET_snprintf (val, 256, "%s.%d",
72 "A teststring that should compress well.", j);
73 if (GNUNET_OK !=
74 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_UNKNOWN, val))
75 {
76 GNUNET_free (val);
77 ABORT (m);
78 }
79 }
80 GNUNET_free (val);
81 if (i != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
82 ABORT (m);
83
84 size =
85 GNUNET_CONTAINER_meta_data_get_serialized_size (m,
86 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
87 val = GNUNET_malloc (size);
88 if (size != GNUNET_CONTAINER_meta_data_serialize (m, val, size,
89 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
90 {
91 GNUNET_free (val);
92 ABORT (m);
93 }
94 GNUNET_CONTAINER_meta_data_destroy (m);
95 m = GNUNET_CONTAINER_meta_data_deserialize (val, size);
96 GNUNET_free (val);
97 if (m == NULL)
98 ABORT (m);
99 val = GNUNET_malloc (256);
100 for (j = 0; j < i; j++)
101 {
102 GNUNET_snprintf (val, 256, "%s.%d",
103 "A teststring that should compress well.", j);
104 if (GNUNET_OK !=
105 GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_UNKNOWN, val))
106 {
107 GNUNET_free (val);
108 ABORT (m);
109 }
110 }
111 GNUNET_free (val);
112 if (0 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
113 {
114 ABORT (m);
115 }
116 GNUNET_CONTAINER_meta_data_destroy (m);
117 return 0;
118}
119
120int
121testMetaMore (int i)
122{
123 struct GNUNET_CONTAINER_MetaData *meta;
124 int q;
125 char txt[128];
126 char *data;
127 unsigned long long size;
128
129 meta = GNUNET_CONTAINER_meta_data_create ();
130 for (q = 0; q <= i; q++)
131 {
132 GNUNET_snprintf (txt, 128, "%u -- %u\n", i, q);
133 GNUNET_CONTAINER_meta_data_insert (meta,
134 q %
135 EXTRACTOR_getHighestKeywordTypeNumber
136 (), txt);
137 }
138 size =
139 GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
140 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
141 data = GNUNET_malloc (size * 4);
142 if (size != GNUNET_CONTAINER_meta_data_serialize (meta,
143 data, size * 4,
144 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
145 {
146 GNUNET_free (data);
147 ABORT (meta);
148 }
149 GNUNET_CONTAINER_meta_data_destroy (meta);
150 GNUNET_free (data);
151 return 0;
152}
153
154static int
155testMetaLink ()
156{
157 struct GNUNET_CONTAINER_MetaData *m;
158 char *val;
159 unsigned int size;
160
161 m = GNUNET_CONTAINER_meta_data_create ();
162 if (GNUNET_OK !=
163 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_UNKNOWN, "link"))
164 ABORT (m);
165 if (GNUNET_OK !=
166 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_FILENAME,
167 "lib-link.m4"))
168 ABORT (m);
169 size =
170 GNUNET_CONTAINER_meta_data_get_serialized_size (m,
171 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
172 val = GNUNET_malloc (size);
173 if (size != GNUNET_CONTAINER_meta_data_serialize (m, val, size,
174 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
175 {
176 GNUNET_free (val);
177 ABORT (m);
178 }
179 GNUNET_CONTAINER_meta_data_destroy (m);
180 m = GNUNET_CONTAINER_meta_data_deserialize (val, size);
181 GNUNET_free (val);
182 if (m == NULL)
183 ABORT (m);
184 GNUNET_CONTAINER_meta_data_destroy (m);
185 return 0;
186}
187
188
189static int
190testThumbnail ()
191{
192 struct GNUNET_CONTAINER_MetaData *m;
193 struct GNUNET_CONTAINER_MetaData *d;
194 EXTRACTOR_ExtractorList *ex;
195 unsigned char *thumb;
196 size_t size;
197 char *date;
198
199 ex = EXTRACTOR_loadConfigLibraries (NULL, "libextractor_thumbnail");
200 if (ex == NULL)
201 {
202 fprintf (stderr,
203 "Test incomplete, have no thumbnail extractor available.\n");
204 return 0; /* can not test, no thumbnailer */
205 }
206 ex = EXTRACTOR_loadConfigLibraries (ex, "libextractor_mime");
207 m = GNUNET_CONTAINER_meta_data_create ();
208 if (3 != GNUNET_CONTAINER_meta_data_extract_from_file (m,
209 "test_container_meta_data_image.jpg",
210 ex))
211 {
212 GNUNET_break (0);
213 EXTRACTOR_removeAll (ex);
214 GNUNET_CONTAINER_meta_data_destroy (m);
215 return 1;
216 }
217 EXTRACTOR_removeAll (ex);
218 d = GNUNET_CONTAINER_meta_data_duplicate (m);
219 GNUNET_CONTAINER_meta_data_destroy (m);
220 size = GNUNET_CONTAINER_meta_data_get_thumbnail (d, &thumb);
221 if (size == 0)
222 {
223 GNUNET_break (0);
224 GNUNET_CONTAINER_meta_data_destroy (d);
225 return 1;
226 }
227 GNUNET_free (thumb);
228 GNUNET_CONTAINER_meta_data_add_publication_date (d);
229 date = GNUNET_CONTAINER_meta_data_get_by_type (d,
230 EXTRACTOR_PUBLICATION_DATE);
231 if (date == NULL)
232 {
233 GNUNET_break (0);
234 GNUNET_CONTAINER_meta_data_destroy (d);
235 return 1;
236 }
237 GNUNET_free (date);
238 GNUNET_CONTAINER_meta_data_destroy (d);
239 return 0;
240}
241
242
243int
244main (int argc, char *argv[])
245{
246 int failureCount = 0;
247 int i;
248
249 GNUNET_log_setup ("test-container-meta-data", "WARNING", NULL);
250 for (i = 0; i < 255; i++)
251 failureCount += testMeta (i);
252 for (i = 1; i < 255; i++)
253 failureCount += testMetaMore (i);
254 failureCount += testMetaLink ();
255 failureCount += testThumbnail ();
256
257 if (failureCount != 0)
258 return 1;
259 return 0;
260}
261
262/* end of metatest.c */
diff --git a/src/util/test_container_meta_data_image.jpg b/src/util/test_container_meta_data_image.jpg
new file mode 100644
index 000000000..3d1ba3307
--- /dev/null
+++ b/src/util/test_container_meta_data_image.jpg
Binary files differ
diff --git a/src/util/test_container_multihashmap.c b/src/util/test_container_multihashmap.c
new file mode 100644
index 000000000..e92ab49ba
--- /dev/null
+++ b/src/util/test_container_multihashmap.c
@@ -0,0 +1,113 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_container_multihashmap.c
23 * @brief Test for container_multihashmap.c
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30
31#define ABORT() { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); if (m != NULL) GNUNET_CONTAINER_multihashmap_destroy(m); return 1; }
32#define CHECK(c) { if (! (c)) ABORT(); }
33
34static int
35testMap (int i)
36{
37 struct GNUNET_CONTAINER_MultiHashMap *m;
38 GNUNET_HashCode k1;
39 GNUNET_HashCode k2;
40 int j;
41 void *r;
42
43 CHECK (NULL != (m = GNUNET_CONTAINER_multihashmap_create (i)));
44 memset (&k1, 0, sizeof (k1));
45 memset (&k2, 1, sizeof (k2));
46 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k1));
47 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2));
48 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k1, NULL));
49 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k2, NULL));
50 CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k1));
51 CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k2));
52 CHECK (0 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1));
53 CHECK (0 == GNUNET_CONTAINER_multihashmap_size (m));
54 CHECK (0 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL));
55 CHECK (0 ==
56 GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL));
57
58 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
59 &k1,
60 "v1",
61 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
62 CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m));
63 CHECK (0 == strcmp ("v1", GNUNET_CONTAINER_multihashmap_get (m, &k1)));
64 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_put (m,
65 &k1,
66 "v1",
67 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
68 CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m));
69 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
70 &k1,
71 "v2",
72 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
73 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
74 &k1,
75 "v3",
76 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
77 CHECK (3 == GNUNET_CONTAINER_multihashmap_size (m));
78 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (m, &k1, "v3"));
79 CHECK (2 == GNUNET_CONTAINER_multihashmap_size (m));
80 CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (m, &k1));
81 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2));
82 CHECK (2 ==
83 GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL));
84 CHECK (0 ==
85 GNUNET_CONTAINER_multihashmap_get_multiple (m, &k2, NULL, NULL));
86 CHECK (2 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL));
87 r = GNUNET_CONTAINER_multihashmap_get_random (m);
88 CHECK (0 == strcmp (r, "v1") || 0 == strcmp (r, "v2"));
89 CHECK (2 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1));
90 for (j = 0; j < 1024; j++)
91 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
92 &k1,
93 "v2",
94 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
95 GNUNET_CONTAINER_multihashmap_destroy (m);
96 return 0;
97}
98
99int
100main (int argc, char *argv[])
101{
102 int failureCount = 0;
103 int i;
104
105 GNUNET_log_setup ("test-container-multihashmap", "WARNING", NULL);
106 for (i = 1; i < 255; i++)
107 failureCount += testMap (i);
108 if (failureCount != 0)
109 return 1;
110 return 0;
111}
112
113/* end of maptest.c */
diff --git a/src/util/test_crypto_aes.c b/src/util/test_crypto_aes.c
new file mode 100644
index 000000000..cdae243e0
--- /dev/null
+++ b/src/util/test_crypto_aes.c
@@ -0,0 +1,180 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21/**
22 * @author Christian Grothoff
23 * @file util/test_crypto_aes.c
24 * @brief test for AES ciphers
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29
30#define TESTSTRING "Hello World!"
31#define INITVALUE "InitializationVectorValue"
32
33static int
34testSymcipher ()
35{
36 struct GNUNET_CRYPTO_AesSessionKey key;
37 char result[100];
38 int size;
39 char res[100];
40
41 GNUNET_CRYPTO_aes_create_session_key (&key);
42 size = GNUNET_CRYPTO_aes_encrypt (TESTSTRING,
43 strlen (TESTSTRING) + 1,
44 &key,
45 (const struct
46 GNUNET_CRYPTO_AesInitializationVector *)
47 INITVALUE, result);
48 if (size == -1)
49 {
50 printf ("symciphertest failed: encryptBlock returned %d\n", size);
51 return 1;
52 }
53 size = GNUNET_CRYPTO_aes_decrypt (&key,
54 result, size,
55 (const struct
56 GNUNET_CRYPTO_AesInitializationVector *)
57 INITVALUE, res);
58 if (strlen (TESTSTRING) + 1 != size)
59 {
60 printf ("symciphertest failed: decryptBlock returned %d\n", size);
61 return 1;
62 }
63 if (0 != strcmp (res, TESTSTRING))
64 {
65 printf ("symciphertest failed: %s != %s\n", res, TESTSTRING);
66 return 1;
67 }
68 else
69 return 0;
70}
71
72int
73verifyCrypto ()
74{
75 struct GNUNET_CRYPTO_AesSessionKey key;
76 char result[GNUNET_CRYPTO_AES_KEY_LENGTH];
77 char *res;
78 int ret;
79
80 unsigned char plain[] =
81 { 29, 128, 192, 253, 74, 171, 38, 187, 84, 219, 76, 76, 209, 118, 33, 249,
82 172, 124, 96, 9, 157, 110, 8, 215, 200, 63, 69, 230, 157, 104, 247, 164
83 };
84 unsigned char raw_key[] =
85 { 106, 74, 209, 88, 145, 55, 189, 135, 125, 180, 225, 108, 183, 54, 25,
86 169, 129, 188, 131, 75, 227, 245, 105, 10, 225, 15, 115, 159, 148, 184,
87 34, 191
88 };
89 unsigned char encrresult[] =
90 { 167, 102, 230, 233, 127, 195, 176, 107, 17, 91, 199, 127, 96, 113, 75,
91 195, 245, 217, 61, 236, 159, 165, 103, 121, 203, 99, 202, 41, 23, 222, 25,
92 102, 1
93 };
94
95 res = NULL;
96 ret = 0;
97
98 memcpy (key.key, raw_key, GNUNET_CRYPTO_AES_KEY_LENGTH);
99 key.crc32 =
100 htonl (GNUNET_CRYPTO_crc32_n (&key, GNUNET_CRYPTO_AES_KEY_LENGTH));
101
102 if (ntohl (key.crc32) != (unsigned int) 38125195LL)
103 {
104 printf ("Static key has different CRC: %u - %u\n",
105 ntohl (key.crc32), key.crc32);
106
107 ret = 1;
108 goto error;
109 }
110
111 if (GNUNET_CRYPTO_AES_KEY_LENGTH !=
112 GNUNET_CRYPTO_aes_encrypt (plain,
113 GNUNET_CRYPTO_AES_KEY_LENGTH,
114 &key,
115 (const struct
116 GNUNET_CRYPTO_AesInitializationVector *)
117 "testtesttesttest", result))
118 {
119 printf ("Wrong return value from encrypt block.\n");
120 ret = 1;
121 goto error;
122 }
123
124 if (memcmp (encrresult, result, GNUNET_CRYPTO_AES_KEY_LENGTH) != 0)
125 {
126 printf ("Encrypted result wrong.\n");
127 ret = 1;
128 goto error;
129 }
130
131 res = GNUNET_malloc (GNUNET_CRYPTO_AES_KEY_LENGTH);
132
133 if (GNUNET_CRYPTO_AES_KEY_LENGTH !=
134 GNUNET_CRYPTO_aes_decrypt (&key,
135 result,
136 GNUNET_CRYPTO_AES_KEY_LENGTH,
137 (const struct
138 GNUNET_CRYPTO_AesInitializationVector *)
139 "testtesttesttest", res))
140 {
141 printf ("Wrong return value from decrypt block.\n");
142 ret = 1;
143 goto error;
144 }
145
146 if (memcmp (res, plain, GNUNET_CRYPTO_AES_KEY_LENGTH) != 0)
147 {
148 printf ("Decrypted result does not match input.\n");
149
150 ret = 1;
151 }
152
153error:
154
155 GNUNET_free_non_null (res);
156
157 return ret;
158}
159
160int
161main (int argc, char *argv[])
162{
163 int failureCount = 0;
164
165 GNUNET_log_setup ("test-crypto-aes", "WARNING", NULL);
166 GNUNET_CRYPTO_random_disable_entropy_gathering ();
167 GNUNET_assert (strlen (INITVALUE) >
168 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
169 failureCount += testSymcipher ();
170 failureCount += verifyCrypto ();
171
172 if (failureCount != 0)
173 {
174 printf ("%d TESTS FAILED!\n", failureCount);
175 return -1;
176 }
177 return 0;
178}
179
180/* end of test_crypto_aes.c */
diff --git a/src/util/test_crypto_aes_weak.c b/src/util/test_crypto_aes_weak.c
new file mode 100644
index 000000000..27ee57968
--- /dev/null
+++ b/src/util/test_crypto_aes_weak.c
@@ -0,0 +1,203 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @author Krista Bennett
24 * @author Christian Grothoff
25 * @file util/test_crypto_aes_weak.c
26 * @brief AES weak key test.
27 */
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_crypto_lib.h"
31#include <gcrypt.h>
32
33#define MAX_WEAK_KEY_TRIALS 100000
34#define GENERATE_WEAK_KEYS GNUNET_NO
35#define WEAK_KEY_TESTSTRING "I hate weak keys."
36
37static void
38printWeakKey (struct GNUNET_CRYPTO_AesSessionKey *key)
39{
40 int i;
41 for (i = 0; i < GNUNET_CRYPTO_AES_KEY_LENGTH; i++)
42 {
43 printf ("%x ", (int) (key->key[i]));
44 }
45}
46
47static int
48testWeakKey ()
49{
50 char result[100];
51 char res[100];
52 int size;
53 struct GNUNET_CRYPTO_AesSessionKey weak_key;
54 struct GNUNET_CRYPTO_AesInitializationVector INITVALUE;
55
56 memset (&INITVALUE, 42,
57 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
58 /* sorry, this is not a weak key -- I don't have
59 any at the moment! */
60 weak_key.key[0] = (char) (0x4c);
61 weak_key.key[1] = (char) (0x31);
62 weak_key.key[2] = (char) (0xc6);
63 weak_key.key[3] = (char) (0x2b);
64 weak_key.key[4] = (char) (0xc1);
65 weak_key.key[5] = (char) (0x5f);
66 weak_key.key[6] = (char) (0x4d);
67 weak_key.key[7] = (char) (0x1f);
68 weak_key.key[8] = (char) (0x31);
69 weak_key.key[9] = (char) (0xaa);
70 weak_key.key[10] = (char) (0x12);
71 weak_key.key[11] = (char) (0x2e);
72 weak_key.key[12] = (char) (0xb7);
73 weak_key.key[13] = (char) (0x82);
74 weak_key.key[14] = (char) (0xc0);
75 weak_key.key[15] = (char) (0xb6);
76 weak_key.key[16] = (char) (0x4d);
77 weak_key.key[17] = (char) (0x1f);
78 weak_key.key[18] = (char) (0x31);
79 weak_key.key[19] = (char) (0xaa);
80 weak_key.key[20] = (char) (0x4c);
81 weak_key.key[21] = (char) (0x31);
82 weak_key.key[22] = (char) (0xc6);
83 weak_key.key[23] = (char) (0x2b);
84 weak_key.key[24] = (char) (0xc1);
85 weak_key.key[25] = (char) (0x5f);
86 weak_key.key[26] = (char) (0x4d);
87 weak_key.key[27] = (char) (0x1f);
88 weak_key.key[28] = (char) (0x31);
89 weak_key.key[29] = (char) (0xaa);
90 weak_key.key[30] = (char) (0xaa);
91 weak_key.key[31] = (char) (0xaa);
92 /* memset(&weak_key, 0, 32); */
93 weak_key.crc32 =
94 htonl (GNUNET_CRYPTO_crc32_n (&weak_key, GNUNET_CRYPTO_AES_KEY_LENGTH));
95
96 size = GNUNET_CRYPTO_aes_encrypt (WEAK_KEY_TESTSTRING,
97 strlen (WEAK_KEY_TESTSTRING) + 1,
98 &weak_key, &INITVALUE, result);
99
100 if (size == -1)
101 {
102 GNUNET_break (0);
103 return 1;
104 }
105
106 size = GNUNET_CRYPTO_aes_decrypt (&weak_key, result, size, &INITVALUE, res);
107
108 if ((strlen (WEAK_KEY_TESTSTRING) + 1) != size)
109 {
110 GNUNET_break (0);
111 return 1;
112 }
113 if (0 != strcmp (res, WEAK_KEY_TESTSTRING))
114 {
115 GNUNET_break (0);
116 return 1;
117 }
118 else
119 return 0;
120}
121
122static int
123getWeakKeys ()
124{
125 struct GNUNET_CRYPTO_AesSessionKey sessionkey;
126 int number_of_weak_keys = 0;
127 int number_of_runs;
128
129 gcry_cipher_hd_t handle;
130 int rc;
131
132 for (number_of_runs = 0; number_of_runs < MAX_WEAK_KEY_TRIALS;
133 number_of_runs++)
134 {
135
136 if (number_of_runs % 1000 == 0)
137 fprintf (stderr, ".");
138 /*printf("Got to run number %d.\n", number_of_runs); */
139 GNUNET_CRYPTO_aes_create_session_key (&sessionkey);
140
141 rc = gcry_cipher_open (&handle,
142 GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 0);
143
144 if (rc)
145 {
146 printf ("testweakkey: gcry_cipher_open failed on trial %d. %s\n",
147 number_of_runs, gcry_strerror (rc));
148 rc = 0;
149 continue;
150 }
151
152 rc =
153 gcry_cipher_setkey (handle, &sessionkey,
154 GNUNET_CRYPTO_AES_KEY_LENGTH);
155
156 if ((char) rc == GPG_ERR_WEAK_KEY)
157 {
158 printf ("\nWeak key (in hex): ");
159 printWeakKey (&sessionkey);
160 printf ("\n");
161 number_of_weak_keys++;
162 }
163 else if (rc)
164 {
165 printf ("\nUnexpected error generating keys. Error is %s\n",
166 gcry_strerror (rc));
167 }
168
169 gcry_cipher_close (handle);
170
171 }
172
173 return number_of_weak_keys;
174}
175
176int
177main (int argc, char *argv[])
178{
179 int weak_keys;
180
181 GNUNET_log_setup ("test-crypto-aes-weak", "WARNING", NULL);
182 GNUNET_CRYPTO_random_disable_entropy_gathering ();
183 if (GENERATE_WEAK_KEYS)
184 {
185 weak_keys = getWeakKeys ();
186
187 if (weak_keys == 0)
188 {
189 printf ("\nNo weak keys found in %d runs.\n", MAX_WEAK_KEY_TRIALS);
190 }
191 else
192 {
193 printf ("\n%d weak keys found in %d runs.\n",
194 weak_keys, MAX_WEAK_KEY_TRIALS);
195 }
196 }
197
198 if (testWeakKey () != 0)
199 return -1;
200 return 0;
201}
202
203/* end of weakkeytest.c */
diff --git a/src/util/test_crypto_crc.c b/src/util/test_crypto_crc.c
new file mode 100644
index 000000000..d1505516b
--- /dev/null
+++ b/src/util/test_crypto_crc.c
@@ -0,0 +1,221 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20 For the actual CRC code:
21 Copyright abandoned; this code is in the public domain.
22 Provided to GNUnet by peter@horizon.com
23*/
24
25/**
26 * @file util/test_crypto_crc.c
27 * @brief testcase for crypto_crc.c
28 */
29#include "platform.h"
30#include "gnunet_common.h"
31#include "gnunet_crypto_lib.h"
32
33static int expected[] = {
34 -1223996378, 929797997, -1048047323, 1791081351, -425765913, 2138425902,
35 82584863, 1939615314, 1806463044, -1505003452, 1878277636, -997353517,
36 201238705, 1723258694, -1107452366, -344562561, -1102247383, 1973035265,
37 715213337, -1886586005, 2021214515, -1387332962, 593019378, -571088044,
38 1412577760, 412164558, -1626111170, 1556494863, -289796528, -850404775,
39 2066714587, -911838105, -1426027382, 499684507, -835420055, 1817119454,
40 -1221795958, 1516966784, -1038806877, -2115880691, 532627620, 1984437415,
41 -396341583, -1345366324, -590766745, -1801923449, 1752427988, -386896390,
42 453906317, 1552589433, -858925718, 1160445643, -740188079, -486609040,
43 1102529269, -515846212, -1614217202, 1572162207, 943558923, -467330358,
44 -1870764193, 1477005328, -793029208, -888983175, -696956020, 842706021,
45 1642390067, -805889494, 1284862057, 1562545388, 2091626273, 1852404553,
46 -2076508101, 370903003, 1186422975, 1936085227, 769358463, 180401058,
47 2032612572, -105461719, -1119935472, 617249831, 1169304728, 1771205256,
48 -2042554284, 653270859, -918610713, 336081663, -913685370, 1962213744,
49 -505406126, -838622649, -1141518710, 893143582, -1330296611, 122119483,
50 1111564496, 688811976, 1016241049, -1803438473, 359630107, 1034798954,
51 -581359286, 1590946527, -389997034, 2020318460, 1695967527, -464069727,
52 -862641495, -1405012109, -771244841, 738226150, -1035328134, -933945474,
53 1254965774, 1661863830, -884127998, 1800460481, 814702567, -1214068102,
54 -541120421, 1898656429, -236825530, 1505866267, 1252462132, -981007520,
55 1502096471, -2134644056, 483221797, 1276403836, 541133290, -1234093967,
56 350748780, 257941070, 1030457090, 434988890, -1098135432, -1000556640,
57 -577128022, 644806294, -787536281, -1288346343, 998079404, 1259353935,
58 955771631, -958377466, 1746756252, 451579658, 1913409243, -952026299,
59 -1556035958, -830279881, 834744289, -1878491428, 700000962, -1027245802,
60 1393574384, -1260409147, -841420884, 892132797, 1494730226, -1649181766,
61 1651097838, -1041807403, -1916675721, -1324525963, 157405899, -655788033,
62 -1943555237, -79747022, 339721623, -138341083, 1111902411, -435322914,
63 -533294200, -190220608, -1718346014, -1631301894, 1706265243, 745533899,
64 1351941230, 1803009594, -1218191958, 1467751062, 84368433, -711251880,
65 1699423788, -768792716, 846639904, 2103267723, -2095288070, -440571408,
66 -362144485, 2020468971, 352105963, -849211036, -1272592429, 1743440467,
67 2020667861, -1649992312, 172682343, 816705364, -1990206923, 902689869,
68 -298510060, 164207498, 190378213, 242531543, 113383268, 304810777,
69 -1081099373, 819221134, -1100982926, -855941239, 1091308887, -934548124,
70 520508733, -1381763773, -491593287, -2143492665, 700894653, -2049034808,
71 -160942046, -2009323577, 1464245054, 1584746011, -768646852, -993282698,
72 1265838699, -1873820824, 575704373, -986682955, 1270688416, 88587481,
73 -1723991633, -409928242, 866669946, -483811323, -181759253, -963525431,
74 -1686612238, -1663460076, -1128449775, -1368922329, 122318131, 795862385,
75 528576131, -19927090, 1369299478, 1285665642, -738964611, 1328292127,
76 552041252, -1431494354, -1205275362, 42768297, -1329537238, -449177266,
77 943925221, 987016465, -945138414, -270064876, 1650366626, -369252552,
78 582030210, -1229235374, 147901387, -517510506, -1609742888, -1086838308,
79 1391998445, -313975512, -613392078, 855706229, 1475706341, -1112105406,
80 2032001400, 1565777625, 2030937777, 435522421, 1823527907, -691390605,
81 -827253664, 1057171580, -314146639, -630099999, -1347514552, 478716232,
82 -1533658804, -1425371979, 761987780, 1560243817, -1945893959, 1205759225,
83 -959343783, -576742354, -154125407, -1158108776, 1183788580, 1354198127,
84 -1534207721, -823991517, -170534462, -912524170, 1858513573, 467072185,
85 2091040157, -1765027018, -1659401643, -1173890143, -1912754057, -84568053,
86 2010781784, -921970156, 944508352, -922040609, 1055102010, 1018688871,
87 -1186761311, -2012263648, 1311654161, 277659086, 2029602288, 1127061510,
88 1029452642, 285677123, -188521091, -641039012, 653836416, -805916340,
89 -1644860596, 1352872213, 691634876, -1477113308, -748430369, 1030697363,
90 -2007864449, -1196662616, 1313997192, 177342476, -566676450, -1118618118,
91 1697953104, 344671484, -1489783116, -889507873, 1259591310, -716567168,
92 2116447062, 324368527, 1789366816, 1558930442, 1950250221, -785460151,
93 1174714258, -430047304, -859487565, -580633932, 607732845, -1128150220,
94 1544355315, 1460298016, -1771194297, 1215703690, 277231808, -416020628,
95 -418936577, -1724839216, 404731389, 1058730508, -1508366681, 229883053,
96 -572310243, 1883189553, 931286849, 1659300867, -94236383, -241524462,
97 548020458, -302406981, 579986475, 73468197, -984957614, 1554382245,
98 2084807492, -1456802798, -1105192593, 629440327, -16313961, -2102585261,
99 1873675206, 161035128, 1497033351, 1990150811, -499405222, 304019482,
100 41935663, -805987182, -571699268, 1748462913, 2096239823, -116359807,
101 -1871127553, -1074832534, -1558866192, 231353861, 2122854560, -2102323721,
102 -281462361, -343403210, -673268171, 1776058383, 1581561150, 2059580579,
103 768848632, 1347190372, -1701705879, 245282007, -563267886, -592558289,
104 1662399958, 1390406821, -1522485580, -706446863, 2069516289, -301855859,
105 -778346387, -1454093198, 1249083752, -1760506745, 262193320, 630751125,
106 -1495939124, -29980580, -1989626563, 659039376, -329477132, -1003507166,
107 -1322549020, 358606508, -2052572059, 1848014133, 1826958586, -1004948862,
108 -1775370541, 2134177912, -1739214473, 1892700918, 926629675, -1042761322,
109 2020075900, 606370962, -1256609305, 117577265, -586848924, 191368285,
110 1653535275, -1329269701, -375879127, -1089901406, 1206489978, 534223924,
111 -1042752982, -1178316881, -445594741, -1501682065, -1598136839,
112 -467688289, 750784023, 1781080461, 1729380226, 16906088, 862168532,
113 -2037752683, 1455274138, -1491220107, 1058323960, 1711530558, 1355062750,
114 227640096, 396568027, -173579098, -408975801, -993618329, -1470751562,
115 371076647, 209563718, 2015405719, -723460281, -1423934420, -2089643958,
116 353260489, 2084264341, -792676687, 701391030, -1440658244, 1479321011,
117 1907822880, 1232524257, -256712289, 401077577, 621808069, 868263613,
118 1244930119, 2020996902, 117483907, 1341376744, -1936988014, -445200547,
119 -843751811, -435291191, 1041695743, 476132726, -1226874735, -1436046747,
120 -297047422, 1739645396, 1948680937, -718144374, 1141983978, 1673650568,
121 -197244350, 1604464002, 1424069853, -485626505, 1708710014, -849136541,
122 1573778103, 530360999, 1777767203, 1376958336, -1088364352, 1826167753,
123 742735448, -1386211659, -1991323164, -444115655, -443055378, -1586901006,
124 -1741686587, 1925818034, -2118916824, 803890920, -1481793154, 992278937,
125 1302616410, 444517030, 1393144770, -2025632978, 1902300505, -1683582981,
126 800654133, 873850324, -619580878, -2002070410, -2024936385, 1978986634,
127 2012024264, 675768872, 389435615, -867217540, 231209167, -303917385,
128 1445676969, -1385982721, 1310476490, 580273453, -160600202, -1330895874,
129 487110497, 1124384798, 227637416, -1829783306, 1014818058, -1336870683,
130 -1042199518, -468525587, -1186267363, -472843891, 1215617600, -2056648329,
131 -873216891, 156780951, -1883246047, -842549253, -717684332, 760531638,
132 1074787431, 786267513, 814031289, -561255343, -110302255, -1837376592,
133 989669060, -81350614, 546038730, 222899882, 1298746805, 1791615733,
134 1565630269, 1516024174, 421691479, 1860326051, -1973359550, 1854393443,
135 -1401468528, -158562295, 1509929255, -124024738, -462937489, 259890715,
136 -1515121317, -289511197, -913738664, 698079062, -1631229382, -507275144,
137 1897739663, -1118192766, -1687033399, 61405556, -1913606579, -473308896,
138 -259107170, -576944609, -1689355510, 322156799, 545090192, 127425176,
139 -1815211748, -2070235628, -1172529316, 599259550, -910906653, 1797380363,
140 -938649427, 142991392, 504559631, 1208867355, -807699247, -616021271,
141 -254935281, -57151221, -1095534993, 1998380318, 1772459584, 713271407,
142 -1197898266, 808881935, -308133481, -1314455137, 284321772, -743117625,
143 -1622364240, -1667535152, 118713606, 1053615347, -2072876023, -178189072,
144 -828319551, 2047304928, -1311435786, -1970672907, -747972100, 86806159,
145 -436088421, 1464645587, 735840899, 32600466, -190473426, -735703440,
146 482872155, 475662392, -713681085, 1424078728, -150668609, -1137197868,
147 -1682762563, -48035649, 1143959866, -1542015129, 284920371, -1587695586,
148 -625236551, -753893357, -433976266, -1329796037, -1636712478, 1686783454,
149 27839146, 1748631474, -879528256, 2057796026, 773734654, 112269667,
150 -2011541314, 1517797297, -1943171794, 268166111, -1037010413, -1945824504,
151 -1672323792, 306260758, -692968628, -701704965, -462980996, 939188824,
152 553289792, 1790245000, 2093793129, -658085781, -186055037, -2130433650,
153 -1013235433, 1190870089, -2126586963, -1509655742, -1291895256,
154 -1427857845, 309538950, 388316741, 259659733, -1895092434, 110126220,
155 -170175575, -419430224, -696234084, -832170948, -353431720, -797675726,
156 -1644136054, 715163272, -1305904349, -145786463, -99586244, -695450446,
157 -871327102, -725496060, 952863853, -688441983, -1729929460, -103732092,
158 1059054528, 568873585, -982665223, -128672783, 2099418320, 1508239336,
159 -2089480835, -390935727, 664306522, -1607364342, -163246802, -1121295140,
160 -128375779, -615694409, -2079391797, 760542037, 677761593, -750117849,
161 -1060525080, 2128437080, 525250908, 1987657172, 2032530557, -2011247936,
162 1942775263, 1681562788, 688229491, -803856505, 684707948, 1308988965,
163 1455480037, 790659611, 1557968784, -383203149, -361510986, -742575828,
164 558837193, -1214977424, 1253274105, -119513513, -993964385, -33438767,
165 -177452803, 1186928041, -2073533871, 1188528559, 1896514695, 1200128512,
166 1930588755, -1914141443, 1534656032, -1192989829, -1848274656, -220848455,
167 1001806509, 1298797392, 1533031884, -1912322446, 1705583815, 1568094347,
168 -1397640627, 807828512, -1852996497, -1529733505, -1575634185,
169 -1280270160, -1567624159, -1861904922, 1276738579, 1163432999, 626879833,
170 316942006, -1871138342, 1341039701, 1595907877, 1950911580, 1634717748,
171 1071476055, -809354290, -1161553341, -2081621710, -2085557943, 19360224,
172 322135580, -698485151, 1267663094, -233890834, -126361189, -1426257522,
173 1094007921, 500179855, -283548002, -1678987343, 1946999943, 1489410849,
174 2089571262, 1430799093, 1961848046, -99462663, -552833264, 1168700661,
175 -1783882181, 2089196401, 1092839657, 914488673, 80263859, -2140947098,
176 -726384741, -1022448237, 2113887675, 1485770846, -112922517, 1995461466,
177 774613726, 944068011, 1521975359, 289086919, -386920759, -1960513175,
178 358460021, -238698524, -1913640563, -1000324864, 1731755224, -1271586254,
179 -1917469655, 2134162829, -828097534, -1089292503, -1514835999, 1682931514,
180 -482307169, 2110243841, 115744834, -2038340170, 65889188, -539445712,
181 -1713206408, -1842396726, -1659545588, -909558923, 860164922, 1328713040,
182 1044007120, -2103807103, -1073990344, -1312783785, -884980824, -705318011,
183 -1263408788, -2032228692, -1732844111, -1813827156, 1462566279,
184 1179250845, 1732421772, 604429013, -92284336, -1192166516, 304654351,
185 1998552034, -1802461575, -1802704071, -1704833934, -976264396, 1005840702,
186 2108843914, 1363909309, 843040834, -1039625241, 1285007226, 91610001,
187 418426329, 678422358, -945360697, -440008081, -1053091357, 425719777,
188 -1372778676, 591912153, 1229089037, -56663158, 2140251400, 830257037,
189 763914157, 175610373, -2105655963, -1040826150, 1174443038, 339290593,
190 346618443, -180504100, -1363190515, 210620018, 1028894425, 573529714,
191 698460117, 136999397, 1015621712, -1401813739, -297990684, -1820934845,
192 -1299093313, 1299361369, -366522415, 91527707, 1113466178, -956229484,
193 22204763, -1394374195, -1912666711, -1453789804, 1613408399, -169509567,
194 1350520309, 540761213, -2086682848, 1095131491, -812787911, 1860108594,
195 -1121378737, -1667252487, -486084366, 166519760, 1609891237, 728218405,
196 291075010, 646168382, 108462277, -1616661910, 1016600360, 2099958568,
197 27934736, 183821196, 13660496, -805589719, 936068730, -439037934,
198 1414622584, 215845485, -1352304469, -1817427526, -1318710977, -110207199,
199 228524335, 1704746590, 998293651, -1521016702, -641956531, -2089808167,
200 2094404052, -1446381065, -662186492, 1670154584, 9637833, 493925511,
201 660047318, 1197537103, 1696017374, -204994399, -1104145601, -852330465,
202 -1936369658, -829716674, -1255255217, 1264013799, 1642611772, -652520861,
203 777247164, 2028895987, -1424241853, -54367829, -1940161761, -1802831079,
204 -449405299, 838242661, -323055438, 794295411, -136989378, -446686673,
205 -421252799, -16777216,
206};
207
208int
209main (int argc, char *argv[])
210{
211 char buf[1024];
212 int i;
213
214 GNUNET_log_setup ("test-crypto-crc", "WARNING", NULL);
215 for (i = 0; i < 1024; i++)
216 buf[i] = (char) i;
217 for (i = 0; i < 1024; i++)
218 if (expected[i] != GNUNET_CRYPTO_crc32_n (&buf[i], 1024 - i))
219 return 1;
220 return 0;
221}
diff --git a/src/util/test_crypto_hash.c b/src/util/test_crypto_hash.c
new file mode 100644
index 000000000..a22a0e16a
--- /dev/null
+++ b/src/util/test_crypto_hash.c
@@ -0,0 +1,165 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @author Christian Grothoff
23 * @file util/test_crypto_hash.c
24 * @brief Test for crypto_hash.c
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_scheduler_lib.h"
30
31static char block[65536];
32
33#define FILENAME "testblock.dat"
34
35static int
36test (int number)
37{
38 GNUNET_HashCode h1;
39 GNUNET_HashCode h2;
40 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
41
42 memset (&h1, number, sizeof (GNUNET_HashCode));
43 GNUNET_CRYPTO_hash_to_enc (&h1, &enc);
44 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &h2))
45 {
46 printf ("enc2hash failed!\n");
47 return 1;
48 }
49 if (0 != memcmp (&h1, &h2, sizeof (GNUNET_HashCode)))
50 return 1;
51 return 0;
52}
53
54static int
55testEncoding ()
56{
57 int i;
58 for (i = 0; i < 255; i++)
59 if (0 != test (i))
60 return 1;
61 return 0;
62}
63
64static int
65testArithmetic ()
66{
67 static struct GNUNET_CRYPTO_AesSessionKey zskey;
68 static struct GNUNET_CRYPTO_AesInitializationVector ziv;
69 GNUNET_HashCode h1;
70 GNUNET_HashCode h2;
71 GNUNET_HashCode d;
72 GNUNET_HashCode s;
73 struct GNUNET_CRYPTO_AesSessionKey skey;
74 struct GNUNET_CRYPTO_AesInitializationVector iv;
75
76 GNUNET_CRYPTO_hash_create_random (&h1);
77 GNUNET_CRYPTO_hash_create_random (&h2);
78 if (GNUNET_CRYPTO_hash_distance_u32 (&h1, &h2) !=
79 GNUNET_CRYPTO_hash_distance_u32 (&h2, &h1))
80 return 1;
81 GNUNET_CRYPTO_hash_difference (&h1, &h2, &d);
82 GNUNET_CRYPTO_hash_sum (&h1, &d, &s);
83 if (0 != GNUNET_CRYPTO_hash_cmp (&s, &h2))
84 return 1;
85 GNUNET_CRYPTO_hash_xor (&h1, &h2, &d);
86 GNUNET_CRYPTO_hash_xor (&h1, &d, &s);
87 if (0 != GNUNET_CRYPTO_hash_cmp (&s, &h2))
88 return 1;
89 if (0 != GNUNET_CRYPTO_hash_xorcmp (&s, &h2, &h1))
90 return 1;
91 if (-1 != GNUNET_CRYPTO_hash_xorcmp (&h1, &h2, &h1))
92 return 1;
93 if (1 != GNUNET_CRYPTO_hash_xorcmp (&h1, &h2, &h2))
94 return 1;
95 memset (&d, 0xF0, sizeof (d));
96 if (0 != GNUNET_CRYPTO_hash_get_bit (&d, 3))
97 return 1;
98 if (1 != GNUNET_CRYPTO_hash_get_bit (&d, 6))
99 return 1;
100 memset (&d, 0, sizeof (d));
101 GNUNET_CRYPTO_hash_to_AES_key (&d, &skey, &iv);
102 if ((0 != memcmp (&skey, &zskey, sizeof (skey) - sizeof (unsigned int))) ||
103 (0 != memcmp (&iv, &ziv, sizeof (iv))))
104 return 1;
105 return 0;
106}
107
108static void
109finished_task (void *cls, const GNUNET_HashCode * res)
110{
111 int *ret = cls;
112 GNUNET_HashCode want;
113
114 GNUNET_CRYPTO_hash (block, sizeof (block), &want);
115 if (0 != memcmp (res, &want, sizeof (want)))
116 *ret = 2;
117 else
118 *ret = 0;
119}
120
121
122static void
123file_hasher (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
124{
125 GNUNET_CRYPTO_hash_file (tc->sched,
126 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
127 GNUNET_NO, FILENAME, 1024, &finished_task, cls);
128}
129
130
131static int
132testFileHash ()
133{
134 int ret;
135 FILE *f;
136
137 memset (block, 42, sizeof (block) / 2);
138 memset (&block[sizeof (block) / 2], 43, sizeof (block) / 2);
139 GNUNET_assert (NULL != (f = fopen (FILENAME, "w+")));
140 GNUNET_break (sizeof (block) == fwrite (block, 1, sizeof (block), f));
141 GNUNET_break (0 == fclose (f));
142 ret = 1;
143 GNUNET_SCHEDULER_run (&file_hasher, &ret);
144 GNUNET_break (0 == UNLINK (FILENAME));
145 return ret;
146}
147
148
149int
150main (int argc, char *argv[])
151{
152 int failureCount = 0;
153 int i;
154
155 GNUNET_log_setup ("test-crypto-hash", "WARNING", NULL);
156 for (i = 0; i < 10; i++)
157 failureCount += testEncoding ();
158 failureCount += testArithmetic ();
159 failureCount += testFileHash ();
160 if (failureCount != 0)
161 return 1;
162 return 0;
163}
164
165/* end of hashingtest.c */
diff --git a/src/util/test_crypto_ksk.c b/src/util/test_crypto_ksk.c
new file mode 100644
index 000000000..c8555b5de
--- /dev/null
+++ b/src/util/test_crypto_ksk.c
@@ -0,0 +1,220 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_crypto_ksk.c
23 * @brief testcase for util/crypto_ksk.c
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_time_lib.h"
31
32#define TESTSTRING "Hello World\0"
33#define MAX_TESTVAL 20
34#define UNIQUE_ITER 6
35#define ITER 25
36
37
38static int
39testMultiKey (const char *word)
40{
41 GNUNET_HashCode in;
42 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
43 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
44 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey1;
45 int i;
46
47 fprintf (stderr, "Testing KBlock key uniqueness (%s) ", word);
48 GNUNET_CRYPTO_hash (word, strlen (word), &in);
49 hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
50 if (hostkey == NULL)
51 {
52 GNUNET_break (0);
53 return GNUNET_SYSERR;
54 }
55 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
56 /*
57 for (i=0;i<sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded);i++)
58 printf("%02x", ((unsigned char*) &pkey)[i]);
59 printf("\n"); */
60 GNUNET_CRYPTO_rsa_key_free (hostkey);
61 for (i = 0; i < UNIQUE_ITER; i++)
62 {
63 fprintf (stderr, ".");
64 hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
65 if (hostkey == NULL)
66 {
67 GNUNET_break (0);
68 fprintf (stderr, " ERROR\n");
69 return GNUNET_SYSERR;
70 }
71 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey1);
72 GNUNET_CRYPTO_rsa_key_free (hostkey);
73 if (0 !=
74 memcmp (&pkey, &pkey1,
75 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
76 {
77 GNUNET_break (0);
78 fprintf (stderr, " ERROR\n");
79 return GNUNET_SYSERR;
80 }
81 }
82 fprintf (stderr, " OK\n");
83 return GNUNET_OK;
84}
85
86
87static int
88testEncryptDecrypt (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
89{
90 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
91 struct GNUNET_CRYPTO_RsaEncryptedData target;
92 char result[MAX_TESTVAL];
93 int i;
94 struct GNUNET_TIME_Absolute start;
95 int ok;
96
97 fprintf (stderr, "W");
98 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
99
100 ok = 0;
101 start = GNUNET_TIME_absolute_get ();
102 for (i = 0; i < ITER; i++)
103 {
104 fprintf (stderr, ".");
105 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING,
106 strlen (TESTSTRING) + 1,
107 &pkey, &target))
108 {
109 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
110 ok++;
111 continue;
112 }
113 if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey,
114 &target, result,
115 strlen (TESTSTRING) + 1))
116 {
117 fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n");
118 ok++;
119 continue;
120 }
121 if (strncmp (TESTSTRING, result, strlen (TESTSTRING)) != 0)
122 {
123 printf ("%s != %.*s - testEncryptDecrypt failed!\n",
124 TESTSTRING, MAX_TESTVAL, result);
125 ok++;
126 continue;
127 }
128 }
129 printf ("%d RSA encrypt/decrypt operations %llums (%d failures)\n",
130 ITER,
131 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
132 value, ok);
133 if (ok == 0)
134 return GNUNET_OK;
135 else
136 return GNUNET_SYSERR;
137}
138
139static int
140testSignVerify (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
141{
142 struct GNUNET_CRYPTO_RsaSignature sig;
143 struct GNUNET_CRYPTO_RsaSignaturePurpose purp;
144 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
145 int i;
146 struct GNUNET_TIME_Absolute start;
147 int ok = GNUNET_OK;
148
149 fprintf (stderr, "W");
150 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
151 start = GNUNET_TIME_absolute_get ();
152 purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose));
153 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
154 for (i = 0; i < ITER; i++)
155 {
156 fprintf (stderr, ".");
157 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
158 {
159 fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n");
160 ok = GNUNET_SYSERR;
161 continue;
162 }
163 if (GNUNET_SYSERR ==
164 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TEST,
165 &purp, &sig, &pkey))
166 {
167 printf ("GNUNET_CRYPTO_rsa_verify failed!\n");
168 ok = GNUNET_SYSERR;
169 continue;
170 }
171 if (GNUNET_SYSERR !=
172 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO,
173 &purp, &sig, &pkey))
174 {
175 printf ("GNUNET_CRYPTO_rsa_verify failed to fail!\n");
176 ok = GNUNET_SYSERR;
177 continue;
178 }
179 }
180 printf ("%d RSA sign/verify operations %llums\n",
181 ITER,
182 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
183 value);
184 return ok;
185}
186
187
188int
189main (int argc, char *argv[])
190{
191 int failureCount = 0;
192 GNUNET_HashCode in;
193 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
194
195 GNUNET_log_setup ("test-crypto-ksk", "WARNING", NULL);
196 GNUNET_CRYPTO_hash_create_random (&in);
197 hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
198 if (hostkey == NULL)
199 {
200 printf ("\nGNUNET_CRYPTO_rsa_key_create_from_hash failed!\n");
201 return 1;
202 }
203
204 if (GNUNET_OK != testMultiKey ("foo"))
205 failureCount++;
206 if (GNUNET_OK != testMultiKey ("bar"))
207 failureCount++;
208 if (GNUNET_OK != testEncryptDecrypt (hostkey))
209 failureCount++;
210 if (GNUNET_OK != testSignVerify (hostkey))
211 failureCount++;
212 GNUNET_CRYPTO_rsa_key_free (hostkey);
213
214 if (failureCount != 0)
215 {
216 printf ("\n\n%d TESTS FAILED!\n\n", failureCount);
217 return -1;
218 }
219 return 0;
220}
diff --git a/src/util/test_crypto_random.c b/src/util/test_crypto_random.c
new file mode 100644
index 000000000..e237c14a4
--- /dev/null
+++ b/src/util/test_crypto_random.c
@@ -0,0 +1,71 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @file util/test_crypto_random.c
24 * @brief testcase for crypto_random.c
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29
30static int
31test (enum GNUNET_CRYPTO_Quality mode)
32{
33
34 int buf[1024];
35 unsigned int *b2;
36 int i;
37 unsigned long long n;
38
39 for (i = 0; i < 1024; i++)
40 GNUNET_break (1024 > (buf[i] = GNUNET_CRYPTO_random_u32 (mode, 1024)));
41 for (i = 0; i < 10; i++)
42 {
43 b2 = GNUNET_CRYPTO_random_permute (mode, 1024);
44 if (0 == memcmp (b2, buf, sizeof (buf)))
45 {
46 fprintf (stderr, "!");
47 GNUNET_free (b2);
48 continue;
49 }
50 GNUNET_free (b2);
51 break;
52 }
53 if (i == 10)
54 return 1; /* virtually impossible... */
55
56 for (n = 10; n < 1024LL * 1024LL * 1024LL; n *= 10)
57 GNUNET_break (n > GNUNET_CRYPTO_random_u64 (mode, n));
58 return 0;
59}
60
61int
62main (int argc, char *argv[])
63{
64 GNUNET_log_setup ("test-crypto-random", "WARNING", NULL);
65 if (0 != test (GNUNET_CRYPTO_QUALITY_WEAK))
66 return 1;
67 if (0 != test (GNUNET_CRYPTO_QUALITY_STRONG))
68 return 1;
69
70 return 0;
71}
diff --git a/src/util/test_crypto_rsa.c b/src/util/test_crypto_rsa.c
new file mode 100644
index 000000000..e71decca8
--- /dev/null
+++ b/src/util/test_crypto_rsa.c
@@ -0,0 +1,335 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21/**
22 * @file util/test_crypto_rsa.c
23 * @brief testcase for RSA public key crypto
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_time_lib.h"
31
32#define TESTSTRING "Hello World\0"
33#define MAX_TESTVAL sizeof(struct GNUNET_CRYPTO_AesSessionKey)
34#define ITER 25
35#define KEYFILE "/tmp/test-gnunet-crypto-rsa.key"
36
37#define PERF GNUNET_YES
38
39static int
40testEncryptDecrypt ()
41{
42 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
43 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
44 struct GNUNET_CRYPTO_RsaEncryptedData target;
45 char result[MAX_TESTVAL];
46 int i;
47 struct GNUNET_TIME_Absolute start;
48 int ok;
49
50 fprintf (stderr, "W");
51 hostkey = GNUNET_CRYPTO_rsa_key_create ();
52 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
53
54 ok = 0;
55 start = GNUNET_TIME_absolute_get ();
56 for (i = 0; i < ITER; i++)
57 {
58 fprintf (stderr, ".");
59 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING,
60 strlen (TESTSTRING) + 1,
61 &pkey, &target))
62 {
63 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
64 ok++;
65 continue;
66 }
67 if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey,
68 &target, result,
69 strlen (TESTSTRING) + 1))
70 {
71 fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n");
72 ok++;
73 continue;
74
75 }
76 if (strncmp (TESTSTRING, result, strlen (TESTSTRING)) != 0)
77 {
78 printf ("%s != %.*s - testEncryptDecrypt failed!\n",
79 TESTSTRING, MAX_TESTVAL, result);
80 ok++;
81 continue;
82 }
83 }
84 printf ("%d RSA encrypt/decrypt operations %llums (%d failures)\n",
85 ITER,
86 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
87 value, ok);
88 GNUNET_CRYPTO_rsa_key_free (hostkey);
89 if (ok == 0)
90 return GNUNET_OK;
91 else
92 return GNUNET_SYSERR;
93}
94
95#if PERF
96static int
97testEncryptPerformance ()
98{
99 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
100 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
101 struct GNUNET_CRYPTO_RsaEncryptedData target;
102 int i;
103 struct GNUNET_TIME_Absolute start;
104 int ok;
105
106 fprintf (stderr, "W");
107 hostkey = GNUNET_CRYPTO_rsa_key_create ();
108 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
109
110 ok = 0;
111 start = GNUNET_TIME_absolute_get ();
112 for (i = 0; i < ITER; i++)
113 {
114 fprintf (stderr, ".");
115 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING,
116 strlen (TESTSTRING) + 1,
117 &pkey, &target))
118 {
119 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
120 ok++;
121 continue;
122 }
123 }
124 printf ("%d RSA encrypt operations %llu ms (%d failures)\n",
125 ITER,
126 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
127 value, ok);
128 GNUNET_CRYPTO_rsa_key_free (hostkey);
129 if (ok != 0)
130 return GNUNET_SYSERR;
131 return GNUNET_OK;
132}
133#endif
134
135static int
136testEncryptDecryptSK ()
137{
138 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
139 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
140 struct GNUNET_CRYPTO_RsaEncryptedData target;
141 struct GNUNET_CRYPTO_AesSessionKey insk;
142 struct GNUNET_CRYPTO_AesSessionKey outsk;
143 int i;
144 struct GNUNET_TIME_Absolute start;
145 int ok;
146
147 fprintf (stderr, "W");
148 hostkey = GNUNET_CRYPTO_rsa_key_create ();
149 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
150
151 ok = 0;
152 start = GNUNET_TIME_absolute_get ();
153 for (i = 0; i < ITER; i++)
154 {
155 fprintf (stderr, ".");
156 GNUNET_CRYPTO_aes_create_session_key (&insk);
157 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (&insk,
158 sizeof (struct
159 GNUNET_CRYPTO_AesSessionKey),
160 &pkey, &target))
161 {
162 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
163 ok++;
164 continue;
165 }
166 if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey,
167 &target, &outsk,
168 sizeof (struct
169 GNUNET_CRYPTO_AesSessionKey)))
170 {
171 fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n");
172 ok++;
173 continue;
174 }
175 if (0 !=
176 memcmp (&insk, &outsk, sizeof (struct GNUNET_CRYPTO_AesSessionKey)))
177 {
178 printf ("testEncryptDecryptSK failed!\n");
179 ok++;
180 continue;
181 }
182 }
183 printf ("%d RSA encrypt/decrypt SK operations %llus (%d failures)\n",
184 ITER,
185 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
186 value, ok);
187 GNUNET_CRYPTO_rsa_key_free (hostkey);
188 if (ok != 0)
189 return GNUNET_SYSERR;
190 return GNUNET_OK;
191}
192
193
194static int
195testSignVerify ()
196{
197 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
198 struct GNUNET_CRYPTO_RsaSignature sig;
199 struct GNUNET_CRYPTO_RsaSignaturePurpose purp;
200 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
201 int i;
202 struct GNUNET_TIME_Absolute start;
203 int ok = GNUNET_OK;
204
205 fprintf (stderr, "W");
206 hostkey = GNUNET_CRYPTO_rsa_key_create ();
207 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
208 start = GNUNET_TIME_absolute_get ();
209 purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose));
210 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
211
212 for (i = 0; i < ITER; i++)
213 {
214 fprintf (stderr, ".");
215 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
216 {
217 fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n");
218 ok = GNUNET_SYSERR;
219 continue;
220 }
221 if (GNUNET_SYSERR ==
222 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TEST,
223 &purp, &sig, &pkey))
224 {
225 printf ("GNUNET_CRYPTO_rsa_verify failed!\n");
226 ok = GNUNET_SYSERR;
227 continue;
228 }
229 if (GNUNET_SYSERR !=
230 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO,
231 &purp, &sig, &pkey))
232 {
233 printf ("GNUNET_CRYPTO_rsa_verify failed to fail!\n");
234 ok = GNUNET_SYSERR;
235 continue;
236 }
237 }
238 printf ("%d RSA sign/verify operations %llums\n",
239 ITER,
240 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
241 value);
242 GNUNET_CRYPTO_rsa_key_free (hostkey);
243 return ok;
244}
245
246
247#if PERF
248static int
249testSignPerformance ()
250{
251 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
252 struct GNUNET_CRYPTO_RsaSignaturePurpose purp;
253 struct GNUNET_CRYPTO_RsaSignature sig;
254 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
255 int i;
256 struct GNUNET_TIME_Absolute start;
257 int ok = GNUNET_OK;
258
259 purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose));
260 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
261 fprintf (stderr, "W");
262 hostkey = GNUNET_CRYPTO_rsa_key_create ();
263 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
264 start = GNUNET_TIME_absolute_get ();
265 for (i = 0; i < ITER; i++)
266 {
267 fprintf (stderr, ".");
268 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
269 {
270 fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n");
271 ok = GNUNET_SYSERR;
272 continue;
273 }
274 }
275 printf ("%d RSA sign operations %llu ms\n", ITER,
276 GNUNET_TIME_absolute_get_duration (start).value);
277 GNUNET_CRYPTO_rsa_key_free (hostkey);
278 return ok;
279}
280#endif
281
282
283static int
284testCreateFromFile ()
285{
286 struct GNUNET_CRYPTO_RsaPrivateKey *key;
287 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded p1;
288 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded p2;
289
290 key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE);
291 GNUNET_CRYPTO_rsa_key_get_public (key, &p1);
292 GNUNET_CRYPTO_rsa_key_free (key);
293 key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE);
294 GNUNET_CRYPTO_rsa_key_get_public (key, &p2);
295 GNUNET_assert (0 == memcmp (&p1, &p2, sizeof (p1)));
296 GNUNET_CRYPTO_rsa_key_free (key);
297 GNUNET_assert (0 == UNLINK (KEYFILE));
298 key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE);
299 GNUNET_CRYPTO_rsa_key_get_public (key, &p2);
300 GNUNET_assert (0 != memcmp (&p1, &p2, sizeof (p1)));
301 GNUNET_CRYPTO_rsa_key_free (key);
302 GNUNET_assert (0 == UNLINK (KEYFILE));
303 return GNUNET_OK;
304}
305
306
307int
308main (int argc, char *argv[])
309{
310 int failureCount = 0;
311
312 GNUNET_log_setup ("test-crypto-rsa", "WARNING", NULL);
313 GNUNET_CRYPTO_random_disable_entropy_gathering ();
314 if (GNUNET_OK != testCreateFromFile ())
315 failureCount++;
316#if PERF
317 if (GNUNET_OK != testEncryptPerformance ())
318 failureCount++;
319 if (GNUNET_OK != testSignPerformance ())
320 failureCount++;
321#endif
322 if (GNUNET_OK != testEncryptDecryptSK ())
323 failureCount++;
324 if (GNUNET_OK != testEncryptDecrypt ())
325 failureCount++;
326 if (GNUNET_OK != testSignVerify ())
327 failureCount++;
328
329 if (failureCount != 0)
330 {
331 printf ("\n\n%d TESTS FAILED!\n\n", failureCount);
332 return -1;
333 }
334 return 0;
335} /* end of main */
diff --git a/src/util/test_disk.c b/src/util/test_disk.c
new file mode 100644
index 000000000..0d385afa7
--- /dev/null
+++ b/src/util/test_disk.c
@@ -0,0 +1,274 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_disk.c
23 * @brief testcase for the storage module
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_disk_lib.h"
29#include "gnunet_scheduler_lib.h"
30
31#define TESTSTRING "Hello World\0"
32
33static int
34testReadWrite ()
35{
36 char tmp[100 + 1];
37 int ret;
38
39 if (GNUNET_OK !=
40 GNUNET_DISK_file_write (".testfile", TESTSTRING, strlen (TESTSTRING),
41 "644"))
42 return 1;
43 if (GNUNET_OK != GNUNET_DISK_file_test (".testfile"))
44 return 1;
45 ret = GNUNET_DISK_file_read (".testfile", sizeof (tmp) - 1, tmp);
46 if (ret < 0)
47 {
48 fprintf (stderr,
49 "Error reading file `%s' in testReadWrite\n", ".testfile");
50 return 1;
51 }
52 tmp[ret] = '\0';
53 if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1))
54 {
55 fprintf (stderr,
56 "Error in testReadWrite: *%s* != *%s* for file %s\n",
57 tmp, TESTSTRING, ".testfile");
58 return 1;
59 }
60 GNUNET_DISK_file_copy (".testfile", ".testfile2");
61 memset (tmp, 0, sizeof (tmp));
62 ret = GNUNET_DISK_file_read (".testfile2", sizeof (tmp) - 1, tmp);
63 if (ret < 0)
64 {
65 fprintf (stderr,
66 "Error reading file `%s' in testReadWrite\n", ".testfile2");
67 return 1;
68 }
69 tmp[ret] = '\0';
70 if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1))
71 {
72 fprintf (stderr,
73 "Error in testReadWrite: *%s* != *%s* for file %s\n",
74 tmp, TESTSTRING, ".testfile2");
75 return 1;
76 }
77
78 GNUNET_break (0 == UNLINK (".testfile"));
79 GNUNET_break (0 == UNLINK (".testfile2"));
80 if (GNUNET_NO != GNUNET_DISK_file_test (".testfile"))
81 return 1;
82
83 return 0;
84}
85
86static int
87testOpenClose ()
88{
89 int fd;
90 unsigned long long size;
91 long avail;
92
93 fd = GNUNET_DISK_file_open (".testfile",
94 O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
95 GNUNET_assert (-1 != fd);
96 GNUNET_break (5 == WRITE (fd, "Hello", 5));
97 GNUNET_DISK_file_close (".testfile", fd);
98 GNUNET_break (GNUNET_OK ==
99 GNUNET_DISK_file_size (".testfile", &size, GNUNET_NO));
100 if (size != 5)
101 return 1;
102 GNUNET_break (0 == UNLINK (".testfile"));
103
104 /* test that avail goes down as we fill the disk... */
105 GNUNET_log_skip (1);
106 avail = GNUNET_DISK_get_blocks_available (".testfile");
107 GNUNET_log_skip (0);
108 fd = GNUNET_DISK_file_open (".testfile",
109 O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
110 GNUNET_assert (-1 != fd);
111 while ((avail == GNUNET_DISK_get_blocks_available (".testfile")) &&
112 (avail != -1))
113 if (16 != WRITE (fd, "HelloWorld123456", 16))
114 {
115 GNUNET_DISK_file_close (".testfile", fd);
116 GNUNET_break (0 == UNLINK (".testfile"));
117 return 1;
118 }
119 GNUNET_DISK_file_close (".testfile", fd);
120 GNUNET_break (0 == UNLINK (".testfile"));
121
122 return 0;
123}
124
125static int ok;
126
127static int
128scan_callback (void *want, const char *filename)
129{
130 if (NULL != strstr (filename, want))
131 ok++;
132 return GNUNET_OK;
133}
134
135static int
136testDirScan ()
137{
138 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry"))
139 return 1;
140 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_more"))
141 return 1;
142 GNUNET_DISK_directory_scan ("test", &scan_callback, "test/entry");
143 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
144 return 1;
145 if (ok < 2)
146 return 1;
147 return 0;
148}
149
150static void
151iter_callback (void *cls,
152 struct GNUNET_DISK_DirectoryIterator *di,
153 const char *filename, const char *dirname)
154{
155 int *i = cls;
156 (*i)++;
157 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
158}
159
160static void
161iter_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
162{
163 GNUNET_DISK_directory_iterator_start (tc->sched,
164 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
165 "test", &iter_callback, cls);
166}
167
168static int
169testDirIter ()
170{
171 int i;
172
173 i = 0;
174 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry"))
175 return 1;
176 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_many"))
177 return 1;
178 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_more"))
179 return 1;
180 GNUNET_SCHEDULER_run (&iter_task, &i);
181 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
182 return 1;
183 if (i < 3)
184 return 1;
185 return 0;
186}
187
188
189static int
190testGetHome ()
191{
192 struct GNUNET_CONFIGURATION_Handle *cfg;
193 char *fn;
194 int ret;
195
196 cfg = GNUNET_CONFIGURATION_create ();
197 GNUNET_assert (cfg != NULL);
198 GNUNET_CONFIGURATION_set_value_string (cfg, "service", "HOME",
199 "/tmp/a/b/c");
200 fn = GNUNET_DISK_get_home_filename (cfg, "service", "d", "e", NULL);
201 GNUNET_assert (fn != NULL);
202 GNUNET_CONFIGURATION_destroy (cfg);
203 ret = strcmp ("/tmp/a/b/c/d/e", fn);
204 GNUNET_free (fn);
205 return ret;
206}
207
208static int
209testCanonicalize ()
210{
211 char *fn = GNUNET_strdup ("ab?><|cd*ef:/g\"");
212 GNUNET_DISK_filename_canonicalize (fn);
213 if (0 != strcmp (fn, "ab____cd_ef__g_"))
214 {
215 GNUNET_free (fn);
216 return 1;
217 }
218 GNUNET_free (fn);
219 return 0;
220}
221
222static int
223testChangeOwner ()
224{
225 GNUNET_log_skip (1);
226 if (GNUNET_OK == GNUNET_DISK_file_change_owner ("/dev/null", "unknownuser"))
227 return 1;
228 return 0;
229}
230
231static int
232testDirMani ()
233{
234 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file ("test/ing"))
235 return 1;
236 if (GNUNET_NO != GNUNET_DISK_file_test ("test"))
237 return 1;
238 if (GNUNET_NO != GNUNET_DISK_file_test ("test/ing"))
239 return 1;
240 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
241 return 1;
242 if (GNUNET_OK != GNUNET_DISK_directory_create ("test"))
243 return 1;
244 if (GNUNET_YES != GNUNET_DISK_directory_test ("test"))
245 return 1;
246 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
247 return 1;
248
249
250 return 0;
251}
252
253
254int
255main (int argc, char *argv[])
256{
257 unsigned int failureCount = 0;
258
259 GNUNET_log_setup ("test-disk", "WARNING", NULL);
260 failureCount += testReadWrite ();
261 failureCount += testOpenClose ();
262 failureCount += testDirScan ();
263 failureCount += testDirIter ();
264 failureCount += testGetHome ();
265 failureCount += testCanonicalize ();
266 failureCount += testChangeOwner ();
267 failureCount += testDirMani ();
268 if (failureCount != 0)
269 {
270 fprintf (stderr, "\n%u TESTS FAILED!\n", failureCount);
271 return -1;
272 }
273 return 0;
274} /* end of main */
diff --git a/src/util/test_getopt.c b/src/util/test_getopt.c
new file mode 100644
index 000000000..89e7be863
--- /dev/null
+++ b/src/util/test_getopt.c
@@ -0,0 +1,239 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_getopt.c
22 * @brief testcase for util/getopt.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_configuration_lib.h"
27#include "gnunet_getopt_lib.h"
28
29#define VERBOSE 0
30
31static int
32testMinimal ()
33{
34 struct GNUNET_CONFIGURATION_Handle *cfg;
35 char *const emptyargv[] = {
36 "test",
37 NULL
38 };
39 const struct GNUNET_GETOPT_CommandLineOption emptyoptionlist[] = {
40 GNUNET_GETOPT_OPTION_END
41 };
42
43 cfg = GNUNET_CONFIGURATION_create ();
44 if (1 != GNUNET_GETOPT_run ("test", cfg, emptyoptionlist, 1, emptyargv))
45 {
46 GNUNET_CONFIGURATION_destroy (cfg);
47 return 1;
48 }
49 GNUNET_CONFIGURATION_destroy (cfg);
50
51 return 0;
52}
53
54static int
55testVerbose ()
56{
57 struct GNUNET_CONFIGURATION_Handle *cfg;
58 char *const myargv[] = {
59 "test",
60 "-V",
61 "-V",
62 "more",
63 NULL
64 };
65 unsigned int vflags = 0;
66 const struct GNUNET_GETOPT_CommandLineOption verboseoptionlist[] = {
67 GNUNET_GETOPT_OPTION_VERBOSE (&vflags),
68 GNUNET_GETOPT_OPTION_END
69 };
70
71 cfg = GNUNET_CONFIGURATION_create ();
72 if (3 != GNUNET_GETOPT_run ("test", cfg, verboseoptionlist, 4, myargv))
73 {
74 GNUNET_break (0);
75 GNUNET_CONFIGURATION_destroy (cfg);
76 return 1;
77 }
78 GNUNET_CONFIGURATION_destroy (cfg);
79 if (vflags != 2)
80 {
81 GNUNET_break (0);
82 return 1;
83 }
84 return 0;
85}
86
87static int
88testVersion ()
89{
90 struct GNUNET_CONFIGURATION_Handle *cfg;
91 char *const myargv[] = {
92 "test_getopt",
93 "-v",
94 NULL
95 };
96 const struct GNUNET_GETOPT_CommandLineOption versionoptionlist[] = {
97 GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION),
98 GNUNET_GETOPT_OPTION_END
99 };
100
101 cfg = GNUNET_CONFIGURATION_create ();
102 if (-1 != GNUNET_GETOPT_run ("test_getopt",
103 cfg, versionoptionlist, 2, myargv))
104 {
105 GNUNET_break (0);
106 GNUNET_CONFIGURATION_destroy (cfg);
107 return 1;
108 }
109 GNUNET_CONFIGURATION_destroy (cfg);
110 return 0;
111}
112
113static int
114testAbout ()
115{
116 struct GNUNET_CONFIGURATION_Handle *cfg;
117 char *const myargv[] = {
118 "test_getopt",
119 "-h",
120 NULL
121 };
122 const struct GNUNET_GETOPT_CommandLineOption aboutoptionlist[] = {
123 GNUNET_GETOPT_OPTION_HELP ("Testing"),
124 GNUNET_GETOPT_OPTION_END
125 };
126
127 cfg = GNUNET_CONFIGURATION_create ();
128 if (-1 != GNUNET_GETOPT_run ("test_getopt",
129 cfg, aboutoptionlist, 2, myargv))
130 {
131 GNUNET_break (0);
132 GNUNET_CONFIGURATION_destroy (cfg);
133 return 1;
134 }
135 GNUNET_CONFIGURATION_destroy (cfg);
136 return 0;
137}
138
139static int
140testLogOpts ()
141{
142 struct GNUNET_CONFIGURATION_Handle *cfg;
143 char *const myargv[] = {
144 "test_getopt",
145 "-l", "filename",
146 "-L", "WARNING",
147 NULL
148 };
149 char *level = GNUNET_strdup ("stuff");
150 char *fn = NULL;
151 const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] = {
152 GNUNET_GETOPT_OPTION_LOGFILE (&fn),
153 GNUNET_GETOPT_OPTION_LOGLEVEL (&level),
154 GNUNET_GETOPT_OPTION_END
155 };
156
157 cfg = GNUNET_CONFIGURATION_create ();
158 if (5 != GNUNET_GETOPT_run ("test_getopt", cfg, logoptionlist, 5, myargv))
159 {
160 GNUNET_break (0);
161 GNUNET_CONFIGURATION_destroy (cfg);
162 return 1;
163 }
164 GNUNET_assert (fn != NULL);
165 GNUNET_CONFIGURATION_destroy (cfg);
166 if ((0 != strcmp (level, "WARNING")) || (0 != strcmp (fn, "filename")))
167 {
168 GNUNET_break (0);
169 GNUNET_free (level);
170 GNUNET_free (fn);
171 return 1;
172 }
173 GNUNET_free (level);
174 GNUNET_free (fn);
175 return 0;
176}
177
178static int
179testFlagNum ()
180{
181 struct GNUNET_CONFIGURATION_Handle *cfg;
182 char *const myargv[] = {
183 "test_getopt",
184 "-f",
185 "-n", "42",
186 "-N", "42",
187 NULL
188 };
189 int flag = 0;
190 unsigned int num = 0;
191 unsigned long long lnum = 0;
192 const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] = {
193 {'f', "--flag", NULL, "helptext", 0, &GNUNET_GETOPT_set_one,
194 (void *) &flag},
195 {'n', "--num", "ARG", "helptext", 1, &GNUNET_GETOPT_set_uint,
196 (void *) &num},
197 {'N', "--lnum", "ARG", "helptext", 1, &GNUNET_GETOPT_set_ulong,
198 (void *) &lnum},
199 GNUNET_GETOPT_OPTION_END
200 };
201
202 cfg = GNUNET_CONFIGURATION_create ();
203 if (6 != GNUNET_GETOPT_run ("test_getopt", cfg, logoptionlist, 6, myargv))
204 {
205 GNUNET_break (0);
206 GNUNET_CONFIGURATION_destroy (cfg);
207 return 1;
208 }
209 GNUNET_CONFIGURATION_destroy (cfg);
210 if ((1 != flag) || (42 != num) || (42 != lnum))
211 {
212 GNUNET_break (0);
213 return 1;
214 }
215 return 0;
216}
217
218int
219main (int argc, char *argv[])
220{
221 int errCnt = 0;
222
223 GNUNET_log_setup ("test_getopt", "WARNING", NULL);
224 /* suppress output from -h, -v options */
225 GNUNET_break (0 == CLOSE (1));
226 if (0 != testMinimal ())
227 errCnt++;
228 if (0 != testVerbose ())
229 errCnt++;
230 if (0 != testVersion ())
231 errCnt++;
232 if (0 != testAbout ())
233 errCnt++;
234 if (0 != testLogOpts ())
235 errCnt++;
236 if (0 != testFlagNum ())
237 errCnt++;
238 return errCnt;
239}
diff --git a/src/util/test_network.c b/src/util/test_network.c
new file mode 100644
index 000000000..377ec836f
--- /dev/null
+++ b/src/util/test_network.c
@@ -0,0 +1,209 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34
35static struct GNUNET_NETWORK_SocketHandle *csock;
36
37static struct GNUNET_NETWORK_SocketHandle *asock;
38
39static struct GNUNET_NETWORK_SocketHandle *lsock;
40
41static size_t sofar;
42
43static int ls;
44
45
46
47/**
48 * Create and initialize a listen socket for the server.
49 *
50 * @return -1 on error, otherwise the listen socket
51 */
52static int
53open_listen_socket ()
54{
55 const static int on = 1;
56 struct sockaddr_in sa;
57 int fd;
58
59 memset (&sa, 0, sizeof (sa));
60 sa.sin_port = htons (PORT);
61 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
62 GNUNET_assert (fd >= 0);
63 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
64 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
65 "setsockopt");
66 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
67 LISTEN (fd, 5);
68 return fd;
69}
70
71static void
72receive_check (void *cls,
73 const void *buf,
74 size_t available,
75 const struct sockaddr *addr, socklen_t addrlen, int errCode)
76{
77 int *ok = cls;
78
79#if VERBOSE
80 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive validates incoming data\n");
81#endif
82 GNUNET_assert (buf != NULL); /* no timeout */
83 if (0 == memcmp (&"Hello World"[sofar], buf, available))
84 sofar += available;
85 if (sofar < 12)
86 {
87#if VERBOSE
88 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive needs more data\n");
89#endif
90 GNUNET_NETWORK_receive (asock,
91 1024,
92 GNUNET_TIME_relative_multiply
93 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check,
94 cls);
95 }
96 else
97 {
98#if VERBOSE
99 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
100 "Receive closes accepted socket\n");
101#endif
102 *ok = 0;
103 GNUNET_NETWORK_socket_destroy (asock);
104 }
105}
106
107
108static void
109run_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
110{
111#if VERBOSE
112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test accepts connection\n");
113#endif
114 asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
115 NULL, NULL, ls, 1024);
116 GNUNET_assert (asock != NULL);
117 GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock));
118#if VERBOSE
119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys listen socket\n");
120#endif
121 GNUNET_NETWORK_socket_destroy (lsock);
122#if VERBOSE
123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124 "Test asks to receive on accepted socket\n");
125#endif
126 GNUNET_NETWORK_receive (asock,
127 1024,
128 GNUNET_TIME_relative_multiply
129 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, cls);
130}
131
132static size_t
133make_hello (void *cls, size_t size, void *buf)
134{
135#if VERBOSE
136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
137 "Test prepares to transmit on connect socket\n");
138#endif
139 GNUNET_assert (size >= 12);
140 strcpy ((char *) buf, "Hello World");
141 return 12;
142}
143
144static void
145task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
146{
147 ls = open_listen_socket ();
148 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
149 GNUNET_assert (lsock != NULL);
150 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
151 "localhost", PORT, 1024);
152 GNUNET_assert (csock != NULL);
153#if VERBOSE
154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test asks for write notification\n");
155#endif
156 GNUNET_assert (NULL !=
157 GNUNET_NETWORK_notify_transmit_ready (csock,
158 12,
159 GNUNET_TIME_UNIT_SECONDS,
160 &make_hello, NULL));
161#if VERBOSE
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys client socket\n");
163#endif
164 GNUNET_NETWORK_socket_destroy (csock);
165#if VERBOSE
166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test prepares to accept\n");
167#endif
168 GNUNET_SCHEDULER_add_read (tc->sched,
169 GNUNET_NO,
170 GNUNET_SCHEDULER_PRIORITY_HIGH,
171 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
172 GNUNET_TIME_UNIT_FOREVER_REL,
173 ls, &run_accept, cls);
174}
175
176
177/**
178 * Main method, starts scheduler with task ,
179 * checks that "ok" is correct at the end.
180 */
181static int
182check ()
183{
184 int ok;
185
186 ok = 1;
187 GNUNET_SCHEDULER_run (&task, &ok);
188 return ok;
189}
190
191
192
193int
194main (int argc, char *argv[])
195{
196 int ret = 0;
197
198 GNUNET_log_setup ("test_network",
199#if VERBOSE
200 "DEBUG",
201#else
202 "WARNING",
203#endif
204 NULL);
205 ret += check ();
206 return ret;
207}
208
209/* end of test_network.c */
diff --git a/src/util/test_network_addressing.c b/src/util/test_network_addressing.c
new file mode 100644
index 000000000..8312b721f
--- /dev/null
+++ b/src/util/test_network_addressing.c
@@ -0,0 +1,193 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_addressing.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34
35static struct GNUNET_NETWORK_SocketHandle *csock;
36
37static struct GNUNET_NETWORK_SocketHandle *asock;
38
39static struct GNUNET_NETWORK_SocketHandle *lsock;
40
41static size_t sofar;
42
43static int ls;
44
45
46
47/**
48 * Create and initialize a listen socket for the server.
49 *
50 * @return -1 on error, otherwise the listen socket
51 */
52static int
53open_listen_socket ()
54{
55 const static int on = 1;
56 struct sockaddr_in sa;
57 int fd;
58
59 memset (&sa, 0, sizeof (sa));
60 sa.sin_port = htons (PORT);
61 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
62 GNUNET_assert (fd >= 0);
63 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
64 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
65 "setsockopt");
66 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
67 LISTEN (fd, 5);
68 return fd;
69}
70
71
72static void
73receive_check (void *cls,
74 const void *buf,
75 size_t available,
76 const struct sockaddr *addr, socklen_t addrlen, int errCode)
77{
78 int *ok = cls;
79
80 GNUNET_assert (buf != NULL); /* no timeout */
81 if (0 == memcmp (&"Hello World"[sofar], buf, available))
82 sofar += available;
83 if (sofar < 12)
84 {
85 GNUNET_NETWORK_receive (asock,
86 1024,
87 GNUNET_TIME_relative_multiply
88 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check,
89 cls);
90 }
91 else
92 {
93 *ok = 0;
94 GNUNET_NETWORK_socket_destroy (asock);
95 }
96}
97
98
99static void
100run_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
101{
102 void *addr;
103 size_t alen;
104 struct sockaddr_in *v4;
105 struct sockaddr_in expect;
106
107 asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
108 NULL, NULL, ls, 1024);
109 GNUNET_assert (asock != NULL);
110 GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock));
111 GNUNET_assert (GNUNET_OK ==
112 GNUNET_NETWORK_socket_get_address (asock, &addr, &alen));
113 GNUNET_assert (alen == sizeof (struct sockaddr_in));
114 v4 = addr;
115 memset (&expect, 0, sizeof (expect));
116 expect.sin_family = AF_INET;
117 expect.sin_port = v4->sin_port;
118 expect.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
119 GNUNET_assert (0 == memcmp (&expect, v4, alen));
120 GNUNET_NETWORK_socket_destroy (lsock);
121 GNUNET_NETWORK_receive (asock,
122 1024,
123 GNUNET_TIME_relative_multiply
124 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, cls);
125}
126
127static size_t
128make_hello (void *cls, size_t size, void *buf)
129{
130 GNUNET_assert (size >= 12);
131 strcpy ((char *) buf, "Hello World");
132 return 12;
133}
134
135static void
136task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
137{
138 struct sockaddr_in v4;
139 ls = open_listen_socket ();
140 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
141 GNUNET_assert (lsock != NULL);
142
143 v4.sin_family = AF_INET;
144 v4.sin_port = htons (PORT);
145 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
146 csock = GNUNET_NETWORK_socket_create_from_sockaddr (tc->sched,
147 AF_INET,
148 (const struct sockaddr
149 *) &v4, sizeof (v4),
150 1024);
151 GNUNET_assert (csock != NULL);
152 GNUNET_assert (NULL !=
153 GNUNET_NETWORK_notify_transmit_ready (csock,
154 12,
155 GNUNET_TIME_UNIT_SECONDS,
156 &make_hello, NULL));
157 GNUNET_NETWORK_socket_destroy (csock);
158 GNUNET_SCHEDULER_add_read (tc->sched,
159 GNUNET_NO,
160 GNUNET_SCHEDULER_PRIORITY_HIGH,
161 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
162 GNUNET_TIME_UNIT_FOREVER_REL,
163 ls, &run_accept, cls);
164}
165
166
167/**
168 * Main method, starts scheduler with task ,
169 * checks that "ok" is correct at the end.
170 */
171static int
172check ()
173{
174 int ok;
175
176 ok = 1;
177 GNUNET_SCHEDULER_run (&task, &ok);
178 return ok;
179}
180
181
182
183int
184main (int argc, char *argv[])
185{
186 int ret = 0;
187
188 GNUNET_log_setup ("test_network_addressing", "WARNING", NULL);
189 ret += check ();
190 return ret;
191}
192
193/* end of test_network_addressing.c */
diff --git a/src/util/test_network_receive_cancel.c b/src/util/test_network_receive_cancel.c
new file mode 100644
index 000000000..e22e24d8c
--- /dev/null
+++ b/src/util/test_network_receive_cancel.c
@@ -0,0 +1,164 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_receive_cancel.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34
35static struct GNUNET_NETWORK_SocketHandle *csock;
36
37static struct GNUNET_NETWORK_SocketHandle *asock;
38
39static struct GNUNET_NETWORK_SocketHandle *lsock;
40
41static int ls;
42
43static GNUNET_SCHEDULER_TaskIdentifier receive_task;
44
45
46
47
48/**
49 * Create and initialize a listen socket for the server.
50 *
51 * @return -1 on error, otherwise the listen socket
52 */
53static int
54open_listen_socket ()
55{
56 const static int on = 1;
57 struct sockaddr_in sa;
58 int fd;
59
60 memset (&sa, 0, sizeof (sa));
61 sa.sin_port = htons (PORT);
62 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
63 GNUNET_assert (fd >= 0);
64 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
65 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
66 "setsockopt");
67 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
68 LISTEN (fd, 5);
69 return fd;
70}
71
72
73
74static void
75dead_receive (void *cls,
76 const void *buf,
77 size_t available,
78 const struct sockaddr *addr, socklen_t addrlen, int errCode)
79{
80 GNUNET_assert (0);
81}
82
83
84static void
85run_accept_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
86{
87
88 asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
89 NULL, NULL, ls, 1024);
90 GNUNET_assert (asock != NULL);
91 GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock));
92 GNUNET_NETWORK_socket_destroy (lsock);
93 receive_task
94 = GNUNET_NETWORK_receive (asock,
95 1024,
96 GNUNET_TIME_relative_multiply
97 (GNUNET_TIME_UNIT_SECONDS, 5), &dead_receive,
98 cls);
99}
100
101
102static void
103receive_cancel_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
104{
105 int *ok = cls;
106 GNUNET_NETWORK_receive_cancel (asock, receive_task);
107 GNUNET_NETWORK_socket_destroy (csock);
108 GNUNET_NETWORK_socket_destroy (asock);
109 *ok = 0;
110}
111
112
113
114static void
115task_receive_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
116{
117 ls = open_listen_socket ();
118 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
119 GNUNET_assert (lsock != NULL);
120 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
121 "localhost", PORT, 1024);
122 GNUNET_assert (csock != NULL);
123 GNUNET_SCHEDULER_add_read (tc->sched,
124 GNUNET_NO,
125 GNUNET_SCHEDULER_PRIORITY_HIGH,
126 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
127 GNUNET_TIME_UNIT_FOREVER_REL,
128 ls, &run_accept_cancel, cls);
129 GNUNET_SCHEDULER_add_delayed (tc->sched,
130 GNUNET_NO,
131 GNUNET_SCHEDULER_PRIORITY_KEEP,
132 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
133 GNUNET_TIME_UNIT_SECONDS,
134 &receive_cancel_task, cls);
135}
136
137
138
139/**
140 * Main method, starts scheduler with task_timeout.
141 */
142static int
143check_receive_cancel ()
144{
145 int ok;
146
147 ok = 1;
148 GNUNET_SCHEDULER_run (&task_receive_cancel, &ok);
149 return ok;
150}
151
152
153int
154main (int argc, char *argv[])
155{
156 int ret = 0;
157
158 GNUNET_log_setup ("test_network_receive_cancel", "WARNING", NULL);
159 ret += check_receive_cancel ();
160
161 return ret;
162}
163
164/* end of test_network.c */
diff --git a/src/util/test_network_timeout.c b/src/util/test_network_timeout.c
new file mode 100644
index 000000000..2c2f6f9f2
--- /dev/null
+++ b/src/util/test_network_timeout.c
@@ -0,0 +1,144 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_timeout.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34static struct GNUNET_NETWORK_SocketHandle *csock;
35
36static struct GNUNET_NETWORK_SocketHandle *lsock;
37
38static int ls;
39
40
41/**
42 * Create and initialize a listen socket for the server.
43 *
44 * @return -1 on error, otherwise the listen socket
45 */
46static int
47open_listen_socket ()
48{
49 const static int on = 1;
50 struct sockaddr_in sa;
51 int fd;
52
53 memset (&sa, 0, sizeof (sa));
54 sa.sin_port = htons (PORT);
55 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
56 GNUNET_assert (fd >= 0);
57 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
58 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
59 "setsockopt");
60 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
61 LISTEN (fd, 5);
62 return fd;
63}
64
65
66static size_t
67send_kilo (void *cls, size_t size, void *buf)
68{
69 int *ok = cls;
70 if (size == 0)
71 {
72#if VERBOSE
73 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got the desired timeout!\n");
74#endif
75 GNUNET_assert (buf == NULL);
76 *ok = 0;
77 GNUNET_NETWORK_socket_destroy (lsock);
78 GNUNET_NETWORK_socket_destroy (csock);
79 return 0;
80 }
81#if VERBOSE
82 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending kilo to fill buffer.\n");
83#endif
84 GNUNET_assert (size >= 1024);
85 memset (buf, 42, 1024);
86
87 GNUNET_assert (NULL !=
88 GNUNET_NETWORK_notify_transmit_ready (csock,
89 1024,
90 GNUNET_TIME_UNIT_SECONDS,
91 &send_kilo, cls));
92 return 1024;
93}
94
95
96static void
97task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
98{
99
100 ls = open_listen_socket ();
101 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
102 GNUNET_assert (lsock != NULL);
103 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
104 "localhost", PORT, 1024);
105 GNUNET_assert (csock != NULL);
106 GNUNET_assert (NULL !=
107 GNUNET_NETWORK_notify_transmit_ready (csock,
108 1024,
109 GNUNET_TIME_UNIT_SECONDS,
110 &send_kilo, cls));
111}
112
113
114
115/**
116 * Main method, starts scheduler with task_timeout.
117 */
118static int
119check_timeout ()
120{
121 int ok;
122
123 ok = 1;
124 GNUNET_SCHEDULER_run (&task_timeout, &ok);
125 return ok;
126}
127
128int
129main (int argc, char *argv[])
130{
131 int ret = 0;
132
133 GNUNET_log_setup ("test_network_timeout",
134#if VERBOSE
135 "DEBUG",
136#else
137 "WARNING",
138#endif
139 NULL);
140 ret += check_timeout ();
141 return ret;
142}
143
144/* end of test_network_timeout.c */
diff --git a/src/util/test_network_timeout_no_connect.c b/src/util/test_network_timeout_no_connect.c
new file mode 100644
index 000000000..fd5e82dcd
--- /dev/null
+++ b/src/util/test_network_timeout_no_connect.c
@@ -0,0 +1,95 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_timeout.c
22 * @brief tests for network.c, doing timeout which connect failure
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 13425
33
34static struct GNUNET_NETWORK_SocketHandle *csock;
35
36static size_t
37handle_timeout (void *cls, size_t size, void *buf)
38{
39 int *ok = cls;
40#if VERBOSE
41 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received timeout signal.\n");
42#endif
43
44 GNUNET_assert (size == 0);
45 GNUNET_assert (buf == NULL);
46 *ok = 0;
47 return 0;
48}
49
50
51static void
52task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
53{
54 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
55 "localhost", PORT, 1024);
56 GNUNET_assert (csock != NULL);
57 GNUNET_assert (NULL !=
58 GNUNET_NETWORK_notify_transmit_ready (csock,
59 1024,
60 GNUNET_TIME_UNIT_SECONDS,
61 &handle_timeout, cls));
62}
63
64
65
66/**
67 * Main method, starts scheduler with task_timeout.
68 */
69static int
70check_timeout ()
71{
72 int ok;
73
74 ok = 1;
75 GNUNET_SCHEDULER_run (&task_timeout, &ok);
76 return ok;
77}
78
79int
80main (int argc, char *argv[])
81{
82 int ret = 0;
83
84 GNUNET_log_setup ("test_network_timeout_no_connect",
85#if VERBOSE
86 "DEBUG",
87#else
88 "WARNING",
89#endif
90 NULL);
91 ret += check_timeout ();
92 return ret;
93}
94
95/* end of test_network_timeout_no_connect.c */
diff --git a/src/util/test_network_transmit_cancel.c b/src/util/test_network_transmit_cancel.c
new file mode 100644
index 000000000..29732d202
--- /dev/null
+++ b/src/util/test_network_transmit_cancel.c
@@ -0,0 +1,97 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_transmit_cancel.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_YES
31
32#define PORT 12435
33
34
35static size_t
36not_run (void *cls, size_t size, void *buf)
37{
38 GNUNET_assert (0);
39 return 0;
40}
41
42
43static void
44task_transmit_cancel (void *cls,
45 const struct GNUNET_SCHEDULER_TaskContext *tc)
46{
47 int *ok = cls;
48 struct GNUNET_NETWORK_TransmitHandle *th;
49 struct GNUNET_NETWORK_SocketHandle *csock;
50
51 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
52 "localhost", PORT, 1024);
53 GNUNET_assert (csock != NULL);
54 th = GNUNET_NETWORK_notify_transmit_ready (csock,
55 12,
56 GNUNET_TIME_UNIT_MINUTES,
57 &not_run, cls);
58 GNUNET_NETWORK_notify_transmit_ready_cancel (th);
59 GNUNET_NETWORK_socket_destroy (csock);
60 *ok = 0;
61}
62
63
64
65
66/**
67 * Main method, starts scheduler with task_timeout.
68 */
69static int
70check_transmit_cancel ()
71{
72 int ok;
73
74 ok = 1;
75 GNUNET_SCHEDULER_run (&task_transmit_cancel, &ok);
76 return ok;
77}
78
79
80int
81main (int argc, char *argv[])
82{
83 int ret = 0;
84
85 GNUNET_log_setup ("test_network_transmit_cancel",
86#if VERBOSE
87 "DEBUG",
88#else
89 "WARNING",
90#endif
91 NULL);
92 ret += check_transmit_cancel ();
93
94 return ret;
95}
96
97/* end of test_network_transmit_cancel.c */
diff --git a/src/util/test_os_load.c b/src/util/test_os_load.c
new file mode 100644
index 000000000..2f82f60c7
--- /dev/null
+++ b/src/util/test_os_load.c
@@ -0,0 +1,182 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_os_load.c
22 * @brief testcase for util/os_load.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_configuration_lib.h"
27#include "gnunet_crypto_lib.h"
28#include "gnunet_disk_lib.h"
29#include "gnunet_os_lib.h"
30#include "gnunet_time_lib.h"
31
32#define VERBOSE 0
33
34static int
35testcpu ()
36{
37 static long k;
38 int ret;
39 struct GNUNET_TIME_Absolute start;
40 struct GNUNET_CONFIGURATION_Handle *cfg;
41
42 fprintf (stderr, "CPU load test, this may take a while.");
43 cfg = GNUNET_CONFIGURATION_create ();
44 GNUNET_assert (cfg != NULL);
45 /* need to run each phase for more than 10s since
46 statuscalls only refreshes that often... */
47 GNUNET_CONFIGURATION_set_value_number (cfg, "LOAD", "MAXCPULOAD", 100);
48 GNUNET_OS_load_cpu_get (cfg);
49 start = GNUNET_TIME_absolute_get ();
50 while ((GNUNET_TIME_absolute_get_duration (start).value < 120 * 1000) &&
51 (0 != GNUNET_OS_load_cpu_get (cfg)))
52 sleep (1);
53 start = GNUNET_TIME_absolute_get ();
54 ret = GNUNET_OS_load_cpu_get (cfg);
55 if (ret > 10)
56 {
57 fprintf (stderr,
58 "\nWARNING: base load too high (%d) to run CPU load test.\n",
59 ret);
60 GNUNET_CONFIGURATION_destroy (cfg);
61 return 0;
62 }
63 if (ret == -1)
64 {
65 fprintf (stderr, "\nWARNING: CPU load determination not supported.\n");
66 GNUNET_CONFIGURATION_destroy (cfg);
67 return 0;
68 }
69 while (GNUNET_TIME_absolute_get_duration (start).value < 60 * 1000)
70 {
71 k++; /* do some processing to drive load up */
72 if (ret < GNUNET_OS_load_cpu_get (cfg))
73 break;
74 }
75 if (ret >= GNUNET_OS_load_cpu_get (cfg))
76 {
77 fprintf (stderr,
78 "\nbusy loop failed to increase CPU load: %d >= %d.",
79 ret, GNUNET_OS_load_cpu_get (cfg));
80 ret = 1;
81 }
82 else
83 {
84#if VERBOSE
85 fprintf (stderr,
86 "\nbusy loop increased CPU load: %d < %d.",
87 ret, GNUNET_OS_load_cpu_get (cfg));
88#endif
89 ret = 0;
90 }
91 fprintf (stderr, "\n");
92
93
94 GNUNET_CONFIGURATION_destroy (cfg);
95 return ret;
96}
97
98static int
99testdisk ()
100{
101 int ret;
102 int fd;
103 char buf[65536];
104 struct GNUNET_TIME_Absolute start;
105 struct GNUNET_CONFIGURATION_Handle *cfg;
106
107 fprintf (stderr, "IO load test, this may take a while.");
108 cfg = GNUNET_CONFIGURATION_create ();
109 GNUNET_assert (cfg != NULL);
110 /* need to run each phase for more than 10s since
111 statuscalls only refreshes that often... */
112 GNUNET_CONFIGURATION_set_value_number (cfg, "LOAD", "MAXIOLOAD", 100);
113 GNUNET_OS_load_disk_get (cfg);
114 start = GNUNET_TIME_absolute_get ();
115 while ((GNUNET_TIME_absolute_get_duration (start).value < 12 * 1000) &&
116 (0 != GNUNET_OS_load_disk_get (cfg)))
117 sleep (1);
118 start = GNUNET_TIME_absolute_get ();
119 ret = GNUNET_OS_load_disk_get (cfg);
120 if (ret > 10)
121 {
122 fprintf (stderr,
123 "WARNING: base load too high (%d) to run IO load test.\n",
124 ret);
125 GNUNET_CONFIGURATION_destroy (cfg);
126 return 0;
127 }
128 if (ret == -1)
129 {
130 fprintf (stderr, "WARNING: IO load determination not supported.\n");
131 GNUNET_CONFIGURATION_destroy (cfg);
132 return 0;
133 }
134 memset (buf, 42, sizeof (buf));
135 fd =
136 GNUNET_DISK_file_open (".loadfile", O_WRONLY | O_CREAT,
137 S_IRUSR | S_IWUSR);
138 GNUNET_assert (fd != -1);
139 while (GNUNET_TIME_absolute_get_duration (start).value < 60 * 1000)
140 {
141 LSEEK (fd, GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
142 1024 * 1024 * 1024), SEEK_SET);
143 GNUNET_assert (sizeof (buf) == WRITE (fd, buf, sizeof (buf)));
144 fsync (fd);
145 if (ret < GNUNET_OS_load_disk_get (cfg))
146 break;
147 }
148 GNUNET_break (0 == CLOSE (fd));
149 GNUNET_break (0 == UNLINK (".loadfile"));
150 if (ret >= GNUNET_OS_load_disk_get (cfg))
151 {
152 fprintf (stderr,
153 "\nbusy loop failed to increase IO load: %d >= %d.",
154 ret, GNUNET_OS_load_disk_get (cfg));
155 ret = 1;
156 }
157 else
158 {
159#if VERBOSE
160 fprintf (stderr,
161 "\nbusy loop increased disk load: %d < %d.",
162 ret, GNUNET_OS_load_disk_get (cfg));
163#endif
164 ret = 0;
165 }
166 fprintf (stderr, "\n");
167 GNUNET_CONFIGURATION_destroy (cfg);
168 return 0;
169}
170
171int
172main (int argc, char *argv[])
173{
174 int errCnt = 0;
175
176 GNUNET_log_setup ("test-os-load", "WARNING", NULL);
177 if (0 != testcpu ())
178 errCnt++;
179 if (0 != testdisk ())
180 errCnt++;
181 return errCnt;
182}
diff --git a/src/util/test_os_network.c b/src/util/test_os_network.c
new file mode 100644
index 000000000..ca57765da
--- /dev/null
+++ b/src/util/test_os_network.c
@@ -0,0 +1,73 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_os_network.c
22 * @brief testcase for util/os_network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_configuration_lib.h"
27#include "gnunet_os_lib.h"
28
29#define VERBOSE 0
30
31/**
32 * Check if the address we got is IPv4 or IPv6 loopback (which should
33 * be present on all systems at all times); if so, set ok to 0
34 * (success).
35 */
36static int
37proc (void *cls,
38 const char *name,
39 int isDefault, const struct sockaddr *addr, socklen_t addrlen)
40{
41 int *ok = cls;
42 char buf[INET6_ADDRSTRLEN];
43
44 inet_ntop (addr->sa_family,
45 (addr->sa_family == AF_INET) ?
46 (void *) &((struct sockaddr_in *) addr)->sin_addr :
47 (void *) &((struct sockaddr_in6 *) addr)->sin6_addr,
48 buf, sizeof (buf));
49 if ((0 == strcmp ("::1", buf)) || (0 == strcmp ("127.0.0.1", buf)))
50 *ok = 0;
51 return GNUNET_OK;
52}
53
54static int
55testifcs ()
56{
57 int ret;
58
59 ret = 1;
60 GNUNET_OS_network_interfaces_list (&proc, &ret);
61 return ret;
62}
63
64int
65main (int argc, char *argv[])
66{
67 int errCnt = 0;
68
69 GNUNET_log_setup ("test-os-network", "WARNING", NULL);
70 if (0 != testifcs ())
71 errCnt++;
72 return errCnt;
73}
diff --git a/src/util/test_os_priority.c b/src/util/test_os_priority.c
new file mode 100644
index 000000000..8284af5b8
--- /dev/null
+++ b/src/util/test_os_priority.c
@@ -0,0 +1,61 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_os_priority.c
22 * @brief testcase for util/os_priority.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_os_lib.h"
27
28#define VERBOSE 0
29
30static int
31testprio ()
32{
33 pid_t child;
34 if (GNUNET_OK !=
35 GNUNET_OS_set_process_priority (getpid (),
36 GNUNET_SCHEDULER_PRIORITY_DEFAULT))
37 return 1;
38#ifndef MINGW
39 child = fork ();
40 if (child == 0)
41 {
42 sleep (10);
43 exit (0);
44 }
45 if (GNUNET_OK !=
46 GNUNET_OS_set_process_priority (child, GNUNET_SCHEDULER_PRIORITY_IDLE))
47 return 1;
48#endif
49 return 0;
50}
51
52int
53main (int argc, char *argv[])
54{
55 int errCnt = 0;
56
57 GNUNET_log_setup ("test_os_priority", "WARNING", NULL);
58 if (0 != testprio ())
59 errCnt++;
60 return errCnt;
61}
diff --git a/src/util/test_plugin.c b/src/util/test_plugin.c
new file mode 100644
index 000000000..22bf78b92
--- /dev/null
+++ b/src/util/test_plugin.c
@@ -0,0 +1,64 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_plugin.c
22 * @brief testcase for plugin.c
23 */
24#include "platform.h"
25#include "gnunet_plugin_lib.h"
26
27#define VERBOSE GNUNET_NO
28
29static int
30check ()
31{
32 void *ret;
33
34 GNUNET_log_skip (1);
35 ret = GNUNET_PLUGIN_load ("libgnunet_plugin_missing", NULL);
36 GNUNET_log_skip (0);
37 if (ret != NULL)
38 return 1;
39 ret = GNUNET_PLUGIN_load ("libgnunet_plugin_test", "in");
40 if (ret == NULL)
41 return 1;
42 if (0 != strcmp (ret, "Hello"))
43 return 2;
44 ret = GNUNET_PLUGIN_unload ("libgnunet_plugin_test", "out");
45 if (ret == NULL)
46 return 3;
47 if (0 != strcmp (ret, "World"))
48 return 4;
49 free (ret);
50 return 0;
51}
52
53int
54main (int argc, char *argv[])
55{
56 int ret;
57
58 GNUNET_log_setup ("test-plugin", "WARNING", NULL);
59 ret = check ();
60
61 return ret;
62}
63
64/* end of test_plugin.c */
diff --git a/src/util/test_plugin_plug.c b/src/util/test_plugin_plug.c
new file mode 100644
index 000000000..ca4bef277
--- /dev/null
+++ b/src/util/test_plugin_plug.c
@@ -0,0 +1,42 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_plugin_plug.c
22 * @brief plugin for testing
23 */
24#include "platform.h"
25
26void *
27libgnunet_plugin_test_init (void *arg)
28{
29 if (0 == strcmp (arg, "in"))
30 return "Hello";
31 return NULL;
32}
33
34void *
35libgnunet_plugin_test_done (void *arg)
36{
37 if (0 == strcmp (arg, "out"))
38 return strdup ("World");
39 return NULL;
40}
41
42/* end of test_plugin_plug.c */
diff --git a/src/util/test_program.c b/src/util/test_program.c
new file mode 100644
index 000000000..dee602e2a
--- /dev/null
+++ b/src/util/test_program.c
@@ -0,0 +1,94 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_program.c
22 * @brief tests for program.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_program_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30static int setme;
31
32static struct GNUNET_GETOPT_CommandLineOption options[] = {
33 {'n', "name", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme},
34 GNUNET_GETOPT_OPTION_END
35};
36
37/**
38 * Main function that will be run.
39 */
40static void
41runner (void *cls,
42 struct GNUNET_SCHEDULER_Handle *sched,
43 char *const *args,
44 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
45{
46 int *ok = cls;
47 GNUNET_assert (setme == 1);
48 GNUNET_assert (sched != NULL);
49 GNUNET_assert (0 == strcmp (args[0], "extra"));
50 GNUNET_assert (args[1] == NULL);
51 GNUNET_assert (0 == strcmp (cfgfile, "test_program_data.conf"));
52
53 *ok = 0;
54}
55
56
57/**
58 * Main method, starts scheduler with task1,
59 * checks that "ok" is correct at the end.
60 */
61static int
62check ()
63{
64 int ok = 1;
65 char *const argv[] = {
66 "test_program",
67 "-c",
68 "test_program_data.conf",
69 "-L",
70 "WARNING",
71 "-n",
72 "extra",
73 NULL
74 };
75 GNUNET_assert (GNUNET_OK ==
76 GNUNET_PROGRAM_run (7,
77 argv,
78 "test_program",
79 "A test", options, &runner, &ok));
80 return ok;
81}
82
83int
84main (int argc, char *argv[])
85{
86 int ret = 0;
87
88 GNUNET_log_setup ("test_program", "WARNING", NULL);
89 ret += check ();
90
91 return ret;
92}
93
94/* end of test_program.c */
diff --git a/src/util/test_program_data.conf b/src/util/test_program_data.conf
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/util/test_program_data.conf
diff --git a/src/util/test_pseudonym.c b/src/util/test_pseudonym.c
new file mode 100644
index 000000000..6d31c8a07
--- /dev/null
+++ b/src/util/test_pseudonym.c
@@ -0,0 +1,138 @@
1/*
2 This file is part of GNUnet.
3 (C) 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_pseudonym.c
23 * @brief testcase for pseudonym.c
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30#include "gnunet_crypto_lib.h"
31#include "gnunet_disk_lib.h"
32#include "gnunet_pseudonym_lib.h"
33
34#define CHECK(a) if (!(a)) { ok = GNUNET_NO; GNUNET_break(0); goto FAILURE; }
35
36static struct GNUNET_CONTAINER_MetaData *meta;
37
38static GNUNET_HashCode id1;
39
40static int
41iter (void *cls,
42 const GNUNET_HashCode *
43 pseudonym, const struct GNUNET_CONTAINER_MetaData *md, int rating)
44{
45 int *ok = cls;
46
47 if ((0 == memcmp (pseudonym,
48 &id1,
49 sizeof (GNUNET_HashCode))) &&
50 (!GNUNET_CONTAINER_meta_data_test_equal (md, meta)))
51 {
52 *ok = GNUNET_NO;
53 GNUNET_break (0);
54 }
55 return GNUNET_OK;
56}
57
58static int
59noti_callback (void *cls,
60 const GNUNET_HashCode *
61 pseudonym,
62 const struct GNUNET_CONTAINER_MetaData *md, int rating)
63{
64 int *ret = cls;
65 (*ret)++;
66 return GNUNET_OK;
67}
68
69
70int
71main (int argc, char *argv[])
72{
73 int ok;
74 GNUNET_HashCode rid1;
75 GNUNET_HashCode id2;
76 GNUNET_HashCode rid2;
77 int old;
78 int newVal;
79 struct GNUNET_CONFIGURATION_Handle *cfg;
80 char *name1;
81 char *name2;
82 int notiCount;
83
84 GNUNET_log_setup ("test-psuedonym", "WARNING", NULL);
85 ok = GNUNET_YES;
86 GNUNET_CRYPTO_random_disable_entropy_gathering ();
87 GNUNET_DISK_directory_remove ("/tmp/gnunet-pseudonym-test");
88 cfg = GNUNET_CONFIGURATION_create ();
89 if (-1 == GNUNET_CONFIGURATION_parse (cfg, "test_pseudonym_data.conf"))
90 {
91 GNUNET_CONFIGURATION_destroy (cfg);
92 GNUNET_break (0);
93 return -1;
94 }
95 notiCount = 0;
96 GNUNET_PSEUDONYM_discovery_callback_register (cfg,
97 &noti_callback, &notiCount);
98 /* ACTUAL TEST CODE */
99 old = GNUNET_PSEUDONYM_list_all (cfg, NULL, NULL);
100 meta = GNUNET_CONTAINER_meta_data_create ();
101 GNUNET_CONTAINER_meta_data_insert (meta, EXTRACTOR_TITLE, "test");
102 GNUNET_CRYPTO_hash_create_random (&id1);
103 GNUNET_PSEUDONYM_add (cfg, &id1, meta);
104 CHECK (notiCount == 1);
105 GNUNET_PSEUDONYM_add (cfg, &id1, meta);
106 CHECK (notiCount == 2);
107 newVal = GNUNET_PSEUDONYM_list_all (cfg, &iter, &ok);
108 CHECK (old < newVal);
109 old = newVal;
110 GNUNET_CRYPTO_hash_create_random (&id2);
111 GNUNET_PSEUDONYM_add (cfg, &id2, meta);
112 CHECK (notiCount == 3);
113 newVal = GNUNET_PSEUDONYM_list_all (cfg, &iter, &ok);
114 CHECK (old < newVal);
115 name2 = GNUNET_PSEUDONYM_id_to_name (cfg, &id2);
116 CHECK (name2 != NULL);
117 name1 = GNUNET_PSEUDONYM_id_to_name (cfg, &id1);
118 CHECK (name1 != NULL);
119 CHECK (0 != strcmp (name1, name2));
120 CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name2, &rid2));
121 CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name1, &rid1));
122 CHECK (0 == memcmp (&id1, &rid1, sizeof (GNUNET_HashCode)));
123 CHECK (0 == memcmp (&id2, &rid2, sizeof (GNUNET_HashCode)));
124 CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 0));
125 CHECK (5 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5));
126 CHECK (-5 == GNUNET_PSEUDONYM_rank (cfg, &id1, -10));
127 CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5));
128 GNUNET_free (name1);
129 GNUNET_free (name2);
130 /* END OF TEST CODE */
131FAILURE:
132 GNUNET_PSEUDONYM_discovery_callback_unregister (&noti_callback, &notiCount);
133 GNUNET_CONTAINER_meta_data_destroy (meta);
134 GNUNET_CONFIGURATION_destroy (cfg);
135 return (ok == GNUNET_YES) ? 0 : 1;
136}
137
138/* end of test_pseudoynm.c */
diff --git a/src/util/test_pseudonym_data.conf b/src/util/test_pseudonym_data.conf
new file mode 100644
index 000000000..fd2048586
--- /dev/null
+++ b/src/util/test_pseudonym_data.conf
@@ -0,0 +1,8 @@
1# General settings
2[client]
3HOME = "/tmp/gnunet-pseudonym-test"
4
5[TESTING]
6WEAKRANDOM = YES
7
8
diff --git a/src/util/test_scheduler.c b/src/util/test_scheduler.c
new file mode 100644
index 000000000..fe15e987b
--- /dev/null
+++ b/src/util/test_scheduler.c
@@ -0,0 +1,263 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_scheduler.c
22 * @brief tests for the scheduler
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_scheduler_lib.h"
27#include "gnunet_time_lib.h"
28
29#define VERBOSE GNUNET_NO
30
31static void
32task2 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
33{
34 int *ok = cls;
35 GNUNET_assert (2 == *ok);
36 (*ok) = 3;
37}
38
39static void
40task3 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
41{
42 int *ok = cls;
43 /* t4 should be ready (albeit with lower priority) */
44 GNUNET_assert (1 == GNUNET_SCHEDULER_get_load (tc->sched,
45 GNUNET_SCHEDULER_PRIORITY_COUNT));
46 GNUNET_assert (3 == *ok);
47 (*ok) = 4;
48}
49
50static void
51task4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
52{
53 int *ok = cls;
54 GNUNET_assert (4 == *ok);
55 (*ok) = 5;
56}
57
58static int fds[2];
59
60
61static void
62taskWrt (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
63{
64 static char c;
65 int *ok = cls;
66 GNUNET_assert (6 == *ok);
67 GNUNET_assert (FD_ISSET (fds[1], tc->write_ready));
68 (*ok) = 7;
69 GNUNET_assert (1 == WRITE (fds[1], &c, 1));
70 GNUNET_break (0 == CLOSE (fds[1]));
71}
72
73
74static void
75taskNeverRun (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
76{
77 GNUNET_assert (0);
78}
79
80static void
81taskLast (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
82{
83 int *ok = cls;
84 /* t4 should be ready (albeit with lower priority) */
85 GNUNET_assert (8 == *ok);
86 (*ok) = 0;
87}
88
89static void
90taskRd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
91{
92 static char c;
93 int *ok = cls;
94 GNUNET_assert (7 == *ok);
95 GNUNET_assert (FD_ISSET (fds[0], tc->read_ready));
96 GNUNET_assert (1 == READ (fds[0], &c, 1));
97 GNUNET_break (0 == CLOSE (fds[0]));
98 (*ok) = 8;
99 GNUNET_SCHEDULER_add_after (tc->sched,
100 GNUNET_NO,
101 GNUNET_SCHEDULER_PRIORITY_UI,
102 0, &taskNeverRun, NULL);
103 GNUNET_SCHEDULER_add_after (tc->sched,
104 GNUNET_YES,
105 GNUNET_SCHEDULER_PRIORITY_IDLE,
106 0, &taskLast, cls);
107 GNUNET_SCHEDULER_shutdown (tc->sched);
108}
109
110
111static void
112task5 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
113{
114 int *ok = cls;
115 GNUNET_assert (5 == *ok);
116 (*ok) = 6;
117 GNUNET_assert (0 == pipe (fds));
118 GNUNET_SCHEDULER_add_read (tc->sched,
119 GNUNET_NO,
120 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
121 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
122 GNUNET_TIME_UNIT_FOREVER_REL,
123 fds[0], &taskRd, cls);
124 GNUNET_SCHEDULER_add_write (tc->sched,
125 GNUNET_NO,
126 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
127 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
128 GNUNET_TIME_UNIT_FOREVER_REL,
129 fds[1], &taskWrt, cls);
130}
131
132
133static void
134task1 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
135{
136 int *ok = cls;
137 GNUNET_SCHEDULER_TaskIdentifier t2;
138 GNUNET_SCHEDULER_TaskIdentifier t3;
139 GNUNET_SCHEDULER_TaskIdentifier t4;
140
141 GNUNET_assert (1 == *ok);
142 (*ok) = 2;
143 /* t2 will go first -- prereq for all */
144 t2 = GNUNET_SCHEDULER_add_after (tc->sched,
145 GNUNET_NO,
146 GNUNET_SCHEDULER_PRIORITY_IDLE,
147 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
148 &task2, cls);
149 /* t3 will go before t4: higher priority */
150 t4 = GNUNET_SCHEDULER_add_after (tc->sched,
151 GNUNET_NO,
152 GNUNET_SCHEDULER_PRIORITY_IDLE,
153 t2, &task4, cls);
154 t3 = GNUNET_SCHEDULER_add_delayed (tc->sched,
155 GNUNET_NO,
156 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
157 t2,
158 GNUNET_TIME_relative_get_zero (),
159 &task3, cls);
160 /* t4 will go first: lower prio, but prereq! */
161 GNUNET_SCHEDULER_add_after (tc->sched,
162 GNUNET_NO,
163 GNUNET_SCHEDULER_PRIORITY_UI, t4, &task5, cls);
164}
165
166
167
168/**
169 * Main method, starts scheduler with task1,
170 * checks that "ok" is correct at the end.
171 */
172static int
173check ()
174{
175 int ok;
176
177 ok = 1;
178 GNUNET_SCHEDULER_run (&task1, &ok);
179 return ok;
180}
181
182
183static void
184taskSig (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
185{
186 int *ok = cls;
187 GNUNET_assert (1 == *ok);
188 *ok = 8;
189 GNUNET_SCHEDULER_add_after (tc->sched,
190 GNUNET_NO,
191 GNUNET_SCHEDULER_PRIORITY_UI,
192 0, &taskNeverRun, NULL);
193 GNUNET_SCHEDULER_add_after (tc->sched,
194 GNUNET_YES,
195 GNUNET_SCHEDULER_PRIORITY_UI,
196 0, &taskLast, cls);
197 GNUNET_break (0 == PLIBC_KILL (getpid (), SIGTERM));
198}
199
200
201/**
202 * Main method, starts scheduler with task1,
203 * checks that "ok" is correct at the end.
204 */
205static int
206checkSignal ()
207{
208 int ok;
209
210 ok = 1;
211 GNUNET_SCHEDULER_run (&taskSig, &ok);
212 return ok;
213}
214
215
216
217
218static void
219taskCancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
220{
221 int *ok = cls;
222
223 GNUNET_assert (1 == *ok);
224 *ok = 0;
225 GNUNET_SCHEDULER_cancel (tc->sched,
226 GNUNET_SCHEDULER_add_after (tc->sched,
227 GNUNET_NO,
228 GNUNET_SCHEDULER_PRIORITY_UI,
229 0,
230 &taskNeverRun, NULL));
231}
232
233
234/**
235 * Main method, starts scheduler with task1,
236 * checks that "ok" is correct at the end.
237 */
238static int
239checkCancel ()
240{
241 int ok;
242
243 ok = 1;
244 GNUNET_SCHEDULER_run (&taskCancel, &ok);
245 return ok;
246}
247
248
249
250int
251main (int argc, char *argv[])
252{
253 int ret = 0;
254
255 GNUNET_log_setup ("test_scheduler", "WARNING", NULL);
256 ret += check ();
257 ret += checkSignal ();
258 ret += checkCancel ();
259
260 return ret;
261}
262
263/* end of test_scheduler.c */
diff --git a/src/util/test_scheduler_delay.c b/src/util/test_scheduler_delay.c
new file mode 100644
index 000000000..76cbf94d8
--- /dev/null
+++ b/src/util/test_scheduler_delay.c
@@ -0,0 +1,107 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_scheduler_delay.c
22 * @brief testcase for delay of scheduler, measures how
23 * precise the timers are. Expect values between 10 and 20 ms on
24 * modern machines.
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_time_lib.h"
30
31#define VERBOSE GNUNET_NO
32
33static struct GNUNET_TIME_Absolute target;
34
35static int i;
36
37static unsigned long long cumDelta;
38
39#define INCR 47
40
41#define MAXV 1500
42
43/**
44 * Signature of the main function of a task.
45 *
46 * @param cls closure
47 * @param tc context
48 */
49static void
50test_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
51{
52 struct GNUNET_TIME_Absolute now;
53
54 now = GNUNET_TIME_absolute_get ();
55 if (now.value > target.value)
56 cumDelta += (now.value - target.value);
57 else
58 cumDelta += (target.value - now.value);
59 target =
60 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply
61 (GNUNET_TIME_UNIT_MILLISECONDS, i));
62 fprintf (stderr, ".");
63 if (i > MAXV)
64 {
65 fprintf (stderr, "\n");
66 return;
67 }
68 GNUNET_SCHEDULER_add_delayed (tc->sched,
69 GNUNET_NO,
70 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
71 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
72 GNUNET_TIME_relative_multiply
73 (GNUNET_TIME_UNIT_MILLISECONDS, i),
74 &test_task, NULL);
75 i += INCR;
76}
77
78static int
79check ()
80{
81 target = GNUNET_TIME_absolute_get ();
82 GNUNET_SCHEDULER_run (&test_task, NULL);
83 FPRINTF (stdout,
84 "Sleep precision: %llu ms. ", cumDelta / 1000 / (MAXV / INCR));
85 if (cumDelta <= 10 * MAXV / INCR)
86 fprintf (stdout, "Timer precision is excellent.\n");
87 else if (cumDelta <= 50 * MAXV / INCR) /* 50 ms average deviation */
88 fprintf (stdout, "Timer precision is good.\n");
89 else if (cumDelta > 250 * MAXV / INCR)
90 fprintf (stdout, "Timer precision is awful.\n");
91 else
92 fprintf (stdout, "Timer precision is acceptable.\n");
93 return 0;
94}
95
96int
97main (int argc, char *argv[])
98{
99 int ret;
100
101 GNUNET_log_setup ("test-scheduler-delay", "WARNING", NULL);
102 ret = check ();
103
104 return ret;
105}
106
107/* end of test_scheduler_delay.c */
diff --git a/src/util/test_server.c b/src/util/test_server.c
new file mode 100644
index 000000000..e3df0b456
--- /dev/null
+++ b/src/util/test_server.c
@@ -0,0 +1,287 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_server.c
22 * @brief tests for server.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_scheduler_lib.h"
27#include "gnunet_server_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34#define MY_TYPE 128
35#define MY_TYPE2 129
36
37static struct GNUNET_SERVER_Handle *server;
38
39static struct GNUNET_SCHEDULER_Handle *sched;
40
41static void
42recv_fin_cb (void *cls,
43 struct GNUNET_SERVER_Handle *server,
44 struct GNUNET_SERVER_Client *client,
45 const struct GNUNET_MessageHeader *message)
46{
47 int *ok = cls;
48 GNUNET_assert (2 == *ok);
49 GNUNET_SERVER_receive_done (client, GNUNET_OK);
50 *ok = 3;
51}
52
53struct SignalTimeoutContext
54{
55 GNUNET_NETWORK_Receiver cb;
56 void *cb_cls;
57};
58
59
60static void
61signal_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
62{
63 struct SignalTimeoutContext *stctx = cls;
64
65 stctx->cb (stctx->cb_cls, NULL, 0, NULL, 0, 0);
66 GNUNET_free (stctx);
67}
68
69
70static GNUNET_SCHEDULER_TaskIdentifier
71my_receive (void *cls,
72 size_t max,
73 struct GNUNET_TIME_Relative timeout,
74 GNUNET_NETWORK_Receiver receiver, void *receiver_cls)
75{
76 int *ok = cls;
77 struct GNUNET_MessageHeader msg;
78 struct SignalTimeoutContext *stctx;
79 GNUNET_SCHEDULER_TaskIdentifier ret;
80
81 ret = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
82 switch (*ok)
83 {
84 case 1:
85 *ok = 2; /* report success */
86 msg.type = htons (MY_TYPE2);
87 msg.size = htons (sizeof (msg));
88 receiver (receiver_cls, &msg, sizeof (msg), NULL, 0, 0);
89 break;
90 case 3:
91 /* called after first receive instantly
92 produced a reply;
93 schedule receiver call with timeout
94 after timeout expires! */
95 *ok = 4;
96 stctx = GNUNET_malloc (sizeof (struct SignalTimeoutContext));
97 stctx->cb = receiver;
98 stctx->cb_cls = receiver_cls;
99 ret = GNUNET_SCHEDULER_add_delayed (sched,
100 GNUNET_NO,
101 GNUNET_SCHEDULER_PRIORITY_KEEP,
102 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
103 timeout, &signal_timeout, stctx);
104 break;
105 default:
106 GNUNET_assert (0);
107 }
108 return ret;
109}
110
111
112static void
113my_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti)
114{
115 GNUNET_SCHEDULER_cancel (sched, ti);
116}
117
118static void *
119my_transmit_ready_cb (void *cls,
120 size_t size,
121 struct GNUNET_TIME_Relative timeout,
122 GNUNET_NETWORK_TransmitReadyNotify notify,
123 void *notify_cls)
124{
125 static int non_null_addr;
126 int *ok = cls;
127 char buf[size];
128 struct GNUNET_MessageHeader msg;
129
130 GNUNET_assert (4 == *ok);
131 GNUNET_assert (size == sizeof (struct GNUNET_MessageHeader));
132 notify (notify_cls, size, buf);
133 msg.type = htons (MY_TYPE);
134 msg.size = htons (sizeof (msg));
135 GNUNET_assert (0 == memcmp (&msg, buf, size));
136 *ok = 5; /* report success */
137 return &non_null_addr;
138}
139
140
141static void
142my_transmit_ready_cancel_cb (void *cls, void *ctx)
143{
144 GNUNET_assert (0);
145}
146
147
148static int
149my_check (void *cls)
150{
151 return GNUNET_YES;
152}
153
154
155static void my_destroy (void *cls);
156
157
158struct CopyContext
159{
160 struct GNUNET_SERVER_Client *client;
161 struct GNUNET_MessageHeader *cpy;
162};
163
164static size_t
165copy_msg (void *cls, size_t size, void *buf)
166{
167 struct CopyContext *ctx = cls;
168 struct GNUNET_MessageHeader *cpy = ctx->cpy;
169 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (cpy->size));
170 GNUNET_assert (size >= ntohs (cpy->size));
171 memcpy (buf, cpy, ntohs (cpy->size));
172 GNUNET_free (cpy);
173 GNUNET_free (ctx);
174 return sizeof (struct GNUNET_MessageHeader);
175}
176
177
178static void
179recv_cb (void *cls,
180 struct GNUNET_SERVER_Handle *server,
181 struct GNUNET_SERVER_Client *argclient,
182 const struct GNUNET_MessageHeader *message)
183{
184 struct GNUNET_SERVER_Client *client;
185 struct CopyContext *cc;
186 struct GNUNET_MessageHeader *cpy;
187
188 GNUNET_assert (argclient == NULL);
189 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) ==
190 ntohs (message->size));
191 GNUNET_assert (MY_TYPE == ntohs (message->type));
192 client = GNUNET_SERVER_connect_callback (server,
193 cls,
194 &my_receive,
195 &my_cancel,
196 &my_transmit_ready_cb,
197 &my_transmit_ready_cancel_cb,
198 &my_check, &my_destroy);
199 cc = GNUNET_malloc (sizeof (struct CopyContext));
200 cc->client = client;
201 cpy = GNUNET_malloc (ntohs (message->size));
202 memcpy (cpy, message, ntohs (message->size));
203 cc->cpy = cpy;
204 GNUNET_assert (NULL !=
205 GNUNET_SERVER_notify_transmit_ready (client,
206 ntohs (message->size),
207 GNUNET_TIME_UNIT_SECONDS,
208 &copy_msg, cc));
209 GNUNET_SERVER_client_drop (client);
210}
211
212
213static struct GNUNET_SERVER_MessageHandler handlers[] = {
214 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
215 {&recv_fin_cb, NULL, MY_TYPE2, sizeof (struct GNUNET_MessageHeader)},
216 {NULL, NULL, 0, 0}
217};
218
219
220static void
221my_destroy (void *cls)
222{
223 int *ok = cls;
224 GNUNET_assert (5 == *ok);
225 *ok = 0; /* report success */
226 /* this will cause us to terminate */
227 GNUNET_SERVER_destroy (server);
228}
229
230
231static void
232task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
233{
234 struct sockaddr_in sa;
235 struct GNUNET_MessageHeader msg;
236
237 sched = tc->sched;
238 memset (&sa, 0, sizeof (sa));
239 sa.sin_family = AF_INET;
240 sa.sin_port = htons (PORT);
241 server = GNUNET_SERVER_create (tc->sched,
242 NULL,
243 NULL,
244 (const struct sockaddr *) &sa,
245 sizeof (sa),
246 1024,
247 GNUNET_TIME_relative_multiply
248 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
249 GNUNET_NO);
250 GNUNET_assert (server != NULL);
251 handlers[0].callback_cls = cls;
252 handlers[1].callback_cls = cls;
253 GNUNET_SERVER_add_handlers (server, handlers);
254 msg.type = htons (MY_TYPE);
255 msg.size = htons (sizeof (msg));
256 GNUNET_SERVER_inject (server, NULL, &msg);
257 memset (&msg, 0, sizeof (msg));
258}
259
260
261/**
262 * Main method, starts scheduler with task1,
263 * checks that "ok" is correct at the end.
264 */
265static int
266check ()
267{
268 int ok;
269
270 ok = 1;
271 GNUNET_SCHEDULER_run (&task, &ok);
272 return ok;
273}
274
275
276int
277main (int argc, char *argv[])
278{
279 int ret = 0;
280
281 GNUNET_log_setup ("test_server", "WARNING", NULL);
282 ret += check ();
283
284 return ret;
285}
286
287/* end of test_server.c */
diff --git a/src/util/test_server_disconnect.c b/src/util/test_server_disconnect.c
new file mode 100644
index 000000000..60604b35c
--- /dev/null
+++ b/src/util/test_server_disconnect.c
@@ -0,0 +1,241 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_server_disconnect.c
22 * @brief tests for server.c and client.c,
23 * specifically client_disconnect
24 */
25#include "platform.h"
26#include "gnunet_common.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_client_lib.h"
29#include "gnunet_server_lib.h"
30#include "gnunet_time_lib.h"
31
32#define VERBOSE GNUNET_NO
33
34#define PORT 22335
35
36#define MY_TYPE 128
37
38
39static struct GNUNET_SERVER_Handle *server;
40
41static struct GNUNET_CLIENT_Connection *client;
42
43static struct GNUNET_SCHEDULER_Handle *sched;
44
45static struct GNUNET_CONFIGURATION_Handle *cfg;
46
47static int ok;
48
49static void
50send_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
51{
52 struct GNUNET_SERVER_Client *argclient = cls;
53 GNUNET_assert (ok == 3);
54 ok++;
55 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
56}
57
58static void
59server_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
60{
61 struct GNUNET_SERVER_Client *argclient = cls;
62 GNUNET_assert (ok == 5);
63 ok++;
64 GNUNET_SERVER_client_disconnect (argclient);
65}
66
67
68static void
69recv_cb (void *cls,
70 struct GNUNET_SERVER_Handle *server,
71 struct GNUNET_SERVER_Client *argclient,
72 const struct GNUNET_MessageHeader *message)
73{
74 void *addr;
75 size_t addrlen;
76 struct sockaddr_in sa;
77 struct sockaddr_in *have;
78
79 GNUNET_assert (GNUNET_OK ==
80 GNUNET_SERVER_client_get_address (argclient,
81 &addr, &addrlen));
82
83 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
84 have = addr;
85 memset (&sa, 0, sizeof (sa));
86 sa.sin_family = AF_INET;
87 sa.sin_port = have->sin_port;
88 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
89 GNUNET_assert (0 == memcmp (&sa, addr, addrlen));
90 GNUNET_free (addr);
91 switch (ok)
92 {
93 case 2:
94 ok++;
95 GNUNET_SCHEDULER_add_delayed (sched,
96 GNUNET_YES,
97 GNUNET_SCHEDULER_PRIORITY_KEEP,
98 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
99 GNUNET_TIME_relative_multiply
100 (GNUNET_TIME_UNIT_MILLISECONDS, 50),
101 &send_done, argclient);
102 break;
103 case 4:
104 ok++;
105 GNUNET_SCHEDULER_add_delayed (sched,
106 GNUNET_YES,
107 GNUNET_SCHEDULER_PRIORITY_KEEP,
108 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
109 GNUNET_TIME_relative_multiply
110 (GNUNET_TIME_UNIT_MILLISECONDS, 50),
111 &server_disconnect, argclient);
112 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
113 break;
114 default:
115 GNUNET_assert (0);
116 }
117
118}
119
120static void
121disconnect_notify (void *cls, const struct GNUNET_MessageHeader *msg)
122{
123 GNUNET_assert (msg == NULL);
124 GNUNET_assert (ok == 7);
125 ok = 0;
126 GNUNET_CLIENT_disconnect (client);
127 GNUNET_SCHEDULER_shutdown (sched);
128 GNUNET_CONFIGURATION_destroy (cfg);
129}
130
131
132/**
133 * Functions with this signature are called whenever a client
134 * is disconnected on the network level.
135 *
136 * @param cls closure
137 * @param client identification of the client
138 */
139static void
140notify_disconnect (void *cls, struct GNUNET_SERVER_Client *clientarg)
141{
142 GNUNET_assert (ok == 6);
143 ok++;
144 GNUNET_CLIENT_receive (client,
145 &disconnect_notify,
146 NULL, GNUNET_TIME_UNIT_FOREVER_REL);
147}
148
149
150static size_t
151notify_ready (void *cls, size_t size, void *buf)
152{
153 struct GNUNET_MessageHeader *msg;
154
155 GNUNET_assert (size >= 256);
156 GNUNET_assert (1 == ok);
157 ok++;
158 msg = buf;
159 msg->type = htons (MY_TYPE);
160 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
161 msg++;
162 msg->type = htons (MY_TYPE);
163 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
164 return 2 * sizeof (struct GNUNET_MessageHeader);
165}
166
167
168static struct GNUNET_SERVER_MessageHandler handlers[] = {
169 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
170 {NULL, NULL, 0, 0}
171};
172
173
174static void
175task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
176{
177 struct sockaddr_in sa;
178
179 sched = tc->sched;
180 memset (&sa, 0, sizeof (sa));
181 sa.sin_family = AF_INET;
182 sa.sin_port = htons (PORT);
183 server = GNUNET_SERVER_create (tc->sched,
184 NULL,
185 NULL,
186 (const struct sockaddr *) &sa,
187 sizeof (sa),
188 1024,
189 GNUNET_TIME_relative_multiply
190 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
191 GNUNET_NO);
192 GNUNET_assert (server != NULL);
193 handlers[0].callback_cls = cls;
194 GNUNET_SERVER_add_handlers (server, handlers);
195 GNUNET_SERVER_disconnect_notify (server, &notify_disconnect, cls);
196 cfg = GNUNET_CONFIGURATION_create ();
197 GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT);
198 GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME",
199 "localhost");
200 client = GNUNET_CLIENT_connect (tc->sched, "test", cfg);
201 GNUNET_assert (client != NULL);
202 GNUNET_CLIENT_notify_transmit_ready (client,
203 256,
204 GNUNET_TIME_relative_multiply
205 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
206 &notify_ready, NULL);
207}
208
209
210/**
211 * Main method, starts scheduler with task1,
212 * checks that "ok" is correct at the end.
213 */
214static int
215check ()
216{
217
218 ok = 1;
219 GNUNET_SCHEDULER_run (&task, NULL);
220 return ok;
221}
222
223
224int
225main (int argc, char *argv[])
226{
227 int ret = 0;
228
229 GNUNET_log_setup ("test_server_disconnect",
230#if VERBOSE
231 "DEBUG",
232#else
233 "WARNING",
234#endif
235 NULL);
236 ret += check ();
237
238 return ret;
239}
240
241/* end of test_server_disconnect.c */
diff --git a/src/util/test_server_with_client.c b/src/util/test_server_with_client.c
new file mode 100644
index 000000000..4ca2b5bc3
--- /dev/null
+++ b/src/util/test_server_with_client.c
@@ -0,0 +1,215 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_server_with_client.c
22 * @brief tests for server.c and client.c,
23 * specifically disconnect_notify,
24 * client_get_address and receive_done (resume processing)
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_client_lib.h"
30#include "gnunet_server_lib.h"
31#include "gnunet_time_lib.h"
32
33#define VERBOSE GNUNET_NO
34
35#define PORT 22335
36
37#define MY_TYPE 128
38
39
40static struct GNUNET_SERVER_Handle *server;
41
42static struct GNUNET_CLIENT_Connection *client;
43
44static struct GNUNET_SCHEDULER_Handle *sched;
45
46static struct GNUNET_CONFIGURATION_Handle *cfg;
47
48static int ok;
49
50static void
51send_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
52{
53 struct GNUNET_SERVER_Client *argclient = cls;
54 GNUNET_assert (ok == 3);
55 ok++;
56 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
57}
58
59
60static void
61recv_cb (void *cls,
62 struct GNUNET_SERVER_Handle *server,
63 struct GNUNET_SERVER_Client *argclient,
64 const struct GNUNET_MessageHeader *message)
65{
66 void *addr;
67 size_t addrlen;
68 struct sockaddr_in sa;
69 struct sockaddr_in *have;
70
71 GNUNET_assert (GNUNET_OK ==
72 GNUNET_SERVER_client_get_address (argclient,
73 &addr, &addrlen));
74
75 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
76 have = addr;
77 memset (&sa, 0, sizeof (sa));
78 sa.sin_family = AF_INET;
79 sa.sin_port = have->sin_port;
80 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
81 GNUNET_assert (0 == memcmp (&sa, addr, addrlen));
82 GNUNET_free (addr);
83 switch (ok)
84 {
85 case 2:
86 ok++;
87 GNUNET_SCHEDULER_add_delayed (sched,
88 GNUNET_YES,
89 GNUNET_SCHEDULER_PRIORITY_KEEP,
90 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
91 GNUNET_TIME_relative_multiply
92 (GNUNET_TIME_UNIT_MILLISECONDS, 50),
93 &send_done, argclient);
94 break;
95 case 4:
96 ok++;
97 GNUNET_CLIENT_disconnect (client);
98 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
99 break;
100 default:
101 GNUNET_assert (0);
102 }
103
104}
105
106
107/**
108 * Functions with this signature are called whenever a client
109 * is disconnected on the network level.
110 *
111 * @param cls closure
112 * @param client identification of the client
113 */
114static void
115notify_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
116{
117 GNUNET_assert (ok == 5);
118 ok = 0;
119 GNUNET_SCHEDULER_shutdown (sched);
120 GNUNET_CONFIGURATION_destroy (cfg);
121}
122
123
124static size_t
125notify_ready (void *cls, size_t size, void *buf)
126{
127 struct GNUNET_MessageHeader *msg;
128
129 GNUNET_assert (size >= 256);
130 GNUNET_assert (1 == ok);
131 ok++;
132 msg = buf;
133 msg->type = htons (MY_TYPE);
134 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
135 msg++;
136 msg->type = htons (MY_TYPE);
137 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
138 return 2 * sizeof (struct GNUNET_MessageHeader);
139}
140
141
142static struct GNUNET_SERVER_MessageHandler handlers[] = {
143 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
144 {NULL, NULL, 0, 0}
145};
146
147
148static void
149task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
150{
151 struct sockaddr_in sa;
152
153 sched = tc->sched;
154 memset (&sa, 0, sizeof (sa));
155 sa.sin_family = AF_INET;
156 sa.sin_port = htons (PORT);
157 server = GNUNET_SERVER_create (tc->sched,
158 NULL,
159 NULL,
160 (const struct sockaddr *) &sa,
161 sizeof (sa),
162 1024,
163 GNUNET_TIME_relative_multiply
164 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
165 GNUNET_NO);
166 GNUNET_assert (server != NULL);
167 handlers[0].callback_cls = cls;
168 GNUNET_SERVER_add_handlers (server, handlers);
169 GNUNET_SERVER_disconnect_notify (server, &notify_disconnect, cls);
170 cfg = GNUNET_CONFIGURATION_create ();
171 GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT);
172 GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME",
173 "localhost");
174 client = GNUNET_CLIENT_connect (tc->sched, "test", cfg);
175 GNUNET_assert (client != NULL);
176 GNUNET_CLIENT_notify_transmit_ready (client,
177 256,
178 GNUNET_TIME_relative_multiply
179 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
180 &notify_ready, NULL);
181}
182
183
184/**
185 * Main method, starts scheduler with task1,
186 * checks that "ok" is correct at the end.
187 */
188static int
189check ()
190{
191
192 ok = 1;
193 GNUNET_SCHEDULER_run (&task, NULL);
194 return ok;
195}
196
197
198int
199main (int argc, char *argv[])
200{
201 int ret = 0;
202
203 GNUNET_log_setup ("test_server_with_client",
204#if VERBOSE
205 "DEBUG",
206#else
207 "WARNING",
208#endif
209 NULL);
210 ret += check ();
211
212 return ret;
213}
214
215/* end of test_server_with_client.c */
diff --git a/src/util/test_service.c b/src/util/test_service.c
new file mode 100644
index 000000000..b5cf0805e
--- /dev/null
+++ b/src/util/test_service.c
@@ -0,0 +1,337 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_service.c
22 * @brief tests for service.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_client_lib.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_program_lib.h"
29#include "gnunet_service_lib.h"
30#include "gnunet_scheduler_lib.h"
31#include "gnunet_time_lib.h"
32
33#define VERBOSE GNUNET_NO
34
35#define PORT 12435
36
37#define MY_TYPE 256
38
39static struct GNUNET_SCHEDULER_Handle *sched;
40
41static struct GNUNET_SERVICE_Context *sctx;
42
43static void
44end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
45{
46 struct GNUNET_CLIENT_Connection *client = cls;
47
48 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down service\n");
49 GNUNET_CLIENT_service_shutdown (client);
50 GNUNET_CLIENT_disconnect (client);
51 if (sctx != NULL)
52 GNUNET_SERVICE_stop (sctx);
53}
54
55
56static size_t
57build_msg (void *cls, size_t size, void *buf)
58{
59 struct GNUNET_CLIENT_Connection *client = cls;
60 struct GNUNET_MessageHeader *msg = buf;
61
62 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected, transmitting\n");
63 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
64 msg->type = htons (MY_TYPE);
65 msg->size = htons (sizeof (msg));
66 GNUNET_SCHEDULER_add_continuation (sched,
67 GNUNET_YES,
68 &end_it,
69 client,
70 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
71 return sizeof (struct GNUNET_MessageHeader);
72}
73
74static void
75ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
76{
77 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
78 struct GNUNET_CLIENT_Connection *client;
79
80 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service confirmed running\n");
81 sched = tc->sched;
82 GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE));
83 client = GNUNET_CLIENT_connect (tc->sched, "test_service", cfg);
84 GNUNET_assert (client != NULL);
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "Client connecting, waiting to transmit\n");
87 GNUNET_CLIENT_notify_transmit_ready (client,
88 sizeof (struct GNUNET_MessageHeader),
89 GNUNET_TIME_UNIT_SECONDS,
90 &build_msg, client);
91}
92
93static void
94recv_cb (void *cls,
95 struct GNUNET_SERVER_Handle *server,
96 struct GNUNET_SERVER_Client *client,
97 const struct GNUNET_MessageHeader *message)
98{
99 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving client message...\n");
100 GNUNET_SERVER_receive_done (client, GNUNET_OK);
101}
102
103static struct GNUNET_SERVER_MessageHandler myhandlers[] = {
104 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
105 {NULL, NULL, 0, 0}
106};
107
108static void
109runner (void *cls,
110 struct GNUNET_SCHEDULER_Handle *sched,
111 struct GNUNET_SERVER_Handle *server,
112 struct GNUNET_CONFIGURATION_Handle *cfg)
113{
114 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service initializing\n");
115 GNUNET_SERVER_add_handlers (server, myhandlers);
116 GNUNET_CLIENT_service_test (sched,
117 "test_service",
118 cfg, GNUNET_TIME_UNIT_SECONDS, &ready, cfg);
119}
120
121static void
122term (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
123{
124 int *ok = cls;
125 *ok = 0;
126}
127
128/**
129 * Main method, starts scheduler with task1,
130 * checks that "ok" is correct at the end.
131 */
132static int
133check ()
134{
135 int ok = 1;
136 char *const argv[] = {
137 "test_service",
138 "-c",
139 "test_service_data.conf",
140 "-L",
141#if VERBOSE
142 "DEBUG",
143#else
144 "WARNING",
145#endif
146 NULL
147 };
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting service\n");
149 GNUNET_assert (GNUNET_OK ==
150 GNUNET_SERVICE_run (5,
151 argv,
152 "test_service",
153 &runner, &ok, &term, &ok));
154 GNUNET_assert (0 == ok);
155 return ok;
156}
157
158static void
159ready6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
160{
161 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
162 struct GNUNET_CLIENT_Connection *client;
163
164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "V6 ready\n");
165 sched = tc->sched;
166 GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE));
167 client = GNUNET_CLIENT_connect (tc->sched, "test_service6", cfg);
168 GNUNET_assert (client != NULL);
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "V6 client connected\n");
170 GNUNET_CLIENT_notify_transmit_ready (client,
171 sizeof (struct GNUNET_MessageHeader),
172 GNUNET_TIME_UNIT_SECONDS,
173 &build_msg, client);
174}
175
176static void
177runner6 (void *cls,
178 struct GNUNET_SCHEDULER_Handle *sched,
179 struct GNUNET_SERVER_Handle *server,
180 struct GNUNET_CONFIGURATION_Handle *cfg)
181{
182 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing v6 service\n");
183 GNUNET_SERVER_add_handlers (server, myhandlers);
184 GNUNET_CLIENT_service_test (sched,
185 "test_service6",
186 cfg, GNUNET_TIME_UNIT_SECONDS, &ready6, cfg);
187}
188
189/**
190 * Main method, starts scheduler with task1,
191 * checks that "ok" is correct at the end.
192 */
193static int
194check6 ()
195{
196 int ok = 1;
197 char *const argv[] = {
198 "test_service6",
199 "-c",
200 "test_service_data.conf",
201 "-L",
202#if VERBOSE
203 "DEBUG",
204#else
205 "WARNING",
206#endif
207 NULL
208 };
209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting v6 service\n");
210 GNUNET_assert (GNUNET_OK ==
211 GNUNET_SERVICE_run (5,
212 argv,
213 "test_service6",
214 &runner6, &ok, &term, &ok));
215 GNUNET_assert (0 == ok);
216 return ok;
217}
218
219
220/**
221 * Main method, starts scheduler with task1,
222 * checks that "ok" is correct at the end.
223 */
224static int
225check6d ()
226{
227 int ok = 1;
228 char *const argv[] = {
229 "test_service6",
230 "-c",
231 "test_service_data.conf",
232 "-L",
233#if VERBOSE
234 "DEBUG",
235#else
236 "WARNING",
237#endif
238 "-d",
239 NULL
240 };
241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting V6 as daemon\n");
242 GNUNET_assert (GNUNET_OK ==
243 GNUNET_SERVICE_run (6,
244 argv,
245 "test_service6",
246 &runner6, &ok, &term, &ok));
247 GNUNET_break (0 == ok);
248 return ok;
249}
250
251
252static void
253start_stop_main (void *cls,
254 struct GNUNET_SCHEDULER_Handle *sched,
255 char *const *args,
256 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
257{
258 int *ret = cls;
259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260 "Starting service using start method\n");
261 sctx = GNUNET_SERVICE_start ("test_service", sched, cfg);
262 runner (cls, sched, GNUNET_SERVICE_get_server (sctx), cfg);
263 *ret = 0;
264}
265
266
267static int
268check_start_stop ()
269{
270 char *const argv[] = {
271 "test-service-program",
272 "-c",
273 "test_service_data.conf",
274 "-L",
275#if VERBOSE
276 "DEBUG",
277#else
278 "WARNING",
279#endif
280 NULL
281 };
282 const struct GNUNET_GETOPT_CommandLineOption options[] = {
283 GNUNET_GETOPT_OPTION_END
284 };
285 int ret = 1;
286 GNUNET_assert (GNUNET_OK ==
287 GNUNET_PROGRAM_run (5,
288 argv,
289 "test-service-program",
290 "no help",
291 options, &start_stop_main, &ret));
292
293 GNUNET_break (0 == ret);
294 return ret;
295}
296
297
298int
299main (int argc, char *argv[])
300{
301 int ret = 0;
302 int s;
303
304 GNUNET_log_setup ("test-service",
305#if VERBOSE
306 "DEBUG",
307#else
308 "WARNING",
309#endif
310 NULL);
311 ret += check ();
312 ret += check ();
313 s = SOCKET (PF_INET6, SOCK_STREAM, 0);
314 if (s == -1)
315 {
316 if ((errno == ENOBUFS) ||
317 (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES))
318 {
319 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
320 return 1;
321 }
322 fprintf (stderr,
323 "IPv6 support seems to not be available (%s), not testing it!\n",
324 strerror (errno));
325 }
326 else
327 {
328 GNUNET_break (0 == CLOSE (s));
329 ret += check6 ();
330 ret += check6d (); /* with daemonization */
331 }
332 ret += check_start_stop ();
333
334 return ret;
335}
336
337/* end of test_service.c */
diff --git a/src/util/test_service_data.conf b/src/util/test_service_data.conf
new file mode 100644
index 000000000..92c723619
--- /dev/null
+++ b/src/util/test_service_data.conf
@@ -0,0 +1,26 @@
1[test_service]
2PORT=12435
3BINDTO=localhost
4PIDFILE=/tmp/test-service.pid
5TIMEOUT=30000
6MAXBUF=1024
7DISABLEV6=NO
8ACCEPT_FROM=127.0.0.1;
9REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0;
10ACCEPT_FROM6=::1;
11REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40;
12HOSTNAME=localhost
13ALLOW_SHUTDOWN=YES
14
15[test_service6]
16PORT=12435
17PIDFILE=/tmp/test-service.pid
18TIMEOUT=30000
19MAXBUF=1024
20DISABLEV6=NO
21ACCEPT_FROM=127.0.0.1;
22REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0;
23ACCEPT_FROM6=::1;
24REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40;
25HOSTNAME=::1
26ALLOW_SHUTDOWN=YES
diff --git a/src/util/test_strings.c b/src/util/test_strings.c
new file mode 100644
index 000000000..be166e629
--- /dev/null
+++ b/src/util/test_strings.c
@@ -0,0 +1,111 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_strings.c
22 * @brief testcase for strings.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_strings_lib.h"
27
28#define VERBOSE GNUNET_NO
29
30#define WANT(a,b) if (0 != strcmp(a,b)) { fprintf(stderr, "Got `%s', wanted `%s'\n", b, a); GNUNET_free(b); GNUNET_break(0); return 1;} else { GNUNET_free (b); }
31#define WANTB(a,b,l) if (0 != memcmp(a,b,l)) { GNUNET_break(0); return 1;} else { }
32
33static int
34check ()
35{
36 char buf[128];
37 char *r;
38 char *b;
39 struct GNUNET_TIME_Absolute at;
40
41 sprintf (buf, "4%s", _( /* size unit */ "b"));
42 b = GNUNET_STRINGS_byte_size_fancy (4);
43 WANT (buf, b);
44 sprintf (buf, "10%s", _( /* size unit */ "KiB"));
45 b = GNUNET_STRINGS_byte_size_fancy (10240);
46 WANT (buf, b);
47 sprintf (buf, "10%s", _( /* size unit */ "TiB"));
48 b = GNUNET_STRINGS_byte_size_fancy (10240LL * 1024LL * 1024LL * 1024LL);
49 WANT (buf, b);
50 sprintf (buf, "4%s", _( /* time unit */ "ms"));
51 b =
52 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
53 (GNUNET_TIME_UNIT_MILLISECONDS,
54 4));
55 WANT (buf, b);
56 sprintf (buf, "7%s", _( /* time unit */ "s"));
57 b =
58 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
59 (GNUNET_TIME_UNIT_MILLISECONDS,
60 7 * 1000));
61 WANT (buf, b);
62 sprintf (buf, "7%s", _( /* time unit */ "h"));
63 b =
64 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
65 (GNUNET_TIME_UNIT_MILLISECONDS,
66 7 * 60 * 60 * 1000));
67 WANT (buf, b);
68 sprintf (buf, "%s%s", getenv ("HOME"), DIR_SEPARATOR_STR);
69 b = GNUNET_STRINGS_filename_expand ("~");
70 WANT (buf, b);
71 GNUNET_STRINGS_buffer_fill (buf, sizeof (buf), 3, "a", "btx", "c");
72 WANTB ("a\0btx\0c", buf, 8);
73 if (6 != GNUNET_STRINGS_buffer_tokenize (buf, sizeof (buf), 2, &r, &b))
74 return 1;
75 r = GNUNET_strdup (r);
76 WANT ("a", r);
77 b = GNUNET_strdup (b);
78 WANT ("btx", b);
79 if (0 != GNUNET_STRINGS_buffer_tokenize (buf, 2, 2, &r, &b))
80 return 1;
81 at.value = 5000;
82 r = GNUNET_STRINGS_absolute_time_to_string (at);
83 /* r should be something like "Wed Dec 31 17:00:05 1969"
84 where the details of the day and hour depend on the timezone;
85 however, the "0:05 19" should always be there; hence: */
86 if (NULL == strstr (r, "0:05 19"))
87 {
88 fprintf (stderr, "Got %s\n", r);
89 GNUNET_break (0);
90 GNUNET_free (r);
91 return 1;
92 }
93 b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "ASCII");
94 WANT ("TEST", b);
95 b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "unknown");
96 WANT ("TEST", b);
97 GNUNET_free (r);
98 return 0;
99}
100
101int
102main (int argc, char *argv[])
103{
104 int ret;
105
106 GNUNET_log_setup ("test_strings", "ERROR", NULL);
107 ret = check ();
108 return ret;
109}
110
111/* end of test_strings.c */
diff --git a/src/util/test_time.c b/src/util/test_time.c
new file mode 100644
index 000000000..9505952c2
--- /dev/null
+++ b/src/util/test_time.c
@@ -0,0 +1,122 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_time.c
22 * @brief testcase for time.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_time_lib.h"
27
28#define VERBOSE GNUNET_NO
29
30static int
31check ()
32{
33 struct GNUNET_TIME_Absolute now;
34 struct GNUNET_TIME_AbsoluteNBO nown;
35 struct GNUNET_TIME_Absolute future;
36 struct GNUNET_TIME_Absolute past;
37 struct GNUNET_TIME_Absolute last;
38 struct GNUNET_TIME_Relative rel;
39 struct GNUNET_TIME_RelativeNBO reln;
40 unsigned int i;
41
42 last = now = GNUNET_TIME_absolute_get ();
43 while (now.value == last.value)
44 now = GNUNET_TIME_absolute_get ();
45 GNUNET_assert (now.value > last.value);
46
47 /* test overflow checking in multiply */
48 rel = GNUNET_TIME_UNIT_SECONDS;
49 GNUNET_log_skip (1);
50 for (i = 0; i < 55; i++)
51 rel = GNUNET_TIME_relative_multiply (rel, 2);
52 GNUNET_log_skip (0);
53 GNUNET_assert (rel.value == GNUNET_TIME_UNIT_FOREVER_REL.value);
54
55 /* test infinity-check for relative to absolute */
56 last = GNUNET_TIME_relative_to_absolute (rel);
57 GNUNET_assert (last.value == GNUNET_TIME_UNIT_FOREVER_ABS.value);
58
59 /* check overflow for r2a */
60 rel.value = ((uint64_t) - 1LL) - 1024;
61 GNUNET_log_skip (1);
62 last = GNUNET_TIME_relative_to_absolute (rel);
63 GNUNET_log_skip (0);
64 GNUNET_assert (last.value == GNUNET_TIME_UNIT_FOREVER_ABS.value);
65
66 /* check overflow for relative add */
67 GNUNET_log_skip (1);
68 rel = GNUNET_TIME_relative_add (rel, rel);
69 GNUNET_log_skip (0);
70 GNUNET_assert (rel.value == GNUNET_TIME_UNIT_FOREVER_REL.value);
71
72 /* check relation check in get_duration */
73 future.value = now.value + 1000000;
74 GNUNET_assert (GNUNET_TIME_absolute_get_difference (now, future).value ==
75 1000000);
76 GNUNET_assert (GNUNET_TIME_absolute_get_difference (future, now).value ==
77 0);
78
79 past.value = now.value - 1000000;
80 rel = GNUNET_TIME_absolute_get_duration (future);
81 GNUNET_assert (rel.value == 0);
82 rel = GNUNET_TIME_absolute_get_duration (past);
83 GNUNET_assert (rel.value >= 1000000);
84
85 /* check get remaining */
86 rel = GNUNET_TIME_absolute_get_remaining (now);
87 GNUNET_assert (rel.value == 0);
88 rel = GNUNET_TIME_absolute_get_remaining (past);
89 GNUNET_assert (rel.value == 0);
90 rel = GNUNET_TIME_absolute_get_remaining (future);
91 GNUNET_assert (rel.value > 0);
92 GNUNET_assert (rel.value <= 1000000);
93
94 /* check endianess */
95 reln = GNUNET_TIME_relative_hton (rel);
96 GNUNET_assert (rel.value == GNUNET_TIME_relative_ntoh (reln).value);
97 nown = GNUNET_TIME_absolute_hton (now);
98 GNUNET_assert (now.value == GNUNET_TIME_absolute_ntoh (nown).value);
99
100 /* check absolute addition */
101 future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_SECONDS);
102 GNUNET_assert (future.value == now.value + 1000);
103
104 /* check zero */
105 future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_ZERO);
106 GNUNET_assert (future.value == now.value);
107
108 return 0;
109}
110
111int
112main (int argc, char *argv[])
113{
114 int ret;
115
116 GNUNET_log_setup ("test-time", "WARNING", NULL);
117 ret = check ();
118
119 return ret;
120}
121
122/* end of test_time.c */
diff --git a/src/util/time.c b/src/util/time.c
new file mode 100644
index 000000000..3ae472561
--- /dev/null
+++ b/src/util/time.c
@@ -0,0 +1,286 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/time.c
23 * @author Christian Grothoff
24 * @brief functions for handling time and time arithmetic
25 */
26#include "platform.h"
27#include "gnunet_time_lib.h"
28
29
30/**
31 * Get the current time (works just as "time", just that we use the
32 * unit of time that the cron-jobs use (and is 64 bit)).
33 *
34 * @return the current time
35 */
36struct GNUNET_TIME_Absolute
37GNUNET_TIME_absolute_get ()
38{
39 struct GNUNET_TIME_Absolute ret;
40 struct timeval tv;
41
42 gettimeofday (&tv, NULL);
43 ret.value = tv.tv_sec * 1000 + tv.tv_usec / 1000;
44 return ret;
45}
46
47
48/**
49 * Return relative time of 0ms.
50 */
51struct GNUNET_TIME_Relative
52GNUNET_TIME_relative_get_zero ()
53{
54 static struct GNUNET_TIME_Relative zero;
55 return zero;
56}
57
58/**
59 * Return relative time of 1ms.
60 */
61struct GNUNET_TIME_Relative
62GNUNET_TIME_relative_get_unit ()
63{
64 static struct GNUNET_TIME_Relative one = { 1 };
65 return one;
66}
67
68/**
69 * Return "forever".
70 */
71struct GNUNET_TIME_Relative
72GNUNET_TIME_relative_get_forever ()
73{
74 static struct GNUNET_TIME_Relative forever = { (uint64_t) - 1LL };
75 return forever;
76}
77
78/**
79 * Return "forever".
80 */
81struct GNUNET_TIME_Absolute
82GNUNET_TIME_absolute_get_forever ()
83{
84 static struct GNUNET_TIME_Absolute forever = { (uint64_t) - 1LL };
85 return forever;
86}
87
88/**
89 * Convert relative time to an absolute time in the
90 * future.
91 *
92 * @return timestamp that is "rel" in the future, or FOREVER if rel==FOREVER (or if we would overflow)
93 */
94struct GNUNET_TIME_Absolute
95GNUNET_TIME_relative_to_absolute (struct GNUNET_TIME_Relative rel)
96{
97 struct GNUNET_TIME_Absolute ret;
98 if (rel.value == (uint64_t) - 1LL)
99 return GNUNET_TIME_absolute_get_forever ();
100 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
101 if (rel.value + now.value < rel.value)
102 {
103 GNUNET_break (0); /* overflow... */
104 return GNUNET_TIME_absolute_get_forever ();
105 }
106 ret.value = rel.value + now.value;
107 return ret;
108}
109
110/**
111 * Given a timestamp in the future, how much time
112 * remains until then?
113 *
114 * @return future - now, or 0 if now >= future, or FOREVER if future==FOREVER.
115 */
116struct GNUNET_TIME_Relative
117GNUNET_TIME_absolute_get_remaining (struct GNUNET_TIME_Absolute future)
118{
119 struct GNUNET_TIME_Relative ret;
120 if (future.value == (uint64_t) - 1LL)
121 return GNUNET_TIME_relative_get_forever ();
122 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
123 if (now.value > future.value)
124 return GNUNET_TIME_relative_get_zero ();
125 ret.value = future.value - now.value;
126 return ret;
127}
128
129/**
130 * Compute the time difference between the given start and end times.
131 * Use this function instead of actual subtraction to ensure that
132 * "FOREVER" and overflows are handeled correctly.
133 *
134 * @return 0 if start >= end; FOREVER if end==FOREVER; otherwise end - start
135 */
136struct GNUNET_TIME_Relative
137GNUNET_TIME_absolute_get_difference (struct GNUNET_TIME_Absolute start,
138 struct GNUNET_TIME_Absolute end)
139{
140 struct GNUNET_TIME_Relative ret;
141 if (end.value == (uint64_t) - 1LL)
142 return GNUNET_TIME_relative_get_forever ();
143 if (end.value < start.value)
144 return GNUNET_TIME_relative_get_zero ();
145 ret.value = end.value - start.value;
146 return ret;
147}
148
149/**
150 * Get the duration of an operation as the
151 * difference of the current time and the given start time "hence".
152 *
153 * @return aborts if hence==FOREVER, 0 if hence > now, otherwise now-hence.
154 */
155struct GNUNET_TIME_Relative
156GNUNET_TIME_absolute_get_duration (struct GNUNET_TIME_Absolute hence)
157{
158 struct GNUNET_TIME_Absolute now;
159 struct GNUNET_TIME_Relative ret;
160
161 now = GNUNET_TIME_absolute_get ();
162 GNUNET_assert (hence.value != (uint64_t) - 1LL);
163 if (hence.value > now.value)
164 return GNUNET_TIME_relative_get_zero ();
165 ret.value = now.value - hence.value;
166 return ret;
167}
168
169
170/**
171 * Add a given relative duration to the
172 * given start time.
173 *
174 * @return FOREVER if either argument is FOREVER or on overflow; start+duration otherwise
175 */
176struct GNUNET_TIME_Absolute
177GNUNET_TIME_absolute_add (struct GNUNET_TIME_Absolute start,
178 struct GNUNET_TIME_Relative duration)
179{
180 struct GNUNET_TIME_Absolute ret;
181
182 if ((start.value == (uint64_t) - 1LL) ||
183 (duration.value == (uint64_t) - 1LL))
184 return GNUNET_TIME_absolute_get_forever ();
185 if (start.value + duration.value < start.value)
186 {
187 GNUNET_break (0);
188 return GNUNET_TIME_absolute_get_forever ();
189 }
190 ret.value = start.value + duration.value;
191 return ret;
192}
193
194/**
195 * Multiply relative time by a given factor.
196 *
197 * @return FOREVER if rel=FOREVER or on overflow; otherwise rel*factor
198 */
199struct GNUNET_TIME_Relative
200GNUNET_TIME_relative_multiply (struct GNUNET_TIME_Relative rel,
201 unsigned int factor)
202{
203 struct GNUNET_TIME_Relative ret;
204 if (factor == 0)
205 return GNUNET_TIME_relative_get_zero ();
206 ret.value = rel.value * factor;
207 if (ret.value / factor != rel.value)
208 {
209 GNUNET_break (0);
210 return GNUNET_TIME_relative_get_forever ();
211 }
212 return ret;
213}
214
215/**
216 * Add relative times together.
217 *
218 * @return FOREVER if either argument is FOREVER or on overflow; a1+a2 otherwise
219 */
220struct GNUNET_TIME_Relative
221GNUNET_TIME_relative_add (struct GNUNET_TIME_Relative a1,
222 struct GNUNET_TIME_Relative a2)
223{
224 struct GNUNET_TIME_Relative ret;
225
226 if ((a1.value == (uint64_t) - 1LL) || (a2.value == (uint64_t) - 1LL))
227 return GNUNET_TIME_relative_get_forever ();
228 if (a1.value + a2.value < a1.value)
229 {
230 GNUNET_break (0);
231 return GNUNET_TIME_relative_get_forever ();
232 }
233 ret.value = a1.value + a2.value;
234 return ret;
235}
236
237
238/**
239 * Convert relative time to network byte order.
240 */
241struct GNUNET_TIME_RelativeNBO
242GNUNET_TIME_relative_hton (struct GNUNET_TIME_Relative a)
243{
244 struct GNUNET_TIME_RelativeNBO ret;
245 ret.value = GNUNET_htonll (a.value);
246 return ret;
247}
248
249/**
250 * Convert relative time from network byte order.
251 */
252struct GNUNET_TIME_Relative
253GNUNET_TIME_relative_ntoh (struct GNUNET_TIME_RelativeNBO a)
254{
255 struct GNUNET_TIME_Relative ret;
256 ret.value = GNUNET_ntohll (a.value);
257 return ret;
258
259}
260
261/**
262 * Convert absolute time to network byte order.
263 */
264struct GNUNET_TIME_AbsoluteNBO
265GNUNET_TIME_absolute_hton (struct GNUNET_TIME_Absolute a)
266{
267 struct GNUNET_TIME_AbsoluteNBO ret;
268 ret.value = GNUNET_htonll (a.value);
269 return ret;
270}
271
272/**
273 * Convert absolute time from network byte order.
274 */
275struct GNUNET_TIME_Absolute
276GNUNET_TIME_absolute_ntoh (struct GNUNET_TIME_AbsoluteNBO a)
277{
278 struct GNUNET_TIME_Absolute ret;
279 ret.value = GNUNET_ntohll (a.value);
280 return ret;
281
282}
283
284
285
286/* end of time.c */