diff options
Diffstat (limited to 'src/util')
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 @@ | |||
1 | INCLUDES = -I$(top_srcdir)/src/include | ||
2 | |||
3 | plugindir = $(libdir)/gnunet | ||
4 | |||
5 | if MINGW | ||
6 | WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -luuid -liconv -lstdc++ -lcomdlg32 -lgdi32 | ||
7 | endif | ||
8 | |||
9 | if USE_COVERAGE | ||
10 | AM_CFLAGS = --coverage | ||
11 | endif | ||
12 | |||
13 | lib_LTLIBRARIES = libgnunetutil.la | ||
14 | |||
15 | libgnunetutil_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 | |||
51 | libgnunetutil_la_LIBADD = \ | ||
52 | $(GCLIBADD) \ | ||
53 | $(LIBGCRYPT_LIBS) \ | ||
54 | -lgmp -lltdl -lz -lextractor | ||
55 | |||
56 | libgnunetutil_la_LDFLAGS = \ | ||
57 | $(GN_LIB_LDFLAGS) $(WINFLAGS) \ | ||
58 | -version-info 4:0:0 | ||
59 | |||
60 | |||
61 | plugin_LTLIBRARIES = \ | ||
62 | libgnunet_plugin_test.la | ||
63 | |||
64 | libgnunet_plugin_test_la_SOURCES = \ | ||
65 | test_plugin_plug.c | ||
66 | libgnunet_plugin_test_la_LDFLAGS = \ | ||
67 | $(GN_PLUGIN_LDFLAGS) | ||
68 | |||
69 | |||
70 | check_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 | |||
110 | TESTS = $(check_PROGRAMS) | ||
111 | |||
112 | test_client_SOURCES = \ | ||
113 | test_client.c | ||
114 | test_client_LDADD = \ | ||
115 | $(top_builddir)/src/util/libgnunetutil.la | ||
116 | |||
117 | test_common_allocation_SOURCES = \ | ||
118 | test_common_allocation.c | ||
119 | test_common_allocation_LDADD = \ | ||
120 | $(top_builddir)/src/util/libgnunetutil.la | ||
121 | |||
122 | test_common_endian_SOURCES = \ | ||
123 | test_common_endian.c | ||
124 | test_common_endian_LDADD = \ | ||
125 | $(top_builddir)/src/util/libgnunetutil.la | ||
126 | |||
127 | test_common_logging_SOURCES = \ | ||
128 | test_common_logging.c | ||
129 | test_common_logging_LDADD = \ | ||
130 | $(top_builddir)/src/util/libgnunetutil.la | ||
131 | |||
132 | test_configuration_SOURCES = \ | ||
133 | test_configuration.c | ||
134 | test_configuration_LDADD = \ | ||
135 | $(top_builddir)/src/util/libgnunetutil.la | ||
136 | |||
137 | test_container_bloomfilter_SOURCES = \ | ||
138 | test_container_bloomfilter.c | ||
139 | test_container_bloomfilter_LDADD = \ | ||
140 | $(top_builddir)/src/util/libgnunetutil.la | ||
141 | |||
142 | test_container_meta_data_SOURCES = \ | ||
143 | test_container_meta_data.c | ||
144 | test_container_meta_data_LDADD = \ | ||
145 | $(top_builddir)/src/util/libgnunetutil.la | ||
146 | |||
147 | test_container_multihashmap_SOURCES = \ | ||
148 | test_container_multihashmap.c | ||
149 | test_container_multihashmap_LDADD = \ | ||
150 | $(top_builddir)/src/util/libgnunetutil.la | ||
151 | |||
152 | test_crypto_aes_SOURCES = \ | ||
153 | test_crypto_aes.c | ||
154 | test_crypto_aes_LDADD = \ | ||
155 | $(top_builddir)/src/util/libgnunetutil.la | ||
156 | |||
157 | test_crypto_aes_weak_SOURCES = \ | ||
158 | test_crypto_aes_weak.c | ||
159 | test_crypto_aes_weak_LDADD = \ | ||
160 | $(top_builddir)/src/util/libgnunetutil.la | ||
161 | |||
162 | test_crypto_crc_SOURCES = \ | ||
163 | test_crypto_crc.c | ||
164 | test_crypto_crc_LDADD = \ | ||
165 | $(top_builddir)/src/util/libgnunetutil.la | ||
166 | |||
167 | test_crypto_hash_SOURCES = \ | ||
168 | test_crypto_hash.c | ||
169 | test_crypto_hash_LDADD = \ | ||
170 | $(top_builddir)/src/util/libgnunetutil.la | ||
171 | |||
172 | test_crypto_ksk_SOURCES = \ | ||
173 | test_crypto_ksk.c | ||
174 | test_crypto_ksk_LDADD = \ | ||
175 | $(top_builddir)/src/util/libgnunetutil.la | ||
176 | |||
177 | test_crypto_random_SOURCES = \ | ||
178 | test_crypto_random.c | ||
179 | test_crypto_random_LDADD = \ | ||
180 | $(top_builddir)/src/util/libgnunetutil.la | ||
181 | |||
182 | test_crypto_rsa_SOURCES = \ | ||
183 | test_crypto_rsa.c | ||
184 | test_crypto_rsa_LDADD = \ | ||
185 | $(top_builddir)/src/util/libgnunetutil.la | ||
186 | |||
187 | test_disk_SOURCES = \ | ||
188 | test_disk.c | ||
189 | test_disk_LDADD = \ | ||
190 | $(top_builddir)/src/util/libgnunetutil.la | ||
191 | |||
192 | test_getopt_SOURCES = \ | ||
193 | test_getopt.c | ||
194 | test_getopt_LDADD = \ | ||
195 | $(top_builddir)/src/util/libgnunetutil.la | ||
196 | |||
197 | test_network_SOURCES = \ | ||
198 | test_network.c | ||
199 | test_network_LDADD = \ | ||
200 | $(top_builddir)/src/util/libgnunetutil.la | ||
201 | |||
202 | test_network_addressing_SOURCES = \ | ||
203 | test_network_addressing.c | ||
204 | test_network_addressing_LDADD = \ | ||
205 | $(top_builddir)/src/util/libgnunetutil.la | ||
206 | |||
207 | test_network_receive_cancel_SOURCES = \ | ||
208 | test_network_receive_cancel.c | ||
209 | test_network_receive_cancel_LDADD = \ | ||
210 | $(top_builddir)/src/util/libgnunetutil.la | ||
211 | |||
212 | test_network_timeout_SOURCES = \ | ||
213 | test_network_timeout.c | ||
214 | test_network_timeout_LDADD = \ | ||
215 | $(top_builddir)/src/util/libgnunetutil.la | ||
216 | |||
217 | test_network_timeout_no_connect_SOURCES = \ | ||
218 | test_network_timeout_no_connect.c | ||
219 | test_network_timeout_no_connect_LDADD = \ | ||
220 | $(top_builddir)/src/util/libgnunetutil.la | ||
221 | |||
222 | test_network_transmit_cancel_SOURCES = \ | ||
223 | test_network_transmit_cancel.c | ||
224 | test_network_transmit_cancel_LDADD = \ | ||
225 | $(top_builddir)/src/util/libgnunetutil.la | ||
226 | |||
227 | test_os_load_SOURCES = \ | ||
228 | test_os_load.c | ||
229 | test_os_load_LDADD = \ | ||
230 | $(top_builddir)/src/util/libgnunetutil.la | ||
231 | |||
232 | test_os_network_SOURCES = \ | ||
233 | test_os_network.c | ||
234 | test_os_network_LDADD = \ | ||
235 | $(top_builddir)/src/util/libgnunetutil.la | ||
236 | |||
237 | test_os_priority_SOURCES = \ | ||
238 | test_os_priority.c | ||
239 | test_os_priority_LDADD = \ | ||
240 | $(top_builddir)/src/util/libgnunetutil.la | ||
241 | |||
242 | test_plugin_SOURCES = \ | ||
243 | test_plugin.c | ||
244 | test_plugin_LDADD = \ | ||
245 | $(top_builddir)/src/util/libgnunetutil.la | ||
246 | |||
247 | test_program_SOURCES = \ | ||
248 | test_program.c | ||
249 | test_program_LDADD = \ | ||
250 | $(top_builddir)/src/util/libgnunetutil.la | ||
251 | |||
252 | test_pseudonym_SOURCES = \ | ||
253 | test_pseudonym.c | ||
254 | test_pseudonym_LDADD = \ | ||
255 | $(top_builddir)/src/util/libgnunetutil.la | ||
256 | |||
257 | test_scheduler_SOURCES = \ | ||
258 | test_scheduler.c | ||
259 | test_scheduler_LDADD = \ | ||
260 | $(top_builddir)/src/util/libgnunetutil.la | ||
261 | |||
262 | test_scheduler_delay_SOURCES = \ | ||
263 | test_scheduler_delay.c | ||
264 | test_scheduler_delay_LDADD = \ | ||
265 | $(top_builddir)/src/util/libgnunetutil.la | ||
266 | |||
267 | test_server_SOURCES = \ | ||
268 | test_server.c | ||
269 | test_server_LDADD = \ | ||
270 | $(top_builddir)/src/util/libgnunetutil.la | ||
271 | |||
272 | test_server_disconnect_SOURCES = \ | ||
273 | test_server_disconnect.c | ||
274 | test_server_disconnect_LDADD = \ | ||
275 | $(top_builddir)/src/util/libgnunetutil.la | ||
276 | |||
277 | test_server_with_client_SOURCES = \ | ||
278 | test_server_with_client.c | ||
279 | test_server_with_client_LDADD = \ | ||
280 | $(top_builddir)/src/util/libgnunetutil.la | ||
281 | |||
282 | test_service_SOURCES = \ | ||
283 | test_service.c | ||
284 | test_service_LDADD = \ | ||
285 | $(top_builddir)/src/util/libgnunetutil.la | ||
286 | |||
287 | test_strings_SOURCES = \ | ||
288 | test_strings.c | ||
289 | test_strings_LDADD = \ | ||
290 | $(top_builddir)/src/util/libgnunetutil.la | ||
291 | |||
292 | test_time_SOURCES = \ | ||
293 | test_time.c | ||
294 | test_time_LDADD = \ | ||
295 | $(top_builddir)/src/util/libgnunetutil.la | ||
296 | |||
297 | perf_crypto_hash_SOURCES = \ | ||
298 | perf_crypto_hash.c | ||
299 | perf_crypto_hash_LDADD = \ | ||
300 | $(top_builddir)/src/util/libgnunetutil.la | ||
301 | |||
302 | |||
303 | EXTRA_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 | */ | ||
45 | struct 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 | */ | ||
124 | struct GNUNET_CLIENT_Connection * | ||
125 | GNUNET_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 | */ | ||
169 | static void | ||
170 | finish_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 | */ | ||
183 | void | ||
184 | GNUNET_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 | */ | ||
200 | static void | ||
201 | check_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 | */ | ||
223 | static void | ||
224 | receive_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 | */ | ||
275 | static void | ||
276 | receive_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 | */ | ||
312 | void | ||
313 | GNUNET_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 | |||
343 | static size_t | ||
344 | write_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 | */ | ||
364 | void | ||
365 | GNUNET_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 | */ | ||
377 | static void | ||
378 | service_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 | */ | ||
395 | static void | ||
396 | confirm_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 | |||
422 | static size_t | ||
423 | write_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 | */ | ||
457 | void | ||
458 | GNUNET_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 | */ | ||
513 | struct GNUNET_NETWORK_TransmitHandle * | ||
514 | GNUNET_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 | */ | ||
46 | void * | ||
47 | GNUNET_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 | |||
56 | void * | ||
57 | GNUNET_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 | */ | ||
84 | void * | ||
85 | GNUNET_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 | */ | ||
105 | void | ||
106 | GNUNET_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 | */ | ||
120 | char * | ||
121 | GNUNET_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 | */ | ||
143 | void | ||
144 | GNUNET_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 | |||
176 | int | ||
177 | GNUNET_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 | |||
192 | int | ||
193 | GNUNET_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 | |||
30 | unsigned long long | ||
31 | GNUNET_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 | |||
40 | unsigned long long | ||
41 | GNUNET_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 | |||
30 | void __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 | */ | ||
61 | struct 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 | */ | ||
84 | static char last_bulk[BULK_TRACK_SIZE]; | ||
85 | |||
86 | /** | ||
87 | * Type of the last bulk message. | ||
88 | */ | ||
89 | static enum GNUNET_ErrorType last_bulk_kind; | ||
90 | |||
91 | /** | ||
92 | * Time of the last bulk error message (0 for none) | ||
93 | */ | ||
94 | static struct GNUNET_TIME_Absolute last_bulk_time; | ||
95 | |||
96 | /** | ||
97 | * Number of times that bulk message has been repeated since. | ||
98 | */ | ||
99 | static unsigned int last_bulk_repeat; | ||
100 | |||
101 | /** | ||
102 | * Component when the last bulk was logged. | ||
103 | */ | ||
104 | static const char *last_bulk_comp; | ||
105 | |||
106 | /** | ||
107 | * Running component. | ||
108 | */ | ||
109 | static const char *component; | ||
110 | |||
111 | /** | ||
112 | * Minimum log level. | ||
113 | */ | ||
114 | static enum GNUNET_ErrorType min_level; | ||
115 | |||
116 | /** | ||
117 | * Linked list of our custom loggres. | ||
118 | */ | ||
119 | static struct CustomLogger *loggers; | ||
120 | |||
121 | /** | ||
122 | * Number of log calls to ignore. | ||
123 | */ | ||
124 | static 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 | */ | ||
131 | static enum GNUNET_ErrorType | ||
132 | get_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 | */ | ||
151 | int | ||
152 | GNUNET_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 | */ | ||
178 | void | ||
179 | GNUNET_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 | */ | ||
196 | void | ||
197 | GNUNET_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 | |||
218 | static void | ||
219 | output_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 | |||
234 | static void | ||
235 | flush_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 | */ | ||
275 | void | ||
276 | GNUNET_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 | |||
290 | static void | ||
291 | mylog (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 | |||
342 | void | ||
343 | GNUNET_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 | |||
352 | void | ||
353 | GNUNET_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 | */ | ||
366 | const char * | ||
367 | GNUNET_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 | */ | ||
390 | const char * | ||
391 | GNUNET_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 | */ | ||
39 | struct 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 | */ | ||
61 | struct 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 | */ | ||
82 | struct 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 | */ | ||
101 | struct GNUNET_CONFIGURATION_Handle * | ||
102 | GNUNET_CONFIGURATION_create () | ||
103 | { | ||
104 | return GNUNET_malloc (sizeof (struct GNUNET_CONFIGURATION_Handle)); | ||
105 | } | ||
106 | |||
107 | void | ||
108 | GNUNET_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 | |||
129 | int | ||
130 | GNUNET_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 | |||
237 | int | ||
238 | GNUNET_CONFIGURATION_test_dirty (struct GNUNET_CONFIGURATION_Handle *cfg) | ||
239 | { | ||
240 | return cfg->dirty; | ||
241 | } | ||
242 | |||
243 | int | ||
244 | GNUNET_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 | |||
318 | static struct ConfigSection * | ||
319 | findSection (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 | |||
330 | static struct ConfigEntry * | ||
331 | findEntry (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 | |||
346 | void | ||
347 | GNUNET_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 | |||
377 | void | ||
378 | GNUNET_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 | |||
388 | int | ||
389 | GNUNET_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 | |||
404 | int | ||
405 | GNUNET_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 | |||
421 | int | ||
422 | GNUNET_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 | */ | ||
457 | int | ||
458 | GNUNET_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 | */ | ||
475 | char * | ||
476 | GNUNET_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 | */ | ||
527 | int | ||
528 | GNUNET_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 | */ | ||
558 | int | ||
559 | GNUNET_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 | */ | ||
581 | int | ||
582 | GNUNET_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 | |||
646 | static char * | ||
647 | escape_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 | |||
676 | static int | ||
677 | test_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 | */ | ||
692 | int | ||
693 | GNUNET_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 | */ | ||
735 | int | ||
736 | GNUNET_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 | */ | ||
813 | int | ||
814 | GNUNET_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 | |||
47 | struct 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 | */ | ||
85 | static void | ||
86 | setBit (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 | */ | ||
103 | static void | ||
104 | clearBit (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 | */ | ||
121 | static int | ||
122 | testBit (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 | */ | ||
144 | static void | ||
145 | incrementBit (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 | */ | ||
189 | static void | ||
190 | decrementBit (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 | */ | ||
242 | static int | ||
243 | makeEmptyFile (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 | */ | ||
284 | typedef 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 | */ | ||
296 | static void | ||
297 | iterateBits (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 | */ | ||
337 | static void | ||
338 | incrementBitCallback (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 | */ | ||
351 | static void | ||
352 | decrementBitCallback (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 | */ | ||
365 | static void | ||
366 | testBitCallback (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 | */ | ||
386 | struct GNUNET_CONTAINER_BloomFilter * | ||
387 | GNUNET_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 | */ | ||
476 | struct GNUNET_CONTAINER_BloomFilter * | ||
477 | GNUNET_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 | */ | ||
515 | int | ||
516 | GNUNET_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 | */ | ||
535 | void | ||
536 | GNUNET_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 | */ | ||
552 | void | ||
553 | GNUNET_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 | */ | ||
571 | int | ||
572 | GNUNET_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 | */ | ||
590 | void | ||
591 | GNUNET_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 | */ | ||
608 | int | ||
609 | GNUNET_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 | */ | ||
631 | void | ||
632 | GNUNET_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 | */ | ||
653 | void | ||
654 | GNUNET_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 | */ | ||
36 | struct 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 | */ | ||
48 | struct 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 | |||
62 | struct 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 | |||
76 | void | ||
77 | internal_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 | |||
92 | void | ||
93 | printTree (struct GNUNET_CONTAINER_Heap *root) | ||
94 | { | ||
95 | internal_print (root->root); | ||
96 | } | ||
97 | |||
98 | struct GNUNET_CONTAINER_Heap * | ||
99 | GNUNET_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 | |||
112 | void | ||
113 | GNUNET_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 | |||
125 | struct GNUNET_CONTAINER_heap_node * | ||
126 | find_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 | |||
148 | static struct GNUNET_CONTAINER_heap_node * | ||
149 | getNextPos (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 | |||
191 | static struct GNUNET_CONTAINER_heap_node * | ||
192 | getPos (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 | |||
221 | void | ||
222 | swapNodes (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 | |||
254 | void | ||
255 | percolateHeap (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 | |||
274 | void | ||
275 | percolateDownHeap (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 | |||
320 | void * | ||
321 | GNUNET_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 | |||
372 | int | ||
373 | GNUNET_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 | |||
402 | void * | ||
403 | GNUNET_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 | |||
432 | static int | ||
433 | updatedCost (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 | |||
458 | int | ||
459 | GNUNET_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 | |||
474 | void | ||
475 | internal_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 | |||
486 | int | ||
487 | GNUNET_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 | |||
495 | void * | ||
496 | GNUNET_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 | |||
527 | unsigned int | ||
528 | GNUNET_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 | |||
37 | struct Item | ||
38 | { | ||
39 | EXTRACTOR_KeywordType type; | ||
40 | char *data; | ||
41 | }; | ||
42 | |||
43 | /** | ||
44 | * Meta data to associate with a file, directory or namespace. | ||
45 | */ | ||
46 | struct GNUNET_CONTAINER_MetaData | ||
47 | { | ||
48 | uint32_t itemCount; | ||
49 | struct Item *items; | ||
50 | }; | ||
51 | |||
52 | /** | ||
53 | * Create a fresh struct CONTAINER_MetaData token. | ||
54 | */ | ||
55 | struct GNUNET_CONTAINER_MetaData * | ||
56 | GNUNET_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 | */ | ||
68 | void | ||
69 | GNUNET_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 | */ | ||
85 | void | ||
86 | GNUNET_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 | */ | ||
104 | int | ||
105 | GNUNET_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 | */ | ||
144 | int | ||
145 | GNUNET_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 | */ | ||
175 | int | ||
176 | GNUNET_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 | */ | ||
205 | char * | ||
206 | GNUNET_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 | */ | ||
222 | char * | ||
223 | GNUNET_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 | */ | ||
253 | size_t | ||
254 | GNUNET_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 | */ | ||
282 | struct GNUNET_CONTAINER_MetaData * | ||
283 | GNUNET_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 | */ | ||
304 | int | ||
305 | GNUNET_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 | |||
335 | static unsigned int | ||
336 | tryCompression (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 | */ | ||
370 | static char * | ||
371 | decompress (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 | |||
401 | struct 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 | */ | ||
444 | int | ||
445 | GNUNET_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 | */ | ||
535 | unsigned int | ||
536 | GNUNET_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 | */ | ||
591 | struct GNUNET_CONTAINER_MetaData * | ||
592 | GNUNET_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; | ||
684 | FAILURE: | ||
685 | GNUNET_free_non_null (data); | ||
686 | return NULL; /* size too small */ | ||
687 | } | ||
688 | |||
689 | /** | ||
690 | * Test if two MDs are equal. | ||
691 | */ | ||
692 | int | ||
693 | GNUNET_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 | |||
31 | struct MapEntry | ||
32 | { | ||
33 | GNUNET_HashCode key; | ||
34 | void *value; | ||
35 | struct MapEntry *next; | ||
36 | }; | ||
37 | |||
38 | struct GNUNET_CONTAINER_MultiHashMap | ||
39 | { | ||
40 | |||
41 | struct MapEntry **map; | ||
42 | |||
43 | unsigned int size; | ||
44 | |||
45 | unsigned int map_length; | ||
46 | }; | ||
47 | |||
48 | struct GNUNET_CONTAINER_MultiHashMap * | ||
49 | GNUNET_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 | |||
61 | void | ||
62 | GNUNET_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 | |||
80 | static unsigned int | ||
81 | idx_of (const struct GNUNET_CONTAINER_MultiHashMap *m, | ||
82 | const GNUNET_HashCode * key) | ||
83 | { | ||
84 | return (*(unsigned int *) key) % m->map_length; | ||
85 | } | ||
86 | |||
87 | unsigned int | ||
88 | GNUNET_CONTAINER_multihashmap_size (const struct GNUNET_CONTAINER_MultiHashMap | ||
89 | *map) | ||
90 | { | ||
91 | return map->size; | ||
92 | } | ||
93 | |||
94 | void * | ||
95 | GNUNET_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 | |||
110 | int | ||
111 | GNUNET_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 | |||
135 | int | ||
136 | GNUNET_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 | |||
166 | int | ||
167 | GNUNET_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 | |||
204 | int | ||
205 | GNUNET_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 | |||
223 | static void | ||
224 | grow (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 | |||
248 | int | ||
249 | GNUNET_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 | |||
287 | int | ||
288 | GNUNET_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 | |||
312 | void * | ||
313 | GNUNET_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 | */ | ||
36 | void | ||
37 | GNUNET_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 | */ | ||
50 | int | ||
51 | GNUNET_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 | */ | ||
75 | int | ||
76 | GNUNET_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 | */ | ||
117 | int | ||
118 | GNUNET_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 | ||
37 | typedef unsigned int uLong; | ||
38 | #elif ULONG_MAX >= 0xffffffff | ||
39 | typedef 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 | ||
48 | static 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 | */ | ||
55 | void __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 | */ | ||
79 | static uLong | ||
80 | crc32 (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 | */ | ||
97 | int | ||
98 | GNUNET_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 | |||
41 | struct sha512_ctx | ||
42 | { | ||
43 | unsigned long long state[8]; | ||
44 | unsigned int count[4]; | ||
45 | unsigned char buf[128]; | ||
46 | }; | ||
47 | |||
48 | static unsigned long long | ||
49 | Ch (unsigned long long x, unsigned long long y, unsigned long long z) | ||
50 | { | ||
51 | return z ^ (x & (y ^ z)); | ||
52 | } | ||
53 | |||
54 | static unsigned long long | ||
55 | Maj (unsigned long long x, unsigned long long y, unsigned long long z) | ||
56 | { | ||
57 | return (x & y) | (z & (x | y)); | ||
58 | } | ||
59 | |||
60 | static unsigned long long | ||
61 | RORu64 (unsigned long long x, unsigned long long y) | ||
62 | { | ||
63 | return (x >> y) | (x << (64 - y)); | ||
64 | } | ||
65 | |||
66 | const 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 | |||
143 | static void | ||
144 | sha512_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 | |||
223 | static void | ||
224 | sha512_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 | |||
238 | static void | ||
239 | sha512_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 | |||
278 | static void | ||
279 | sha512_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 | */ | ||
366 | void | ||
367 | GNUNET_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 | */ | ||
381 | struct 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 | */ | ||
441 | static void | ||
442 | file_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 | */ | ||
458 | static void | ||
459 | file_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 | */ | ||
503 | void | ||
504 | GNUNET_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 | */ | ||
548 | static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; | ||
549 | |||
550 | static unsigned int | ||
551 | getValue__ (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 | */ | ||
571 | void | ||
572 | GNUNET_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 | */ | ||
616 | int | ||
617 | GNUNET_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 | */ | ||
657 | unsigned int | ||
658 | GNUNET_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 | |||
666 | void | ||
667 | GNUNET_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 | |||
675 | void | ||
676 | GNUNET_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 | |||
686 | void | ||
687 | GNUNET_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 | |||
697 | void | ||
698 | GNUNET_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 | */ | ||
710 | void | ||
711 | GNUNET_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 | */ | ||
732 | int | ||
733 | GNUNET_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 | */ | ||
744 | int | ||
745 | GNUNET_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 | */ | ||
770 | int | ||
771 | GNUNET_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 | |||
49 | typedef 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 | */ | ||
63 | struct 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 */ | ||
71 | static 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])) | ||
155 | static 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 | */ | ||
167 | static unsigned int | ||
168 | get_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 | */ | ||
181 | static void | ||
182 | set_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 | |||
192 | static void | ||
193 | mpz_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 | */ | ||
219 | static int | ||
220 | is_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 | |||
283 | leave: | ||
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 | |||
294 | static void | ||
295 | gen_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 | */ | ||
370 | static int | ||
371 | test_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 | */ | ||
398 | static void | ||
399 | generate_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 | */ | ||
468 | struct 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 | */ | ||
489 | static struct KskRsaPrivateKeyBinaryEncoded * | ||
490 | makeKblockKeyInternal (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 | */ | ||
564 | static struct GNUNET_CRYPTO_RsaPrivateKey * | ||
565 | ksk_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 | |||
727 | typedef struct | ||
728 | { | ||
729 | GNUNET_HashCode hc; | ||
730 | struct KskRsaPrivateKeyBinaryEncoded *pke; | ||
731 | } KBlockKeyCacheLine; | ||
732 | |||
733 | static KBlockKeyCacheLine **cache; | ||
734 | static unsigned int cacheSize; | ||
735 | |||
736 | /** | ||
737 | * Deterministically (!) create a hostkey using only the | ||
738 | * given HashCode as input to the PRNG. | ||
739 | */ | ||
740 | struct GNUNET_CRYPTO_RsaPrivateKey * | ||
741 | GNUNET_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 | |||
764 | void __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 | |||
780 | void __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 | */ | ||
35 | unsigned int | ||
36 | GNUNET_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 | */ | ||
77 | unsigned int * | ||
78 | GNUNET_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 | */ | ||
102 | unsigned long long | ||
103 | GNUNET_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 | */ | ||
129 | void | ||
130 | GNUNET_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 | */ | ||
44 | struct 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 | */ | ||
57 | struct 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 | */ | ||
91 | static void | ||
92 | adjust (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 | */ | ||
104 | struct GNUNET_CRYPTO_RsaPrivateKey * | ||
105 | GNUNET_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 | */ | ||
128 | void | ||
129 | GNUNET_CRYPTO_rsa_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey) | ||
130 | { | ||
131 | gcry_sexp_release (hostkey->sexp); | ||
132 | GNUNET_free (hostkey); | ||
133 | } | ||
134 | |||
135 | static int | ||
136 | key_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 | */ | ||
192 | void | ||
193 | GNUNET_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 | */ | ||
239 | static struct GNUNET_CRYPTO_RsaPrivateKey * | ||
240 | public2PrivateKey (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 | */ | ||
299 | static struct RsaPrivateKeyBinaryEncoded * | ||
300 | rsa_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 | */ | ||
384 | static struct GNUNET_CRYPTO_RsaPrivateKey * | ||
385 | rsa_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 | */ | ||
559 | struct GNUNET_CRYPTO_RsaPrivateKey * | ||
560 | GNUNET_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 | */ | ||
736 | int | ||
737 | GNUNET_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 | */ | ||
786 | int | ||
787 | GNUNET_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 | */ | ||
838 | int | ||
839 | GNUNET_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 | */ | ||
886 | int | ||
887 | GNUNET_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 | |||
70 | typedef struct | ||
71 | { | ||
72 | unsigned long long total; | ||
73 | int include_sym_links; | ||
74 | } GetFileSizeData; | ||
75 | |||
76 | static int | ||
77 | getSizeRec (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 | */ | ||
117 | int | ||
118 | GNUNET_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 | */ | ||
139 | long | ||
140 | GNUNET_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 | */ | ||
184 | int | ||
185 | GNUNET_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 | */ | ||
216 | int | ||
217 | GNUNET_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 | */ | ||
259 | int | ||
260 | GNUNET_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 | */ | ||
339 | int | ||
340 | GNUNET_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 | */ | ||
368 | int | ||
369 | GNUNET_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 | */ | ||
392 | static int | ||
393 | atoo (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 | */ | ||
413 | int | ||
414 | GNUNET_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 | */ | ||
458 | int | ||
459 | GNUNET_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 | */ | ||
544 | struct 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 | */ | ||
587 | static void | ||
588 | directory_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 | */ | ||
613 | int | ||
614 | GNUNET_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 | */ | ||
663 | void | ||
664 | GNUNET_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 | |||
683 | static int | ||
684 | remove_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 | */ | ||
698 | int | ||
699 | GNUNET_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 | |||
727 | void | ||
728 | GNUNET_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 | |||
734 | int | ||
735 | GNUNET_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 | */ | ||
784 | int | ||
785 | GNUNET_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; | ||
824 | FAIL: | ||
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 | */ | ||
836 | void | ||
837 | GNUNET_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 | */ | ||
862 | int | ||
863 | GNUNET_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 | */ | ||
896 | char * | ||
897 | GNUNET_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 | |||
9 | NOTE: The canonical source of this file is maintained with the GNU C Library. | ||
10 | Bugs can be reported to bug-glibc@prep.ai.mit.edu. | ||
11 | |||
12 | This program is free software; you can redistribute it and/or modify it | ||
13 | under the terms of the GNU General Public License as published by the | ||
14 | Free Software Foundation; either version 2, or (at your option) any | ||
15 | later version. | ||
16 | |||
17 | This program is distributed in the hope that it will be useful, | ||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | GNU General Public License for more details. | ||
21 | |||
22 | You should have received a copy of the GNU General Public License | ||
23 | along with this program; if not, write to the Free Software | ||
24 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | ||
25 | USA. | ||
26 | |||
27 | |||
28 | This code was heavily modified for GNUnet. | ||
29 | Copyright (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 | |||
90 | struct 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 | |||
121 | static 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. */ | ||
136 | static 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 | |||
142 | static 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 | |||
151 | static char *nextchar; | ||
152 | |||
153 | /* Callers store zero here to inhibit the error message | ||
154 | for unrecognized options. */ | ||
155 | |||
156 | static 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 | |||
162 | static 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 | |||
193 | static enum | ||
194 | { | ||
195 | REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER | ||
196 | } ordering; | ||
197 | |||
198 | /* Value of POSIXLY_CORRECT environment variable. */ | ||
199 | static 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 | |||
213 | char *getenv (); | ||
214 | |||
215 | static char * | ||
216 | my_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. */ | ||
237 | extern 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 | |||
249 | static int first_nonopt; | ||
250 | static 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 */ | ||
257 | extern char *__getopt_nonoption_flags; | ||
258 | |||
259 | static int nonoption_flags_max_len; | ||
260 | static int nonoption_flags_len; | ||
261 | |||
262 | static int original_argc; | ||
263 | static char *const *original_argv; | ||
264 | |||
265 | extern 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. */ | ||
270 | static 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 | |||
279 | text_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__ | ||
302 | static void exchange (char **); | ||
303 | #endif | ||
304 | |||
305 | static void | ||
306 | exchange (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__ | ||
388 | static const char *_getopt_initialize (int, char *const *, const char *); | ||
389 | #endif | ||
390 | static 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 | |||
515 | static int | ||
516 | GN_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 | |||
976 | static int | ||
977 | GNgetopt_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 | */ | ||
998 | int | ||
999 | GNUNET_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 | |||
32 | int | ||
33 | GNUNET_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 | |||
47 | int | ||
48 | GNUNET_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 | |||
134 | int | ||
135 | GNUNET_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 | |||
145 | int | ||
146 | GNUNET_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 | |||
154 | int | ||
155 | GNUNET_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 | |||
168 | int | ||
169 | GNUNET_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 | |||
184 | int | ||
185 | GNUNET_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 | |||
45 | struct 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 | */ | ||
85 | struct 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 | */ | ||
201 | struct GNUNET_NETWORK_SocketHandle * | ||
202 | GNUNET_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 | */ | ||
228 | struct GNUNET_NETWORK_SocketHandle * | ||
229 | GNUNET_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 | */ | ||
325 | int | ||
326 | GNUNET_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 | */ | ||
343 | static int | ||
344 | socket_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 | */ | ||
401 | static int | ||
402 | try_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 | */ | ||
476 | static void | ||
477 | connect_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 | */ | ||
537 | struct GNUNET_NETWORK_SocketHandle * | ||
538 | GNUNET_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 | */ | ||
594 | struct GNUNET_NETWORK_SocketHandle * | ||
595 | GNUNET_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 | */ | ||
637 | int | ||
638 | GNUNET_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 | */ | ||
650 | static void | ||
651 | destroy_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 | */ | ||
721 | void | ||
722 | GNUNET_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 | */ | ||
742 | static void | ||
743 | signal_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 | */ | ||
760 | static void | ||
761 | signal_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 | */ | ||
774 | static void | ||
775 | receive_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)); | ||
807 | RETRY: | ||
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 | */ | ||
837 | static void | ||
838 | receive_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 | */ | ||
913 | GNUNET_SCHEDULER_TaskIdentifier | ||
914 | GNUNET_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 | */ | ||
947 | void * | ||
948 | GNUNET_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 | */ | ||
966 | static int | ||
967 | process_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 | */ | ||
1013 | static void | ||
1014 | transmit_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 | |||
1028 | static void | ||
1029 | transmit_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 | */ | ||
1047 | static void | ||
1048 | transmit_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 | } | ||
1103 | RETRY: | ||
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 | ||
1141 | SCHEDULE_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 | */ | ||
1167 | struct GNUNET_NETWORK_TransmitHandle * | ||
1168 | GNUNET_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 | */ | ||
1223 | void | ||
1224 | GNUNET_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 | ||
28 | extern "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 | */ | ||
54 | static char * | ||
55 | get_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 | */ | ||
90 | static char * | ||
91 | get_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 | */ | ||
124 | static char * | ||
125 | get_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 | ||
141 | typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize); | ||
142 | |||
143 | static char * | ||
144 | get_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 | |||
177 | static char * | ||
178 | get_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 | |||
208 | static char * | ||
209 | get_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 | |||
252 | static char * | ||
253 | get_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 | */ | ||
269 | static char * | ||
270 | os_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 | */ | ||
311 | static char * | ||
312 | os_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 | */ | ||
343 | char * | ||
344 | GNUNET_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 | |||
55 | static processor_cpu_load_info_t prev_cpu_load; | ||
56 | #endif | ||
57 | |||
58 | #define DEBUG_STATUSCALLS GNUNET_NO | ||
59 | |||
60 | #ifdef LINUX | ||
61 | static FILE *proc_stat; | ||
62 | #endif | ||
63 | |||
64 | /** | ||
65 | * Current CPU load, as percentage of CPU cycles not idle or | ||
66 | * blocked on IO. | ||
67 | */ | ||
68 | static int currentCPULoad; | ||
69 | |||
70 | static double agedCPULoad = -1; | ||
71 | |||
72 | /** | ||
73 | * Current IO load, as percentage of CPU cycles blocked on IO. | ||
74 | */ | ||
75 | static int currentIOLoad; | ||
76 | |||
77 | static double agedIOLoad = -1; | ||
78 | |||
79 | #ifdef OSX | ||
80 | static int | ||
81 | initMachCpuStats () | ||
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 | */ | ||
121 | static int | ||
122 | updateUsage () | ||
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 | */ | ||
519 | static void | ||
520 | updateAgedLoad (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 | */ | ||
575 | int | ||
576 | GNUNET_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 | */ | ||
598 | int | ||
599 | GNUNET_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 | */ | ||
620 | void __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 | */ | ||
637 | void __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 | */ | ||
38 | void | ||
39 | GNUNET_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 | */ | ||
34 | int | ||
35 | GNUNET_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 | */ | ||
123 | pid_t | ||
124 | GNUNET_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 | */ | ||
164 | pid_t | ||
165 | GNUNET_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 | |||
31 | static void | ||
32 | perfHash () | ||
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 | |||
52 | int | ||
53 | main (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 | */ | ||
36 | struct 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 | */ | ||
58 | static char *old_dlsearchpath; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * List of plugins we have loaded. | ||
63 | */ | ||
64 | static struct PluginList *plugins; | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Setup libtool paths. | ||
69 | */ | ||
70 | void __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 | */ | ||
117 | void __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 | */ | ||
137 | static GNUNET_PLUGIN_Callback | ||
138 | resolve_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 | */ | ||
168 | void * | ||
169 | GNUNET_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 | */ | ||
209 | void * | ||
210 | GNUNET_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 | */ | ||
41 | struct 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 | */ | ||
75 | static void | ||
76 | program_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 | */ | ||
88 | static int | ||
89 | cmd_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 | */ | ||
117 | int | ||
118 | GNUNET_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 | |||
36 | struct DiscoveryCallback | ||
37 | { | ||
38 | struct DiscoveryCallback *next; | ||
39 | GNUNET_PSEUDONYM_Iterator callback; | ||
40 | void *closure; | ||
41 | }; | ||
42 | |||
43 | static struct DiscoveryCallback *head; | ||
44 | |||
45 | /** | ||
46 | * Internal notification about new tracked URI. | ||
47 | */ | ||
48 | static void | ||
49 | internal_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 | */ | ||
66 | int | ||
67 | GNUNET_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 | */ | ||
87 | int | ||
88 | GNUNET_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 | */ | ||
117 | static char * | ||
118 | get_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 | |||
133 | static void | ||
134 | write_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 | |||
180 | static int | ||
181 | read_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 | */ | ||
274 | char * | ||
275 | GNUNET_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 | */ | ||
355 | int | ||
356 | GNUNET_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 | |||
402 | struct ListPseudonymClosure | ||
403 | { | ||
404 | GNUNET_PSEUDONYM_Iterator iterator; | ||
405 | void *closure; | ||
406 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
407 | }; | ||
408 | |||
409 | static int | ||
410 | list_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 | */ | ||
440 | int | ||
441 | GNUNET_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 | */ | ||
467 | int | ||
468 | GNUNET_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 | */ | ||
493 | static int | ||
494 | merge_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 | */ | ||
510 | void | ||
511 | GNUNET_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 | */ | ||
35 | struct 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 | */ | ||
112 | struct 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 | */ | ||
163 | static enum GNUNET_SCHEDULER_Priority | ||
164 | check_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 | */ | ||
176 | static void | ||
177 | update_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 | */ | ||
191 | static void | ||
192 | set_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 | */ | ||
208 | static int | ||
209 | is_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 | */ | ||
248 | static void | ||
249 | update_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 | */ | ||
288 | static int | ||
289 | set_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 | */ | ||
311 | static int | ||
312 | is_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 | */ | ||
345 | static void | ||
346 | queue_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 | */ | ||
358 | static void | ||
359 | check_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 | */ | ||
396 | static void | ||
397 | run_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 | */ | ||
435 | static volatile int sig_shutdown; | ||
436 | |||
437 | |||
438 | /** | ||
439 | * Signal handler called for signals that should cause us to shutdown. | ||
440 | */ | ||
441 | static void | ||
442 | sighandler_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 | */ | ||
457 | void | ||
458 | GNUNET_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 | */ | ||
536 | void | ||
537 | GNUNET_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 | */ | ||
552 | unsigned int | ||
553 | GNUNET_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 | */ | ||
581 | void * | ||
582 | GNUNET_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 | */ | ||
642 | void | ||
643 | GNUNET_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 | */ | ||
679 | GNUNET_SCHEDULER_TaskIdentifier | ||
680 | GNUNET_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 | */ | ||
716 | GNUNET_SCHEDULER_TaskIdentifier | ||
717 | GNUNET_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 | */ | ||
755 | GNUNET_SCHEDULER_TaskIdentifier | ||
756 | GNUNET_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 | */ | ||
798 | GNUNET_SCHEDULER_TaskIdentifier | ||
799 | GNUNET_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 | */ | ||
853 | GNUNET_SCHEDULER_TaskIdentifier | ||
854 | GNUNET_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 | */ | ||
40 | struct 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 | */ | ||
57 | struct 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 | */ | ||
79 | struct 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 | */ | ||
151 | struct 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 | */ | ||
270 | static void | ||
271 | destroy_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 | */ | ||
304 | static void | ||
305 | process_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 | */ | ||
357 | static int | ||
358 | open_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 | */ | ||
425 | struct GNUNET_SERVER_Handle * | ||
426 | GNUNET_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 | */ | ||
481 | void | ||
482 | GNUNET_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 | */ | ||
508 | void | ||
509 | GNUNET_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 | */ | ||
537 | int | ||
538 | GNUNET_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 | */ | ||
591 | static void | ||
592 | shutdown_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 | |||
633 | static void | ||
634 | process_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 | */ | ||
672 | static void | ||
673 | process_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 | |||
736 | static void | ||
737 | restart_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 | */ | ||
754 | static void | ||
755 | add_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 | |||
768 | static GNUNET_SCHEDULER_TaskIdentifier | ||
769 | sock_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 | |||
777 | static void | ||
778 | sock_receive_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti) | ||
779 | { | ||
780 | GNUNET_NETWORK_receive_cancel (cls, ti); | ||
781 | } | ||
782 | |||
783 | |||
784 | static void * | ||
785 | sock_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 | |||
796 | static void | ||
797 | sock_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 | */ | ||
809 | static int | ||
810 | sock_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 | */ | ||
821 | static void | ||
822 | sock_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 | */ | ||
840 | struct GNUNET_SERVER_Client * | ||
841 | GNUNET_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 | */ | ||
879 | struct GNUNET_SERVER_Client * | ||
880 | GNUNET_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 | */ | ||
919 | void | ||
920 | GNUNET_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 | */ | ||
934 | void | ||
935 | GNUNET_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 | */ | ||
952 | int | ||
953 | GNUNET_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 | */ | ||
973 | void | ||
974 | GNUNET_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 | */ | ||
996 | void | ||
997 | GNUNET_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 | */ | ||
1024 | struct GNUNET_NETWORK_TransmitHandle * | ||
1025 | GNUNET_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 | */ | ||
1048 | void | ||
1049 | GNUNET_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 | |||
45 | struct 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 | */ | ||
77 | static size_t | ||
78 | transmit_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 | */ | ||
129 | struct GNUNET_SERVER_TransmitContext * | ||
130 | GNUNET_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 | */ | ||
151 | void | ||
152 | GNUNET_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 | */ | ||
180 | void | ||
181 | GNUNET_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 | */ | ||
43 | struct IPv4NetworkSet | ||
44 | { | ||
45 | struct in_addr network; | ||
46 | struct in_addr netmask; | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * @brief network in CIDR notation for IPV6. | ||
51 | */ | ||
52 | struct 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 | */ | ||
70 | static struct IPv4NetworkSet * | ||
71 | parse_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 | */ | ||
235 | static struct IPv6NetworkSet * | ||
236 | parse_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 | */ | ||
356 | static int | ||
357 | check_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 | */ | ||
383 | static int | ||
384 | check_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; | ||
396 | NEXT: | ||
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 | */ | ||
418 | struct 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 | |||
526 | static size_t | ||
527 | write_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 | */ | ||
552 | static void | ||
553 | handle_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 | */ | ||
576 | static void | ||
577 | handle_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 | */ | ||
604 | static 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 | */ | ||
620 | static int | ||
621 | check_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 | */ | ||
675 | static char * | ||
676 | get_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 | */ | ||
693 | static int | ||
694 | process_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 | */ | ||
722 | static int | ||
723 | process_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 | */ | ||
765 | static int | ||
766 | setup_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 | */ | ||
1005 | static char * | ||
1006 | get_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 | */ | ||
1022 | static int | ||
1023 | write_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 | */ | ||
1078 | static void | ||
1079 | service_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 | */ | ||
1118 | static int | ||
1119 | detach_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 | */ | ||
1196 | static int | ||
1197 | set_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 | */ | ||
1241 | static void | ||
1242 | pid_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 | */ | ||
1266 | int | ||
1267 | GNUNET_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 | */ | ||
1375 | struct GNUNET_SERVICE_Context * | ||
1376 | GNUNET_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 | */ | ||
1424 | struct GNUNET_SERVER_Handle * | ||
1425 | GNUNET_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 | */ | ||
1436 | void | ||
1437 | GNUNET_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 | |||
31 | struct 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 | |||
42 | struct GNUNET_SIGNAL_Context * | ||
43 | GNUNET_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 | |||
66 | void | ||
67 | GNUNET_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 | */ | ||
51 | unsigned int | ||
52 | GNUNET_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 | */ | ||
92 | unsigned int | ||
93 | GNUNET_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 | */ | ||
126 | char * | ||
127 | GNUNET_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 | */ | ||
166 | char * | ||
167 | GNUNET_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 | */ | ||
225 | char * | ||
226 | GNUNET_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 | */ | ||
342 | char * | ||
343 | GNUNET_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 | */ | ||
378 | char * | ||
379 | GNUNET_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 | |||
38 | static struct GNUNET_CLIENT_Connection *client; | ||
39 | |||
40 | static struct GNUNET_SERVER_Handle *server; | ||
41 | |||
42 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
43 | |||
44 | #define MY_TYPE 130 | ||
45 | |||
46 | struct CopyContext | ||
47 | { | ||
48 | struct GNUNET_SERVER_Client *client; | ||
49 | struct GNUNET_MessageHeader *cpy; | ||
50 | }; | ||
51 | |||
52 | static size_t | ||
53 | copy_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 | */ | ||
71 | static void | ||
72 | echo_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 | ©_msg, cc)); | ||
92 | } | ||
93 | |||
94 | |||
95 | static struct GNUNET_SERVER_MessageHandler handlers[] = { | ||
96 | {&echo_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, | ||
97 | {NULL, NULL, 0, 0} | ||
98 | }; | ||
99 | |||
100 | |||
101 | static void | ||
102 | recv_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 | |||
119 | static size_t | ||
120 | make_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 | |||
130 | static void | ||
131 | task (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 | */ | ||
169 | static int | ||
170 | check () | ||
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 | |||
184 | int | ||
185 | main (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 | |||
28 | static int | ||
29 | check () | ||
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 | |||
100 | int | ||
101 | main (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 | |||
29 | int | ||
30 | main (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 | |||
29 | static void | ||
30 | my_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 | |||
39 | int | ||
40 | main (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 | |||
30 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
31 | |||
32 | static int | ||
33 | testConfig () | ||
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 | |||
80 | static const char *want[] = { | ||
81 | "/Hello", | ||
82 | "/File Name", | ||
83 | "/World", | ||
84 | NULL, | ||
85 | NULL, | ||
86 | }; | ||
87 | |||
88 | static int | ||
89 | check (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 | |||
101 | static int | ||
102 | testConfigFilenames () | ||
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 | |||
168 | int | ||
169 | main (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] | ||
2 | SUBST=/hello | ||
3 | |||
4 | [GNUNET] | ||
5 | SUBST=hello | ||
6 | GNUNET_HOME=/tmp | ||
7 | |||
8 | [test] | ||
9 | a=a | ||
10 | b=b | ||
11 | five=5 | ||
12 | |||
13 | [more] | ||
14 | c=c | ||
15 | five=42 | ||
16 | |||
17 | |||
18 | [last] | ||
19 | test = $SUBST/world | ||
20 | boom = "1 2 3 testing" | ||
21 | trailing = YES | ||
22 | |||
23 | [FILENAMES] | ||
24 | test = "/Hello /File\ Name /World" | ||
25 | |||
26 | |||
27 | [TESTING] | ||
28 | WEAKRANDOM = 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 | */ | ||
38 | static void | ||
39 | nextHC (GNUNET_HashCode * hc) | ||
40 | { | ||
41 | GNUNET_CRYPTO_hash_create_random (hc); | ||
42 | } | ||
43 | |||
44 | static int | ||
45 | add_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 | |||
57 | int | ||
58 | main (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 | |||
31 | static int | ||
32 | iterator_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 | |||
44 | int | ||
45 | main (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 | |||
33 | static int | ||
34 | testMeta (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 | |||
120 | int | ||
121 | testMetaMore (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 | |||
154 | static int | ||
155 | testMetaLink () | ||
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 | |||
189 | static int | ||
190 | testThumbnail () | ||
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 | |||
243 | int | ||
244 | main (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 | |||
34 | static int | ||
35 | testMap (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 | |||
99 | int | ||
100 | main (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 | |||
33 | static int | ||
34 | testSymcipher () | ||
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 | |||
72 | int | ||
73 | verifyCrypto () | ||
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 | |||
153 | error: | ||
154 | |||
155 | GNUNET_free_non_null (res); | ||
156 | |||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | int | ||
161 | main (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 | |||
37 | static void | ||
38 | printWeakKey (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 | |||
47 | static int | ||
48 | testWeakKey () | ||
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 | |||
122 | static int | ||
123 | getWeakKeys () | ||
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 | |||
176 | int | ||
177 | main (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 | |||
33 | static 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 | |||
208 | int | ||
209 | main (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 | |||
31 | static char block[65536]; | ||
32 | |||
33 | #define FILENAME "testblock.dat" | ||
34 | |||
35 | static int | ||
36 | test (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 | |||
54 | static int | ||
55 | testEncoding () | ||
56 | { | ||
57 | int i; | ||
58 | for (i = 0; i < 255; i++) | ||
59 | if (0 != test (i)) | ||
60 | return 1; | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static int | ||
65 | testArithmetic () | ||
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 | |||
108 | static void | ||
109 | finished_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 | |||
122 | static void | ||
123 | file_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 | |||
131 | static int | ||
132 | testFileHash () | ||
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 | |||
149 | int | ||
150 | main (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 | |||
38 | static int | ||
39 | testMultiKey (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 | |||
87 | static int | ||
88 | testEncryptDecrypt (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 | |||
139 | static int | ||
140 | testSignVerify (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 | |||
188 | int | ||
189 | main (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 | |||
30 | static int | ||
31 | test (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 | |||
61 | int | ||
62 | main (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 | |||
39 | static int | ||
40 | testEncryptDecrypt () | ||
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 | ||
96 | static int | ||
97 | testEncryptPerformance () | ||
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 | |||
135 | static int | ||
136 | testEncryptDecryptSK () | ||
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 | |||
194 | static int | ||
195 | testSignVerify () | ||
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 | ||
248 | static int | ||
249 | testSignPerformance () | ||
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 | |||
283 | static int | ||
284 | testCreateFromFile () | ||
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 | |||
307 | int | ||
308 | main (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 | |||
33 | static int | ||
34 | testReadWrite () | ||
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 | |||
86 | static int | ||
87 | testOpenClose () | ||
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 | |||
125 | static int ok; | ||
126 | |||
127 | static int | ||
128 | scan_callback (void *want, const char *filename) | ||
129 | { | ||
130 | if (NULL != strstr (filename, want)) | ||
131 | ok++; | ||
132 | return GNUNET_OK; | ||
133 | } | ||
134 | |||
135 | static int | ||
136 | testDirScan () | ||
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 | |||
150 | static void | ||
151 | iter_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 | |||
160 | static void | ||
161 | iter_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 | |||
168 | static int | ||
169 | testDirIter () | ||
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 | |||
189 | static int | ||
190 | testGetHome () | ||
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 | |||
208 | static int | ||
209 | testCanonicalize () | ||
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 | |||
222 | static int | ||
223 | testChangeOwner () | ||
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 | |||
231 | static int | ||
232 | testDirMani () | ||
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 | |||
254 | int | ||
255 | main (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 | |||
31 | static int | ||
32 | testMinimal () | ||
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 | |||
54 | static int | ||
55 | testVerbose () | ||
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 | |||
87 | static int | ||
88 | testVersion () | ||
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 | |||
113 | static int | ||
114 | testAbout () | ||
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 | |||
139 | static int | ||
140 | testLogOpts () | ||
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 | |||
178 | static int | ||
179 | testFlagNum () | ||
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 | |||
218 | int | ||
219 | main (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 | |||
35 | static struct GNUNET_NETWORK_SocketHandle *csock; | ||
36 | |||
37 | static struct GNUNET_NETWORK_SocketHandle *asock; | ||
38 | |||
39 | static struct GNUNET_NETWORK_SocketHandle *lsock; | ||
40 | |||
41 | static size_t sofar; | ||
42 | |||
43 | static 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 | */ | ||
52 | static int | ||
53 | open_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 | static void | ||
72 | receive_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 | |||
108 | static void | ||
109 | run_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 | |||
132 | static size_t | ||
133 | make_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 | |||
144 | static void | ||
145 | task (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 | */ | ||
181 | static int | ||
182 | check () | ||
183 | { | ||
184 | int ok; | ||
185 | |||
186 | ok = 1; | ||
187 | GNUNET_SCHEDULER_run (&task, &ok); | ||
188 | return ok; | ||
189 | } | ||
190 | |||
191 | |||
192 | |||
193 | int | ||
194 | main (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 | |||
35 | static struct GNUNET_NETWORK_SocketHandle *csock; | ||
36 | |||
37 | static struct GNUNET_NETWORK_SocketHandle *asock; | ||
38 | |||
39 | static struct GNUNET_NETWORK_SocketHandle *lsock; | ||
40 | |||
41 | static size_t sofar; | ||
42 | |||
43 | static 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 | */ | ||
52 | static int | ||
53 | open_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 | |||
72 | static void | ||
73 | receive_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 | |||
99 | static void | ||
100 | run_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 | |||
127 | static size_t | ||
128 | make_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 | |||
135 | static void | ||
136 | task (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 | */ | ||
171 | static int | ||
172 | check () | ||
173 | { | ||
174 | int ok; | ||
175 | |||
176 | ok = 1; | ||
177 | GNUNET_SCHEDULER_run (&task, &ok); | ||
178 | return ok; | ||
179 | } | ||
180 | |||
181 | |||
182 | |||
183 | int | ||
184 | main (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 | |||
35 | static struct GNUNET_NETWORK_SocketHandle *csock; | ||
36 | |||
37 | static struct GNUNET_NETWORK_SocketHandle *asock; | ||
38 | |||
39 | static struct GNUNET_NETWORK_SocketHandle *lsock; | ||
40 | |||
41 | static int ls; | ||
42 | |||
43 | static 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 | */ | ||
53 | static int | ||
54 | open_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 | |||
74 | static void | ||
75 | dead_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 | |||
84 | static void | ||
85 | run_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 | |||
102 | static void | ||
103 | receive_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 | |||
114 | static void | ||
115 | task_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 | */ | ||
142 | static int | ||
143 | check_receive_cancel () | ||
144 | { | ||
145 | int ok; | ||
146 | |||
147 | ok = 1; | ||
148 | GNUNET_SCHEDULER_run (&task_receive_cancel, &ok); | ||
149 | return ok; | ||
150 | } | ||
151 | |||
152 | |||
153 | int | ||
154 | main (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 | |||
34 | static struct GNUNET_NETWORK_SocketHandle *csock; | ||
35 | |||
36 | static struct GNUNET_NETWORK_SocketHandle *lsock; | ||
37 | |||
38 | static 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 | */ | ||
46 | static int | ||
47 | open_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 | |||
66 | static size_t | ||
67 | send_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 | |||
96 | static void | ||
97 | task_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 | */ | ||
118 | static int | ||
119 | check_timeout () | ||
120 | { | ||
121 | int ok; | ||
122 | |||
123 | ok = 1; | ||
124 | GNUNET_SCHEDULER_run (&task_timeout, &ok); | ||
125 | return ok; | ||
126 | } | ||
127 | |||
128 | int | ||
129 | main (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 | |||
34 | static struct GNUNET_NETWORK_SocketHandle *csock; | ||
35 | |||
36 | static size_t | ||
37 | handle_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 | |||
51 | static void | ||
52 | task_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 | */ | ||
69 | static int | ||
70 | check_timeout () | ||
71 | { | ||
72 | int ok; | ||
73 | |||
74 | ok = 1; | ||
75 | GNUNET_SCHEDULER_run (&task_timeout, &ok); | ||
76 | return ok; | ||
77 | } | ||
78 | |||
79 | int | ||
80 | main (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 | |||
35 | static size_t | ||
36 | not_run (void *cls, size_t size, void *buf) | ||
37 | { | ||
38 | GNUNET_assert (0); | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | |||
43 | static void | ||
44 | task_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 | ¬_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 | */ | ||
69 | static int | ||
70 | check_transmit_cancel () | ||
71 | { | ||
72 | int ok; | ||
73 | |||
74 | ok = 1; | ||
75 | GNUNET_SCHEDULER_run (&task_transmit_cancel, &ok); | ||
76 | return ok; | ||
77 | } | ||
78 | |||
79 | |||
80 | int | ||
81 | main (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 | |||
34 | static int | ||
35 | testcpu () | ||
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 | |||
98 | static int | ||
99 | testdisk () | ||
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 | |||
171 | int | ||
172 | main (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 | */ | ||
36 | static int | ||
37 | proc (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 | |||
54 | static int | ||
55 | testifcs () | ||
56 | { | ||
57 | int ret; | ||
58 | |||
59 | ret = 1; | ||
60 | GNUNET_OS_network_interfaces_list (&proc, &ret); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | int | ||
65 | main (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 | |||
30 | static int | ||
31 | testprio () | ||
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 | |||
52 | int | ||
53 | main (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 | |||
29 | static int | ||
30 | check () | ||
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 | |||
53 | int | ||
54 | main (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 | |||
26 | void * | ||
27 | libgnunet_plugin_test_init (void *arg) | ||
28 | { | ||
29 | if (0 == strcmp (arg, "in")) | ||
30 | return "Hello"; | ||
31 | return NULL; | ||
32 | } | ||
33 | |||
34 | void * | ||
35 | libgnunet_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 | |||
30 | static int setme; | ||
31 | |||
32 | static 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 | */ | ||
40 | static void | ||
41 | runner (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 | */ | ||
61 | static int | ||
62 | check () | ||
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 | |||
83 | int | ||
84 | main (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 | |||
36 | static struct GNUNET_CONTAINER_MetaData *meta; | ||
37 | |||
38 | static GNUNET_HashCode id1; | ||
39 | |||
40 | static int | ||
41 | iter (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 | |||
58 | static int | ||
59 | noti_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 | |||
70 | int | ||
71 | main (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 | ¬i_callback, ¬iCount); | ||
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 */ | ||
131 | FAILURE: | ||
132 | GNUNET_PSEUDONYM_discovery_callback_unregister (¬i_callback, ¬iCount); | ||
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] | ||
3 | HOME = "/tmp/gnunet-pseudonym-test" | ||
4 | |||
5 | [TESTING] | ||
6 | WEAKRANDOM = 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 | |||
31 | static void | ||
32 | task2 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
33 | { | ||
34 | int *ok = cls; | ||
35 | GNUNET_assert (2 == *ok); | ||
36 | (*ok) = 3; | ||
37 | } | ||
38 | |||
39 | static void | ||
40 | task3 (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 | |||
50 | static void | ||
51 | task4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
52 | { | ||
53 | int *ok = cls; | ||
54 | GNUNET_assert (4 == *ok); | ||
55 | (*ok) = 5; | ||
56 | } | ||
57 | |||
58 | static int fds[2]; | ||
59 | |||
60 | |||
61 | static void | ||
62 | taskWrt (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 | |||
74 | static void | ||
75 | taskNeverRun (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
76 | { | ||
77 | GNUNET_assert (0); | ||
78 | } | ||
79 | |||
80 | static void | ||
81 | taskLast (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 | |||
89 | static void | ||
90 | taskRd (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 | |||
111 | static void | ||
112 | task5 (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 | |||
133 | static void | ||
134 | task1 (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 | */ | ||
172 | static int | ||
173 | check () | ||
174 | { | ||
175 | int ok; | ||
176 | |||
177 | ok = 1; | ||
178 | GNUNET_SCHEDULER_run (&task1, &ok); | ||
179 | return ok; | ||
180 | } | ||
181 | |||
182 | |||
183 | static void | ||
184 | taskSig (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 | */ | ||
205 | static int | ||
206 | checkSignal () | ||
207 | { | ||
208 | int ok; | ||
209 | |||
210 | ok = 1; | ||
211 | GNUNET_SCHEDULER_run (&taskSig, &ok); | ||
212 | return ok; | ||
213 | } | ||
214 | |||
215 | |||
216 | |||
217 | |||
218 | static void | ||
219 | taskCancel (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 | */ | ||
238 | static int | ||
239 | checkCancel () | ||
240 | { | ||
241 | int ok; | ||
242 | |||
243 | ok = 1; | ||
244 | GNUNET_SCHEDULER_run (&taskCancel, &ok); | ||
245 | return ok; | ||
246 | } | ||
247 | |||
248 | |||
249 | |||
250 | int | ||
251 | main (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 | |||
33 | static struct GNUNET_TIME_Absolute target; | ||
34 | |||
35 | static int i; | ||
36 | |||
37 | static 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 | */ | ||
49 | static void | ||
50 | test_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 | |||
78 | static int | ||
79 | check () | ||
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 | |||
96 | int | ||
97 | main (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 | |||
37 | static struct GNUNET_SERVER_Handle *server; | ||
38 | |||
39 | static struct GNUNET_SCHEDULER_Handle *sched; | ||
40 | |||
41 | static void | ||
42 | recv_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 | |||
53 | struct SignalTimeoutContext | ||
54 | { | ||
55 | GNUNET_NETWORK_Receiver cb; | ||
56 | void *cb_cls; | ||
57 | }; | ||
58 | |||
59 | |||
60 | static void | ||
61 | signal_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 | |||
70 | static GNUNET_SCHEDULER_TaskIdentifier | ||
71 | my_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 | |||
112 | static void | ||
113 | my_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti) | ||
114 | { | ||
115 | GNUNET_SCHEDULER_cancel (sched, ti); | ||
116 | } | ||
117 | |||
118 | static void * | ||
119 | my_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 | |||
141 | static void | ||
142 | my_transmit_ready_cancel_cb (void *cls, void *ctx) | ||
143 | { | ||
144 | GNUNET_assert (0); | ||
145 | } | ||
146 | |||
147 | |||
148 | static int | ||
149 | my_check (void *cls) | ||
150 | { | ||
151 | return GNUNET_YES; | ||
152 | } | ||
153 | |||
154 | |||
155 | static void my_destroy (void *cls); | ||
156 | |||
157 | |||
158 | struct CopyContext | ||
159 | { | ||
160 | struct GNUNET_SERVER_Client *client; | ||
161 | struct GNUNET_MessageHeader *cpy; | ||
162 | }; | ||
163 | |||
164 | static size_t | ||
165 | copy_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 | |||
178 | static void | ||
179 | recv_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 | ©_msg, cc)); | ||
209 | GNUNET_SERVER_client_drop (client); | ||
210 | } | ||
211 | |||
212 | |||
213 | static 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 | |||
220 | static void | ||
221 | my_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 | |||
231 | static void | ||
232 | task (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 | */ | ||
265 | static int | ||
266 | check () | ||
267 | { | ||
268 | int ok; | ||
269 | |||
270 | ok = 1; | ||
271 | GNUNET_SCHEDULER_run (&task, &ok); | ||
272 | return ok; | ||
273 | } | ||
274 | |||
275 | |||
276 | int | ||
277 | main (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 | |||
39 | static struct GNUNET_SERVER_Handle *server; | ||
40 | |||
41 | static struct GNUNET_CLIENT_Connection *client; | ||
42 | |||
43 | static struct GNUNET_SCHEDULER_Handle *sched; | ||
44 | |||
45 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
46 | |||
47 | static int ok; | ||
48 | |||
49 | static void | ||
50 | send_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 | |||
58 | static void | ||
59 | server_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 | |||
68 | static void | ||
69 | recv_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 | |||
120 | static void | ||
121 | disconnect_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 | */ | ||
139 | static void | ||
140 | notify_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 | |||
150 | static size_t | ||
151 | notify_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 | |||
168 | static struct GNUNET_SERVER_MessageHandler handlers[] = { | ||
169 | {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, | ||
170 | {NULL, NULL, 0, 0} | ||
171 | }; | ||
172 | |||
173 | |||
174 | static void | ||
175 | task (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, ¬ify_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 | ¬ify_ready, NULL); | ||
207 | } | ||
208 | |||
209 | |||
210 | /** | ||
211 | * Main method, starts scheduler with task1, | ||
212 | * checks that "ok" is correct at the end. | ||
213 | */ | ||
214 | static int | ||
215 | check () | ||
216 | { | ||
217 | |||
218 | ok = 1; | ||
219 | GNUNET_SCHEDULER_run (&task, NULL); | ||
220 | return ok; | ||
221 | } | ||
222 | |||
223 | |||
224 | int | ||
225 | main (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 | |||
40 | static struct GNUNET_SERVER_Handle *server; | ||
41 | |||
42 | static struct GNUNET_CLIENT_Connection *client; | ||
43 | |||
44 | static struct GNUNET_SCHEDULER_Handle *sched; | ||
45 | |||
46 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
47 | |||
48 | static int ok; | ||
49 | |||
50 | static void | ||
51 | send_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 | |||
60 | static void | ||
61 | recv_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 | */ | ||
114 | static void | ||
115 | notify_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 | |||
124 | static size_t | ||
125 | notify_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 | |||
142 | static struct GNUNET_SERVER_MessageHandler handlers[] = { | ||
143 | {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, | ||
144 | {NULL, NULL, 0, 0} | ||
145 | }; | ||
146 | |||
147 | |||
148 | static void | ||
149 | task (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, ¬ify_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 | ¬ify_ready, NULL); | ||
181 | } | ||
182 | |||
183 | |||
184 | /** | ||
185 | * Main method, starts scheduler with task1, | ||
186 | * checks that "ok" is correct at the end. | ||
187 | */ | ||
188 | static int | ||
189 | check () | ||
190 | { | ||
191 | |||
192 | ok = 1; | ||
193 | GNUNET_SCHEDULER_run (&task, NULL); | ||
194 | return ok; | ||
195 | } | ||
196 | |||
197 | |||
198 | int | ||
199 | main (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 | |||
39 | static struct GNUNET_SCHEDULER_Handle *sched; | ||
40 | |||
41 | static struct GNUNET_SERVICE_Context *sctx; | ||
42 | |||
43 | static void | ||
44 | end_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 | |||
56 | static size_t | ||
57 | build_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 | |||
74 | static void | ||
75 | ready (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 | |||
93 | static void | ||
94 | recv_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 | |||
103 | static struct GNUNET_SERVER_MessageHandler myhandlers[] = { | ||
104 | {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, | ||
105 | {NULL, NULL, 0, 0} | ||
106 | }; | ||
107 | |||
108 | static void | ||
109 | runner (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 | |||
121 | static void | ||
122 | term (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 | */ | ||
132 | static int | ||
133 | check () | ||
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 | |||
158 | static void | ||
159 | ready6 (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 | |||
176 | static void | ||
177 | runner6 (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 | */ | ||
193 | static int | ||
194 | check6 () | ||
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 | */ | ||
224 | static int | ||
225 | check6d () | ||
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 | |||
252 | static void | ||
253 | start_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 | |||
267 | static int | ||
268 | check_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 | |||
298 | int | ||
299 | main (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] | ||
2 | PORT=12435 | ||
3 | BINDTO=localhost | ||
4 | PIDFILE=/tmp/test-service.pid | ||
5 | TIMEOUT=30000 | ||
6 | MAXBUF=1024 | ||
7 | DISABLEV6=NO | ||
8 | ACCEPT_FROM=127.0.0.1; | ||
9 | REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0; | ||
10 | ACCEPT_FROM6=::1; | ||
11 | REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40; | ||
12 | HOSTNAME=localhost | ||
13 | ALLOW_SHUTDOWN=YES | ||
14 | |||
15 | [test_service6] | ||
16 | PORT=12435 | ||
17 | PIDFILE=/tmp/test-service.pid | ||
18 | TIMEOUT=30000 | ||
19 | MAXBUF=1024 | ||
20 | DISABLEV6=NO | ||
21 | ACCEPT_FROM=127.0.0.1; | ||
22 | REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0; | ||
23 | ACCEPT_FROM6=::1; | ||
24 | REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40; | ||
25 | HOSTNAME=::1 | ||
26 | ALLOW_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 | |||
33 | static int | ||
34 | check () | ||
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 | |||
101 | int | ||
102 | main (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 | |||
30 | static int | ||
31 | check () | ||
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 | |||
111 | int | ||
112 | main (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 | */ | ||
36 | struct GNUNET_TIME_Absolute | ||
37 | GNUNET_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 | */ | ||
51 | struct GNUNET_TIME_Relative | ||
52 | GNUNET_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 | */ | ||
61 | struct GNUNET_TIME_Relative | ||
62 | GNUNET_TIME_relative_get_unit () | ||
63 | { | ||
64 | static struct GNUNET_TIME_Relative one = { 1 }; | ||
65 | return one; | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Return "forever". | ||
70 | */ | ||
71 | struct GNUNET_TIME_Relative | ||
72 | GNUNET_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 | */ | ||
81 | struct GNUNET_TIME_Absolute | ||
82 | GNUNET_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 | */ | ||
94 | struct GNUNET_TIME_Absolute | ||
95 | GNUNET_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 | */ | ||
116 | struct GNUNET_TIME_Relative | ||
117 | GNUNET_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 | */ | ||
136 | struct GNUNET_TIME_Relative | ||
137 | GNUNET_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 | */ | ||
155 | struct GNUNET_TIME_Relative | ||
156 | GNUNET_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 | */ | ||
176 | struct GNUNET_TIME_Absolute | ||
177 | GNUNET_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 | */ | ||
199 | struct GNUNET_TIME_Relative | ||
200 | GNUNET_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 | */ | ||
220 | struct GNUNET_TIME_Relative | ||
221 | GNUNET_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 | */ | ||
241 | struct GNUNET_TIME_RelativeNBO | ||
242 | GNUNET_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 | */ | ||
252 | struct GNUNET_TIME_Relative | ||
253 | GNUNET_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 | */ | ||
264 | struct GNUNET_TIME_AbsoluteNBO | ||
265 | GNUNET_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 | */ | ||
275 | struct GNUNET_TIME_Absolute | ||
276 | GNUNET_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 */ | ||