aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-05-29 00:46:26 +0000
committerChristian Grothoff <christian@grothoff.org>2009-05-29 00:46:26 +0000
commit0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9 (patch)
tree6b552f40eb089db96409a312a98d9b12bd669102 /src
downloadgnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.tar.gz
gnunet-0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9.zip
ng
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am17
-rw-r--r--src/arm/Makefile.am58
-rw-r--r--src/arm/arm.h32
-rw-r--r--src/arm/arm_api.c337
-rw-r--r--src/arm/gnunet-arm.c180
-rw-r--r--src/arm/gnunet-service-arm.c712
-rw-r--r--src/arm/test_arm_api.c140
-rw-r--r--src/arm/test_arm_api_data.conf7
-rwxr-xr-xsrc/arm/test_gnunet_arm.sh117
-rw-r--r--src/core/Makefile.am68
-rw-r--r--src/core/core.h308
-rw-r--r--src/core/core_api.c1071
-rw-r--r--src/core/gnunet-service-core.c2859
-rw-r--r--src/core/test_core_api.c368
-rw-r--r--src/core/test_core_api_data.conf27
-rw-r--r--src/core/test_core_api_peer1.conf40
-rw-r--r--src/core/test_core_api_peer2.conf39
-rw-r--r--src/core/test_core_api_start_only.c257
-rw-r--r--src/datastore/plugin_datastore.h241
-rw-r--r--src/fragmentation/Makefile.am28
-rw-r--r--src/fragmentation/fragmentation.c702
-rw-r--r--src/fragmentation/test_fragmentation.c439
-rw-r--r--src/hello/Makefile.am28
-rw-r--r--src/hello/hello.c482
-rw-r--r--src/hello/test_hello.c185
-rw-r--r--src/include/Makefile.am42
-rw-r--r--src/include/gettext.h71
-rw-r--r--src/include/gnunet_arm_service.h107
-rw-r--r--src/include/gnunet_client_lib.h160
-rw-r--r--src/include/gnunet_common.h469
-rw-r--r--src/include/gnunet_configuration_lib.h238
-rw-r--r--src/include/gnunet_container_lib.h784
-rw-r--r--src/include/gnunet_core_service.h323
-rw-r--r--src/include/gnunet_crypto_lib.h567
-rw-r--r--src/include/gnunet_datastore_service.h187
-rw-r--r--src/include/gnunet_directories.h.in34
-rw-r--r--src/include/gnunet_disk_lib.h279
-rw-r--r--src/include/gnunet_fragmentation_lib.h113
-rw-r--r--src/include/gnunet_getopt_lib.h251
-rw-r--r--src/include/gnunet_hello_lib.h201
-rw-r--r--src/include/gnunet_network_lib.h308
-rw-r--r--src/include/gnunet_os_lib.h158
-rw-r--r--src/include/gnunet_peerinfo_service.h111
-rw-r--r--src/include/gnunet_plugin_lib.h84
-rw-r--r--src/include/gnunet_program_lib.h90
-rw-r--r--src/include/gnunet_protocols.h319
-rw-r--r--src/include/gnunet_pseudonym_lib.h125
-rw-r--r--src/include/gnunet_resolver_service.h135
-rw-r--r--src/include/gnunet_scheduler_lib.h442
-rw-r--r--src/include/gnunet_server_lib.h498
-rw-r--r--src/include/gnunet_service_lib.h140
-rw-r--r--src/include/gnunet_signal_lib.h73
-rw-r--r--src/include/gnunet_signatures.h77
-rw-r--r--src/include/gnunet_statistics_service.h157
-rw-r--r--src/include/gnunet_strings_lib.h139
-rw-r--r--src/include/gnunet_time_lib.h246
-rw-r--r--src/include/gnunet_transport_service.h241
-rw-r--r--src/include/gnunet_upnp_service.h75
-rw-r--r--src/include/gnunet_util_lib.h64
-rw-r--r--src/include/platform.h221
-rw-r--r--src/include/plibc.h582
-rw-r--r--src/include/winproc.h216
-rw-r--r--src/peerinfo/Makefile.am56
-rw-r--r--src/peerinfo/gnunet-peerinfo.c152
-rw-r--r--src/peerinfo/gnunet-service-peerinfo.c708
-rw-r--r--src/peerinfo/peerinfo.h135
-rw-r--r--src/peerinfo/peerinfo_api.c302
-rw-r--r--src/peerinfo/test_peerinfo_api.c175
-rw-r--r--src/peerinfo/test_peerinfo_api_data.conf5
-rw-r--r--src/resolver/Makefile.am46
-rw-r--r--src/resolver/gnunet-service-resolver.c483
-rw-r--r--src/resolver/resolver.h63
-rw-r--r--src/resolver/resolver_api.c468
-rw-r--r--src/resolver/test_resolver_api.c229
-rw-r--r--src/resolver/test_resolver_api_data.conf5
-rw-r--r--src/statistics/Makefile.am59
-rw-r--r--src/statistics/gnunet-service-statistics.c470
-rw-r--r--src/statistics/gnunet-statistics.c179
-rw-r--r--src/statistics/statistics.h94
-rw-r--r--src/statistics/statistics_api.c688
-rwxr-xr-xsrc/statistics/test_gnunet_statistics.sh177
-rw-r--r--src/statistics/test_statistics_api.c177
-rw-r--r--src/statistics/test_statistics_api_data.conf5
-rw-r--r--src/template/Makefile.am37
-rw-r--r--src/template/gnunet-service-template.c87
-rw-r--r--src/template/gnunet-template.c81
-rw-r--r--src/template/test_template_api.c45
-rw-r--r--src/transport/Makefile.am84
-rw-r--r--src/transport/NOTES46
-rw-r--r--src/transport/gnunet-service-transport.c2852
-rw-r--r--src/transport/gnunet-transport.c42
-rw-r--r--src/transport/plugin_transport.h468
-rw-r--r--src/transport/plugin_transport_http.c2085
-rw-r--r--src/transport/plugin_transport_smtp.c906
-rw-r--r--src/transport/plugin_transport_tcp.c1782
-rw-r--r--src/transport/plugin_transport_template.c335
-rw-r--r--src/transport/plugin_transport_udp.c592
-rw-r--r--src/transport/test_transport_api.c305
-rw-r--r--src/transport/test_transport_api_data.conf24
-rw-r--r--src/transport/test_transport_api_peer1.conf25
-rw-r--r--src/transport/test_transport_api_peer2.conf25
-rw-r--r--src/transport/transport.h238
-rw-r--r--src/transport/transport_api.c1863
-rw-r--r--src/upnp/Makefile.am59
-rw-r--r--src/upnp/draft-cheshire-nat-pmp.txt1160
-rw-r--r--src/upnp/test_upnp.c110
-rw-r--r--src/upnp/upnp.c721
-rw-r--r--src/upnp/upnp.h82
-rw-r--r--src/upnp/upnp_init.c208
-rw-r--r--src/upnp/upnp_ip.c47
-rw-r--r--src/upnp/upnp_ip.h40
-rw-r--r--src/upnp/upnp_util.c152
-rw-r--r--src/upnp/upnp_util.h68
-rw-r--r--src/upnp/upnp_xmlnode.c487
-rw-r--r--src/upnp/upnp_xmlnode.h92
-rw-r--r--src/util/Makefile.am308
-rw-r--r--src/util/client.c526
-rw-r--r--src/util/common_allocation.c206
-rw-r--r--src/util/common_endian.c52
-rw-r--r--src/util/common_gettext.c42
-rw-r--r--src/util/common_logging.c401
-rw-r--r--src/util/configuration.c848
-rw-r--r--src/util/container_bloomfilter.c677
-rw-r--r--src/util/container_heap.c533
-rw-r--r--src/util/container_meta_data.c721
-rw-r--r--src/util/container_multihashmap.c334
-rw-r--r--src/util/crypto_aes.c148
-rw-r--r--src/util/crypto_crc.c106
-rw-r--r--src/util/crypto_hash.c791
-rw-r--r--src/util/crypto_ksk.c791
-rw-r--r--src/util/crypto_random.c136
-rw-r--r--src/util/crypto_rsa.c948
-rw-r--r--src/util/disk.c954
-rw-r--r--src/util/getopt.c1077
-rw-r--r--src/util/getopt_helpers.c200
-rw-r--r--src/util/network.c1239
-rw-r--r--src/util/os_installation.c443
-rw-r--r--src/util/os_load.c653
-rw-r--r--src/util/os_network.c286
-rw-r--r--src/util/os_priority.c186
-rw-r--r--src/util/perf_crypto_hash.c64
-rw-r--r--src/util/plugin.c243
-rw-r--r--src/util/program.c202
-rw-r--r--src/util/pseudonym.c545
-rw-r--r--src/util/scheduler.c886
-rw-r--r--src/util/server.c1091
-rw-r--r--src/util/server_tc.c198
-rw-r--r--src/util/service.c1451
-rw-r--r--src/util/signal.c76
-rw-r--r--src/util/strings.c396
-rw-r--r--src/util/test_client.c195
-rw-r--r--src/util/test_common_allocation.c112
-rw-r--r--src/util/test_common_endian.c42
-rw-r--r--src/util/test_common_logging.c100
-rw-r--r--src/util/test_configuration.c229
-rw-r--r--src/util/test_configuration_data.conf30
-rw-r--r--src/util/test_container_bloomfilter.c246
-rw-r--r--src/util/test_container_heap.c112
-rw-r--r--src/util/test_container_meta_data.c262
-rw-r--r--src/util/test_container_meta_data_image.jpgbin0 -> 136107 bytes
-rw-r--r--src/util/test_container_multihashmap.c113
-rw-r--r--src/util/test_crypto_aes.c180
-rw-r--r--src/util/test_crypto_aes_weak.c203
-rw-r--r--src/util/test_crypto_crc.c221
-rw-r--r--src/util/test_crypto_hash.c165
-rw-r--r--src/util/test_crypto_ksk.c220
-rw-r--r--src/util/test_crypto_random.c71
-rw-r--r--src/util/test_crypto_rsa.c335
-rw-r--r--src/util/test_disk.c274
-rw-r--r--src/util/test_getopt.c239
-rw-r--r--src/util/test_network.c209
-rw-r--r--src/util/test_network_addressing.c193
-rw-r--r--src/util/test_network_receive_cancel.c164
-rw-r--r--src/util/test_network_timeout.c144
-rw-r--r--src/util/test_network_timeout_no_connect.c95
-rw-r--r--src/util/test_network_transmit_cancel.c97
-rw-r--r--src/util/test_os_load.c182
-rw-r--r--src/util/test_os_network.c73
-rw-r--r--src/util/test_os_priority.c61
-rw-r--r--src/util/test_plugin.c64
-rw-r--r--src/util/test_plugin_plug.c42
-rw-r--r--src/util/test_program.c94
-rw-r--r--src/util/test_program_data.conf0
-rw-r--r--src/util/test_pseudonym.c138
-rw-r--r--src/util/test_pseudonym_data.conf8
-rw-r--r--src/util/test_scheduler.c263
-rw-r--r--src/util/test_scheduler_delay.c107
-rw-r--r--src/util/test_server.c287
-rw-r--r--src/util/test_server_disconnect.c241
-rw-r--r--src/util/test_server_with_client.c215
-rw-r--r--src/util/test_service.c337
-rw-r--r--src/util/test_service_data.conf26
-rw-r--r--src/util/test_strings.c111
-rw-r--r--src/util/test_time.c122
-rw-r--r--src/util/time.c286
195 files changed, 61559 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 000000000..c8852ad04
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,17 @@
1#if WANT_FRAMEWORK
2# INTLEMU_SUBDIRS = intlemu
3#endif
4
5SUBDIRS = \
6 include $(INTLEMU_SUBDIRS) \
7 util \
8 arm \
9 fragmentation \
10 hello \
11 peerinfo \
12 resolver \
13 statistics \
14 template \
15 transport \
16 core
17
diff --git a/src/arm/Makefile.am b/src/arm/Makefile.am
new file mode 100644
index 000000000..65fa145ee
--- /dev/null
+++ b/src/arm/Makefile.am
@@ -0,0 +1,58 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11lib_LTLIBRARIES = libgnunetarm.la
12
13libgnunetarm_la_SOURCES = \
14 arm_api.c arm.h
15libgnunetarm_la_LIBADD = \
16 $(top_builddir)/src/util/libgnunetutil.la \
17 $(GN_LIBINTL)
18libgnunetarm_la_LDFLAGS = \
19 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
20 -version-info 0:0:0
21
22
23bin_PROGRAMS = \
24 gnunet-arm \
25 gnunet-service-arm
26
27gnunet_arm_SOURCES = \
28 gnunet-arm.c
29gnunet_arm_LDADD = \
30 $(top_builddir)/src/arm/libgnunetarm.la \
31 $(top_builddir)/src/util/libgnunetutil.la \
32 $(GN_LIBINTL)
33
34gnunet_service_arm_SOURCES = \
35 gnunet-service-arm.c
36gnunet_service_arm_LDADD = \
37 $(top_builddir)/src/util/libgnunetutil.la \
38 $(GN_LIBINTL)
39
40
41check_PROGRAMS = \
42 test_arm_api
43
44check_SCRIPTS = \
45 test_gnunet_arm.sh
46
47TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
48
49test_arm_api_SOURCES = \
50 test_arm_api.c
51test_arm_api_LDADD = \
52 $(top_builddir)/src/arm/libgnunetarm.la \
53 $(top_builddir)/src/resolver/libgnunetresolver.la \
54 $(top_builddir)/src/util/libgnunetutil.la
55
56
57EXTRA_DIST = \
58 test_arm_api_data.conf
diff --git a/src/arm/arm.h b/src/arm/arm.h
new file mode 100644
index 000000000..2293ec036
--- /dev/null
+++ b/src/arm/arm.h
@@ -0,0 +1,32 @@
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 * @author Christian Grothoff
23 * @file arm/arm.h
24 */
25#ifndef ARM_H
26#define ARM_H
27
28#include "gnunet_common.h"
29
30#define DEBUG_ARM GNUNET_NO
31
32#endif
diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c
new file mode 100644
index 000000000..c5bb15fd3
--- /dev/null
+++ b/src/arm/arm_api.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/**
22 * @file arm/arm_api.c
23 * @brief API for accessing the ARM service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_client_lib.h"
29#include "gnunet_getopt_lib.h"
30#include "gnunet_os_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_server_lib.h"
33#include "arm.h"
34
35
36struct ArmContext
37{
38 GNUNET_ARM_Callback callback;
39 void *cls;
40 char *service_name;
41 struct GNUNET_CLIENT_Connection *client;
42 struct GNUNET_CONFIGURATION_Handle *cfg;
43 struct GNUNET_TIME_Absolute timeout;
44 uint16_t type;
45};
46
47
48static void
49arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
50{
51 struct ArmContext *pos = cls;
52 pid_t pid;
53 char *binary;
54 char *config;
55
56 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
57 {
58 if (pos->callback != NULL)
59 pos->callback (pos->cls, GNUNET_YES);
60 GNUNET_free (pos);
61 return;
62 }
63 binary = NULL;
64 config = NULL;
65 /* start service */
66 if ((GNUNET_OK !=
67 GNUNET_CONFIGURATION_get_value_filename (pos->cfg,
68 "arm",
69 "BINARY",
70 &binary)) ||
71 (GNUNET_OK !=
72 GNUNET_CONFIGURATION_get_value_filename (pos->cfg,
73 "arm", "CONFIG", &config)))
74 {
75 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
76 _("Configuration file or binary for ARM not known!\n"));
77 if (pos->callback != NULL)
78 pos->callback (pos->cls, GNUNET_SYSERR);
79 GNUNET_free_non_null (binary);
80 GNUNET_free (pos);
81 return;
82 }
83 pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config,
84#if DEBUG_ARM
85 "-L", "DEBUG",
86#endif
87 NULL);
88 GNUNET_free (binary);
89 GNUNET_free (config);
90 if (pid == -1)
91 {
92 if (pos->callback != NULL)
93 pos->callback (pos->cls, GNUNET_SYSERR);
94 GNUNET_free (pos);
95 return;
96 }
97 /* FIXME: consider checking again to see if it worked!? */
98 if (pos->callback != NULL)
99 pos->callback (pos->cls, GNUNET_YES);
100 GNUNET_free (pos);
101}
102
103
104static void
105handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
106{
107 struct ArmContext *sc = cls;
108 int ret;
109
110 if (msg == NULL)
111 {
112 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
113 _("Error receiving response from ARM service\n"));
114 GNUNET_CLIENT_disconnect (sc->client);
115 if (sc->callback != NULL)
116 sc->callback (sc->cls, GNUNET_SYSERR);
117 GNUNET_free (sc);
118 return;
119 }
120#if DEBUG_ARM
121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
122 _("Received response from ARM service\n"));
123#endif
124 switch (ntohs (msg->type))
125 {
126 case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
127 ret = GNUNET_YES;
128 break;
129 case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
130 ret = GNUNET_NO;
131 break;
132 default:
133 GNUNET_break (0);
134 ret = GNUNET_SYSERR;
135 }
136 GNUNET_CLIENT_disconnect (sc->client);
137 if (sc->callback != NULL)
138 sc->callback (sc->cls, ret);
139 GNUNET_free (sc);
140}
141
142
143static size_t
144send_service_msg (void *cls, size_t size, void *buf)
145{
146 struct ArmContext *sctx = cls;
147 struct GNUNET_MessageHeader *msg;
148 size_t slen;
149
150 if (buf == NULL)
151 {
152 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
153 _("Error while trying to transmit to ARM service\n"));
154 GNUNET_CLIENT_disconnect (sctx->client);
155 if (sctx->callback != NULL)
156 sctx->callback (sctx->cls, GNUNET_SYSERR);
157 GNUNET_free (sctx->service_name);
158 GNUNET_free (sctx);
159 return 0;
160 }
161#if DEBUG_ARM
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163 _("Transmitting service request to ARM.\n"));
164#endif
165 slen = strlen (sctx->service_name) + 1;
166 GNUNET_assert (size >= slen);
167 msg = buf;
168 msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
169 msg->type = htons (sctx->type);
170 memcpy (&msg[1], sctx->service_name, slen);
171 GNUNET_free (sctx->service_name);
172 sctx->service_name = NULL;
173 GNUNET_CLIENT_receive (sctx->client,
174 &handle_response,
175 sctx,
176 GNUNET_TIME_absolute_get_remaining (sctx->timeout));
177 return slen + sizeof (struct GNUNET_MessageHeader);
178}
179
180
181/**
182 * Start or stop a service.
183 *
184 * @param service_name name of the service
185 * @param cfg configuration to use (needed to contact ARM;
186 * the ARM service may internally use a different
187 * configuration to determine how to start the service).
188 * @param sched scheduler to use
189 * @param timeout how long to wait before failing for good
190 * @param cb callback to invoke when service is ready
191 * @param cb_cls closure for callback
192 */
193static void
194change_service (const char *service_name,
195 struct GNUNET_CONFIGURATION_Handle *cfg,
196 struct GNUNET_SCHEDULER_Handle *sched,
197 struct GNUNET_TIME_Relative timeout,
198 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
199{
200 struct GNUNET_CLIENT_Connection *client;
201 struct ArmContext *sctx;
202 size_t slen;
203
204 slen = strlen (service_name) + 1;
205 if (slen + sizeof (struct GNUNET_MessageHeader) >
206 GNUNET_SERVER_MAX_MESSAGE_SIZE)
207 {
208 GNUNET_break (0);
209 if (cb != NULL)
210 cb (cb_cls, GNUNET_NO);
211 return;
212 }
213 client = GNUNET_CLIENT_connect (sched, "arm", cfg);
214 if (client == NULL)
215 {
216 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
217 _("Failed to connect to ARM service\n"));
218 if (cb != NULL)
219 cb (cb_cls, GNUNET_SYSERR);
220 return;
221 }
222#if DEBUG_ARM
223 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
224 _("ARM requests starting of service `%s'.\n"), service_name);
225#endif
226 sctx = GNUNET_malloc (sizeof (struct ArmContext));
227 sctx->callback = cb;
228 sctx->cls = cb_cls;
229 sctx->client = client;
230 sctx->service_name = GNUNET_strdup (service_name);
231 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
232 sctx->type = type;
233 if (NULL ==
234 GNUNET_CLIENT_notify_transmit_ready (client,
235 slen +
236 sizeof (struct
237 GNUNET_MessageHeader),
238 timeout, &send_service_msg, sctx))
239 {
240 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
241 _("Failed to transmit request to ARM service\n"));
242 GNUNET_free (sctx->service_name);
243 GNUNET_free (sctx);
244 if (cb != NULL)
245 cb (cb_cls, GNUNET_SYSERR);
246 GNUNET_CLIENT_disconnect (client);
247 return;
248 }
249}
250
251
252/**
253 * Start a service.
254 *
255 * @param service_name name of the service
256 * @param cfg configuration to use (needed to contact ARM;
257 * the ARM service may internally use a different
258 * configuration to determine how to start the service).
259 * @param sched scheduler to use
260 * @param timeout how long to wait before failing for good
261 * @param cb callback to invoke when service is ready
262 * @param cb_cls closure for callback
263 */
264void
265GNUNET_ARM_start_service (const char *service_name,
266 struct GNUNET_CONFIGURATION_Handle *cfg,
267 struct GNUNET_SCHEDULER_Handle *sched,
268 struct GNUNET_TIME_Relative timeout,
269 GNUNET_ARM_Callback cb, void *cb_cls)
270{
271 struct ArmContext *sctx;
272
273 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
274 _("Starting service `%s'\n"), service_name);
275 if (0 == strcmp ("arm", service_name))
276 {
277 sctx = GNUNET_malloc (sizeof (struct ArmContext));
278 sctx->callback = cb;
279 sctx->cls = cb_cls;
280 sctx->cfg = cfg;
281 GNUNET_CLIENT_service_test (sched,
282 "arm",
283 cfg, timeout, &arm_service_report, sctx);
284 return;
285 }
286 change_service (service_name,
287 cfg,
288 sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
289}
290
291
292
293
294/**
295 * Stop a service.
296 *
297 * @param service_name name of the service
298 * @param cfg configuration to use (needed to contact ARM;
299 * the ARM service may internally use a different
300 * configuration to determine how to start the service).
301 * @param sched scheduler to use
302 * @param timeout how long to wait before failing for good
303 * @param cb callback to invoke when service is ready
304 * @param cb_cls closure for callback
305 */
306void
307GNUNET_ARM_stop_service (const char *service_name,
308 struct GNUNET_CONFIGURATION_Handle *cfg,
309 struct GNUNET_SCHEDULER_Handle *sched,
310 struct GNUNET_TIME_Relative timeout,
311 GNUNET_ARM_Callback cb, void *cb_cls)
312{
313 struct GNUNET_CLIENT_Connection *client;
314
315 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
316 _("Stopping service `%s'\n"), service_name);
317 if (0 == strcmp ("arm", service_name))
318 {
319 client = GNUNET_CLIENT_connect (sched, "arm", cfg);
320 if (client == NULL)
321 {
322 if (cb != NULL)
323 cb (cb_cls, GNUNET_SYSERR);
324 return;
325 }
326 GNUNET_CLIENT_service_shutdown (client);
327 GNUNET_CLIENT_disconnect (client);
328 if (cb != NULL)
329 cb (cb_cls, GNUNET_NO);
330 return;
331 }
332 change_service (service_name,
333 cfg,
334 sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
335}
336
337/* end of arm_api.c */
diff --git a/src/arm/gnunet-arm.c b/src/arm/gnunet-arm.c
new file mode 100644
index 000000000..f8f5bc20f
--- /dev/null
+++ b/src/arm/gnunet-arm.c
@@ -0,0 +1,180 @@
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 arm/gnunet-arm.c
23 * @brief arm for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_client_lib.h"
29#include "gnunet_getopt_lib.h"
30#include "gnunet_program_lib.h"
31#include "gnunet_time_lib.h"
32
33/**
34 * Timeout for all operations.
35 */
36#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
37
38/**
39 * Set if we are to shutdown all services (including ARM).
40 */
41static int end;
42
43/**
44 * Set if we are to start default services (including ARM).
45 */
46static int start;
47
48/**
49 * Set to the name of a service to start.
50 */
51static char *init;
52
53/**
54 * Set to the name of a service to kill.
55 */
56static char *term;
57
58/**
59 * Set to the name of a service to test.
60 */
61static char *test;
62
63/**
64 * Final status code.
65 */
66static int ret;
67
68
69static void
70confirm_cb (void *cls, int success)
71{
72 const char *service = cls;
73 switch (success)
74 {
75 case GNUNET_OK:
76 fprintf (stdout, _("Service `%s' is now running.\n"), service);
77 break;
78 case GNUNET_NO:
79 fprintf (stdout, _("Service `%s' is not running.\n"), service);
80 break;
81 case GNUNET_SYSERR:
82 fprintf (stdout,
83 _("Error updating service `%s': ARM not running\n"), service);
84 break;
85 }
86}
87
88
89static void
90confirm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
91{
92 const char *service = cls;
93
94 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
95 fprintf (stdout, _("Service `%s' is running.\n"), service);
96 else
97 fprintf (stdout, _("Service `%s' is not running.\n"), service);
98}
99
100
101/**
102 * Main function that will be run by the scheduler.
103 *
104 * @param cls closure
105 * @param sched the scheduler to use
106 * @param args remaining command-line arguments
107 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
108 * @param cfg configuration
109 */
110static void
111run (void *cls,
112 struct GNUNET_SCHEDULER_Handle *sched,
113 char *const *args,
114 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
115{
116 if (term != NULL)
117 {
118 GNUNET_ARM_stop_service (term, cfg, sched, TIMEOUT, &confirm_cb, term);
119 }
120 if (end)
121 {
122 GNUNET_ARM_stop_service ("arm",
123 cfg, sched, TIMEOUT, &confirm_cb, "arm");
124 }
125 if (start)
126 {
127 GNUNET_ARM_start_service ("arm",
128 cfg, sched, TIMEOUT, &confirm_cb, "arm");
129 }
130 if (init != NULL)
131 {
132 GNUNET_ARM_start_service (init, cfg, sched, TIMEOUT, &confirm_cb, init);
133 }
134 if (test != NULL)
135 {
136 GNUNET_CLIENT_service_test (sched,
137 test, cfg, TIMEOUT, &confirm_task, test);
138 }
139}
140
141
142/**
143 * gnunet-arm command line options
144 */
145static struct GNUNET_GETOPT_CommandLineOption options[] = {
146 {'e', "end", NULL, gettext_noop ("stop all GNUnet services"),
147 GNUNET_NO, &GNUNET_GETOPT_set_one, &end},
148 {'i', "init", "SERVICE", gettext_noop ("start a particular service"),
149 GNUNET_YES, &GNUNET_GETOPT_set_string, &init},
150 {'k', "kill", "SERVICE", gettext_noop ("stop a particular service"),
151 GNUNET_YES, &GNUNET_GETOPT_set_string, &term},
152 {'s', "start", NULL, gettext_noop ("start all GNUnet default services"),
153 GNUNET_NO, &GNUNET_GETOPT_set_one, &start},
154 {'t', "test", "SERVICE",
155 gettext_noop ("test if a particular service is running"),
156 GNUNET_YES, &GNUNET_GETOPT_set_string, &test},
157 GNUNET_GETOPT_OPTION_END
158};
159
160
161/**
162 * The main function to obtain arm from gnunetd.
163 *
164 * @param argc number of arguments from the command line
165 * @param argv command line arguments
166 * @return 0 ok, 1 on error
167 */
168int
169main (int argc, char *const *argv)
170{
171 return (GNUNET_OK ==
172 GNUNET_PROGRAM_run (argc,
173 argv,
174 "gnunet-arm",
175 gettext_noop
176 ("Control services and the Automated Restart Manager (ARM)"),
177 options, &run, NULL)) ? ret : 1;
178}
179
180/* end of gnunet-arm.c */
diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c
new file mode 100644
index 000000000..97d507890
--- /dev/null
+++ b/src/arm/gnunet-service-arm.c
@@ -0,0 +1,712 @@
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 arm/gnunet-service-arm.c
23 * @brief the automated restart manager service
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - multiple start-stop requests with RC>1 can result
28 * in UP/DOWN signals based on "pending" that are inaccurate...
29 * => have list of clients waiting for a resolution instead of
30 * giving instant (but incorrect) replies
31 * - code could go into restart-loop for a service
32 * if service crashes instantly -- need exponential back-off
33 * - need to test auto-restart code on configuration changes;
34 * - should refine restart code to check if *relevant* parts of the
35 * configuration were changed (anything in the section for the service)
36 * - should have a way to specify dependencies between services and
37 * manage restarts of groups of services
38 */
39#include "platform.h"
40#include "gnunet_client_lib.h"
41#include "gnunet_getopt_lib.h"
42#include "gnunet_os_lib.h"
43#include "gnunet_protocols.h"
44#include "gnunet_service_lib.h"
45#include "arm.h"
46
47
48/**
49 * Run maintenance every second.
50 */
51#define MAINT_FREQUENCY GNUNET_TIME_UNIT_SECONDS
52
53/**
54 * How long do we wait until we decide that a service
55 * did not start?
56 */
57#define CHECK_TIMEOUT GNUNET_TIME_UNIT_MINUTES
58
59struct ServiceList;
60
61typedef void (*CleanCallback) (void *cls, struct ServiceList * pos);
62
63/**
64 * List of our services.
65 */
66struct ServiceList
67{
68 /**
69 * This is a linked list.
70 */
71 struct ServiceList *next;
72
73 /**
74 * Name of the service.
75 */
76 char *name;
77
78 /**
79 * Name of the binary used.
80 */
81 char *binary;
82
83 /**
84 * Name of the configuration file used.
85 */
86 char *config;
87
88 /**
89 * Function to call upon kill completion (waitpid), NULL
90 * if we should simply restart the process.
91 */
92 CleanCallback kill_continuation;
93
94 /**
95 * Closure for kill_continuation.
96 */
97 void *kill_continuation_cls;
98
99 /**
100 * Process ID of the child.
101 */
102 pid_t pid;
103
104 /**
105 * Last time the config of this service was
106 * modified.
107 */
108 time_t mtime;
109
110 /**
111 * Reference counter (counts how many times we've been
112 * asked to start the service). We only actually stop
113 * it once rc hits zero.
114 */
115 unsigned int rc;
116
117};
118
119/**
120 * List of running services.
121 */
122static struct ServiceList *running;
123
124/**
125 * Our configuration
126 */
127static struct GNUNET_CONFIGURATION_Handle *cfg;
128
129/**
130 * Our scheduler.
131 */
132static struct GNUNET_SCHEDULER_Handle *sched;
133
134/**
135 * Command to prepend to each actual command.
136 */
137static char *prefix_command;
138
139
140static size_t
141write_result (void *cls, size_t size, void *buf)
142{
143 uint16_t *res = cls;
144 struct GNUNET_MessageHeader *msg;
145
146 if (buf == NULL)
147 return 0; /* error, not much we can do */
148 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
149 msg = buf;
150 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
151 msg->type = htons (*res);
152 GNUNET_free (res);
153 return sizeof (struct GNUNET_MessageHeader);
154}
155
156
157
158/**
159 * Signal our client that we will start or stop the
160 * service.
161 *
162 * @return NULL if it was not found
163 */
164static void
165signal_result (struct GNUNET_SERVER_Client *client,
166 const char *name, uint16_t result)
167{
168 uint16_t *res;
169
170#if DEBUG_ARM
171 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
172 "Telling client that service `%s' is now %s\n",
173 name,
174 result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up");
175#endif
176 res = GNUNET_malloc (sizeof (uint16_t));
177 *res = result;
178 GNUNET_SERVER_notify_transmit_ready (client,
179 sizeof (struct GNUNET_MessageHeader),
180 GNUNET_TIME_UNIT_FOREVER_REL,
181 &write_result, res);
182}
183
184
185/**
186 * Find the process with the given PID in the
187 * given list.
188 *
189 * @return NULL if it was not found
190 */
191static struct ServiceList *
192find_pid (pid_t pid)
193{
194 struct ServiceList *pos;
195
196 pos = running;
197 while (pos != NULL)
198 {
199 if (pos->pid == pid)
200 return pos;
201 pos = pos->next;
202 }
203 return NULL;
204}
205
206
207/**
208 * Find the process with the given service
209 * name in the given list, remove it and return it.
210 *
211 * @return NULL if it was not found
212 */
213static struct ServiceList *
214find_name (const char *name)
215{
216 struct ServiceList *pos;
217 struct ServiceList *prev;
218
219 pos = running;
220 prev = NULL;
221 while (pos != NULL)
222 {
223 if (0 == strcmp (pos->name, name))
224 {
225 if (prev == NULL)
226 running = pos->next;
227 else
228 prev->next = pos->next;
229 pos->next = NULL;
230 return pos;
231 }
232 prev = pos;
233 pos = pos->next;
234 }
235 return NULL;
236}
237
238
239static void
240free_entry (struct ServiceList *pos)
241{
242 GNUNET_free_non_null (pos->config);
243 GNUNET_free_non_null (pos->binary);
244 GNUNET_free (pos->name);
245 GNUNET_free (pos);
246}
247
248
249
250
251/**
252 * Actually start the process for the given service.
253 *
254 * @param sl identifies service to start
255 */
256static void
257start_process (struct ServiceList *sl)
258{
259 char *loprefix;
260 char **argv;
261 unsigned int argv_size;
262 char *lopos;
263 const char *firstarg;
264 int use_debug;
265
266 /* start service */
267 if (GNUNET_OK !=
268 GNUNET_CONFIGURATION_get_value_string (cfg,
269 sl->name, "PREFIX", &loprefix))
270 loprefix = GNUNET_strdup (prefix_command);
271 use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
272
273 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name);
274#if DEBUG_ARM
275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
276 "Starting service `%s' using binary `%s' and configuration `%s'\n",
277 sl->name, sl->binary, sl->config);
278#endif
279 argv_size = 5;
280 if (use_debug)
281 argv_size += 2;
282 lopos = loprefix;
283 while ('\0' != *lopos)
284 {
285 if (*lopos == ' ')
286 argv_size++;
287 lopos++;
288 }
289 firstarg = NULL;
290 argv = GNUNET_malloc (argv_size * sizeof (char *));
291 argv_size = 0;
292 lopos = loprefix;
293 while ('\0' != *lopos)
294 {
295 while (*lopos == ' ')
296 lopos++;
297 if (*lopos == '\0')
298 continue;
299 if (argv_size == 0)
300 firstarg = lopos;
301 argv[argv_size++] = lopos;
302 while (('\0' != *lopos) && (' ' != *lopos))
303 lopos++;
304 if ('\0' == *lopos)
305 continue;
306 *lopos = '\0';
307 lopos++;
308 }
309 if (argv_size == 0)
310 firstarg = sl->binary;
311 argv[argv_size++] = sl->binary;
312 argv[argv_size++] = "-c";
313 argv[argv_size++] = sl->config;
314 if (GNUNET_YES == use_debug)
315 {
316 argv[argv_size++] = "-L";
317 argv[argv_size++] = "DEBUG";
318 }
319 argv[argv_size++] = NULL;
320 sl->pid = GNUNET_OS_start_process_v (firstarg, argv);
321 GNUNET_free (argv);
322 GNUNET_free (loprefix);
323}
324
325
326/**
327 * Start the specified service.
328 */
329static void
330start_service (struct GNUNET_SERVER_Handle *server,
331 struct GNUNET_SERVER_Client *client, const char *servicename)
332{
333 struct ServiceList *sl;
334 char *binary;
335 char *config;
336 struct stat sbuf;
337 sl = find_name (servicename);
338 if (sl != NULL)
339 {
340 /* already running, just increment RC */
341 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
342 _("Service `%s' already running.\n"), servicename);
343 sl->rc++;
344 sl->next = running;
345 running = sl;
346 signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
347 return;
348 }
349 if (GNUNET_OK !=
350 GNUNET_CONFIGURATION_get_value_string (cfg,
351 servicename, "BINARY", &binary))
352 {
353 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
354 _("Binary implementing service `%s' not known!\n"),
355 servicename);
356 signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
357 return;
358 }
359 if ((GNUNET_OK !=
360 GNUNET_CONFIGURATION_get_value_filename (cfg,
361 servicename,
362 "CONFIG",
363 &config)) ||
364 (0 != STAT (config, &sbuf)))
365 {
366 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
367 _("Configuration file `%s' for service `%s' not known!\n"),
368 config, servicename);
369 signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
370 GNUNET_free (binary);
371 return;
372 }
373 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
374 _("Preparing to start `%s'\n"), servicename);
375 sl = GNUNET_malloc (sizeof (struct ServiceList));
376 sl->name = GNUNET_strdup (servicename);
377 sl->next = running;
378 sl->rc = 1;
379 sl->binary = binary;
380 sl->config = config;
381 sl->mtime = sbuf.st_mtime;
382 running = sl;
383 start_process (sl);
384 signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
385}
386
387
388static void
389free_and_signal (void *cls, struct ServiceList *pos)
390{
391 struct GNUNET_SERVER_Client *client = cls;
392 /* find_name will remove "pos" from the list! */
393 GNUNET_assert (pos == find_name (pos->name));
394 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Service `%s' stopped\n", pos->name);
395 signal_result (client, pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
396 GNUNET_SERVER_receive_done (client, GNUNET_OK);
397 GNUNET_SERVER_client_drop (client);
398 free_entry (pos);
399}
400
401
402/**
403 * Stop the specified service.
404 */
405static void
406stop_service (struct GNUNET_SERVER_Handle *server,
407 struct GNUNET_SERVER_Client *client, const char *servicename)
408{
409 struct ServiceList *pos;
410 struct GNUNET_CLIENT_Connection *sc;
411
412 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
413 "Preparing to stop `%s'\n", servicename);
414 pos = find_name (servicename);
415 if (pos->kill_continuation != NULL)
416 {
417 /* killing already in progress */
418 signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
419 return;
420 }
421 if ((pos != NULL) && (pos->rc > 1))
422 {
423 /* RC>1, just decrement RC */
424 pos->rc--;
425 pos->next = running;
426 running = pos;
427 signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
428 GNUNET_SERVER_receive_done (client, GNUNET_OK);
429 return;
430 }
431 if (pos != NULL)
432 {
433 if (0 != PLIBC_KILL (pos->pid, SIGTERM))
434 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
435 pos->next = running;
436 running = pos;
437 pos->kill_continuation = &free_and_signal;
438 pos->kill_continuation_cls = client;
439 GNUNET_SERVER_client_keep (client);
440 }
441 else
442 {
443 sc = GNUNET_CLIENT_connect (sched, servicename, cfg);
444 GNUNET_CLIENT_service_shutdown (sc);
445 GNUNET_CLIENT_disconnect (sc);
446 signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
447 GNUNET_SERVER_receive_done (client, GNUNET_OK);
448 }
449}
450
451
452/**
453 * Handle START-message.
454 *
455 * @param cls closure (always NULL)
456 * @param server the server handling the message
457 * @param client identification of the client
458 * @param message the actual message
459 * @return GNUNET_OK to keep the connection open,
460 * GNUNET_SYSERR to close it (signal serious error)
461 */
462static void
463handle_start (void *cls,
464 struct GNUNET_SERVER_Handle *server,
465 struct GNUNET_SERVER_Client *client,
466 const struct GNUNET_MessageHeader *message)
467{
468 const char *servicename;
469 uint16_t size;
470
471 size = ntohs (message->size);
472 size -= sizeof (struct GNUNET_MessageHeader);
473 servicename = (const char *) &message[1];
474 if ((size == 0) || (servicename[size - 1] != '\0'))
475 {
476 GNUNET_break (0);
477 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
478 return;
479 }
480 start_service (server, client, servicename);
481 GNUNET_SERVER_receive_done (client, GNUNET_OK);
482}
483
484
485/**
486 * Handle STOP-message.
487 *
488 * @param cls closure (always NULL)
489 * @param server the server handling the message
490 * @param client identification of the client
491 * @param message the actual message
492 * @return GNUNET_OK to keep the connection open,
493 * GNUNET_SYSERR to close it (signal serious error)
494 */
495static void
496handle_stop (void *cls,
497 struct GNUNET_SERVER_Handle *server,
498 struct GNUNET_SERVER_Client *client,
499 const struct GNUNET_MessageHeader *message)
500{
501 const char *servicename;
502 uint16_t size;
503
504 size = ntohs (message->size);
505 size -= sizeof (struct GNUNET_MessageHeader);
506 servicename = (const char *) &message[1];
507 if ((size == 0) || (servicename[size - 1] != '\0'))
508 {
509 GNUNET_break (0);
510 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
511 return;
512 }
513 stop_service (server, client, servicename);
514}
515
516
517
518/**
519 * Background task doing maintenance.
520 *
521 * @param cls closure
522 * @param tc context
523 */
524static void
525maint (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
526{
527 struct ServiceList *pos;
528 pid_t pid;
529 int status;
530 const char *statstr;
531 int statcode;
532 struct stat sbuf;
533
534 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
535 {
536 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopping all services\n"));
537 while (NULL != (pos = running))
538 {
539 running = pos->next;
540 if (0 != PLIBC_KILL (pos->pid, SIGTERM))
541 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
542 if (pos->pid != waitpid (pos->pid, NULL, 0))
543 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
544 free_entry (pos);
545 }
546 return;
547 }
548 GNUNET_SCHEDULER_add_delayed (tc->sched,
549 GNUNET_YES,
550 GNUNET_SCHEDULER_PRIORITY_IDLE,
551 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
552 MAINT_FREQUENCY, &maint, cfg);
553
554 /* check for services that died (WAITPID) */
555 while (0 < (pid = waitpid (0, &status, WNOHANG)))
556 {
557 if (WIFSTOPPED (status) || WIFCONTINUED (status))
558 continue;
559 pos = find_pid (pid);
560 if (pos == NULL)
561 {
562 /* we killed the service */
563 continue;
564 }
565 if (WIFEXITED (status))
566 {
567 statstr = _( /* process termination method */ "exit");
568 statcode = WEXITSTATUS (status);
569 }
570 else if (WTERMSIG (status))
571 {
572 statstr = _( /* process termination method */ "signal");
573 statcode = WTERMSIG (status);
574 }
575 else
576 {
577 statstr = _( /* process termination method */ "unknown");
578 statcode = 0;
579 }
580 if (NULL != pos->kill_continuation)
581 {
582 pos->kill_continuation (pos->kill_continuation_cls, pos);
583 }
584 else
585 {
586 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
587 _
588 ("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
589 pos->name, statstr, statcode);
590 /* schedule restart */
591 pos->pid = 0;
592 }
593 }
594
595 /* check for services that need to be restarted due to
596 configuration changes or because the last restart failed */
597 pos = running;
598 while (pos != NULL)
599 {
600 if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime))
601 {
602 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
603 _
604 ("Restarting service `%s' due to configuration file change.\n"));
605 if (0 != PLIBC_KILL (pos->pid, SIGTERM))
606 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
607 }
608 if (pos->pid == 0)
609 {
610 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
611 _("Restarting service `%s'.\n"), pos->name);
612 /* FIXME: should have some exponentially
613 increasing timer to avoid tight restart loops */
614 start_process (pos);
615 }
616 pos = pos->next;
617 }
618}
619
620
621/**
622 * List of handlers for the messages understood by this
623 * service.
624 */
625static struct GNUNET_SERVER_MessageHandler handlers[] = {
626 {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
627 {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
628 {NULL, NULL, 0, 0}
629};
630
631
632/**
633 * Process arm requests.
634 *
635 * @param cls closure
636 * @param s scheduler to use
637 * @param server the initialized server
638 * @param c configuration to use
639 */
640static void
641run (void *cls,
642 struct GNUNET_SCHEDULER_Handle *s,
643 struct GNUNET_SERVER_Handle *server,
644 struct GNUNET_CONFIGURATION_Handle *c)
645{
646 char *defaultservices;
647 char *pos;
648
649 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting...\n");
650 cfg = c;
651 sched = s;
652 if (GNUNET_OK !=
653 GNUNET_CONFIGURATION_get_value_string (cfg,
654 "ARM",
655 "GLOBAL_PREFIX",
656 &prefix_command))
657 prefix_command = GNUNET_strdup ("");
658 /* start default services... */
659 if (GNUNET_OK ==
660 GNUNET_CONFIGURATION_get_value_string (cfg,
661 "ARM",
662 "DEFAULTSERVICES",
663 &defaultservices))
664 {
665#if DEBUG_ARM
666 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
667 "Starting default services `%s'\n", defaultservices);
668#endif
669 pos = strtok (defaultservices, " ");
670 while (pos != NULL)
671 {
672 start_service (server, NULL, pos);
673 pos = strtok (NULL, " ");
674 }
675 GNUNET_free (defaultservices);
676 }
677 else
678 {
679#if DEBUG_ARM
680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681 "No default services configured.\n");
682#endif
683 }
684
685 /* process client requests */
686 GNUNET_SERVER_add_handlers (server, handlers);
687
688 /* manage services */
689 GNUNET_SCHEDULER_add_delayed (sched,
690 GNUNET_YES,
691 GNUNET_SCHEDULER_PRIORITY_IDLE,
692 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
693 MAINT_FREQUENCY, &maint, NULL);
694}
695
696
697/**
698 * The main function for the arm service.
699 *
700 * @param argc number of arguments from the command line
701 * @param argv command line arguments
702 * @return 0 ok, 1 on error
703 */
704int
705main (int argc, char *const *argv)
706{
707 return (GNUNET_OK ==
708 GNUNET_SERVICE_run (argc,
709 argv, "arm", &run, NULL, NULL, NULL)) ? 0 : 1;
710}
711
712/* end of gnunet-service-arm.c */
diff --git a/src/arm/test_arm_api.c b/src/arm/test_arm_api.c
new file mode 100644
index 000000000..89f63d3ec
--- /dev/null
+++ b/src/arm/test_arm_api.c
@@ -0,0 +1,140 @@
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 arm/test_arm_api.c
22 * @brief testcase for arm_api.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_arm_service.h"
27#include "gnunet_client_lib.h"
28#include "gnunet_configuration_lib.h"
29#include "gnunet_getopt_lib.h"
30#include "gnunet_program_lib.h"
31#include "gnunet_resolver_service.h"
32
33#define VERBOSE GNUNET_NO
34
35#define START_ARM GNUNET_YES
36
37#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
38
39static struct GNUNET_SCHEDULER_Handle *sched;
40
41static struct GNUNET_CONFIGURATION_Handle *cfg;
42
43static int ok = 1;
44
45static void
46dns_notify (void *cls, const struct sockaddr *addr, socklen_t addrlen)
47{
48 if (addr == NULL)
49 {
50 GNUNET_assert (ok == 0);
51#if START_ARM
52 GNUNET_ARM_stop_service ("arm", cfg, sched, TIMEOUT, NULL, NULL);
53#endif
54 return;
55 }
56 GNUNET_assert (addr != NULL);
57 ok = 0;
58}
59
60
61static void
62resolver_notify (void *cls, int success)
63{
64 GNUNET_assert (success == GNUNET_YES);
65 sleep (1); /* FIXME: that we need to do this is a problem... */
66 GNUNET_RESOLVER_ip_get (sched,
67 cfg,
68 "localhost", AF_INET, TIMEOUT, &dns_notify, NULL);
69}
70
71static void
72arm_notify (void *cls, int success)
73{
74 GNUNET_assert (success == GNUNET_YES);
75#if START_ARM
76 sleep (1); /* FIXME: that we need to do this is a problem... */
77#endif
78 GNUNET_ARM_start_service ("resolver",
79 cfg, sched, TIMEOUT, &resolver_notify, NULL);
80}
81
82
83static void
84task (void *cls,
85 struct GNUNET_SCHEDULER_Handle *s,
86 char *const *args,
87 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *c)
88{
89 cfg = c;
90 sched = s;
91#if START_ARM
92 GNUNET_ARM_start_service ("arm", cfg, sched, TIMEOUT, &arm_notify, NULL);
93#else
94 arm_notify (NULL, GNUNET_YES);
95#endif
96}
97
98
99
100static int
101check ()
102{
103 char *const argv[] = {
104 "test-arm-api",
105 "-c", "test_arm_api_data.conf",
106#if VERBOSE
107 "-L", "DEBUG",
108#endif
109 NULL
110 };
111 struct GNUNET_GETOPT_CommandLineOption options[] = {
112 GNUNET_GETOPT_OPTION_END
113 };
114 GNUNET_assert (GNUNET_OK ==
115 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
116 argv,
117 "test-arm-api",
118 "nohelp", options, &task, NULL));
119 return ok;
120}
121
122int
123main (int argc, char *argv[])
124{
125 int ret;
126
127
128 GNUNET_log_setup ("test-arm-api",
129#if VERBOSE
130 "DEBUG",
131#else
132 "WARNING",
133#endif
134 NULL);
135 ret = check ();
136
137 return ret;
138}
139
140/* end of test_arm_api.c */
diff --git a/src/arm/test_arm_api_data.conf b/src/arm/test_arm_api_data.conf
new file mode 100644
index 000000000..b25924917
--- /dev/null
+++ b/src/arm/test_arm_api_data.conf
@@ -0,0 +1,7 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-arm/
3DEFAULTCONFIG = test_arm_api_data.conf
4
5[arm]
6PORT = 23354
7
diff --git a/src/arm/test_gnunet_arm.sh b/src/arm/test_gnunet_arm.sh
new file mode 100755
index 000000000..72f7a176b
--- /dev/null
+++ b/src/arm/test_gnunet_arm.sh
@@ -0,0 +1,117 @@
1#!/bin/bash
2
3rm -rf /tmp/test-gnunetd-arm/
4exe="./gnunet-arm -c test_arm_api_data.conf"
5base=/tmp/gnunet-test-arm
6#DEBUG="-L DEBUG"
7
8# ----------------------------------------------------------------------------------
9echo -n "TEST: Bad argument checking... "
10
11if $exe -x 2> /dev/null; then
12 echo "FAIL: error running $exe"
13 exit 1
14fi
15echo "PASS"
16
17# ----------------------------------------------------------------------------------
18echo -n "TEST: Start ARM... "
19
20if ! $exe $DEBUG -s > /dev/null ; then
21 echo "FAIL: error running $exe"
22 exit 1
23fi
24LINES=`ps ax | grep gnunet-service-arm | grep -v grep | wc -l`
25if test $LINES -eq 0; then
26 echo "FAIL: found $LINES gnunet-service-arm processes"
27 exit 1
28fi
29echo "PASS"
30
31# ----------------------------------------------------------------------------------
32echo -n "TEST: Start another service... "
33
34if ! $exe $DEBUG -i resolver > /dev/null ; then
35 echo "FAIL: error running $exe"
36 kill %%
37 exit 1
38fi
39sleep 1
40LINES=`ps ax | grep gnunet-service-resolver | grep -v grep | wc -l`
41if test $LINES -ne 1; then
42 echo "FAIL: unexpected output (got $LINES lines, wanted 1)"
43 $exe -e
44 exit 1
45fi
46echo "PASS"
47
48# ----------------------------------------------------------------------------------
49echo -n "TEST: Test -t on running service... "
50
51if ! $exe $DEBUG -t resolver > $base.out; then
52 echo "FAIL: error running $exe"
53 exit 1
54fi
55LINES=`cat $base.out | grep resolver | grep not | wc -l`
56if test $LINES -ne 0; then
57 echo "FAIL: unexpected output"
58 $exe -e
59 exit 1
60fi
61LINES=`cat $base.out | grep resolver | grep -v not | wc -l`
62if test $LINES -ne 1; then
63 echo "FAIL: unexpected output"
64 $exe -e
65 exit 1
66fi
67echo "PASS"
68
69# ----------------------------------------------------------------------------------
70echo -n "TEST: Stop a service... "
71
72if ! $exe $DEBUG -k resolver > /dev/null; then
73 echo "FAIL: error running $exe"
74 $exe -e
75 exit 1
76fi
77sleep 1
78LINES=`ps ax | grep gnunet-service-resolver | grep -v grep | wc -l`
79if test $LINES -ne 0; then
80 echo "FAIL: unexpected output"
81 $exe -e
82 exit 1
83fi
84echo "PASS"
85
86# ----------------------------------------------------------------------------------
87echo -n "TEST: Test -t on stopped service... "
88
89if ! $exe $DEBUG -t resolver > $base.out; then
90 echo "FAIL: error running $exe"
91 $exe -e
92 exit 1
93fi
94LINES=`cat $base.out | grep resolver | grep not | wc -l`
95if test $LINES -ne 1; then
96 echo "FAIL: unexpected output"
97 $exe -e
98 exit 1
99fi
100echo "PASS"
101
102# ----------------------------------------------------------------------------------
103echo -n "TEST: Stop ARM... "
104
105if ! $exe $DEBUG -e > /dev/null; then
106 echo "FAIL: error running $exe"
107 exit 1
108fi
109LINES=`ps ax | grep gnunet-service-arm | grep -v grep | wc -l`
110if test $LINES -ne 0; then
111 echo "FAIL: unexpected output"
112 exit 1
113fi
114echo "PASS"
115
116rm -rf /tmp/test-gnunetd-arm/
117rm -f $base.out
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
new file mode 100644
index 000000000..55ef757c2
--- /dev/null
+++ b/src/core/Makefile.am
@@ -0,0 +1,68 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3plugindir = $(libdir)/gnunet
4
5if MINGW
6 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
7endif
8
9if USE_COVERAGE
10 AM_CFLAGS = -fprofile-arcs -ftest-coverage
11endif
12
13
14lib_LTLIBRARIES = \
15 libgnunetcore.la
16
17libgnunetcore_la_SOURCES = \
18 core_api.c core.h
19libgnunetcore_la_LIBADD = \
20 $(top_builddir)/src/arm/libgnunetarm.la \
21 $(top_builddir)/src/util/libgnunetutil.la \
22 $(GN_LIBINTL)
23libgnunetcore_la_LDFLAGS = \
24 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
25 -version-info 0:0:0
26
27
28bin_PROGRAMS = \
29 gnunet-service-core
30
31gnunet_service_core_SOURCES = \
32 gnunet-service-core.c
33gnunet_service_core_LDADD = \
34 $(top_builddir)/src/hello/libgnunethello.la \
35 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
36 $(top_builddir)/src/transport/libgnunettransport.la \
37 $(top_builddir)/src/util/libgnunetutil.la \
38 $(GN_LIBINTL)
39
40
41check_PROGRAMS = \
42 test_core_api_start_only \
43 test_core_api
44
45TESTS = $(check_PROGRAMS)
46
47test_core_api_SOURCES = \
48 test_core_api.c
49test_core_api_LDADD = \
50 $(top_builddir)/src/arm/libgnunetarm.la \
51 $(top_builddir)/src/core/libgnunetcore.la \
52 $(top_builddir)/src/transport/libgnunettransport.la \
53 $(top_builddir)/src/util/libgnunetutil.la
54
55
56test_core_api_start_only_SOURCES = \
57 test_core_api_start_only.c
58test_core_api_start_only_LDADD = \
59 $(top_builddir)/src/arm/libgnunetarm.la \
60 $(top_builddir)/src/core/libgnunetcore.la \
61 $(top_builddir)/src/util/libgnunetutil.la
62
63
64EXTRA_DIST = \
65 test_core_api_data.conf \
66 test_core_api_peer1.conf \
67 test_core_api_peer2.conf
68
diff --git a/src/core/core.h b/src/core/core.h
new file mode 100644
index 000000000..840c7e143
--- /dev/null
+++ b/src/core/core.h
@@ -0,0 +1,308 @@
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 core/core.h
23 * @brief common internal definitions for core service
24 * @author Christian Grothoff
25 */
26#include "gnunet_crypto_lib.h"
27#include "gnunet_time_lib.h"
28
29#define DEBUG_CORE GNUNET_NO
30
31/**
32 * Definition of bits in the InitMessage's options field that specify
33 * which events this client cares about. Note that inbound messages
34 * for handlers that were specifically registered are always
35 * transmitted to the client.
36 */
37#define GNUNET_CORE_OPTION_NOTHING 0
38#define GNUNET_CORE_OPTION_SEND_CONNECT 1
39#define GNUNET_CORE_OPTION_SEND_DISCONNECT 2
40#define GNUNET_CORE_OPTION_SEND_BFC 4
41#define GNUNET_CORE_OPTION_SEND_FULL_INBOUND 8
42#define GNUNET_CORE_OPTION_SEND_HDR_INBOUND 16
43#define GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND 32
44#define GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND 64
45
46
47/**
48 * Message transmitted core clients to gnunet-service-core
49 * to start the interaction. This header is followed by
50 * uint16_t type values specifying which messages this
51 * client is interested in.
52 */
53struct InitMessage
54{
55
56 /**
57 * Header with type GNUNET_MESSAGE_TYPE_CORE_INIT.
58 */
59 struct GNUNET_MessageHeader header;
60
61 /**
62 * Options, see GNUNET_CORE_OPTION_ values.
63 */
64 uint32_t options GNUNET_PACKED;
65
66};
67
68
69/**
70 * Message transmitted by the gnunet-service-core process
71 * to its clients in response to an INIT message.
72 */
73struct InitReplyMessage
74{
75
76 /**
77 * Header with type GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY
78 */
79 struct GNUNET_MessageHeader header;
80
81 /**
82 * Always zero.
83 */
84 uint32_t reserved GNUNET_PACKED;
85
86 /**
87 * Public key of the local peer.
88 */
89 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
90
91};
92
93
94/**
95 * Message sent by the service to clients to notify them
96 * about a peer connecting or disconnecting.
97 */
98struct ConnectNotifyMessage
99{
100 /**
101 * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT
102 * or GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT.
103 */
104 struct GNUNET_MessageHeader header;
105
106 /**
107 * Available bandwidth to this peer; zero for disconnect.
108 * [TODO: currently set to hard-coded constant and hence
109 * not really useful, right?]
110 */
111 uint32_t bpm_available GNUNET_PACKED;
112
113 /**
114 * Identity of the connecting peer.
115 */
116 struct GNUNET_PeerIdentity peer;
117
118 /**
119 * Time of our last interaction with the peer; close
120 * to "now" for connect messages.
121 * [TODO: is this useful?]
122 */
123 struct GNUNET_TIME_AbsoluteNBO last_activity;
124
125};
126
127
128
129/**
130 * Message sent by the service to clients to notify them about
131 * messages being received or transmitted. This overall message is
132 * followed by the real message, or just the header of the real
133 * message (depending on the client's preferences). The receiver can
134 * tell if he got the full message or only a partial message by
135 * looking at the size field in the header of NotifyTrafficMessage and
136 * checking it with the size field in the message that follows.
137 */
138struct NotifyTrafficMessage
139{
140 /**
141 * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND
142 * or GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND.
143 */
144 struct GNUNET_MessageHeader header;
145
146 /**
147 * Always zero.
148 */
149 uint32_t reserved GNUNET_PACKED;
150
151 /**
152 * Identity of the receiver or sender.
153 */
154 struct GNUNET_PeerIdentity peer;
155
156};
157
158
159/**
160 * Message sent to the core asking for configuration
161 * information and possibly preference changes.
162 */
163struct RequestConfigureMessage
164{
165 /**
166 * Header with type GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE
167 */
168 struct GNUNET_MessageHeader header;
169
170 /**
171 * Always zero.
172 */
173 uint32_t reserved GNUNET_PACKED;
174
175 /**
176 * Limit the number of bytes of outbound traffic to this
177 * peer to at most the specified amount (naturally, the
178 * amount is also limited by the receiving peer).
179 */
180 uint32_t limit_outbound_bpm GNUNET_PACKED;
181
182 /**
183 * Number of bytes of inbound traffic to reserve, can
184 * be negative (to unreserve). NBO.
185 */
186 int32_t reserve_inbound GNUNET_PACKED;
187
188 /**
189 * Increment the current traffic preference for the given peer by
190 * the specified amont. The traffic preference is used to determine
191 * the share of bandwidth this peer will typcially be assigned.
192 */
193 double preference_change GNUNET_PACKED;
194
195 /**
196 * Identity of the peer being configured.
197 */
198 struct GNUNET_PeerIdentity peer;
199
200};
201
202
203/**
204 * Response from the core to a "RequestConfigureMessage"
205 * providing traffic status information for a peer.
206 */
207struct ConfigurationInfoMessage
208{
209 /**
210 * Header with type GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO
211 */
212 struct GNUNET_MessageHeader header;
213
214 /**
215 * Amount of traffic (inbound number of bytes) that was reserved in
216 * response to the configuration change request. Negative for
217 * "unreserved" bytes.
218 */
219 int32_t reserved_amount GNUNET_PACKED;
220
221 /**
222 * Available bandwidth in (in bytes per minute) for this peer.
223 * 0 if we have been disconnected.
224 */
225 uint32_t bpm_in GNUNET_PACKED;
226
227 /**
228 * Available bandwidth out (in bytes per minute) for this peer,
229 * 0 if we have been disconnected.
230 */
231 uint32_t bpm_out GNUNET_PACKED;
232
233 /**
234 * Latest transport latency estimate for the peer.
235 * FOREVER if we have been disconnected.
236 */
237 struct GNUNET_TIME_RelativeNBO latency;
238
239 /**
240 * Current traffic preference for the peer.
241 * 0 if we have been disconnected.
242 */
243 double preference;
244
245 /**
246 * Identity of the receiver or sender.
247 */
248 struct GNUNET_PeerIdentity peer;
249
250};
251
252
253/**
254 * Core asking a client to generate traffic for a particular
255 * target.
256 */
257struct SolicitTrafficMessage
258{
259 /**
260 * Header with type GNUNET_MESSAGE_TYPE_CORE_SOLICIT_TRAFFIC
261 * or GNUNET_MESSAGE_TYPE_CORE_RECV_OK
262 */
263 struct GNUNET_MessageHeader header;
264
265 /**
266 * Number of bytes of traffic being solicited.
267 */
268 uint32_t solicit_size GNUNET_PACKED;
269
270 /**
271 * Identity of the receiver or sender.
272 */
273 struct GNUNET_PeerIdentity peer;
274
275};
276
277
278/**
279 * Client asking core to transmit a particular message to
280 * a particular target. Does NOT have to be solicited.
281 */
282struct SendMessage
283{
284 /**
285 * Header with type GNUNET_MESSAGE_TYPE_CORE_SEND
286 */
287 struct GNUNET_MessageHeader header;
288
289 /**
290 * How important is this message?
291 */
292 uint32_t priority GNUNET_PACKED;
293
294 /**
295 * By what time would the sender really like to see this
296 * message transmitted?
297 */
298 struct GNUNET_TIME_AbsoluteNBO deadline;
299
300 /**
301 * Identity of the receiver or sender.
302 */
303 struct GNUNET_PeerIdentity peer;
304
305};
306
307
308/* end of core.h */
diff --git a/src/core/core_api.c b/src/core/core_api.c
new file mode 100644
index 000000000..10fa0ccdd
--- /dev/null
+++ b/src/core/core_api.c
@@ -0,0 +1,1071 @@
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 core/core_api.c
23 * @brief core service; this is the main API for encrypted P2P
24 * communications
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_core_service.h"
29#include "core.h"
30
31
32/**
33 * Context for the core service connection.
34 */
35struct GNUNET_CORE_Handle
36{
37
38 /**
39 * Our scheduler.
40 */
41 struct GNUNET_SCHEDULER_Handle *sched;
42
43 /**
44 * Configuration we're using.
45 */
46 struct GNUNET_CONFIGURATION_Handle *cfg;
47
48 /**
49 * Closure for the various callbacks.
50 */
51 void *cls;
52
53 /**
54 * Function to call once we've handshaked with the core service.
55 */
56 GNUNET_CORE_StartupCallback init;
57
58 /**
59 * Function to call whenever we're notified about a peer connecting.
60 */
61 GNUNET_CORE_ClientEventHandler connects;
62
63 /**
64 * Function to call whenever we're notified about a peer disconnecting.
65 */
66 GNUNET_CORE_ClientEventHandler disconnects;
67
68 /**
69 * Function to call whenever we're asked to generate traffic
70 * (data provided to be transmitted back to the service).
71 */
72 GNUNET_CORE_BufferFillCallback bfc;
73
74 /**
75 * Function to call whenever we receive an inbound message.
76 */
77 GNUNET_CORE_MessageCallback inbound_notify;
78
79 /**
80 * Function to call whenever we receive an outbound message.
81 */
82 GNUNET_CORE_MessageCallback outbound_notify;
83
84 /**
85 * Function handlers for messages of particular type.
86 */
87 const struct GNUNET_CORE_MessageHandler *handlers;
88
89 /**
90 * Our connection to the service.
91 */
92 struct GNUNET_CLIENT_Connection *client;
93
94 /**
95 * Handle for our current transmission request.
96 */
97 struct GNUNET_NETWORK_TransmitHandle *th;
98
99 /**
100 * Head of doubly-linked list of pending requests.
101 */
102 struct GNUNET_CORE_TransmitHandle *pending_head;
103
104 /**
105 * Tail of doubly-linked list of pending requests.
106 */
107 struct GNUNET_CORE_TransmitHandle *pending_tail;
108
109 /**
110 * Currently submitted request (or NULL)
111 */
112 struct GNUNET_CORE_TransmitHandle *submitted;
113
114 /**
115 * How long to wait until we time out the connection attempt?
116 */
117 struct GNUNET_TIME_Absolute startup_timeout;
118
119 /**
120 * ID of reconnect task (if any).
121 */
122 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
123
124 /**
125 * Number of entries in the handlers array.
126 */
127 unsigned int hcnt;
128
129 /**
130 * For inbound notifications without a specific handler, do
131 * we expect to only receive headers?
132 */
133 int inbound_hdr_only;
134
135 /**
136 * For outbound notifications without a specific handler, do
137 * we expect to only receive headers?
138 */
139 int outbound_hdr_only;
140
141 /**
142 * Are we currently disconnected and hence unable to forward
143 * requests?
144 */
145 int currently_down;
146};
147
148
149/**
150 * Handle for a transmission request.
151 */
152struct GNUNET_CORE_TransmitHandle
153{
154
155 /**
156 * We keep active transmit handles in a doubly-linked list.
157 */
158 struct GNUNET_CORE_TransmitHandle *next;
159
160 /**
161 * We keep active transmit handles in a doubly-linked list.
162 */
163 struct GNUNET_CORE_TransmitHandle *prev;
164
165 /**
166 * Corresponding core handle.
167 */
168 struct GNUNET_CORE_Handle *ch;
169
170 /**
171 * Function that will be called to get the actual request
172 * (once we are ready to transmit this request to the core).
173 * The function will be called with a NULL buffer to signal
174 * timeout.
175 */
176 GNUNET_NETWORK_TransmitReadyNotify get_message;
177
178 /**
179 * Closure for get_message.
180 */
181 void *get_message_cls;
182
183 /**
184 * If this entry is for a configuration request, pointer
185 * to the information callback; otherwise NULL.
186 */
187 GNUNET_CORE_PeerConfigurationInfoCallback info;
188
189 /**
190 * Closure for info.
191 */
192 void *info_cls;
193
194 /**
195 * If this entry is for a transmission request, pointer
196 * to the notify callback; otherwise NULL.
197 */
198 GNUNET_NETWORK_TransmitReadyNotify notify;
199
200 /**
201 * Closure for notify.
202 */
203 void *notify_cls;
204
205 /**
206 * Peer the request is about.
207 */
208 struct GNUNET_PeerIdentity peer;
209
210 /**
211 * Timeout for this handle.
212 */
213 struct GNUNET_TIME_Absolute timeout;
214
215 /**
216 * ID of timeout task.
217 */
218 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
219
220 /**
221 * How important is this message?
222 */
223 uint32_t priority;
224
225 /**
226 * Size of this request.
227 */
228 uint16_t msize;
229
230
231};
232
233
234/**
235 * Function called when we are ready to transmit our
236 * "START" message (or when this operation timed out).
237 *
238 * @param cls closure
239 * @param size number of bytes available in buf
240 * @param buf where the callee should write the message
241 * @return number of bytes written to buf
242 */
243static size_t transmit_start (void *cls, size_t size, void *buf);
244
245
246/**
247 * Our current client connection went down. Clean it up
248 * and try to reconnect!
249 */
250static void
251reconnect (struct GNUNET_CORE_Handle *h)
252{
253 GNUNET_CLIENT_disconnect (h->client);
254 h->currently_down = GNUNET_YES;
255 h->client = GNUNET_CLIENT_connect (h->sched, "core", h->cfg);
256 h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
257 sizeof (struct InitMessage) +
258 sizeof (uint16_t) * h->hcnt,
259 GNUNET_TIME_UNIT_SECONDS,
260 &transmit_start, h);
261}
262
263
264/**
265 * The given request hit its timeout. Remove from the
266 * doubly-linked list and call the respective continuation.
267 *
268 * @param cls the transmit handle of the request that timed out
269 * @param tc context, can be NULL (!)
270 */
271static void
272timeout_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
273{
274 struct GNUNET_CORE_TransmitHandle *th = cls;
275 struct GNUNET_CORE_Handle *h;
276
277 h = th->ch;
278 th->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
279 GNUNET_assert (0 == th->get_message (th->get_message_cls, 0, NULL));
280 GNUNET_CORE_notify_transmit_ready_cancel (th);
281}
282
283
284/**
285 * Function called when we are ready to transmit a request from our
286 * request list (or when this operation timed out).
287 *
288 * @param cls closure
289 * @param size number of bytes available in buf
290 * @param buf where the callee should write the message
291 * @return number of bytes written to buf
292 */
293static size_t
294request_start (void *cls, size_t size, void *buf)
295{
296 struct GNUNET_CORE_Handle *h = cls;
297 struct GNUNET_CORE_TransmitHandle *th;
298 size_t ret;
299
300 h->th = NULL;
301 th = h->pending_head;
302 if (buf == NULL)
303 {
304 timeout_request (th, NULL);
305 return 0;
306 }
307 /* create new timeout task (in case core takes too long to respond!) */
308 th->timeout_task = GNUNET_SCHEDULER_add_delayed (h->sched,
309 GNUNET_NO,
310 GNUNET_SCHEDULER_PRIORITY_KEEP,
311 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
312 GNUNET_TIME_absolute_get_remaining
313 (th->timeout),
314 &timeout_request, th);
315 /* remove th from doubly-linked pending list, move to submitted */
316 GNUNET_assert (th->prev == NULL);
317 h->pending_head = th->next;
318 if (th->next == NULL)
319 h->pending_tail = NULL;
320 else
321 th->next->prev = NULL;
322 GNUNET_assert (h->submitted == NULL);
323 h->submitted = th;
324 GNUNET_assert (size >= th->msize);
325 ret = th->get_message (th->get_message_cls, size, buf);
326 GNUNET_assert (ret <= size);
327 return ret;
328}
329
330
331/**
332 * Check the list of pending requests, send the next
333 * one to the core.
334 */
335static void
336trigger_next_request (struct GNUNET_CORE_Handle *h)
337{
338 struct GNUNET_CORE_TransmitHandle *th;
339 if (h->currently_down)
340 return; /* connection temporarily down */
341 if (NULL == (th = h->pending_head))
342 return; /* no requests pending */
343 GNUNET_assert (NULL == h->th);
344 GNUNET_SCHEDULER_cancel (h->sched, th->timeout_task);
345 th->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
346 h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
347 th->msize,
348 GNUNET_TIME_absolute_get_remaining
349 (th->timeout), &request_start,
350 h);
351}
352
353
354/**
355 * cls is a pointer to a 32 bit number followed by that
356 * amount of data. If possible, copy to buf and return
357 * number of bytes copied. Always free the buffer.
358 */
359static size_t
360copy_and_free (void *cls, size_t size, void *buf)
361{
362 char *cbuf = cls;
363 uint32_t have;
364
365 memcpy (&have, cbuf, sizeof (uint32_t));
366 if (have > size)
367 {
368 /* timeout / error case */
369 GNUNET_free (cbuf);
370 return 0;
371 }
372 memcpy (buf, cbuf + sizeof (uint32_t), have);
373 GNUNET_free (cbuf);
374 return have;
375}
376
377
378/**
379 * Call bfc callback to solicit traffic for the given peer.
380 */
381static void
382solicit_traffic (struct GNUNET_CORE_Handle *h,
383 const struct GNUNET_PeerIdentity *peer, uint32_t amount)
384{
385 char buf[amount];
386 size_t have;
387 char *cbuf;
388
389 have = h->bfc (h->cls, peer, buf, amount);
390 if (have == 0)
391 return;
392 GNUNET_assert (have >= sizeof (struct GNUNET_MessageHeader));
393 cbuf = GNUNET_malloc (have + sizeof (uint32_t));
394 memcpy (cbuf, &have, sizeof (uint32_t));
395 memcpy (cbuf + sizeof (uint32_t), buf, have);
396 GNUNET_CORE_notify_transmit_ready (h,
397 0,
398 GNUNET_TIME_UNIT_SECONDS,
399 peer, have, &copy_and_free, cbuf);
400}
401
402
403/**
404 * Handler for most messages received from the core.
405 */
406static void
407main_handler (void *cls, const struct GNUNET_MessageHeader *msg)
408{
409 struct GNUNET_CORE_Handle *h = cls;
410 unsigned int hpos;
411 const struct ConnectNotifyMessage *cnm;
412 const struct NotifyTrafficMessage *ntm;
413 const struct ConfigurationInfoMessage *cim;
414 const struct SolicitTrafficMessage *stm;
415 const struct GNUNET_MessageHeader *em;
416 uint16_t msize;
417 uint16_t et;
418 uint32_t ss;
419 const struct GNUNET_CORE_MessageHandler *mh;
420
421 if (msg == NULL)
422 {
423 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
424 _
425 ("Client was disconnected from core service, trying to reconnect.\n"));
426 reconnect (h);
427 return;
428 }
429 msize = ntohs (msg->size);
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 "Processing message of type %u and size %u from core service\n",
432 ntohs (msg->type), msize);
433 switch (ntohs (msg->type))
434 {
435 case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT:
436 if (NULL == h->connects)
437 {
438 GNUNET_break (0);
439 break;
440 }
441 if (msize != sizeof (struct ConnectNotifyMessage))
442 {
443 GNUNET_break (0);
444 break;
445 }
446 cnm = (const struct ConnectNotifyMessage *) msg;
447 break;
448 case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT:
449 if (NULL == h->disconnects)
450 {
451 GNUNET_break (0);
452 break;
453 }
454 if (msize != sizeof (struct ConnectNotifyMessage))
455 {
456 GNUNET_break (0);
457 break;
458 }
459 cnm = (const struct ConnectNotifyMessage *) msg;
460 break;
461 case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND:
462 if (msize <
463 sizeof (struct NotifyTrafficMessage) +
464 sizeof (struct GNUNET_MessageHeader))
465 {
466 GNUNET_break (0);
467 break;
468 }
469 ntm = (const struct NotifyTrafficMessage *) msg;
470 em = (const struct GNUNET_MessageHeader *) &ntm[1];
471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
472 "Received message of type %u from peer `%4s'\n",
473 ntohs (em->type), GNUNET_i2s (&ntm->peer));
474 if ((GNUNET_NO == h->inbound_hdr_only) &&
475 (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage)))
476 {
477 GNUNET_break (0);
478 break;
479 }
480 et = ntohs (em->type);
481 for (hpos = 0; hpos < h->hcnt; hpos++)
482 {
483 mh = &h->handlers[hpos];
484 if (mh->type != et)
485 continue;
486 if ((mh->expected_size != ntohs (em->size)) &&
487 (mh->expected_size != 0))
488 {
489 GNUNET_break (0);
490 continue;
491 }
492 if (GNUNET_OK !=
493 h->handlers[hpos].callback (h->cls, &ntm->peer, em))
494 {
495 /* error in processing, disconnect ! */
496 reconnect (h);
497 return;
498 }
499 }
500 if (NULL != h->inbound_notify)
501 h->inbound_notify (h->cls, &ntm->peer, em);
502 break;
503 case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND:
504 if (msize <
505 sizeof (struct NotifyTrafficMessage) +
506 sizeof (struct GNUNET_MessageHeader))
507 {
508 GNUNET_break (0);
509 break;
510 }
511 ntm = (const struct NotifyTrafficMessage *) msg;
512 em = (const struct GNUNET_MessageHeader *) &ntm[1];
513 if ((GNUNET_NO == h->outbound_hdr_only) &&
514 (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage)))
515 {
516 GNUNET_break (0);
517 break;
518 }
519 if (NULL == h->outbound_notify)
520 {
521 GNUNET_break (0);
522 break;
523 }
524 h->outbound_notify (h->cls, &ntm->peer, em);
525 break;
526 case GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO:
527 if (msize != sizeof (struct ConfigurationInfoMessage))
528 {
529 GNUNET_break (0);
530 break;
531 }
532 if (NULL == h->submitted)
533 break;
534 cim = (const struct ConfigurationInfoMessage *) msg;
535
536 /* process configuration data */
537 if (h->submitted->info != NULL)
538 h->submitted->info (h->submitted->info_cls,
539 &h->submitted->peer,
540 ntohl (cim->bpm_in),
541 ntohl (cim->bpm_out),
542 GNUNET_TIME_relative_ntoh (cim->latency),
543 (int) ntohl (cim->reserved_amount),
544 cim->preference);
545 /* done, clean up! */
546 GNUNET_CORE_notify_transmit_ready_cancel (h->submitted);
547 trigger_next_request (h);
548 break;
549 case GNUNET_MESSAGE_TYPE_CORE_SOLICIT_TRAFFIC:
550 if (msize != sizeof (struct SolicitTrafficMessage))
551 {
552 GNUNET_break (0);
553 break;
554 }
555 stm = (const struct SolicitTrafficMessage *) msg;
556 if (NULL == h->bfc)
557 {
558 GNUNET_break (0);
559 break;
560 }
561 ss = ntohl (stm->solicit_size);
562 if ((ss > GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
563 (ss + sizeof (struct SendMessage) > GNUNET_SERVER_MAX_MESSAGE_SIZE))
564 {
565 GNUNET_break (0);
566 break;
567 }
568 solicit_traffic (h, &stm->peer, ss);
569 break;
570 default:
571 GNUNET_break (0);
572 break;
573 }
574 GNUNET_CLIENT_receive (h->client,
575 &main_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
576}
577
578
579
580/**
581 * Function called when we are ready to transmit our
582 * "START" message (or when this operation timed out).
583 *
584 * @param cls closure
585 * @param size number of bytes available in buf
586 * @param buf where the callee should write the message
587 * @return number of bytes written to buf
588 */
589static size_t transmit_start (void *cls, size_t size, void *buf);
590
591
592/**
593 * Function called on the first message received from
594 * the service (contains our public key, etc.).
595 * Should trigger calling the init callback
596 * and then start our regular message processing.
597 *
598 * @param cls closure
599 * @param msg message received, NULL on timeout or fatal error
600 */
601static void
602init_reply_handler (void *cls, const struct GNUNET_MessageHeader *msg)
603{
604 struct GNUNET_CORE_Handle *h = cls;
605 const struct InitReplyMessage *m;
606 GNUNET_CORE_StartupCallback init;
607 struct GNUNET_PeerIdentity my_identity;
608
609 if ((msg == NULL) ||
610 (ntohs (msg->size) != sizeof (struct InitReplyMessage)) ||
611 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY))
612 {
613 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
614 _
615 ("Error connecting to core service (failed to receive `%s' message).\n"),
616 "INIT_REPLY");
617 GNUNET_break (msg == NULL);
618 transmit_start (h, 0, NULL);
619 return;
620 }
621 m = (const struct InitReplyMessage *) msg;
622 /* start our message processing loop */
623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624 _
625 ("Successfully connected to core service, starting processing loop.\n"));
626 h->currently_down = GNUNET_NO;
627 trigger_next_request (h);
628 GNUNET_CLIENT_receive (h->client,
629 &main_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
630 if (NULL != (init = h->init))
631 {
632 /* mark so we don't call init on reconnect */
633 h->init = NULL;
634 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
635 _("Successfully connected to core service.\n"));
636 GNUNET_CRYPTO_hash (&m->publicKey,
637 sizeof (struct
638 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
639 &my_identity.hashPubKey);
640 init (h->cls, h, &my_identity, &m->publicKey);
641 }
642}
643
644
645static void
646reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
647{
648 struct GNUNET_CORE_Handle *h = cls;
649 h->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
650 reconnect (h);
651}
652
653
654/**
655 * Function called when we are ready to transmit our
656 * "START" message (or when this operation timed out).
657 *
658 * @param cls closure
659 * @param size number of bytes available in buf
660 * @param buf where the callee should write the message
661 * @return number of bytes written to buf
662 */
663static size_t
664transmit_start (void *cls, size_t size, void *buf)
665{
666 struct GNUNET_CORE_Handle *h = cls;
667 struct InitMessage *init;
668 uint16_t *ts;
669 uint16_t msize;
670 uint32_t opt;
671 unsigned int hpos;
672 struct GNUNET_TIME_Relative delay;
673
674 h->th = NULL;
675 if (size == 0)
676 {
677 if ((h->init == NULL) ||
678 (GNUNET_TIME_absolute_get ().value < h->startup_timeout.value))
679 {
680 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
681 _("Failed to connect to core service, retrying.\n"));
682 delay = GNUNET_TIME_absolute_get_remaining (h->startup_timeout);
683 if ((h->init == NULL) || (delay.value > 1000))
684 delay = GNUNET_TIME_UNIT_SECONDS;
685 if (h->init == NULL)
686 h->startup_timeout =
687 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
688 h->reconnect_task =
689 GNUNET_SCHEDULER_add_delayed (h->sched, GNUNET_NO,
690 GNUNET_SCHEDULER_PRIORITY_IDLE,
691 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
692 delay, &reconnect_task, h);
693 return 0;
694 }
695 /* timeout on initial connect */
696 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
697 _("Failed to connect to core service, giving up.\n"));
698 h->init (h->cls, NULL, NULL, NULL);
699 GNUNET_CORE_disconnect (h);
700 return 0;
701 }
702 msize = h->hcnt * sizeof (uint16_t) + sizeof (struct InitMessage);
703 GNUNET_assert (size >= msize);
704 init = buf;
705 init->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT);
706 init->header.size = htons (msize);
707 opt = GNUNET_CORE_OPTION_NOTHING;
708 if (h->connects != NULL)
709 opt |= GNUNET_CORE_OPTION_SEND_CONNECT;
710 if (h->disconnects != NULL)
711 opt |= GNUNET_CORE_OPTION_SEND_DISCONNECT;
712 if (h->bfc != NULL)
713 opt |= GNUNET_CORE_OPTION_SEND_BFC;
714 if (h->inbound_notify != NULL)
715 {
716 if (h->inbound_hdr_only)
717 opt |= GNUNET_CORE_OPTION_SEND_HDR_INBOUND;
718 else
719 opt |= GNUNET_CORE_OPTION_SEND_FULL_INBOUND;
720 }
721 if (h->outbound_notify != NULL)
722 {
723 if (h->outbound_hdr_only)
724 opt |= GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND;
725 else
726 opt |= GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND;
727 }
728 init->options = htonl (opt);
729 ts = (uint16_t *) & init[1];
730 for (hpos = 0; hpos < h->hcnt; hpos++)
731 ts[hpos] = htons (h->handlers[hpos].type);
732 GNUNET_CLIENT_receive (h->client,
733 &init_reply_handler,
734 h,
735 GNUNET_TIME_absolute_get_remaining (h->
736 startup_timeout));
737 return sizeof (struct InitMessage) + h->hcnt * sizeof (uint16_t);
738}
739
740
741/**
742 * Connect to the core service. Note that the connection may
743 * complete (or fail) asynchronously.
744 *
745 * @param sched scheduler to use
746 * @param cfg configuration to use
747 * @param timeout after how long should we give up trying to connect to the core service?
748 * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
749 * @param init callback to call on timeout or once we have successfully
750 * connected to the core service
751 * @param connects function to call on peer connect, can be NULL
752 * @param disconnects function to call on peer disconnect / timeout, can be NULL
753 * @param bfc function to call to fill up spare bandwidth, can be NULL
754 * @param inbound_notify function to call for all inbound messages, can be NULL
755 * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the
756 * GNUNET_MessageHeader and hence we do not need to give it the full message;
757 * can be used to improve efficiency, ignored if inbound_notify is NULLL
758 * @param outbound_notify function to call for all outbound messages, can be NULL
759 * @param outbound_hdr_only set to GNUNET_YES if outbound_notify will only read the
760 * GNUNET_MessageHeader and hence we do not need to give it the full message
761 * can be used to improve efficiency, ignored if outbound_notify is NULLL
762 * @param handlers callbacks for messages we care about, NULL-terminated
763 */
764void
765GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
766 struct GNUNET_CONFIGURATION_Handle *cfg,
767 struct GNUNET_TIME_Relative timeout,
768 void *cls,
769 GNUNET_CORE_StartupCallback init,
770 GNUNET_CORE_ClientEventHandler connects,
771 GNUNET_CORE_ClientEventHandler disconnects,
772 GNUNET_CORE_BufferFillCallback bfc,
773 GNUNET_CORE_MessageCallback inbound_notify,
774 int inbound_hdr_only,
775 GNUNET_CORE_MessageCallback outbound_notify,
776 int outbound_hdr_only,
777 const struct GNUNET_CORE_MessageHandler *handlers)
778{
779 struct GNUNET_CORE_Handle *h;
780
781 GNUNET_assert (init != NULL);
782 h = GNUNET_malloc (sizeof (struct GNUNET_CORE_Handle));
783 h->sched = sched;
784 h->cfg = cfg;
785 h->cls = cls;
786 h->init = init;
787 h->connects = connects;
788 h->disconnects = disconnects;
789 h->bfc = bfc;
790 h->inbound_notify = inbound_notify;
791 h->outbound_notify = outbound_notify;
792 h->inbound_hdr_only = inbound_hdr_only;
793 h->outbound_hdr_only = outbound_hdr_only;
794 h->handlers = handlers;
795 h->client = GNUNET_CLIENT_connect (sched, "core", cfg);
796 h->startup_timeout = GNUNET_TIME_relative_to_absolute (timeout);
797 h->hcnt = 0;
798 while (handlers[h->hcnt].callback != NULL)
799 h->hcnt++;
800 GNUNET_assert (h->hcnt <
801 (GNUNET_SERVER_MAX_MESSAGE_SIZE -
802 sizeof (struct InitMessage)) / sizeof (uint16_t));
803 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804 "Trying to connect to core service in next %llu ms.\n",
805 timeout.value);
806 h->th =
807 GNUNET_CLIENT_notify_transmit_ready (h->client,
808 sizeof (struct InitMessage) +
809 sizeof (uint16_t) * h->hcnt, timeout,
810 &transmit_start, h);
811}
812
813
814/**
815 * Disconnect from the core service.
816 *
817 * @param handle connection to core to disconnect
818 */
819void
820GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
821{
822 if (handle->th != NULL)
823 GNUNET_NETWORK_notify_transmit_ready_cancel (handle->th);
824 if (handle->reconnect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
825 GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task);
826 GNUNET_CLIENT_disconnect (handle->client);
827 GNUNET_free (handle);
828}
829
830
831/**
832 * Build the configure message.
833 */
834static size_t
835produce_configure_message (void *cls, size_t size, void *buf)
836{
837 struct GNUNET_CORE_TransmitHandle *th = cls;
838 struct GNUNET_CORE_Handle *ch = th->ch;
839
840 if (buf == NULL)
841 {
842 /* communicate handle timeout/error! */
843 if (th->info != NULL)
844 th->info (th->info_cls, NULL, 0, 0, GNUNET_TIME_UNIT_ZERO, 0, 0.0);
845 if (th->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
846 GNUNET_CORE_notify_transmit_ready_cancel (th);
847 if (ch->submitted == th)
848 ch->submitted = NULL;
849 trigger_next_request (ch);
850 return 0;
851 }
852 GNUNET_assert (size >= sizeof (struct RequestConfigureMessage));
853 memcpy (buf, &th[1], sizeof (struct RequestConfigureMessage));
854 if (th->prev == NULL)
855 ch->pending_head = th->next;
856 else
857 th->prev->next = th->next;
858 if (th->next == NULL)
859 ch->pending_tail = th->prev;
860 else
861 th->next->prev = th->prev;
862 GNUNET_assert (ch->submitted == NULL);
863 ch->submitted = th;
864 return sizeof (struct RequestConfigureMessage);
865}
866
867
868/**
869 * Obtain statistics and/or change preferences for the given peer.
870 *
871 * @param handle connection to core to use
872 * @param peer identifies the peer
873 * @param timeout after how long should we give up (and call "info" with NULL
874 * for "peer" to signal an error)?
875 * @param bpm_out set to the current bandwidth limit (sending) for this peer,
876 * caller should set "bpm_out" to "-1" to avoid changing
877 * the current value; otherwise "bpm_out" will be lowered to
878 * the specified value; passing a pointer to "0" can be used to force
879 * us to disconnect from the peer; "bpm_out" might not increase
880 * as specified since the upper bound is generally
881 * determined by the other peer!
882 * @param amount reserve N bytes for receiving, negative
883 * amounts can be used to undo a (recent) reservation;
884 * @param preference increase incoming traffic share preference by this amount;
885 * in the absence of "amount" reservations, we use this
886 * preference value to assign proportional bandwidth shares
887 * to all connected peers
888 * @param info function to call with the resulting configuration information
889 * @param info_cls closure for info
890 */
891void
892GNUNET_CORE_peer_configure (struct GNUNET_CORE_Handle *handle,
893 const struct GNUNET_PeerIdentity *peer,
894 struct GNUNET_TIME_Relative timeout,
895 unsigned int bpm_out,
896 int amount,
897 double preference,
898 GNUNET_CORE_PeerConfigurationInfoCallback info,
899 void *info_cls)
900{
901 struct RequestConfigureMessage *rcm;
902 struct GNUNET_CORE_TransmitHandle *th;
903
904 th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle) +
905 sizeof (struct RequestConfigureMessage));
906 /* append to list */
907 th->prev = handle->pending_tail;
908 if (handle->pending_tail == NULL)
909 handle->pending_head = th;
910 else
911 handle->pending_tail->next = th;
912 th->ch = handle;
913 th->get_message = &produce_configure_message;
914 th->get_message_cls = th;
915 th->info = info;
916 th->info_cls = info_cls;
917 th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
918 th->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->sched,
919 GNUNET_NO,
920 GNUNET_SCHEDULER_PRIORITY_KEEP,
921 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
922 timeout,
923 &timeout_request, th);
924 th->msize = sizeof (struct RequestConfigureMessage);
925 rcm = (struct RequestConfigureMessage *) &th[1];
926 rcm->header.size = htons (sizeof (struct RequestConfigureMessage));
927 rcm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE);
928 rcm->reserved = htonl (0);
929 rcm->limit_outbound_bpm = htonl (bpm_out);
930 rcm->reserve_inbound = htonl (amount);
931 rcm->preference_change = preference;
932 rcm->peer = *peer;
933 if (handle->pending_head == th)
934 trigger_next_request (handle);
935}
936
937
938/**
939 * Build the message requesting data transmission.
940 */
941static size_t
942produce_send (void *cls, size_t size, void *buf)
943{
944 struct GNUNET_CORE_TransmitHandle *th = cls;
945 struct GNUNET_CORE_Handle *h;
946 struct SendMessage *sm;
947 size_t dt;
948 GNUNET_NETWORK_TransmitReadyNotify notify;
949 void *notify_cls;
950
951 h = th->ch;
952 if (buf == NULL)
953 {
954 /* timeout or error */
955 GNUNET_assert (0 == th->notify (th->notify_cls, 0, NULL));
956 if (th->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
957 GNUNET_CORE_notify_transmit_ready_cancel (th);
958 trigger_next_request (h);
959 return 0;
960 }
961 GNUNET_assert (th->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
962 sm = (struct SendMessage *) buf;
963 sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND);
964 sm->priority = htonl (th->priority);
965 sm->deadline = GNUNET_TIME_absolute_hton (th->timeout);
966 sm->peer = th->peer;
967 notify = th->notify;
968 notify_cls = th->notify_cls;
969 GNUNET_CORE_notify_transmit_ready_cancel (th);
970 trigger_next_request (h);
971 GNUNET_assert (size >= sizeof (struct SendMessage));
972 dt = notify (notify_cls, size - sizeof (struct SendMessage), &sm[1]);
973 sm->header.size = htons (dt + sizeof (struct SendMessage));
974 GNUNET_assert (dt + sizeof (struct SendMessage) < size);
975 return dt + sizeof (struct SendMessage);
976}
977
978
979/**
980 * Ask the core to call "notify" once it is ready to transmit the
981 * given number of bytes to the specified "target". If we are not yet
982 * connected to the specified peer, a call to this function will cause
983 * us to try to establish a connection.
984 *
985 * @param handle connection to core service
986 * @param priority how important is the message?
987 * @param maxdelay how long can the message wait?
988 * @param target who should receive the message,
989 * use NULL for this peer (loopback)
990 * @param notify_size how many bytes of buffer space does notify want?
991 * @param notify function to call when buffer space is available
992 * @param notify_cls closure for notify
993 * @return non-NULL if the notify callback was queued,
994 * NULL if we can not even queue the request (insufficient
995 * memory); if NULL is returned, "notify" will NOT be called.
996 */
997struct GNUNET_CORE_TransmitHandle *
998GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
999 unsigned int priority,
1000 struct GNUNET_TIME_Relative maxdelay,
1001 const struct GNUNET_PeerIdentity *target,
1002 size_t notify_size,
1003 GNUNET_NETWORK_TransmitReadyNotify notify,
1004 void *notify_cls)
1005{
1006 struct GNUNET_CORE_TransmitHandle *th;
1007
1008 GNUNET_assert (notify_size + sizeof (struct SendMessage) <
1009 GNUNET_SERVER_MAX_MESSAGE_SIZE);
1010 th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle));
1011 th->ch = handle;
1012 /* append to list */
1013 th->prev = handle->pending_tail;
1014 if (handle->pending_tail == NULL)
1015 handle->pending_head = th;
1016 else
1017 handle->pending_tail->next = th;
1018 th->get_message = &produce_send;
1019 th->get_message_cls = th;
1020 th->notify = notify;
1021 th->notify_cls = notify_cls;
1022 th->peer = *target;
1023 th->timeout = GNUNET_TIME_relative_to_absolute (maxdelay);
1024 th->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->sched,
1025 GNUNET_NO,
1026 GNUNET_SCHEDULER_PRIORITY_KEEP,
1027 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1028 maxdelay,
1029 &timeout_request, th);
1030 th->priority = priority;
1031 th->msize = sizeof (struct SendMessage) + notify_size;
1032 /* was the request queue previously empty? */
1033 if (handle->pending_head == th)
1034 trigger_next_request (handle);
1035 return NULL;
1036}
1037
1038
1039/**
1040 * Cancel the specified transmission-ready notification.
1041 *
1042 * @param h handle that was returned by "notify_transmit_ready".
1043 */
1044void
1045GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle
1046 *h)
1047{
1048 struct GNUNET_CORE_Handle *handle = h->ch;
1049
1050 if (handle->submitted == h)
1051 {
1052 handle->submitted = NULL;
1053 }
1054 else
1055 {
1056 if (h->prev == NULL)
1057 handle->pending_head = h->next;
1058 else
1059 h->prev->next = h->next;
1060 if (h->next == NULL)
1061 handle->pending_tail = h->prev;
1062 else
1063 h->next->prev = h->prev;
1064 }
1065 if (h->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1066 GNUNET_SCHEDULER_cancel (handle->sched, h->timeout_task);
1067 GNUNET_free (h);
1068}
1069
1070
1071/* end of core_api.c */
diff --git a/src/core/gnunet-service-core.c b/src/core/gnunet-service-core.c
new file mode 100644
index 000000000..e9e076bcc
--- /dev/null
+++ b/src/core/gnunet-service-core.c
@@ -0,0 +1,2859 @@
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 core/gnunet-service-core.c
23 * @brief high-level P2P messaging
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * TESTING:
28 * - write test for basic core functions:
29 * + connect to peer
30 * + transmit (encrypted) message [with handshake]
31 * + receive (encrypted) message, forward plaintext to clients
32 * POST-TESTING:
33 * - revisit API (which arguments are used, needed)?
34 * - add code to bound queue size when handling client's SEND message
35 * - add code to bound message queue size when passing messages to clients
36 * - add code to discard_expired_messages
37 * - add code to re-transmit key if first attempt failed
38 * + timeout on connect / key exchange, etc.
39 * + timeout for automatic re-try, etc.
40 * - add code to give up re-transmission of key if many attempts fail
41 * - add code to send PINGs if we are about to time-out otherwise
42 * ? add heuristic to do another send_key in "handle_set_key"
43 * in case previous attempt failed / didn't work / persist
44 * (but don't do it always to avoid storm of SET_KEY's going
45 * back and forth!) --- alternatively, add "status" field
46 * of the other peer to the set key message, that way we'd
47 * know for sure!
48 * - check that hostkey used by transport (for HELLOs) is the
49 * same as the hostkey that we are using!
50 * - free list of clients on exit
51 * - topology management:
52 * + bootstrapping (transport offer hello, plugins)
53 * + internal neighbour selection
54 * + update bandwidth usage statistics
55 * + bandwidth allocation (transport set quota)
56 * - optimize lookup (many O(n) list traversals
57 * could ideally be changed to O(1) hash map lookups)
58 */
59#include "platform.h"
60#include "gnunet_util_lib.h"
61#include "gnunet_hello_lib.h"
62#include "gnunet_peerinfo_service.h"
63#include "gnunet_protocols.h"
64#include "gnunet_signatures.h"
65#include "gnunet_transport_service.h"
66#include "core.h"
67
68
69/**
70 * Receive and send buffer windows grow over time. For
71 * how long can 'unused' bandwidth accumulate before we
72 * need to cap it? (specified in ms).
73 */
74#define MAX_WINDOW_TIME (5 * 60 * 1000)
75
76
77/**
78 * Amount of bytes per minute (in/out) to assume initially
79 * (before either peer has communicated any particular
80 * preference). Should be rather low.
81 */
82#define DEFAULT_BPM_IN_OUT 2048
83
84
85/**
86 * What is the maximum delay for a SET_KEY message?
87 */
88#define MAX_SET_KEY_DELAY GNUNET_TIME_UNIT_SECONDS
89
90
91/**
92 * What how long do we wait for SET_KEY confirmation initially?
93 */
94#define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
95
96
97/**
98 * What is the maximum delay for a PING message?
99 */
100#define MAX_PING_DELAY GNUNET_TIME_UNIT_SECONDS
101
102
103/**
104 * What is the maximum delay for a PONG message?
105 */
106#define MAX_PONG_DELAY GNUNET_TIME_UNIT_SECONDS
107
108
109/**
110 * What is the priority for a SET_KEY message?
111 */
112#define SET_KEY_PRIORITY 0xFFFFFF
113
114
115/**
116 * What is the priority for a PING message?
117 */
118#define PING_PRIORITY 0xFFFFFF
119
120
121/**
122 * What is the priority for a PONG message?
123 */
124#define PONG_PRIORITY 0xFFFFFF
125
126
127/**
128 * What is the maximum age of a message for us to consider
129 * processing it? Note that this looks at the timestamp used
130 * by the other peer, so clock skew between machines does
131 * come into play here. So this should be picked high enough
132 * so that a little bit of clock skew does not prevent peers
133 * from connecting to us.
134 */
135#define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS
136
137
138/**
139 * What is the maximum size for encrypted messages? Note that this
140 * number imposes a clear limit on the maximum size of any message.
141 * Set to a value close to 64k but not so close that transports will
142 * have trouble with their headers.
143 */
144#define MAX_ENCRYPTED_MESSAGE_SIZE (63 * 1024)
145
146
147/**
148 * State machine for our P2P encryption handshake. Everyone starts in
149 * "DOWN", if we receive the other peer's key (other peer initiated)
150 * we start in state RECEIVED (since we will immediately send our
151 * own); otherwise we start in SENT. If we get back a PONG from
152 * within either state, we move up to CONFIRMED (the PONG will always
153 * be sent back encrypted with the key we sent to the other peer).
154 */
155enum PeerStateMachine
156{
157 PEER_STATE_DOWN,
158 PEER_STATE_KEY_SENT,
159 PEER_STATE_KEY_RECEIVED,
160 PEER_STATE_KEY_CONFIRMED
161};
162
163
164/**
165 * Number of bytes (at the beginning) of "struct EncryptedMessage"
166 * that are NOT encrypted.
167 */
168#define ENCRYPTED_HEADER_SIZE (sizeof(struct GNUNET_MessageHeader) + sizeof(uint32_t) + sizeof(GNUNET_HashCode))
169
170/**
171 * Encapsulation for encrypted messages exchanged between
172 * peers. Followed by the actual encrypted data.
173 */
174struct EncryptedMessage
175{
176 /**
177 * Message type is either CORE_ENCRYPTED_MESSAGE.
178 */
179 struct GNUNET_MessageHeader header;
180
181 /**
182 * Always zero.
183 */
184 uint32_t reserved GNUNET_PACKED;
185
186 /**
187 * Hash of the plaintext, used to verify message integrity;
188 * ALSO used as the IV for the symmetric cipher! Everything
189 * after this hash will be encrypted. ENCRYPTED_HEADER_SIZE
190 * must be set to the offset of the next field.
191 */
192 GNUNET_HashCode plaintext_hash;
193
194 /**
195 * Sequence number, in network byte order. This field
196 * must be the first encrypted/decrypted field and the
197 * first byte that is hashed for the plaintext hash.
198 */
199 uint32_t sequence_number GNUNET_PACKED;
200
201 /**
202 * Desired bandwidth (how much we should send to this
203 * peer / how much is the sender willing to receive),
204 * in bytes per minute.
205 */
206 uint32_t inbound_bpm_limit GNUNET_PACKED;
207
208 /**
209 * Timestamp. Used to prevent reply of ancient messages
210 * (recent messages are caught with the sequence number).
211 */
212 struct GNUNET_TIME_AbsoluteNBO timestamp;
213
214};
215
216/**
217 * We're sending an (encrypted) PING to the other peer to check if he
218 * can decrypt. The other peer should respond with a PONG with the
219 * same content, except this time encrypted with the receiver's key.
220 */
221struct PingMessage
222{
223 /**
224 * Message type is either CORE_PING or CORE_PONG.
225 */
226 struct GNUNET_MessageHeader header;
227
228 /**
229 * Random number chosen to make reply harder.
230 */
231 uint32_t challenge GNUNET_PACKED;
232
233 /**
234 * Intended target of the PING, used primarily to check
235 * that decryption actually worked.
236 */
237 struct GNUNET_PeerIdentity target;
238};
239
240
241/**
242 * Message transmitted to set (or update) a session key.
243 */
244struct SetKeyMessage
245{
246
247 /**
248 * Message type is either CORE_SET_KEY.
249 */
250 struct GNUNET_MessageHeader header;
251
252 /**
253 * Status of the sender (should be in "enum PeerStateMachine"), nbo.
254 */
255 int32_t sender_status GNUNET_PACKED;
256
257 /**
258 * Purpose of the signature, will be
259 * GNUNET_SIGNATURE_PURPOSE_SET_KEY.
260 */
261 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
262
263 /**
264 * At what time was this key created?
265 */
266 struct GNUNET_TIME_AbsoluteNBO creation_time;
267
268 /**
269 * The encrypted session key.
270 */
271 struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
272
273 /**
274 * Who is the intended recipient?
275 */
276 struct GNUNET_PeerIdentity target;
277
278 /**
279 * Signature of the stuff above (starting at purpose).
280 */
281 struct GNUNET_CRYPTO_RsaSignature signature;
282
283};
284
285
286/**
287 * Message waiting for transmission. This struct
288 * is followed by the actual content of the message.
289 */
290struct MessageEntry
291{
292
293 /**
294 * We keep messages in a linked list (for now).
295 */
296 struct MessageEntry *next;
297
298 /**
299 * By when are we supposed to transmit this message?
300 */
301 struct GNUNET_TIME_Absolute deadline;
302
303 /**
304 * How important is this message to us?
305 */
306 unsigned int priority;
307
308 /**
309 * How long is the message? (number of bytes following
310 * the "struct MessageEntry", but not including the
311 * size of "struct MessageEntry" itself!)
312 */
313 uint16_t size;
314
315 /**
316 * Was this message selected for transmission in the
317 * current round? GNUNET_YES or GNUNET_NO.
318 */
319 int16_t do_transmit;
320
321};
322
323
324struct Neighbour
325{
326 /**
327 * We keep neighbours in a linked list (for now).
328 */
329 struct Neighbour *next;
330
331 /**
332 * Unencrypted messages destined for this peer.
333 */
334 struct MessageEntry *messages;
335
336 /**
337 * Head of the batched, encrypted message queue (already ordered,
338 * transmit starting with the head).
339 */
340 struct MessageEntry *encrypted_head;
341
342 /**
343 * Tail of the batched, encrypted message queue (already ordered,
344 * append new messages to tail)
345 */
346 struct MessageEntry *encrypted_tail;
347
348 /**
349 * Handle for pending requests for transmission to this peer
350 * with the transport service. NULL if no request is pending.
351 */
352 struct GNUNET_TRANSPORT_TransmitHandle *th;
353
354 /**
355 * Public key of the neighbour, NULL if we don't have it yet.
356 */
357 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
358
359 /**
360 * We received a PING message before we got the "public_key"
361 * (or the SET_KEY). We keep it here until we have a key
362 * to decrypt it. NULL if no PING is pending.
363 */
364 struct PingMessage *pending_ping;
365
366 /**
367 * Identity of the neighbour.
368 */
369 struct GNUNET_PeerIdentity peer;
370
371 /**
372 * Key we use to encrypt our messages for the other peer
373 * (initialized by us when we do the handshake).
374 */
375 struct GNUNET_CRYPTO_AesSessionKey encrypt_key;
376
377 /**
378 * Key we use to decrypt messages from the other peer
379 * (given to us by the other peer during the handshake).
380 */
381 struct GNUNET_CRYPTO_AesSessionKey decrypt_key;
382
383 /**
384 * ID of task used for re-trying plaintext scheduling.
385 */
386 GNUNET_SCHEDULER_TaskIdentifier retry_plaintext_task;
387
388 /**
389 * ID of task used for re-trying SET_KEY and PING message.
390 */
391 GNUNET_SCHEDULER_TaskIdentifier retry_set_key_task;
392
393 /**
394 * At what time did we generate our encryption key?
395 */
396 struct GNUNET_TIME_Absolute encrypt_key_created;
397
398 /**
399 * At what time did the other peer generate the decryption key?
400 */
401 struct GNUNET_TIME_Absolute decrypt_key_created;
402
403 /**
404 * At what time did we initially establish (as in, complete session
405 * key handshake) this connection? Should be zero if status != KEY_CONFIRMED.
406 */
407 struct GNUNET_TIME_Absolute time_established;
408
409 /**
410 * At what time did we last receive an encrypted message from the
411 * other peer? Should be zero if status != KEY_CONFIRMED.
412 */
413 struct GNUNET_TIME_Absolute last_activity;
414
415 /**
416 * Last latency observed from this peer.
417 */
418 struct GNUNET_TIME_Relative last_latency;
419
420 /**
421 * At what frequency are we currently re-trying SET KEY messages?
422 */
423 struct GNUNET_TIME_Relative set_key_retry_frequency;
424
425 /**
426 * Time of our last update to the "available_send_window".
427 */
428 struct GNUNET_TIME_Absolute last_asw_update;
429
430 /**
431 * Time of our last update to the "available_recv_window".
432 */
433 struct GNUNET_TIME_Absolute last_arw_update;
434
435 /**
436 * Number of bytes that we are eligible to transmit to this
437 * peer at this point. Incremented every minute by max_out_bpm,
438 * bounded by max_bpm (no back-log larger than MAX_BUF_FACT minutes,
439 * bandwidth-hogs are sampled at a frequency of about 78s!);
440 * may get negative if we have VERY high priority content.
441 */
442 long long available_send_window;
443
444 /**
445 * How much downstream capacity of this peer has been reserved for
446 * our traffic? (Our clients can request that a certain amount of
447 * bandwidth is available for replies to them; this value is used to
448 * make sure that this reserved amount of bandwidth is actually
449 * available).
450 */
451 long long available_recv_window;
452
453 /**
454 * How valueable were the messages of this peer recently?
455 */
456 double current_preference;
457
458 /**
459 * Bit map indicating which of the 32 sequence numbers before the last
460 * were received (good for accepting out-of-order packets and
461 * estimating reliability of the connection)
462 */
463 unsigned int last_packets_bitmap;
464
465 /**
466 * Number of messages in the message queue for this peer.
467 */
468 unsigned int message_queue_size;
469
470 /**
471 * last sequence number received on this connection (highest)
472 */
473 uint32_t last_sequence_number_received;
474
475 /**
476 * last sequence number transmitted
477 */
478 uint32_t last_sequence_number_sent;
479
480 /**
481 * Available bandwidth in for this peer (current target).
482 */
483 uint32_t bpm_in;
484
485 /**
486 * Available bandwidth out for this peer (current target).
487 */
488 uint32_t bpm_out;
489
490 /**
491 * Internal bandwidth limit set for this peer (initially
492 * typcially set to "-1"). "bpm_out" is MAX of
493 * "bpm_out_internal_limit" and "bpm_out_external_limit".
494 */
495 uint32_t bpm_out_internal_limit;
496
497 /**
498 * External bandwidth limit set for this peer by the
499 * peer that we are communicating with. "bpm_out" is MAX of
500 * "bpm_out_internal_limit" and "bpm_out_external_limit".
501 */
502 uint32_t bpm_out_external_limit;
503
504 /**
505 * What was our PING challenge number?
506 */
507 uint32_t ping_challenge;
508
509 /**
510 * What is our connection status?
511 */
512 enum PeerStateMachine status;
513
514};
515
516
517/**
518 * Events are messages for clients. The struct
519 * itself is followed by the actual message.
520 */
521struct Event
522{
523 /**
524 * This is a linked list.
525 */
526 struct Event *next;
527
528 /**
529 * Size of the message.
530 */
531 size_t size;
532
533 /**
534 * Could this event be dropped if this queue
535 * is getting too large? (NOT YET USED!)
536 */
537 int can_drop;
538
539};
540
541
542/**
543 * Data structure for each client connected to the core service.
544 */
545struct Client
546{
547 /**
548 * Clients are kept in a linked list.
549 */
550 struct Client *next;
551
552 /**
553 * Handle for the client with the server API.
554 */
555 struct GNUNET_SERVER_Client *client_handle;
556
557 /**
558 * Linked list of messages we still need to deliver to
559 * this client.
560 */
561 struct Event *event_head;
562
563 /**
564 * Tail of the linked list of events.
565 */
566 struct Event *event_tail;
567
568 /**
569 * Current transmit handle, NULL if no transmission request
570 * is pending.
571 */
572 struct GNUNET_NETWORK_TransmitHandle *th;
573
574 /**
575 * Array of the types of messages this peer cares
576 * about (with "tcnt" entries). Allocated as part
577 * of this client struct, do not free!
578 */
579 uint16_t *types;
580
581 /**
582 * Options for messages this client cares about,
583 * see GNUNET_CORE_OPTION_ values.
584 */
585 uint32_t options;
586
587 /**
588 * Number of types of incoming messages this client
589 * specifically cares about. Size of the "types" array.
590 */
591 unsigned int tcnt;
592
593};
594
595
596/**
597 * Our public key.
598 */
599static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
600
601/**
602 * Our identity.
603 */
604static struct GNUNET_PeerIdentity my_identity;
605
606/**
607 * Our private key.
608 */
609static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
610
611/**
612 * Our scheduler.
613 */
614struct GNUNET_SCHEDULER_Handle *sched;
615
616/**
617 * Our configuration.
618 */
619struct GNUNET_CONFIGURATION_Handle *cfg;
620
621/**
622 * Our server.
623 */
624static struct GNUNET_SERVER_Handle *server;
625
626/**
627 * Transport service.
628 */
629static struct GNUNET_TRANSPORT_Handle *transport;
630
631/**
632 * We keep neighbours in a linked list (for now).
633 */
634static struct Neighbour *neighbours;
635
636/**
637 * Linked list of our clients.
638 */
639static struct Client *clients;
640
641
642/**
643 * Recalculate the number of bytes we expect to
644 * receive or transmit in a given window.
645 *
646 * @param window pointer to the byte counter (updated)
647 * @param ts pointer to the timestamp (updated)
648 * @param bpm number of bytes per minute that should
649 * be added to the window.
650 */
651static void
652update_window (long long *window,
653 struct GNUNET_TIME_Absolute *ts, unsigned int bpm)
654{
655 struct GNUNET_TIME_Relative since;
656
657 since = GNUNET_TIME_absolute_get_duration (*ts);
658 if (since.value < 60 * 1000)
659 return; /* not even a minute has passed */
660 *ts = GNUNET_TIME_absolute_get ();
661 *window += (bpm * since.value) / 60 / 1000;
662 if (*window > MAX_WINDOW_TIME * bpm)
663 *window = MAX_WINDOW_TIME * bpm;
664}
665
666
667/**
668 * Find the entry for the given neighbour.
669 *
670 * @param peer identity of the neighbour
671 * @return NULL if we are not connected, otherwise the
672 * neighbour's entry.
673 */
674static struct Neighbour *
675find_neighbour (const struct GNUNET_PeerIdentity *peer)
676{
677 struct Neighbour *ret;
678
679 ret = neighbours;
680 while ((ret != NULL) &&
681 (0 != memcmp (&ret->peer,
682 peer, sizeof (struct GNUNET_PeerIdentity))))
683 ret = ret->next;
684 return ret;
685}
686
687
688/**
689 * Find the entry for the given client.
690 *
691 * @param client handle for the client
692 * @return NULL if we are not connected, otherwise the
693 * client's struct.
694 */
695static struct Client *
696find_client (const struct GNUNET_SERVER_Client *client)
697{
698 struct Client *ret;
699
700 ret = clients;
701 while ((ret != NULL) && (client != ret->client_handle))
702 ret = ret->next;
703 return ret;
704}
705
706
707/**
708 * If necessary, initiate a request with the server to
709 * transmit messages from the queue of the given client.
710 * @param client who to transfer messages to
711 */
712static void request_transmit (struct Client *client);
713
714
715/**
716 * Client is ready to receive data, provide it.
717 * @param cls closure
718 * @param size number of bytes available in buf
719 * @param buf where the callee should write the message
720 * @return number of bytes written to buf
721 */
722static size_t
723do_client_transmit (void *cls, size_t size, void *buf)
724{
725 struct Client *client = cls;
726 struct Event *e;
727 char *tgt;
728 size_t ret;
729
730 client->th = NULL;
731 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732 "Client ready to receive %u bytes.\n", size);
733 if (buf == NULL)
734 {
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736 "Failed to transmit data to client (disconnect)?\n");
737 return 0; /* we'll surely get a disconnect soon... */
738 }
739 tgt = buf;
740 ret = 0;
741 while ((NULL != (e = client->event_head)) && (e->size <= size))
742 {
743 memcpy (&tgt[ret], &e[1], e->size);
744 size -= e->size;
745 ret += e->size;
746 client->event_head = e->next;
747 GNUNET_free (e);
748 }
749 GNUNET_assert (ret > 0);
750 if (client->event_head == NULL)
751 client->event_tail = NULL;
752 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
753 "Transmitting %u bytes to client\n", ret);
754 request_transmit (client);
755 return ret;
756}
757
758
759/**
760 * If necessary, initiate a request with the server to
761 * transmit messages from the queue of the given client.
762 * @param client who to transfer messages to
763 */
764static void
765request_transmit (struct Client *client)
766{
767
768 if (NULL != client->th)
769 return; /* already pending */
770 if (NULL == client->event_head)
771 return; /* no more events pending */
772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
773 "Asking server to transmit %u bytes to client\n",
774 client->event_head->size);
775 client->th
776 = GNUNET_SERVER_notify_transmit_ready (client->client_handle,
777 client->event_head->size,
778 GNUNET_TIME_UNIT_FOREVER_REL,
779 &do_client_transmit, client);
780}
781
782
783/**
784 * Send a message to one of our clients.
785 * @param client target for the message
786 * @param msg message to transmit
787 * @param can_drop could this message be dropped if the
788 * client's queue is getting too large?
789 */
790static void
791send_to_client (struct Client *client,
792 const struct GNUNET_MessageHeader *msg, int can_drop)
793{
794 struct Event *e;
795 uint16_t msize;
796
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798 "Preparing to send message of type %u to client.\n",
799 ntohs (msg->type));
800 msize = ntohs (msg->size);
801 e = GNUNET_malloc (sizeof (struct Event) + msize);
802 /* append */
803 if (client->event_tail != NULL)
804 client->event_tail->next = e;
805 else
806 client->event_head = e;
807 client->event_tail = e;
808 e->can_drop = can_drop;
809 e->size = msize;
810 memcpy (&e[1], msg, msize);
811 request_transmit (client);
812}
813
814
815/**
816 * Send a message to all of our current clients.
817 */
818static void
819send_to_all_clients (const struct GNUNET_MessageHeader *msg, int can_drop)
820{
821 struct Client *c;
822
823 c = clients;
824 while (c != NULL)
825 {
826 send_to_client (c, msg, can_drop);
827 c = c->next;
828 }
829}
830
831
832/**
833 * Handle CORE_INIT request.
834 */
835static void
836handle_client_init (void *cls,
837 struct GNUNET_SERVER_Handle *server,
838 struct GNUNET_SERVER_Client *client,
839 const struct GNUNET_MessageHeader *message)
840{
841 const struct InitMessage *im;
842 struct InitReplyMessage irm;
843 struct Client *c;
844 uint16_t msize;
845 const uint16_t *types;
846 struct Neighbour *n;
847 struct ConnectNotifyMessage cnm;
848
849 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
850 "Client connecting to core service with `%s' message\n",
851 "INIT");
852 /* check that we don't have an entry already */
853 c = clients;
854 while (c != NULL)
855 {
856 if (client == c->client_handle)
857 {
858 GNUNET_break (0);
859 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
860 return;
861 }
862 c = c->next;
863 }
864 msize = ntohs (message->size);
865 if (msize < sizeof (struct InitMessage))
866 {
867 GNUNET_break (0);
868 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
869 return;
870 }
871 im = (const struct InitMessage *) message;
872 types = (const uint16_t *) &im[1];
873 msize -= sizeof (struct InitMessage);
874 c = GNUNET_malloc (sizeof (struct Client) + msize);
875 c->client_handle = client;
876 c->next = clients;
877 clients = c;
878 memcpy (&c[1], types, msize);
879 c->types = (uint16_t *) & c[1];
880 c->options = ntohl (im->options);
881 c->tcnt = msize / sizeof (uint16_t);
882 /* send init reply message */
883 irm.header.size = htons (sizeof (struct InitReplyMessage));
884 irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
885 irm.reserved = htonl (0);
886 memcpy (&irm.publicKey,
887 &my_public_key,
888 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
890 "Sending `%s' message to client.\n", "INIT_REPLY");
891 send_to_client (c, &irm.header, GNUNET_NO);
892 /* notify new client about existing neighbours */
893 cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
894 cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
895 n = neighbours;
896 while (n != NULL)
897 {
898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
899 "Sending `%s' message to client.\n", "NOTIFY_CONNECT");
900 cnm.bpm_available = htonl (n->bpm_out);
901 cnm.last_activity = GNUNET_TIME_absolute_hton (n->last_activity);
902 cnm.peer = n->peer;
903 send_to_client (c, &cnm.header, GNUNET_NO);
904 n = n->next;
905 }
906 GNUNET_SERVER_receive_done (client, GNUNET_OK);
907}
908
909
910/**
911 * A client disconnected, clean up.
912 *
913 * @param cls closure
914 * @param client identification of the client
915 */
916static void
917handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
918{
919 struct Client *pos;
920 struct Client *prev;
921
922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
923 "Client has disconnected from core service.\n");
924 prev = NULL;
925 pos = clients;
926 while (pos != NULL)
927 {
928 if (client == pos->client_handle)
929 {
930 if (prev == NULL)
931 clients = pos->next;
932 else
933 prev->next = pos->next;
934 if (pos->th != NULL)
935 GNUNET_NETWORK_notify_transmit_ready_cancel (pos->th);
936 GNUNET_free (pos);
937 return;
938 }
939 prev = pos;
940 pos = pos->next;
941 }
942 /* client never sent INIT */
943}
944
945
946/**
947 * Handle REQUEST_CONFIGURE request.
948 */
949static void
950handle_client_request_configure (void *cls,
951 struct GNUNET_SERVER_Handle *server,
952 struct GNUNET_SERVER_Client *client,
953 const struct GNUNET_MessageHeader *message)
954{
955 const struct RequestConfigureMessage *rcm;
956 struct Neighbour *n;
957 struct ConfigurationInfoMessage cim;
958 struct Client *c;
959 int reserv;
960
961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962 "Core service receives `%s' request.\n", "CONFIGURE");
963 rcm = (const struct RequestConfigureMessage *) message;
964 n = find_neighbour (&rcm->peer);
965 memset (&cim, 0, sizeof (cim));
966 if ((n != NULL) && (n->status == PEER_STATE_KEY_CONFIRMED))
967 {
968 n->bpm_out_internal_limit = ntohl (rcm->limit_outbound_bpm);
969 n->bpm_out = GNUNET_MAX (n->bpm_out_internal_limit,
970 n->bpm_out_external_limit);
971 reserv = ntohl (rcm->reserve_inbound);
972 if (reserv < 0)
973 {
974 n->available_recv_window += reserv;
975 }
976 else if (reserv > 0)
977 {
978 update_window (&n->available_recv_window,
979 &n->last_arw_update, n->bpm_in);
980 if (n->available_recv_window < reserv)
981 reserv = n->available_recv_window;
982 n->available_recv_window -= reserv;
983 }
984 n->current_preference += rcm->preference_change;
985 if (n->current_preference < 0)
986 n->current_preference = 0;
987 cim.reserved_amount = htonl (reserv);
988 cim.bpm_in = htonl (n->bpm_in);
989 cim.bpm_out = htonl (n->bpm_out);
990 cim.latency = GNUNET_TIME_relative_hton (n->last_latency);
991 cim.preference = n->current_preference;
992 }
993 cim.header.size = htons (sizeof (struct ConfigurationInfoMessage));
994 cim.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO);
995 cim.peer = rcm->peer;
996 c = find_client (client);
997 if (c == NULL)
998 {
999 GNUNET_break (0);
1000 return;
1001 }
1002 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1003 "Sending `%s' message to client.\n", "CONFIGURATION_INFO");
1004 send_to_client (c, &cim.header, GNUNET_NO);
1005}
1006
1007
1008/**
1009 * Check if we have encrypted messages for the specified neighbour
1010 * pending, and if so, check with the transport about sending them
1011 * out.
1012 *
1013 * @param n neighbour to check.
1014 */
1015static void process_encrypted_neighbour_queue (struct Neighbour *n);
1016
1017
1018/**
1019 * Function called when the transport service is ready to
1020 * receive an encrypted message for the respective peer
1021 *
1022 * @param cls neighbour to use message from
1023 * @param size number of bytes we can transmit
1024 * @param buf where to copy the message
1025 * @return number of bytes transmitted
1026 */
1027static size_t
1028notify_encrypted_transmit_ready (void *cls, size_t size, void *buf)
1029{
1030 struct Neighbour *n = cls;
1031 struct MessageEntry *m;
1032 size_t ret;
1033 char *cbuf;
1034
1035 n->th = NULL;
1036 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037 "Transport ready to receive %u bytes for `%4s'\n",
1038 size, GNUNET_i2s (&n->peer));
1039 GNUNET_assert (NULL != (m = n->encrypted_head));
1040 n->encrypted_head = m->next;
1041 if (m->next == NULL)
1042 n->encrypted_tail = NULL;
1043 ret = 0;
1044 cbuf = buf;
1045 if (buf != NULL)
1046 {
1047 GNUNET_assert (size >= m->size);
1048 memcpy (cbuf, &m[1], m->size);
1049 ret = m->size;
1050 process_encrypted_neighbour_queue (n);
1051 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1052 "Copied message of type %u and size %u into transport buffer for `%4s'\n",
1053 ntohs (((struct GNUNET_MessageHeader *) &m[1])->type),
1054 ret, GNUNET_i2s (&n->peer));
1055 }
1056 else
1057 {
1058 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1059 "Transmission for message of type %u and size %u failed\n",
1060 ntohs (((struct GNUNET_MessageHeader *) &m[1])->type),
1061 m->size);
1062 }
1063 GNUNET_free (m);
1064 return ret;
1065}
1066
1067
1068/**
1069 * Check if we have plaintext messages for the specified neighbour
1070 * pending, and if so, consider batching and encrypting them (and
1071 * then trigger processing of the encrypted queue if needed).
1072 *
1073 * @param n neighbour to check.
1074 */
1075static void process_plaintext_neighbour_queue (struct Neighbour *n);
1076
1077
1078/**
1079 * Check if we have encrypted messages for the specified neighbour
1080 * pending, and if so, check with the transport about sending them
1081 * out.
1082 *
1083 * @param n neighbour to check.
1084 */
1085static void
1086process_encrypted_neighbour_queue (struct Neighbour *n)
1087{
1088 if (n->th != NULL)
1089 {
1090 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1091 "Asked to process encrypted queue, but request already pending.\n");
1092 return; /* already pending */
1093 }
1094 if (n->encrypted_head == NULL)
1095 {
1096 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1097 "Encrypted queue empty, trying plaintext queue instead.\n");
1098 process_plaintext_neighbour_queue (n);
1099 return;
1100 }
1101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102 "Asking transport for transmission of %u bytes to `%4s' in next %llu ms\n",
1103 n->encrypted_head->size,
1104 GNUNET_i2s (&n->peer),
1105 GNUNET_TIME_absolute_get_remaining (n->encrypted_head->
1106 deadline).value);
1107 n->th =
1108 GNUNET_TRANSPORT_notify_transmit_ready (transport, &n->peer,
1109 n->encrypted_head->size,
1110 GNUNET_TIME_absolute_get_remaining
1111 (n->encrypted_head->deadline),
1112 &notify_encrypted_transmit_ready,
1113 n);
1114 if (n->th == NULL)
1115 {
1116 /* message request too large (oops) */
1117 GNUNET_break (0);
1118 /* FIXME: handle error somehow! */
1119 }
1120}
1121
1122
1123/**
1124 * Decrypt size bytes from in and write the result to out. Use the
1125 * key for inbound traffic of the given neighbour. This function does
1126 * NOT do any integrity-checks on the result.
1127 *
1128 * @param n neighbour we are receiving from
1129 * @param iv initialization vector to use
1130 * @param in ciphertext
1131 * @param out plaintext
1132 * @param size size of in/out
1133 * @return GNUNET_OK on success
1134 */
1135static int
1136do_decrypt (struct Neighbour *n,
1137 const GNUNET_HashCode * iv,
1138 const void *in, void *out, size_t size)
1139{
1140 if (size != (uint16_t) size)
1141 {
1142 GNUNET_break (0);
1143 return GNUNET_NO;
1144 }
1145 if ((n->status != PEER_STATE_KEY_RECEIVED) &&
1146 (n->status != PEER_STATE_KEY_CONFIRMED))
1147 {
1148 GNUNET_break_op (0);
1149 return GNUNET_SYSERR;
1150 }
1151 if (size !=
1152 GNUNET_CRYPTO_aes_decrypt (&n->decrypt_key,
1153 in,
1154 (uint16_t) size,
1155 (const struct
1156 GNUNET_CRYPTO_AesInitializationVector *) iv,
1157 out))
1158 {
1159 GNUNET_break (0);
1160 return GNUNET_SYSERR;
1161 }
1162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163 "Decrypted %u bytes from `%4s' using key %u\n",
1164 size, GNUNET_i2s (&n->peer), n->decrypt_key.crc32);
1165 return GNUNET_OK;
1166}
1167
1168
1169/**
1170 * Encrypt size bytes from in and write the result to out. Use the
1171 * key for outbound traffic of the given neighbour.
1172 *
1173 * @param n neighbour we are sending to
1174 * @param iv initialization vector to use
1175 * @param in ciphertext
1176 * @param out plaintext
1177 * @param size size of in/out
1178 * @return GNUNET_OK on success
1179 */
1180static int
1181do_encrypt (struct Neighbour *n,
1182 const GNUNET_HashCode * iv,
1183 const void *in, void *out, size_t size)
1184{
1185 if (size != (uint16_t) size)
1186 {
1187 GNUNET_break (0);
1188 return GNUNET_NO;
1189 }
1190 GNUNET_assert (size ==
1191 GNUNET_CRYPTO_aes_encrypt (in,
1192 (uint16_t) size,
1193 &n->encrypt_key,
1194 (const struct
1195 GNUNET_CRYPTO_AesInitializationVector
1196 *) iv, out));
1197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1198 "Encrypted %u bytes for `%4s' using key %u\n", size,
1199 GNUNET_i2s (&n->peer), n->encrypt_key.crc32);
1200 return GNUNET_OK;
1201}
1202
1203
1204/**
1205 * Select messages for transmission. This heuristic uses a combination
1206 * of earliest deadline first (EDF) scheduling (with bounded horizon)
1207 * and priority-based discard (in case no feasible schedule exist) and
1208 * speculative optimization (defer any kind of transmission until
1209 * we either create a batch of significant size, 25% of max, or until
1210 * we are close to a deadline). Furthermore, when scheduling the
1211 * heuristic also packs as many messages into the batch as possible,
1212 * starting with those with the earliest deadline. Yes, this is fun.
1213 *
1214 * @param n neighbour to select messages from
1215 * @param size number of bytes to select for transmission
1216 * @param retry_time set to the time when we should try again
1217 * (only valid if this function returns zero)
1218 * @return number of bytes selected, or 0 if we decided to
1219 * defer scheduling overall; in that case, retry_time is set.
1220 */
1221static size_t
1222select_messages (struct Neighbour *n,
1223 size_t size, struct GNUNET_TIME_Relative *retry_time)
1224{
1225 struct MessageEntry *pos;
1226 struct MessageEntry *min;
1227 struct MessageEntry *last;
1228 unsigned int min_prio;
1229 struct GNUNET_TIME_Absolute t;
1230 struct GNUNET_TIME_Absolute now;
1231 uint64_t delta;
1232 uint64_t avail;
1233 unsigned long long slack; /* how long could we wait before missing deadlines? */
1234 size_t off;
1235 int discard_low_prio;
1236
1237 GNUNET_assert (NULL != n->messages);
1238 now = GNUNET_TIME_absolute_get ();
1239 /* last entry in linked list of messages processed */
1240 last = NULL;
1241 /* should we remove the entry with the lowest
1242 priority from consideration for scheduling at the
1243 end of the loop? */
1244 discard_low_prio = GNUNET_YES;
1245 while (GNUNET_YES == discard_low_prio)
1246 {
1247 min = NULL;
1248 min_prio = -1;
1249 discard_low_prio = GNUNET_NO;
1250 /* number of bytes available for transmission at time "t" */
1251 avail = n->available_send_window;
1252 t = n->last_asw_update;
1253 /* how many bytes have we (hyptothetically) scheduled so far */
1254 off = 0;
1255 /* maximum time we can wait before transmitting anything
1256 and still make all of our deadlines */
1257 slack = -1;
1258
1259 pos = n->messages;
1260 /* note that we use "*2" here because we want to look
1261 a bit further into the future; much more makes no
1262 sense since new message might be scheduled in the
1263 meantime... */
1264 while ((pos != NULL) && (off < size * 2))
1265 {
1266 if (pos->do_transmit == GNUNET_YES)
1267 {
1268 /* already removed from consideration */
1269 pos = pos->next;
1270 continue;
1271 }
1272 if (discard_low_prio == GNUNET_NO)
1273 {
1274 delta = pos->deadline.value;
1275 if (delta < t.value)
1276 delta = 0;
1277 else
1278 delta = t.value - delta;
1279 avail += delta * n->bpm_out / 1000 / 60;
1280 if (avail < pos->size)
1281 {
1282 discard_low_prio = GNUNET_YES; /* we could not schedule this one! */
1283 }
1284 else
1285 {
1286 avail -= pos->size;
1287 /* update slack, considering both its absolute deadline
1288 and relative deadlines caused by other messages
1289 with their respective load */
1290 slack = GNUNET_MIN (slack, avail / n->bpm_out);
1291 if (pos->deadline.value < now.value)
1292 slack = 0;
1293 else
1294 slack =
1295 GNUNET_MIN (slack, pos->deadline.value - now.value);
1296 }
1297 }
1298 off += pos->size;
1299 t.value = GNUNET_MAX (pos->deadline.value, t.value);
1300 if (pos->priority <= min_prio)
1301 {
1302 /* update min for discard */
1303 min_prio = pos->priority;
1304 min = pos;
1305 }
1306 pos = pos->next;
1307 }
1308 if (discard_low_prio)
1309 {
1310 /* remove lowest-priority entry from consideration */
1311 min->do_transmit = GNUNET_YES; /* means: discard (for now) */
1312 }
1313 last = pos;
1314 }
1315 /* guard against sending "tiny" messages with large headers without
1316 urgent deadlines */
1317 if ((slack > 1000) && (size > 4 * off))
1318 {
1319 /* less than 25% of message would be filled with
1320 deadlines still being met if we delay by one
1321 second or more; so just wait for more data */
1322 retry_time->value = slack / 2;
1323 /* reset do_transmit values for next time */
1324 while (pos != last)
1325 {
1326 pos->do_transmit = GNUNET_NO;
1327 pos = pos->next;
1328 }
1329 return 0;
1330 }
1331 /* select marked messages (up to size) for transmission */
1332 off = 0;
1333 pos = n->messages;
1334 while (pos != last)
1335 {
1336 if ((pos->size <= size) && (pos->do_transmit == GNUNET_NO))
1337 {
1338 pos->do_transmit = GNUNET_YES; /* mark for transmission */
1339 off += pos->size;
1340 size -= pos->size;
1341 }
1342 else
1343 pos->do_transmit = GNUNET_NO; /* mark for not transmitting! */
1344 pos = pos->next;
1345 }
1346 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1347 "Selected %u bytes of plaintext messages for transmission to `%4s'.\n",
1348 off, GNUNET_i2s (&n->peer));
1349 return off;
1350}
1351
1352
1353/**
1354 * Batch multiple messages into a larger buffer.
1355 *
1356 * @param n neighbour to take messages from
1357 * @param buf target buffer
1358 * @param size size of buf
1359 * @param deadline set to transmission deadline for the result
1360 * @param retry_time set to the time when we should try again
1361 * (only valid if this function returns zero)
1362 * @param priority set to the priority of the batch
1363 * @return number of bytes written to buf (can be zero)
1364 */
1365static size_t
1366batch_message (struct Neighbour *n,
1367 char *buf,
1368 size_t size,
1369 struct GNUNET_TIME_Absolute *deadline,
1370 struct GNUNET_TIME_Relative *retry_time,
1371 unsigned int *priority)
1372{
1373 struct MessageEntry *pos;
1374 struct MessageEntry *prev;
1375 struct MessageEntry *next;
1376 size_t ret;
1377
1378 ret = 0;
1379 *priority = 0;
1380 *deadline = GNUNET_TIME_UNIT_FOREVER_ABS;
1381 *retry_time = GNUNET_TIME_UNIT_FOREVER_REL;
1382 if (0 == select_messages (n, size, retry_time))
1383 {
1384 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1385 "No messages selected, will try again in %llu ms\n",
1386 retry_time->value);
1387 return 0;
1388 }
1389 pos = n->messages;
1390 prev = NULL;
1391 while ((pos != NULL) && (size >= sizeof (struct GNUNET_MessageHeader)))
1392 {
1393 next = pos->next;
1394 if (GNUNET_YES == pos->do_transmit)
1395 {
1396 GNUNET_assert (pos->size <= size);
1397 memcpy (&buf[ret], &pos[1], pos->size);
1398 ret += pos->size;
1399 size -= pos->size;
1400 *priority += pos->priority;
1401 deadline->value = GNUNET_MIN (deadline->value, pos->deadline.value);
1402 GNUNET_free (pos);
1403 if (prev == NULL)
1404 n->messages = next;
1405 else
1406 prev->next = next;
1407 }
1408 else
1409 {
1410 prev = pos;
1411 }
1412 pos = next;
1413 }
1414 return ret;
1415}
1416
1417
1418/**
1419 * Remove messages with deadlines that have long expired from
1420 * the queue.
1421 *
1422 * @param n neighbour to inspect
1423 */
1424static void
1425discard_expired_messages (struct Neighbour *n)
1426{
1427 /* FIXME */
1428}
1429
1430
1431/**
1432 * Signature of the main function of a task.
1433 *
1434 * @param cls closure
1435 * @param tc context information (why was this task triggered now)
1436 */
1437static void
1438retry_plaintext_processing (void *cls,
1439 const struct GNUNET_SCHEDULER_TaskContext *tc)
1440{
1441 struct Neighbour *n = cls;
1442
1443 n->retry_plaintext_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1444 process_plaintext_neighbour_queue (n);
1445}
1446
1447
1448/**
1449 * Send our key (and encrypted PING) to the other peer.
1450 *
1451 * @param n the other peer
1452 */
1453static void send_key (struct Neighbour *n);
1454
1455
1456/**
1457 * Check if we have plaintext messages for the specified neighbour
1458 * pending, and if so, consider batching and encrypting them (and
1459 * then trigger processing of the encrypted queue if needed).
1460 *
1461 * @param n neighbour to check.
1462 */
1463static void
1464process_plaintext_neighbour_queue (struct Neighbour *n)
1465{
1466 char pbuf[MAX_ENCRYPTED_MESSAGE_SIZE]; /* plaintext */
1467 size_t used;
1468 size_t esize;
1469 struct EncryptedMessage *em; /* encrypted message */
1470 struct EncryptedMessage *ph; /* plaintext header */
1471 struct MessageEntry *me;
1472 unsigned int priority;
1473 struct GNUNET_TIME_Absolute deadline;
1474 struct GNUNET_TIME_Relative retry_time;
1475
1476 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1477 "Processing plaintext message queue for `%4s', scheduling messages.\n",
1478 GNUNET_i2s (&n->peer));
1479 if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1480 {
1481 GNUNET_SCHEDULER_cancel (sched, n->retry_plaintext_task);
1482 n->retry_plaintext_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1483 }
1484 switch (n->status)
1485 {
1486 case PEER_STATE_DOWN:
1487 send_key (n);
1488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1489 "Not yet connected, deferring processing of plaintext messages.\n");
1490 return;
1491 case PEER_STATE_KEY_SENT:
1492 GNUNET_assert (n->retry_set_key_task !=
1493 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1494 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1495 "Not yet connected, deferring processing of plaintext messages.\n");
1496 return;
1497 case PEER_STATE_KEY_RECEIVED:
1498 GNUNET_assert (n->retry_set_key_task !=
1499 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1501 "Not yet connected, deferring processing of plaintext messages.\n");
1502 return;
1503 case PEER_STATE_KEY_CONFIRMED:
1504 /* ready to continue */
1505 break;
1506 }
1507 if (n->messages == NULL)
1508 {
1509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1510 "Plaintext message queue is empty.\n");
1511 return; /* no pending messages */
1512 }
1513 discard_expired_messages (n);
1514 if (n->encrypted_head != NULL)
1515 {
1516 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1517 "Encrypted message queue is still full, delaying plaintext processing.\n");
1518 return; /* wait for messages already encrypted to be
1519 processed first! */
1520 }
1521 ph = (struct EncryptedMessage *) pbuf;
1522 deadline = GNUNET_TIME_UNIT_FOREVER_ABS;
1523 priority = 0;
1524 used = sizeof (struct EncryptedMessage);
1525
1526 used += batch_message (n,
1527 &pbuf[used],
1528 MAX_ENCRYPTED_MESSAGE_SIZE - used,
1529 &deadline, &retry_time, &priority);
1530 if (used == sizeof (struct EncryptedMessage))
1531 {
1532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1533 "No messages selected for processing at this time, will try again later.\n");
1534 /* no messages selected for sending, try again later... */
1535 n->retry_plaintext_task =
1536 GNUNET_SCHEDULER_add_delayed (sched,
1537 GNUNET_NO,
1538 GNUNET_SCHEDULER_PRIORITY_IDLE,
1539 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1540 retry_time,
1541 &retry_plaintext_processing, n);
1542 return;
1543 }
1544
1545 ph->sequence_number = htonl (++n->last_sequence_number_sent);
1546 ph->inbound_bpm_limit = htonl (n->bpm_in);
1547 ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1548
1549 /* setup encryption message header */
1550 me = GNUNET_malloc (sizeof (struct MessageEntry) + used);
1551 me->deadline = deadline;
1552 me->priority = priority;
1553 me->size = used;
1554 em = (struct EncryptedMessage *) &me[1];
1555 em->header.size = htons (used);
1556 em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE);
1557 em->reserved = htonl (0);
1558 esize = used - ENCRYPTED_HEADER_SIZE;
1559 GNUNET_CRYPTO_hash (&ph->sequence_number, esize, &em->plaintext_hash);
1560 /* encrypt */
1561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1562 "Encrypting plaintext messages for transmission.\n");
1563 GNUNET_assert (GNUNET_OK ==
1564 do_encrypt (n,
1565 &em->plaintext_hash,
1566 &ph->sequence_number,
1567 &em->sequence_number, esize));
1568 /* append to transmission list */
1569 if (n->encrypted_tail == NULL)
1570 n->encrypted_head = me;
1571 else
1572 n->encrypted_tail->next = me;
1573 n->encrypted_tail = me;
1574 process_encrypted_neighbour_queue (n);
1575}
1576
1577
1578/**
1579 * Handle CORE_SEND request.
1580 */
1581static void
1582handle_client_send (void *cls,
1583 struct GNUNET_SERVER_Handle *server,
1584 struct GNUNET_SERVER_Client *client,
1585 const struct GNUNET_MessageHeader *message);
1586
1587
1588/**
1589 * Function called to notify us that we either succeeded
1590 * or failed to connect (at the transport level) to another
1591 * peer. We should either free the message we were asked
1592 * to transmit or re-try adding it to the queue.
1593 *
1594 * @param cls closure
1595 * @param size number of bytes available in buf
1596 * @param buf where the callee should write the message
1597 * @return number of bytes written to buf
1598 */
1599static size_t
1600send_connect_continuation (void *cls, size_t size, void *buf)
1601{
1602 struct SendMessage *sm = cls;
1603
1604 if (buf == NULL)
1605 {
1606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1607 "Asked to send message to disconnected peer `%4s' and connection failed. Discarding message.\n",
1608 GNUNET_i2s (&sm->peer));
1609 GNUNET_free (sm);
1610 return 0;
1611 }
1612 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1613 "Connection to peer `%4s' succeeded, retrying original send request\n",
1614 GNUNET_i2s (&sm->peer));
1615 handle_client_send (NULL, NULL, NULL, &sm->header);
1616 GNUNET_free (sm);
1617 return 0;
1618}
1619
1620
1621/**
1622 * Handle CORE_SEND request.
1623 */
1624static void
1625handle_client_send (void *cls,
1626 struct GNUNET_SERVER_Handle *server,
1627 struct GNUNET_SERVER_Client *client,
1628 const struct GNUNET_MessageHeader *message)
1629{
1630 const struct SendMessage *sm;
1631 struct SendMessage *smc;
1632 const struct GNUNET_MessageHeader *mh;
1633 struct Neighbour *n;
1634 struct MessageEntry *pred;
1635 struct MessageEntry *pos;
1636 struct MessageEntry *e;
1637 uint16_t msize;
1638
1639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1640 "Core service receives `%s' request.\n", "SEND");
1641 msize = ntohs (message->size);
1642 if (msize <
1643 sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader))
1644 {
1645 GNUNET_break (0);
1646 if (client != NULL)
1647 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1648 return;
1649 }
1650 sm = (const struct SendMessage *) message;
1651 msize -= sizeof (struct SendMessage);
1652 mh = (const struct GNUNET_MessageHeader *) &sm[1];
1653 if (msize != ntohs (mh->size))
1654 {
1655 GNUNET_break (0);
1656 if (client != NULL)
1657 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1658 return;
1659 }
1660 n = find_neighbour (&sm->peer);
1661 if (n == NULL)
1662 {
1663 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1664 "Not yet connected to `%4s', will try to establish connection\n",
1665 GNUNET_i2s (&sm->peer));
1666 msize += sizeof (struct SendMessage);
1667 /* ask transport to connect to the peer */
1668 /* FIXME: this code does not handle the
1669 case where we get multiple SendMessages before
1670 transport responds to this request;
1671 => need to track pending requests! */
1672 smc = GNUNET_malloc (msize);
1673 memcpy (smc, sm, msize);
1674 GNUNET_TRANSPORT_notify_transmit_ready (transport,
1675 &sm->peer,
1676 0,
1677 GNUNET_TIME_absolute_get_remaining
1678 (GNUNET_TIME_absolute_ntoh
1679 (sm->deadline)),
1680 &send_connect_continuation,
1681 smc);
1682 if (client != NULL)
1683 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1684 return;
1685 }
1686 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1687 "Core queues %u bytes of plaintext data for transmission to `%4s'.\n",
1688 msize, GNUNET_i2s (&sm->peer));
1689 /* FIXME: consider bounding queue size */
1690 e = GNUNET_malloc (sizeof (struct MessageEntry) + msize);
1691 e->deadline = GNUNET_TIME_absolute_ntoh (sm->deadline);
1692 e->priority = ntohl (sm->priority);
1693 e->size = msize;
1694 memcpy (&e[1], mh, msize);
1695
1696 /* insert, keep list sorted by deadline */
1697 pred = NULL;
1698 pos = n->messages;
1699 while ((pos != NULL) && (pos->deadline.value < e->deadline.value))
1700 {
1701 pred = pos;
1702 pos = pos->next;
1703 }
1704 if (pred == NULL)
1705 n->messages = e;
1706 else
1707 pred->next = e;
1708 e->next = pos;
1709
1710 /* consider scheduling now */
1711 process_plaintext_neighbour_queue (n);
1712 if (client != NULL)
1713 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1714}
1715
1716
1717/**
1718 * List of handlers for the messages understood by this
1719 * service.
1720 */
1721static struct GNUNET_SERVER_MessageHandler handlers[] = {
1722 {&handle_client_init, NULL,
1723 GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
1724 {&handle_client_request_configure, NULL,
1725 GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE,
1726 sizeof (struct RequestConfigureMessage)},
1727 {&handle_client_send, NULL,
1728 GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
1729 {NULL, NULL, 0, 0}
1730};
1731
1732
1733/**
1734 * PEERINFO is giving us a HELLO for a peer. Add the
1735 * public key to the neighbour's struct and retry
1736 * send_key. Or, if we did not get a HELLO, just do
1737 * nothing.
1738 *
1739 * @param cls NULL
1740 * @param peer the peer for which this is the HELLO
1741 * @param hello HELLO message of that peer
1742 * @param trust amount of trust we currently have in that peer
1743 */
1744static void
1745process_hello_retry_send_key (void *cls,
1746 const struct GNUNET_PeerIdentity *peer,
1747 const struct GNUNET_HELLO_Message *hello,
1748 uint32_t trust)
1749{
1750 struct Neighbour *n;
1751
1752 if (peer == NULL)
1753 return;
1754 n = find_neighbour (peer);
1755 if (n == NULL)
1756 return;
1757 if (n->public_key != NULL)
1758 return;
1759 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1760 "Received new HELLO for `%4s', initiating key exchange.\n",
1761 GNUNET_i2s (peer));
1762 n->public_key =
1763 GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
1764 if (GNUNET_OK != GNUNET_HELLO_get_key (hello, n->public_key))
1765 {
1766 GNUNET_free (n->public_key);
1767 n->public_key = NULL;
1768 return;
1769 }
1770 send_key (n);
1771}
1772
1773
1774/**
1775 * Task that will retry "send_key" if our previous attempt failed
1776 * to yield a PONG.
1777 */
1778static void
1779set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1780{
1781 struct Neighbour *n = cls;
1782
1783 GNUNET_assert (n->status != PEER_STATE_KEY_CONFIRMED);
1784 n->retry_set_key_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1785 n->set_key_retry_frequency =
1786 GNUNET_TIME_relative_multiply (n->set_key_retry_frequency, 2);
1787 send_key (n);
1788}
1789
1790
1791/**
1792 * Send our key (and encrypted PING) to the other peer.
1793 *
1794 * @param n the other peer
1795 */
1796static void
1797send_key (struct Neighbour *n)
1798{
1799 struct SetKeyMessage *sm;
1800 struct MessageEntry *me;
1801 struct PingMessage pp;
1802 struct PingMessage *pm;
1803
1804 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1805 "Asked to perform key exchange with `%4s'.\n",
1806 GNUNET_i2s (&n->peer));
1807 if (n->public_key == NULL)
1808 {
1809 /* lookup n's public key, then try again */
1810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1811 "Lacking public key for `%4s', trying to obtain one.\n",
1812 GNUNET_i2s (&n->peer));
1813 GNUNET_PEERINFO_for_all (cfg,
1814 sched,
1815 &n->peer,
1816 0,
1817 GNUNET_TIME_UNIT_MINUTES,
1818 &process_hello_retry_send_key, NULL);
1819 return;
1820 }
1821 /* first, set key message */
1822 me = GNUNET_malloc (sizeof (struct MessageEntry) +
1823 sizeof (struct SetKeyMessage));
1824 me->deadline = GNUNET_TIME_relative_to_absolute (MAX_SET_KEY_DELAY);
1825 me->priority = SET_KEY_PRIORITY;
1826 me->size = sizeof (struct SetKeyMessage);
1827 if (n->encrypted_head == NULL)
1828 n->encrypted_head = me;
1829 else
1830 n->encrypted_tail->next = me;
1831 n->encrypted_tail = me;
1832 sm = (struct SetKeyMessage *) &me[1];
1833 sm->header.size = htons (sizeof (struct SetKeyMessage));
1834 sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SET_KEY);
1835 sm->sender_status = htonl ((int32_t) ((n->status == PEER_STATE_DOWN) ?
1836 PEER_STATE_KEY_SENT : n->status));
1837 sm->purpose.size =
1838 htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
1839 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
1840 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) +
1841 sizeof (struct GNUNET_PeerIdentity));
1842 sm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_KEY);
1843 sm->creation_time = GNUNET_TIME_absolute_hton (n->encrypt_key_created);
1844 sm->target = n->peer;
1845 GNUNET_assert (GNUNET_OK ==
1846 GNUNET_CRYPTO_rsa_encrypt (&n->encrypt_key,
1847 sizeof (struct
1848 GNUNET_CRYPTO_AesSessionKey),
1849 n->public_key,
1850 &sm->encrypted_key));
1851 GNUNET_assert (GNUNET_OK ==
1852 GNUNET_CRYPTO_rsa_sign (my_private_key, &sm->purpose,
1853 &sm->signature));
1854
1855 /* second, encrypted PING message */
1856 me = GNUNET_malloc (sizeof (struct MessageEntry) +
1857 sizeof (struct PingMessage));
1858 me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PING_DELAY);
1859 me->priority = PING_PRIORITY;
1860 me->size = sizeof (struct PingMessage);
1861 n->encrypted_tail->next = me;
1862 n->encrypted_tail = me;
1863 pm = (struct PingMessage *) &me[1];
1864 pm->header.size = htons (sizeof (struct PingMessage));
1865 pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
1866 pp.challenge = htonl (n->ping_challenge);
1867 pp.target = n->peer;
1868 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1869 "Encrypting `%s' and `%s' messages for `%4s'.\n",
1870 "SET_KEY", "PING", GNUNET_i2s (&n->peer));
1871 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1872 "Sending `%s' to `%4s' with challenge %u encrypted using key %u\n",
1873 "PING",
1874 GNUNET_i2s (&n->peer), n->ping_challenge, n->encrypt_key.crc32);
1875 do_encrypt (n,
1876 &n->peer.hashPubKey,
1877 &pp.challenge,
1878 &pm->challenge,
1879 sizeof (struct PingMessage) -
1880 sizeof (struct GNUNET_MessageHeader));
1881 /* update status */
1882 switch (n->status)
1883 {
1884 case PEER_STATE_DOWN:
1885 n->status = PEER_STATE_KEY_SENT;
1886 break;
1887 case PEER_STATE_KEY_SENT:
1888 break;
1889 case PEER_STATE_KEY_RECEIVED:
1890 break;
1891 case PEER_STATE_KEY_CONFIRMED:
1892 GNUNET_break (0);
1893 break;
1894 default:
1895 GNUNET_break (0);
1896 break;
1897 }
1898 /* trigger queue processing */
1899 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1900 "Triggering processing of encrypted message queue.\n");
1901 process_encrypted_neighbour_queue (n);
1902 if (n->status != PEER_STATE_KEY_CONFIRMED)
1903 n->retry_set_key_task
1904 = GNUNET_SCHEDULER_add_delayed (sched,
1905 GNUNET_NO,
1906 GNUNET_SCHEDULER_PRIORITY_KEEP,
1907 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1908 n->set_key_retry_frequency,
1909 &set_key_retry_task, n);
1910}
1911
1912
1913/**
1914 * We received a SET_KEY message. Validate and update
1915 * our key material and status.
1916 *
1917 * @param n the neighbour from which we received message m
1918 * @param m the set key message we received
1919 */
1920static void
1921handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m);
1922
1923
1924/**
1925 * PEERINFO is giving us a HELLO for a peer. Add the public key to
1926 * the neighbour's struct and retry handling the set_key message. Or,
1927 * if we did not get a HELLO, just free the set key message.
1928 *
1929 * @param cls pointer to the set key message
1930 * @param peer the peer for which this is the HELLO
1931 * @param hello HELLO message of that peer
1932 * @param trust amount of trust we currently have in that peer
1933 */
1934static void
1935process_hello_retry_handle_set_key (void *cls,
1936 const struct GNUNET_PeerIdentity *peer,
1937 const struct GNUNET_HELLO_Message *hello,
1938 uint32_t trust)
1939{
1940 struct SetKeyMessage *sm = cls;
1941 struct Neighbour *n;
1942
1943 if (peer == NULL)
1944 {
1945 GNUNET_free (sm);
1946 return;
1947 }
1948 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1949 "Looking for peer `%4s' to handle `%s' message.\n",
1950 GNUNET_i2s (peer), "SET_KEY");
1951 n = find_neighbour (peer);
1952 if (n == NULL)
1953 {
1954 GNUNET_break (0);
1955 return;
1956 }
1957 if (n->public_key != NULL)
1958 return; /* multiple HELLOs match!? */
1959 n->public_key =
1960 GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
1961 if (GNUNET_OK != GNUNET_HELLO_get_key (hello, n->public_key))
1962 {
1963 GNUNET_free (n->public_key);
1964 n->public_key = NULL;
1965 return;
1966 }
1967 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1968 "Received `%s' for `%4s', continuing processing of `%s' message.\n",
1969 "HELLO", GNUNET_i2s (peer), "SET_KEY");
1970 handle_set_key (n, sm);
1971}
1972
1973
1974/**
1975 * We received a PING message. Validate and transmit
1976 * PONG.
1977 *
1978 * @param n sender of the PING
1979 * @param m the encrypted PING message itself
1980 */
1981static void
1982handle_ping (struct Neighbour *n, const struct PingMessage *m)
1983{
1984 struct PingMessage t;
1985 struct PingMessage *tp;
1986 struct MessageEntry *me;
1987
1988 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1989 "Core service receives `%s' request from `%4s'.\n",
1990 "PING", GNUNET_i2s (&n->peer));
1991 if (GNUNET_OK !=
1992 do_decrypt (n,
1993 &my_identity.hashPubKey,
1994 &m->challenge,
1995 &t.challenge,
1996 sizeof (struct PingMessage) -
1997 sizeof (struct GNUNET_MessageHeader)))
1998 return;
1999 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2000 "Decrypted `%s' to `%4s' with challenge %u decrypted using key %u\n",
2001 "PING",
2002 GNUNET_i2s (&t.target),
2003 ntohl (t.challenge), n->decrypt_key.crc32);
2004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2005 "Target of `%s' request is `%4s'.\n",
2006 "PING", GNUNET_i2s (&t.target));
2007 if (0 != memcmp (&t.target,
2008 &my_identity, sizeof (struct GNUNET_PeerIdentity)))
2009 {
2010 GNUNET_break_op (0);
2011 return;
2012 }
2013 me = GNUNET_malloc (sizeof (struct MessageEntry) +
2014 sizeof (struct PingMessage));
2015 if (n->encrypted_tail != NULL)
2016 n->encrypted_tail->next = me;
2017 else
2018 {
2019 n->encrypted_tail = me;
2020 n->encrypted_head = me;
2021 }
2022 me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PONG_DELAY);
2023 me->priority = PONG_PRIORITY;
2024 me->size = sizeof (struct PingMessage);
2025 tp = (struct PingMessage *) &me[1];
2026 tp->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG);
2027 tp->header.size = htons (sizeof (struct PingMessage));
2028 do_encrypt (n,
2029 &my_identity.hashPubKey,
2030 &t.challenge,
2031 &tp->challenge,
2032 sizeof (struct PingMessage) -
2033 sizeof (struct GNUNET_MessageHeader));
2034 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2035 "Encrypting `%s' with challenge %u using key %u\n", "PONG",
2036 ntohl (t.challenge), n->encrypt_key.crc32);
2037 /* trigger queue processing */
2038 process_encrypted_neighbour_queue (n);
2039}
2040
2041
2042/**
2043 * We received a SET_KEY message. Validate and update
2044 * our key material and status.
2045 *
2046 * @param n the neighbour from which we received message m
2047 * @param m the set key message we received
2048 */
2049static void
2050handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
2051{
2052 struct SetKeyMessage *m_cpy;
2053 struct GNUNET_TIME_Absolute t;
2054 struct GNUNET_CRYPTO_AesSessionKey k;
2055 struct PingMessage *ping;
2056 enum PeerStateMachine sender_status;
2057
2058 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2059 "Core service receives `%s' request from `%4s'.\n",
2060 "SET_KEY", GNUNET_i2s (&n->peer));
2061 if (n->public_key == NULL)
2062 {
2063 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2064 "Lacking public key for peer, trying to obtain one.\n");
2065 m_cpy = GNUNET_malloc (sizeof (struct SetKeyMessage));
2066 memcpy (m_cpy, m, sizeof (struct SetKeyMessage));
2067 /* lookup n's public key, then try again */
2068 GNUNET_PEERINFO_for_all (cfg,
2069 sched,
2070 &n->peer,
2071 0,
2072 GNUNET_TIME_UNIT_MINUTES,
2073 &process_hello_retry_handle_set_key, m_cpy);
2074 return;
2075 }
2076 if ((ntohl (m->purpose.size) !=
2077 sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
2078 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
2079 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) +
2080 sizeof (struct GNUNET_PeerIdentity)) ||
2081 (GNUNET_OK !=
2082 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_SET_KEY,
2083 &m->purpose, &m->signature, n->public_key)))
2084 {
2085 /* invalid signature */
2086 GNUNET_break_op (0);
2087 return;
2088 }
2089 t = GNUNET_TIME_absolute_ntoh (m->creation_time);
2090 if (((n->status == PEER_STATE_KEY_RECEIVED) ||
2091 (n->status == PEER_STATE_KEY_CONFIRMED)) &&
2092 (t.value < n->decrypt_key_created.value))
2093 {
2094 /* this could rarely happen due to massive re-ordering of
2095 messages on the network level, but is most likely either
2096 a bug or some adversary messing with us. Report. */
2097 GNUNET_break_op (0);
2098 return;
2099 }
2100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decrypting key material.\n");
2101 if ((GNUNET_CRYPTO_rsa_decrypt (my_private_key,
2102 &m->encrypted_key,
2103 &k,
2104 sizeof (struct GNUNET_CRYPTO_AesSessionKey))
2105 != sizeof (struct GNUNET_CRYPTO_AesSessionKey)) ||
2106 (GNUNET_OK != GNUNET_CRYPTO_aes_check_session_key (&k)))
2107 {
2108 /* failed to decrypt !? */
2109 GNUNET_break_op (0);
2110 return;
2111 }
2112
2113 n->decrypt_key = k;
2114 if (n->decrypt_key_created.value != t.value)
2115 {
2116 /* fresh key, reset sequence numbers */
2117 n->last_sequence_number_received = 0;
2118 n->last_packets_bitmap = 0;
2119 n->decrypt_key_created = t;
2120 }
2121 sender_status = (enum PeerStateMachine) ntohl (m->sender_status);
2122 switch (n->status)
2123 {
2124 case PEER_STATE_DOWN:
2125 n->status = PEER_STATE_KEY_RECEIVED;
2126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2127 "Responding to `%s' with my own key.\n", "SET_KEY");
2128 send_key (n);
2129 break;
2130 case PEER_STATE_KEY_SENT:
2131 case PEER_STATE_KEY_RECEIVED:
2132 n->status = PEER_STATE_KEY_RECEIVED;
2133 if ((sender_status != PEER_STATE_KEY_RECEIVED) &&
2134 (sender_status != PEER_STATE_KEY_CONFIRMED))
2135 {
2136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2137 "Responding to `%s' with my own key (other peer has status %u).\n",
2138 "SET_KEY", sender_status);
2139 send_key (n);
2140 }
2141 break;
2142 case PEER_STATE_KEY_CONFIRMED:
2143 if ((sender_status != PEER_STATE_KEY_RECEIVED) &&
2144 (sender_status != PEER_STATE_KEY_CONFIRMED))
2145 {
2146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2147 "Responding to `%s' with my own key (other peer has status %u), I was already fully up.\n",
2148 "SET_KEY", sender_status);
2149 send_key (n);
2150 }
2151 break;
2152 default:
2153 GNUNET_break (0);
2154 break;
2155 }
2156 if (n->pending_ping != NULL)
2157 {
2158 ping = n->pending_ping;
2159 n->pending_ping = NULL;
2160 handle_ping (n, ping);
2161 GNUNET_free (ping);
2162 }
2163}
2164
2165
2166/**
2167 * We received a PONG message. Validate and update
2168 * our status.
2169 *
2170 * @param n sender of the PONG
2171 * @param m the encrypted PONG message itself
2172 */
2173static void
2174handle_pong (struct Neighbour *n, const struct PingMessage *m)
2175{
2176 struct PingMessage t;
2177
2178 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2179 "Core service receives `%s' request from `%4s'.\n",
2180 "PONG", GNUNET_i2s (&n->peer));
2181 if (GNUNET_OK !=
2182 do_decrypt (n,
2183 &n->peer.hashPubKey,
2184 &m->challenge,
2185 &t.challenge,
2186 sizeof (struct PingMessage) -
2187 sizeof (struct GNUNET_MessageHeader)))
2188 return;
2189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2190 "Decrypted `%s' from `%4s' with challenge %u using key %u\n",
2191 "PONG",
2192 GNUNET_i2s (&t.target),
2193 ntohl (t.challenge), n->decrypt_key.crc32);
2194 if ((0 != memcmp (&t.target,
2195 &n->peer,
2196 sizeof (struct GNUNET_PeerIdentity))) ||
2197 (n->ping_challenge != ntohl (t.challenge)))
2198 {
2199 /* PONG malformed */
2200 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2201 "Received malfromed `%s' wanted sender `%4s' with challenge %u\n",
2202 "PONG", GNUNET_i2s (&n->peer), n->ping_challenge);
2203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2204 "Received malfromed `%s' received from `%4s' with challenge %u\n",
2205 "PONG", GNUNET_i2s (&t.target), ntohl (t.challenge));
2206 GNUNET_break_op (0);
2207 return;
2208 }
2209 switch (n->status)
2210 {
2211 case PEER_STATE_DOWN:
2212 GNUNET_break (0); /* should be impossible */
2213 return;
2214 case PEER_STATE_KEY_SENT:
2215 GNUNET_break (0); /* should be impossible, how did we decrypt? */
2216 return;
2217 case PEER_STATE_KEY_RECEIVED:
2218 n->status = PEER_STATE_KEY_CONFIRMED;
2219 if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
2220 {
2221 GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task);
2222 n->retry_set_key_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
2223 }
2224 process_encrypted_neighbour_queue (n);
2225 break;
2226 case PEER_STATE_KEY_CONFIRMED:
2227 /* duplicate PONG? */
2228 break;
2229 default:
2230 GNUNET_break (0);
2231 break;
2232 }
2233}
2234
2235
2236/**
2237 * Send a P2P message to a client.
2238 *
2239 * @param sender who sent us the message?
2240 * @param client who should we give the message to?
2241 * @param m contains the message to transmit
2242 * @param msize number of bytes in buf to transmit
2243 */
2244static void
2245send_p2p_message_to_client (struct Neighbour *sender,
2246 struct Client *client,
2247 const void *m, size_t msize)
2248{
2249 char buf[msize + sizeof (struct NotifyTrafficMessage)];
2250 struct NotifyTrafficMessage *ntm;
2251
2252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2253 "Core service passes P2P message of type %u to client.\n",
2254 ntohs (((const struct GNUNET_MessageHeader *) m)->type));
2255 ntm = (struct NotifyTrafficMessage *) buf;
2256 ntm->header.size = htons (msize + sizeof (struct NotifyTrafficMessage));
2257 ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
2258 ntm->reserved = htonl (0);
2259 ntm->peer = sender->peer;
2260 memcpy (&ntm[1], m, msize);
2261 send_to_client (client, &ntm->header, GNUNET_YES);
2262}
2263
2264
2265/**
2266 * Deliver P2P message to interested clients.
2267 *
2268 * @param sender who sent us the message?
2269 * @param m the message
2270 * @param msize size of the message (including header)
2271 */
2272static void
2273deliver_message (struct Neighbour *sender,
2274 const struct GNUNET_MessageHeader *m, size_t msize)
2275{
2276 struct Client *cpos;
2277 uint16_t type;
2278 unsigned int tpos;
2279 int deliver_full;
2280
2281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2282 "Passing decrypted P2P message to interested clients.\n");
2283 type = ntohs (m->type);
2284 cpos = clients;
2285 while (cpos != NULL)
2286 {
2287 deliver_full = GNUNET_NO;
2288 if (cpos->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)
2289 deliver_full = GNUNET_YES;
2290 else
2291 {
2292 for (tpos = 0; tpos < cpos->tcnt; tpos++)
2293 {
2294 if (type != cpos->types[tpos])
2295 continue;
2296 deliver_full = GNUNET_YES;
2297 break;
2298 }
2299 }
2300 if (GNUNET_YES == deliver_full)
2301 send_p2p_message_to_client (sender, cpos, m, msize);
2302 else if (cpos->options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)
2303 send_p2p_message_to_client (sender, cpos, m,
2304 sizeof (struct GNUNET_MessageHeader));
2305 cpos = cpos->next;
2306 }
2307}
2308
2309
2310/**
2311 * Align P2P message and then deliver to interested clients.
2312 *
2313 * @param sender who sent us the message?
2314 * @param buffer unaligned (!) buffer containing message
2315 * @param msize size of the message (including header)
2316 */
2317static void
2318align_and_deliver (struct Neighbour *sender, const char *buffer, size_t msize)
2319{
2320 char abuf[msize];
2321
2322 /* TODO: call to statistics? */
2323 memcpy (abuf, buffer, msize);
2324 deliver_message (sender, (const struct GNUNET_MessageHeader *) abuf, msize);
2325}
2326
2327
2328/**
2329 * Deliver P2P messages to interested clients.
2330 *
2331 * @param sender who sent us the message?
2332 * @param buffer buffer containing messages, can be modified
2333 * @param buffer_size size of the buffer (overall)
2334 * @param offset offset where messages in the buffer start
2335 */
2336static void
2337deliver_messages (struct Neighbour *sender,
2338 const char *buffer, size_t buffer_size, size_t offset)
2339{
2340 struct GNUNET_MessageHeader *mhp;
2341 struct GNUNET_MessageHeader mh;
2342 uint16_t msize;
2343 int need_align;
2344
2345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2346 "Delivering %u bytes of plaintext to interested clients.\n",
2347 buffer_size);
2348 while (offset + sizeof (struct GNUNET_MessageHeader) <= buffer_size)
2349 {
2350 if (0 != offset % sizeof (uint16_t))
2351 {
2352 /* outch, need to copy to access header */
2353 memcpy (&mh, &buffer[offset], sizeof (struct GNUNET_MessageHeader));
2354 mhp = &mh;
2355 }
2356 else
2357 {
2358 /* can access header directly */
2359 mhp = (struct GNUNET_MessageHeader *) &buffer[offset];
2360 }
2361 msize = ntohs (mhp->size);
2362 if (msize + offset > buffer_size)
2363 {
2364 /* malformed message, header says it is larger than what
2365 would fit into the overall buffer */
2366 GNUNET_break_op (0);
2367 break;
2368 }
2369#if HAVE_UNALIGNED_64_ACCESS
2370 need_align = (0 != offset % 4) ? GNUNET_YES : GNUNET_NO;
2371#else
2372 need_align = (0 != offset % 8) ? GNUNET_YES : GNUNET_NO;
2373#endif
2374 if (GNUNET_YES == need_align)
2375 align_and_deliver (sender, &buffer[offset], msize);
2376 else
2377 deliver_message (sender,
2378 (const struct GNUNET_MessageHeader *)
2379 &buffer[offset], msize);
2380 offset += msize;
2381 }
2382}
2383
2384
2385/**
2386 * We received an encrypted message. Decrypt, validate and
2387 * pass on to the appropriate clients.
2388 */
2389static void
2390handle_encrypted_message (struct Neighbour *n,
2391 const struct EncryptedMessage *m)
2392{
2393 size_t size = ntohs (m->header.size);
2394 char buf[size];
2395 struct EncryptedMessage *pt; /* plaintext */
2396 GNUNET_HashCode ph;
2397 size_t off;
2398 uint32_t snum;
2399 struct GNUNET_TIME_Absolute t;
2400
2401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2402 "Core service receives `%s' request from `%4s'.\n",
2403 "ENCRYPTED_MESSAGE", GNUNET_i2s (&n->peer));
2404 /* decrypt */
2405 if (GNUNET_OK !=
2406 do_decrypt (n,
2407 &m->plaintext_hash,
2408 &m->sequence_number,
2409 &buf[ENCRYPTED_HEADER_SIZE], size - ENCRYPTED_HEADER_SIZE))
2410 return;
2411 pt = (struct EncryptedMessage *) buf;
2412
2413 /* validate hash */
2414 GNUNET_CRYPTO_hash (&pt->sequence_number,
2415 size - ENCRYPTED_HEADER_SIZE, &ph);
2416 if (0 != memcmp (&ph, &m->plaintext_hash, sizeof (GNUNET_HashCode)))
2417 {
2418 /* checksum failed */
2419 GNUNET_break_op (0);
2420 return;
2421 }
2422
2423 /* validate sequence number */
2424 snum = ntohl (pt->sequence_number);
2425 if (n->last_sequence_number_received == snum)
2426 {
2427 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2428 "Received duplicate message, ignoring.\n");
2429 /* duplicate, ignore */
2430 return;
2431 }
2432 if ((n->last_sequence_number_received > snum) &&
2433 (n->last_sequence_number_received - snum > 32))
2434 {
2435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2436 "Received ancient out of sequence message, ignoring.\n");
2437 /* ancient out of sequence, ignore */
2438 return;
2439 }
2440 if (n->last_sequence_number_received > snum)
2441 {
2442 unsigned int rotbit =
2443 1 << (n->last_sequence_number_received - snum - 1);
2444 if ((n->last_packets_bitmap & rotbit) != 0)
2445 {
2446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2447 "Received duplicate message, ignoring.\n");
2448 /* duplicate, ignore */
2449 return;
2450 }
2451 n->last_packets_bitmap |= rotbit;
2452 }
2453 if (n->last_sequence_number_received < snum)
2454 {
2455 n->last_packets_bitmap <<= (snum - n->last_sequence_number_received);
2456 n->last_sequence_number_received = snum;
2457 }
2458
2459 /* check timestamp */
2460 t = GNUNET_TIME_absolute_ntoh (pt->timestamp);
2461 if (GNUNET_TIME_absolute_get_duration (t).value > MAX_MESSAGE_AGE.value)
2462 {
2463 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2464 _
2465 ("Message received far too old (%llu ms). Content ignored.\n"),
2466 GNUNET_TIME_absolute_get_duration (t).value);
2467 return;
2468 }
2469
2470 /* process decrypted message(s) */
2471 n->bpm_out_external_limit = ntohl (pt->inbound_bpm_limit);
2472 n->bpm_out = GNUNET_MAX (n->bpm_out_external_limit,
2473 n->bpm_out_internal_limit);
2474 n->last_activity = GNUNET_TIME_absolute_get ();
2475 off = sizeof (struct EncryptedMessage);
2476 deliver_messages (n, buf, size, off);
2477}
2478
2479
2480/**
2481 * Function called by the transport for each received message.
2482 *
2483 * @param cls closure
2484 * @param latency estimated latency for communicating with the
2485 * given peer
2486 * @param peer (claimed) identity of the other peer
2487 * @param message the message
2488 */
2489static void
2490handle_transport_receive (void *cls,
2491 struct GNUNET_TIME_Relative latency,
2492 const struct GNUNET_PeerIdentity *peer,
2493 const struct GNUNET_MessageHeader *message)
2494{
2495 struct Neighbour *n;
2496 struct GNUNET_TIME_Absolute now;
2497 int up;
2498 uint16_t type;
2499 uint16_t size;
2500
2501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2502 "Received message of type %u from `%4s', demultiplexing.\n",
2503 ntohs (message->type), GNUNET_i2s (peer));
2504 n = find_neighbour (peer);
2505 if (n == NULL)
2506 {
2507 GNUNET_break (0);
2508 return;
2509 }
2510 n->last_latency = latency;
2511 up = n->status == PEER_STATE_KEY_CONFIRMED;
2512 type = ntohs (message->type);
2513 size = ntohs (message->size);
2514 switch (type)
2515 {
2516 case GNUNET_MESSAGE_TYPE_CORE_SET_KEY:
2517 if (size != sizeof (struct SetKeyMessage))
2518 {
2519 GNUNET_break_op (0);
2520 return;
2521 }
2522 handle_set_key (n, (const struct SetKeyMessage *) message);
2523 break;
2524 case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE:
2525 if (size < sizeof (struct EncryptedMessage) +
2526 sizeof (struct GNUNET_MessageHeader))
2527 {
2528 GNUNET_break_op (0);
2529 return;
2530 }
2531 if ((n->status != PEER_STATE_KEY_RECEIVED) &&
2532 (n->status != PEER_STATE_KEY_CONFIRMED))
2533 {
2534 GNUNET_break_op (0);
2535 return;
2536 }
2537 handle_encrypted_message (n, (const struct EncryptedMessage *) message);
2538 break;
2539 case GNUNET_MESSAGE_TYPE_CORE_PING:
2540 if (size != sizeof (struct PingMessage))
2541 {
2542 GNUNET_break_op (0);
2543 return;
2544 }
2545 if ((n->status != PEER_STATE_KEY_RECEIVED) &&
2546 (n->status != PEER_STATE_KEY_CONFIRMED))
2547 {
2548 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2549 "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n",
2550 "PING", GNUNET_i2s (&n->peer));
2551 GNUNET_free_non_null (n->pending_ping);
2552 n->pending_ping = GNUNET_malloc (sizeof (struct PingMessage));
2553 memcpy (n->pending_ping, message, sizeof (struct PingMessage));
2554 return;
2555 }
2556 handle_ping (n, (const struct PingMessage *) message);
2557 break;
2558 case GNUNET_MESSAGE_TYPE_CORE_PONG:
2559 if (size != sizeof (struct PingMessage))
2560 {
2561 GNUNET_break_op (0);
2562 return;
2563 }
2564 if ((n->status != PEER_STATE_KEY_SENT) &&
2565 (n->status != PEER_STATE_KEY_RECEIVED) &&
2566 (n->status != PEER_STATE_KEY_CONFIRMED))
2567 {
2568 /* could not decrypt pong, oops! */
2569 GNUNET_break_op (0);
2570 return;
2571 }
2572 handle_pong (n, (const struct PingMessage *) message);
2573 break;
2574 default:
2575 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2576 _("Unsupported message of type %u received.\n"), type);
2577 return;
2578 }
2579 if (n->status == PEER_STATE_KEY_CONFIRMED)
2580 {
2581 now = GNUNET_TIME_absolute_get ();
2582 n->last_activity = now;
2583 if (!up)
2584 n->time_established = now;
2585 }
2586}
2587
2588
2589/**
2590 * Function called by transport to notify us that
2591 * a peer connected to us (on the network level).
2592 *
2593 * @param cls closure
2594 * @param peer the peer that connected
2595 * @param latency current latency of the connection
2596 */
2597static void
2598handle_transport_notify_connect (void *cls,
2599 const struct GNUNET_PeerIdentity *peer,
2600 struct GNUNET_TIME_Relative latency)
2601{
2602 struct Neighbour *n;
2603 struct GNUNET_TIME_Absolute now;
2604 struct ConnectNotifyMessage cnm;
2605
2606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2607 "Received connection from `%4s'.\n", GNUNET_i2s (peer));
2608 n = find_neighbour (peer);
2609 if (n != NULL)
2610 {
2611 /* duplicate connect notification!? */
2612 GNUNET_break (0);
2613 return;
2614 }
2615 now = GNUNET_TIME_absolute_get ();
2616 n = GNUNET_malloc (sizeof (struct Neighbour));
2617 n->next = neighbours;
2618 neighbours = n;
2619 n->peer = *peer;
2620 n->last_latency = latency;
2621 GNUNET_CRYPTO_aes_create_session_key (&n->encrypt_key);
2622 n->encrypt_key_created = now;
2623 n->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY;
2624 n->last_asw_update = now;
2625 n->last_arw_update = now;
2626 n->bpm_in = DEFAULT_BPM_IN_OUT;
2627 n->bpm_out = DEFAULT_BPM_IN_OUT;
2628 n->bpm_out_internal_limit = (uint32_t) - 1;
2629 n->bpm_out_external_limit = DEFAULT_BPM_IN_OUT;
2630 n->ping_challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
2631 (uint32_t) - 1);
2632 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2633 "Created entry for new neighbour `%4s'.\n",
2634 GNUNET_i2s (&n->peer));
2635 cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
2636 cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
2637 cnm.bpm_available = htonl (DEFAULT_BPM_IN_OUT);
2638 cnm.peer = *peer;
2639 cnm.last_activity = GNUNET_TIME_absolute_hton (now);
2640 send_to_all_clients (&cnm.header, GNUNET_YES);
2641}
2642
2643
2644/**
2645 * Free the given entry for the neighbour (it has
2646 * already been removed from the list at this point).
2647 * @param n neighbour to free
2648 */
2649static void
2650free_neighbour (struct Neighbour *n)
2651{
2652 struct MessageEntry *m;
2653
2654 while (NULL != (m = n->messages))
2655 {
2656 n->messages = m->next;
2657 GNUNET_free (m);
2658 }
2659 while (NULL != (m = n->encrypted_head))
2660 {
2661 n->encrypted_head = m->next;
2662 GNUNET_free (m);
2663 }
2664 if (NULL != n->th)
2665 GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th);
2666 if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
2667 GNUNET_SCHEDULER_cancel (sched, n->retry_plaintext_task);
2668 if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
2669 GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task);
2670 GNUNET_free_non_null (n->public_key);
2671 GNUNET_free_non_null (n->pending_ping);
2672 GNUNET_free (n);
2673}
2674
2675
2676/**
2677 * Function called by transport telling us that a peer
2678 * disconnected.
2679 *
2680 * @param cls closure
2681 * @param peer the peer that disconnected
2682 */
2683static void
2684handle_transport_notify_disconnect (void *cls,
2685 const struct GNUNET_PeerIdentity *peer)
2686{
2687 struct ConnectNotifyMessage cnm;
2688 struct Neighbour *n;
2689 struct Neighbour *p;
2690
2691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2692 "Peer `%4s' disconnected from us.\n", GNUNET_i2s (peer));
2693 p = NULL;
2694 n = neighbours;
2695 while ((n != NULL) &&
2696 (0 != memcmp (&n->peer, peer, sizeof (struct GNUNET_PeerIdentity))))
2697 {
2698 p = n;
2699 n = n->next;
2700 }
2701 if (n == NULL)
2702 {
2703 GNUNET_break (0);
2704 return;
2705 }
2706 if (p == NULL)
2707 neighbours = n->next;
2708 else
2709 p->next = n->next;
2710 cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
2711 cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
2712 cnm.bpm_available = htonl (0);
2713 cnm.peer = *peer;
2714 cnm.last_activity = GNUNET_TIME_absolute_hton (n->last_activity);
2715 send_to_all_clients (&cnm.header, GNUNET_YES);
2716 free_neighbour (n);
2717}
2718
2719
2720/**
2721 * Last task run during shutdown. Disconnects us from
2722 * the transport.
2723 */
2724static void
2725cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2726{
2727 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service shutting down.\n");
2728 GNUNET_assert (transport != NULL);
2729 GNUNET_TRANSPORT_disconnect (transport);
2730 transport = NULL;
2731}
2732
2733
2734/**
2735 * Initiate core service.
2736 *
2737 * @param cls closure
2738 * @param s scheduler to use
2739 * @param serv the initialized server
2740 * @param c configuration to use
2741 */
2742static void
2743run (void *cls,
2744 struct GNUNET_SCHEDULER_Handle *s,
2745 struct GNUNET_SERVER_Handle *serv, struct GNUNET_CONFIGURATION_Handle *c)
2746{
2747#if 0
2748 unsigned long long qin;
2749 unsigned long long qout;
2750 unsigned long long tneigh;
2751#endif
2752 char *keyfile;
2753
2754 sched = s;
2755 cfg = c;
2756 /* parse configuration */
2757 if (
2758#if 0
2759 (GNUNET_OK !=
2760 GNUNET_CONFIGURATION_get_value_number (c,
2761 "CORE",
2762 "XX",
2763 &qin)) ||
2764 (GNUNET_OK !=
2765 GNUNET_CONFIGURATION_get_value_number (c,
2766 "CORE",
2767 "YY",
2768 &qout)) ||
2769 (GNUNET_OK !=
2770 GNUNET_CONFIGURATION_get_value_number (c,
2771 "CORE",
2772 "ZZ_LIMIT", &tneigh)) ||
2773#endif
2774 (GNUNET_OK !=
2775 GNUNET_CONFIGURATION_get_value_filename (c,
2776 "GNUNETD",
2777 "HOSTKEY", &keyfile)))
2778 {
2779 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2780 _
2781 ("Core service is lacking key configuration settings. Exiting.\n"));
2782 GNUNET_SCHEDULER_shutdown (s);
2783 return;
2784 }
2785 my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2786 GNUNET_free (keyfile);
2787 if (my_private_key == NULL)
2788 {
2789 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2790 _("Core service could not access hostkey. Exiting.\n"));
2791 GNUNET_SCHEDULER_shutdown (s);
2792 return;
2793 }
2794 GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
2795 GNUNET_CRYPTO_hash (&my_public_key,
2796 sizeof (my_public_key), &my_identity.hashPubKey);
2797 /* setup notification */
2798 server = serv;
2799 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
2800 /* setup transport connection */
2801 transport = GNUNET_TRANSPORT_connect (sched,
2802 cfg,
2803 NULL,
2804 &handle_transport_receive,
2805 &handle_transport_notify_connect,
2806 &handle_transport_notify_disconnect);
2807 GNUNET_assert (NULL != transport);
2808 GNUNET_SCHEDULER_add_delayed (sched,
2809 GNUNET_YES,
2810 GNUNET_SCHEDULER_PRIORITY_IDLE,
2811 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
2812 GNUNET_TIME_UNIT_FOREVER_REL,
2813 &cleaning_task, NULL);
2814 /* process client requests */
2815 GNUNET_SERVER_add_handlers (server, handlers);
2816 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2817 _("Core service of `%4s' ready.\n"), GNUNET_i2s (&my_identity));
2818}
2819
2820
2821/**
2822 * Function called during shutdown. Clean up our state.
2823 */
2824static void
2825cleanup (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
2826{
2827 struct Neighbour *n;
2828
2829 if (my_private_key != NULL)
2830 GNUNET_CRYPTO_rsa_key_free (my_private_key);
2831 while (NULL != (n = neighbours))
2832 {
2833 neighbours = n->next;
2834 free_neighbour (n);
2835 }
2836 /*
2837 FIXME:
2838 - free clients
2839 */
2840}
2841
2842
2843/**
2844 * The main function for the transport service.
2845 *
2846 * @param argc number of arguments from the command line
2847 * @param argv command line arguments
2848 * @return 0 ok, 1 on error
2849 */
2850int
2851main (int argc, char *const *argv)
2852{
2853 return (GNUNET_OK ==
2854 GNUNET_SERVICE_run (argc,
2855 argv,
2856 "core", &run, NULL, &cleanup, NULL)) ? 0 : 1;
2857}
2858
2859/* end of gnunet-service-core.c */
diff --git a/src/core/test_core_api.c b/src/core/test_core_api.c
new file mode 100644
index 000000000..e1b3dcd0d
--- /dev/null
+++ b/src/core/test_core_api.c
@@ -0,0 +1,368 @@
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 transport/test_core_api.c
22 * @brief testcase for core_api.c
23 *
24 * FIXME:
25 * - make sure connect callback is invoked properly as well!
26 */
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_arm_service.h"
30#include "gnunet_core_service.h"
31#include "gnunet_getopt_lib.h"
32#include "gnunet_os_lib.h"
33#include "gnunet_program_lib.h"
34#include "gnunet_scheduler_lib.h"
35#include "gnunet_transport_service.h"
36
37#define VERBOSE GNUNET_NO
38
39#define START_ARM GNUNET_YES
40
41
42/**
43 * How long until we give up on transmitting the message?
44 */
45#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
46
47#define MTYPE 12345
48
49struct PeerContext
50{
51 struct GNUNET_CONFIGURATION_Handle *cfg;
52 struct GNUNET_CORE_Handle *ch;
53 struct GNUNET_PeerIdentity id; /* FIXME: this is always all-zeros! */
54 struct GNUNET_TRANSPORT_Handle *th;
55 struct GNUNET_MessageHeader *hello;
56#if START_ARM
57 pid_t arm_pid;
58#endif
59};
60
61static struct PeerContext p1;
62
63static struct PeerContext p2;
64
65static struct GNUNET_SCHEDULER_Handle *sched;
66
67static int ok;
68
69#if VERBOSE
70#define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
71#else
72#define OKPP do { ok++; } while (0)
73#endif
74
75
76
77static void
78terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
79{
80 GNUNET_assert (ok == 6);
81 GNUNET_CORE_disconnect (p1.ch);
82 GNUNET_CORE_disconnect (p2.ch);
83 GNUNET_TRANSPORT_disconnect (p1.th);
84 GNUNET_TRANSPORT_disconnect (p2.th);
85 GNUNET_ARM_stop_service ("core", p1.cfg, sched, TIMEOUT, NULL, NULL);
86 GNUNET_ARM_stop_service ("core", p2.cfg, sched, TIMEOUT, NULL, NULL);
87 ok = 0;
88}
89
90
91static void
92connect_notify (void *cls,
93 const struct GNUNET_PeerIdentity *peer,
94 unsigned int bpm, struct GNUNET_TIME_Absolute last_activity)
95{
96 GNUNET_assert ((ok == 5) || (ok == 6));
97 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
98 "Encrypted connection established to peer `%4s' (%u bpm)\n",
99 GNUNET_i2s (peer), bpm);
100}
101
102
103static void
104disconnect_notify (void *cls,
105 const struct GNUNET_PeerIdentity *peer,
106 unsigned int bpm,
107 struct GNUNET_TIME_Absolute last_activity)
108{
109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
110 "Encrypted connection to `%4s' cut\n", GNUNET_i2s (peer));
111}
112
113
114static unsigned int
115bfc_callback (void *cls,
116 const struct GNUNET_PeerIdentity *receiver,
117 void *position, unsigned int padding)
118{
119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
120 "Core requests data for `%4s', I have none.\n",
121 GNUNET_i2s (receiver));
122 return 0;
123}
124
125
126static int
127inbound_notify (void *cls,
128 const struct GNUNET_PeerIdentity *other,
129 const struct GNUNET_MessageHeader *message)
130{
131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
132 "Core provides inbound data from `%4s'.\n", GNUNET_i2s (other));
133 return GNUNET_OK;
134}
135
136
137static int
138outbound_notify (void *cls,
139 const struct GNUNET_PeerIdentity *other,
140 const struct GNUNET_MessageHeader *message)
141{
142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
143 "Core notifies about outbound data for `%4s'.\n",
144 GNUNET_i2s (other));
145 return GNUNET_OK;
146}
147
148
149static int
150process_mtype (void *cls,
151 const struct GNUNET_PeerIdentity *peer,
152 const struct GNUNET_MessageHeader *message)
153{
154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
155 "Receiving message from `%4s'.\n", GNUNET_i2s (peer));
156 GNUNET_assert (ok == 5);
157 OKPP;
158 GNUNET_SCHEDULER_add_delayed (sched,
159 GNUNET_NO,
160 GNUNET_SCHEDULER_PRIORITY_KEEP,
161 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
162 GNUNET_TIME_UNIT_ZERO, &terminate_task, NULL);
163 return GNUNET_OK;
164}
165
166
167static struct GNUNET_CORE_MessageHandler handlers[] = {
168 {&process_mtype, MTYPE, sizeof (struct GNUNET_MessageHeader)},
169 {NULL, 0, 0}
170};
171
172
173static size_t
174transmit_ready (void *cls, size_t size, void *buf)
175{
176 struct PeerContext *p = cls;
177 struct GNUNET_MessageHeader *m;
178
179 GNUNET_assert (ok == 4);
180 OKPP;
181 GNUNET_assert (p == &p1);
182 GNUNET_assert (buf != NULL);
183 m = (struct GNUNET_MessageHeader *) buf;
184 m->type = htons (MTYPE);
185 m->size = htons (sizeof (struct GNUNET_MessageHeader));
186 return sizeof (struct GNUNET_MessageHeader);
187}
188
189
190
191static void
192init_notify (void *cls,
193 struct GNUNET_CORE_Handle *server,
194 const struct GNUNET_PeerIdentity *my_identity,
195 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
196{
197 struct PeerContext *p = cls;
198
199 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
200 "Core connection to `%4s' established\n",
201 GNUNET_i2s (my_identity));
202 GNUNET_assert (server != NULL);
203 p->id = *my_identity;
204 p->ch = server;
205 if (cls == &p1)
206 {
207 GNUNET_assert (ok == 2);
208 OKPP;
209 /* connect p2 */
210 GNUNET_CORE_connect (sched,
211 p2.cfg,
212 TIMEOUT,
213 &p2,
214 &init_notify,
215 &connect_notify,
216 &disconnect_notify,
217 &bfc_callback,
218 &inbound_notify,
219 GNUNET_YES,
220 &outbound_notify, GNUNET_YES, handlers);
221 }
222 else
223 {
224 GNUNET_assert (ok == 3);
225 OKPP;
226 GNUNET_assert (cls == &p2);
227 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228 "Asking core (1) for transmission to peer `%4s'\n",
229 GNUNET_i2s (&p2.id));
230 GNUNET_CORE_notify_transmit_ready (p1.ch,
231 0,
232 TIMEOUT,
233 &p2.id,
234 sizeof (struct GNUNET_MessageHeader),
235 &transmit_ready, &p1);
236
237 }
238}
239
240
241static void
242process_hello (void *cls,
243 struct GNUNET_TIME_Relative latency,
244 const struct GNUNET_PeerIdentity *peer,
245 const struct GNUNET_MessageHeader *message)
246{
247 struct PeerContext *p = cls;
248
249 GNUNET_assert (peer != NULL);
250 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251 "Received (my) `%s' from transport service of `%4s'\n",
252 "HELLO", GNUNET_i2s (peer));
253 GNUNET_assert (message != NULL);
254 p->hello = GNUNET_malloc (ntohs (message->size));
255 memcpy (p->hello, message, ntohs (message->size));
256 if ((p == &p1) && (p2.th != NULL))
257 GNUNET_TRANSPORT_offer_hello (p2.th, message);
258 if ((p == &p2) && (p1.th != NULL))
259 GNUNET_TRANSPORT_offer_hello (p1.th, message);
260
261 if ((p == &p1) && (p2.hello != NULL))
262 GNUNET_TRANSPORT_offer_hello (p1.th, p2.hello);
263 if ((p == &p2) && (p1.hello != NULL))
264 GNUNET_TRANSPORT_offer_hello (p2.th, p1.hello);
265}
266
267
268
269static void
270setup_peer (struct PeerContext *p, const char *cfgname)
271{
272 p->cfg = GNUNET_CONFIGURATION_create ();
273#if START_ARM
274 p->arm_pid = GNUNET_OS_start_process ("gnunet-service-arm",
275 "gnunet-service-arm",
276#if VERBOSE
277 "-L", "DEBUG",
278#endif
279 "-c", cfgname, NULL);
280 sleep (1); /* allow ARM to start */
281#endif
282 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
283 GNUNET_ARM_start_service ("core", p->cfg, sched, TIMEOUT, NULL, NULL);
284 p->th = GNUNET_TRANSPORT_connect (sched, p->cfg, p, NULL, NULL, NULL);
285 GNUNET_assert (p->th != NULL);
286 GNUNET_TRANSPORT_get_hello (p->th, TIMEOUT, &process_hello, p);
287}
288
289
290static void
291run (void *cls,
292 struct GNUNET_SCHEDULER_Handle *s,
293 char *const *args,
294 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
295{
296 GNUNET_assert (ok == 1);
297 OKPP;
298 sched = s;
299 setup_peer (&p1, "test_core_api_peer1.conf");
300 setup_peer (&p2, "test_core_api_peer2.conf");
301 GNUNET_CORE_connect (sched,
302 p1.cfg,
303 TIMEOUT,
304 &p1,
305 &init_notify,
306 &connect_notify,
307 &disconnect_notify,
308 &bfc_callback,
309 &inbound_notify,
310 GNUNET_YES, &outbound_notify, GNUNET_YES, handlers);
311}
312
313
314static void
315stop_arm (struct PeerContext *p)
316{
317#if START_ARM
318 if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
319 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
320 if (p->arm_pid != waitpid (p->arm_pid, NULL, 0))
321 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323 "ARM process %u stopped\n", p->arm_pid);
324#endif
325 GNUNET_CONFIGURATION_destroy (p->cfg);
326}
327
328static int
329check ()
330{
331 char *const argv[] = { "test-core-api",
332 "-c",
333 "test_core_api_data.conf",
334#if VERBOSE
335 "-L", "DEBUG",
336#endif
337 NULL
338 };
339 struct GNUNET_GETOPT_CommandLineOption options[] = {
340 GNUNET_GETOPT_OPTION_END
341 };
342 sleep (1);
343 ok = 1;
344 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
345 argv, "test-core-api", "nohelp", options, &run, &ok);
346 stop_arm (&p1);
347 stop_arm (&p2);
348 return ok;
349}
350
351int
352main (int argc, char *argv[])
353{
354 int ret;
355
356 GNUNET_log_setup ("test-core-api",
357#if VERBOSE
358 "DEBUG",
359#else
360 "WARNING",
361#endif
362 NULL);
363 ret = check ();
364
365 return ret;
366}
367
368/* end of test_core_api.c */
diff --git a/src/core/test_core_api_data.conf b/src/core/test_core_api_data.conf
new file mode 100644
index 000000000..7a35faaf0
--- /dev/null
+++ b/src/core/test_core_api_data.conf
@@ -0,0 +1,27 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-core-master/
3
4[resolver]
5PORT = 2464
6
7[transport]
8PORT = 2465
9PLUGINS = tcp
10
11[arm]
12PORT = 2466
13
14[statistics]
15PORT = 2467
16
17[tcp]
18PORT = 2468
19
20[peerinfo]
21PORT = 2469
22
23[core]
24PORT = 2470
25
26[testing]
27WEAKRANDOM = YES
diff --git a/src/core/test_core_api_peer1.conf b/src/core/test_core_api_peer1.conf
new file mode 100644
index 000000000..fb060e3e6
--- /dev/null
+++ b/src/core/test_core_api_peer1.conf
@@ -0,0 +1,40 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-core-peer-1/
3DEFAULTCONFIG = test_core_api_peer1.conf
4
5[resolver]
6PORT = 12464
7#PREFIX = xterm -T resolver1 -e valgrind --tool=memcheck
8
9[transport]
10PORT = 12465
11PLUGINS = tcp
12#PREFIX = xterm -T transport1 -e
13#PREFIX = xterm -T transport1 -e valgrind --tool=memcheck
14#DEBUG = YES
15
16[arm]
17PORT = 12466
18#GLOBAL_PREFIX = xterm -e valgrind --tool=memcheck
19
20[statistics]
21PORT = 12467
22#PREFIX = xterm -T statistics1 -e valgrind --tool=memcheck
23
24[tcp]
25PORT = 12468
26
27[peerinfo]
28PORT = 12469
29#PREFIX = xterm -T peerinfo1 -e valgrind --tool=memcheck
30#PREFIX = xterm -T peerinfo1 -e
31
32[core]
33PORT = 12470
34#PREFIX = xterm -T core1 -e valgrind --tool=memcheck
35#PREFIX = xterm -T core1 -e gdb -x cmd --args
36#PREFIX = xterm -T core1 -e
37#DEBUG = YES
38
39[testing]
40WEAKRANDOM = YES
diff --git a/src/core/test_core_api_peer2.conf b/src/core/test_core_api_peer2.conf
new file mode 100644
index 000000000..e3862da0f
--- /dev/null
+++ b/src/core/test_core_api_peer2.conf
@@ -0,0 +1,39 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-core-peer-2/
3DEFAULTCONFIG = test_core_api_peer2.conf
4
5[resolver]
6PORT = 22464
7#PREFIX = xterm -T resolver2 -e valgrind --tool=memcheck
8
9[transport]
10PORT = 22465
11PLUGINS = tcp
12#PREFIX = xterm -T transport2 -e
13#PREFIX = xterm -T transport2 -e valgrind --tool=memcheck
14#DEBUG = YES
15
16[arm]
17PORT = 22466
18#GLOBAL_PREFIX = xterm -e valgrind --tool=memcheck
19
20[statistics]
21PORT = 22467
22#PREFIX = xterm -T statistics2 -e valgrind --tool=memcheck
23
24[tcp]
25PORT = 22468
26
27[peerinfo]
28PORT = 22469
29#PREFIX = xterm -T peerinfo2 -e valgrind --tool=memcheck
30#PREFIX = xterm -T peerinfo2 -e
31
32[core]
33PORT = 22470
34#PREFIX = xterm -T core2 -e
35#PREFIX = xterm -T core2 -e valgrind --tool=memcheck
36#DEBUG = YES
37
38[testing]
39WEAKRANDOM = YES
diff --git a/src/core/test_core_api_start_only.c b/src/core/test_core_api_start_only.c
new file mode 100644
index 000000000..dc4bfea98
--- /dev/null
+++ b/src/core/test_core_api_start_only.c
@@ -0,0 +1,257 @@
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 transport/test_core_api_start_only.c
22 * @brief testcase for core_api.c that only starts two peers,
23 * connects to the core service and shuts down again
24 */
25#include "platform.h"
26#include "gnunet_common.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_core_service.h"
29#include "gnunet_getopt_lib.h"
30#include "gnunet_os_lib.h"
31#include "gnunet_program_lib.h"
32#include "gnunet_scheduler_lib.h"
33
34#define VERBOSE GNUNET_NO
35
36#define START_ARM GNUNET_YES
37
38
39/**
40 * How long until we give up on transmitting the message?
41 */
42#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
43
44#define MTYPE 12345
45
46struct PeerContext
47{
48 struct GNUNET_CONFIGURATION_Handle *cfg;
49 struct GNUNET_CORE_Handle *ch;
50 struct GNUNET_PeerIdentity id;
51#if START_ARM
52 pid_t arm_pid;
53#endif
54};
55
56static struct PeerContext p1;
57
58static struct PeerContext p2;
59
60static struct GNUNET_SCHEDULER_Handle *sched;
61
62static int ok;
63
64#if VERBOSE
65#define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
66#else
67#define OKPP do { ok++; } while (0)
68#endif
69
70
71
72static void
73connect_notify (void *cls,
74 const struct GNUNET_PeerIdentity *peer,
75 unsigned int bpm, struct GNUNET_TIME_Absolute last_activity)
76{
77}
78
79
80static void
81disconnect_notify (void *cls,
82 const struct GNUNET_PeerIdentity *peer,
83 unsigned int bpm,
84 struct GNUNET_TIME_Absolute last_activity)
85{
86}
87
88
89static unsigned int
90bfc_callback (void *cls,
91 const struct GNUNET_PeerIdentity *receiver,
92 void *position, unsigned int padding)
93{
94 return 0;
95}
96
97
98static int
99inbound_notify (void *cls,
100 const struct GNUNET_PeerIdentity *other,
101 const struct GNUNET_MessageHeader *message)
102{
103 return GNUNET_OK;
104}
105
106
107static int
108outbound_notify (void *cls,
109 const struct GNUNET_PeerIdentity *other,
110 const struct GNUNET_MessageHeader *message)
111{
112 return GNUNET_OK;
113}
114
115
116static struct GNUNET_CORE_MessageHandler handlers[] = {
117 {NULL, 0, 0}
118};
119
120
121
122static void
123init_notify (void *cls,
124 struct GNUNET_CORE_Handle *server,
125 const struct GNUNET_PeerIdentity *my_identity,
126 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
127{
128 struct PeerContext *p = cls;
129
130 GNUNET_assert (server != NULL);
131 p->ch = server;
132 if (cls == &p1)
133 {
134 /* connect p2 */
135 GNUNET_CORE_connect (sched,
136 p2.cfg,
137 TIMEOUT,
138 &p2,
139 &init_notify,
140 &connect_notify,
141 &disconnect_notify,
142 &bfc_callback,
143 &inbound_notify,
144 GNUNET_YES,
145 &outbound_notify, GNUNET_YES, handlers);
146 }
147 else
148 {
149 GNUNET_assert (cls == &p2);
150 GNUNET_CORE_disconnect (p1.ch);
151 GNUNET_CORE_disconnect (p2.ch);
152 GNUNET_ARM_stop_service ("core", p1.cfg, sched, TIMEOUT, NULL, NULL);
153 GNUNET_ARM_stop_service ("core", p2.cfg, sched, TIMEOUT, NULL, NULL);
154
155 ok = 0;
156 }
157}
158
159
160static void
161setup_peer (struct PeerContext *p, const char *cfgname)
162{
163 p->cfg = GNUNET_CONFIGURATION_create ();
164#if START_ARM
165 p->arm_pid = GNUNET_OS_start_process ("gnunet-service-arm",
166 "gnunet-service-arm",
167#if VERBOSE
168 "-L", "DEBUG",
169#endif
170 "-c", cfgname, NULL);
171 sleep (1); /* allow ARM to start */
172#endif
173 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
174 GNUNET_ARM_start_service ("core", p->cfg, sched, TIMEOUT, NULL, NULL);
175}
176
177
178static void
179run (void *cls,
180 struct GNUNET_SCHEDULER_Handle *s,
181 char *const *args,
182 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
183{
184 GNUNET_assert (ok == 1);
185 OKPP;
186 sched = s;
187 setup_peer (&p1, "test_core_api_peer1.conf");
188 setup_peer (&p2, "test_core_api_peer2.conf");
189 GNUNET_CORE_connect (sched,
190 p1.cfg,
191 TIMEOUT,
192 &p1,
193 &init_notify,
194 &connect_notify,
195 &disconnect_notify,
196 &bfc_callback,
197 &inbound_notify,
198 GNUNET_YES, &outbound_notify, GNUNET_YES, handlers);
199}
200
201
202static void
203stop_arm (struct PeerContext *p)
204{
205#if START_ARM
206 if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
207 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
208 if (p->arm_pid != waitpid (p->arm_pid, NULL, 0))
209 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211 "ARM process %u stopped\n", p->arm_pid);
212#endif
213 GNUNET_CONFIGURATION_destroy (p->cfg);
214}
215
216
217static int
218check ()
219{
220 char *const argv[] = { "test-core-api",
221 "-c",
222 "test_core_api_data.conf",
223#if VERBOSE
224 "-L", "DEBUG",
225#endif
226 NULL
227 };
228 struct GNUNET_GETOPT_CommandLineOption options[] = {
229 GNUNET_GETOPT_OPTION_END
230 };
231
232 ok = 1;
233 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
234 argv, "test-core-api", "nohelp", options, &run, &ok);
235 stop_arm (&p1);
236 stop_arm (&p2);
237 return ok;
238}
239
240int
241main (int argc, char *argv[])
242{
243 int ret;
244
245 GNUNET_log_setup ("test-core-api",
246#if VERBOSE
247 "DEBUG",
248#else
249 "WARNING",
250#endif
251 NULL);
252 ret = check ();
253
254 return ret;
255}
256
257/* end of test_core_api_start_only.c */
diff --git a/src/datastore/plugin_datastore.h b/src/datastore/plugin_datastore.h
new file mode 100644
index 000000000..b4dc87f9d
--- /dev/null
+++ b/src/datastore/plugin_datastore.h
@@ -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/**
22 * @file datastore/plugin_datastore.h
23 * @brief API for the database backend plugins.
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - consider defining enumeration or at least typedef
28 * for the type of "type" (instead of using uint32_t)
29 */
30#ifndef PLUGIN_DATASTORE_H
31#define PLUGIN_DATASTORE_H
32
33#include "gnunet_configuration_lib.h"
34#include "gnunet_scheduler_lib.h"
35#include "gnunet_datastore_service.h"
36
37/**
38 * The datastore service will pass a pointer to a struct
39 * of this type as the first and only argument to the
40 * entry point of each datastore plugin.
41 */
42struct GNUNET_DATASTORE_PluginEnvironment
43{
44 /**
45 * Configuration to use.
46 */
47 struct GNUNET_CONFIGURATION_Handle *cfg;
48
49 /**
50 * Scheduler to use.
51 */
52 struct GNUNET_SCHEDULER_Handle *sched;
53
54};
55
56
57/**
58 * Get an estimate of how much space the database is
59 * currently using.
60 * @return number of bytes used on disk
61 */
62typedef unsigned long long (*GNUNET_DATASTORE_GetSize) (void *cls);
63
64
65/**
66 * Store an item in the datastore.
67 *
68 * @param cls closure
69 * @param key key for the item
70 * @param size number of bytes in data
71 * @param data content stored
72 * @param type type of the content
73 * @param priority priority of the content
74 * @param anonymity anonymity-level for the content
75 * @param expiration expiration time for the content
76 */
77typedef void
78 (*GNUNET_DATASTORE_Put) (void *cls,
79 const GNUNET_HashCode * key,
80 uint32_t size,
81 const void *data,
82 unit32_t type,
83 uint32_t priority,
84 uint32_t anonymity,
85 struct GNUNET_TIME_Absolute expiration);
86
87
88/**
89 * Iterate over the results for a particular key
90 * in the datastore.
91 *
92 * @param cls closure
93 * @param key maybe NULL (to match all entries)
94 * @param vhash hash of the value, maybe NULL (to
95 * match all values that have the right key).
96 * Note that for DBlocks there is no difference
97 * betwen key and vhash, but for other blocks
98 * there may be!
99 * @param type entries of which type are relevant?
100 * Use 0 for any type.
101 * @param iter function to call on each matching value;
102 * will be called once with a NULL value at the end
103 * @param iter_cls closure for iter
104 */
105typedef void
106 (*GNUNET_DATASTORE_Get) (void *cls,
107 const GNUNET_HashCode * key,
108 const GNUNET_HashCode * vhash,
109 uint32_t type,
110 GNUNET_DATASTORE_Iterator iter, void *iter_cls);
111
112
113/**
114 * Update the priority for a particular key in the datastore. If
115 * the expiration time in value is different than the time found in
116 * the datastore, the higher value should be kept. For the
117 * anonymity level, the lower value is to be used. The specified
118 * priority should be added to the existing priority, ignoring the
119 * priority in value.
120 *
121 * Note that it is possible for multiple values to match this put.
122 * In that case, all of the respective values are updated.
123 *
124 * @param uid unique identifier of the datum
125 * @param delta by how much should the priority
126 * change? If priority + delta < 0 the
127 * priority should be set to 0 (never go
128 * negative).
129 * @param expire new expiration time should be the
130 * MAX of any existing expiration time and
131 * this value
132 */
133typedef void
134 (*GNUNET_DATASTORE_Update) (void *cls,
135 unsigned long long uid,
136 int delta, struct GNUNET_TIME_Absolute expire);
137
138
139/**
140 * Select a subset of the items in the datastore and call
141 * the given iterator for each of them.
142 *
143 * @param type entries of which type should be considered?
144 * Use 0 for any type.
145 * @param iter function to call on each matching value;
146 * will be called once with a NULL value at the end
147 * @param iter_cls closure for iter
148 */
149typedef void
150 (*GNUNET_DATASTORE_Selector) (void *cls,
151 uint32_t type,
152 GNUNET_DATASTORE_Iterator iter,
153 void *iter_cls);
154
155/**
156 * Drop database.
157 */
158typedef void (*GNUNET_DATASTORE_Drop) (void *cls);
159
160
161
162/**
163 * Each plugin is required to return a pointer to a struct of this
164 * type as the return value from its entry point.
165 */
166struct GNUNET_DATASTORE_PluginFunctions
167{
168
169 /**
170 * Closure to use for all of the following callbacks.
171 */
172 void *cls;
173
174 /**
175 * Get the current on-disk size of the SQ store. Estimates are
176 * fine, if that's the only thing available.
177 */
178 GNUNET_DATASTORE_GetSize size;
179
180 /**
181 * Function to store an item in the datastore.
182 */
183 GNUNET_DATASTORE_Put put;
184
185 /**
186 * Function to iterate over the results for a particular key
187 * in the datastore.
188 */
189 GNUNET_DATASTORE_Get get;
190
191 /**
192 * Update the priority for a particular key in the datastore. If
193 * the expiration time in value is different than the time found in
194 * the datastore, the higher value should be kept. For the
195 * anonymity level, the lower value is to be used. The specified
196 * priority should be added to the existing priority, ignoring the
197 * priority in value.
198 */
199 GNUNET_DATASTORE_Update update;
200
201 /**
202 * Iterate over the items in the datastore in ascending
203 * order of priority.
204 */
205 GNUNET_DATASTORE_Selector iter_low_priority;
206
207 /**
208 * Iterate over content with anonymity zero.
209 */
210 GNUNET_DATASTORE_Selector iter_zero_anonymity;
211
212 /**
213 * Iterate over the items in the datastore in ascending
214 * order of expiration time.
215 */
216 GNUNET_DATSTORE_Selector iter_ascending_expiration;
217
218 /**
219 * Iterate over the items in the datastore in migration
220 * order.
221 */
222 GNUNET_DATASTORE_Selector iter_migration_order;
223
224 /**
225 * Iterate over all the items in the datastore
226 * as fast as possible in a single transaction
227 * (can lock datastore while this happens, focus
228 * is on doing it fast).
229 */
230 GNUNET_DATASTORE_Selector iter_all_now;
231
232 /**
233 * Delete the database. The next operation is
234 * guaranteed to be unloading of the module.
235 */
236 GNUNET_DATASTORE_Drop drop;
237
238};
239
240
241#endif
diff --git a/src/fragmentation/Makefile.am b/src/fragmentation/Makefile.am
new file mode 100644
index 000000000..d3a47d920
--- /dev/null
+++ b/src/fragmentation/Makefile.am
@@ -0,0 +1,28 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = --coverage
9endif
10
11lib_LTLIBRARIES = libgnunetfragmentation.la
12
13libgnunetfragmentation_la_SOURCES = \
14 fragmentation.c
15libgnunetfragmentation_la_LIBADD = \
16 $(top_builddir)/src/util/libgnunetutil.la
17
18check_PROGRAMS = \
19 test_fragmentation
20
21TESTS = $(check_PROGRAMS)
22
23test_fragmentation_SOURCES = \
24 test_fragmentation.c
25test_fragmentation_LDADD = \
26 $(top_builddir)/src/fragmentation/libgnunetfragmentation.la \
27 $(top_builddir)/src/util/libgnunetutil.la
28
diff --git a/src/fragmentation/fragmentation.c b/src/fragmentation/fragmentation.c
new file mode 100644
index 000000000..9550663c7
--- /dev/null
+++ b/src/fragmentation/fragmentation.c
@@ -0,0 +1,702 @@
1/*
2 This file is part of GNUnet
3 (C) 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 fragmentation/fragmentation.c
22 * @brief fragmentation and defragmentation, this code allows
23 * sending and receiving messages that are larger than
24 * the MTU of the transport. Messages are still limited
25 * to a maximum size of 65535 bytes, which is a good
26 * idea because otherwise we may need ungainly fragmentation
27 * buffers. Each connected peer can have at most one
28 * fragmented packet at any given point in time (prevents
29 * DoS attacks). Fragmented messages that have not been
30 * completed after a certain amount of time are discarded.
31 * @author Christian Grothoff
32 */
33
34#include "platform.h"
35#include "gnunet_fragmentation_lib.h"
36
37/**
38 * Message fragment. This header is followed
39 * by the actual data of the fragment.
40 */
41struct Fragment
42{
43
44 struct GNUNET_MessageHeader header;
45
46 /**
47 * Fragment offset.
48 */
49 uint32_t off GNUNET_PACKED;
50
51 /**
52 * "unique" id for the fragment
53 */
54 uint64_t id GNUNET_PACKED;
55
56};
57
58
59/**
60 * Defragmentation context.
61 */
62struct GNUNET_FRAGMENT_Context
63{
64};
65
66
67/**
68 * Fragment an over-sized message.
69 *
70 * @param msg the message to fragment
71 * @param mtu the maximum message size
72 * @param proc function to call for each fragment
73 * @param proc_cls closure for proc
74 */
75void
76GNUNET_FRAGMENT_fragment (const struct GNUNET_MessageHeader *msg,
77 uint16_t mtu,
78 GNUNET_FRAGMENT_MessageProcessor proc,
79 void *proc_cls)
80{
81 GNUNET_assert (0);
82}
83
84
85/**
86 * Create a defragmentation context.
87 *
88 * @param stats statistics context
89 * @param proc function to call with defragmented messages
90 * @param proc_cls closure for proc
91 * @return the defragmentation context
92 */
93struct GNUNET_FRAGMENT_Context *
94GNUNET_FRAGMENT_context_create (struct GNUNET_STATISTICS_Handle *stats,
95 GNUNET_FRAGMENT_MessageProcessor proc,
96 void *proc_cls)
97{
98 return NULL;
99}
100
101
102/**
103 * Destroy the given defragmentation context.
104 */
105void
106GNUNET_FRAGMENT_context_destroy (struct GNUNET_FRAGMENT_Context *ctx)
107{
108 GNUNET_assert (0);
109}
110
111
112/**
113 * We have received a fragment. Process it.
114 *
115 * @param ctx the context
116 * @param sender who transmitted the fragment
117 * @param msg the message that was received
118 */
119void
120GNUNET_FRAGMENT_process (struct GNUNET_FRAGMENT_Context *ctx,
121 const struct GNUNET_PeerIdentity *sender,
122 const struct GNUNET_MessageHeader *msg)
123{
124 GNUNET_assert (0);
125}
126
127
128
129#if 0
130
131/**
132 * How many buckets does the fragment hash table
133 * have?
134 */
135#define DEFRAG_BUCKET_COUNT 16
136
137/**
138 * After how long do fragments time out?
139 */
140#ifndef DEFRAGMENTATION_TIMEOUT
141#define DEFRAGMENTATION_TIMEOUT (3 * GNUNET_CRON_MINUTES)
142#endif
143
144/**
145 * Entry in the linked list of fragments.
146 */
147typedef struct FL
148{
149 struct FL *link;
150 P2P_fragmentation_MESSAGE *frag;
151} FL;
152
153/**
154 * Entry in the GNUNET_hash table of fragments.
155 */
156typedef struct FC
157{
158 struct FC *next;
159 FL *head;
160 GNUNET_PeerIdentity sender;
161 int id;
162 GNUNET_CronTime ttl;
163} FC;
164
165#define FRAGSIZE(fl) ((ntohs(fl->frag->header.size)-sizeof(P2P_fragmentation_MESSAGE)))
166
167static GNUNET_CoreAPIForPlugins *coreAPI;
168
169static GNUNET_Stats_ServiceAPI *stats;
170
171static int stat_defragmented;
172
173static int stat_fragmented;
174
175static int stat_discarded;
176
177/**
178 * Hashtable *with* collision management!
179 */
180static FC *defragmentationCache[DEFRAG_BUCKET_COUNT];
181
182/**
183 * Lock for the defragmentation cache.
184 */
185static struct GNUNET_Mutex *defragCacheLock;
186
187static void
188freeFL (FL * fl, int c)
189{
190 while (fl != NULL)
191 {
192 FL *link = fl->link;
193 if (stats != NULL)
194 stats->change (stat_discarded, c);
195 GNUNET_free (fl->frag);
196 GNUNET_free (fl);
197 fl = link;
198 }
199}
200
201/**
202 * This cron job ensures that we purge buffers of fragments
203 * that have timed out. It can run in much longer intervals
204 * than the defragmentationCron, e.g. every 60s.
205 * <p>
206 * This method goes through the hashtable, finds entries that
207 * have timed out and removes them (and all the fragments that
208 * belong to the entry). It's a bit more complicated as the
209 * collision list is also collapsed.
210 */
211static void
212defragmentationPurgeCron (void *unused)
213{
214 int i;
215 FC *smf;
216 FC *next;
217 FC *last;
218
219 GNUNET_mutex_lock (defragCacheLock);
220 for (i = 0; i < DEFRAG_BUCKET_COUNT; i++)
221 {
222 last = NULL;
223 smf = defragmentationCache[i];
224 while (smf != NULL)
225 {
226 if (smf->ttl < GNUNET_get_time ())
227 {
228 /* free linked list of fragments */
229 freeFL (smf->head, 1);
230 next = smf->next;
231 GNUNET_free (smf);
232 if (last == NULL)
233 defragmentationCache[i] = next;
234 else
235 last->next = next;
236 smf = next;
237 }
238 else
239 {
240 last = smf;
241 smf = smf->next;
242 }
243 } /* while smf != NULL */
244 } /* for all buckets */
245 GNUNET_mutex_unlock (defragCacheLock);
246}
247
248/**
249 * Check if this fragment-list is complete. If yes, put it together,
250 * process and free all buffers. Does not free the pep
251 * itself (but sets the TTL to 0 to have the cron free it
252 * in the next iteration).
253 *
254 * @param pep the entry in the GNUNET_hash table
255 */
256static void
257checkComplete (FC * pep)
258{
259 FL *pos;
260 unsigned short off;
261 unsigned short len;
262 char *msg;
263
264 GNUNET_GE_ASSERT (NULL, pep != NULL);
265 pos = pep->head;
266 if (pos == NULL)
267 return;
268 len = ntohs (pos->frag->len);
269 if (len == 0)
270 goto CLEANUP; /* really bad error! */
271 off = 0;
272 while ((pos != NULL) && (ntohs (pos->frag->off) <= off))
273 {
274 if (off >= off + FRAGSIZE (pos))
275 goto CLEANUP; /* error! */
276 if (ntohs (pos->frag->off) + FRAGSIZE (pos) > off)
277 off = ntohs (pos->frag->off) + FRAGSIZE (pos);
278 else
279 goto CLEANUP; /* error! */
280 pos = pos->link;
281 }
282 if (off < len)
283 return; /* some fragment is still missing */
284
285 msg = GNUNET_malloc (len);
286 pos = pep->head;
287 while (pos != NULL)
288 {
289 memcpy (&msg[ntohs (pos->frag->off)], &pos->frag[1], FRAGSIZE (pos));
290 pos = pos->link;
291 }
292 if (stats != NULL)
293 stats->change (stat_defragmented, 1);
294#if 0
295 printf ("Finished defragmentation!\n");
296#endif
297 /* handle message! */
298 coreAPI->loopback_send (&pep->sender, msg, len, GNUNET_YES, NULL);
299 GNUNET_free (msg);
300CLEANUP:
301 /* free fragment buffers */
302 freeFL (pep->head, 0);
303 pep->head = NULL;
304 pep->ttl = 0;
305}
306
307/**
308 * See if the new fragment is a part of this entry and join them if
309 * yes. Return GNUNET_SYSERR if the fragments do not match. Return GNUNET_OK if
310 * the fragments do match and the fragment has been processed. The
311 * defragCacheLock is already acquired by the caller whenever this
312 * method is called.<p>
313 *
314 * @param entry the entry in the cache
315 * @param pep the new entry
316 * @param packet the ip part in the new entry
317 */
318static int
319tryJoin (FC * entry,
320 const GNUNET_PeerIdentity * sender,
321 const P2P_fragmentation_MESSAGE * packet)
322{
323 /* frame before ours; may end in the middle of
324 our frame or before it starts; NULL if we are
325 the earliest position we have received so far */
326 FL *before;
327 /* frame after ours; may start in the middle of
328 our frame or after it; NULL if we are the last
329 fragment we have received so far */
330 FL *after;
331 /* current position in the frame-list */
332 FL *pos;
333 /* the new entry that we're inserting */
334 FL *pep;
335 FL *tmp;
336 unsigned short end;
337
338 GNUNET_GE_ASSERT (NULL, entry != NULL);
339 if (0 != memcmp (sender, &entry->sender, sizeof (GNUNET_PeerIdentity)))
340 return GNUNET_SYSERR; /* wrong fragment list, try another! */
341 if (ntohl (packet->id) != entry->id)
342 return GNUNET_SYSERR; /* wrong fragment list, try another! */
343#if 0
344 printf ("Received fragment %u from %u to %u\n",
345 ntohl (packet->id),
346 ntohs (packet->off),
347 ntohs (packet->off) + ntohs (packet->header.size) -
348 sizeof (P2P_fragmentation_MESSAGE));
349#endif
350 pos = entry->head;
351 if ((pos != NULL) && (packet->len != pos->frag->len))
352 return GNUNET_SYSERR; /* wrong fragment size */
353
354 before = NULL;
355 /* find the before-frame */
356 while ((pos != NULL) && (ntohs (pos->frag->off) < ntohs (packet->off)))
357 {
358 before = pos;
359 pos = pos->link;
360 }
361
362 /* find the after-frame */
363 end =
364 ntohs (packet->off) + ntohs (packet->header.size) -
365 sizeof (P2P_fragmentation_MESSAGE);
366 if (end <= ntohs (packet->off))
367 {
368 GNUNET_GE_LOG (NULL,
369 GNUNET_GE_DEVELOPER | GNUNET_GE_DEBUG | GNUNET_GE_BULK,
370 "Received invalid fragment at %s:%d\n", __FILE__,
371 __LINE__);
372 return GNUNET_SYSERR; /* yuck! integer overflow! */
373 }
374
375 if (before != NULL)
376 after = before;
377 else
378 after = entry->head;
379 while ((after != NULL) && (ntohs (after->frag->off) < end))
380 after = after->link;
381
382 if ((before != NULL) && (before == after))
383 {
384 /* this implies after or before != NULL and thereby the new
385 fragment is redundant as it is fully enclosed in an earlier
386 fragment */
387 if (stats != NULL)
388 stats->change (stat_defragmented, 1);
389 return GNUNET_OK; /* drop, there is a packet that spans our range! */
390 }
391
392 if ((before != NULL) &&
393 (after != NULL) &&
394 ((htons (before->frag->off) +
395 FRAGSIZE (before)) >= htons (after->frag->off)))
396 {
397 /* this implies that the fragment that starts before us and the
398 fragment that comes after this one leave no space in the middle
399 or even overlap; thus we can drop this redundant piece */
400 if (stats != NULL)
401 stats->change (stat_defragmented, 1);
402 return GNUNET_OK;
403 }
404
405 /* allocate pep */
406 pep = GNUNET_malloc (sizeof (FC));
407 pep->frag = GNUNET_malloc (ntohs (packet->header.size));
408 memcpy (pep->frag, packet, ntohs (packet->header.size));
409 pep->link = NULL;
410
411 if (before == NULL)
412 {
413 pep->link = after;
414 pos = entry->head;
415 while (pos != after)
416 {
417 tmp = pos->link;
418 GNUNET_free (pos->frag);
419 GNUNET_free (pos);
420 pos = tmp;
421 }
422 entry->head = pep;
423 goto FINISH;
424 /* end of insert first */
425 }
426
427 if (after == NULL)
428 {
429 /* insert last: find the end, free everything after it */
430 freeFL (before->link, 1);
431 before->link = pep;
432 goto FINISH;
433 }
434
435 /* ok, we are filling the middle between two fragments; insert. If
436 there is anything else in the middle, it can be dropped as we're
437 bigger & cover that area as well */
438 /* free everything between before and after */
439 pos = before->link;
440 while (pos != after)
441 {
442 tmp = pos->link;
443 GNUNET_free (pos->frag);
444 GNUNET_free (pos);
445 pos = tmp;
446 }
447 before->link = pep;
448 pep->link = after;
449
450FINISH:
451 entry->ttl = GNUNET_get_time () + DEFRAGMENTATION_TIMEOUT;
452 checkComplete (entry);
453 return GNUNET_OK;
454}
455
456/**
457 * Defragment the given fragment and pass to handler once
458 * defragmentation is complete.
459 *
460 * @param frag the packet to defragment
461 * @return GNUNET_SYSERR if the fragment is invalid
462 */
463static int
464processFragment (const GNUNET_PeerIdentity * sender,
465 const GNUNET_MessageHeader * frag)
466{
467 unsigned int hash;
468 FC *smf;
469
470 if (ntohs (frag->size) < sizeof (P2P_fragmentation_MESSAGE))
471 return GNUNET_SYSERR;
472
473 GNUNET_mutex_lock (defragCacheLock);
474 hash = sender->hashPubKey.bits[0] % DEFRAG_BUCKET_COUNT;
475 smf = defragmentationCache[hash];
476 while (smf != NULL)
477 {
478 if (GNUNET_OK ==
479 tryJoin (smf, sender, (P2P_fragmentation_MESSAGE *) frag))
480 {
481 GNUNET_mutex_unlock (defragCacheLock);
482 return GNUNET_OK;
483 }
484 if (0 == memcmp (sender, &smf->sender, sizeof (GNUNET_PeerIdentity)))
485 {
486 freeFL (smf->head, 1);
487 break;
488 }
489 smf = smf->next;
490 }
491 if (smf == NULL)
492 {
493 smf = GNUNET_malloc (sizeof (FC));
494 smf->next = defragmentationCache[hash];
495 defragmentationCache[hash] = smf;
496 smf->ttl = GNUNET_get_time () + DEFRAGMENTATION_TIMEOUT;
497 smf->sender = *sender;
498 }
499 smf->id = ntohl (((P2P_fragmentation_MESSAGE *) frag)->id);
500 smf->head = GNUNET_malloc (sizeof (FL));
501 smf->head->link = NULL;
502 smf->head->frag = GNUNET_malloc (ntohs (frag->size));
503 memcpy (smf->head->frag, frag, ntohs (frag->size));
504
505 GNUNET_mutex_unlock (defragCacheLock);
506 return GNUNET_OK;
507}
508
509typedef struct
510{
511 GNUNET_PeerIdentity sender;
512 /* maximums size of each fragment */
513 unsigned short mtu;
514 /** how long is this message part expected to be? */
515 unsigned short len;
516 /** when did we intend to transmit? */
517 GNUNET_CronTime transmissionTime;
518} FragmentBMC;
519
520/**
521 * Send a message that had to be fragmented (right now!). First grabs
522 * the first part of the message (obtained from ctx->se) and stores
523 * that in a P2P_fragmentation_MESSAGE envelope. The remaining fragments are
524 * added to the send queue with GNUNET_EXTREME_PRIORITY (to ensure that they
525 * will be transmitted next). The logic here is that if the priority
526 * for the first fragment was sufficiently high, the priority should
527 * also have been sufficiently high for all of the other fragments (at
528 * this time) since they have the same priority. And we want to make
529 * sure that we send all of them since just sending the first fragment
530 * and then going to other messages of equal priority would not be
531 * such a great idea (i.e. would just waste bandwidth).
532 */
533static int
534fragmentBMC (void *buf, void *cls, unsigned short len)
535{
536 FragmentBMC *ctx = cls;
537 static int idGen = 0;
538 P2P_fragmentation_MESSAGE *frag;
539 unsigned int pos;
540 int id;
541 unsigned short mlen;
542
543 if ((len < ctx->mtu) || (buf == NULL))
544 {
545 GNUNET_free (ctx);
546 return GNUNET_SYSERR;
547 }
548 if (stats != NULL)
549 stats->change (stat_fragmented, 1);
550 id = (idGen++) + GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 512);
551 /* write first fragment to buf */
552 frag = (P2P_fragmentation_MESSAGE *) buf;
553 frag->header.size = htons (len);
554 frag->header.type = htons (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT);
555 frag->id = id;
556 frag->off = htons (0);
557 frag->len = htons (ctx->len);
558 memcpy (&frag[1], &ctx[1], len - sizeof (P2P_fragmentation_MESSAGE));
559
560 /* create remaining fragments, add to queue! */
561 pos = len - sizeof (P2P_fragmentation_MESSAGE);
562 frag = GNUNET_malloc (ctx->mtu);
563 while (pos < ctx->len)
564 {
565 mlen = sizeof (P2P_fragmentation_MESSAGE) + ctx->len - pos;
566 if (mlen > ctx->mtu)
567 mlen = ctx->mtu;
568 GNUNET_GE_ASSERT (NULL, mlen > sizeof (P2P_fragmentation_MESSAGE));
569 frag->header.size = htons (mlen);
570 frag->header.type = htons (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT);
571 frag->id = id;
572 frag->off = htons (pos);
573 frag->len = htons (ctx->len);
574 memcpy (&frag[1],
575 &((char *) (&ctx[1]))[pos],
576 mlen - sizeof (P2P_fragmentation_MESSAGE));
577 coreAPI->ciphertext_send (&ctx->sender,
578 &frag->header,
579 GNUNET_EXTREME_PRIORITY,
580 ctx->transmissionTime - GNUNET_get_time ());
581 pos += mlen - sizeof (P2P_fragmentation_MESSAGE);
582 }
583 GNUNET_GE_ASSERT (NULL, pos == ctx->len);
584 GNUNET_free (frag);
585 GNUNET_free (ctx);
586 return GNUNET_OK;
587}
588
589/**
590 * The given message must be fragmented. Produce a placeholder that
591 * corresponds to the first fragment. Once that fragment is scheduled
592 * for transmission, the placeholder should automatically add all of
593 * the other fragments (with very high priority).
594 */
595void
596fragment (const GNUNET_PeerIdentity * peer,
597 unsigned int mtu,
598 unsigned int prio,
599 unsigned int targetTime,
600 unsigned int len, GNUNET_BuildMessageCallback bmc, void *bmcClosure)
601{
602 FragmentBMC *fbmc;
603 int xlen;
604
605 GNUNET_GE_ASSERT (NULL, len > mtu);
606 GNUNET_GE_ASSERT (NULL, mtu > sizeof (P2P_fragmentation_MESSAGE));
607 fbmc = GNUNET_malloc (sizeof (FragmentBMC) + len);
608 fbmc->mtu = mtu;
609 fbmc->sender = *peer;
610 fbmc->transmissionTime = targetTime;
611 fbmc->len = len;
612 if (bmc == NULL)
613 {
614 memcpy (&fbmc[1], bmcClosure, len);
615 GNUNET_free (bmcClosure);
616 }
617 else
618 {
619 if (GNUNET_SYSERR == bmc (&fbmc[1], bmcClosure, len))
620 {
621 GNUNET_free (fbmc);
622 return;
623 }
624 }
625 xlen = mtu - sizeof (P2P_fragmentation_MESSAGE);
626 coreAPI->ciphertext_send_with_callback (peer, &fragmentBMC, fbmc, mtu, prio * xlen / len, /* compute new priority */
627 targetTime);
628}
629
630/**
631 * Initialize Fragmentation module.
632 */
633GNUNET_Fragmentation_ServiceAPI *
634provide_module_fragmentation (GNUNET_CoreAPIForPlugins * capi)
635{
636 static GNUNET_Fragmentation_ServiceAPI ret;
637 int i;
638
639 coreAPI = capi;
640 stats = coreAPI->service_request ("stats");
641 if (stats != NULL)
642 {
643 stat_defragmented =
644 stats->create (gettext_noop ("# messages defragmented"));
645 stat_fragmented =
646 stats->create (gettext_noop ("# messages fragmented"));
647 stat_discarded = stats->create (gettext_noop ("# fragments discarded"));
648 }
649 for (i = 0; i < DEFRAG_BUCKET_COUNT; i++)
650 defragmentationCache[i] = NULL;
651 defragCacheLock = GNUNET_mutex_create (GNUNET_NO);
652 GNUNET_cron_add_job (coreAPI->cron,
653 &defragmentationPurgeCron,
654 60 * GNUNET_CRON_SECONDS, 60 * GNUNET_CRON_SECONDS,
655 NULL);
656 GNUNET_GE_LOG (capi->ectx,
657 GNUNET_GE_INFO | GNUNET_GE_USER | GNUNET_GE_REQUEST,
658 _("`%s' registering handler %d\n"), "fragmentation",
659 GNUNET_P2P_PROTO_MESSAGE_FRAGMENT);
660 capi->p2p_ciphertext_handler_register (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT,
661 &processFragment);
662
663 ret.fragment = &fragment;
664 return &ret;
665}
666
667/**
668 * Shutdown fragmentation.
669 */
670void
671release_module_fragmentation ()
672{
673 int i;
674
675 coreAPI->p2p_ciphertext_handler_unregister
676 (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT, &processFragment);
677 GNUNET_cron_del_job (coreAPI->cron, &defragmentationPurgeCron,
678 60 * GNUNET_CRON_SECONDS, NULL);
679 for (i = 0; i < DEFRAG_BUCKET_COUNT; i++)
680 {
681 FC *pos = defragmentationCache[i];
682 while (pos != NULL)
683 {
684 FC *next = pos->next;
685 freeFL (pos->head, 1);
686 GNUNET_free (pos);
687 pos = next;
688 }
689 }
690 if (stats != NULL)
691 {
692 coreAPI->service_release (stats);
693 stats = NULL;
694 }
695 GNUNET_mutex_destroy (defragCacheLock);
696 defragCacheLock = NULL;
697 coreAPI = NULL;
698}
699
700#endif
701
702/* end of fragmentation.c */
diff --git a/src/fragmentation/test_fragmentation.c b/src/fragmentation/test_fragmentation.c
new file mode 100644
index 000000000..e6e3e5d22
--- /dev/null
+++ b/src/fragmentation/test_fragmentation.c
@@ -0,0 +1,439 @@
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 fragmentation/test_fragmentation.c
22 * @brief test for fragmentation.c
23 * @author Christian Grothoff
24 */
25
26/**
27 * Testcase for defragmentation code.
28 * We have testcases for:
29 * - 2 fragments, aligned, [0,16),[16,32)
30 * - n (50) fragments, [i*16,(i+1)*16)
31 * - n (50) fragments, [0,i*16) + [50*16,51*16)
32 * - n (100) fragments, inserted in interleaved order (holes in sequence)
33 * - holes in sequence
34 * - other overlaps
35 * - timeouts
36 * - multiple entries in GNUNET_hash-list
37 * - id collisions in GNUNET_hash-list
38 */
39
40#include "platform.h"
41#include "gnunet_fragmentation_lib.h"
42
43#if 0
44
45/* -- to speed up the testcases -- */
46#define DEFRAGMENTATION_TIMEOUT (1 * GNUNET_CRON_SECONDS)
47
48
49static GNUNET_PeerIdentity mySender;
50static char *myMsg;
51static unsigned short myMsgLen;
52
53/* static buffers to avoid lots of malloc/free */
54static char masterBuffer[65536];
55static char resultBuffer[65536];
56
57static void
58handleHelper (const GNUNET_PeerIdentity * sender,
59 const char *msg,
60 const unsigned int len, int wasEncrypted, GNUNET_TSession * ts)
61{
62 GNUNET_GE_ASSERT (NULL,
63 0 == memcmp (sender, &mySender,
64 sizeof (GNUNET_PeerIdentity)));
65 myMsg = resultBuffer;
66 memcpy (resultBuffer, msg, len);
67 myMsgLen = len;
68}
69
70/**
71 * Wait long enough to force all fragments to timeout.
72 */
73static void
74makeTimeout ()
75{
76 GNUNET_thread_sleep (DEFRAGMENTATION_TIMEOUT * 2);
77 defragmentationPurgeCron (NULL);
78}
79
80/**
81 * Create a fragment. The data-portion will be filled
82 * with a sequence of numbers from start+id to start+len-1+id.
83 *
84 * @param pep pointer to the ethernet frame/buffer
85 * @param ip pointer to the ip-header
86 * @param start starting-offset
87 * @param length of the data portion
88 * @param id the identity of the fragment
89 */
90static GNUNET_MessageHeader *
91makeFragment (unsigned short start,
92 unsigned short size, unsigned short tot, int id)
93{
94 P2P_fragmentation_MESSAGE *frag;
95 int i;
96
97 frag = (P2P_fragmentation_MESSAGE *) masterBuffer;
98 frag->id = htonl (id);
99 frag->off = htons (start);
100 frag->len = htons (tot);
101 frag->header.size = htons (sizeof (P2P_fragmentation_MESSAGE) + size);
102
103 for (i = 0; i < size; i++)
104 ((char *) &frag[1])[i] = (char) i + id + start;
105 return &frag->header;
106}
107
108/**
109 * Check that the packet received is what we expected to
110 * get.
111 * @param id the expected id
112 * @param len the expected length
113 */
114static void
115checkPacket (int id, unsigned int len)
116{
117 int i;
118
119 GNUNET_GE_ASSERT (NULL, myMsg != NULL);
120 GNUNET_GE_ASSERT (NULL, myMsgLen == len);
121 for (i = 0; i < len; i++)
122 GNUNET_GE_ASSERT (NULL, myMsg[i] == (char) (i + id));
123 myMsgLen = 0;
124 myMsg = NULL;
125}
126
127
128/* **************** actual testcases ***************** */
129
130static void
131testSimpleFragment ()
132{
133 GNUNET_MessageHeader *pep;
134
135 pep = makeFragment (0, 16, 32, 42);
136 processFragment (&mySender, pep);
137 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
138 pep = makeFragment (16, 16, 32, 42);
139 processFragment (&mySender, pep);
140 checkPacket (42, 32);
141}
142
143static void
144testSimpleFragmentTimeout ()
145{
146 GNUNET_MessageHeader *pep;
147
148 pep = makeFragment (0, 16, 32, 42);
149 processFragment (&mySender, pep);
150 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
151 makeTimeout ();
152 pep = makeFragment (16, 16, 32, 42);
153 processFragment (&mySender, pep);
154 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
155 pep = makeFragment (0, 16, 32, 42);
156 processFragment (&mySender, pep);
157 checkPacket (42, 32);
158}
159
160static void
161testSimpleFragmentReverse ()
162{
163 GNUNET_MessageHeader *pep;
164
165 pep = makeFragment (16, 16, 32, 42);
166 processFragment (&mySender, pep);
167 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
168 pep = makeFragment (0, 16, 32, 42);
169 processFragment (&mySender, pep);
170 checkPacket (42, 32);
171}
172
173static void
174testManyFragments ()
175{
176 GNUNET_MessageHeader *pep;
177 int i;
178
179 for (i = 0; i < 50; i++)
180 {
181 pep = makeFragment (i * 16, 16, 51 * 16, 42);
182 processFragment (&mySender, pep);
183 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
184 }
185 pep = makeFragment (50 * 16, 16, 51 * 16, 42);
186 processFragment (&mySender, pep);
187 checkPacket (42, 51 * 16);
188}
189
190static void
191testManyFragmentsMegaLarge ()
192{
193 GNUNET_MessageHeader *pep;
194 int i;
195
196 for (i = 0; i < 4000; i++)
197 {
198 pep = makeFragment (i * 16, 16, 4001 * 16, 42);
199 processFragment (&mySender, pep);
200 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
201 }
202 pep = makeFragment (4000 * 16, 16, 4001 * 16, 42);
203 processFragment (&mySender, pep);
204 checkPacket (42, 4001 * 16);
205}
206
207static void
208testLastFragmentEarly ()
209{
210 GNUNET_MessageHeader *pep;
211 int i;
212
213 for (i = 0; i < 5; i++)
214 {
215 pep = makeFragment (i * 16, 8, 6 * 16 + 8, 42);
216 processFragment (&mySender, pep);
217 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
218 }
219 pep = makeFragment (5 * 16, 24, 6 * 16 + 8, 42);
220 processFragment (&mySender, pep);
221 for (i = 0; i < 5; i++)
222 {
223 pep = makeFragment (i * 16 + 8, 8, 6 * 16 + 8, 42);
224 processFragment (&mySender, pep);
225 }
226 checkPacket (42, 6 * 16 + 8);
227}
228
229static void
230testManyInterleavedFragments ()
231{
232 GNUNET_MessageHeader *pep;
233 int i;
234
235 for (i = 0; i < 50; i++)
236 {
237 pep = makeFragment (i * 16, 8, 51 * 16 + 8, 42);
238 processFragment (&mySender, pep);
239 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
240 }
241 for (i = 0; i < 50; i++)
242 {
243 pep = makeFragment (i * 16 + 8, 8, 51 * 16 + 8, 42);
244 processFragment (&mySender, pep);
245 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
246 }
247 pep = makeFragment (50 * 16, 24, 51 * 16 + 8, 42);
248 processFragment (&mySender, pep);
249 checkPacket (42, 51 * 16 + 8);
250}
251
252static void
253testManyInterleavedOverlappingFragments ()
254{
255 GNUNET_MessageHeader *pep;
256 int i;
257
258 for (i = 0; i < 50; i++)
259 {
260 pep = makeFragment (i * 32, 16, 51 * 32, 42);
261 processFragment (&mySender, pep);
262 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
263 }
264 for (i = 0; i < 50; i++)
265 {
266 pep = makeFragment (i * 32 + 8, 24, 51 * 32, 42);
267 processFragment (&mySender, pep);
268 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
269 }
270 pep = makeFragment (50 * 32, 32, 51 * 32, 42);
271 processFragment (&mySender, pep);
272 checkPacket (42, 51 * 32);
273}
274
275static void
276testManyOverlappingFragments ()
277{
278 GNUNET_MessageHeader *pep;
279 int i;
280
281 for (i = 0; i < 50; i++)
282 {
283 pep = makeFragment (0, i * 16 + 16, 51 * 16, 42);
284 processFragment (&mySender, pep);
285 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
286 }
287 pep = makeFragment (50 * 16, 16, 51 * 16, 42);
288 processFragment (&mySender, pep);
289 checkPacket (42, 51 * 16);
290}
291
292static void
293testManyOverlappingFragmentsTimeout ()
294{
295 GNUNET_MessageHeader *pep;
296 int i;
297
298 for (i = 0; i < 50; i++)
299 {
300 pep = makeFragment (0, i * 16 + 16, 51 * 16 + 8, 42);
301 processFragment (&mySender, pep);
302 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
303 }
304 makeTimeout ();
305 pep = makeFragment (50 * 16, 24, 51 * 16 + 8, 42);
306 processFragment (&mySender, pep);
307 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
308 for (i = 0; i < 50; i++)
309 {
310 pep = makeFragment (0, i * 16 + 16, 51 * 16 + 8, 42);
311 processFragment (&mySender, pep);
312 }
313 checkPacket (42, 51 * 16 + 8);
314}
315
316static void
317testManyFragmentsMultiId ()
318{
319 GNUNET_MessageHeader *pep;
320 int i;
321 int id;
322
323 for (i = 0; i < 50; i++)
324 {
325 for (id = 0; id < DEFRAG_BUCKET_COUNT; id++)
326 {
327 pep = makeFragment (i * 16, 16, 51 * 16, id + 5);
328 mySender.hashPubKey.bits[0] = id;
329 processFragment (&mySender, pep);
330 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
331 }
332 }
333 for (id = 0; id < DEFRAG_BUCKET_COUNT; id++)
334 {
335 pep = makeFragment (50 * 16, 16, 51 * 16, id + 5);
336 mySender.hashPubKey.bits[0] = id;
337 processFragment (&mySender, pep);
338 checkPacket (id + 5, 51 * 16);
339 }
340}
341
342static void
343testManyFragmentsMultiIdCollisions ()
344{
345 GNUNET_MessageHeader *pep;
346 int i;
347 int id;
348
349 for (i = 0; i < 5; i++)
350 {
351 for (id = 0; id < DEFRAG_BUCKET_COUNT * 4; id++)
352 {
353 pep = makeFragment (i * 16, 16, 6 * 16, id + 5);
354 mySender.hashPubKey.bits[0] = id;
355 processFragment (&mySender, pep);
356 GNUNET_GE_ASSERT (NULL, myMsg == NULL);
357 }
358 }
359 for (id = 0; id < DEFRAG_BUCKET_COUNT * 4; id++)
360 {
361 pep = makeFragment (5 * 16, 16, 6 * 16, id + 5);
362 mySender.hashPubKey.bits[0] = id;
363 processFragment (&mySender, pep);
364 checkPacket (id + 5, 6 * 16);
365 }
366}
367
368/* ************* driver ****************** */
369
370static int
371p2p_register_handler (const unsigned short type,
372 GNUNET_P2PRequestHandler callback)
373{
374 return GNUNET_OK;
375}
376
377static int
378p2p_unregister_handler (const unsigned short type,
379 GNUNET_P2PRequestHandler callback)
380{
381 return GNUNET_OK;
382}
383
384
385static void *
386request_service (const char *name)
387{
388 return NULL;
389}
390
391#endif
392
393int
394main (int argc, char *argv[])
395{
396 fprintf (stderr, "WARNING: testcase not yet ported to new API.\n");
397#if 0
398 GNUNET_CoreAPIForPlugins capi;
399
400 memset (&capi, 0, sizeof (GNUNET_CoreAPIForPlugins));
401 capi.cron = GNUNET_cron_create (NULL);
402 capi.loopback_send = &handleHelper;
403 capi.service_request = &request_service;
404 capi.p2p_ciphertext_handler_register = &p2p_register_handler;
405 capi.p2p_ciphertext_handler_unregister = &p2p_unregister_handler;
406 provide_module_fragmentation (&capi);
407
408 fprintf (stderr, ".");
409 testSimpleFragment ();
410 fprintf (stderr, ".");
411 testSimpleFragmentTimeout ();
412 fprintf (stderr, ".");
413 testSimpleFragmentReverse ();
414 fprintf (stderr, ".");
415 testManyFragments ();
416 fprintf (stderr, ".");
417 testManyFragmentsMegaLarge ();
418 fprintf (stderr, ".");
419 testManyFragmentsMultiId ();
420 fprintf (stderr, ".");
421
422 testManyInterleavedFragments ();
423 fprintf (stderr, ".");
424 testManyInterleavedOverlappingFragments ();
425 fprintf (stderr, ".");
426 testManyOverlappingFragments ();
427 fprintf (stderr, ".");
428 testManyOverlappingFragmentsTimeout ();
429 fprintf (stderr, ".");
430 testLastFragmentEarly ();
431 fprintf (stderr, ".");
432 testManyFragmentsMultiIdCollisions ();
433 fprintf (stderr, ".");
434 release_module_fragmentation ();
435 fprintf (stderr, "\n");
436 GNUNET_cron_destroy (capi.cron);
437#endif
438 return 0; /* testcase passed */
439}
diff --git a/src/hello/Makefile.am b/src/hello/Makefile.am
new file mode 100644
index 000000000..cffb7201c
--- /dev/null
+++ b/src/hello/Makefile.am
@@ -0,0 +1,28 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = --coverage
9endif
10
11lib_LTLIBRARIES = libgnunethello.la
12
13libgnunethello_la_SOURCES = \
14 hello.c
15libgnunethello_la_LIBADD = \
16 $(top_builddir)/src/util/libgnunetutil.la
17
18check_PROGRAMS = \
19 test_hello
20
21TESTS = $(check_PROGRAMS)
22
23test_hello_SOURCES = \
24 test_hello.c
25test_hello_LDADD = \
26 $(top_builddir)/src/hello/libgnunethello.la \
27 $(top_builddir)/src/util/libgnunetutil.la
28
diff --git a/src/hello/hello.c b/src/hello/hello.c
new file mode 100644
index 000000000..5a21bd41f
--- /dev/null
+++ b/src/hello/hello.c
@@ -0,0 +1,482 @@
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 hello/hello.c
23 * @brief helper library for handling HELLOs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_hello_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_server_lib.h"
30
31/**
32 * A HELLO message is used to exchange information about
33 * transports with other peers. This struct is always
34 * followed by the actual network addresses which have
35 * the format:
36 *
37 * 1) transport-name (0-terminated)
38 * 2) address-length (uint32_t, network byte order; possibly
39 * unaligned!)
40 * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly
41 * unaligned!)
42 * 4) address (address-length bytes; possibly unaligned!)
43 */
44struct GNUNET_HELLO_Message
45{
46 /**
47 * Type will be GNUNET_MESSAGE_TYPE_HELLO.
48 */
49 struct GNUNET_MessageHeader header;
50
51 /**
52 * Always zero (for alignment).
53 */
54 uint32_t reserved GNUNET_PACKED;
55
56 /**
57 * The public key of the peer.
58 */
59 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
60
61};
62
63
64/**
65 * Copy the given address information into
66 * the given buffer using the format of HELLOs.
67 *
68 * @param tname name of the transport plugin
69 * @param expiration expiration for the address
70 * @param addr the address
71 * @param addr_len length of the address in bytes
72 * @param target where to copy the address
73 * @param max maximum number of bytes to copy to target
74 * @return number of bytes copied, 0 if
75 * the target buffer was not big enough.
76 */
77size_t
78GNUNET_HELLO_add_address (const char *tname,
79 struct GNUNET_TIME_Absolute expiration,
80 const void *addr,
81 size_t addr_len, char *target, size_t max)
82{
83 uint32_t alen;
84 size_t slen;
85 struct GNUNET_TIME_AbsoluteNBO exp;
86
87 slen = strlen (tname) + 1;
88 if (slen + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
89 addr_len > max)
90 return 0;
91 exp = GNUNET_TIME_absolute_hton (expiration);
92 alen = htonl ((uint32_t) addr_len);
93 memcpy (target, tname, slen);
94 memcpy (&target[slen], &alen, sizeof (uint32_t));
95 slen += sizeof (uint32_t);
96 memcpy (&target[slen], &exp, sizeof (struct GNUNET_TIME_AbsoluteNBO));
97 slen += sizeof (struct GNUNET_TIME_AbsoluteNBO);
98 memcpy (&target[slen], addr, addr_len);
99 slen += addr_len;
100 return slen;
101}
102
103
104/**
105 * Get the size of an address entry in a HELLO message.
106 *
107 * @param buf pointer to the start of the address entry
108 * @param max maximum size of the entry (end of buf)
109 * @param ralen set to the address length
110 * @return size of the entry, or 0 if max is not large enough
111 */
112static size_t
113get_hello_address_size (const char *buf, size_t max, uint32_t * ralen)
114{
115 const char *pos;
116 uint32_t alen;
117 size_t left;
118 size_t slen;
119
120 left = max;
121 pos = buf;
122 slen = 1;
123 while ((left > 0) && ('\0' != *pos))
124 {
125 left--;
126 pos++;
127 slen++;
128 }
129 if ('\0' != *pos)
130 {
131 /* 0-termination not found */
132 GNUNET_break_op (0);
133 return 0;
134 }
135 pos++;
136 if (left < sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO))
137 {
138 /* not enough space for addrlen */
139 GNUNET_break_op (0);
140 return 0;
141 }
142 memcpy (&alen, pos, sizeof (uint32_t));
143 alen = ntohl (alen);
144 *ralen = alen;
145 slen += alen + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO);
146 if (max < slen)
147 {
148 /* not enough space for addr */
149 GNUNET_break_op (0);
150 return 0;
151 }
152 return slen;
153}
154
155
156/**
157 * Construct a HELLO message given the public key,
158 * expiration time and an iterator that spews the
159 * transport addresses.
160 *
161 * @return the hello message
162 */
163struct GNUNET_HELLO_Message *
164GNUNET_HELLO_create (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
165 *publicKey,
166 GNUNET_HELLO_GenerateAddressListCallback addrgen,
167 void *addrgen_cls)
168{
169 char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 256 -
170 sizeof (struct GNUNET_HELLO_Message)];
171 size_t max;
172 size_t used;
173 size_t ret;
174 struct GNUNET_HELLO_Message *hello;
175
176 max = sizeof (buffer);
177 used = 0;
178 while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used])))
179 {
180 max -= ret;
181 used += ret;
182 }
183 hello = GNUNET_malloc (sizeof (struct GNUNET_HELLO_Message) + used);
184 hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO);
185 hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used);
186 memcpy (&hello->publicKey, publicKey,
187 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
188 memcpy (&hello[1], buffer, used);
189 return hello;
190}
191
192
193/**
194 * Iterate over all of the addresses in the HELLO.
195 *
196 * @param msg HELLO to iterate over
197 * @param return_modified if a modified copy should be returned,
198 * otherwise NULL will be returned
199 * @param it iterator to call on each address
200 * @param it_cls closure for it
201 */
202struct GNUNET_HELLO_Message *
203GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg,
204 int return_modified,
205 GNUNET_HELLO_AddressIterator it, void *it_cls)
206{
207 uint16_t msize;
208 struct GNUNET_HELLO_Message *ret;
209 const char *inptr;
210 size_t insize;
211 size_t esize;
212 size_t wpos;
213 char *woff;
214 uint32_t alen;
215 struct GNUNET_TIME_AbsoluteNBO expire;
216 int iret;
217
218 msize = GNUNET_HELLO_size (msg);
219 if ((msize < sizeof (struct GNUNET_HELLO_Message)) ||
220 (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
221 return NULL;
222 ret = NULL;
223 if (return_modified)
224 {
225 ret = GNUNET_malloc (msize);
226 memcpy (ret, msg, msize);
227 }
228 inptr = (const char *) &msg[1];
229 insize = msize - sizeof (struct GNUNET_HELLO_Message);
230 wpos = 0;
231 woff = (ret != NULL) ? (char *) &ret[1] : NULL;
232 while (insize > 0)
233 {
234 esize = get_hello_address_size (inptr, insize, &alen);
235 if (esize == 0)
236 {
237 GNUNET_break (0);
238 GNUNET_free_non_null (ret);
239 return NULL;
240 }
241 memcpy (&expire,
242 &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)],
243 sizeof (struct GNUNET_TIME_AbsoluteNBO));
244 iret = it (it_cls,
245 inptr,
246 GNUNET_TIME_absolute_ntoh (expire),
247 &inptr[esize - alen], alen);
248 if (iret == GNUNET_SYSERR)
249 {
250 if (ret != NULL)
251 ret->header.size =
252 ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
253 return ret;
254 }
255 if ((iret == GNUNET_OK) && (ret != NULL))
256 {
257 memcpy (woff, inptr, esize);
258 woff += esize;
259 wpos += esize;
260 }
261 insize -= esize;
262 inptr += esize;
263 }
264 if (ret != NULL)
265 ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
266 return ret;
267}
268
269
270struct ExpireContext
271{
272 const void *addr;
273 size_t addrlen;
274 int found;
275 struct GNUNET_TIME_Absolute expiration;
276};
277
278
279static int
280get_match_exp (void *cls,
281 const char *tname,
282 struct GNUNET_TIME_Absolute expiration,
283 const void *addr, size_t addrlen)
284{
285 struct ExpireContext *ec = cls;
286
287 if ((addrlen == ec->addrlen) && (0 == memcmp (addr, ec->addr, addrlen)))
288 {
289 ec->found = GNUNET_YES;
290 ec->expiration = expiration;
291 return GNUNET_SYSERR; /* done here */
292 }
293 return GNUNET_OK;
294}
295
296
297struct MergeContext
298{
299 const struct GNUNET_HELLO_Message *h1;
300 const struct GNUNET_HELLO_Message *h2;
301 const struct GNUNET_HELLO_Message *other;
302 char *buf;
303 size_t max;
304 size_t ret;
305 int take_equal;
306
307};
308
309
310static int
311copy_latest (void *cls,
312 const char *tname,
313 struct GNUNET_TIME_Absolute expiration,
314 const void *addr, size_t addrlen)
315{
316 struct MergeContext *mc = cls;
317 struct ExpireContext ec;
318
319 ec.addr = addr;
320 ec.addrlen = addrlen;
321 ec.found = GNUNET_NO;
322 GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
323 if ((ec.found == GNUNET_NO) ||
324 ((ec.expiration.value < expiration.value) ||
325 ((ec.expiration.value == expiration.value) &&
326 (mc->take_equal == GNUNET_YES))))
327 mc->ret += GNUNET_HELLO_add_address (tname,
328 expiration,
329 addr,
330 addrlen,
331 &mc->buf[mc->ret],
332 mc->max - mc->ret);
333 return GNUNET_OK;
334}
335
336
337static size_t
338merge_addr (void *cls, size_t max, void *buf)
339{
340 struct MergeContext *mc = cls;
341
342 if (mc->h1 == NULL)
343 return 0;
344 mc->ret = 0;
345 mc->max = max;
346 mc->buf = buf;
347 mc->take_equal = GNUNET_NO;
348 mc->other = mc->h2;
349 GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, &copy_latest, mc);
350 mc->take_equal = GNUNET_YES;
351 mc->other = mc->h1;
352 GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, &copy_latest, mc);
353 mc->h1 = NULL;
354 return mc->ret;
355}
356
357
358/**
359 * Construct a HELLO message by merging the
360 * addresses in two existing HELLOs (which
361 * must be for the same peer).
362 *
363 * @param h1 first HELLO message
364 * @param h2 the second HELLO message
365 * @return the combined hello message
366 */
367struct GNUNET_HELLO_Message *
368GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
369 const struct GNUNET_HELLO_Message *h2)
370{
371 struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
372
373 return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc);
374}
375
376
377struct DeltaContext
378{
379 struct GNUNET_TIME_Absolute expiration_limit;
380
381 GNUNET_HELLO_AddressIterator it;
382
383 void *it_cls;
384
385 const struct GNUNET_HELLO_Message *old_hello;
386};
387
388
389static int
390delta_match (void *cls,
391 const char *tname,
392 struct GNUNET_TIME_Absolute expiration,
393 const void *addr, size_t addrlen)
394{
395 struct DeltaContext *dc = cls;
396 int ret;
397 struct ExpireContext ec;
398
399 ec.addr = addr;
400 ec.addrlen = addrlen;
401 ec.found = GNUNET_NO;
402 GNUNET_HELLO_iterate_addresses (dc->old_hello,
403 GNUNET_NO, &get_match_exp, &ec);
404 if ((ec.found == GNUNET_YES) &&
405 ((ec.expiration.value > expiration.value) ||
406 (ec.expiration.value >= dc->expiration_limit.value)))
407 return GNUNET_YES; /* skip */
408 ret = dc->it (dc->it_cls, tname, expiration, addr, addrlen);
409 return ret;
410}
411
412
413/**
414 * Iterate over addresses in "new_hello" that
415 * are NOT already present in "old_hello".
416 *
417 * @param new_hello a HELLO message
418 * @param old_hello a HELLO message
419 * @param expiration_limit ignore addresses in old_hello
420 * that expired before the given time stamp
421 * @param it iterator to call on each address
422 * @param it_cls closure for it
423 */
424void
425GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
426 *new_hello,
427 const struct GNUNET_HELLO_Message
428 *old_hello,
429 struct GNUNET_TIME_Absolute
430 expiration_limit,
431 GNUNET_HELLO_AddressIterator it,
432 void *it_cls)
433{
434 struct DeltaContext dc;
435
436 dc.expiration_limit = expiration_limit;
437 dc.it = it;
438 dc.it_cls = it_cls;
439 dc.old_hello = old_hello;
440 GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc);
441}
442
443
444/**
445 * Return the size of the given HELLO message.
446 * @param hello to inspect
447 * @return the size, 0 if HELLO is invalid
448 */
449uint16_t
450GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
451{
452 uint16_t ret = ntohs (hello->header.size);
453 if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
454 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
455 return 0;
456 return ret;
457}
458
459
460/**
461 * Get the public key from a HELLO message.
462 *
463 * @param hello the hello message
464 * @param publicKey where to copy the public key information, can be NULL
465 * @return GNUNET_SYSERR if the HELLO was malformed
466 */
467int
468GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
469 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
470 *publicKey)
471{
472 uint16_t ret = ntohs (hello->header.size);
473 if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
474 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
475 return GNUNET_SYSERR;
476 *publicKey = hello->publicKey;
477 return GNUNET_OK;
478}
479
480
481
482/* end of hello.c */
diff --git a/src/hello/test_hello.c b/src/hello/test_hello.c
new file mode 100644
index 000000000..728afd63f
--- /dev/null
+++ b/src/hello/test_hello.c
@@ -0,0 +1,185 @@
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 hello/test_hello.c
22 * @brief test for hello.c
23 * @author Christian Grothoff
24 */
25
26/**
27 * Testcase for HELLO code.
28 */
29#include "platform.h"
30#include "gnunet_hello_lib.h"
31
32#define DEBUG 0
33
34static size_t
35my_addr_gen (void *cls, size_t max, void *buf)
36{
37 unsigned int *i = cls;
38 size_t ret;
39
40#if DEBUG
41 fprintf (stderr, "DEBUG: my_addr_gen called with i = %d\n", *i);
42#endif
43 if (0 == *i)
44 return 0;
45 ret = GNUNET_HELLO_add_address ("test",
46 GNUNET_TIME_absolute_get (),
47 "address_information", *i, buf, max);
48 (*i)--;
49 return ret;
50}
51
52
53static int
54check_addr (void *cls,
55 const char *tname,
56 struct GNUNET_TIME_Absolute expiration,
57 const void *addr, size_t addrlen)
58{
59 unsigned int *i = cls;
60
61#if DEBUG
62 fprintf (stderr, "DEBUG: check_addr called with i = %d and addrlen = %u\n",
63 *i, addrlen);
64#endif
65 GNUNET_assert (addrlen > 0);
66 GNUNET_assert (*i & (1 << (addrlen - 1)));
67 *i -= (1 << (addrlen - 1));
68 GNUNET_assert (0 == strncmp ("address_information", addr, addrlen));
69 GNUNET_assert (0 == strcmp ("test", tname));
70 return GNUNET_OK;
71}
72
73
74static int
75remove_some (void *cls,
76 const char *tname,
77 struct GNUNET_TIME_Absolute expiration,
78 const void *addr, size_t addrlen)
79{
80 unsigned int *i = cls;
81
82#if DEBUG
83 fprintf (stderr, "DEBUG: remove_some called with i = %d and addrlen = %u\n",
84 *i, addrlen);
85#endif
86 GNUNET_assert (addrlen > 0);
87 if (*i & (1 << (addrlen - 1)))
88 {
89 *i -= (1 << (addrlen - 1));
90 return GNUNET_NO;
91 }
92 return GNUNET_OK;
93}
94
95
96int
97main (int argc, char *argv[])
98{
99 struct GNUNET_HELLO_Message *msg1;
100 struct GNUNET_HELLO_Message *msg2;
101 struct GNUNET_HELLO_Message *msg3;
102 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
103 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
104 struct GNUNET_TIME_Absolute startup_time;
105 int ok;
106 unsigned int i;
107
108 GNUNET_log_setup ("test-hello", "DEBUG", NULL);
109 startup_time = GNUNET_TIME_absolute_get ();
110 ok = 0;
111 memset (&publicKey, 42, sizeof (publicKey));
112 fprintf (stderr, "Testing HELLO creation (without addresses)...\n");
113 i = 0;
114 msg1 = GNUNET_HELLO_create (&publicKey, &my_addr_gen, &i);
115 GNUNET_assert (msg1 != NULL);
116 GNUNET_assert (0 < GNUNET_HELLO_size (msg1));
117
118 fprintf (stderr, "Testing address iteration (empty set)...\n");
119 GNUNET_assert (NULL ==
120 GNUNET_HELLO_iterate_addresses (msg1,
121 GNUNET_NO, &check_addr, &i));
122
123 fprintf (stderr, "Testing HELLO creation (with one address)...\n");
124 i = 1;
125 msg2 = GNUNET_HELLO_create (&publicKey, &my_addr_gen, &i);
126 GNUNET_assert (msg2 != NULL);
127 GNUNET_assert (GNUNET_HELLO_size (msg1) < GNUNET_HELLO_size (msg2));
128
129 fprintf (stderr, "Testing address iteration (one address)...\n");
130 i = 1;
131 GNUNET_assert (NULL ==
132 GNUNET_HELLO_iterate_addresses (msg2,
133 GNUNET_NO, &check_addr, &i));
134 GNUNET_assert (i == 0);
135
136 fprintf (stderr, "Testing get_key from HELLO...\n");
137 GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_key (msg2, &pk));
138 GNUNET_assert (0 == memcmp (&publicKey, &pk, sizeof (pk)));
139 GNUNET_free (msg1);
140
141 fprintf (stderr, "Testing HELLO creation (with two addresses)...\n");
142 i = 2;
143 msg3 = GNUNET_HELLO_create (&publicKey, &my_addr_gen, &i);
144 GNUNET_assert (msg3 != NULL);
145 GNUNET_assert (GNUNET_HELLO_size (msg2) < GNUNET_HELLO_size (msg3));
146
147 fprintf (stderr, "Testing address iteration (two addresses)...\n");
148 i = 3;
149 GNUNET_assert (NULL ==
150 GNUNET_HELLO_iterate_addresses (msg3,
151 GNUNET_NO, &check_addr, &i));
152 GNUNET_assert (i == 0);
153
154 fprintf (stderr, "Testing HELLO merge...\n");
155 msg1 = GNUNET_HELLO_merge (msg2, msg3);
156 GNUNET_assert (GNUNET_HELLO_size (msg1) == GNUNET_HELLO_size (msg3));
157
158 i = 3;
159 GNUNET_assert (NULL ==
160 GNUNET_HELLO_iterate_addresses (msg1,
161 GNUNET_NO, &check_addr, &i));
162 GNUNET_assert (i == 0);
163 GNUNET_free (msg1);
164
165 fprintf (stderr, "Testing address iteration to copy HELLO...\n");
166 i = 2;
167 msg1 = GNUNET_HELLO_iterate_addresses (msg3, GNUNET_YES, &remove_some, &i);
168 GNUNET_assert (msg1 != NULL);
169 GNUNET_assert (i == 0);
170 i = 1;
171 GNUNET_assert (NULL ==
172 GNUNET_HELLO_iterate_addresses (msg1,
173 GNUNET_NO, &check_addr, &i));
174 GNUNET_assert (i == 0);
175 GNUNET_free (msg1);
176
177 fprintf (stderr, "Testing delta address iteration...\n");
178 i = 2;
179 GNUNET_HELLO_iterate_new_addresses (msg3,
180 msg2, startup_time, &check_addr, &i);
181 GNUNET_assert (i == 0);
182 GNUNET_free (msg2);
183 GNUNET_free (msg3);
184 return 0; /* testcase passed */
185}
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
new file mode 100644
index 000000000..8c49690e7
--- /dev/null
+++ b/src/include/Makefile.am
@@ -0,0 +1,42 @@
1SUBDIRS = .
2
3EXTRA_DIST = \
4 gettext.h \
5 platform.h \
6 plibc.h \
7 winproc.h
8
9gnunetincludedir = $(includedir)/GNUnet
10
11nodist_gnunetinclude_HEADERS = \
12 gnunet_directories.h
13
14gnunetinclude_HEADERS = \
15 gnunet_arm_service.h \
16 gnunet_client_lib.h \
17 gnunet_common.h \
18 gnunet_configuration_lib.h \
19 gnunet_container_lib.h \
20 gnunet_core_service.h \
21 gnunet_crypto_lib.h \
22 gnunet_disk_lib.h \
23 gnunet_fragmentation_lib.h \
24 gnunet_getopt_lib.h \
25 gnunet_hello_lib.h \
26 gnunet_network_lib.h \
27 gnunet_peerinfo_service.h \
28 gnunet_program_lib.h \
29 gnunet_protocols.h \
30 gnunet_pseudonym_lib.h \
31 gnunet_resolver_service.h \
32 gnunet_scheduler_lib.h \
33 gnunet_server_lib.h \
34 gnunet_service_lib.h \
35 gnunet_signal_lib.h \
36 gnunet_signatures.h \
37 gnunet_statistics_service.h \
38 gnunet_strings_lib.h \
39 gnunet_time_lib.h \
40 gnunet_transport_service.h \
41 gnunet_upnp_service.h \
42 gnunet_util_lib.h
diff --git a/src/include/gettext.h b/src/include/gettext.h
new file mode 100644
index 000000000..c89197cfc
--- /dev/null
+++ b/src/include/gettext.h
@@ -0,0 +1,71 @@
1/* Convenience header for conditional use of GNU <libintl.h>.
2 Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published
6 by the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 USA. */
18
19#ifndef _LIBGETTEXT_H
20#define _LIBGETTEXT_H 1
21
22/* NLS can be disabled through the configure --disable-nls option. */
23#if ENABLE_NLS
24
25/* Get declarations of GNU message catalog functions. */
26# include <libintl.h>
27
28#else
29
30/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
31 chokes if dcgettext is defined as a macro. So include it now, to make
32 later inclusions of <locale.h> a NOP. We don't include <libintl.h>
33 as well because people using "gettext.h" will not include <libintl.h>,
34 and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
35 is GNUNET_OK. */
36#if defined(__sun)
37# include <locale.h>
38#endif
39
40/* Disabled NLS.
41 The casts to 'const char *' serve the purpose of producing warnings
42 for invalid uses of the value returned from these functions.
43 On pre-ANSI systems without 'const', the config.h file is supposed to
44 contain "#define const". */
45# define gettext(Msgid) ((const char *) (Msgid))
46# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
47# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
48# define ngettext(Msgid1, Msgid2, N) \
49 ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
50# define dngettext(Domainname, Msgid1, Msgid2, N) \
51 ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
52# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
53 ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
54/* slight modification here to avoid warnings: generate GNUNET_NO code,
55 not even the cast... */
56# define textdomain(Domainname)
57# define bindtextdomain(Domainname, Dirname)
58# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
59
60#endif
61
62/* A pseudo function call that serves as a marker for the automated
63 extraction of messages, but does not call gettext(). The run-time
64 translation is done at a different place in the code.
65 The argument, String, should be a literal string. Concatenated strings
66 and other string expressions won't work.
67 The macro's expansion is not parenthesized, so that it is suitable as
68 initializer for static 'char[]' or 'const char[]' variables. */
69#define gettext_noop(String) String
70
71#endif /* _LIBGETTEXT_H */
diff --git a/src/include/gnunet_arm_service.h b/src/include/gnunet_arm_service.h
new file mode 100644
index 000000000..012c27877
--- /dev/null
+++ b/src/include/gnunet_arm_service.h
@@ -0,0 +1,107 @@
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 include/gnunet_arm_service.h
23 * @brief API to access gnunet-arm
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_ARM_SERVICE_H
28#define GNUNET_ARM_SERVICE_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_configuration_lib.h"
39#include "gnunet_scheduler_lib.h"
40#include "gnunet_time_lib.h"
41
42/**
43 * Version of the arm API.
44 */
45#define GNUNET_ARM_VERSION 0x00000000
46
47
48/**
49 * Callback function invoked when operation is complete.
50 *
51 * @param cls closure
52 * @param success GNUNET_YES if we think the service is running
53 * GNUNET_NO if we think the service is stopped
54 * GNUNET_SYSERR if we think ARM was not running
55 */
56typedef void (*GNUNET_ARM_Callback) (void *cls, int success);
57
58
59/**
60 * Start a service.
61 *
62 * @param service_name name of the service
63 * @param cfg configuration to use (needed to contact ARM;
64 * the ARM service may internally use a different
65 * configuration to determine how to start the service).
66 * @param sched scheduler to use
67 * @param timeout how long to wait before failing for good
68 * @param cb callback to invoke when service is ready
69 * @param cb_cls closure for callback
70 */
71void
72GNUNET_ARM_start_service (const char *service_name,
73 struct GNUNET_CONFIGURATION_Handle *cfg,
74 struct GNUNET_SCHEDULER_Handle *sched,
75 struct GNUNET_TIME_Relative timeout,
76 GNUNET_ARM_Callback cb, void *cb_cls);
77
78
79/**
80 * Stop a service.
81 *
82 * @param service_name name of the service
83 * @param cfg configuration to use (needed to contact ARM;
84 * the ARM service may internally use a different
85 * configuration to determine how to start the service).
86 * @param sched scheduler to use
87 * @param timeout how long to wait before failing for good
88 * @param cb callback to invoke when service is ready
89 * @param cb_cls closure for callback
90 */
91void
92GNUNET_ARM_stop_service (const char *service_name,
93 struct GNUNET_CONFIGURATION_Handle *cfg,
94 struct GNUNET_SCHEDULER_Handle *sched,
95 struct GNUNET_TIME_Relative timeout,
96 GNUNET_ARM_Callback cb, void *cb_cls);
97
98
99
100#if 0 /* keep Emacsens' auto-indent happy */
101{
102#endif
103#ifdef __cplusplus
104}
105#endif
106
107#endif
diff --git a/src/include/gnunet_client_lib.h b/src/include/gnunet_client_lib.h
new file mode 100644
index 000000000..6a77aa391
--- /dev/null
+++ b/src/include/gnunet_client_lib.h
@@ -0,0 +1,160 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_client_lib.h
23 * @brief functions related to accessing services
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_CLIENT_LIB_H
28#define GNUNET_CLIENT_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_common.h"
39#include "gnunet_configuration_lib.h"
40#include "gnunet_network_lib.h"
41#include "gnunet_scheduler_lib.h"
42#include "gnunet_time_lib.h"
43
44/**
45 * Opaque handle for a connection to a service.
46 */
47struct GNUNET_CLIENT_Connection;
48
49/**
50 * Get a connection with a service.
51 *
52 * @param sched scheduler to use
53 * @param service_name name of the service
54 * @param cfg configuration to use
55 * @return NULL on error (service unknown to configuration)
56 */
57struct GNUNET_CLIENT_Connection *GNUNET_CLIENT_connect (struct
58 GNUNET_SCHEDULER_Handle
59 *sched,
60 const char
61 *service_name,
62 struct
63 GNUNET_CONFIGURATION_Handle
64 *cfg);
65
66/**
67 * Destroy connection with the service. This will
68 * automatically cancel any pending "receive" request
69 * (however, the handler will *NOT* be called, not
70 * even with a NULL message).
71 */
72void GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock);
73
74/**
75 * Type of a function to call when we receive a message
76 * from the service.
77 *
78 * @param cls closure
79 * @param msg message received, NULL on timeout or fatal error
80 */
81typedef void (*GNUNET_CLIENT_MessageHandler) (void *cls,
82 const struct
83 GNUNET_MessageHeader * msg);
84
85/**
86 * Read from the service.
87 *
88 * @param sched scheduler to use
89 * @param sock the service
90 * @param handler function to call with the message
91 * @param cls closure for handler
92 * @param timeout how long to wait until timing out
93 */
94void GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock,
95 GNUNET_CLIENT_MessageHandler handler,
96 void *cls, struct GNUNET_TIME_Relative timeout);
97
98
99/**
100 * Ask the client to call us once the specified number of bytes
101 * are free in the transmission buffer. May call the notify
102 * method immediately if enough space is available.
103 *
104 * @param client connection to the service
105 * @param size number of bytes to send
106 * @param timeout after how long should we give up (and call
107 * notify with buf NULL and size 0)?
108 * @param notify function to call
109 * @param notify_cls closure for notify
110 * @return NULL if someone else is already waiting to be notified
111 * non-NULL if the notify callback was queued (can be used to cancel
112 * using GNUNET_NETWORK_notify_transmit_ready_cancel)
113 */
114struct GNUNET_NETWORK_TransmitHandle
115 *GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock,
116 size_t size,
117 struct GNUNET_TIME_Relative timeout,
118 GNUNET_NETWORK_TransmitReadyNotify
119 notify, void *notify_cls);
120
121
122/**
123 * Request that the service should shutdown.
124 * Afterwards, the connection should be disconnected.
125 *
126 * @param sched scheduler to use
127 * @param sock the socket connected to the service
128 */
129void GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock);
130
131
132/**
133 * Wait until the service is running.
134 *
135 * @param sched scheduler to use
136 * @param service name of the service to wait for
137 * @param cfg configuration to use
138 * @param timeout how long to wait at most in ms
139 * @param task task to run if service is running
140 * (reason will be "PREREQ_DONE" (service running)
141 * or "TIMEOUT" (service not known to be running))
142 * @param task_cls closure for task
143 */
144void GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched,
145 const char *service,
146 struct GNUNET_CONFIGURATION_Handle *cfg,
147 struct GNUNET_TIME_Relative timeout,
148 GNUNET_SCHEDULER_Task task, void *task_cls);
149
150
151#if 0 /* keep Emacsens' auto-indent happy */
152{
153#endif
154#ifdef __cplusplus
155}
156#endif
157
158/* ifndef GNUNET_CLIENT_LIB_H */
159#endif
160/* end of gnunet_client_lib.h */
diff --git a/src/include/gnunet_common.h b/src/include/gnunet_common.h
new file mode 100644
index 000000000..61b572eb8
--- /dev/null
+++ b/src/include/gnunet_common.h
@@ -0,0 +1,469 @@
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 include/gnunet_common.h
23 * @brief commonly used definitions; globals in this file
24 * are exempt from the rule that the module name ("common")
25 * must be part of the symbol name.
26 *
27 * @author Christian Grothoff
28 * @author Nils Durner
29 */
30#ifndef GNUNET_COMMON_H
31#define GNUNET_COMMON_H
32
33/**
34 * Version of the API (for entire gnunetutil.so library).
35 */
36#define GNUNET_UTIL_VERSION 0x00000000
37
38/**
39 * Name used for "services" that are actually command-line
40 * programs invoked by the end user.
41 */
42#define GNUNET_CLIENT_SERVICE_NAME "client"
43
44/**
45 * Named constants for return values. The following
46 * invariants hold: "GNUNET_NO == 0" (to allow "if (GNUNET_NO)")
47 * "GNUNET_OK != GNUNET_SYSERR", "GNUNET_OK != GNUNET_NO", "GNUNET_NO != GNUNET_SYSERR"
48 * and finally "GNUNET_YES != GNUNET_NO".
49 */
50#define GNUNET_OK 1
51#define GNUNET_SYSERR -1
52#define GNUNET_YES 1
53#define GNUNET_NO 0
54
55#define GNUNET_MIN(a,b) (((a) < (b)) ? (a) : (b))
56
57#define GNUNET_MAX(a,b) (((a) > (b)) ? (a) : (b))
58
59/**
60 * gcc-ism to get packed structs.
61 */
62#define GNUNET_PACKED __attribute__((packed))
63
64
65/* ************************ super-general types *********************** */
66
67/**
68 * Header for all communications.
69 */
70struct GNUNET_MessageHeader
71{
72
73 /**
74 * The length of the struct (in bytes, including the length field itself)
75 */
76 uint16_t size GNUNET_PACKED;
77
78 /**
79 * The type of the message (XX_CS_PROTO_XXXX)
80 */
81 uint16_t type GNUNET_PACKED;
82
83};
84
85
86/**
87 * @brief 512-bit hashcode
88 */
89typedef struct
90{
91 uint32_t bits[512 / 8 / sizeof (uint32_t)]; /* = 16 */
92}
93GNUNET_HashCode;
94
95
96/**
97 * The identity of the host (basically the SHA-512 hashcode of
98 * it's public key).
99 */
100struct GNUNET_PeerIdentity
101{
102 GNUNET_HashCode hashPubKey GNUNET_PACKED;
103};
104
105
106/**
107 * Function called with a filename.
108 *
109 * @param filename complete filename (absolute path)
110 * @param cls closure
111 * @return GNUNET_OK to continue to iterate,
112 * GNUNET_SYSERR to abort iteration with error!
113 */
114typedef int (*GNUNET_FileNameCallback) (void *cls, const char *filename);
115
116
117/* ****************************** logging ***************************** */
118
119/**
120 * Types of errors.
121 */
122enum GNUNET_ErrorType
123{
124 GNUNET_ERROR_TYPE_ERROR = 1,
125 GNUNET_ERROR_TYPE_WARNING = 2,
126 GNUNET_ERROR_TYPE_INFO = 4,
127 GNUNET_ERROR_TYPE_DEBUG = 8,
128 GNUNET_ERROR_TYPE_INVALID = 16,
129 GNUNET_ERROR_TYPE_BULK = 32
130};
131
132/**
133 * User-defined handler for log messages.
134 *
135 * @param cls closure
136 * @param kind severeity
137 * @param component what component is issuing the message?
138 * @param date when was the message logged?
139 * @param message what is the message
140 */
141typedef void (*GNUNET_Logger) (void *cls,
142 enum GNUNET_ErrorType kind,
143 const char *component,
144 const char *date, const char *message);
145
146/**
147 * Main log function.
148 *
149 * @param kind how serious is the error?
150 * @param message what is the message (format string)
151 * @param ... arguments for format string
152 */
153void GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...);
154
155
156
157/**
158 * Log function that specifies an alternative component.
159 * This function should be used by plugins.
160 *
161 * @param kind how serious is the error?
162 * @param comp component responsible for generating the message
163 * @param message what is the message (format string)
164 * @param ... arguments for format string
165 */
166void
167GNUNET_log_from (enum GNUNET_ErrorType kind,
168 const char *comp, const char *message, ...);
169
170
171/**
172 * Ignore the next n calls to the log function.
173 *
174 * @param n number of log calls to ignore, use 0 to
175 * assert that the log skip counter is currently zero.
176 */
177void GNUNET_log_skip (unsigned int n);
178
179
180/**
181 * Setup logging.
182 *
183 * @param component default component to use
184 * @param loglevel what types of messages should be logged
185 * @param logfile change logging to logfile (use NULL to keep stderr)
186 * @return GNUNET_OK on success, GNUNET_SYSERR if logfile could not be opened
187 */
188int
189GNUNET_log_setup (const char *component,
190 const char *loglevel, const char *logfile);
191
192/**
193 * Add a custom logger.
194 *
195 * @param logger log function
196 * @param logger_cls closure for logger
197 */
198void GNUNET_logger_add (GNUNET_Logger logger, void *logger_cls);
199
200/**
201 * Remove a custom logger.
202 *
203 * @param logger log function
204 * @param logger_cls closure for logger
205 */
206void GNUNET_logger_remove (GNUNET_Logger logger, void *logger_cls);
207
208
209/**
210 * Convert a peer identity to a string (for printing debug messages).
211 * This is one of the very few calls in the entire API that is
212 * NOT reentrant!
213 *
214 * @param pid the peer identity
215 * @return string form of the pid; will be overwritten by next
216 * call to GNUNET_i2s.
217 */
218const char *GNUNET_i2s (const struct GNUNET_PeerIdentity *pid);
219
220/**
221 * Convert error type to string.
222 *
223 * @param kind type to convert
224 * @return string corresponding to the type
225 */
226const char *GNUNET_error_type_to_string (enum GNUNET_ErrorType kind);
227
228/**
229 * Use this for fatal errors that cannot be handled
230 */
231#define GNUNET_assert(cond) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Assertion failed at %s:%d.\n"), __FILE__, __LINE__); abort(); } } while(0)
232
233/**
234 * Use this for fatal errors that cannot be handled
235 */
236#define GNUNET_assert_at(cond, f, l) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Assertion failed at %s:%d.\n"), f, l); abort(); } } while(0)
237
238/**
239 * Use this for internal assertion violations that are
240 * not fatal (can be handled) but should not occur.
241 */
242#define GNUNET_break(cond) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Assertion failed at %s:%d.\n"), __FILE__, __LINE__); } } while(0)
243
244/**
245 * Use this for assertion violations caused by other
246 * peers (i.e. protocol violations). We do not want to
247 * confuse end-users (say, some other peer runs an
248 * older, broken or incompatible GNUnet version), but
249 * we still want to see these problems during
250 * development and testing. "OP == other peer".
251 */
252#define GNUNET_break_op(cond) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("External protocol violation detected at %s:%d.\n"), __FILE__, __LINE__); } } while(0)
253
254/**
255 * Log an error message at log-level 'level' that indicates
256 * a failure of the command 'cmd' with the message given
257 * by strerror(errno).
258 */
259#define GNUNET_log_strerror(level, cmd) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, STRERROR(errno)); } while(0)
260
261/**
262 * Log an error message at log-level 'level' that indicates
263 * a failure of the command 'cmd' with the message given
264 * by strerror(errno).
265 */
266#define GNUNET_log_strerror_file(level, cmd, filename) do { GNUNET_log(level, _("`%s' failed on file `%s' at %s:%d with error: %s\n"), cmd, filename,__FILE__, __LINE__, STRERROR(errno)); } while(0)
267
268/* ************************* endianess conversion ****************** */
269
270/**
271 * Convert a long-long to host-byte-order.
272 * @param n the value in network byte order
273 * @return the same value in host byte order
274 */
275unsigned long long GNUNET_ntohll (unsigned long long n);
276
277/**
278 * Convert a long long to network-byte-order.
279 * @param n the value in host byte order
280 * @return the same value in network byte order
281 */
282unsigned long long GNUNET_htonll (unsigned long long n);
283
284
285/* ************************* allocation functions ****************** */
286
287/**
288 * Maximum allocation with GNUNET_malloc macro.
289 */
290#define GNUNET_MAX_GNUNET_MALLOC_CHECKED (1024 * 1024 * 40)
291
292/**
293 * Wrapper around malloc. Allocates size bytes of memory.
294 * The memory will be zero'ed out.
295 *
296 * @param size the number of bytes to allocate, must be
297 * smaller than 40 MB.
298 * @return pointer to size bytes of memory
299 */
300#define GNUNET_malloc(size) GNUNET_xmalloc_(size, __FILE__, __LINE__)
301
302/**
303 * Wrapper around malloc. Allocates size bytes of memory.
304 * The memory will be zero'ed out.
305 *
306 * @param size the number of bytes to allocate
307 * @return pointer to size bytes of memory
308 */
309#define GNUNET_malloc_large(size) GNUNET_xmalloc_unchecked_(size, __FILE__, __LINE__)
310
311/**
312 * Wrapper around realloc. Rellocates size bytes of memory.
313 *
314 * @param ptr the pointer to reallocate
315 * @param size the number of bytes to reallocate
316 * @return pointer to size bytes of memory
317 */
318#define GNUNET_realloc(ptr, size) GNUNET_xrealloc_(ptr, size, __FILE__, __LINE__)
319
320/**
321 * Wrapper around free. Frees the memory referred to by ptr.
322 * Note that is is generally better to free memory that was
323 * allocated with GNUNET_array_grow using GNUNET_array_grow(mem, size, 0) instead of GNUNET_free.
324 *
325 * @param ptr location where to free the memory. ptr must have
326 * been returned by GNUNET_strdup, GNUNET_malloc or GNUNET_array_grow earlier.
327 */
328#define GNUNET_free(ptr) GNUNET_xfree_(ptr, __FILE__, __LINE__)
329
330/**
331 * Free the memory pointed to by ptr if ptr is not NULL.
332 * Equivalent to if (ptr!=null)GNUNET_free(ptr).
333 *
334 * @param ptr the location in memory to free
335 */
336#define GNUNET_free_non_null(ptr) do { void * __x__ = ptr; if (__x__ != NULL) { GNUNET_free(__x__); } } while(0)
337
338/**
339 * Wrapper around GNUNET_strdup. Makes a copy of the zero-terminated string
340 * pointed to by a.
341 *
342 * @param a pointer to a zero-terminated string
343 * @return a copy of the string including zero-termination
344 */
345#define GNUNET_strdup(a) GNUNET_xstrdup_(a,__FILE__,__LINE__)
346
347/**
348 * Grow a well-typed (!) array. This is a convenience
349 * method to grow a vector <tt>arr</tt> of size <tt>size</tt>
350 * to the new (target) size <tt>tsize</tt>.
351 * <p>
352 *
353 * Example (simple, well-typed stack):
354 *
355 * <pre>
356 * static struct foo * myVector = NULL;
357 * static int myVecLen = 0;
358 *
359 * static void push(struct foo * elem) {
360 * GNUNET_array_grow(myVector, myVecLen, myVecLen+1);
361 * memcpy(&myVector[myVecLen-1], elem, sizeof(struct foo));
362 * }
363 *
364 * static void pop(struct foo * elem) {
365 * if (myVecLen == 0) die();
366 * memcpy(elem, myVector[myVecLen-1], sizeof(struct foo));
367 * GNUNET_array_grow(myVector, myVecLen, myVecLen-1);
368 * }
369 * </pre>
370 *
371 * @param arr base-pointer of the vector, may be NULL if size is 0;
372 * will be updated to reflect the new address. The TYPE of
373 * arr is important since size is the number of elements and
374 * not the size in bytes
375 * @param size the number of elements in the existing vector (number
376 * of elements to copy over)
377 * @param tsize the target size for the resulting vector, use 0 to
378 * free the vector (then, arr will be NULL afterwards).
379 */
380#define GNUNET_array_grow(arr,size,tsize) GNUNET_xgrow_((void**)&arr, sizeof(arr[0]), &size, tsize, __FILE__, __LINE__)
381
382/**
383 * Append an element to a list (growing the
384 * list by one).
385 */
386#define GNUNET_array_append(arr,size,element) do { GNUNET_array_grow(arr,size,size+1); arr[size-1] = element; } while(0)
387
388/**
389 * Like snprintf, just aborts if the buffer is of insufficient size.
390 */
391int GNUNET_snprintf (char *buf, size_t size, const char *format, ...);
392
393/**
394 * Like asprintf, just portable.
395 */
396int GNUNET_asprintf (char **buf, const char *format, ...);
397
398
399/* ************** internal implementations, use macros above! ************** */
400
401/**
402 * Allocate memory. Checks the return value, aborts if no more
403 * memory is available. Don't use GNUNET_xmalloc_ directly. Use the
404 * GNUNET_malloc macro.
405 * The memory will be zero'ed out.
406 */
407void *GNUNET_xmalloc_ (size_t size, const char *filename, int linenumber);
408
409/**
410 * Allocate memory. This function does not check if the
411 * allocation request is within reasonable bounds, allowing
412 * allocations larger than 40 MB. If you don't expect the
413 * possibility of very large allocations, use GNUNET_malloc instead.
414 * The memory will be zero'ed out.
415 */
416void *GNUNET_xmalloc_unchecked_ (size_t size,
417 const char *filename, int linenumber);
418
419/**
420 * Reallocate memory. Checks the return value, aborts if no more
421 * memory is available.
422 */
423void *GNUNET_xrealloc_ (void *ptr,
424 const size_t n, const char *filename, int linenumber);
425
426/**
427 * Free memory. Merely a wrapper for the case that we
428 * want to keep track of allocations. Don't use GNUNET_xfree_
429 * directly. Use the GNUNET_free macro.
430 */
431void GNUNET_xfree_ (void *ptr, const char *filename, int linenumber);
432
433
434/**
435 * Dup a string. Don't call GNUNET_xstrdup_ directly. Use the GNUNET_strdup macro.
436 */
437char *GNUNET_xstrdup_ (const char *str, const char *filename, int linenumber);
438
439/**
440 * Grow an array, the new elements are zeroed out.
441 * Grows old by (*oldCount-newCount)*elementSize
442 * bytes and sets *oldCount to newCount.
443 *
444 * Don't call GNUNET_xgrow_ directly. Use the GNUNET_array_grow macro.
445 *
446 * @param old address of the pointer to the array
447 * *old may be NULL
448 * @param elementSize the size of the elements of the array
449 * @param oldCount address of the number of elements in the *old array
450 * @param newCount number of elements in the new array, may be 0 (then *old will be NULL afterwards)
451 */
452void GNUNET_xgrow_ (void **old,
453 size_t elementSize,
454 unsigned int *oldCount,
455 unsigned int newCount,
456 const char *filename, int linenumber);
457
458
459
460
461#if __STDC_VERSION__ < 199901L
462# if __GNUC__ >= 2
463# define __func__ __FUNCTION__
464# else
465# define __func__ "<unknown>"
466# endif
467#endif
468
469#endif /*GNUNET_COMMON_H_ */
diff --git a/src/include/gnunet_configuration_lib.h b/src/include/gnunet_configuration_lib.h
new file mode 100644
index 000000000..fefc6884a
--- /dev/null
+++ b/src/include/gnunet_configuration_lib.h
@@ -0,0 +1,238 @@
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 include/gnunet_configuration_lib.h
23 * @brief configuration API
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_CONFIGURATION_LIB_H
29#define GNUNET_CONFIGURATION_LIB_H
30
31
32#ifdef __cplusplus
33extern "C"
34{
35#if 0 /* keep Emacsens' auto-indent happy */
36}
37#endif
38#endif
39
40#include "gnunet_common.h"
41
42/**
43 * A configuration object.
44 */
45struct GNUNET_CONFIGURATION_Handle;
46
47/**
48 * Create a new configuration object.
49 *
50 * @param component name of responsible component
51 */
52struct GNUNET_CONFIGURATION_Handle *GNUNET_CONFIGURATION_create (void);
53
54/**
55 * Destroy configuration object.
56 */
57void GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg);
58
59/**
60 * Load configuration. This function will first parse the
61 * defaults and then parse the specific configuration file
62 * to overwrite the defaults.
63 *
64 * @param filename name of the configuration file
65 * @return GNUNET_OK on success, GNUNET_SYSERR on error
66 */
67int GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
68 const char *filename);
69
70/**
71 * Parse a configuration file, add all of the options in the
72 * file to the configuration environment.
73 * @return GNUNET_OK on success, GNUNET_SYSERR on error
74 */
75int GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg,
76 const char *filename);
77
78/**
79 * Write configuration file.
80 * @return GNUNET_OK on success, GNUNET_SYSERR on error
81 */
82int GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg,
83 const char *filename);
84
85/**
86 * Test if there are configuration options that were
87 * changed since the last save.
88 * @return GNUNET_NO if clean, GNUNET_YES if dirty, GNUNET_SYSERR on error (i.e. last save failed)
89 */
90int GNUNET_CONFIGURATION_is_dirty (struct GNUNET_CONFIGURATION_Handle *cfg);
91
92/**
93 * Get a configuration value that should be a number.
94 * @return GNUNET_OK on success, GNUNET_SYSERR on error
95 */
96int GNUNET_CONFIGURATION_get_value_number (struct GNUNET_CONFIGURATION_Handle
97 *cfg, const char *section,
98 const char *option,
99 unsigned long long *number);
100
101/**
102 * Test if we have a value for a particular option
103 * @return GNUNET_YES if so, GNUNET_NO if not.
104 */
105int GNUNET_CONFIGURATION_have_value (struct GNUNET_CONFIGURATION_Handle *cfg,
106 const char *section, const char *option);
107
108/**
109 * Get a configuration value that should be a string.
110 * @param value will be set to a freshly allocated configuration
111 * value, or NULL if option is not specified
112 * @return GNUNET_OK on success, GNUNET_SYSERR on error
113 */
114int GNUNET_CONFIGURATION_get_value_string (struct GNUNET_CONFIGURATION_Handle
115 *cfg, const char *section,
116 const char *option, char **value);
117
118/**
119 * Get a configuration value that should be the name of a file
120 * or directory.
121 *
122 * @param value will be set to a freshly allocated configuration
123 * value, or NULL if option is not specified
124 * @return GNUNET_OK on success, GNUNET_SYSERR on error
125 */
126int GNUNET_CONFIGURATION_get_value_filename (struct
127 GNUNET_CONFIGURATION_Handle *cfg,
128 const char *section,
129 const char *option,
130 char **value);
131
132/**
133 * Iterate over the set of filenames stored in a configuration value.
134 *
135 * @return number of filenames iterated over, -1 on error
136 */
137int GNUNET_CONFIGURATION_iterate_value_filenames (struct
138 GNUNET_CONFIGURATION_Handle
139 *cfg,
140 const char *section,
141 const char *option,
142 GNUNET_FileNameCallback
143 cb, void *cls);
144
145/**
146 * Get a configuration value that should be in a set of
147 * predefined strings
148 *
149 * @param choices NULL-terminated list of legal values
150 * @param value will be set to an entry in the legal list,
151 * or NULL if option is not specified and no default given
152 * @return GNUNET_OK on success, GNUNET_SYSERR on error
153 */
154int GNUNET_CONFIGURATION_get_value_choice (struct GNUNET_CONFIGURATION_Handle
155 *cfg, const char *section,
156 const char *option,
157 const char **choices,
158 const char **value);
159
160/**
161 * Get a configuration value that should be in a set of
162 * "YES" or "NO".
163 *
164 * @return GNUNET_YES, GNUNET_NO or if option has no valid value, GNUNET_SYSERR
165 */
166int GNUNET_CONFIGURATION_get_value_yesno (struct GNUNET_CONFIGURATION_Handle
167 *cfg, const char *section,
168 const char *option);
169
170/**
171 * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR"
172 * where either in the "PATHS" section or the environtment
173 * "FOO" is set to "DIRECTORY".
174
175 * @param old string to $-expand (will be freed!)
176 * @return $-expanded string
177 */
178char *GNUNET_CONFIGURATION_expand_dollar (struct GNUNET_CONFIGURATION_Handle
179 *cfg, char *old);
180
181/**
182 * Set a configuration value that should be a number.
183 */
184void
185GNUNET_CONFIGURATION_set_value_number (struct GNUNET_CONFIGURATION_Handle
186 *cfg,
187 const char *section,
188 const char *option,
189 unsigned long long number);
190
191
192/**
193 * Set a configuration value that should be a string.
194 * @param value
195 */
196void
197GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle
198 *cfg,
199 const char *section,
200 const char *option, const char *value);
201
202/**
203 * Remove a filename from a configuration value that
204 * represents a list of filenames
205 *
206 * @param value filename to remove
207 * @return GNUNET_OK on success,
208 * GNUNET_SYSERR if the filename is not in the list
209 */
210int GNUNET_CONFIGURATION_remove_value_filename (struct
211 GNUNET_CONFIGURATION_Handle
212 *cfg,
213 const char *section,
214 const char *option,
215 const char *value);
216
217/**
218 * Append a filename to a configuration value that
219 * represents a list of filenames
220 *
221 * @param value filename to append
222 * @return GNUNET_OK on success,
223 * GNUNET_SYSERR if the filename already in the list
224 */
225int GNUNET_CONFIGURATION_append_value_filename (struct
226 GNUNET_CONFIGURATION_Handle
227 *cfg, const char *section,
228 const char *option,
229 const char *value);
230
231#if 0 /* keep Emacsens' auto-indent happy */
232{
233#endif
234#ifdef __cplusplus
235}
236#endif
237
238#endif
diff --git a/src/include/gnunet_container_lib.h b/src/include/gnunet_container_lib.h
new file mode 100644
index 000000000..255f68a89
--- /dev/null
+++ b/src/include/gnunet_container_lib.h
@@ -0,0 +1,784 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 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/**
22 * @file include/gnunet_container_lib.h
23 * @brief container classes for GNUnet
24 *
25 * @author Christian Grothoff
26 * @author Nils Durner
27 */
28
29#ifndef GNUNET_CONTAINER_LIB_H
30#define GNUNET_CONTAINER_LIB_H
31
32/* add error and config prototypes */
33#include "gnunet_crypto_lib.h"
34#include <extractor.h>
35
36#ifdef __cplusplus
37extern "C"
38{
39#if 0 /* keep Emacsens' auto-indent happy */
40}
41#endif
42#endif
43
44
45/* ******************* bloomfilter ***************** */
46
47/**
48 * @brief bloomfilter representation (opaque)
49 */
50struct GNUNET_CONTAINER_BloomFilter;
51
52/**
53 * Iterator over HashCodes.
54 *
55 * @return GNUNET_YES if next was updated
56 * GNUNET_NO if there are no more entries
57 */
58typedef int (*GNUNET_HashCodeIterator) (GNUNET_HashCode * next, void *arg);
59
60/**
61 * Load a bloom-filter from a file.
62 * @param filename the name of the file (or the prefix)
63 * @param size the size of the bloom-filter (number of
64 * bytes of storage space to use)
65 * @param k the number of GNUNET_CRYPTO_hash-functions to apply per
66 * element (number of bits set per element in the set)
67 * @return the bloomfilter
68 */
69struct GNUNET_CONTAINER_BloomFilter *GNUNET_CONTAINER_bloomfilter_load (const
70 char
71 *filename,
72 unsigned
73 int
74 size,
75 unsigned
76 int
77 k);
78
79/**
80 * Create a bloom filter from raw bits.
81 *
82 * @param data the raw bits in memory (maybe NULL,
83 * in which case all bits should be considered
84 * to be zero).
85 * @param size the size of the bloom-filter (number of
86 * bytes of storage space to use); also size of data
87 * -- unless data is NULL. Must be a power of 2.
88 * @param k the number of GNUNET_CRYPTO_hash-functions to apply per
89 * element (number of bits set per element in the set)
90 * @return the bloomfilter
91 */
92struct GNUNET_CONTAINER_BloomFilter *GNUNET_CONTAINER_bloomfilter_init (const
93 char
94 *data,
95 unsigned
96 int
97 size,
98 unsigned
99 int
100 k);
101
102/**
103 * Copy the raw data of this bloomfilter into
104 * the given data array.
105 *
106 * @param data where to write the data
107 * @param size the size of the given data array
108 * @return GNUNET_SYSERR if the data array of the wrong size
109 */
110int GNUNET_CONTAINER_bloomfilter_get_raw_data (struct
111 GNUNET_CONTAINER_BloomFilter
112 *bf, char *data,
113 unsigned int size);
114
115/**
116 * Test if an element is in the filter.
117 * @param e the element
118 * @param bf the filter
119 * @return GNUNET_YES if the element is in the filter, GNUNET_NO if not
120 */
121int GNUNET_CONTAINER_bloomfilter_test (struct GNUNET_CONTAINER_BloomFilter
122 *bf, const GNUNET_HashCode * e);
123
124/**
125 * Add an element to the filter
126 * @param bf the filter
127 * @param e the element
128 */
129void GNUNET_CONTAINER_bloomfilter_add (struct GNUNET_CONTAINER_BloomFilter
130 *bf, const GNUNET_HashCode * e);
131
132/**
133 * Remove an element from the filter.
134 * @param bf the filter
135 * @param e the element to remove
136 */
137void GNUNET_CONTAINER_bloomfilter_remove (struct GNUNET_CONTAINER_BloomFilter
138 *bf, const GNUNET_HashCode * e);
139
140/**
141 * Free the space associcated with a filter
142 * in memory, flush to drive if needed (do not
143 * free the space on the drive)
144 * @param bf the filter
145 */
146void GNUNET_CONTAINER_bloomfilter_free (struct GNUNET_CONTAINER_BloomFilter
147 *bf);
148
149/**
150 * Reset a bloom filter to empty.
151 * @param bf the filter
152 */
153void GNUNET_CONTAINER_bloomfilter_clear (struct GNUNET_CONTAINER_BloomFilter
154 *bf);
155
156/**
157 * Or the entries of the given raw data array with the
158 * data of the given bloom filter. Assumes that
159 * the size of the data array and the current filter
160 * match.
161 * @param bf the filter
162 */
163int GNUNET_CONTAINER_bloomfilter_or (struct GNUNET_CONTAINER_BloomFilter *bf,
164 const char *data, unsigned int size);
165
166/**
167 * Resize a bloom filter. Note that this operation
168 * is pretty costly. Essentially, the bloom filter
169 * needs to be completely re-build.
170 *
171 * @param bf the filter
172 * @param iterator an iterator over all elements stored in the BF
173 * @param iterator_arg argument to the iterator function
174 * @param size the new size for the filter
175 * @param k the new number of GNUNET_CRYPTO_hash-function to apply per element
176 */
177void GNUNET_CONTAINER_bloomfilter_resize (struct GNUNET_CONTAINER_BloomFilter
178 *bf,
179 GNUNET_HashCodeIterator iterator,
180 void *iterator_arg,
181 unsigned int size, unsigned int k);
182
183/* ****************** metadata ******************* */
184
185/**
186 * Meta data to associate with a file, directory or namespace.
187 */
188struct GNUNET_CONTAINER_MetaData;
189
190/**
191 * Iterator over meta data.
192 * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort
193 */
194typedef int (*GNUNET_CONTAINER_MetaDataProcessor) (EXTRACTOR_KeywordType type,
195 const char *data,
196 void *closure);
197
198/**
199 * Create a fresh MetaData token.
200 */
201struct GNUNET_CONTAINER_MetaData *GNUNET_CONTAINER_meta_data_create (void);
202
203/**
204 * Duplicate a MetaData token.
205 */
206struct GNUNET_CONTAINER_MetaData *GNUNET_CONTAINER_meta_data_duplicate (const
207 struct
208 GNUNET_CONTAINER_MetaData
209 *meta);
210
211/**
212 * Free meta data.
213 */
214void GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData
215 *md);
216
217/**
218 * Test if two MDs are equal.
219 */
220int GNUNET_CONTAINER_meta_data_test_equal (const struct
221 GNUNET_CONTAINER_MetaData *md1,
222 const struct
223 GNUNET_CONTAINER_MetaData *md2);
224
225
226/**
227 * Extend metadata.
228 * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists
229 */
230int GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
231 EXTRACTOR_KeywordType type,
232 const char *data);
233
234/**
235 * Remove an item.
236 * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md
237 */
238int GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
239 EXTRACTOR_KeywordType type,
240 const char *data);
241
242/**
243 * Add the current time as the publication date
244 * to the meta-data.
245 */
246void GNUNET_CONTAINER_meta_data_add_publication_date (struct
247 GNUNET_CONTAINER_MetaData
248 *md);
249
250/**
251 * Iterate over MD entries, excluding thumbnails.
252 *
253 * @return number of entries
254 */
255int GNUNET_CONTAINER_meta_data_get_contents (const struct
256 GNUNET_CONTAINER_MetaData *md,
257 GNUNET_CONTAINER_MetaDataProcessor
258 iterator, void *closure);
259
260/**
261 * Get the first MD entry of the given type.
262 * @return NULL if we do not have any such entry,
263 * otherwise client is responsible for freeing the value!
264 */
265char *GNUNET_CONTAINER_meta_data_get_by_type (const struct
266 GNUNET_CONTAINER_MetaData *md,
267 EXTRACTOR_KeywordType type);
268
269/**
270 * Get the first matching MD entry of the given types.
271 * @paarm ... -1-terminated list of types
272 * @return NULL if we do not have any such entry,
273 * otherwise client is responsible for freeing the value!
274 */
275char *GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
276 GNUNET_CONTAINER_MetaData
277 *md, ...);
278
279/**
280 * Get a thumbnail from the meta-data (if present).
281 *
282 * @param thumb will be set to the thumbnail data. Must be
283 * freed by the caller!
284 * @return number of bytes in thumbnail, 0 if not available
285 */
286size_t GNUNET_CONTAINER_meta_data_get_thumbnail (const struct
287 GNUNET_CONTAINER_MetaData
288 *md, unsigned char **thumb);
289
290/**
291 * Extract meta-data from a file.
292 *
293 * @return GNUNET_SYSERR on error, otherwise the number
294 * of meta-data items obtained
295 */
296int GNUNET_CONTAINER_meta_data_extract_from_file (struct
297 GNUNET_CONTAINER_MetaData
298 *md, const char *filename,
299 EXTRACTOR_ExtractorList *
300 extractors);
301
302enum GNUNET_CONTAINER_MetaDataSerializationOptions
303{
304 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL = GNUNET_NO,
305 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART = GNUNET_YES,
306 GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS = 2
307};
308
309
310
311/**
312 * Serialize meta-data to target.
313 *
314 * @param size maximum number of bytes available
315 * @param opt is it ok to just write SOME of the
316 * meta-data to match the size constraint,
317 * possibly discarding some data?
318 * @return number of bytes written on success,
319 * GNUNET_SYSERR on error (typically: not enough
320 * space)
321 */
322int GNUNET_CONTAINER_meta_data_serialize (const struct
323 GNUNET_CONTAINER_MetaData *md,
324 char *target, unsigned int size,
325 enum
326 GNUNET_CONTAINER_MetaDataSerializationOptions
327 opt);
328
329/**
330 * Compute size of the meta-data in
331 * serialized form.
332 * @param opt is it ok to just write SOME of the
333 * meta-data to match the size constraint,
334 * possibly discarding some data?
335 */
336unsigned int GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
337 GNUNET_CONTAINER_MetaData
338 *md,
339 enum
340 GNUNET_CONTAINER_MetaDataSerializationOptions
341 opt);
342
343/**
344 * Deserialize meta-data. Initializes md.
345 * @param size number of bytes available
346 * @return MD on success, NULL on error (i.e.
347 * bad format)
348 */
349struct GNUNET_CONTAINER_MetaData
350 *GNUNET_CONTAINER_meta_data_deserialize (const char *input,
351 unsigned int size);
352
353/**
354 * Does the meta-data claim that this is a directory?
355 * Checks if the mime-type is that of a GNUnet directory.
356 *
357 * @return GNUNET_YES if it is, GNUNET_NO if it is not, GNUNET_SYSERR if
358 * we have no mime-type information (treat as 'GNUNET_NO')
359 */
360int GNUNET_CONTAINER_meta_data_test_for_directory (const struct
361 GNUNET_CONTAINER_MetaData
362 *md);
363
364
365/* ******************************* HashMap **************************** */
366
367/**
368 * Opaque handle for a HashMap.
369 */
370struct GNUNET_CONTAINER_MultiHashMap;
371
372/**
373 * Options for storing values in the HashMap.
374 */
375enum GNUNET_CONTAINER_MultiHashMapOption
376{
377 /**
378 * If a value with the given key exists, replace it.
379 * Note that the old value would NOT be freed
380 * by replace (the application has to make sure that
381 * this happens if required).
382 */
383 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE,
384
385 /**
386 * Allow multiple values with the same key.
387 */
388 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE,
389
390 /**
391 * There must only be one value per key; storing
392 * a value should fail if a value under the same
393 * key already exists.
394 */
395 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY,
396
397 /**
398 * There must only be one value per key, but don't
399 * bother checking if a value already exists
400 * (faster than UNIQUE_ONLY; implemented just like
401 * MULTIPLE but this option documents better what
402 * is intended if UNIQUE is what is desired).
403 */
404 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST
405};
406
407/**
408 * Iterator over HashCodes.
409 *
410 * @param key current key code
411 * @param value value in the hash map
412 * @param cls client-defined argument
413 * @return GNUNET_YES if we should continue to
414 * iterate,
415 * GNUNET_NO if not.
416 */
417typedef int (*GNUNET_CONTAINER_HashMapIterator) (const GNUNET_HashCode * key,
418 void *value, void *cls);
419
420
421/**
422 * Create a multi hash map.
423 *
424 * @param map the map
425 * @param len initial size (map will grow as needed)
426 * @return NULL on error
427 */
428struct GNUNET_CONTAINER_MultiHashMap
429 *GNUNET_CONTAINER_multihashmap_create (unsigned int len);
430
431/**
432 * Destroy a hash map. Will not free any values
433 * stored in the hash map!
434 *
435 * @param map the map
436 */
437void GNUNET_CONTAINER_multihashmap_destroy (struct
438 GNUNET_CONTAINER_MultiHashMap
439 *map);
440
441/**
442 * Given a key find a value in the
443 * map matching the key.
444 *
445 * @param map the map
446 * @param key what to look for
447 * @return NULL if no value was found; note that
448 * this is indistinguishable from values that just
449 * happen to be NULL; use "contains" to test for
450 * key-value pairs with value NULL
451 */
452void *GNUNET_CONTAINER_multihashmap_get (const struct
453 GNUNET_CONTAINER_MultiHashMap *map,
454 const GNUNET_HashCode * key);
455
456/**
457 * Remove the given key-value pair from the map.
458 * Note that if the key-value pair is in the map
459 * multiple times, only one of the pairs will be
460 * removed.
461 *
462 * @param map the map
463 * @param key key of the key-value pair
464 * @param value value of the key-value pair
465 * @return GNUNET_YES on success, GNUNET_NO if the key-value pair
466 * is not in the map
467 */
468int GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap
469 *map, const GNUNET_HashCode * key,
470 void *value);
471
472/**
473 * Remove all entries for the given key from the map.
474 * Note that the values would not be "freed".
475 *
476 * @param map the map
477 * @param key identifies values to be removed
478 * @return number of values removed
479 */
480int GNUNET_CONTAINER_multihashmap_remove_all (struct
481 GNUNET_CONTAINER_MultiHashMap
482 *map,
483 const GNUNET_HashCode * key);
484
485/**
486 * Check if the map contains any value under the given
487 * key (including values that are NULL).
488 *
489 * @param map the map
490 * @param key the key to test if a value exists for it
491 * @return GNUNET_YES if such a value exists,
492 * GNUNET_NO if not
493 */
494int GNUNET_CONTAINER_multihashmap_contains (const struct
495 GNUNET_CONTAINER_MultiHashMap
496 *map,
497 const GNUNET_HashCode * key);
498
499/**
500 * Store a key-value pair in the map.
501 *
502 * @param map the map
503 * @param key key to use
504 * @param value value to use
505 * @param opt options for put
506 * @return GNUNET_OK on success,
507 * GNUNET_NO if a value was replaced (with REPLACE)
508 * GNUNET_SYSERR if UNIQUE_ONLY was the option and the
509 * value already exists
510 */
511int GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap
512 *map, const GNUNET_HashCode * key,
513 void *value,
514 enum
515 GNUNET_CONTAINER_MultiHashMapOption
516 opt);
517
518/**
519 * Get the number of key-value pairs in the map.
520 *
521 * @param map the map
522 * @return the number of key value pairs
523 */
524unsigned int GNUNET_CONTAINER_multihashmap_size (const struct
525 GNUNET_CONTAINER_MultiHashMap
526 *map);
527
528
529/**
530 * Iterate over all entries in the map.
531 *
532 * @param map the map
533 * @param iterator function to call on each entry
534 * @param cls extra argument to it
535 * @return the number of key value pairs processed,
536 * GNUNET_SYSERR if it aborted iteration
537 */
538int GNUNET_CONTAINER_multihashmap_iterate (const struct
539 GNUNET_CONTAINER_MultiHashMap *map,
540 GNUNET_CONTAINER_HashMapIterator
541 iterator, void *cls);
542
543/**
544 * Iterate over all entries in the map
545 * that match a particular key.
546 *
547 * @param map the map
548 * @param key key that the entries must correspond to
549 * @param iterator function to call on each entry
550 * @param cls extra argument to it
551 * @return the number of key value pairs processed,
552 * GNUNET_SYSERR if it aborted iteration
553 */
554int GNUNET_CONTAINER_multihashmap_get_multiple (const struct
555 GNUNET_CONTAINER_MultiHashMap
556 *map,
557 const GNUNET_HashCode * key,
558 GNUNET_CONTAINER_HashMapIterator
559 iterator, void *cls);
560/**
561 * Returns the stored value of a random non-null entry
562 * in the hash table. Returns only the first value, does
563 * not go inside bucket linked list (yet). Runs with a
564 * worst case time of N, so it's not efficient in any way
565 * shape or form!!!!.
566 */
567void *GNUNET_CONTAINER_multihashmap_get_random (const struct
568 GNUNET_CONTAINER_MultiHashMap
569 *map);
570
571
572
573
574/* ******************** doubly-linked list *************** */
575
576/**
577 * Insert an element into a DLL. Assumes
578 * that head, tail and element are structs
579 * with prev and next fields.
580 */
581#define GNUNET_CONTAINER_DLL_insert(head,tail,element) \
582 (element)->next = (head); \
583 (element)->prev = NULL; \
584 if ((tail) == NULL) \
585 (tail) = element; \
586 else \
587 (head)->prev = element; \
588 (head) = (element);
589
590/**
591 * Insert an element into a DLL after the given other
592 * element. Insert at the head if the other
593 * element is NULL.
594 */
595#define GNUNET_CONTAINER_DLL_insert_after(head,tail,other,element) \
596 (element)->prev = (other); \
597 if (NULL == other) \
598 { \
599 (element)->next = (head); \
600 (head) = (element); \
601 } \
602 else \
603 { \
604 (element)->next = (other)->next; \
605 (other)->next = (element); \
606 } \
607 if (NULL == (element)->next) \
608 (tail) = (element); \
609 else \
610 (element)->next->prev = (element);
611
612
613
614
615/**
616 * Remove an element from a DLL. Assumes
617 * that head, tail and element are structs
618 * with prev and next fields.
619 */
620#define GNUNET_CONTAINER_DLL_remove(head,tail,element) \
621 if ((element)->prev == NULL) \
622 (head) = (element)->next; \
623 else \
624 (element)->prev->next = (element)->next; \
625 if ((element)->next == NULL) \
626 (tail) = (element)->prev; \
627 else \
628 (element)->next->prev = (element)->prev;
629
630
631
632/* ******************** Heap *************** */
633
634
635/**
636 * Cost by which elements in a heap can be ordered.
637 */
638typedef unsigned int GNUNET_CONTAINER_HeapCost;
639
640/*
641 * Heap type, either max or min. Hopefully makes the
642 * implementation more useful.
643 */
644enum GNUNET_CONTAINER_HeapOrder
645{
646 /**
647 * Heap with the maximum cost at the root.
648 */
649 GNUNET_CONTAINER_HEAP_ORDER_MAX,
650
651 /**
652 * Heap with the minimum cost at the root.
653 */
654 GNUNET_CONTAINER_HEAP_ORDER_MIN
655};
656
657/**
658 * Handle to a Heap.
659 */
660struct GNUNET_CONTAINER_Heap;
661
662/**
663 * Create a new heap.
664 *
665 * @param type should the minimum or the maximum element be the root
666 * @return NULL on error, otherwise a fresh heap
667 */
668struct GNUNET_CONTAINER_Heap *GNUNET_CONTAINER_heap_create (enum
669 GNUNET_CONTAINER_HeapOrder
670 type);
671
672/**
673 * Free a heap
674 *
675 * @param h heap to free.
676 */
677void GNUNET_CONTAINER_heap_destroy (struct GNUNET_CONTAINER_Heap *h);
678
679/**
680 * Function called on elements of a heap.
681 *
682 * @param cls closure
683 * @param element obj stored in heap
684 * @param cost cost of the element
685 * @return GNUNET_YES if we should continue to iterate,
686 * GNUNET_NO if not.
687 */
688typedef int (*GNUNET_CONTAINER_HeapIterator) (void *cls,
689 void *element,
690 GNUNET_CONTAINER_HeapCost cost);
691/**
692 * Iterate over all entries in the map.
693 *
694 * @param heap the heap
695 * @param iterator function to call on each entry
696 * @param iterator_cls closure for iterator
697 * @return number of items handled
698 * GNUNET_SYSERR if iteration was aborted by iterator
699 */
700int GNUNET_CONTAINER_heap_iterate (struct GNUNET_CONTAINER_Heap *heap,
701 GNUNET_CONTAINER_HeapIterator iterator,
702 void *iterator_cls);
703
704
705/**
706 * Inserts a new item into the heap, item is always neighbor now.
707 * @param heap the heap
708 */
709int
710GNUNET_CONTAINER_heap_insert (struct GNUNET_CONTAINER_Heap *heap,
711 void *element, GNUNET_CONTAINER_HeapCost cost);
712
713/**
714 * Removes root of the tree, is remove max if a max heap and remove min
715 * if a min heap, returns the data stored at the node.
716 *
717 * @param heap the heap
718 * @return NULL if the heap is empty
719 */
720void *GNUNET_CONTAINER_heap_remove_root (struct GNUNET_CONTAINER_Heap *heap);
721
722/**
723 * Returns element stored at root of tree, doesn't effect anything
724 *
725 * @param heap the heap
726 * @return NULL if the heap is empty
727 */
728void *GNUNET_CONTAINER_heap_peek (struct GNUNET_CONTAINER_Heap *heap);
729
730/**
731 * Removes any node from the tree based on the neighbor given, does
732 * not traverse the tree (backpointers) but may take more time due to
733 * percolation of nodes.
734 * @param heap the heap
735 */
736void *GNUNET_CONTAINER_heap_remove_node (struct GNUNET_CONTAINER_Heap *heap,
737 void *element);
738
739/**
740 * Updates the cost of any node in the tree
741 *
742 * @param heap the heap
743 * @param element the element for which the cost is updated
744 * @param new_cost new cost for the element
745 * @return WHAT?
746 */
747int
748GNUNET_CONTAINER_heap_update_cost (struct GNUNET_CONTAINER_Heap *heap,
749 void *element,
750 GNUNET_CONTAINER_HeapCost new_cost);
751
752/**
753 * Random walk of the tree, returns the data stored at the next random node
754 * in the walk. Calls callee with the data, or NULL if the tree is empty
755 * or some other problem crops up.
756 *
757 * @param heap the heap
758 * @return the next element from the random walk
759 */
760void *GNUNET_CONTAINER_heap_walk_get_next (struct GNUNET_CONTAINER_Heap
761 *heap);
762
763/**
764 * Returns the current size of the heap
765 *
766 * @param heap the heap to get the size of
767 * @return number of elements in the heap
768 */
769unsigned int
770GNUNET_CONTAINER_heap_get_size (struct GNUNET_CONTAINER_Heap *heap);
771
772
773
774#if 0 /* keep Emacsens' auto-indent happy */
775{
776#endif
777#ifdef __cplusplus
778}
779#endif
780
781
782/* ifndef GNUNET_CONTAINER_LIB_H */
783#endif
784/* end of gnunet_container_lib.h */
diff --git a/src/include/gnunet_core_service.h b/src/include/gnunet_core_service.h
new file mode 100644
index 000000000..8b75271b9
--- /dev/null
+++ b/src/include/gnunet_core_service.h
@@ -0,0 +1,323 @@
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 include/gnunet_core_service.h
23 * @brief core service; this is the main API for encrypted P2P
24 * communications
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_CORE_SERVICE_H
29#define GNUNET_CORE_SERVICE_H
30
31#ifdef __cplusplus
32extern "C"
33{
34#if 0 /* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39#include "gnunet_util_lib.h"
40
41/**
42 * Version number of GNUnet-core API.
43 */
44#define GNUNET_CORE_VERSION 0x00000000
45
46
47/**
48 * Opaque handle to the service.
49 */
50struct GNUNET_CORE_Handle;
51
52
53/**
54 * Method called whenever a given peer either connects or
55 * disconnects (or list of connections was requested).
56 *
57 * @param cls closure
58 * @param peer peer identity this notification is about
59 * @param bpm how much bandwidth is available
60 * (for sending) to this peer
61 * @param last_activity when did we last
62 * receive anything from this peer?
63 */
64typedef void (*GNUNET_CORE_ClientEventHandler) (void *cls,
65 const struct
66 GNUNET_PeerIdentity * peer,
67 unsigned int bpm,
68 struct GNUNET_TIME_Absolute
69 last_activity);
70
71
72/**
73 * Type of a send callback to fill up buffers.
74 *
75 * @param receiver the receiver of the message
76 * @param position is the reference to the
77 * first unused position in the buffer where GNUnet is building
78 * the message
79 * @param padding is the number of bytes left in that buffer.
80 * @return the number of bytes written to
81 * that buffer (must be a positive number).
82 */
83typedef unsigned int
84 (*GNUNET_CORE_BufferFillCallback) (void *cls,
85 const struct GNUNET_PeerIdentity *
86 receiver,
87 void *position, unsigned int padding);
88
89
90/**
91 * Functions with this signature are called whenever a message is
92 * received or transmitted.
93 *
94 * @param cls closure
95 * @param peer the other peer involved (sender or receiver, NULL
96 * for loopback messages where we are both sender and receiver)
97 * @param message the actual message
98 * @return GNUNET_OK to keep the connection open,
99 * GNUNET_SYSERR to close it (signal serious error)
100 */
101typedef int
102 (*GNUNET_CORE_MessageCallback) (void *cls,
103 const struct GNUNET_PeerIdentity * other,
104 const struct GNUNET_MessageHeader *
105 message);
106
107
108/**
109 * Message handler. Each struct specifies how to handle on particular
110 * type of message received.
111 */
112struct GNUNET_CORE_MessageHandler
113{
114 /**
115 * Function to call for messages of "type".
116 */
117 GNUNET_CORE_MessageCallback callback;
118
119 /**
120 * Type of the message this handler covers.
121 */
122 uint16_t type;
123
124 /**
125 * Expected size of messages of this type. Use 0 for variable-size.
126 * If non-zero, messages of the given type will be discarded if they
127 * do not have the right size.
128 */
129 uint16_t expected_size;
130
131};
132
133
134/**
135 * Function called after GNUNET_CORE_connect has succeeded
136 * (or failed for good). Note that the private key of the
137 * peer is intentionally not exposed here; if you need it,
138 * your process should try to read the private key file
139 * directly (which should work if you are authorized...).
140 *
141 * @param cls closure
142 * @param server handle to the server, NULL if we failed
143 * @param my_identity ID of this peer, NULL if we failed
144 * @param publicKey public key of this peer, NULL if we failed
145 */
146typedef void
147 (*GNUNET_CORE_StartupCallback) (void *cls,
148 struct GNUNET_CORE_Handle * server,
149 const struct GNUNET_PeerIdentity *
150 my_identity,
151 const struct
152 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
153 publicKey);
154
155
156/**
157 * Connect to the core service. Note that the connection may
158 * complete (or fail) asynchronously.
159 *
160 * @param sched scheduler to use
161 * @param cfg configuration to use
162 * @param timeout after how long should we give up trying to connect to the core service?
163 * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
164 * @param init callback to call on timeout or once we have successfully
165 * connected to the core service
166 * @param connects function to call on peer connect, can be NULL
167 * @param disconnects function to call on peer disconnect / timeout, can be NULL
168 * @param bfc function to call to fill up spare bandwidth, can be NULL
169 * @param inbound_notify function to call for all inbound messages, can be NULL
170 * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the
171 * GNUNET_MessageHeader and hence we do not need to give it the full message;
172 * can be used to improve efficiency, ignored if inbound_notify is NULLL
173 * @param outbound_notify function to call for all outbound messages, can be NULL
174 * @param outbound_hdr_only set to GNUNET_YES if outbound_notify will only read the
175 * GNUNET_MessageHeader and hence we do not need to give it the full message
176 * can be used to improve efficiency, ignored if outbound_notify is NULLL
177 * @param handlers callbacks for messages we care about, NULL-terminated
178 */
179void
180GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
181 struct GNUNET_CONFIGURATION_Handle *cfg,
182 struct GNUNET_TIME_Relative timeout,
183 void *cls,
184 GNUNET_CORE_StartupCallback init,
185 GNUNET_CORE_ClientEventHandler connects,
186 GNUNET_CORE_ClientEventHandler disconnects,
187 GNUNET_CORE_BufferFillCallback bfc,
188 GNUNET_CORE_MessageCallback inbound_notify,
189 int inbound_hdr_only,
190 GNUNET_CORE_MessageCallback outbound_notify,
191 int outbound_hdr_only,
192 const struct GNUNET_CORE_MessageHandler *handlers);
193
194
195/**
196 * Disconnect from the core service.
197 *
198 * @param handle connection to core to disconnect
199 */
200void GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle);
201
202
203/**
204 * Function called with statistics about the given peer.
205 *
206 * @param peer identifies the peer
207 * @param latency current latency estimate, "FOREVER" if we have been
208 * disconnected
209 * @param bpm_in set to the current bandwidth limit (receiving) for this peer
210 * @param bpm_out set to the current bandwidth limit (sending) for this peer
211 * @param amount set to the amount that was actually reserved or unreserved
212 * @param preference current traffic preference for the given peer
213 */
214typedef void
215 (*GNUNET_CORE_PeerConfigurationInfoCallback) (void *cls,
216 const struct
217 GNUNET_PeerIdentity * peer,
218 unsigned int bpm_in,
219 unsigned int bpm_out,
220 struct GNUNET_TIME_Relative
221 latency, int amount,
222 double preference);
223
224
225/**
226 * Obtain statistics and/or change preferences for the given peer.
227 *
228 * @param handle connection to core to use
229 * @param peer identifies the peer
230 * @param timeout after how long should we give up (and call "info" with NULL
231 * for "peer" to signal an error)?
232 * @param bpm_out set to the current bandwidth limit (sending) for this peer,
233 * caller should set "bpm_out" to "-1" to avoid changing
234 * the current value; otherwise "bpm_out" will be lowered to
235 * the specified value; passing a pointer to "0" can be used to force
236 * us to disconnect from the peer; "bpm_out" might not increase
237 * as specified since the upper bound is generally
238 * determined by the other peer!
239 * @param amount reserve N bytes for receiving, negative
240 * amounts can be used to undo a (recent) reservation;
241 * @param preference increase incoming traffic share preference by this amount;
242 * in the absence of "amount" reservations, we use this
243 * preference value to assign proportional bandwidth shares
244 * to all connected peers
245 * @param info function to call with the resulting configuration information
246 * @param info_cls closure for info
247 */
248void
249GNUNET_CORE_peer_configure (struct GNUNET_CORE_Handle *handle,
250 const struct GNUNET_PeerIdentity *peer,
251 struct GNUNET_TIME_Relative timeout,
252 unsigned int bpm_out,
253 int amount,
254 double preference,
255 GNUNET_CORE_PeerConfigurationInfoCallback info,
256 void *info_cls);
257
258
259/**
260 * Handle for a transmission request.
261 */
262struct GNUNET_CORE_TransmitHandle;
263
264
265/**
266 * Ask the core to call "notify" once it is ready to transmit the
267 * given number of bytes to the specified "target". If we are not yet
268 * connected to the specified peer, a call to this function will cause
269 * us to try to establish a connection.
270 *
271 * @param handle connection to core service
272 * @param priority how important is the message?
273 * @param maxdelay how long can the message wait?
274 * @param target who should receive the message,
275 * use NULL for this peer (loopback)
276 * @param notify_size how many bytes of buffer space does notify want?
277 * @param notify function to call when buffer space is available
278 * @param notify_cls closure for notify
279 * @return non-NULL if the notify callback was queued,
280 * NULL if we can not even queue the request (insufficient
281 * memory); if NULL is returned, "notify" will NOT be called.
282 */
283struct GNUNET_CORE_TransmitHandle *GNUNET_CORE_notify_transmit_ready (struct
284 GNUNET_CORE_Handle
285 *handle,
286 unsigned
287 int
288 priority,
289 struct
290 GNUNET_TIME_Relative
291 maxdelay,
292 const
293 struct
294 GNUNET_PeerIdentity
295 *target,
296 size_t
297 notify_size,
298 GNUNET_NETWORK_TransmitReadyNotify
299 notify,
300 void
301 *notify_cls);
302
303
304/**
305 * Cancel the specified transmission-ready notification.
306 *
307 * @param h handle that was returned by "notify_transmit_ready".
308 */
309void
310GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle
311 *h);
312
313
314#if 0 /* keep Emacsens' auto-indent happy */
315{
316#endif
317#ifdef __cplusplus
318}
319#endif
320
321/* ifndef GNUNET_CORE_SERVICE_H */
322#endif
323/* end of gnunet_core_service.h */
diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h
new file mode 100644
index 000000000..361d244e2
--- /dev/null
+++ b/src/include/gnunet_crypto_lib.h
@@ -0,0 +1,567 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 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/**
22 * @file include/gnunet_crypto_lib.h
23 * @brief cryptographic primitives for GNUnet
24 *
25 * @author Christian Grothoff
26 * @author Krista Bennett
27 * @author Gerd Knorr <kraxel@bytesex.org>
28 * @author Ioana Patrascu
29 * @author Tzvetan Horozov
30 */
31
32#ifndef GNUNET_CRYPTO_LIB_H
33#define GNUNET_CRYPTO_LIB_H
34
35#ifdef __cplusplus
36extern "C"
37{
38#if 0 /* keep Emacsens' auto-indent happy */
39}
40#endif
41#endif
42
43#include "gnunet_common.h"
44#include "gnunet_scheduler_lib.h"
45
46
47enum GNUNET_CRYPTO_Quality
48{
49 GNUNET_CRYPTO_QUALITY_WEAK,
50 GNUNET_CRYPTO_QUALITY_STRONG
51};
52
53
54/**
55 * @brief length of the sessionkey in bytes (256 BIT sessionkey)
56 */
57#define GNUNET_CRYPTO_AES_KEY_LENGTH (256/8)
58
59
60/**
61 * @brief Length of RSA encrypted data (2048 bit)
62 *
63 * We currently do not handle encryption of data
64 * that can not be done in a single call to the
65 * RSA methods (read: large chunks of data).
66 * We should never need that, as we can use
67 * the GNUNET_CRYPTO_hash for larger pieces of data for signing,
68 * and for encryption, we only need to encode sessionkeys!
69 */
70#define GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH 256
71
72
73/**
74 * Length of an RSA KEY (d,e,len), 2048 bit (=256 octests) key d, 2 byte e
75 */
76#define GNUNET_CRYPTO_RSA_KEY_LENGTH 258
77
78
79/**
80 * The private information of an RSA key pair.
81 */
82struct GNUNET_CRYPTO_RsaPrivateKey;
83
84
85/**
86 * @brief 0-terminated ASCII encoding of a GNUNET_HashCode.
87 */
88struct GNUNET_CRYPTO_HashAsciiEncoded
89{
90 unsigned char encoding[104];
91};
92
93
94
95/**
96 * @brief an RSA signature
97 */
98struct GNUNET_CRYPTO_RsaSignature
99{
100 unsigned char sig[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH];
101};
102
103
104/**
105 * @brief header of what an RSA signature signs
106 * this must be followed by "size - 8" bytes of
107 * the actual signed data
108 */
109struct GNUNET_CRYPTO_RsaSignaturePurpose
110{
111 /**
112 * How many bytes does this signature sign?
113 * (including this purpose header); in network
114 * byte order (!).
115 */
116 uint32_t size GNUNET_PACKED;
117
118 /**
119 * What does this signature vouch for? This
120 * must contain a GNUNET_SIGNATURE_PURPOSE_XXX
121 * constant (from gnunet_signatures.h). In
122 * network byte order!
123 */
124 uint32_t purpose GNUNET_PACKED;
125
126};
127
128
129/**
130 * @brief A public key.
131 */
132struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
133{
134 /**
135 * In big-endian, must be GNUNET_CRYPTO_RSA_KEY_LENGTH+4
136 */
137 uint16_t len GNUNET_PACKED;
138
139 /**
140 * Size of n in key; in big-endian!
141 */
142 uint16_t sizen GNUNET_PACKED;
143
144 /**
145 * The key itself, contains n followed by e.
146 */
147 unsigned char key[GNUNET_CRYPTO_RSA_KEY_LENGTH];
148
149 /**
150 * Padding (must be 0)
151 */
152 uint16_t padding GNUNET_PACKED;
153};
154
155
156/**
157 * RSA Encrypted data.
158 */
159struct GNUNET_CRYPTO_RsaEncryptedData
160{
161 unsigned char encoding[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH];
162};
163
164
165/**
166 * @brief type for session keys
167 */
168struct GNUNET_CRYPTO_AesSessionKey
169{
170 /**
171 * Actual key.
172 */
173 unsigned char key[GNUNET_CRYPTO_AES_KEY_LENGTH];
174
175 /**
176 * checksum!
177 */
178 uint32_t crc32 GNUNET_PACKED;
179};
180
181
182/**
183 * @brief IV for sym cipher
184 *
185 * NOTE: must be smaller (!) in size than the
186 * GNUNET_HashCode.
187 */
188struct GNUNET_CRYPTO_AesInitializationVector
189{
190 unsigned char iv[GNUNET_CRYPTO_AES_KEY_LENGTH / 2];
191};
192
193
194/* **************** Functions and Macros ************* */
195
196
197/**
198 * Compute the CRC32 checksum for the first len
199 * bytes of the buffer.
200 *
201 * @param buf the data over which we're taking the CRC
202 * @param len the length of the buffer in bytes
203 * @return the resulting CRC32 checksum
204 */
205int GNUNET_CRYPTO_crc32_n (const void *buf, unsigned int len);
206
207
208/**
209 * Produce a random value.
210 *
211 * @param i the upper limit (exclusive) for the random number
212 * @return a random value in the interval [0,i[.
213 */
214unsigned int GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality,
215 unsigned int i);
216
217
218/**
219 * Random on unsigned 64-bit values. We break them down into signed
220 * 32-bit values and reassemble the 64-bit random value bit-wise.
221 */
222unsigned long long GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode,
223 unsigned long long u);
224
225
226/**
227 * Get an array with a random permutation of the
228 * numbers 0...n-1.
229 * @param mode GNUNET_CRYPTO_QUALITY_STRONG if the strong (but expensive) PRNG should be used, GNUNET_CRYPTO_QUALITY_WEAK otherwise
230 * @param n the size of the array
231 * @return the permutation array (allocated from heap)
232 */
233unsigned int *GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode,
234 unsigned int n);
235
236
237/**
238 * Create a new Session key.
239 */
240void GNUNET_CRYPTO_aes_create_session_key (struct GNUNET_CRYPTO_AesSessionKey
241 *key);
242
243
244/**
245 * Check that a new session key is well-formed.
246 *
247 * @return GNUNET_OK if the key is valid
248 */
249int GNUNET_CRYPTO_aes_check_session_key (const struct
250 GNUNET_CRYPTO_AesSessionKey *key);
251
252
253/**
254 * Encrypt a block with the public key of another
255 * host that uses the same cyper.
256 *
257 * @param block the block to encrypt
258 * @param len the size of the block
259 * @param sessionkey the key used to encrypt
260 * @param iv the initialization vector to use, use INITVALUE
261 * for streams.
262 * @returns the size of the encrypted block, -1 for errors
263 */
264int GNUNET_CRYPTO_aes_encrypt (const void *block,
265 uint16_t len,
266 const struct GNUNET_CRYPTO_AesSessionKey
267 *sessionkey,
268 const struct
269 GNUNET_CRYPTO_AesInitializationVector *iv,
270 void *result);
271
272
273/**
274 * Decrypt a given block with the sessionkey.
275 *
276 * @param sessionkey the key used to decrypt
277 * @param block the data to decrypt, encoded as returned by encrypt
278 * @param size how big is the block?
279 * @param iv the initialization vector to use
280 * @param result address to store the result at
281 * @return -1 on failure, size of decrypted block on success
282 */
283int GNUNET_CRYPTO_aes_decrypt (const struct GNUNET_CRYPTO_AesSessionKey
284 *sessionkey, const void *block, uint16_t size,
285 const struct
286 GNUNET_CRYPTO_AesInitializationVector *iv,
287 void *result);
288
289
290/**
291 * Convert GNUNET_CRYPTO_hash to ASCII encoding.
292 * @param block the GNUNET_CRYPTO_hash code
293 * @param result where to store the encoding (struct GNUNET_CRYPTO_HashAsciiEncoded can be
294 * safely cast to char*, a '\0' termination is set).
295 */
296void GNUNET_CRYPTO_hash_to_enc (const GNUNET_HashCode * block,
297 struct GNUNET_CRYPTO_HashAsciiEncoded
298 *result);
299
300
301/**
302 * Convert ASCII encoding back to GNUNET_CRYPTO_hash
303 * @param enc the encoding
304 * @param result where to store the GNUNET_CRYPTO_hash code
305 * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding
306 */
307int GNUNET_CRYPTO_hash_from_string (const char *enc,
308 GNUNET_HashCode * result);
309
310
311/**
312 * Compute the distance between 2 hashcodes.
313 * The computation must be fast, not involve
314 * a.a or a.e (they're used elsewhere), and
315 * be somewhat consistent. And of course, the
316 * result should be a positive number.
317 * @return number between 0 and 65536
318 */
319unsigned int GNUNET_CRYPTO_hash_distance_u32 (const GNUNET_HashCode * a,
320 const GNUNET_HashCode * b);
321
322
323/**
324 * Hash block of given size.
325 * @param block the data to GNUNET_CRYPTO_hash, length is given as a second argument
326 * @param ret pointer to where to write the hashcode
327 */
328void GNUNET_CRYPTO_hash (const void *block, unsigned int size,
329 GNUNET_HashCode * ret);
330
331
332/**
333 * Function called once the hash computation over the
334 * specified file has completed.
335 *
336 * @param cls closure
337 * @param res resulting hash, NULL on error
338 */
339typedef void (*GNUNET_CRYPTO_HashCompletedCallback) (void *cls,
340 const GNUNET_HashCode *
341 res);
342
343
344/**
345 * Compute the hash of an entire file.
346 *
347 * @param sched scheduler to use
348 * @param priority scheduling priority to use
349 * @param run_on_shutdown should we complete even on shutdown?
350 * @param filename name of file to hash
351 * @param blocksize number of bytes to process in one task
352 * @param callback function to call upon completion
353 * @param callback_cls closure for callback
354 */
355void GNUNET_CRYPTO_hash_file (struct GNUNET_SCHEDULER_Handle *sched,
356 enum GNUNET_SCHEDULER_Priority priority,
357 int run_on_shutdown,
358 const char *filename,
359 size_t blocksize,
360 GNUNET_CRYPTO_HashCompletedCallback callback,
361 void *callback_cls);
362
363
364/**
365 * Create a random hash code.
366 */
367void GNUNET_CRYPTO_hash_create_random (GNUNET_HashCode * result);
368
369
370/**
371 * compute result(delta) = b - a
372 */
373void GNUNET_CRYPTO_hash_difference (const GNUNET_HashCode * a,
374 const GNUNET_HashCode * b,
375 GNUNET_HashCode * result);
376
377
378/**
379 * compute result(b) = a + delta
380 */
381void GNUNET_CRYPTO_hash_sum (const GNUNET_HashCode * a,
382 const GNUNET_HashCode * delta,
383 GNUNET_HashCode * result);
384
385
386/**
387 * compute result = a ^ b
388 */
389void GNUNET_CRYPTO_hash_xor (const GNUNET_HashCode * a,
390 const GNUNET_HashCode * b,
391 GNUNET_HashCode * result);
392
393
394/**
395 * Convert a hashcode into a key.
396 */
397void GNUNET_CRYPTO_hash_to_AES_key (const GNUNET_HashCode * hc,
398 struct GNUNET_CRYPTO_AesSessionKey *skey,
399 struct
400 GNUNET_CRYPTO_AesInitializationVector
401 *iv);
402
403
404/**
405 * Obtain a bit from a hashcode.
406 * @param code the GNUNET_CRYPTO_hash to index bit-wise
407 * @param bit index into the hashcode, [0...159]
408 * @return Bit \a bit from hashcode \a code, -1 for invalid index
409 */
410int GNUNET_CRYPTO_hash_get_bit (const GNUNET_HashCode * code,
411 unsigned int bit);
412
413
414/**
415 * Compare function for HashCodes, producing a total ordering
416 * of all hashcodes.
417 * @return 1 if h1 > h2, -1 if h1 < h2 and 0 if h1 == h2.
418 */
419int GNUNET_CRYPTO_hash_cmp (const GNUNET_HashCode * h1,
420 const GNUNET_HashCode * h2);
421
422
423/**
424 * Find out which of the two GNUNET_CRYPTO_hash codes is closer to target
425 * in the XOR metric (Kademlia).
426 * @return -1 if h1 is closer, 1 if h2 is closer and 0 if h1==h2.
427 */
428int GNUNET_CRYPTO_hash_xorcmp (const GNUNET_HashCode * h1,
429 const GNUNET_HashCode * h2,
430 const GNUNET_HashCode * target);
431
432
433/**
434 * Create a new private key. Caller must free return value.
435 */
436struct GNUNET_CRYPTO_RsaPrivateKey *GNUNET_CRYPTO_rsa_key_create (void);
437
438
439/**
440 * Create a new private key by reading it from a file. If the
441 * files does not exist, create a new key and write it to the
442 * file. Caller must free return value. Note that this function
443 * can not guarantee that another process might not be trying
444 * the same operation on the same file at the same time. The
445 * caller must somehow know that the file either already exists
446 * with a valid key OR be sure that no other process is calling
447 * this function at the same time. If the contents of the file
448 * are invalid the old file is deleted and a fresh key is
449 * created.
450 *
451 * @return new private key, NULL on error (for example,
452 * permission denied)
453 */
454struct GNUNET_CRYPTO_RsaPrivateKey
455 *GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename);
456
457
458/**
459 * Deterministically (!) create a private key using only the
460 * given HashCode as input to the PRNG.
461 */
462struct GNUNET_CRYPTO_RsaPrivateKey
463 *GNUNET_CRYPTO_rsa_key_create_from_hash (const GNUNET_HashCode * input);
464
465
466/**
467 * Free memory occupied by the private key.
468 * @param hostkey pointer to the memory to free
469 */
470void GNUNET_CRYPTO_rsa_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey);
471
472
473/**
474 * Extract the public key of the host.
475 * @param result where to write the result.
476 */
477void GNUNET_CRYPTO_rsa_key_get_public (const struct
478 GNUNET_CRYPTO_RsaPrivateKey *hostkey,
479 struct
480 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
481 *result);
482
483
484/**
485 * Encrypt a block with the public key of another host that uses the
486 * same cyper.
487 *
488 * @param block the block to encrypt
489 * @param size the size of block
490 * @param publicKey the encoded public key used to encrypt
491 * @param target where to store the encrypted block
492 * @returns GNUNET_SYSERR on error, GNUNET_OK if ok
493 */
494int GNUNET_CRYPTO_rsa_encrypt (const void *block,
495 uint16_t size,
496 const struct
497 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
498 *publicKey,
499 struct GNUNET_CRYPTO_RsaEncryptedData *target);
500
501
502/**
503 * Decrypt a given block with the hostkey.
504 *
505 * @param key the key to use
506 * @param block the data to decrypt, encoded as returned by encrypt, not consumed
507 * @param result pointer to a location where the result can be stored
508 * @param size how many bytes of a result are expected? Must be exact.
509 * @returns the size of the decrypted block (that is, size) or -1 on error
510 */
511int GNUNET_CRYPTO_rsa_decrypt (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
512 const struct GNUNET_CRYPTO_RsaEncryptedData
513 *block, void *result, uint16_t size);
514
515
516/**
517 * Sign a given block.
518 *
519 * @param key private key to use for the signing
520 * @param purpose what to sign (size, purpose)
521 * @param result where to write the signature
522 * @return GNUNET_SYSERR on error, GNUNET_OK on success
523 */
524int GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
525 const struct GNUNET_CRYPTO_RsaSignaturePurpose
526 *purpose,
527 struct GNUNET_CRYPTO_RsaSignature *result);
528
529
530/**
531 * Verify signature. Note that the caller MUST have already
532 * checked that "validate->size" bytes are actually available.
533 *
534 * @param purpose what is the purpose that validate should have?
535 * @param validate block to validate (size, purpose, data)
536 * @param sig signature that is being validated
537 * @param publicKey public key of the signer
538 * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
539 */
540int GNUNET_CRYPTO_rsa_verify (uint32_t purpose,
541 const struct GNUNET_CRYPTO_RsaSignaturePurpose
542 *validate,
543 const struct GNUNET_CRYPTO_RsaSignature *sig,
544 const struct
545 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
546 *publicKey);
547
548
549
550/**
551 * This function should only be called in testcases
552 * where strong entropy gathering is not desired
553 * (for example, for hostkey generation).
554 */
555void GNUNET_CRYPTO_random_disable_entropy_gathering (void);
556
557#if 0 /* keep Emacsens' auto-indent happy */
558{
559#endif
560#ifdef __cplusplus
561}
562#endif
563
564
565/* ifndef GNUNET_CRYPTO_LIB_H */
566#endif
567/* end of gnunet_crypto_lib.h */
diff --git a/src/include/gnunet_datastore_service.h b/src/include/gnunet_datastore_service.h
new file mode 100644
index 000000000..b20c6b100
--- /dev/null
+++ b/src/include/gnunet_datastore_service.h
@@ -0,0 +1,187 @@
1/*
2 This file is part of GNUnet
3 (C) 2004, 2005, 2006, 2007, 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 include/gnunet_datastore_service.h
23 * @brief API that can be used manage the
24 * datastore for files stored on a GNUnet node;
25 * note that the datastore is NOT responsible for
26 * on-demand encoding, that is achieved using
27 * a special kind of entry.
28 * @author Christian Grothoff
29 */
30
31#ifndef GNUNET_DATASTORE_SERVICE_H
32#define GNUNET_DATASTORE_SERVICE_H
33
34#include "gnunet_core.h"
35
36#ifdef __cplusplus
37extern "C"
38{
39#if 0 /* keep Emacsens' auto-indent happy */
40}
41#endif
42#endif
43
44
45/**
46 * Handle to the datastore service.
47 */
48struct GNUNET_DATASTORE_Handle;
49
50
51/**
52 * An iterator over a set of items stored in the datastore.
53 *
54 * @param cls closure
55 * @param key key for the content
56 * @param size number of bytes in data
57 * @param data content stored
58 * @param type type of the content
59 * @param priority priority of the content
60 * @param anonymity anonymity-level for the content
61 * @param expiration expiration time for the content
62 * @param uid unique identifier for the datum;
63 * maybe 0 if no unique identifier is available
64 *
65 * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue,
66 * GNUNET_NO to delete the item and continue (if supported)
67 */
68typedef int (*GNUNET_DATASTORE_Iterator) (void *cls,
69 const GNUNET_HashCode * key,
70 uint32_t size,
71 const void *data,
72 uint32_t type,
73 uint32_t priority,
74 uint32_t anonymity,
75 struct GNUNET_TIME_Absolute
76 expiration, unsigned long long uid);
77
78/**
79 * Connect to the datastore service.
80 *
81 * @param cfg configuration to use
82 * @param sched scheduler to use
83 * @return handle to use to access the service
84 */
85struct GNUNET_DATASTORE_Handle *GNUNET_DATASTORE_connect (struct
86 GNUNET_CONFIGURATION_Handle
87 *cfg,
88 struct
89 GNUNET_SCHEDULER_Handle
90 *sched);
91
92
93/**
94 * Disconnect from the datastore service (and free
95 * associated resources).
96 * @param h handle to the datastore
97 */
98void GNUNET_DATASTORE_disconnect (struct GNUNET_DATASTORE_Handle *h);
99
100
101/**
102 * Get the current on-disk size of the datastore.
103 * @param h handle to the datastore
104 * @return size estimate, -1 if datastore is not available (yet)
105 */
106unsigned long long GNUNET_DATASTORE_size (struct GNUNET_DATASTORE_Handle *h);
107
108
109/**
110 * Store an item in the datastore. If the item is already present,
111 * the priorities are summed up and the higher expiration time and
112 * lower anonymity level is used.
113 *
114 * @param h handle to the datastore
115 * @param key key for the value
116 * @param size number of bytes in data
117 * @param data content stored
118 * @param type type of the content
119 * @param priority priority of the content
120 * @param anonymity anonymity-level for the content
121 * @param expiration expiration time for the content
122 */
123void
124GNUNET_DATASTORE_put (struct GNUNET_DATASTORE_Handle *h,
125 const GNUNET_HashCode * key,
126 uint32_t size,
127 const void *data,
128 unit32_t type,
129 uint32_t priority,
130 uint32_t anonymity,
131 struct GNUNET_TIME_Absolute expiration);
132
133/**
134 * Iterate over the results for a particular key
135 * in the datastore.
136 *
137 * @param h handle to the datastore
138 * @param key maybe NULL (to match all entries)
139 * @param type desired type, 0 for any
140 * @param iter function to call on each matching value;
141 * will be called once with a NULL value at the end
142 * @param iter_cls closure for iter
143 */
144void
145GNUNET_DATASTORE_get (struct GNUNET_DATASTORE_Handle *h,
146 const GNUNET_HashCode * key,
147 uint32_t type,
148 GNUNET_DATASTORE_Iterator iter, void *iter_cls);
149
150
151/**
152 * Get a random value from the datastore.
153 *
154 * @param h handle to the datastore
155 * @param iter function to call on each matching value;
156 * will be called exactly once; if no values
157 * are available, the value will be NULL.
158 * @param iter_cls closure for iter
159 */
160void
161GNUNET_DATASTORE_get_random (struct GNUNET_DATASTORE_Handle *h,
162 GNUNET_DATASTORE_Iterator iter, void *iter_cls);
163
164
165/**
166 * Explicitly remove some content from the database.
167 *
168 * @param h handle to the datastore
169 * @param key key for the value
170 * @param size number of bytes in data
171 * @param data content stored
172 */
173void
174GNUNET_DATASTORE_remove (struct GNUNET_DATASTORE_Handle *h,
175 const GNUNET_HashCode * key,
176 uint32_t size, const void *data);
177
178
179#if 0 /* keep Emacsens' auto-indent happy */
180{
181#endif
182#ifdef __cplusplus
183}
184#endif
185
186/* end of gnunet_datastore_service.h */
187#endif
diff --git a/src/include/gnunet_directories.h.in b/src/include/gnunet_directories.h.in
new file mode 100644
index 000000000..b05b6d9ce
--- /dev/null
+++ b/src/include/gnunet_directories.h.in
@@ -0,0 +1,34 @@
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 include/gnunet_directories.h
23 * @brief directories and files in GNUnet (default locations)
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_DIRECTORIES
29#define GNUNET_DIRECTORIES
30
31#define GNUNET_DEFAULT_CLIENT_CONFIG_FILE "@GN_USER_HOME_DIR@/gnunet.conf"
32#define GNUNET_DEFAULT_DAEMON_CONFIG_FILE "@GN_DAEMON_CONFIG_DIR@/gnunetd.conf"
33
34#endif
diff --git a/src/include/gnunet_disk_lib.h b/src/include/gnunet_disk_lib.h
new file mode 100644
index 000000000..3886be7c9
--- /dev/null
+++ b/src/include/gnunet_disk_lib.h
@@ -0,0 +1,279 @@
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 include/gnunet_disk_lib.h
23 * @brief disk IO apis
24 */
25
26#ifndef GNUNET_DISK_LIB_H
27#define GNUNET_DISK_LIB_H
28
29#include "gnunet_configuration_lib.h"
30#include "gnunet_scheduler_lib.h"
31
32/* we need size_t, and since it can be both unsigned int
33 or unsigned long long, this IS platform dependent;
34 but "stdlib.h" should be portable 'enough' to be
35 unconditionally available... */
36#include <stdlib.h>
37
38#ifdef __cplusplus
39extern "C"
40{
41#if 0 /* keep Emacsens' auto-indent happy */
42}
43#endif
44#endif
45
46/**
47 * Get the number of blocks that are left on the partition that
48 * contains the given file (for normal users).
49 *
50 * @param part a file on the partition to check
51 * @return -1 on errors, otherwise the number of free blocks
52 */
53long GNUNET_DISK_get_blocks_available (const char *part);
54
55
56/**
57 * Check that fil corresponds to a filename
58 * (of a file that exists and that is not a directory).
59 *
60 * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
61 * else (will print an error message in that case, too).
62 */
63int GNUNET_DISK_file_test (const char *fil);
64
65
66/**
67 * Get the size of the file (or directory)
68 * of the given file (in bytes).
69 *
70 * @param includeSymLinks should symbolic links be
71 * included?
72 *
73 * @return GNUNET_OK on success, GNUNET_SYSERR on error
74 */
75int GNUNET_DISK_file_size (const char *filename,
76 unsigned long long *size, int includeSymLinks);
77
78
79/**
80 * Wrapper around "open()". Opens a file.
81 *
82 * @return file handle, -1 on error
83 */
84int GNUNET_DISK_file_open (const char *filename, int oflag, ...);
85
86
87/**
88 * Wrapper around "close()". Closes a file.
89 */
90void GNUNET_DISK_file_close (const char *filename, int fd);
91
92
93/**
94 * Read the contents of a binary file into a buffer.
95 * @param fileName the name of the file, not freed,
96 * must already be expanded!
97 * @param len the maximum number of bytes to read
98 * @param result the buffer to write the result to
99 * @return the number of bytes read on success, -1 on failure
100 */
101int GNUNET_DISK_file_read (const char *fileName, int len, void *result);
102
103
104/**
105 * Write a buffer to a file.
106 * @param fileName the name of the file, NOT freed!
107 * @param buffer the data to write
108 * @param n number of bytes to write
109 * @param mode the mode for file permissions
110 * @return GNUNET_OK on success, GNUNET_SYSERR on error
111 */
112int GNUNET_DISK_file_write (const char *fileName,
113 const void *buffer, unsigned int n,
114 const char *mode);
115
116
117/**
118 * Copy a file.
119 * @return GNUNET_OK on success, GNUNET_SYSERR on error
120 */
121int GNUNET_DISK_file_copy (const char *src, const char *dst);
122
123
124/**
125 * Scan a directory for files. The name of the directory
126 * must be expanded first (!).
127 *
128 * @param dirName the name of the directory
129 * @param callback the method to call for each file
130 * @param data argument to pass to callback
131 * @return the number of files found, -1 on error
132 */
133int GNUNET_DISK_directory_scan (const char *dirName,
134 GNUNET_FileNameCallback callback, void *data);
135
136
137/**
138 * Opaque handle used for iterating over a directory.
139 */
140struct GNUNET_DISK_DirectoryIterator;
141
142
143/**
144 * Function called to iterate over a directory.
145 *
146 * @param cls closure
147 * @param di argument to pass to "GNUNET_DISK_directory_iterator_next" to
148 * get called on the next entry (or finish cleanly)
149 * @param filename complete filename (absolute path)
150 * @param dirname directory name (absolute path)
151 */
152typedef void (*GNUNET_DISK_DirectoryIteratorCallback) (void *cls,
153 struct
154 GNUNET_DISK_DirectoryIterator
155 * di,
156 const char *filename,
157 const char *dirname);
158
159
160/**
161 * This function must be called during the DiskIteratorCallback
162 * (exactly once) to schedule the task to process the next
163 * filename in the directory (if there is one).
164 *
165 * @param iter opaque handle for the iterator
166 * @param can set to GNUNET_YES to terminate the iteration early
167 * @return GNUNET_YES if iteration will continue,
168 * GNUNET_NO if this was the last entry (and iteration is complete),
169 * GNUNET_SYSERR if "can" was YES
170 */
171int GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
172 *iter, int can);
173
174
175/**
176 * Scan a directory for files using the scheduler to run a task for
177 * each entry. The name of the directory must be expanded first (!).
178 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
179 * may provide a simpler API.
180 *
181 * @param sched scheduler to use
182 * @param prio priority to use
183 * @param dirName the name of the directory
184 * @param callback the method to call for each file
185 * @param callback_cls closure for callback
186 */
187void GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle
188 *sched,
189 enum GNUNET_SCHEDULER_Priority
190 prio, const char *dirName,
191 GNUNET_DISK_DirectoryIteratorCallback
192 callback, void *callback_cls);
193
194
195/**
196 * Create the directory structure for storing
197 * a file.
198 *
199 * @param filename name of a file in the directory
200 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure,
201 * GNUNET_NO if directory exists but is not writeable
202 */
203int GNUNET_DISK_directory_create_for_file (const char *filename);
204
205
206/**
207 * Test if fil is a directory that can be accessed.
208 * Will not print an error message if the directory
209 * does not exist. Will log errors if GNUNET_SYSERR is
210 * returned.
211 *
212 * @return GNUNET_YES if yes, GNUNET_NO if does not exist, GNUNET_SYSERR
213 * on any error and if exists but not directory
214 */
215int GNUNET_DISK_directory_test (const char *fil);
216
217
218/**
219 * Remove all files in a directory (rm -rf). Call with
220 * caution.
221 *
222 * @param fileName the file to remove
223 * @return GNUNET_OK on success, GNUNET_SYSERR on error
224 */
225int GNUNET_DISK_directory_remove (const char *fileName);
226
227
228/**
229 * Implementation of "mkdir -p"
230 *
231 * @param dir the directory to create
232 * @returns GNUNET_SYSERR on failure, GNUNET_OK otherwise
233 */
234int GNUNET_DISK_directory_create (const char *dir);
235
236
237/**
238 * @brief Removes special characters as ':' from a filename.
239 * @param fn the filename to canonicalize
240 */
241void GNUNET_DISK_filename_canonicalize (char *fn);
242
243
244/**
245 * @brief Change owner of a file
246 * @param filename file to change
247 * @param user new owner of the file
248 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
249 */
250int GNUNET_DISK_file_change_owner (const char *filename, const char *user);
251
252
253/**
254 * Construct full path to a file inside of the private
255 * directory used by GNUnet. Also creates the corresponding
256 * directory. If the resulting name is supposed to be
257 * a directory, end the last argument in '/' (or pass
258 * DIR_SEPARATOR_STR as the last argument before NULL).
259 *
260 * @param serviceName name of the service asking
261 * @param varargs is NULL-terminated list of
262 * path components to append to the
263 * private directory name.
264 * @return the constructed filename
265 */
266char *GNUNET_DISK_get_home_filename (struct GNUNET_CONFIGURATION_Handle *cfg,
267 const char *serviceName, ...);
268
269#if 0 /* keep Emacsens' auto-indent happy */
270{
271#endif
272#ifdef __cplusplus
273}
274#endif
275
276
277/* ifndef GNUNET_DISK_LIB_H */
278#endif
279/* end of gnunet_disk_lib.h */
diff --git a/src/include/gnunet_fragmentation_lib.h b/src/include/gnunet_fragmentation_lib.h
new file mode 100644
index 000000000..5089fae6a
--- /dev/null
+++ b/src/include/gnunet_fragmentation_lib.h
@@ -0,0 +1,113 @@
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 include/gnunet_fragmentation_lib.h
22 * @brief library to help fragment messages
23 * @author Christian Grothoff
24 */
25
26#ifndef GNUNET_FRAGMENTATION_LIB_H
27#define GNUNET_FRAGMENTATION_LIB_H
28
29#include "gnunet_common.h"
30#include "gnunet_statistics_service.h"
31
32#ifdef __cplusplus
33extern "C"
34{
35#if 0 /* keep Emacsens' auto-indent happy */
36}
37#endif
38#endif
39
40/**
41 * Function that is called with messages
42 * created by the fragmentation module.
43 *
44 * @param cls closure
45 * @param msg the message that was created
46 */
47typedef void (*GNUNET_FRAGMENT_MessageProcessor) (void *cls,
48 const struct
49 GNUNET_MessageHeader * msg);
50
51
52/**
53 * Fragment an over-sized message.
54 *
55 * @param msg the message to fragment
56 * @param mtu the maximum message size
57 * @param proc function to call for each fragment
58 * @param proc_cls closure for proc
59 */
60void GNUNET_FRAGMENT_fragment (const struct GNUNET_MessageHeader *msg,
61 uint16_t mtu,
62 GNUNET_FRAGMENT_MessageProcessor proc,
63 void *proc_cls);
64
65/**
66 * Defragmentation context.
67 */
68struct GNUNET_FRAGMENT_Context;
69
70/**
71 * Create a defragmentation context.
72 *
73 * @param stats statistics context
74 * @param proc function to call with defragmented messages
75 * @param proc_cls closure for proc
76 * @return the defragmentation context
77 */
78struct GNUNET_FRAGMENT_Context *GNUNET_FRAGMENT_context_create (struct
79 GNUNET_STATISTICS_Handle
80 *stats,
81 GNUNET_FRAGMENT_MessageProcessor
82 proc,
83 void
84 *proc_cls);
85
86
87/**
88 * Destroy the given defragmentation context.
89 */
90void GNUNET_FRAGMENT_context_destroy (struct GNUNET_FRAGMENT_Context *ctx);
91
92
93/**
94 * We have received a fragment. Process it.
95 *
96 * @param ctx the context
97 * @param sender who transmitted the fragment
98 * @param msg the message that was received
99 */
100void GNUNET_FRAGMENT_process (struct GNUNET_FRAGMENT_Context *ctx,
101 const struct GNUNET_PeerIdentity *sender,
102 const struct GNUNET_MessageHeader *msg);
103
104
105#if 0 /* keep Emacsens' auto-indent happy */
106{
107#endif
108#ifdef __cplusplus
109}
110#endif
111
112/* end of gnunet_fragmentation_lib.h */
113#endif
diff --git a/src/include/gnunet_getopt_lib.h b/src/include/gnunet_getopt_lib.h
new file mode 100644
index 000000000..722155568
--- /dev/null
+++ b/src/include/gnunet_getopt_lib.h
@@ -0,0 +1,251 @@
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 include/gnunet_getopt_lib.h
23 * @brief command line parsing and --help formatting
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_GETOPT_LIB_H
29#define GNUNET_GETOPT_LIB_H
30
31#ifdef __cplusplus
32extern "C"
33{
34#if 0 /* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39#include "gnunet_configuration_lib.h"
40
41/**
42 * @brief General context for command line processors.
43 */
44struct GNUNET_GETOPT_CommandLineProcessorContext
45{
46
47 /**
48 * Name of the application
49 */
50 const char *binaryName;
51
52 /**
53 * Name of application with option summary
54 */
55 const char *binaryOptions;
56
57 /**
58 * Array with all command line options.
59 */
60 const struct GNUNET_GETOPT_CommandLineOption *allOptions;
61
62 /**
63 * For configuration
64 */
65 struct GNUNET_CONFIGURATION_Handle *cfg;
66
67 /**
68 * Original command line
69 */
70 char *const *argv;
71
72 /**
73 * Total number of argv's.
74 */
75 unsigned int argc;
76
77 /**
78 * Current argument.
79 */
80 unsigned int currentArgument;
81
82};
83
84/**
85 * @brief Process a command line option
86 *
87 * @param ctx context for all options
88 * @param scls specific closure (for this processor)
89 * @param option long name of the option (i.e. "config" for --config)
90 * @param value argument, NULL if none was given
91 * @return GNUNET_OK to continue processing other options, GNUNET_SYSERR to abort
92 */
93typedef
94 int (*GNUNET_GETOPT_CommandLineOptionProcessor) (struct
95 GNUNET_GETOPT_CommandLineProcessorContext
96 * ctx, void *scls,
97 const char *option,
98 const char *value);
99
100/**
101 * @brief Definition of a command line option.
102 */
103struct GNUNET_GETOPT_CommandLineOption
104{
105
106 /**
107 * Short name of the option (use '\0' for none).
108 */
109 const char shortName;
110
111 /**
112 * Long name of the option (may not be NULL)
113 */
114 const char *name;
115
116 /**
117 * Name of the argument for the user in help text
118 */
119 const char *argumentHelp;
120
121 /**
122 * Help text for the option (description)
123 */
124 const char *description;
125
126 /**
127 * Is an argument required? 0: GNUNET_NO (includes optional), 1: GNUNET_YES.
128 */
129 int require_argument;
130
131 /**
132 * Handler for the option.
133 */
134 GNUNET_GETOPT_CommandLineOptionProcessor processor;
135
136 /**
137 * Specific closure to pass to the processor.
138 */
139 void *scls;
140
141};
142
143/**
144 * Macro defining the option to print the command line
145 * help text.
146 *
147 * @param about string with brief description of the application
148 */
149#define GNUNET_GETOPT_OPTION_HELP(about) \
150 { 'h', "help", (const char *) NULL, gettext_noop("print this help"), 0, &GNUNET_GETOPT_format_help_, (void *) about }
151
152/**
153 * Macro defining the option to print the version of
154 * the application
155 *
156 * @param version string with the version number
157 */
158#define GNUNET_GETOPT_OPTION_VERSION(version) \
159 { 'v', "version", (const char *) NULL, gettext_noop("print the version number"), 0, &GNUNET_GETOPT_print_version_, (void *) version }
160
161/**
162 * Get the log level
163 */
164#define GNUNET_GETOPT_OPTION_LOGFILE(logfn) \
165 { 'l', "logfile", "LOGFILE", gettext_noop("configure logging to write logs to LOGFILE"), 1, &GNUNET_GETOPT_set_string, (void *) logfn }
166
167/**
168 * Set the configuration option for logging.
169 */
170#define GNUNET_GETOPT_OPTION_LOGLEVEL(loglev) \
171 { 'L', "log", "LOGLEVEL", gettext_noop("configure logging to use LOGLEVEL"), 1, &GNUNET_GETOPT_set_string, (void *) loglev }
172
173/**
174 * Get number of verbose flags
175 */
176#define GNUNET_GETOPT_OPTION_VERBOSE(level) \
177 { 'V', "verbose", (const char *) NULL, gettext_noop("be verbose"), 0, &GNUNET_GETOPT_increment_value, (void *) level }
178
179/**
180 * Get configuration file name
181 */
182#define GNUNET_GETOPT_OPTION_CFG_FILE(fn) \
183 { 'c', "config", "FILENAME", gettext_noop("use configuration file FILENAME"), 1, &GNUNET_GETOPT_set_string, (void *) fn }
184
185/**
186 * Marker to end the list of options.
187 */
188#define GNUNET_GETOPT_OPTION_END \
189 { '\0', NULL, NULL, NULL, 0, NULL, NULL }
190
191/**
192 * Parse the command line.
193 *
194 * @param binaryName name of the binary / application with options
195 * @param cfg for storing/accessing configuration data
196 * @param allOptions defined options and handlers
197 * @param argc number of arguments
198 * @param argv actual arguments
199 * @return index into argv with first non-option
200 * argument, or GNUNET_SYSERR on error
201 */
202int GNUNET_GETOPT_run (const char *binaryName,
203 struct GNUNET_CONFIGURATION_Handle *cfg,
204 const struct GNUNET_GETOPT_CommandLineOption
205 *allOptions, unsigned int argc, char *const *argv);
206
207int GNUNET_GETOPT_set_ulong (struct GNUNET_GETOPT_CommandLineProcessorContext
208 *ctx, void *scls, const char *option,
209 const char *value);
210
211int GNUNET_GETOPT_set_uint (struct GNUNET_GETOPT_CommandLineProcessorContext
212 *ctx, void *scls, const char *option,
213 const char *value);
214
215int GNUNET_GETOPT_set_one (struct GNUNET_GETOPT_CommandLineProcessorContext
216 *ctx, void *scls, const char *option,
217 const char *value);
218
219int GNUNET_GETOPT_set_string (struct GNUNET_GETOPT_CommandLineProcessorContext
220 *ctx, void *scls, const char *option,
221 const char *value);
222
223int
224GNUNET_GETOPT_increment_value (struct
225 GNUNET_GETOPT_CommandLineProcessorContext *ctx,
226 void *scls, const char *option,
227 const char *value);
228
229/* *************** internal prototypes - use macros above! ************* */
230
231int GNUNET_GETOPT_format_help_ (struct
232 GNUNET_GETOPT_CommandLineProcessorContext
233 *ctx, void *scls, const char *option,
234 const char *value);
235
236int GNUNET_GETOPT_print_version_ (struct
237 GNUNET_GETOPT_CommandLineProcessorContext
238 *ctx, void *scls, const char *option,
239 const char *value);
240
241#if 0 /* keep Emacsens' auto-indent happy */
242{
243#endif
244#ifdef __cplusplus
245}
246#endif
247
248
249/* ifndef GNUNET_GETOPT_LIB_H */
250#endif
251/* end of gnunet_getopt_lib.h */
diff --git a/src/include/gnunet_hello_lib.h b/src/include/gnunet_hello_lib.h
new file mode 100644
index 000000000..2ba6df6ab
--- /dev/null
+++ b/src/include/gnunet_hello_lib.h
@@ -0,0 +1,201 @@
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 include/gnunet_hello_lib.h
23 * @brief helper library for handling HELLOs
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_HELLO_LIB_H
28#define GNUNET_HELLO_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_common.h"
39#include "gnunet_crypto_lib.h"
40
41/**
42 * A HELLO message is used to exchange information about
43 * transports with other peers. This struct is guaranteed
44 * to start with a "GNUNET_MessageHeader", everything else
45 * should be internal to the HELLO library.
46 */
47struct GNUNET_HELLO_Message;
48
49
50/**
51 * Copy the given address information into
52 * the given buffer using the format of HELLOs.
53 *
54 * @param tname name of the transport plugin
55 * @param expiration expiration for the address
56 * @param addr the address
57 * @param addr_len length of the address in bytes
58 * @param target where to copy the address
59 * @param max maximum number of bytes to copy to target
60 * @return number of bytes copied, 0 if
61 * the target buffer was not big enough.
62 */
63size_t
64GNUNET_HELLO_add_address (const char *tname,
65 struct GNUNET_TIME_Absolute expiration,
66 const void *addr,
67 size_t addr_len, char *target, size_t max);
68
69
70/**
71 * Callback function used to fill a buffer of max bytes with a list of
72 * addresses in the format used by HELLOs. Should use
73 * "GNUNET_HELLO_add_address" as a helper function.
74 *
75 * @param cls closure
76 * @param max maximum number of bytes that can be written to buf
77 * @param buf where to write the address information
78 * @return number of bytes written, 0 to signal the
79 * end of the iteration.
80 */
81typedef size_t
82 (*GNUNET_HELLO_GenerateAddressListCallback) (void *cls,
83 size_t max, void *buf);
84
85
86/**
87 * Construct a HELLO message given the public key,
88 * expiration time and an iterator that spews the
89 * transport addresses.
90 *
91 * @return the hello message
92 */
93struct GNUNET_HELLO_Message *GNUNET_HELLO_create (const struct
94 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
95 *publicKey,
96 GNUNET_HELLO_GenerateAddressListCallback
97 addrgen, void *addrgen_cls);
98
99
100/**
101 * Return the size of the given HELLO message.
102 * @param hello to inspect
103 * @return the size, 0 if HELLO is invalid
104 */
105uint16_t GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello);
106
107
108/**
109 * Construct a HELLO message by merging the
110 * addresses in two existing HELLOs (which
111 * must be for the same peer).
112 *
113 * @param h1 first HELLO message
114 * @param h2 the second HELLO message
115 * @return the combined hello message
116 */
117struct GNUNET_HELLO_Message *GNUNET_HELLO_merge (const struct
118 GNUNET_HELLO_Message *h1,
119 const struct
120 GNUNET_HELLO_Message *h2);
121
122
123/**
124 * Iterator callback to go over all addresses.
125 *
126 * @param cls closure
127 * @param tname name of the transport
128 * @param expiration expiration time
129 * @param addr the address
130 * @param addrlen length of the address
131 * @return GNUNET_OK to keep the address,
132 * GNUNET_NO to delete it from the HELLO
133 * GNUNET_SYSERR to stop iterating (but keep current address)
134 */
135typedef int
136 (*GNUNET_HELLO_AddressIterator) (void *cls,
137 const char *tname,
138 struct GNUNET_TIME_Absolute expiration,
139 const void *addr, size_t addrlen);
140
141
142/**
143 * Iterate over all of the addresses in the HELLO.
144 *
145 * @param msg HELLO to iterate over; client does not need to
146 * have verified that msg is well-formed (beyond starting
147 * with a GNUNET_MessageHeader of the right type).
148 * @param return_modified if a modified copy should be returned,
149 * otherwise NULL will be returned
150 * @param it iterator to call on each address
151 * @param it_cls closure for it
152 */
153struct GNUNET_HELLO_Message *GNUNET_HELLO_iterate_addresses (const struct
154 GNUNET_HELLO_Message
155 *msg,
156 int
157 return_modified,
158 GNUNET_HELLO_AddressIterator
159 it,
160 void *it_cls);
161
162
163/**
164 * Iterate over addresses in "new_hello" that
165 * are NOT already present in "old_hello".
166 *
167 * @param new_hello a HELLO message
168 * @param old_hello a HELLO message
169 * @param expiration_limit ignore addresses in old_hello
170 * that expired before the given time stamp
171 * @param it iterator to call on each address
172 * @param it_cls closure for it
173 */
174void
175GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
176 *new_hello,
177 const struct GNUNET_HELLO_Message
178 *old_hello,
179 struct GNUNET_TIME_Absolute
180 expiration_limit,
181 GNUNET_HELLO_AddressIterator it,
182 void *it_cls);
183
184
185/**
186 * Get the public key from a HELLO message.
187 *
188 * @param hello the hello message
189 * @param publicKey where to copy the public key information, can be NULL
190 * @return GNUNET_SYSERR if the HELLO was malformed
191 */
192int
193GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
194 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
195 *publicKey);
196
197
198
199/* ifndef GNUNET_HELLO_LIB_H */
200#endif
201/* end of gnunet_hello_lib.h */
diff --git a/src/include/gnunet_network_lib.h b/src/include/gnunet_network_lib.h
new file mode 100644
index 000000000..8731eacec
--- /dev/null
+++ b/src/include/gnunet_network_lib.h
@@ -0,0 +1,308 @@
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 include/gnunet_network_lib.h
23 * @brief basic, low-level TCP networking interface
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_NETWORK_LIB_H
27#define GNUNET_NETWORK_LIB_H
28
29#ifdef __cplusplus
30extern "C"
31{
32#if 0 /* keep Emacsens' auto-indent happy */
33}
34#endif
35#endif
36
37#include "gnunet_scheduler_lib.h"
38#include "gnunet_time_lib.h"
39
40/**
41 * Timeout we use on TCP connect before trying another
42 * result from the DNS resolver. 5s.
43 */
44#define GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
45
46/**
47 * @brief handle for a network socket
48 */
49struct GNUNET_NETWORK_SocketHandle;
50
51
52/**
53 * Function to call for access control checks.
54 *
55 * @param cls closure
56 * @param addr address
57 * @param addrlen length of address
58 * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR
59 * for unknown address family (will be denied).
60 */
61typedef int (*GNUNET_NETWORK_AccessCheck) (void *cls,
62 const struct sockaddr * addr,
63 socklen_t addrlen);
64
65
66/**
67 * Callback function for data received from the network. Note that
68 * both "available" and "err" would be 0 if the read simply timed out.
69 *
70 * @param cls closure
71 * @param buf pointer to received data
72 * @param available number of bytes availabe in "buf",
73 * possibly 0 (on errors)
74 * @param addr address of the sender
75 * @param addrlen size of addr
76 * @param errCode value of errno (on errors receiving)
77 */
78typedef void (*GNUNET_NETWORK_Receiver) (void *cls,
79 const void *buf,
80 size_t available,
81 const struct sockaddr * addr,
82 socklen_t addrlen, int errCode);
83
84
85/**
86 * Create a socket handle by boxing an existing OS socket. The OS
87 * socket should henceforth be no longer used directly.
88 * GNUNET_socket_destroy will close it.
89 *
90 * @param sched scheduler to use
91 * @param osSocket existing socket to box
92 * @param maxbuf maximum write buffer size for the socket (use
93 * 0 for sockets that need no write buffers, such as listen sockets)
94 * @return the boxed socket handle
95 */
96struct GNUNET_NETWORK_SocketHandle
97 *GNUNET_NETWORK_socket_create_from_existing (struct GNUNET_SCHEDULER_Handle
98 *sched, int osSocket,
99 size_t maxbuf);
100
101
102/**
103 * Create a socket handle by accepting on a listen socket. This
104 * function may block if the listen socket has no connection ready.
105 *
106 * @param sched scheduler to use
107 * @param access function to use to check if access is allowed
108 * @param access_cls closure for access
109 * @param lsock listen socket
110 * @param maxbuf maximum write buffer size for the socket (use
111 * 0 for sockets that need no write buffers, such as listen sockets)
112 * @return the socket handle, NULL on error (for example, access refused)
113 */
114struct GNUNET_NETWORK_SocketHandle
115 *GNUNET_NETWORK_socket_create_from_accept (struct GNUNET_SCHEDULER_Handle
116 *sched,
117 GNUNET_NETWORK_AccessCheck
118 access, void *access_cls,
119 int lsock, size_t maxbuf);
120
121
122/**
123 * Create a socket handle by (asynchronously) connecting to a host.
124 * This function returns immediately, even if the connection has not
125 * yet been established. This function only creates TCP connections.
126 *
127 * @param sched scheduler to use
128 * @param hostname name of the host to connect to
129 * @param port port to connect to
130 * @param maxbuf maximum write buffer size for the socket (use
131 * 0 for sockets that need no write buffers, such as listen sockets)
132 * @return the socket handle
133 */
134struct GNUNET_NETWORK_SocketHandle
135 *GNUNET_NETWORK_socket_create_from_connect (struct GNUNET_SCHEDULER_Handle
136 *sched, const char *hostname,
137 uint16_t port, size_t maxbuf);
138
139
140
141/**
142 * Create a socket handle by (asynchronously) connecting to a host.
143 * This function returns immediately, even if the connection has not
144 * yet been established. This function only creates TCP connections.
145 *
146 * @param sched scheduler to use
147 * @param af_family address family to use
148 * @param serv_addr server address
149 * @param addrlen length of server address
150 * @param maxbuf maximum write buffer size for the socket (use
151 * 0 for sockets that need no write buffers, such as listen sockets)
152 * @return the socket handle
153 */
154struct GNUNET_NETWORK_SocketHandle
155 *GNUNET_NETWORK_socket_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle
156 *sched, int af_family,
157 const struct sockaddr
158 *serv_addr, socklen_t addrlen,
159 size_t maxbuf);
160
161/**
162 * Check if socket is valid (no fatal errors have happened so far).
163 * Note that a socket that is still trying to connect is considered
164 * valid.
165 *
166 * @param sock socket to check
167 * @return GNUNET_YES if valid, GNUNET_NO otherwise
168 */
169int GNUNET_NETWORK_socket_check (struct GNUNET_NETWORK_SocketHandle *sock);
170
171
172/**
173 * Obtain the network address of the other party.
174 *
175 * @param sock the client to get the address for
176 * @param addr where to store the address
177 * @param addrlen where to store the length of the address
178 * @return GNUNET_OK on success
179 */
180int GNUNET_NETWORK_socket_get_address (struct GNUNET_NETWORK_SocketHandle
181 *sock, void **addr, size_t * addrlen);
182
183/**
184 * Close the socket and free associated resources. Pending
185 * transmissions are simply dropped. A pending receive call will be
186 * called with an error code of "EPIPE".
187 *
188 * @param sock socket to destroy
189 */
190void GNUNET_NETWORK_socket_destroy (struct GNUNET_NETWORK_SocketHandle *sock);
191
192
193/**
194 * Receive data from the given socket. Note that this function will
195 * call "receiver" asynchronously using the scheduler. It will
196 * "immediately" return. Note that there MUST only be one active
197 * receive call per socket at any given point in time (so do not
198 * call receive again until the receiver callback has been invoked).
199 *
200 * @param sock socket handle
201 * @param max maximum number of bytes to read
202 * @param timeout maximum amount of time to wait
203 * @param receiver function to call with received data
204 * @param receiver_cls closure for receiver
205 * @return scheduler task ID used for receiving, GNUNET_SCHEDULER_NO_PREREQUISITE_TASK on error
206 */
207GNUNET_SCHEDULER_TaskIdentifier
208GNUNET_NETWORK_receive (struct GNUNET_NETWORK_SocketHandle *sock,
209 size_t max,
210 struct GNUNET_TIME_Relative timeout,
211 GNUNET_NETWORK_Receiver receiver, void *receiver_cls);
212
213
214/**
215 * Cancel receive job on the given socket. Note that the
216 * receiver callback must not have been called yet in order
217 * for the cancellation to be valid.
218 *
219 * @param sock socket handle
220 * @param task task identifier returned from the receive call
221 * @return closure of the original receiver callback
222 */
223void *GNUNET_NETWORK_receive_cancel (struct GNUNET_NETWORK_SocketHandle *sock,
224 GNUNET_SCHEDULER_TaskIdentifier task);
225
226
227/**
228 * Function called to notify a client about the socket
229 * begin ready to queue more data. "buf" will be
230 * NULL and "size" zero if the socket was closed for
231 * writing in the meantime.
232 *
233 * @param cls closure
234 * @param size number of bytes available in buf
235 * @param buf where the callee should write the message
236 * @return number of bytes written to buf
237 */
238typedef size_t (*GNUNET_NETWORK_TransmitReadyNotify) (void *cls,
239 size_t size, void *buf);
240
241
242/**
243 * Opaque handle that can be used to cancel
244 * a transmit-ready notification.
245 */
246struct GNUNET_NETWORK_TransmitHandle;
247
248/**
249 * Ask the socket to call us once the specified number of bytes
250 * are free in the transmission buffer. May call the notify
251 * method immediately if enough space is available. Note that
252 * this function will abort if "size" is greater than
253 * "maxbuf" (as specified when the socket handle was created).
254 *
255 * Note that "notify" will be called either when enough
256 * buffer space is available OR when the socket is destroyed.
257 * The size parameter given to notify is guaranteed to be
258 * larger or equal to size if the buffer is ready, or zero
259 * if the socket was destroyed (or at least closed for
260 * writing). Finally, any time before 'notify' is called, a
261 * client may call "notify_transmit_ready_cancel" to cancel
262 * the transmission request.
263 *
264 * Only one transmission request can be scheduled at the same
265 * time. Notify will be run with the same scheduler priority
266 * as that of the caller.
267 *
268 * @param sock socket
269 * @param size number of bytes to send
270 * @param timeout after how long should we give up (and call
271 * notify with buf NULL and size 0)?
272 * @param notify function to call when buffer space is available
273 * @param notify_cls closure for notify
274 * @return non-NULL if the notify callback was queued,
275 * NULL if we are already going to notify someone else (busy)
276 */
277struct GNUNET_NETWORK_TransmitHandle
278 *GNUNET_NETWORK_notify_transmit_ready (struct GNUNET_NETWORK_SocketHandle
279 *sock, size_t size,
280 struct GNUNET_TIME_Relative timeout,
281 GNUNET_NETWORK_TransmitReadyNotify
282 notify, void *notify_cls);
283
284
285/**
286 * Cancel the specified transmission-ready
287 * notification.
288 *
289 * @param h handle for notification to cancel
290 */
291void
292GNUNET_NETWORK_notify_transmit_ready_cancel (struct
293 GNUNET_NETWORK_TransmitHandle
294 *h);
295
296
297
298#if 0 /* keep Emacsens' auto-indent happy */
299{
300#endif
301#ifdef __cplusplus
302}
303#endif
304
305
306/* ifndef GNUNET_NETWORK_LIB_H */
307#endif
308/* end of gnunet_network_lib.h */
diff --git a/src/include/gnunet_os_lib.h b/src/include/gnunet_os_lib.h
new file mode 100644
index 000000000..dfab4a747
--- /dev/null
+++ b/src/include/gnunet_os_lib.h
@@ -0,0 +1,158 @@
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 include/gnunet_os_lib.h
23 * @brief low level process routines
24 * @author Christian Grothoff
25 * @author Krista Bennett
26 * @author Gerd Knorr <kraxel@bytesex.org>
27 * @author Ioana Patrascu
28 * @author Tzvetan Horozov
29 * @author Milan
30 */
31
32#ifndef GNUNET_OS_LIB_H
33#define GNUNET_OS_LIB_H
34
35#ifdef __cplusplus
36extern "C"
37{
38#if 0 /* keep Emacsens' auto-indent happy */
39}
40#endif
41#endif
42
43#include "gnunet_common.h"
44#include "gnunet_configuration_lib.h"
45#include "gnunet_scheduler_lib.h"
46
47
48/**
49 * Possible installation paths to request
50 */
51enum GNUNET_OS_InstallationPathKind
52{
53 GNUNET_OS_IPK_PREFIX,
54 GNUNET_OS_IPK_BINDIR,
55 GNUNET_OS_IPK_LIBDIR,
56 GNUNET_OS_IPK_DATADIR,
57 GNUNET_OS_IPK_LOCALEDIR,
58 GNUNET_OS_IPK_SELF_PREFIX
59};
60
61
62/**
63 * Get the path to a specific GNUnet installation directory or, with
64 * GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation
65 * directory.
66 *
67 * @param dirkind what kind of directory is desired?
68 * @return a pointer to the dir path (to be freed by the caller)
69 */
70char *GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind
71 dirkind);
72
73
74/**
75 * Callback function invoked for each interface found.
76 *
77 * @param cls closure
78 * @param name name of the interface (can be NULL for unknown)
79 * @param isDefault is this presumably the default interface
80 * @param addr address of this interface (can be NULL for unknown or unassigned)
81 * @param addrlen length of the address
82 * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort
83 */
84typedef int (*GNUNET_OS_NetworkInterfaceProcessor) (void *cls,
85 const char *name,
86 int isDefault,
87 const struct sockaddr *
88 addr, socklen_t addrlen);
89
90
91/**
92 * @brief Enumerate all network interfaces
93 * @param callback the callback function
94 */
95void GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor
96 proc, void *cls);
97
98/**
99 * Get the current CPU load.
100 *
101 * @param cfg to determine acceptable load level (LOAD::MAXCPULOAD)
102 * @return -1 on error, otherwise load value (between 0 and 100,
103 * (100 is equivalent to full load for one CPU)
104 */
105int GNUNET_OS_load_cpu_get (struct GNUNET_CONFIGURATION_Handle *cfg);
106
107/**
108 * Get the current IO load.
109 *
110 * @param cfg to determine acceptable load level (LOAD::MAXIOLOAD)
111 * @return -1 on error, otherwise load value (between 0 and 100,
112 * 100 means that we spend all of our cycles waiting for
113 * the disk)
114 */
115int GNUNET_OS_load_disk_get (struct GNUNET_CONFIGURATION_Handle *cfg);
116
117
118/**
119 * Set process priority
120 *
121 * @param proc id of the process
122 * @param prio priority value
123 * @return GNUNET_OK on success, GNUNET_SYSERR on error
124 */
125int GNUNET_OS_set_process_priority (pid_t proc,
126 enum GNUNET_SCHEDULER_Priority prio);
127
128
129/**
130 * Start a process.
131 *
132 * @param filename name of the binary
133 * @param ... NULL-terminated list of arguments to the process
134 * @return process ID of the new process, -1 on error
135 */
136pid_t GNUNET_OS_start_process (const char *filename, ...);
137
138/**
139 * Start a process.
140 *
141 * @param filename name of the binary
142 * @param argv NULL-terminated list of arguments to the process,
143 * including the process name as the first argument
144 * @return process ID of the new process, -1 on error
145 */
146pid_t GNUNET_OS_start_process_v (const char *filename, char *const argv[]);
147
148#if 0 /* keep Emacsens' auto-indent happy */
149{
150#endif
151#ifdef __cplusplus
152}
153#endif
154
155
156/* ifndef GNUNET_OS_LIB_H */
157#endif
158/* end of gnunet_os_lib.h */
diff --git a/src/include/gnunet_peerinfo_service.h b/src/include/gnunet_peerinfo_service.h
new file mode 100644
index 000000000..45c52c447
--- /dev/null
+++ b/src/include/gnunet_peerinfo_service.h
@@ -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 include/gnunet_peerinfo_service.h
22 * @brief Code to maintain the list of currently known hosts
23 * (in memory structure of data/hosts) and their trust ratings
24 * (in memory structure of data/trust)
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_PEERINFO_SERVICE_H
29#define GNUNET_PEERINFO_SERVICE_H
30
31#include "gnunet_common.h"
32#include "gnunet_configuration_lib.h"
33#include "gnunet_crypto_lib.h"
34#include "gnunet_hello_lib.h"
35
36#ifdef __cplusplus
37extern "C"
38{
39#if 0 /* keep Emacsens' auto-indent happy */
40}
41#endif
42#endif
43
44
45/**
46 * Add a host to the persistent list.
47 *
48 * @param cfg configuration to use
49 * @param sched scheduler to use
50 * @param peer identity of the peer
51 * @param hello the verified (!) HELLO message
52 */
53void
54GNUNET_PEERINFO_add_peer (struct GNUNET_CONFIGURATION_Handle *cfg,
55 struct GNUNET_SCHEDULER_Handle *sched,
56 const struct GNUNET_PeerIdentity *peer,
57 const struct GNUNET_HELLO_Message *hello);
58
59/**
60 * Type of an iterator over the hosts. Note that each
61 * host will be called with each available protocol.
62 *
63 * @param cls closure
64 * @param peer id of the peer, NULL for last call
65 * @param hello hello message for the peer (can be NULL)
66 * @param trust amount of trust we have in the peer
67 */
68typedef void
69 (*GNUNET_PEERINFO_Processor) (void *cls,
70 const struct GNUNET_PeerIdentity * peer,
71 const struct GNUNET_HELLO_Message * hello,
72 uint32_t trust);
73
74
75/**
76 * Call a method for each known matching host and change
77 * its trust value. The method will be invoked once for
78 * each host and then finally once with a NULL pointer.
79 * Note that the last call can be triggered by timeout or
80 * by simply being done; however, the trust argument will
81 * be set to zero if we are done, 1 if we timed out and
82 * 2 for fatal error.
83 *
84 * @param cfg configuration to use
85 * @param sched scheduler to use
86 * @param peer restrict iteration to this peer only (can be NULL)
87 * @param trust_delta how much to change the trust in all matching peers
88 * @param timeout how long to wait until timing out
89 * @param callback the method to call for each peer
90 * @param callback_cls closure for callback
91 */
92void
93GNUNET_PEERINFO_for_all (struct GNUNET_CONFIGURATION_Handle *cfg,
94 struct GNUNET_SCHEDULER_Handle *sched,
95 const struct GNUNET_PeerIdentity *peer,
96 int trust_delta,
97 struct GNUNET_TIME_Relative timeout,
98 GNUNET_PEERINFO_Processor callback,
99 void *callback_cls);
100
101
102#if 0 /* keep Emacsens' auto-indent happy */
103{
104#endif
105#ifdef __cplusplus
106}
107#endif
108
109
110/* end of gnunet_peerinfo_service.h */
111#endif
diff --git a/src/include/gnunet_plugin_lib.h b/src/include/gnunet_plugin_lib.h
new file mode 100644
index 000000000..e4f603c4e
--- /dev/null
+++ b/src/include/gnunet_plugin_lib.h
@@ -0,0 +1,84 @@
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 include/gnunet_plugin_lib.h
23 * @brief plugin loading and unloading
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_PLUGIN_LIB_H
28#define GNUNET_PLUGIN_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_common.h"
39#include "gnunet_configuration_lib.h"
40
41
42/**
43 * Signature of any function exported by a plugin.
44 */
45typedef void *(*GNUNET_PLUGIN_Callback) (void *arg);
46
47
48/**
49 * Setup plugin (runs the "init" callback and returns whatever "init"
50 * returned). If "init" returns NULL, the plugin is unloaded.
51 *
52 * Note that the library must export symbols called
53 * "library_name_init" and "library_name_done". These will be called
54 * when the library is loaded and unloaded respectively.
55 *
56 * @param library_name name of the plugin to load
57 * @param arg argument to the plugin initialization function
58 * @return whatever the initialization function returned
59 */
60void *GNUNET_PLUGIN_load (const char *library_name, void *arg);
61
62
63/**
64 * Unload plugin (runs the "done" callback and returns whatever "done"
65 * returned). The plugin is then unloaded.
66 *
67 * @param library_name name of the plugin to unload
68 * @param arg argument to the plugin shutdown function
69 * @return whatever the shutdown function returned
70 */
71void *GNUNET_PLUGIN_unload (const char *library_name, void *arg);
72
73
74#if 0 /* keep Emacsens' auto-indent happy */
75{
76#endif
77#ifdef __cplusplus
78}
79#endif
80
81
82/* ifndef GNUNET_PLUGIN_LIB_H */
83#endif
84/* end of gnunet_plugin_lib.h */
diff --git a/src/include/gnunet_program_lib.h b/src/include/gnunet_program_lib.h
new file mode 100644
index 000000000..fcd00b8ef
--- /dev/null
+++ b/src/include/gnunet_program_lib.h
@@ -0,0 +1,90 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_program_lib.h
23 * @brief functions related to starting programs
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_PROGRAM_LIB_H
28#define GNUNET_PROGRAM_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_configuration_lib.h"
39#include "gnunet_getopt_lib.h"
40#include "gnunet_scheduler_lib.h"
41
42/**
43 * Main function that will be run.
44 *
45 * @param cls closure
46 * @param sched the scheduler to use
47 * @param args remaining command-line arguments
48 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
49 * @param cfg configuration
50 */
51typedef void (*GNUNET_PROGRAM_Main) (void *cls,
52 struct GNUNET_SCHEDULER_Handle * sched,
53 char *const *args,
54 const char *cfgfile,
55 struct GNUNET_CONFIGURATION_Handle *
56 cfg);
57
58
59/**
60 * Run a standard GNUnet command startup sequence (initialize loggers
61 * and configuration, parse options).
62 *
63 * @param argc number of command line arguments
64 * @param argv command line arguments
65 * @param binaryName our expected name
66 * @param binaryHelp helptext for "-h" option (about the app)
67 * @param options command line options
68 * @param task main function to run
69 * @param task_cls closure for task
70 * @return GNUNET_SYSERR on error, GNUNET_OK on success
71 */
72int GNUNET_PROGRAM_run (int argc,
73 char *const *argv,
74 const char *binaryName,
75 const char *binaryHelp,
76 const struct GNUNET_GETOPT_CommandLineOption *options,
77 GNUNET_PROGRAM_Main task, void *task_cls);
78
79
80
81#if 0 /* keep Emacsens' auto-indent happy */
82{
83#endif
84#ifdef __cplusplus
85}
86#endif
87
88/* ifndef GNUNET_PROGRAM_LIB_H */
89#endif
90/* end of gnunet_program_lib.h */
diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h
new file mode 100644
index 000000000..c86ad83ce
--- /dev/null
+++ b/src/include/gnunet_protocols.h
@@ -0,0 +1,319 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_protocols.h
23 * @brief constants for network protocols
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_PROTOCOLS_H
28#define GNUNET_PROTOCOLS_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38/**
39 * Test if service is online.
40 */
41#define GNUNET_MESSAGE_TYPE_TEST 0
42
43/**
44 * Request service shutdown.
45 */
46#define GNUNET_MESSAGE_TYPE_SHUTDOWN 1
47
48
49/**
50 * Request DNS resolution.
51 */
52#define GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST 2
53
54/**
55 * Response to a DNS resolution request.
56 */
57#define GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE 3
58
59
60/**
61 * Set a statistical value.
62 */
63#define GNUNET_MESSAGE_TYPE_STATISTICS_SET 4
64
65/**
66 * Get a statistical value(s).
67 */
68#define GNUNET_MESSAGE_TYPE_STATISTICS_GET 5
69
70/**
71 * Response to a STATISTICS_GET message (with value).
72 */
73#define GNUNET_MESSAGE_TYPE_STATISTICS_VALUE 6
74
75/**
76 * Response to a STATISTICS_GET message (end of value stream).
77 */
78#define GNUNET_MESSAGE_TYPE_STATISTICS_END 7
79
80
81/**
82 * Request to ARM to start a service.
83 */
84#define GNUNET_MESSAGE_TYPE_ARM_START 8
85
86/**
87 * Request to ARM to stop a service.
88 */
89#define GNUNET_MESSAGE_TYPE_ARM_STOP 9
90
91/**
92 * Response from ARM: service is now up.
93 */
94#define GNUNET_MESSAGE_TYPE_ARM_IS_UP 10
95
96/**
97 * Response from ARM: service is now down.
98 * (failed to start it).
99 */
100#define GNUNET_MESSAGE_TYPE_ARM_IS_DOWN 11
101
102
103/**
104 * HELLO message used for communicating peer addresses.
105 * Managed by libgnunethello.
106 */
107#define GNUNET_MESSAGE_TYPE_HELLO 16
108
109/**
110 * FRAGMENT of a larger message.
111 * Managed by libgnunetfragment.
112 */
113#define GNUNET_MESSAGE_TYPE_FRAGMENT 18
114
115
116/**
117 * Message from the core saying that the transport
118 * server should start giving it messages. This
119 * should automatically trigger the transmission of
120 * a HELLO message.
121 */
122#define GNUNET_MESSAGE_TYPE_TRANSPORT_START 20
123
124/**
125 * Message from TRANSPORT notifying about a
126 * client that connected to us.
127 */
128#define GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT 21
129
130/**
131 * Message from TRANSPORT notifying about a
132 * client that disconnected from us.
133 */
134#define GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT 22
135
136/**
137 * Request to TRANSPORT to transmit a message.
138 */
139#define GNUNET_MESSAGE_TYPE_TRANSPORT_SEND 23
140
141/**
142 * Confirmation from TRANSPORT that message for
143 * transmission has been queued (and that the next
144 * message to this peer can now be passed to the
145 * service). Note that this confirmation does NOT
146 * imply that the message was fully transmitted.
147 */
148#define GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK 24
149
150/**
151 * Message from TRANSPORT notifying about a
152 * message that was received.
153 */
154#define GNUNET_MESSAGE_TYPE_TRANSPORT_RECV 25
155
156/**
157 * Message telling transport to limit its receive rate.
158 */
159#define GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA 26
160
161/**
162 * Message telling transport to try to connect to the
163 * given peer.
164 */
165#define GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT 27
166
167/**
168 * Request to other peer to confirm receipt.
169 */
170#define GNUNET_MESSAGE_TYPE_TRANSPORT_PING 28
171
172/**
173 * Message from other peer confirming receipt.
174 */
175#define GNUNET_MESSAGE_TYPE_TRANSPORT_PONG 29
176
177/**
178 * Response to another peer confirming that communication was
179 * established.
180 */
181#define GNUNET_MESSAGE_TYPE_TRANSPORT_ACK 30
182
183
184/**
185 * Request addition of a HELLO
186 */
187#define GNUNET_MESSAGE_TYPE_PEERINFO_ADD 32
188
189/**
190 * Request update and listing of a peer.
191 */
192#define GNUNET_MESSAGE_TYPE_PEERINFO_GET 33
193
194/**
195 * Request update and listing of all peers.
196 */
197#define GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL 34
198
199/**
200 * Information about one of the peers.
201 */
202#define GNUNET_MESSAGE_TYPE_PEERINFO_INFO 35
203
204/**
205 * End of information about other peers.
206 */
207#define GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END 36
208
209
210/**
211 * Welcome message between TCP transports.
212 */
213#define GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME 40
214
215/**
216 * Data message between TCP transports.
217 */
218#define GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA 41
219
220
221/**
222 * Initial setup message from core client to core.
223 */
224#define GNUNET_MESSAGE_TYPE_CORE_INIT 64
225
226/**
227 * Response from core to core client to INIT message.
228 */
229#define GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY 65
230
231/**
232 * Notify clients about new peer-to-peer connections.
233 */
234#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT 66
235
236/**
237 * Notify clients about peer disconnecting.
238 */
239#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT 67
240
241/**
242 * Notify clients about incoming P2P messages.
243 */
244#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND 68
245
246/**
247 * Notify clients about outgoing P2P transmissions.
248 */
249#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND 69
250
251/**
252 * Request from client to "configure" P2P connection.
253 */
254#define GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE 70
255
256/**
257 * Response from server about (possibly updated) P2P
258 * connection configuration.
259 */
260#define GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO 71
261
262/**
263 * Solicitation from server for transmission (may have
264 * been requested or also be transmitted without
265 * client's request).
266 */
267#define GNUNET_MESSAGE_TYPE_CORE_SOLICIT_TRAFFIC 72
268
269/**
270 * Response from client with message to transmit.
271 */
272#define GNUNET_MESSAGE_TYPE_CORE_SEND 73
273
274
275/**
276 * Session key exchange between peers.
277 */
278#define GNUNET_MESSAGE_TYPE_CORE_SET_KEY 80
279
280/**
281 * Encapsulation for an encrypted message between peers.
282 */
283#define GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE 81
284
285/**
286 * Check that other peer is alife (challenge).
287 */
288#define GNUNET_MESSAGE_TYPE_CORE_PING 82
289
290/**
291 * Confirmation that other peer is alife.
292 */
293#define GNUNET_MESSAGE_TYPE_CORE_PONG 83
294
295/**
296 * Request by the other peer to terminate the connection.
297 */
298#define GNUNET_MESSAGE_TYPE_CORE_HANGUP 84
299
300
301/*
302 TODO:
303 - DV
304 - DHT
305 - datastores
306 - applications (FS, VPN, CHAT, TRACEKIT, TBENCH)
307*/
308
309
310#if 0 /* keep Emacsens' auto-indent happy */
311{
312#endif
313#ifdef __cplusplus
314}
315#endif
316
317/* ifndef GNUNET_PROTOCOLS_H */
318#endif
319/* end of gnunet_protocols.h */
diff --git a/src/include/gnunet_pseudonym_lib.h b/src/include/gnunet_pseudonym_lib.h
new file mode 100644
index 000000000..0ee9ce7ac
--- /dev/null
+++ b/src/include/gnunet_pseudonym_lib.h
@@ -0,0 +1,125 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_pseudonym_lib.h
23 * @brief functions related to pseudonyms
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_PSEUDONYM_LIB_H
28#define GNUNET_PSEUDONYM_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_common.h"
39#include "gnunet_configuration_lib.h"
40#include "gnunet_container_lib.h"
41
42/**
43 * Iterator over all known pseudonyms.
44 *
45 * @param rating the local rating of the pseudonym
46 * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort
47 */
48typedef int (*GNUNET_PSEUDONYM_Iterator) (void *cls,
49 const GNUNET_HashCode *
50 pseudonym,
51 const struct
52 GNUNET_CONTAINER_MetaData * md,
53 int rating);
54
55/**
56 * Change the ranking of a pseudonym.
57 *
58 * @param pseudonym id of the pseudonym
59 * @param delta by how much should the rating be changed?
60 * @return new rating of the namespace
61 */
62int GNUNET_PSEUDONYM_rank (struct GNUNET_CONFIGURATION_Handle *cfg,
63 const GNUNET_HashCode * pseudonym, int delta);
64
65/**
66 * Add a pseudonym to the set of known pseudonyms.
67 *
68 * @param pseudonym the pseudonym's identifier
69 */
70void GNUNET_PSEUDONYM_add (struct GNUNET_CONFIGURATION_Handle *cfg,
71 const GNUNET_HashCode * pseudo,
72 const struct GNUNET_CONTAINER_MetaData *meta);
73
74
75/**
76 * List all known pseudonyms.
77 */
78int GNUNET_PSEUDONYM_list_all (struct GNUNET_CONFIGURATION_Handle *cfg,
79 GNUNET_PSEUDONYM_Iterator iterator,
80 void *closure);
81
82/**
83 * Register callback to be invoked whenever we discover
84 * a new pseudonym.
85 */
86int GNUNET_PSEUDONYM_discovery_callback_register (struct
87 GNUNET_CONFIGURATION_Handle
88 *cfg,
89 GNUNET_PSEUDONYM_Iterator
90 iterator, void *closure);
91
92/**
93 * Unregister namespace discovery callback.
94 */
95int
96GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
97 iterator, void *closure);
98
99/**
100 * Return the unique, human readable name for the given pseudonym.
101 *
102 * @return NULL on failure (should never happen)
103 */
104char *GNUNET_PSEUDONYM_id_to_name (struct GNUNET_CONFIGURATION_Handle *cfg,
105 const GNUNET_HashCode * pseudo);
106
107/**
108 * Get the pseudonym ID belonging to the given human readable name.
109 *
110 * @return GNUNET_OK on success
111 */
112int GNUNET_PSEUDONYM_name_to_id (struct GNUNET_CONFIGURATION_Handle *cfg,
113 const char *hname, GNUNET_HashCode * psid);
114
115
116#if 0 /* keep Emacsens' auto-indent happy */
117{
118#endif
119#ifdef __cplusplus
120}
121#endif
122
123/* ifndef GNUNET_PSEUDONYM_LIB_H */
124#endif
125/* end of gnunet_pseudonym_lib.h */
diff --git a/src/include/gnunet_resolver_service.h b/src/include/gnunet_resolver_service.h
new file mode 100644
index 000000000..ac7aebcc7
--- /dev/null
+++ b/src/include/gnunet_resolver_service.h
@@ -0,0 +1,135 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_resolver_service.h
23 * @brief functions related to doing DNS lookups
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_RESOLVER_SERVICE_H
28#define GNUNET_RESOLVER_SERVICE_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_configuration_lib.h"
39#include "gnunet_scheduler_lib.h"
40#include "gnunet_time_lib.h"
41
42
43/**
44 * Function called by the resolver for each address obtained from DNS.
45 *
46 * @param cls closure
47 * @param addr one of the addresses of the host, NULL for the last address
48 * @param addrlen length of the address
49 */
50typedef void (*GNUNET_RESOLVER_AddressCallback) (void *cls,
51 const struct sockaddr * addr,
52 socklen_t addrlen);
53
54
55/**
56 * Convert a string to one or more IP addresses.
57 *
58 * @param sched scheduler to use
59 * @param cfg configuration to use
60 * @param hostname the hostname to resolve
61 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
62 * @param callback function to call with addresses
63 * @param cls closure for callback
64 * @param timeout how long to try resolving
65 */
66void
67GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
68 struct GNUNET_CONFIGURATION_Handle *cfg,
69 const char *hostname,
70 int domain,
71 struct GNUNET_TIME_Relative timeout,
72 GNUNET_RESOLVER_AddressCallback callback, void *cls);
73
74
75/**
76 * Resolve our hostname to an IP address.
77 *
78 * @param sched scheduler to use
79 * @param cfg configuration to use
80 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
81 * @param callback function to call with addresses
82 * @param cls closure for callback
83 * @param timeout how long to try resolving
84 */
85void
86GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
87 struct GNUNET_CONFIGURATION_Handle *cfg,
88 int domain,
89 struct GNUNET_TIME_Relative timeout,
90 GNUNET_RESOLVER_AddressCallback callback,
91 void *cls);
92
93
94/**
95 * Function called by the resolver for each hostname obtained from DNS.
96 *
97 * @param cls closure
98 * @param hostname one of the names for the host, NULL
99 * on the last call to the callback
100 */
101typedef void (*GNUNET_RESOLVER_HostnameCallback) (void *cls,
102 const char *hostname);
103
104
105/**
106 * Get an IP address as a string.
107 *
108 * @param sched scheduler to use
109 * @param cfg configuration to use
110 * @param sa host address
111 * @param salen length of host address
112 * @param do_resolve use GNUNET_NO to return numeric hostname
113 * @param timeout how long to try resolving
114 * @param callback function to call with hostnames
115 * @param cls closure for callback
116 */
117void GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
118 struct GNUNET_CONFIGURATION_Handle *cfg,
119 const struct sockaddr *sa,
120 socklen_t salen,
121 int do_resolve,
122 struct GNUNET_TIME_Relative timeout,
123 GNUNET_RESOLVER_HostnameCallback callback,
124 void *cls);
125
126#if 0 /* keep Emacsens' auto-indent happy */
127{
128#endif
129#ifdef __cplusplus
130}
131#endif
132
133/* ifndef GNUNET_RESOLVER_SERVICE_H */
134#endif
135/* end of gnunet_resolver_service.h */
diff --git a/src/include/gnunet_scheduler_lib.h b/src/include/gnunet_scheduler_lib.h
new file mode 100644
index 000000000..aa5830942
--- /dev/null
+++ b/src/include/gnunet_scheduler_lib.h
@@ -0,0 +1,442 @@
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 include/gnunet_scheduler_lib.h
23 * @brief API to schedule computations using continuation passing style
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_SCHEDULER_LIB_H
28#define GNUNET_SCHEDULER_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_time_lib.h"
39
40
41/**
42 * Opaque handle for the scheduling service.
43 */
44struct GNUNET_SCHEDULER_Handle;
45
46
47/**
48 * Opaque reference to a task.
49 */
50typedef unsigned long long GNUNET_SCHEDULER_TaskIdentifier;
51
52
53/**
54 * Constant used to indicate that the scheduled
55 * task has no others as prerequisites.
56 */
57#define GNUNET_SCHEDULER_NO_PREREQUISITE_TASK ((GNUNET_SCHEDULER_TaskIdentifier) 0)
58
59/**
60 * Reasons why the schedule may have triggered
61 * the task now.
62 */
63enum GNUNET_SCHEDULER_Reason
64{
65 /**
66 * This is the very first task run during startup.
67 */
68 GNUNET_SCHEDULER_REASON_STARTUP = 0,
69
70 /**
71 * We are shutting down and are running all shutdown-related tasks
72 * (regardless of timeout, etc.).
73 */
74 GNUNET_SCHEDULER_REASON_SHUTDOWN = 1,
75
76 /**
77 * The specified timeout has expired.
78 * (also set if the delay given was 0).
79 */
80 GNUNET_SCHEDULER_REASON_TIMEOUT = 2,
81
82 /**
83 * The reading socket is ready.
84 */
85 GNUNET_SCHEDULER_REASON_READ_READY = 4,
86
87 /**
88 * The writing socket is ready.
89 */
90 GNUNET_SCHEDULER_REASON_WRITE_READY = 8,
91
92 /**
93 * The prerequisite task is done.
94 */
95 GNUNET_SCHEDULER_REASON_PREREQ_DONE = 16
96};
97
98
99/**
100 * Valid task priorities. Use these, do not
101 * pass random integers!
102 */
103enum GNUNET_SCHEDULER_Priority
104{
105 /**
106 * Run with the same priority as the current job.
107 */
108 GNUNET_SCHEDULER_PRIORITY_KEEP = 0,
109
110 /**
111 * Run when otherwise idle.
112 */
113 GNUNET_SCHEDULER_PRIORITY_IDLE = 1,
114
115 /**
116 * Run as background job (higher than idle,
117 * lower than default).
118 */
119 GNUNET_SCHEDULER_PRIORITY_BACKGROUND = 2,
120
121 /**
122 * Run with the default priority (normal
123 * P2P operations). Higher than BACKGROUND.
124 */
125 GNUNET_SCHEDULER_PRIORITY_DEFAULT = 3,
126
127 /**
128 * Run with high priority (important requests).
129 * Higher than DEFAULT.
130 */
131 GNUNET_SCHEDULER_PRIORITY_HIGH = 4,
132
133 /**
134 * Run with priority for interactive tasks.
135 * Higher than "HIGH".
136 */
137 GNUNET_SCHEDULER_PRIORITY_UI = 5,
138
139 /**
140 * Run with priority for urgent tasks. Use
141 * for things like aborts and shutdowns that
142 * need to preempt "UI"-level tasks.
143 * Higher than "UI".
144 */
145 GNUNET_SCHEDULER_PRIORITY_URGENT = 6,
146
147 /**
148 * Number of priorities (must be the last priority).
149 * This priority must not be used by clients.
150 */
151 GNUNET_SCHEDULER_PRIORITY_COUNT = 7
152};
153
154
155/**
156 * Context information passed to each scheduler task.
157 */
158struct GNUNET_SCHEDULER_TaskContext
159{
160
161 /**
162 * Scheduler running the task
163 */
164 struct GNUNET_SCHEDULER_Handle *sched;
165
166 /**
167 * Reason why the task is run now
168 */
169 enum GNUNET_SCHEDULER_Reason reason;
170
171 /**
172 * Set of file descriptors ready for reading;
173 * note that additional bits may be set
174 * that were not in the original request
175 */
176 const fd_set *read_ready;
177
178 /**
179 * Set of file descriptors ready for writing;
180 * note that additional bits may be set
181 * that were not in the original request.
182 */
183 const fd_set *write_ready;
184
185};
186
187
188/**
189 * Signature of the main function of a task.
190 *
191 * @param cls closure
192 * @param tc context information (why was this task triggered now)
193 */
194typedef void (*GNUNET_SCHEDULER_Task) (void *cls,
195 const struct
196 GNUNET_SCHEDULER_TaskContext * tc);
197
198
199/**
200 * Initialize and run scheduler. This function will return when
201 * either a shutdown was initiated (via signal) and all tasks marked
202 * to "run_on_shutdown" have been completed or when all tasks in
203 * general have been completed.
204 *
205 * @param task task to run immediately
206 * @param cls closure of task
207 */
208void GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls);
209
210
211/**
212 * Request the shutdown of a scheduler. This function can be used to
213 * stop a scheduler, for example from within the signal
214 * handler for signals causing shutdowns.
215 */
216void GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched);
217
218
219/**
220 * Get information about the current load of this scheduler. Use this
221 * function to determine if an elective task should be added or simply
222 * dropped (if the decision should be made based on the number of
223 * tasks ready to run).
224 *
225 * @param sched scheduler to query
226 * @param p priority-level to query, use KEEP to query the level
227 * of the current task, use COUNT to get the sum over
228 * all priority levels
229 * @return number of tasks pending right now
230 */
231unsigned int GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
232 enum GNUNET_SCHEDULER_Priority p);
233
234
235/**
236 * Cancel the task with the specified identifier.
237 * The task must not yet have run.
238 *
239 * @param sched scheduler to use
240 * @param task id of the task to cancel
241 * @return the closure of the callback of the cancelled task
242 */
243void *GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
244 GNUNET_SCHEDULER_TaskIdentifier task);
245
246
247/**
248 * Continue the current execution with the given function. This is
249 * similar to the other "add" functions except that there is no delay
250 * and the reason code can be specified.
251 *
252 * @param sched scheduler to use
253 * @param main main function of the task
254 * @param cls closure of task
255 * @param reason reason for task invocation
256 */
257void
258GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
259 int run_on_shutdown,
260 GNUNET_SCHEDULER_Task main,
261 void *cls,
262 enum GNUNET_SCHEDULER_Reason reason);
263
264
265/**
266 * Schedule a new task to be run after the specified
267 * prerequisite task has completed.
268 *
269 * @param sched scheduler to use
270 * @param run_on_shutdown run on shutdown?
271 * @param prio how important is this task?
272 * @param prerequisite_task run this task after the task with the given
273 * task identifier completes (and any of our other
274 * conditions, such as delay, read or write-readyness
275 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
276 * on completion of other tasks.
277 * @param main main function of the task
278 * @param cls closure of task
279 * @return unique task identifier for the job
280 * only valid until "main" is started!
281 */
282GNUNET_SCHEDULER_TaskIdentifier
283GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
284 int run_on_shutdown,
285 enum GNUNET_SCHEDULER_Priority prio,
286 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
287 GNUNET_SCHEDULER_Task main, void *cls);
288
289
290/**
291 * Schedule a new task to be run with a specified delay. The task
292 * will be scheduled for execution once the delay has expired and the
293 * prerequisite task has completed.
294 *
295 * @param sched scheduler to use
296 * @param run_on_shutdown run on shutdown? You can use this
297 * argument to run a function only during shutdown
298 * by setting delay to -1. Set this
299 * argument to GNUNET_NO to skip this task if
300 * the user requested process termination.
301 * @param prio how important is this task?
302 * @param prerequisite_task run this task after the task with the given
303 * task identifier completes (and any of our other
304 * conditions, such as delay, read or write-readyness
305 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
306 * on completion of other tasks.
307 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
308 * @param main main function of the task
309 * @param cls closure of task
310 * @return unique task identifier for the job
311 * only valid until "main" is started!
312 */
313GNUNET_SCHEDULER_TaskIdentifier
314GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle *sched,
315 int run_on_shutdown,
316 enum GNUNET_SCHEDULER_Priority prio,
317 GNUNET_SCHEDULER_TaskIdentifier
318 prerequisite_task,
319 struct GNUNET_TIME_Relative delay,
320 GNUNET_SCHEDULER_Task main, void *cls);
321
322
323/**
324 * Schedule a new task to be run with a specified delay or when the
325 * specified file descriptor is ready for reading. The delay can be
326 * used as a timeout on the socket being ready. The task will be
327 * scheduled for execution once either the delay has expired or the
328 * socket operation is ready.
329 *
330 * @param sched scheduler to use
331 * @param run_on_shutdown run on shutdown? Set this
332 * argument to GNUNET_NO to skip this task if
333 * the user requested process termination.
334 * @param prio how important is this task?
335 * @param prerequisite_task run this task after the task with the given
336 * task identifier completes (and any of our other
337 * conditions, such as delay, read or write-readyness
338 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
339 * on completion of other tasks.
340 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
341 * @param rfd read file-descriptor
342 * @param main main function of the task
343 * @param cls closure of task
344 * @return unique task identifier for the job
345 * only valid until "main" is started!
346 */
347GNUNET_SCHEDULER_TaskIdentifier
348GNUNET_SCHEDULER_add_read (struct GNUNET_SCHEDULER_Handle *sched,
349 int run_on_shutdown,
350 enum GNUNET_SCHEDULER_Priority prio,
351 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
352 struct GNUNET_TIME_Relative delay,
353 int rfd, GNUNET_SCHEDULER_Task main, void *cls);
354
355
356/**
357 * Schedule a new task to be run with a specified delay or when the
358 * specified file descriptor is ready for writing. The delay can be
359 * used as a timeout on the socket being ready. The task will be
360 * scheduled for execution once either the delay has expired or the
361 * socket operation is ready.
362 *
363 * @param sched scheduler to use
364 * @param run_on_shutdown run on shutdown? Set this
365 * argument to GNUNET_NO to skip this task if
366 * the user requested process termination.
367 * @param prio how important is this task?
368 * @param prerequisite_task run this task after the task with the given
369 * task identifier completes (and any of our other
370 * conditions, such as delay, read or write-readyness
371 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
372 * on completion of other tasks.
373 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
374 * @param wfd write file-descriptor
375 * @param main main function of the task
376 * @param cls closure of task
377 * @return unique task identifier for the job
378 * only valid until "main" is started!
379 */
380GNUNET_SCHEDULER_TaskIdentifier
381GNUNET_SCHEDULER_add_write (struct GNUNET_SCHEDULER_Handle *sched,
382 int run_on_shutdown,
383 enum GNUNET_SCHEDULER_Priority prio,
384 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
385 struct GNUNET_TIME_Relative delay,
386 int wfd, GNUNET_SCHEDULER_Task main, void *cls);
387
388
389/**
390 * Schedule a new task to be run with a specified delay or when any of
391 * the specified file descriptor sets is ready. The delay can be used
392 * as a timeout on the socket(s) being ready. The task will be
393 * scheduled for execution once either the delay has expired or any of
394 * the socket operations is ready. This is the most general
395 * function of the "add" family. Note that the "prerequisite_task"
396 * must be satisfied in addition to any of the other conditions. In
397 * other words, the task will be started when
398 * <code>
399 * (prerequisite-run)
400 * && (delay-ready
401 * || any-rs-ready
402 * || any-ws-ready
403 * || (shutdown-active && run-on-shutdown) )
404 * </code>
405 *
406 * @param sched scheduler to use
407 * @param run_on_shutdown run on shutdown? Set this
408 * argument to GNUNET_NO to skip this task if
409 * the user requested process termination.
410 * @param prio how important is this task?
411 * @param prerequisite_task run this task after the task with the given
412 * task identifier completes (and any of our other
413 * conditions, such as delay, read or write-readyness
414 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
415 * on completion of other tasks.
416 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
417 * @param nfds highest-numbered file descriptor in any of the two sets plus one
418 * @param rs set of file descriptors we want to read (can be NULL)
419 * @param ws set of file descriptors we want to write (can be NULL)
420 * @param main main function of the task
421 * @param cls closure of task
422 * @return unique task identifier for the job
423 * only valid until "main" is started!
424 */
425GNUNET_SCHEDULER_TaskIdentifier
426GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle *sched,
427 int run_on_shutdown,
428 enum GNUNET_SCHEDULER_Priority prio,
429 GNUNET_SCHEDULER_TaskIdentifier
430 prerequisite_task,
431 struct GNUNET_TIME_Relative delay,
432 int nfds, const fd_set * rs, const fd_set * ws,
433 GNUNET_SCHEDULER_Task main, void *cls);
434
435#if 0 /* keep Emacsens' auto-indent happy */
436{
437#endif
438#ifdef __cplusplus
439}
440#endif
441
442#endif
diff --git a/src/include/gnunet_server_lib.h b/src/include/gnunet_server_lib.h
new file mode 100644
index 000000000..4e4f35e26
--- /dev/null
+++ b/src/include/gnunet_server_lib.h
@@ -0,0 +1,498 @@
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 include/gnunet_server_lib.h
23 * @brief library for building GNUnet network servers
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_SERVER_LIB_H
29#define GNUNET_SERVER_LIB_H
30
31#ifdef __cplusplus
32extern "C"
33{
34#if 0 /* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39#include "gnunet_common.h"
40#include "gnunet_network_lib.h"
41#include "gnunet_scheduler_lib.h"
42
43
44/**
45 * Largest supported message.
46 */
47#define GNUNET_SERVER_MAX_MESSAGE_SIZE 65536
48
49
50/**
51 * @brief handle for a server
52 */
53struct GNUNET_SERVER_Handle;
54
55
56/**
57 * @brief opaque handle for a client of the server
58 */
59struct GNUNET_SERVER_Client;
60
61
62/**
63 * Functions with this signature are called whenever a message is
64 * received.
65 *
66 * @param cls closure
67 * @param server the server handling the message
68 * @param client identification of the client
69 * @param message the actual message
70 */
71typedef void (*GNUNET_SERVER_MessageCallback) (void *cls,
72 struct GNUNET_SERVER_Handle *
73 server,
74 struct GNUNET_SERVER_Client *
75 client,
76 const struct
77 GNUNET_MessageHeader *
78 message);
79
80
81
82/**
83 * Message handler. Each struct specifies how to handle on particular
84 * type of message received.
85 */
86struct GNUNET_SERVER_MessageHandler
87{
88 /**
89 * Function to call for messages of "type".
90 */
91 GNUNET_SERVER_MessageCallback callback;
92
93 /**
94 * Closure argument for "callback".
95 */
96 void *callback_cls;
97
98 /**
99 * Type of the message this handler covers.
100 */
101 uint16_t type;
102
103 /**
104 * Expected size of messages of this type. Use 0 for
105 * variable-size. If non-zero, messages of the given
106 * type will be discarded (and the connection closed)
107 * if they do not have the right size.
108 */
109 uint16_t expected_size;
110
111};
112
113
114/**
115 * Create a new server.
116 *
117 * @param sched scheduler to use
118 * @param access function for access control
119 * @param access_cls closure for access
120 * @param serverAddr address to listen on (including port), use NULL
121 * for internal server (no listening)
122 * @param socklen length of serverAddr
123 * @param maxbuf maximum write buffer size for accepted sockets
124 * @param idle_timeout after how long should we timeout idle connections?
125 * @param require_found if YES, connections sending messages of unknown type
126 * will be closed
127 * @return handle for the new server, NULL on error
128 * (typically, "port" already in use)
129 */
130struct GNUNET_SERVER_Handle *GNUNET_SERVER_create (struct
131 GNUNET_SCHEDULER_Handle
132 *sched,
133 GNUNET_NETWORK_AccessCheck
134 access, void *access_cls,
135 const struct sockaddr
136 *serverAddr,
137 socklen_t socklen,
138 size_t maxbuf,
139 struct GNUNET_TIME_Relative
140 idle_timeout,
141 int require_found);
142
143
144/**
145 * Free resources held by this server.
146 */
147void GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s);
148
149
150/**
151 * Add additional handlers to an existing server.
152 *
153 * @param server the server to add handlers to
154 * @param handlers array of message handlers for
155 * incoming messages; the last entry must
156 * have "NULL" for the "callback"; multiple
157 * entries for the same type are allowed,
158 * they will be called in order of occurence.
159 * These handlers can be removed later;
160 * the handlers array must exist until removed
161 * (or server is destroyed).
162 */
163void
164GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server,
165 const struct GNUNET_SERVER_MessageHandler
166 *handlers);
167
168
169/**
170 * Notify us when the server has enough space to transmit
171 * a message of the given size to the given client.
172 *
173 * @param client client to transmit message to
174 * @param size requested amount of buffer space
175 * @param timeout after how long should we give up (and call
176 * notify with buf NULL and size 0)?
177 * @param callback function to call when space is available
178 * @param callback_cls closure for callback
179 * @return non-NULL if the notify callback was queued; can be used
180 * to cancel the request using
181 * GNUNET_NETWORK_notify_transmit_ready_cancel.
182 * NULL if we are already going to notify someone else (busy)
183 */
184struct GNUNET_NETWORK_TransmitHandle
185 *GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
186 size_t size,
187 struct GNUNET_TIME_Relative timeout,
188 GNUNET_NETWORK_TransmitReadyNotify
189 callback, void *callback_cls);
190
191
192/**
193 * Resume receiving from this client, we are done processing the
194 * current request. This function must be called from within each
195 * GNUNET_SERVER_MessageCallback (or its respective continuations).
196 *
197 * @param client client we were processing a message of
198 * @param success GNUNET_OK to keep the connection open and
199 * continue to receive
200 * GNUNET_SYSERR to close the connection (signal
201 * serious error)
202 */
203void
204GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success);
205
206
207/**
208 * Inject a message into the server, pretend it came
209 * from the specified client. Delivery of the message
210 * will happen instantly (if a handler is installed;
211 * otherwise the call does nothing).
212 *
213 * @param server the server receiving the message
214 * @param sender the "pretended" sender of the message
215 * can be NULL!
216 * @param message message to transmit
217 * @return GNUNET_OK if the message was OK and the
218 * connection can stay open
219 * GNUNET_SYSERR if the connection to the
220 * client should be shut down
221 */
222int
223GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
224 struct GNUNET_SERVER_Client *sender,
225 const struct GNUNET_MessageHeader *message);
226
227
228/**
229 * Add a TCP socket-based connection to the set of handles managed by
230 * this server. Use this function for outgoing (P2P) connections that
231 * we initiated (and where this server should process incoming
232 * messages).
233 *
234 * @param server the server to use
235 * @param connection the connection to manage (client must
236 * stop using this connection from now on)
237 * @return the client handle (client should call
238 * "client_drop" on the return value eventually)
239 */
240struct GNUNET_SERVER_Client *GNUNET_SERVER_connect_socket (struct
241 GNUNET_SERVER_Handle
242 *server,
243 struct
244 GNUNET_NETWORK_SocketHandle
245 *connection);
246
247
248/**
249 * Receive data from the given connection. This function should call
250 * "receiver" asynchronously using the scheduler. It must return
251 * "immediately".
252 *
253 * @param cls closure
254 * @param sched scheduler to use
255 * @param max maximum number of bytes to read
256 * @param timeout maximum amount of time to wait (use -1 for "forever")
257 * @param receiver function to call with received data
258 * @param receiver_cls closure for receiver
259 * @return task identifier that can be used to cancel the receive,
260 * GNUNET_SCHEDULER_NO_PREREQUISITE_TASK should be returned
261 * if the receiver function was already called
262 */
263typedef GNUNET_SCHEDULER_TaskIdentifier
264 (*GNUNET_SERVER_ReceiveCallback) (void *cls,
265 size_t max,
266 struct GNUNET_TIME_Relative timeout,
267 GNUNET_NETWORK_Receiver
268 receiver, void *receiver_cls);
269
270
271/**
272 * Cancel receive request.
273 *
274 * @param cls closure
275 * @param ti task identifier from the receive callback
276 */
277typedef void (*GNUNET_SERVER_ReceiveCancelCallback) (void *cls,
278 GNUNET_SCHEDULER_TaskIdentifier
279 ti);
280
281
282/**
283 * Notify us when the connection is ready to transmit size bytes.
284 *
285 * @param cls closure
286 * @param size number of bytes to be ready for sending
287 * @param timeout after how long should we give up (and call
288 * notify with buf NULL and size 0)?
289 * @param notify function to call
290 * @param notify_cls closure for notify
291 * @return a handle that can be used to cancel
292 * the transmission request or NULL if
293 * queueing a transmission request failed
294 */
295typedef void *(*GNUNET_SERVER_TransmitReadyCallback) (void *cls,
296 size_t size,
297 struct
298 GNUNET_TIME_Relative
299 timeout,
300 GNUNET_NETWORK_TransmitReadyNotify
301 notify,
302 void *notify_cls);
303
304
305/**
306 * Cancel an earlier transmit notification request.
307 *
308 * @param cls closure
309 * @param ctx handle that was returned by the TransmitReadyCallback
310 */
311typedef void (*GNUNET_SERVER_TransmitReadyCancelCallback) (void *cls,
312 void *ctx);
313
314
315/**
316 * Check if connection is still valid (no fatal errors have happened so far).
317 *
318 * @param cls closure
319 * @return GNUNET_YES if valid, GNUNET_NO otherwise
320 */
321typedef int (*GNUNET_SERVER_CheckCallback) (void *cls);
322
323
324/**
325 * Destroy this connection (free resources).
326 *
327 * @param cls closure
328 */
329typedef void (*GNUNET_SERVER_DestroyCallback) (void *cls);
330
331
332/**
333 * Add an arbitrary connection to the set of handles managed by this
334 * server. This can be used if a sending and receiving does not
335 * really go over the network (internal transmission) or for servers
336 * using UDP.
337 *
338 * @param server the server to use
339 * @param chandle opaque handle for the connection
340 * @param creceive receive function for the connection
341 * @param creceive_cancel cancel receive function for the connection
342 * @param cnotify transmit notification function for the connection
343 * @param cnotify_cancel transmit notification cancellation function for the connection
344 * @param ccheck function to test if the connection is still up
345 * @param cdestroy function to close and free the connection
346 * @return the client handle (client should call
347 * "client_drop" on the return value eventually)
348 */
349struct GNUNET_SERVER_Client *GNUNET_SERVER_connect_callback (struct
350 GNUNET_SERVER_Handle
351 *server,
352 void *chandle,
353 GNUNET_SERVER_ReceiveCallback
354 creceive,
355 GNUNET_SERVER_ReceiveCancelCallback
356 ccancel,
357 GNUNET_SERVER_TransmitReadyCallback
358 cnotify,
359 GNUNET_SERVER_TransmitReadyCancelCallback
360 cnotify_cancel,
361 GNUNET_SERVER_CheckCallback
362 ccheck,
363 GNUNET_SERVER_DestroyCallback
364 cdestroy);
365
366
367/**
368 * Notify the server that the given client handle should
369 * be kept (keeps the connection up if possible, increments
370 * the internal reference counter).
371 *
372 * @param client the client to keep
373 */
374void GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client);
375
376
377/**
378 * Notify the server that the given client handle is no
379 * longer required. Decrements the reference counter. If
380 * that counter reaches zero an inactive connection maybe
381 * closed.
382 *
383 * @param client the client to drop
384 */
385void GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client);
386
387
388/**
389 * Obtain the network address of the other party.
390 *
391 * @param client the client to get the address for
392 * @param addr where to store the address
393 * @param addrlen where to store the length of the address
394 * @return GNUNET_OK on success
395 */
396int GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client,
397 void **addr, size_t * addrlen);
398
399
400/**
401 * Functions with this signature are called whenever a client
402 * is disconnected on the network level.
403 *
404 * @param cls closure
405 * @param client identification of the client
406 */
407typedef void (*GNUNET_SERVER_DisconnectCallback) (void *cls,
408 struct GNUNET_SERVER_Client
409 * client);
410
411
412/**
413 * Ask the server to notify us whenever a client disconnects.
414 * This function is called whenever the actual network connection
415 * is closed; the reference count may be zero or larger than zero
416 * at this point.
417 *
418 * @param server the server manageing the clients
419 * @param callback function to call on disconnect
420 * @param callback_cls closure for callback
421 */
422void GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server,
423 GNUNET_SERVER_DisconnectCallback
424 callback, void *callback_cls);
425
426
427/**
428 * Ask the server to disconnect from the given client.
429 * This is the same as returning GNUNET_SYSERR from a message
430 * handler, except that it allows dropping of a client even
431 * when not handling a message from that client.
432 *
433 * @param client the client to disconnect from
434 */
435void GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client);
436
437
438/**
439 * The tansmit context is the key datastructure for a conveniance API
440 * used for transmission of complex results to the client followed
441 * ONLY by signaling receive_done with success or error
442 */
443struct GNUNET_SERVER_TransmitContext;
444
445
446/**
447 * Create a new transmission context for the
448 * given client.
449 *
450 * @param client client to create the context for.
451 * @return NULL on error
452 */
453struct GNUNET_SERVER_TransmitContext
454 *GNUNET_SERVER_transmit_context_create (struct GNUNET_SERVER_Client
455 *client);
456
457
458/**
459 * Append a message to the transmission context.
460 * All messages in the context will be sent by
461 * the transmit_context_run method.
462 *
463 * @param tc context to use
464 * @param data what to append to the result message
465 * @param length length of data
466 * @param type type of the message
467 */
468void
469GNUNET_SERVER_transmit_context_append (struct GNUNET_SERVER_TransmitContext
470 *tc, const void *data, size_t length,
471 uint16_t type);
472
473/**
474 * Execute a transmission context. If there is
475 * an error in the transmission, the receive_done
476 * method will be called with an error code (GNUNET_SYSERR),
477 * otherwise with GNUNET_OK.
478 *
479 * @param tc transmission context to use
480 * @param timeout when to time out and abort the transmission
481 */
482void
483GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc,
484 struct GNUNET_TIME_Relative timeout);
485
486
487
488#if 0 /* keep Emacsens' auto-indent happy */
489{
490#endif
491#ifdef __cplusplus
492}
493#endif
494
495
496/* ifndef GNUNET_SERVER_LIB_H */
497#endif
498/* end of gnunet_server_lib.h */
diff --git a/src/include/gnunet_service_lib.h b/src/include/gnunet_service_lib.h
new file mode 100644
index 000000000..dbfb2588b
--- /dev/null
+++ b/src/include/gnunet_service_lib.h
@@ -0,0 +1,140 @@
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 include/gnunet_service_lib.h
23 * @brief functions related to starting services
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_SERVICE_LIB_H
28#define GNUNET_SERVICE_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_configuration_lib.h"
39#include "gnunet_server_lib.h"
40
41/**
42 * Function called by the service's run
43 * method to run service-specific setup code.
44 *
45 * @param cls closure
46 * @param sched scheduler to use
47 * @param server the initialized server
48 * @param cfg configuration to use
49 */
50typedef void (*GNUNET_SERVICE_Main) (void *cls,
51 struct GNUNET_SCHEDULER_Handle * sched,
52 struct GNUNET_SERVER_Handle * server,
53 struct GNUNET_CONFIGURATION_Handle *
54 cfg);
55
56
57/**
58 * Function called when the service shuts
59 * down to run service-specific teardown code.
60 *
61 * @param cls closure
62 * @param cfg configuration to use
63 */
64typedef void (*GNUNET_SERVICE_Term) (void *cls,
65 struct GNUNET_CONFIGURATION_Handle *
66 cfg);
67
68
69/**
70 * Run a standard GNUnet service startup sequence (initialize loggers
71 * and configuration, parse options).
72 *
73 * @param argc number of command line arguments
74 * @param argv command line arguments
75 * @param serviceName our service name
76 * @param task main task of the service
77 * @param task_cls closure for task
78 * @param term termination task of the service
79 * @param term_cls closure for term
80 * @return GNUNET_SYSERR on error, GNUNET_OK
81 * if we shutdown nicely
82 */
83int GNUNET_SERVICE_run (int argc,
84 char *const *argv,
85 const char *serviceName,
86 GNUNET_SERVICE_Main task,
87 void *task_cls,
88 GNUNET_SERVICE_Term term, void *term_cls);
89
90
91struct GNUNET_SERVICE_Context;
92
93/**
94 * Run a service startup sequence within an existing
95 * initialized system.
96 *
97 * @param serviceName our service name
98 * @param sched scheduler to use
99 * @param cfg configuration to use
100 * @return NULL on error, service handle
101 */
102struct GNUNET_SERVICE_Context *GNUNET_SERVICE_start (const char *serviceName,
103 struct
104 GNUNET_SCHEDULER_Handle
105 *sched,
106 struct
107 GNUNET_CONFIGURATION_Handle
108 *cfg);
109
110
111/**
112 * Obtain the server used by a service. Note that the server must NOT
113 * be destroyed by the caller.
114 *
115 * @param ctx the service context returned from the start function
116 * @return handle to the server for this service, NULL if there is none
117 */
118struct GNUNET_SERVER_Handle *GNUNET_SERVICE_get_server (struct
119 GNUNET_SERVICE_Context
120 *ctx);
121
122
123/**
124 * Stop a service that was started with "GNUNET_SERVICE_start".
125 *
126 * @param ctx the service context returned from the start function
127 */
128void GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *ctx);
129
130
131#if 0 /* keep Emacsens' auto-indent happy */
132{
133#endif
134#ifdef __cplusplus
135}
136#endif
137
138/* ifndef GNUNET_SERVICE_LIB_H */
139#endif
140/* end of gnunet_service_lib.h */
diff --git a/src/include/gnunet_signal_lib.h b/src/include/gnunet_signal_lib.h
new file mode 100644
index 000000000..af1ec0de4
--- /dev/null
+++ b/src/include/gnunet_signal_lib.h
@@ -0,0 +1,73 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_signal_lib.h
23 * @brief functions related to signals
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_SIGNAL_LIB_H
28#define GNUNET_SIGNAL_LIB_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38struct GNUNET_SIGNAL_Context;
39
40/**
41 * A signal handler. Since different OSes have different signatures
42 * for their handlers, the API only gives the most restrictive
43 * signature -- no arguments, no return value. Note that this will
44 * work even if the OS expects a function with arguments. However,
45 * the implementation must guarantee that this handler is not called
46 * for signals other than the one that it has been registered for.
47 */
48typedef void (*GNUNET_SIGNAL_Handler) (void);
49
50/**
51 * Install a signal handler that will be run if the
52 * given signal is received.
53 */
54struct GNUNET_SIGNAL_Context *GNUNET_SIGNAL_handler_install (int signal,
55 GNUNET_SIGNAL_Handler
56 handler);
57
58/**
59 * Uninstall a previously installed signal hander.
60 */
61void GNUNET_SIGNAL_handler_uninstall (struct GNUNET_SIGNAL_Context *ctx);
62
63
64#if 0 /* keep Emacsens' auto-indent happy */
65{
66#endif
67#ifdef __cplusplus
68}
69#endif
70
71/* ifndef GNUNET_SIGNAL_LIB_H */
72#endif
73/* end of gnunet_signal_lib.h */
diff --git a/src/include/gnunet_signatures.h b/src/include/gnunet_signatures.h
new file mode 100644
index 000000000..adb00c1ac
--- /dev/null
+++ b/src/include/gnunet_signatures.h
@@ -0,0 +1,77 @@
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 include/gnunet_signatures.h
23 * @brief constants for network signatures
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_SIGNATURES_H
28#define GNUNET_SIGNATURES_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38/**
39 * Test signature, not valid for anything other than writing
40 * a test. (Note that the signature verification code will
41 * accept this value).
42 */
43#define GNUNET_SIGNATURE_PURPOSE_TEST 0
44
45/**
46 * Signature for confirming HELLOs.
47 */
48#define GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO 1
49
50
51/**
52 * Purpose is to set a session key.
53 */
54#define GNUNET_SIGNATURE_PURPOSE_SET_KEY 2
55
56/**
57 * Signature for a namespace/pseudonym advertisement (by
58 * the namespace owner).
59 */
60#define GNUNET_SIGNATURE_PURPOSE_NAMESPACE_ADVERTISEMENT 3
61
62/**
63 *
64 */
65#define GNUNET_SIGNATURE_PURPOSE_RESOLVER_RESPONSE 3
66
67
68#if 0 /* keep Emacsens' auto-indent happy */
69{
70#endif
71#ifdef __cplusplus
72}
73#endif
74
75/* ifndef GNUNET_SIGNATURES_H */
76#endif
77/* end of gnunet_signatures.h */
diff --git a/src/include/gnunet_statistics_service.h b/src/include/gnunet_statistics_service.h
new file mode 100644
index 000000000..8dd11094c
--- /dev/null
+++ b/src/include/gnunet_statistics_service.h
@@ -0,0 +1,157 @@
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 include/gnunet_statistics_service.h
23 * @brief API to create, modify and access statistics about
24 * the operation of GNUnet; all statistical values
25 * must be of type "unsigned long long".
26 * @author Christian Grothoff
27 */
28
29#ifndef GNUNET_STATISTICS_SERVICE_H
30#define GNUNET_STATISTICS_SERVICE_H
31
32#ifdef __cplusplus
33extern "C"
34{
35#if 0 /* keep Emacsens' auto-indent happy */
36}
37#endif
38#endif
39
40#include "gnunet_common.h"
41#include "gnunet_configuration_lib.h"
42#include "gnunet_scheduler_lib.h"
43
44/**
45 * Version of the statistics API.
46 */
47#define GNUNET_STATISTICS_VERSION 0x00000000
48
49/**
50 * Opaque handle for the statistics service.
51 */
52struct GNUNET_STATISTICS_Handle;
53
54/**
55 * Callback function to process statistic values.
56 *
57 * @param cls closure
58 * @param subsystem name of subsystem that created the statistic
59 * @param name the name of the datum
60 * @param value the current value
61 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
62 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
63 */
64typedef int (*GNUNET_STATISTICS_Iterator) (void *cls,
65 const char *subsystem,
66 const char *name,
67 unsigned long long value,
68 int is_persistent);
69
70/**
71 * Get handle for the statistics service.
72 *
73 * @param sched scheduler to use
74 * @param subsystem name of subsystem using the service
75 * @param cfg services configuration in use
76 * @return handle to use
77 */
78struct GNUNET_STATISTICS_Handle
79 *GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
80 const char *subsystem,
81 struct GNUNET_CONFIGURATION_Handle *cfg);
82
83
84/**
85 * Destroy a handle (free all state associated with
86 * it).
87 */
88void GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *handle);
89
90
91/**
92 * Continuation called by the "get_all" and "get" functions.
93 *
94 * @param cls closure
95 * @param success GNUNET_OK if statistics were
96 * successfully obtained, GNUNET_SYSERR if not.
97 */
98typedef void (*GNUNET_STATISTICS_Callback) (void *cls, int success);
99
100/**
101 * Get statistic from the peer.
102 *
103 * @param handle identification of the statistics service
104 * @param subsystem limit to the specified subsystem, NULL for our subsystem
105 * @param name name of the statistic value, NULL for all values
106 * @param timeout after how long should we give up (and call
107 * notify with buf NULL and size 0)?
108 * @param cont continuation to call when done (can be NULL)
109 * @param proc function to call on each value
110 * @param cls closure for proc and cont
111 */
112void
113GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
114 const char *subsystem,
115 const char *name,
116 struct GNUNET_TIME_Relative timeout,
117 GNUNET_STATISTICS_Callback cont,
118 GNUNET_STATISTICS_Iterator proc, void *cls);
119
120/**
121 * Set statistic value for the peer. Will always use our
122 * subsystem (the argument used when "handle" was created).
123 *
124 * @param handle identification of the statistics service
125 * @param name name of the statistic value
126 * @param value new value to set
127 * @param make_persistent should the value be kept across restarts?
128 */
129void
130GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
131 const char *name,
132 unsigned long long value, int make_persistent);
133
134/**
135 * Set statistic value for the peer. Will always use our
136 * subsystem (the argument used when "handle" was created).
137 *
138 * @param handle identification of the statistics service
139 * @param name name of the statistic value
140 * @param delta change in value (added to existing value)
141 * @param make_persistent should the value be kept across restarts?
142 */
143void
144GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
145 const char *name,
146 long long delta, int make_persistent);
147
148
149
150#if 0 /* keep Emacsens' auto-indent happy */
151{
152#endif
153#ifdef __cplusplus
154}
155#endif
156
157#endif
diff --git a/src/include/gnunet_strings_lib.h b/src/include/gnunet_strings_lib.h
new file mode 100644
index 000000000..98d2ed396
--- /dev/null
+++ b/src/include/gnunet_strings_lib.h
@@ -0,0 +1,139 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_strings_lib.h
23 * @brief strings and string handling functions (including malloc
24 * and string tokenizing)
25 *
26 * @author Christian Grothoff
27 * @author Krista Bennett
28 * @author Gerd Knorr <kraxel@bytesex.org>
29 * @author Ioana Patrascu
30 * @author Tzvetan Horozov
31 */
32
33#ifndef GNUNET_STRINGS_LIB_H
34#define GNUNET_STRINGS_LIB_H
35
36/* we need size_t, and since it can be both unsigned int
37 or unsigned long long, this IS platform dependent;
38 but "stdlib.h" should be portable 'enough' to be
39 unconditionally available... */
40#include <stdlib.h>
41
42#ifdef __cplusplus
43extern "C"
44{
45#if 0 /* keep Emacsens' auto-indent happy */
46}
47#endif
48#endif
49
50#include "gnunet_time_lib.h"
51
52
53/**
54 * Convert a given filesize into a fancy human-readable format.
55 */
56char *GNUNET_STRINGS_byte_size_fancy (unsigned long long size);
57
58/**
59 * Convert the len characters long character sequence
60 * given in input that is in the given charset
61 * to UTF-8.
62 *
63 * @return the converted string (0-terminated)
64 */
65char *GNUNET_STRINGS_to_utf8 (const char *input,
66 size_t len, const char *charset);
67
68/**
69 * Complete filename (a la shell) from abbrevition.
70 *
71 * @param fil the name of the file, may contain ~/ or
72 * be relative to the current directory
73 * @returns the full file name,
74 * NULL is returned on error
75 */
76char *GNUNET_STRINGS_filename_expand (const char *fil);
77
78/**
79 * Fill a buffer of the given size with
80 * count 0-terminated strings (given as varargs).
81 * If "buffer" is NULL, only compute the amount of
82 * space required (sum of "strlen(arg)+1").
83 *
84 * Unlike using "snprintf" with "%s", this function
85 * will add 0-terminators after each string. The
86 * "GNUNET_string_buffer_tokenize" function can be
87 * used to parse the buffer back into individual
88 * strings.
89 *
90 * @return number of bytes written to the buffer
91 * (or number of bytes that would have been written)
92 */
93unsigned int GNUNET_STRINGS_buffer_fill (char *buffer,
94 unsigned int size,
95 unsigned int count, ...);
96
97/**
98 * Given a buffer of a given size, find "count"
99 * 0-terminated strings in the buffer and assign
100 * the count (varargs) of type "const char**" to the
101 * locations of the respective strings in the
102 * buffer.
103 *
104 * @param buffer the buffer to parse
105 * @param size size of the buffer
106 * @param count number of strings to locate
107 * @return offset of the character after the last 0-termination
108 * in the buffer, or 0 on error.
109 */
110unsigned int GNUNET_STRINGS_buffer_tokenize (const char *buffer,
111 unsigned int size,
112 unsigned int count, ...);
113
114
115
116/**
117 * "man ctime_r", except for GNUnet time; also, unlike ctime, the
118 * return value does not include the newline character.
119 */
120char *GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t);
121
122
123/**
124 * Give relative time in human-readable fancy format.
125 * @param delta time in milli seconds
126 */
127char *GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative
128 delta);
129#if 0 /* keep Emacsens' auto-indent happy */
130{
131#endif
132#ifdef __cplusplus
133}
134#endif
135
136
137/* ifndef GNUNET_UTIL_STRING_H */
138#endif
139/* end of gnunet_util_string.h */
diff --git a/src/include/gnunet_time_lib.h b/src/include/gnunet_time_lib.h
new file mode 100644
index 000000000..4ce1e3f1e
--- /dev/null
+++ b/src/include/gnunet_time_lib.h
@@ -0,0 +1,246 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/gnunet_time_lib.h
23 * @brief functions related to time
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_TIME_LIB_H
29#define GNUNET_TIME_LIB_H
30
31#ifdef __cplusplus
32extern "C"
33{
34#if 0 /* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39#include "gnunet_common.h"
40
41/**
42 * Time for absolute times used by GNUnet, in milliseconds.
43 */
44struct GNUNET_TIME_Absolute
45{
46 uint64_t value;
47};
48
49/**
50 * Time for relative time used by GNUnet, in milliseconds.
51 * Always positive, so we can only refer to future time.
52 */
53struct GNUNET_TIME_Relative
54{
55 uint64_t value;
56};
57
58
59/**
60 * Time for relative time used by GNUnet, in milliseconds and in network byte order.
61 */
62struct GNUNET_TIME_RelativeNBO
63{
64 uint64_t value GNUNET_PACKED;
65};
66
67
68/**
69 * Time for absolute time used by GNUnet, in milliseconds and in network byte order.
70 */
71struct GNUNET_TIME_AbsoluteNBO
72{
73 uint64_t value GNUNET_PACKED;
74};
75
76/**
77 * @brief constants to specify time
78 */
79#define GNUNET_TIME_UNIT_ZERO GNUNET_TIME_relative_get_zero()
80#define GNUNET_TIME_UNIT_MILLISECONDS GNUNET_TIME_relative_get_unit()
81#define GNUNET_TIME_UNIT_SECONDS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 1000)
82#define GNUNET_TIME_UNIT_MINUTES GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 60)
83#define GNUNET_TIME_UNIT_HOURS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 60)
84#define GNUNET_TIME_UNIT_DAYS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_HOURS, 24)
85#define GNUNET_TIME_UNIT_WEEKS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_DAYS, 7)
86#define GNUNET_TIME_UNIT_MONTHS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_DAYS, 30)
87#define GNUNET_TIME_UNIT_YEARS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_DAYS, 365)
88
89/**
90 * Constant used to specify "forever". This constant
91 * will be treated specially in all time operations.
92 */
93#define GNUNET_TIME_UNIT_FOREVER_REL GNUNET_TIME_relative_get_forever ()
94
95/**
96 * Constant used to specify "forever". This constant
97 * will be treated specially in all time operations.
98 */
99#define GNUNET_TIME_UNIT_FOREVER_ABS GNUNET_TIME_absolute_get_forever ()
100
101/**
102 * Return relative time of 0ms.
103 */
104struct GNUNET_TIME_Relative GNUNET_TIME_relative_get_zero (void);
105
106/**
107 * Return relative time of 1ms.
108 */
109struct GNUNET_TIME_Relative GNUNET_TIME_relative_get_unit (void);
110
111/**
112 * Return "forever".
113 */
114struct GNUNET_TIME_Relative GNUNET_TIME_relative_get_forever (void);
115
116/**
117 * Return "forever".
118 */
119struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get_forever (void);
120
121/**
122 * Get the current time.
123 *
124 * @return the current time
125 */
126struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get (void);
127
128/**
129 * Convert relative time to an absolute time in the
130 * future.
131 *
132 * @return timestamp that is "rel" in the future, or FOREVER if rel==FOREVER (or if we would overflow)
133 */
134struct GNUNET_TIME_Absolute GNUNET_TIME_relative_to_absolute (struct
135 GNUNET_TIME_Relative
136 rel);
137
138/**
139 * Given a timestamp in the future, how much time
140 * remains until then?
141 *
142 * @return future - now, or 0 if now >= future, or FOREVER if future==FOREVER.
143 */
144struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_remaining (struct
145 GNUNET_TIME_Absolute
146 future);
147
148/**
149 * Compute the time difference between the given start and end times.
150 * Use this function instead of actual subtraction to ensure that
151 * "FOREVER" and overflows are handeled correctly.
152 *
153 * @return 0 if start >= end; FOREVER if end==FOREVER; otherwise end - start
154 */
155struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_difference (struct
156 GNUNET_TIME_Absolute
157 start,
158 struct
159 GNUNET_TIME_Absolute
160 end);
161
162/**
163 * Get the duration of an operation as the
164 * difference of the current time and the given start time "hence".
165 *
166 * @return aborts if hence==FOREVER, 0 if hence > now, otherwise now-hence.
167 */
168struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_duration (struct
169 GNUNET_TIME_Absolute
170 hence);
171
172
173/**
174 * Add a given relative duration to the
175 * given start time.
176 *
177 * @return FOREVER if either argument is FOREVER or on overflow; start+duration otherwise
178 */
179struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_add (struct
180 GNUNET_TIME_Absolute
181 start,
182 struct
183 GNUNET_TIME_Relative
184 duration);
185
186/**
187 * Multiply relative time by a given factor.
188 *
189 * @return FOREVER if rel=FOREVER or on overflow; otherwise rel*factor
190 */
191struct GNUNET_TIME_Relative GNUNET_TIME_relative_multiply (struct
192 GNUNET_TIME_Relative
193 rel,
194 unsigned int
195 factor);
196
197/**
198 * Add relative times together.
199 *
200 * @return FOREVER if either argument is FOREVER or on overflow; a1+a2 otherwise
201 */
202struct GNUNET_TIME_Relative GNUNET_TIME_relative_add (struct
203 GNUNET_TIME_Relative a1,
204 struct
205 GNUNET_TIME_Relative
206 a2);
207
208
209/**
210 * Convert relative time to network byte order.
211 */
212struct GNUNET_TIME_RelativeNBO GNUNET_TIME_relative_hton (struct
213 GNUNET_TIME_Relative
214 a);
215
216/**
217 * Convert relative time from network byte order.
218 */
219struct GNUNET_TIME_Relative GNUNET_TIME_relative_ntoh (struct
220 GNUNET_TIME_RelativeNBO
221 a);
222
223/**
224 * Convert relative time to network byte order.
225 */
226struct GNUNET_TIME_AbsoluteNBO GNUNET_TIME_absolute_hton (struct
227 GNUNET_TIME_Absolute
228 a);
229
230/**
231 * Convert relative time from network byte order.
232 */
233struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_ntoh (struct
234 GNUNET_TIME_AbsoluteNBO
235 a);
236
237#if 0 /* keep Emacsens' auto-indent happy */
238{
239#endif
240#ifdef __cplusplus
241}
242#endif
243
244/* ifndef GNUNET_TIME_LIB_H */
245#endif
246/* end of gnunet_time_lib.h */
diff --git a/src/include/gnunet_transport_service.h b/src/include/gnunet_transport_service.h
new file mode 100644
index 000000000..25ca388a7
--- /dev/null
+++ b/src/include/gnunet_transport_service.h
@@ -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/**
22 * @file include/gnunet_transport_service.h
23 * @brief low-level P2P IO
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_TRANSPORT_SERVICE_H
28#define GNUNET_TRANSPORT_SERVICE_H
29
30#ifdef __cplusplus
31extern "C"
32{
33#if 0 /* keep Emacsens' auto-indent happy */
34}
35#endif
36#endif
37
38#include "gnunet_configuration_lib.h"
39#include "gnunet_crypto_lib.h"
40#include "gnunet_network_lib.h"
41#include "gnunet_scheduler_lib.h"
42#include "gnunet_time_lib.h"
43
44/**
45 * Version number of the transport API.
46 */
47#define GNUNET_TRANSPORT_VERSION 0x00000000
48
49/**
50 * Function called by the transport for each received message.
51 *
52 * @param cls closure
53 * @param latency estimated latency for communicating with the
54 * given peer
55 * @param peer (claimed) identity of the other peer
56 * @param message the message
57 */
58typedef void (*GNUNET_TRANSPORT_ReceiveCallback) (void *cls,
59 struct GNUNET_TIME_Relative
60 latency,
61 const struct
62 GNUNET_PeerIdentity * peer,
63 const struct
64 GNUNET_MessageHeader *
65 message);
66
67
68/**
69 * Opaque handle to the service.
70 */
71struct GNUNET_TRANSPORT_Handle;
72
73
74/**
75 * Function called to notify transport users that another
76 * peer connected to us.
77 *
78 * @param cls closure
79 * @param peer the peer that connected
80 * @param latency current latency of the connection
81 */
82typedef void
83 (*GNUNET_TRANSPORT_NotifyConnect) (void *cls,
84 const struct GNUNET_PeerIdentity * peer,
85 struct GNUNET_TIME_Relative latency);
86
87/**
88 * Function called to notify transport users that another
89 * peer disconnected from us.
90 *
91 * @param cls closure
92 * @param peer the peer that disconnected
93 */
94typedef void
95 (*GNUNET_TRANSPORT_NotifyDisconnect) (void *cls,
96 const struct GNUNET_PeerIdentity *
97 peer);
98
99
100/**
101 * Connect to the transport service. Note that the connection may
102 * complete (or fail) asynchronously.
103 *
104 * @param sched scheduler to use
105 * @param cfg configuration to use
106 * @param cls closure for the callbacks
107 * @param rec receive function to call
108 * @param nc function to call on connect events
109 * @param dc function to call on disconnect events
110 */
111struct GNUNET_TRANSPORT_Handle *GNUNET_TRANSPORT_connect (struct
112 GNUNET_SCHEDULER_Handle
113 *sched,
114 struct
115 GNUNET_CONFIGURATION_Handle
116 *cfg, void *cls,
117 GNUNET_TRANSPORT_ReceiveCallback
118 rec,
119 GNUNET_TRANSPORT_NotifyConnect
120 nc,
121 GNUNET_TRANSPORT_NotifyDisconnect
122 nd);
123
124
125/**
126 * Disconnect from the transport service.
127 */
128void GNUNET_TRANSPORT_disconnect (struct GNUNET_TRANSPORT_Handle *handle);
129
130
131/**
132 * Set the share of incoming/outgoing bandwidth for the given
133 * peer to the specified amount.
134 *
135 * @param handle connection to transport service
136 * @param target who's bandwidth quota is being changed
137 * @param quota_in incoming bandwidth quota in bytes per ms; 0 can
138 * be used to force all traffic to be discarded
139 * @param quota_out outgoing bandwidth quota in bytes per ms; 0 can
140 * be used to force all traffic to be discarded
141 * @param timeout how long to wait until signaling failure if
142 * we can not communicate the quota change
143 * @param cont continuation to call when done, will be called
144 * either with reason "TIMEOUT" or with reason "PREREQ_DONE"
145 * @param cont_cls closure for continuation
146 */
147void
148GNUNET_TRANSPORT_set_quota (struct GNUNET_TRANSPORT_Handle *handle,
149 const struct GNUNET_PeerIdentity *target,
150 uint32_t quota_in,
151 uint32_t quota_out,
152 struct GNUNET_TIME_Relative timeout,
153 GNUNET_SCHEDULER_Task cont, void *cont_cls);
154
155
156/**
157 * Opaque handle for a transmission-ready request.
158 */
159struct GNUNET_TRANSPORT_TransmitHandle;
160
161
162/**
163 * Check if we could queue a message of the given size for
164 * transmission. The transport service will take both its
165 * internal buffers and bandwidth limits imposed by the
166 * other peer into consideration when answering this query.
167 *
168 * @param handle connection to transport service
169 * @param target who should receive the message
170 * @param size how big is the message we want to transmit?
171 * @param timeout after how long should we give up (and call
172 * notify with buf NULL and size 0)?
173 * @param notify function to call when we are ready to
174 * send such a message
175 * @param notify_cls closure for notify
176 * @return NULL if someone else is already waiting to be notified
177 * non-NULL if the notify callback was queued (can be used to cancel
178 * using GNUNET_TRANSPORT_notify_transmit_ready_cancel)
179 */
180struct GNUNET_TRANSPORT_TransmitHandle
181 *GNUNET_TRANSPORT_notify_transmit_ready (struct GNUNET_TRANSPORT_Handle
182 *handle,
183 const struct GNUNET_PeerIdentity
184 *target, size_t size,
185 struct GNUNET_TIME_Relative
186 timeout,
187 GNUNET_NETWORK_TransmitReadyNotify
188 notify, void *notify_cls);
189
190
191/**
192 * Cancel the specified transmission-ready
193 * notification.
194 */
195void
196GNUNET_TRANSPORT_notify_transmit_ready_cancel (struct
197 GNUNET_TRANSPORT_TransmitHandle
198 *h);
199
200
201/**
202 * Obtain the HELLO message for this peer.
203 *
204 * @param handle connection to transport service
205 * @param timeout how long to wait for the HELLO
206 * @param rec function to call with the HELLO, sender will be our peer
207 * identity; message and sender will be NULL on timeout
208 * (handshake with transport service pending/failed).
209 * cost estimate will be 0.
210 * @param rec_cls closure for rec
211 */
212void
213GNUNET_TRANSPORT_get_hello (struct GNUNET_TRANSPORT_Handle *handle,
214 struct GNUNET_TIME_Relative timeout,
215 GNUNET_TRANSPORT_ReceiveCallback rec,
216 void *rec_cls);
217
218
219/**
220 * Offer the transport service the HELLO of another peer. Note that
221 * the transport service may just ignore this message if the HELLO is
222 * malformed or useless due to our local configuration.
223 *
224 * @param handle connection to transport service
225 * @param hello the hello message
226 */
227void
228GNUNET_TRANSPORT_offer_hello (struct GNUNET_TRANSPORT_Handle *handle,
229 const struct GNUNET_MessageHeader *hello);
230
231
232#if 0 /* keep Emacsens' auto-indent happy */
233{
234#endif
235#ifdef __cplusplus
236}
237#endif
238
239/* ifndef GNUNET_TRANSPORT_SERVICE_H */
240#endif
241/* end of gnunet_transport_service.h */
diff --git a/src/include/gnunet_upnp_service.h b/src/include/gnunet_upnp_service.h
new file mode 100644
index 000000000..758d5c598
--- /dev/null
+++ b/src/include/gnunet_upnp_service.h
@@ -0,0 +1,75 @@
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 include/gnunet_upnp_service.h
23 * @brief API for UPnP access
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_UPNP_SERVICE_H
28#define GNUNET_UPNP_SERVICE_H
29
30#include "gnunet_resolver_service.h"
31
32#ifdef __cplusplus
33extern "C"
34{
35#if 0 /* keep Emacsens' auto-indent happy */
36}
37#endif
38#endif
39
40
41/**
42 * Get the external IP address for the local machine and
43 * install a port mapping if possible. The external port
44 * will be returned as part of the address.
45 *
46 * @param sched scheduler to use
47 * @param cfg configuration to use
48 * @param domain communication domain (i.e. PF_INET or PF_INET6)
49 * @param type communication semantics (SOCK_STREAM, SOCK_DGRAM)
50 * @param protocol protocol to use, 0 for default (see protocols(5))
51 * @param port port to map
52 * @param timeout after how long should we give up (and call
53 * notify with buf NULL and size 0)?
54 * @param callback function to call with the external address;
55 * function will be called with NULL on error
56 * @param cls closure for callback
57 */
58int GNUNET_UPNP_get_ip (struct GNUNET_SCHEDULER_Handle *sched,
59 struct GNUNET_CONFIGURATION_Handle *cfg,
60 int domain,
61 int type,
62 int protocol,
63 uint16_t port,
64 struct GNUNET_TIME_Relative timeout,
65 GNUNET_RESOLVER_AddressCallback callback, void *cls);
66
67
68#if 0 /* keep Emacsens' auto-indent happy */
69{
70#endif
71#ifdef __cplusplus
72}
73#endif
74
75#endif
diff --git a/src/include/gnunet_util_lib.h b/src/include/gnunet_util_lib.h
new file mode 100644
index 000000000..c152377c3
--- /dev/null
+++ b/src/include/gnunet_util_lib.h
@@ -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/**
22 * @file include/gnunet_util_lib.h
23 * @brief convenience header including all headers of subsystems in
24 * gnunet_util library
25 * @author Christian Grothoff
26 */
27
28#ifndef GNUNET_UTIL_LIB_H
29#define GNUNET_UTIL_LIB_H
30
31#ifdef __cplusplus
32extern "C"
33{
34#if 0 /* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39#include "gnunet_common.h"
40#include "gnunet_client_lib.h"
41#include "gnunet_configuration_lib.h"
42#include "gnunet_container_lib.h"
43#include "gnunet_crypto_lib.h"
44#include "gnunet_disk_lib.h"
45#include "gnunet_getopt_lib.h"
46#include "gnunet_network_lib.h"
47#include "gnunet_program_lib.h"
48#include "gnunet_protocols.h"
49#include "gnunet_pseudonym_lib.h"
50#include "gnunet_scheduler_lib.h"
51#include "gnunet_server_lib.h"
52#include "gnunet_service_lib.h"
53#include "gnunet_signal_lib.h"
54#include "gnunet_strings_lib.h"
55#include "gnunet_time_lib.h"
56
57#if 0 /* keep Emacsens' auto-indent happy */
58{
59#endif
60#ifdef __cplusplus
61}
62#endif
63
64#endif
diff --git a/src/include/platform.h b/src/include/platform.h
new file mode 100644
index 000000000..07fbe0ee7
--- /dev/null
+++ b/src/include/platform.h
@@ -0,0 +1,221 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 include/platform.h
23 * @brief plaform specifics
24 *
25 * @author Nils Durner
26 *
27 * This file should never be included by installed
28 * header files (thos starting with "gnunet_").
29 */
30
31#ifndef PLATFORM_H
32#define PLATFORM_H
33
34#ifndef HAVE_USED_CONFIG_H
35#define HAVE_USED_CONFIG_H
36#if HAVE_CONFIG_H
37#include "gnunet_config.h"
38#endif
39#endif
40
41#ifdef WINDOWS
42#define BREAKPOINT asm("int $3;");
43#else
44#define BREAKPOINT
45#endif
46
47#ifdef HAVE_SYS_TYPES_H
48#include <sys/types.h>
49#endif
50
51#define ALLOW_EXTRA_CHECKS GNUNET_NO
52
53/**
54 * For strptime (glibc2 needs this).
55 */
56#ifndef _XOPEN_SOURCE
57#define _XOPEN_SOURCE
58#endif
59
60#ifndef _REENTRANT
61#define _REENTRANT
62#endif
63
64/* configuration options */
65
66#define VERBOSE_STATS 0
67
68#ifdef CYGWIN
69#include <sys/reent.h>
70#define _REENT_ONLY
71#endif
72
73#ifdef CYGWIN
74#undef _REENT_ONLY
75#endif
76
77#ifdef _MSC_VER
78#include <Winsock2.h>
79#else
80#ifndef MINGW
81#include <netdb.h>
82#include <sys/socket.h>
83#include <netinet/in.h>
84#include <arpa/inet.h>
85#include <netinet/tcp.h>
86#include <pwd.h>
87#include <sys/ioctl.h>
88#include <sys/wait.h>
89#include <grp.h>
90#else
91#include "winproc.h"
92#endif
93#endif
94
95#include <string.h>
96#include <stdio.h>
97#include <stdlib.h>
98#include <stdarg.h>
99#include <errno.h>
100#include <signal.h>
101#ifndef _MSC_VER
102#include <unistd.h> /* KLB_FIX */
103#endif
104#include <sys/stat.h>
105#include <sys/types.h>
106#ifndef _MSC_VER
107#include <dirent.h> /* KLB_FIX */
108#endif
109#include <fcntl.h>
110#include <math.h>
111#if HAVE_SYS_PARAM_H
112#include <sys/param.h>
113#endif
114#if TIME_WITH_SYS_TIME
115#include <sys/time.h>
116#include <time.h>
117#else
118#if HAVE_SYS_TIME_H
119#include <sys/time.h>
120#else
121#include <time.h>
122#endif
123#endif
124
125#ifdef SOMEBSD
126#include <net/if.h>
127#endif
128#ifdef GNUNET_freeBSD
129#include <semaphore.h>
130#endif
131#ifdef OSX
132#include <dlfcn.h>
133#include <semaphore.h>
134#include <net/if.h>
135#endif
136#ifdef LINUX
137#include <net/if.h>
138#endif
139#ifdef SOLARIS
140#include <sys/sockio.h>
141#include <sys/loadavg.h>
142#include <semaphore.h>
143#endif
144#ifdef CYGWIN
145#include <windows.h>
146#include <cygwin/if.h>
147#endif
148#if HAVE_IFADDRS_H
149#include <ifaddrs.h>
150#endif
151#include <errno.h>
152#include <limits.h>
153
154#if HAVE_CTYPE_H
155#include <ctype.h>
156#endif
157#if HAVE_SYS_RESOURCE_H
158#include <sys/resource.h>
159#endif
160
161#include "plibc.h"
162
163#include <pthread.h>
164#include <locale.h>
165#ifndef FRAMEWORK_BUILD
166#include "gettext.h"
167/**
168 * GNU gettext support macro.
169 */
170#define _(String) dgettext("gnunet",String)
171#define LIBEXTRACTOR_GETTEXT_DOMAIN "libextractor"
172#else
173#include "libintlemu.h"
174#define _(String) dgettext("org.gnunet.gnunet",String)
175#define LIBEXTRACTOR_GETTEXT_DOMAIN "org.gnunet.libextractor"
176#endif
177
178#ifdef CYGWIN
179#define SIOCGIFCONF _IOW('s', 100, struct ifconf) /* get if list */
180#define SIOCGIFFLAGS _IOW('s', 101, struct ifreq) /* Get if flags */
181#define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */
182#endif
183
184#ifndef MINGW
185#include <sys/mman.h>
186#endif
187
188#ifdef FREEBSD
189#define __BYTE_ORDER BYTE_ORDER
190#define __BIG_ENDIAN BIG_ENDIAN
191#endif
192
193#ifdef OSX
194#define socklen_t unsigned int
195#define __BYTE_ORDER BYTE_ORDER
196#define __BIG_ENDIAN BIG_ENDIAN
197 /* not available on OS X, override configure */
198#undef HAVE_STAT64
199#undef HAVE_MREMAP
200#endif
201
202
203#if !HAVE_ATOLL
204long long atoll (const char *nptr);
205#endif
206
207#if ENABLE_NLS
208#include "langinfo.h"
209#endif
210
211#ifndef O_LARGEFILE
212#define O_LARGEFILE 0
213#endif
214
215#if defined(__sparc__)
216#define MAKE_UNALIGNED(val) ({ __typeof__((val)) __tmp; memmove(&__tmp, &(val), sizeof((val))); __tmp; })
217#else
218#define MAKE_UNALIGNED(val) val
219#endif
220
221#endif
diff --git a/src/include/plibc.h b/src/include/plibc.h
new file mode 100644
index 000000000..93ca012e1
--- /dev/null
+++ b/src/include/plibc.h
@@ -0,0 +1,582 @@
1/*
2 This file is part of PlibC.
3 (C) 2005, 2006, 2007, 2008 Nils Durner (and other contributing authors)
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
20/**
21 * @file include/plibc.h
22 * @brief PlibC header
23 * @attention This file is usually not installed under Unix,
24 * so ship it with your application
25 * @version $Revision: 1.46 $
26 */
27
28#ifndef _PLIBC_H_
29#define _PLIBC_H_
30
31#ifndef SIGALRM
32#define SIGALRM 14
33#endif /* */
34
35#ifdef __cplusplus
36extern "C"
37{
38
39#endif /* */
40
41#ifdef Q_OS_WIN32
42#define WINDOWS 1
43#endif /* */
44
45#define HAVE_PLIBC_FD 0
46
47#ifdef WINDOWS
48
49#if ENABLE_NLS
50#include "langinfo.h"
51#endif /* */
52
53#include <windows.h>
54#include <Ws2tcpip.h>
55#include <time.h>
56#include <stdio.h>
57#include <sys/types.h>
58#include <sys/stat.h>
59#include <dirent.h>
60#include <errno.h>
61#include <stdarg.h>
62
63#define __BYTE_ORDER BYTE_ORDER
64#define __BIG_ENDIAN BIG_ENDIAN
65
66/* Conflicts with our definitions */
67#define __G_WIN32_H__
68
69/* Convert LARGE_INTEGER to double */
70#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + \
71 (double) ((x).LowPart))
72#define socklen_t int
73#define ssize_t int
74#define off_t int
75#define int64_t long long
76#define int32_t long
77 struct stat64
78 {
79 _dev_t st_dev;
80 _ino_t st_ino;
81 _mode_t st_mode;
82 short st_nlink;
83 short st_uid;
84 short st_gid;
85 _dev_t st_rdev;
86 __int64 st_size;
87 __time64_t st_atime;
88 __time64_t st_mtime;
89 __time64_t st_ctime;
90 };
91
92#ifndef pid_t
93#define pid_t int
94#endif /* */
95
96#ifndef WEXITSTATUS
97#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
98#endif /* */
99
100/* Thanks to the Cygwin project */
101#define ENOCSI 43 /* No CSI structure available */
102#define EL2HLT 44 /* Level 2 halted */
103#ifndef EDEADLK
104#define EDEADLK 45 /* Deadlock condition */
105#endif /* */
106#ifndef ENOLCK
107#define ENOLCK 46 /* No record locks available */
108#endif /* */
109#define EBADE 50 /* Invalid exchange */
110#define EBADR 51 /* Invalid request descriptor */
111#define EXFULL 52 /* Exchange full */
112#define ENOANO 53 /* No anode */
113#define EBADRQC 54 /* Invalid request code */
114#define EBADSLT 55 /* Invalid slot */
115#ifndef EDEADLOCK
116#define EDEADLOCK EDEADLK /* File locking deadlock error */
117#endif /* */
118#define EBFONT 57 /* Bad font file fmt */
119#define ENOSTR 60 /* Device not a stream */
120#define ENODATA 61 /* No data (for no delay io) */
121#define ETIME 62 /* Timer expired */
122#define ENOSR 63 /* Out of streams resources */
123#define ENONET 64 /* Machine is not on the network */
124#define ENOPKG 65 /* Package not installed */
125#define EREMOTE 66 /* The object is remote */
126#define ENOLINK 67 /* The link has been severed */
127#define EADV 68 /* Advertise error */
128#define ESRMNT 69 /* Srmount error */
129#define ECOMM 70 /* Communication error on send */
130#define EPROTO 71 /* Protocol error */
131#define EMULTIHOP 74 /* Multihop attempted */
132#define ELBIN 75 /* Inode is remote (not really error) */
133#define EDOTDOT 76 /* Cross mount point (not really error) */
134#define EBADMSG 77 /* Trying to read unreadable message */
135#define ENOTUNIQ 80 /* Given log. name not unique */
136#define EBADFD 81 /* f.d. invalid for this operation */
137#define EREMCHG 82 /* Remote address changed */
138#define ELIBACC 83 /* Can't access a needed shared lib */
139#define ELIBBAD 84 /* Accessing a corrupted shared lib */
140#define ELIBSCN 85 /* .lib section in a.out corrupted */
141#define ELIBMAX 86 /* Attempting to link in too many libs */
142#define ELIBEXEC 87 /* Attempting to exec a shared library */
143#ifndef ENOSYS
144#define ENOSYS 88 /* Function not implemented */
145#endif /* */
146#define ENMFILE 89 /* No more files */
147#ifndef ENOTEMPTY
148#define ENOTEMPTY 90 /* Directory not empty */
149#endif /* */
150#ifndef ENAMETOOLONG
151#define ENAMETOOLONG 91 /* File or path name too long */
152#endif /* */
153#define ELOOP 92 /* Too many symbolic links */
154#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
155#define EPFNOSUPPORT 96 /* Protocol family not supported */
156#define ECONNRESET 104 /* Connection reset by peer */
157#define ENOBUFS 105 /* No buffer space available */
158#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */
159#define EPROTOTYPE 107 /* Protocol wrong type for socket */
160#define ENOTSOCK 108 /* Socket operation on non-socket */
161#define ENOPROTOOPT 109 /* Protocol not available */
162#define ESHUTDOWN 110 /* Can't send after socket shutdown */
163#define ECONNREFUSED 111 /* Connection refused */
164#define EADDRINUSE 112 /* Address already in use */
165#define ECONNABORTED 113 /* Connection aborted */
166#define ENETUNREACH 114 /* Network is unreachable */
167#define ENETDOWN 115 /* Network interface is not configured */
168#ifndef ETIMEDOUT
169#define ETIMEDOUT 116 /* Connection timed out */
170#endif /* */
171#define EHOSTDOWN 117 /* Host is down */
172#define EHOSTUNREACH 118 /* Host is unreachable */
173#define EINPROGRESS 119 /* Connection already in progress */
174#define EALREADY 120 /* Socket already connected */
175#define EDESTADDRREQ 121 /* Destination address required */
176#define EMSGSIZE 122 /* Message too long */
177#define EPROTONOSUPPORT 123 /* Unknown protocol */
178#define ESOCKTNOSUPPORT 124 /* Socket type not supported */
179#define EADDRNOTAVAIL 125 /* Address not available */
180#define ENETRESET 126 /* Connection aborted by network */
181#define EISCONN 127 /* Socket is already connected */
182#define ENOTCONN 128 /* Socket is not connected */
183#define ETOOMANYREFS 129 /* Too many references: cannot splice */
184#define EPROCLIM 130 /* Too many processes */
185#define EUSERS 131 /* Too many users */
186#define EDQUOT 132 /* Disk quota exceeded */
187#define ESTALE 133 /* Unknown error */
188#ifndef ENOTSUP
189#define ENOTSUP 134 /* Not supported */
190#endif /* */
191#define ENOMEDIUM 135 /* No medium (in tape drive) */
192#define ENOSHARE 136 /* No such host or network path */
193#define ECASECLASH 137 /* Filename exists with different case */
194#define EWOULDBLOCK EAGAIN /* Operation would block */
195#define EOVERFLOW 139 /* Value too large for defined data type */
196
197#undef HOST_NOT_FOUND
198#define HOST_NOT_FOUND 1
199#undef TRY_AGAIN
200#define TRY_AGAIN 2
201#undef NO_RECOVERY
202#define NO_RECOVERY 3
203#undef NO_ADDRESS
204#define NO_ADDRESS 4
205
206#define PROT_READ 0x1
207#define PROT_WRITE 0x2
208#define MAP_SHARED 0x1
209#define MAP_PRIVATE 0x2 /* unsupported */
210#define MAP_FIXED 0x10
211#define MAP_FAILED ((void *)-1)
212 struct statfs
213 {
214 long f_type; /* type of filesystem (see below) */
215 long f_bsize; /* optimal transfer block size */
216 long f_blocks; /* total data blocks in file system */
217 long f_bfree; /* free blocks in fs */
218 long f_bavail; /* free blocks avail to non-superuser */
219 long f_files; /* total file nodes in file system */
220 long f_ffree; /* free file nodes in fs */
221 long f_fsid; /* file system id */
222 long f_namelen; /* maximum length of filenames */
223 long f_spare[6]; /* spare for later */
224 };
225 extern const struct in6_addr in6addr_any; /* :: */
226 extern const struct in6_addr in6addr_loopback; /* ::1 */
227
228/* Taken from the Wine project <http://www.winehq.org>
229 /wine/include/winternl.h */
230 enum SYSTEM_INFORMATION_CLASS
231 { SystemBasicInformation = 0, Unknown1, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, /* was SystemTimeInformation */
232 Unknown4, SystemProcessInformation =
233 5, Unknown6, Unknown7, SystemProcessorPerformanceInformation =
234 8, Unknown9, Unknown10, SystemDriverInformation, Unknown12,
235 Unknown13, Unknown14, Unknown15, SystemHandleList, Unknown17,
236 Unknown18, Unknown19, Unknown20, SystemCacheInformation,
237 Unknown22, SystemInterruptInformation =
238 23, SystemExceptionInformation =
239 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45
240 };
241 typedef struct
242 {
243 LARGE_INTEGER IdleTime;
244 LARGE_INTEGER KernelTime;
245 LARGE_INTEGER UserTime;
246 LARGE_INTEGER Reserved1[2];
247 ULONG Reserved2;
248 } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
249
250#define sleep(secs) (Sleep(secs * 1000))
251
252/*********************** statfs *****************************/
253/* fake block size */
254#define FAKED_BLOCK_SIZE 512
255
256/* linux-compatible values for fs type */
257#define MSDOS_SUPER_MAGIC 0x4d44
258#define NTFS_SUPER_MAGIC 0x5346544E
259
260/*********************** End of statfs ***********************/
261
262#define SHUT_RDWR SD_BOTH
263
264/* Operations for flock() */
265#define LOCK_SH 1 /* shared lock */
266#define LOCK_EX 2 /* exclusive lock */
267#define LOCK_NB 4 /* or'd with one of the above to prevent
268 blocking */
269#define LOCK_UN 8 /* remove lock */
270
271/* Not supported under MinGW */
272#define S_IRGRP 0
273#define S_IWGRP 0
274#define S_IROTH 0
275#define S_IXGRP 0
276#define S_IWOTH 0
277#define S_IXOTH 0
278#define S_ISUID 0
279#define S_ISGID 0
280#define S_ISVTX 0
281#define S_IRWXG 0
282#define S_IRWXO 0
283
284#define SHUT_WR SD_SEND
285#define SHUT_RD SD_RECEIVE
286#define SHUT_RDWR SD_BOTH
287
288#define SIGKILL 9
289#define SIGTERM 15
290
291#define SetErrnoFromWinError(e) _SetErrnoFromWinError(e, __FILE__, __LINE__)
292 BOOL _plibc_CreateShortcut (const char *pszSrc, const char *pszDest);
293 BOOL _plibc_DereferenceShortcut (char *pszShortcut);
294 char *plibc_ChooseDir (char *pszTitle, unsigned long ulFlags);
295 char *plibc_ChooseFile (char *pszTitle, unsigned long ulFlags);
296 long QueryRegistry (HKEY hMainKey, char *pszKey, char *pszSubKey,
297 char *pszBuffer, long *pdLength);
298 BOOL __win_IsHandleMarkedAsBlocking (SOCKET hHandle);
299 void __win_SetHandleBlockingMode (SOCKET s, BOOL bBlocking);
300 void __win_DiscardHandleBlockingMode (SOCKET s);
301 int _win_isSocketValid (int s);
302 int plibc_conv_to_win_path (const char *pszUnix, char *pszWindows);
303 unsigned plibc_get_handle_count ();
304 typedef void (*TPanicProc) (int, char *);
305 void plibc_set_panic_proc (TPanicProc proc);
306 int flock (int fd, int operation);
307 int fsync (int fildes);
308 int inet_pton (int af, const char *src, void *dst);
309 int inet_pton4 (const char *src, u_char * dst, int pton);
310
311#if USE_IPV6
312 int inet_pton6 (const char *src, u_char * dst);
313
314#endif /* */
315 int truncate (const char *fname, int distance);
316 int statfs (const char *path, struct statfs *buf);
317 const char *hstrerror (int err);
318 void gettimeofday (struct timeval *tp, void *tzp);
319 int mkstemp (char *tmplate);
320 char *strptime (const char *buf, const char *format, struct tm *tm);
321 char *ctime (const time_t * clock);
322 char *ctime_r (const time_t * clock, char *buf);
323 const char *inet_ntop (int af, const void *src, char *dst, size_t size);
324 int plibc_init (char *pszOrg, char *pszApp);
325 void plibc_shutdown ();
326 int plibc_initialized ();
327 int plibc_conv_to_win_path_ex (const char *pszUnix, char *pszWindows,
328 int derefLinks);
329 void _SetErrnoFromWinError (long lWinError, char *pszCaller, int iLine);
330 void SetErrnoFromWinsockError (long lWinError);
331 void SetHErrnoFromWinError (long lWinError);
332 void SetErrnoFromHRESULT (HRESULT hRes);
333 FILE *_win_fopen (const char *filename, const char *mode);
334 DIR *_win_opendir (const char *dirname);
335 int _win_open (const char *filename, int oflag, ...);
336
337#ifdef ENABLE_NLS
338 char *_win_bindtextdomain (const char *domainname, const char *dirname);
339
340#endif /* */
341 int _win_chdir (const char *path);
342 int _win_close (int fd);
343 int _win_creat (const char *path, mode_t mode);
344 int _win_fstat (int handle, struct stat *buffer);
345 int _win_ftruncate (int fildes, off_t length);
346 int _win_kill (pid_t pid, int sig);
347 int _win_pipe (int *phandles);
348 int _win_rmdir (const char *path);
349 int _win_access (const char *path, int mode);
350 int _win_chmod (const char *filename, int pmode);
351 char *realpath (const char *file_name, char *resolved_name);
352 long _win_random (void);
353 int _win_remove (const char *path);
354 int _win_rename (const char *oldname, const char *newname);
355 int _win_stat (const char *path, struct stat *buffer);
356 int _win_stat64 (const char *path, struct stat64 *buffer);
357 int _win_unlink (const char *filename);
358 int _win_write (int fildes, const void *buf, size_t nbyte);
359 int _win_read (int fildes, void *buf, size_t nbyte);
360 size_t _win_fwrite (const void *buffer, size_t size, size_t count,
361 FILE * stream);
362 size_t _win_fread (void *buffer, size_t size, size_t count, FILE * stream);
363 int _win_symlink (const char *path1, const char *path2);
364 void *_win_mmap (void *start, size_t len, int access, int flags, int fd,
365 unsigned long long offset);
366 int _win_munmap (void *start, size_t length);
367 int _win_lstat (const char *path, struct stat *buf);
368 int _win_lstat64 (const char *path, struct stat64 *buf);
369 int _win_readlink (const char *path, char *buf, size_t bufsize);
370 int _win_accept (SOCKET s, struct sockaddr *addr, int *addrlen);
371 int _win_printf (const char *format, ...);
372 int _win_fprintf (FILE * f, const char *format, ...);
373 int _win_vprintf (const char *format, va_list ap);
374 int _win_vfprintf (FILE * stream, const char *format, va_list arg_ptr);
375 int _win_vsprintf (char *dest, const char *format, va_list arg_ptr);
376 int _win_vsnprintf (char *str, size_t size, const char *format,
377 va_list arg_ptr);
378 int _win_snprintf (char *str, size_t size, const char *format, ...);
379 int _win_sprintf (char *dest, const char *format, ...);
380 int _win_vsscanf (const char *str, const char *format, va_list arg_ptr);
381 int _win_sscanf (const char *str, const char *format, ...);
382 int _win_vfscanf (FILE * stream, const char *format, va_list arg_ptr);
383 int _win_vscanf (const char *format, va_list arg_ptr);
384 int _win_scanf (const char *format, ...);
385 int _win_fscanf (FILE * stream, const char *format, ...);
386 pid_t _win_waitpid (pid_t pid, int *stat_loc, int options);
387 int _win_bind (SOCKET s, const struct sockaddr *name, int namelen);
388 int _win_connect (SOCKET s, const struct sockaddr *name, int namelen);
389 int _win_getpeername (SOCKET s, struct sockaddr *name, int *namelen);
390 int _win_getsockname (SOCKET s, struct sockaddr *name, int *namelen);
391 int _win_getsockopt (SOCKET s, int level, int optname, char *optval,
392 int *optlen);
393 int _win_listen (SOCKET s, int backlog);
394 int _win_recv (SOCKET s, char *buf, int len, int flags);
395 int _win_recvfrom (SOCKET s, void *buf, int len, int flags,
396 struct sockaddr *from, int *fromlen);
397 int _win_select (int max_fd, fd_set * rfds, fd_set * wfds, fd_set * efds,
398 const struct timeval *tv);
399 int _win_send (SOCKET s, const char *buf, int len, int flags);
400 int _win_sendto (SOCKET s, const char *buf, int len, int flags,
401 const struct sockaddr *to, int tolen);
402 int _win_setsockopt (SOCKET s, int level, int optname, const void *optval,
403 int optlen);
404 int _win_shutdown (SOCKET s, int how);
405 SOCKET _win_socket (int af, int type, int protocol);
406 struct hostent *_win_gethostbyaddr (const char *addr, int len, int type);
407 struct hostent *_win_gethostbyname (const char *name);
408 struct hostent *gethostbyname2 (const char *name, int af);
409 char *_win_strerror (int errnum);
410 int IsWinNT ();
411 char *index (const char *s, int c);
412
413#if !HAVE_STRNDUP
414 char *strndup (const char *s, size_t n);
415
416#endif /* */
417#if !HAVE_STRNLEN
418 size_t strnlen (const char *str, size_t maxlen);
419
420#endif /* */
421
422#define strcasecmp(a, b) stricmp(a, b)
423#define strncasecmp(a, b, c) strnicmp(a, b, c)
424
425#endif /* WINDOWS */
426
427#ifndef WINDOWS
428#define DIR_SEPARATOR '/'
429#define DIR_SEPARATOR_STR "/"
430#define PATH_SEPARATOR ';'
431#define PATH_SEPARATOR_STR ";"
432#define NEWLINE "\n"
433
434#ifdef ENABLE_NLS
435#define BINDTEXTDOMAIN(d, n) bindtextdomain(d, n)
436#endif /* */
437#define CREAT(p, m) creat(p, m)
438#undef FOPEN
439#define FOPEN(f, m) fopen(f, m)
440#define FTRUNCATE(f, l) ftruncate(f, l)
441#define OPENDIR(d) opendir(d)
442#define OPEN open
443#define CHDIR(d) chdir(d)
444#define CLOSE(f) close(f)
445#define LSEEK(f, o, w) lseek(f, o, w)
446#define RMDIR(f) rmdir(f)
447#define ACCESS(p, m) access(p, m)
448#define CHMOD(f, p) chmod(f, p)
449#define FSTAT(h, b) fstat(h, b)
450#define PLIBC_KILL(p, s) kill(p, s)
451#define PIPE(h) pipe(h)
452#define REMOVE(p) remove(p)
453#define RENAME(o, n) rename(o, n)
454#define STAT(p, b) stat(p, b)
455#define STAT64(p, b) stat64(p, b)
456#define UNLINK(f) unlink(f)
457#define WRITE(f, b, n) write(f, b, n)
458#define READ(f, b, n) read(f, b, n)
459#define GN_FREAD(b, s, c, f) fread(b, s, c, f)
460#define GN_FWRITE(b, s, c, f) fwrite(b, s, c, f)
461#define SYMLINK(a, b) symlink(a, b)
462#define MMAP(s, l, p, f, d, o) mmap(s, l, p, f, d, o)
463#define MUNMAP(s, l) munmap(s, l)
464#define STRERROR(i) strerror(i)
465#define RANDOM() random()
466#define READLINK(p, b, s) readlink(p, b, s)
467#define LSTAT(p, b) lstat(p, b)
468#define LSTAT64(p, b) lstat64(p, b)
469#define PRINTF printf
470#define FPRINTF fprintf
471#define VPRINTF(f, a) vprintf(f, a)
472#define VFPRINTF(s, f, a) vfprintf(s, f, a)
473#define VSPRINTF(d, f, a) vsprintf(d, f, a)
474#define VSNPRINTF(str, size, fmt, a) vsnprintf(str, size, fmt, a)
475#define _REAL_SNPRINTF snprintf
476#define SPRINTF sprintf
477#define VSSCANF(s, f, a) vsscanf(s, f, a)
478#define SSCANF sscanf
479#define VFSCANF(s, f, a) vfscanf(s, f, a)
480#define VSCANF(f, a) vscanf(f, a)
481#define SCANF scanf
482#define FSCANF fscanf
483#define WAITPID(p, s, o) waitpid(p, s, o)
484#define ACCEPT(s, a, l) accept(s, a, l)
485#define BIND(s, n, l) bind(s, n, l)
486#define CONNECT(s, n, l) connect(s, n, l)
487#define GETPEERNAME(s, n, l) getpeername(s, n, l)
488#define GETSOCKNAME(s, n, l) getsockname(s, n, l)
489#define GETSOCKOPT(s, l, o, v, p) getsockopt(s, l, o, v, p)
490#define LISTEN(s, b) listen(s, b)
491#define RECV(s, b, l, f) recv(s, b, l, f)
492#define RECVFROM(s, b, l, f, r, o) recvfrom(s, b, l, f, r, o)
493#define SELECT(n, r, w, e, t) select(n, r, w, e, t)
494#define SEND(s, b, l, f) send(s, b, l, f)
495#define SENDTO(s, b, l, f, o, n) sendto(s, b, l, f, o, n)
496#define SETSOCKOPT(s, l, o, v, n) setsockopt(s, l, o, v, n)
497#define SHUTDOWN(s, h) shutdown(s, h)
498#define SOCKET(a, t, p) socket(a, t, p)
499#define GETHOSTBYADDR(a, l, t) gethostbyname(a, l, t)
500#define GETHOSTBYNAME(n) gethostbyname(n)
501#else /* */
502#define DIR_SEPARATOR '\\'
503#define DIR_SEPARATOR_STR "\\"
504#define PATH_SEPARATOR ':'
505#define PATH_SEPARATOR_STR ":"
506#define NEWLINE "\r\n"
507
508#ifdef ENABLE_NLS
509#define BINDTEXTDOMAIN(d, n) _win_bindtextdomain(d, n)
510#endif /* */
511#define CREAT(p, m) _win_creat(p, m)
512#define FOPEN(f, m) _win_fopen(f, m)
513#define FTRUNCATE(f, l) _win_ftruncate(f, l)
514#define OPENDIR(d) _win_opendir(d)
515#define OPEN _win_open
516#define CHDIR(d) _win_chdir(d)
517#define CLOSE(f) _win_close(f)
518#define PLIBC_KILL(p, s) _win_kill(p, s)
519#define LSEEK(f, o, w) _win_lseek(f, o, w)
520#define FSTAT(h, b) _win_fstat(h, b)
521#define RMDIR(f) _win_rmdir(f)
522#define ACCESS(p, m) _win_access(p, m)
523#define CHMOD(f, p) _win_chmod(f, p)
524#define PIPE(h) _win_pipe(h)
525#define RANDOM() _win_random()
526#define REMOVE(p) _win_remove(p)
527#define RENAME(o, n) _win_rename(o, n)
528#define STAT(p, b) _win_stat(p, b)
529#define STAT64(p, b) _win_stat64(p, b)
530#define UNLINK(f) _win_unlink(f)
531#define WRITE(f, b, n) _win_write(f, b, n)
532#define READ(f, b, n) _win_read(f, b, n)
533#define GN_FREAD(b, s, c, f) _win_fread(b, s, c, f)
534#define GN_FWRITE(b, s, c, f) _win_fwrite(b, s, c, f)
535#define SYMLINK(a, b) _win_symlink(a, b)
536#define MMAP(s, l, p, f, d, o) _win_mmap(s, l, p, f, d, o)
537#define MUNMAP(s, l) _win_munmap(s, l)
538#define STRERROR(i) _win_strerror(i)
539#define READLINK(p, b, s) _win_readlink(p, b, s)
540#define LSTAT(p, b) _win_lstat(p, b)
541#define LSTAT64(p, b) _win_lstat64(p, b)
542#define PRINTF(f, ...) _win_printf(f , __VA_ARGS__)
543#define FPRINTF(fil, fmt, ...) _win_fprintf(fil, fmt, __VA_ARGS__)
544#define VPRINTF(f, a) _win_vprintf(f, a)
545#define VFPRINTF(s, f, a) _win_vfprintf(s, f, a)
546#define VSPRINTF(d, f, a) _win_vsprintf(d, f, a)
547#define VSNPRINTF(str, size, fmt, a) _win_vsnprintf(str, size, fmt, a)
548#define _REAL_SNPRINTF(str, size, fmt, ...) _win_snprintf(str, size, fmt, __VA_ARGS__)
549#define SPRINTF(d, f, ...) _win_sprintf(d, f, __VA_ARGS__)
550#define VSSCANF(s, f, a) _win_vsscanf(s, f, a)
551#define SSCANF(s, f, ...) _win_sscanf(s, f, __VA_ARGS__)
552#define VFSCANF(s, f, a) _win_vfscanf(s, f, a)
553#define VSCANF(f, a) _win_vscanf(f, a)
554#define SCANF(f, ...) _win_scanf(f, __VA_ARGS__)
555#define FSCANF(s, f, ...) _win_fscanf(s, f, __VA_ARGS__)
556#define WAITPID(p, s, o) _win_waitpid(p, s, o)
557#define ACCEPT(s, a, l) _win_accept(s, a, l)
558#define BIND(s, n, l) _win_bind(s, n, l)
559#define CONNECT(s, n, l) _win_connect(s, n, l)
560#define GETPEERNAME(s, n, l) _win_getpeername(s, n, l)
561#define GETSOCKNAME(s, n, l) _win_getsockname(s, n, l)
562#define GETSOCKOPT(s, l, o, v, p) _win_getsockopt(s, l, o, v, p)
563#define LISTEN(s, b) _win_listen(s, b)
564#define RECV(s, b, l, f) _win_recv(s, b, l, f)
565#define RECVFROM(s, b, l, f, r, o) _win_recvfrom(s, b, l, f, r, o)
566#define SELECT(n, r, w, e, t) _win_select(n, r, w, e, t)
567#define SEND(s, b, l, f) _win_send(s, b, l, f)
568#define SENDTO(s, b, l, f, o, n) _win_sendto(s, b, l, f, o, n)
569#define SETSOCKOPT(s, l, o, v, n) _win_setsockopt(s, l, o, v, n)
570#define SHUTDOWN(s, h) _win_shutdown(s, h)
571#define SOCKET(a, t, p) _win_socket(a, t, p)
572#define GETHOSTBYADDR(a, l, t) _win_gethostbyname(a, l, t)
573#define GETHOSTBYNAME(n) _win_gethostbyname(n)
574#endif /* */
575
576#ifdef __cplusplus
577}
578#endif /* */
579
580#endif //_PLIBC_H_
581
582/* end of plibc.h */
diff --git a/src/include/winproc.h b/src/include/winproc.h
new file mode 100644
index 000000000..a4a173a13
--- /dev/null
+++ b/src/include/winproc.h
@@ -0,0 +1,216 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005 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/winproc.h
23 * @brief Definitions for MS Windows
24 * @author Nils Durner
25 **/
26
27#ifndef _WINPROC_H
28#define _WINPROC_H
29
30#include <io.h>
31#include <stdio.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/timeb.h>
35#include <time.h>
36#include <dirent.h>
37#include <windows.h>
38#include <winsock.h>
39#include <winerror.h>
40#include <iphlpapi.h>
41#include <shlobj.h>
42#include <objbase.h>
43#include <sys/param.h> /* #define BYTE_ORDER */
44#include <Ntsecapi.h>
45#include <lm.h>
46#include <Aclapi.h>
47#include "gnunet_util.h"
48#include "platform.h"
49
50#ifdef __cplusplus
51extern "C"
52{
53#endif
54
55#ifndef MAX_NAME_LENGTH
56#define MAX_NAME_LENGTH 25
57#endif
58
59 typedef DWORD WINAPI (*TNtQuerySystemInformation) (int, PVOID, ULONG,
60 PULONG);
61 typedef DWORD WINAPI (*TGetIfEntry) (PMIB_IFROW pIfRow);
62 typedef DWORD WINAPI (*TGetIpAddrTable) (PMIB_IPADDRTABLE pIpAddrTable,
63 PULONG pdwSize, BOOL bOrder);
64 typedef DWORD WINAPI (*TGetIfTable) (PMIB_IFTABLE pIfTable, PULONG pdwSize,
65 BOOL bOrder);
66 typedef DWORD WINAPI (*TCreateHardLink) (LPCTSTR lpFileName,
67 LPCTSTR lpExistingFileName,
68 LPSECURITY_ATTRIBUTES
69 lpSecurityAttributes);
70 typedef SC_HANDLE WINAPI (*TOpenSCManager) (LPCTSTR lpMachineName,
71 LPCTSTR lpDatabaseName,
72 DWORD dwDesiredAccess);
73 typedef SC_HANDLE WINAPI (*TCreateService) (SC_HANDLE hSCManager,
74 LPCTSTR lpServiceName,
75 LPCTSTR lpDisplayName,
76 DWORD dwDesiredAccess,
77 DWORD dwServiceType,
78 DWORD dwStartType,
79 DWORD dwErrorControl,
80 LPCTSTR lpBinaryPathName,
81 LPCTSTR lpLoadOrderGroup,
82 LPDWORD lpdwTagId,
83 LPCTSTR lpDependencies,
84 LPCTSTR lpServiceStartName,
85 LPCTSTR lpPassword);
86 typedef BOOL WINAPI (*TCloseServiceHandle) (SC_HANDLE hSCObject);
87 typedef BOOL WINAPI (*TDeleteService) (SC_HANDLE hService);
88 typedef SERVICE_STATUS_HANDLE WINAPI (*TRegisterServiceCtrlHandler) (LPCTSTR
89 lpServiceName,
90 LPHANDLER_FUNCTION
91 lpHandlerProc);
92 typedef BOOL WINAPI (*TSetServiceStatus) (SERVICE_STATUS_HANDLE
93 hServiceStatus,
94 LPSERVICE_STATUS lpServiceStatus);
95 typedef BOOL WINAPI (*TStartServiceCtrlDispatcher) (const
96 LPSERVICE_TABLE_ENTRY
97 lpServiceTable);
98 typedef BOOL WINAPI (*TControlService) (SC_HANDLE hService, DWORD dwControl,
99 LPSERVICE_STATUS lpServiceStatus);
100 typedef SC_HANDLE WINAPI (*TOpenService) (SC_HANDLE hSCManager,
101 LPCTSTR lpServiceName,
102 DWORD dwDesiredAccess);
103 typedef DWORD WINAPI (*TGetBestInterface) (IPAddr dwDestAddr,
104 PDWORD pdwBestIfIndex);
105 typedef DWORD WINAPI (*TGetAdaptersInfo) (PIP_ADAPTER_INFO pAdapterInfo,
106 PULONG pOutBufLen);
107 typedef NET_API_STATUS WINAPI (*TNetUserAdd) (LPCWSTR, DWORD, PBYTE,
108 PDWORD);
109 typedef NET_API_STATUS WINAPI (*TNetUserSetInfo) (LPCWSTR servername,
110 LPCWSTR username,
111 DWORD level, LPBYTE buf,
112 LPDWORD parm_err);
113 typedef NTSTATUS NTAPI (*TLsaOpenPolicy) (PLSA_UNICODE_STRING,
114 PLSA_OBJECT_ATTRIBUTES,
115 ACCESS_MASK, PLSA_HANDLE);
116 typedef NTSTATUS NTAPI (*TLsaAddAccountRights) (LSA_HANDLE, PSID,
117 PLSA_UNICODE_STRING, ULONG);
118 typedef NTSTATUS NTAPI (*TLsaRemoveAccountRights) (LSA_HANDLE, PSID,
119 BOOLEAN,
120 PLSA_UNICODE_STRING,
121 ULONG);
122 typedef NTSTATUS NTAPI (*TLsaClose) (LSA_HANDLE);
123 typedef BOOL WINAPI (*TLookupAccountName) (LPCTSTR lpSystemName,
124 LPCTSTR lpAccountName, PSID Sid,
125 LPDWORD cbSid,
126 LPTSTR ReferencedDomainName,
127 LPDWORD cchReferencedDomainName,
128 PSID_NAME_USE peUse);
129
130 typedef BOOL WINAPI (*TGetFileSecurity) (LPCTSTR lpFileName,
131 SECURITY_INFORMATION
132 RequestedInformation,
133 PSECURITY_DESCRIPTOR
134 pSecurityDescriptor, DWORD nLength,
135 LPDWORD lpnLengthNeeded);
136 typedef BOOL WINAPI (*TInitializeSecurityDescriptor) (PSECURITY_DESCRIPTOR
137 pSecurityDescriptor,
138 DWORD dwRevision);
139 typedef BOOL WINAPI (*TGetSecurityDescriptorDacl) (PSECURITY_DESCRIPTOR
140 pSecurityDescriptor,
141 LPBOOL lpbDaclPresent,
142 PACL * pDacl,
143 LPBOOL lpbDaclDefaulted);
144 typedef BOOL WINAPI (*TGetAclInformation) (PACL pAcl,
145 LPVOID pAclInformation,
146 DWORD nAclInformationLength,
147 ACL_INFORMATION_CLASS
148 dwAclInformationClass);
149 typedef BOOL WINAPI (*TInitializeAcl) (PACL pAcl, DWORD nAclLength,
150 DWORD dwAclRevision);
151 typedef BOOL WINAPI (*TGetAce) (PACL pAcl, DWORD dwAceIndex, LPVOID * pAce);
152 typedef BOOL WINAPI (*TEqualSid) (PSID pSid1, PSID pSid2);
153 typedef BOOL WINAPI (*TAddAce) (PACL pAcl, DWORD dwAceRevision,
154 DWORD dwStartingAceIndex, LPVOID pAceList,
155 DWORD nAceListLength);
156 typedef BOOL WINAPI (*TAddAccessAllowedAce) (PACL pAcl, DWORD dwAceRevision,
157 DWORD AccessMask, PSID pSid);
158 typedef BOOL WINAPI (*TSetNamedSecurityInfo) (LPTSTR pObjectName,
159 SE_OBJECT_TYPE ObjectType,
160 SECURITY_INFORMATION
161 SecurityInfo, PSID psidOwner,
162 PSID psidGroup, PACL pDacl,
163 PACL pSacl);
164
165 extern TNtQuerySystemInformation GNNtQuerySystemInformation;
166 extern TGetIfEntry GNGetIfEntry;
167 extern TGetIpAddrTable GNGetIpAddrTable;
168 extern TGetIfTable GNGetIfTable;
169 extern TCreateHardLink GNCreateHardLink;
170 extern TOpenSCManager GNOpenSCManager;
171 extern TCreateService GNCreateService;
172 extern TCloseServiceHandle GNCloseServiceHandle;
173 extern TDeleteService GNDeleteService;
174 extern TRegisterServiceCtrlHandler GNRegisterServiceCtrlHandler;
175 extern TSetServiceStatus GNSetServiceStatus;
176 extern TStartServiceCtrlDispatcher GNStartServiceCtrlDispatcher;
177 extern TControlService GNControlService;
178 extern TOpenService GNOpenService;
179 extern TGetBestInterface GNGetBestInterface;
180 extern TGetAdaptersInfo GGetAdaptersInfo;
181 extern TNetUserAdd GNNetUserAdd;
182 extern TNetUserSetInfo GNNetUserSetInfo;
183 extern TLsaOpenPolicy GNLsaOpenPolicy;
184 extern TLsaAddAccountRights GNLsaAddAccountRights;
185 extern TLsaRemoveAccountRights GNLsaRemoveAccountRights;
186 extern TLsaClose GNLsaClose;
187 extern TLookupAccountName GNLookupAccountName;
188 extern TGetFileSecurity GNGetFileSecurity;
189 extern TInitializeSecurityDescriptor GNInitializeSecurityDescriptor;
190 extern TGetSecurityDescriptorDacl GNGetSecurityDescriptorDacl;
191 extern TGetAclInformation GNGetAclInformation;
192 extern TInitializeAcl GNInitializeAcl;
193 extern TGetAce GNGetAce;
194 extern TEqualSid GNEqualSid;
195 extern TAddAce GNAddAce;
196 extern TAddAccessAllowedAce GNAddAccessAllowedAce;
197 extern TSetNamedSecurityInfo GNSetNamedSecurityInfo;
198
199
200 BOOL CreateShortcut (const char *pszSrc, const char *pszDest);
201 BOOL DereferenceShortcut (char *pszShortcut);
202 long QueryRegistry (HKEY hMainKey, char *pszKey, char *pszSubKey,
203 char *pszBuffer, long *pdLength);
204 int ListNICs (void (*callback) (const char *, int, void *), void *cls);
205 BOOL AddPathAccessRights (char *lpszFileName, char *lpszAccountName,
206 DWORD dwAccessMask);
207 char *winErrorStr (const char *prefix, int dwErr);
208
209 void GNInitWinEnv ();
210 void GNShutdownWinEnv ();
211
212#ifdef __cplusplus
213}
214#endif
215
216#endif
diff --git a/src/peerinfo/Makefile.am b/src/peerinfo/Makefile.am
new file mode 100644
index 000000000..294ff5cca
--- /dev/null
+++ b/src/peerinfo/Makefile.am
@@ -0,0 +1,56 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -luuid -liconv -lstdc++ -lcomdlg32 -lgdi32
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11lib_LTLIBRARIES = libgnunetpeerinfo.la
12
13libgnunetpeerinfo_la_SOURCES = \
14 peerinfo_api.c peerinfo.h
15libgnunetpeerinfo_la_LIBADD = \
16 $(top_builddir)/src/hello/libgnunethello.la \
17 $(top_builddir)/src/util/libgnunetutil.la
18libgnunetpeerinfo_la_LDFLAGS = \
19 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
20 -version-info 0:0:0
21
22
23bin_PROGRAMS = \
24 gnunet-peerinfo \
25 gnunet-service-peerinfo
26
27gnunet_peerinfo_SOURCES = \
28 gnunet-peerinfo.c
29gnunet_peerinfo_LDADD = \
30 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
31 $(top_builddir)/src/util/libgnunetutil.la
32
33gnunet_service_peerinfo_SOURCES = \
34 gnunet-service-peerinfo.c
35gnunet_service_peerinfo_LDADD = \
36 $(top_builddir)/src/hello/libgnunethello.la \
37 $(top_builddir)/src/util/libgnunetutil.la
38
39
40check_PROGRAMS = \
41 test_peerinfo_api
42
43TESTS = $(check_PROGRAMS) # $(check_SCRIPTS)
44
45test_peerinfo_api_SOURCES = \
46 test_peerinfo_api.c
47test_peerinfo_api_LDADD = \
48 $(top_builddir)/src/hello/libgnunethello.la \
49 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
50 $(top_builddir)/src/util/libgnunetutil.la
51
52EXTRA_DIST = \
53 test_peerinfo_api_data.conf
54
55#check_SCRIPTS = \
56# test_gnunet_peerinfo.sh
diff --git a/src/peerinfo/gnunet-peerinfo.c b/src/peerinfo/gnunet-peerinfo.c
new file mode 100644
index 000000000..6c737c88a
--- /dev/null
+++ b/src/peerinfo/gnunet-peerinfo.c
@@ -0,0 +1,152 @@
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/**
22 * @file peerinfo/gnunet-peerinfo.c
23 * @brief Print information about other known peers.
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_crypto_lib.h"
28#include "gnunet_configuration_lib.h"
29#include "gnunet_getopt_lib.h"
30#include "gnunet_peerinfo_service.h"
31#include "gnunet_program_lib.h"
32
33static int no_resolve;
34
35static int be_quiet;
36
37static int get_self;
38
39/**
40 * Print information about the peer.
41 * Currently prints the GNUNET_PeerIdentity, trust and the IP.
42 * Could of course do more (e.g. resolve via DNS).
43 */
44static void
45print_peer_info (void *cls,
46 const struct GNUNET_PeerIdentity *peer,
47 const struct GNUNET_HELLO_Message *hello, uint32_t trust)
48{
49 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
50
51 /* FIXME: add printing of address information!
52 => need extended transport API! */
53 GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
54 if (be_quiet)
55 printf ("%s\n", (const char *) &enc);
56 else
57 printf (_("Peer `%s' with trust %8u\n"), (const char *) &enc, trust);
58}
59
60/**
61 * Main function that will be run by the scheduler.
62 *
63 * @param cls closure
64 * @param sched the scheduler to use
65 * @param args remaining command-line arguments
66 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
67 * @param cfg configuration
68 */
69static void
70run (void *cls,
71 struct GNUNET_SCHEDULER_Handle *sched,
72 char *const *args,
73 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
74{
75 struct GNUNET_CRYPTO_RsaPrivateKey *priv;
76 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
77 struct GNUNET_PeerIdentity pid;
78 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
79 char *fn;
80
81 if (get_self != GNUNET_YES)
82 {
83 GNUNET_PEERINFO_for_all (cfg,
84 sched,
85 NULL,
86 0,
87 GNUNET_TIME_relative_multiply
88 (GNUNET_TIME_UNIT_SECONDS, 30),
89 &print_peer_info, NULL);
90 }
91 else
92 {
93 if (GNUNET_OK !=
94 GNUNET_CONFIGURATION_get_value_filename (cfg,
95 "GNUNET",
96 "HOSTKEYFILE", &fn))
97 return;
98 priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn);
99 if (priv == NULL)
100 {
101 fprintf (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
102 GNUNET_free (fn);
103 return;
104 }
105 GNUNET_free (fn);
106 GNUNET_CRYPTO_rsa_key_get_public (priv, &pub);
107 GNUNET_CRYPTO_rsa_key_free (priv);
108 GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
109 GNUNET_CRYPTO_hash_to_enc (&pid.hashPubKey, &enc);
110 if (be_quiet)
111 printf ("%s\n", (char *) &enc);
112 else
113 printf (_("I am peer `%s'.\n"), (const char *) &enc);
114 }
115}
116
117
118/**
119 * gnunet-peerinfo command line options
120 */
121static struct GNUNET_GETOPT_CommandLineOption options[] = {
122 {'n', "numeric", NULL,
123 gettext_noop ("don't resolve host names"),
124 0, &GNUNET_GETOPT_set_one, &no_resolve},
125 {'q', "quiet", NULL,
126 gettext_noop ("output only the identity strings"),
127 0, &GNUNET_GETOPT_set_one, &be_quiet},
128 {'s', "self", NULL,
129 gettext_noop ("output our own identity only"),
130 0, &GNUNET_GETOPT_set_one, &get_self},
131 GNUNET_GETOPT_OPTION_END
132};
133
134/**
135 * The main function to obtain peer information.
136 *
137 * @param argc number of arguments from the command line
138 * @param argv command line arguments
139 * @return 0 ok, 1 on error
140 */
141int
142main (int argc, char *const *argv)
143{
144 return (GNUNET_OK ==
145 GNUNET_PROGRAM_run (argc,
146 argv,
147 "gnunet-peerinfo",
148 gettext_noop ("Print information about peers."),
149 options, &run, NULL)) ? 0 : 1;
150}
151
152/* end of gnunet-peerinfo.c */
diff --git a/src/peerinfo/gnunet-service-peerinfo.c b/src/peerinfo/gnunet-service-peerinfo.c
new file mode 100644
index 000000000..b81c7b6ee
--- /dev/null
+++ b/src/peerinfo/gnunet-service-peerinfo.c
@@ -0,0 +1,708 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2004, 2005, 2007, 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 peerinfo/gnunet-service-peerinfo.c
23 * @brief maintains list of known peers
24 *
25 * Code to maintain the list of currently known hosts (in memory
26 * structure of data/hosts/ and data/credit/).
27 *
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_crypto_lib.h"
33#include "gnunet_disk_lib.h"
34#include "gnunet_hello_lib.h"
35#include "gnunet_protocols.h"
36#include "gnunet_service_lib.h"
37#include "peerinfo.h"
38
39/**
40 * How often do we scan the HOST_DIR for new entries?
41 */
42#define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
43
44/**
45 * How often do we flush trust values to disk?
46 */
47#define TRUST_FLUSH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
48
49/**
50 * How often do we discard old entries in data/hosts/?
51 */
52#define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
53
54/**
55 * In-memory cache of known hosts.
56 */
57struct HostEntry
58{
59
60 /**
61 * This is a linked list.
62 */
63 struct HostEntry *next;
64
65 /**
66 * Identity of the peer.
67 */
68 struct GNUNET_PeerIdentity identity;
69
70 /**
71 * Hello for the peer (can be NULL)
72 */
73 struct GNUNET_HELLO_Message *hello;
74
75 /**
76 * Trust rating for this peer
77 */
78 uint32_t trust;
79
80 /**
81 * Trust rating for this peer on disk.
82 */
83 uint32_t disk_trust;
84
85};
86
87/**
88 * The in-memory list of known hosts.
89 */
90static struct HostEntry *hosts;
91
92/**
93 * Directory where the hellos are stored in (data/hosts)
94 */
95static char *networkIdDirectory;
96
97/**
98 * Where do we store trust information?
99 */
100static char *trustDirectory;
101
102
103/**
104 * Address iterator that causes expired entries to be discarded.
105 *
106 * @param cls pointer to the current time
107 * @return GNUNET_NO if expiration smaller than the current time
108 */
109static int
110discard_expired (void *cls,
111 const char *tname,
112 struct GNUNET_TIME_Absolute expiration,
113 const void *addr, size_t addrlen)
114{
115 const struct GNUNET_TIME_Absolute *now = cls;
116 if (now->value > expiration.value)
117 return GNUNET_NO;
118 return GNUNET_OK;
119}
120
121
122/**
123 * Get the filename under which we would store the GNUNET_HELLO_Message
124 * for the given host and protocol.
125 * @return filename of the form DIRECTORY/HOSTID
126 */
127static char *
128get_host_filename (const struct GNUNET_PeerIdentity *id)
129{
130 struct GNUNET_CRYPTO_HashAsciiEncoded fil;
131 char *fn;
132
133 GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
134 GNUNET_asprintf (&fn,
135 "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR, &fil);
136 return fn;
137}
138
139
140/**
141 * Get the filename under which we would store the GNUNET_HELLO_Message
142 * for the given host and protocol.
143 * @return filename of the form DIRECTORY/HOSTID
144 */
145static char *
146get_trust_filename (const struct GNUNET_PeerIdentity *id)
147{
148 struct GNUNET_CRYPTO_HashAsciiEncoded fil;
149 char *fn;
150
151 GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
152 GNUNET_asprintf (&fn, "%s%s%s", trustDirectory, DIR_SEPARATOR_STR, &fil);
153 return fn;
154}
155
156/**
157 * Find the host entry for the given peer. Call
158 * only when synchronized!
159 * @return NULL if not found
160 */
161static struct HostEntry *
162lookup_host_entry (const struct GNUNET_PeerIdentity *id)
163{
164 struct HostEntry *pos;
165
166 pos = hosts;
167 while ((pos != NULL) &&
168 (0 !=
169 memcmp (id, &pos->identity, sizeof (struct GNUNET_PeerIdentity))))
170 pos = pos->next;
171 return pos;
172}
173
174
175/**
176 * Add a host to the list.
177 *
178 * @param identity the identity of the host
179 * @param protocol the protocol for the host
180 */
181static void
182add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
183{
184 struct HostEntry *entry;
185 char *fn;
186 uint32_t trust;
187 char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
188 const struct GNUNET_HELLO_Message *hello;
189 struct GNUNET_HELLO_Message *hello_clean;
190 int size;
191 struct GNUNET_TIME_Absolute now;
192
193 entry = lookup_host_entry (identity);
194 if (entry != NULL)
195 return;
196 entry = GNUNET_malloc (sizeof (struct HostEntry));
197 entry->identity = *identity;
198 fn = get_trust_filename (identity);
199 if ((GNUNET_DISK_file_test (fn) == GNUNET_YES) &&
200 (sizeof (trust) == GNUNET_DISK_file_read (fn, sizeof (trust), &trust)))
201 entry->disk_trust = entry->trust = ntohl (trust);
202 GNUNET_free (fn);
203
204 fn = get_host_filename (identity);
205 if (GNUNET_DISK_file_test (fn) == GNUNET_YES)
206 {
207 size = GNUNET_DISK_file_read (fn, sizeof (buffer), buffer);
208 hello = (const struct GNUNET_HELLO_Message *) buffer;
209 now = GNUNET_TIME_absolute_get ();
210 hello_clean = GNUNET_HELLO_iterate_addresses (hello,
211 GNUNET_YES,
212 &discard_expired, &now);
213 entry->hello = hello_clean;
214 }
215 GNUNET_free (fn);
216 entry->next = hosts;
217 hosts = entry;
218}
219
220
221/**
222 * Increase the host credit by a value.
223 *
224 * @param hostId is the identity of the host
225 * @param value is the int value by which the
226 * host credit is to be increased or decreased
227 * @returns the actual change in trust (positive or negative)
228 */
229static int
230change_host_trust (const struct GNUNET_PeerIdentity *hostId, int value)
231{
232 struct HostEntry *host;
233
234 if (value == 0)
235 return 0;
236 host = lookup_host_entry (hostId);
237 if (host == NULL)
238 {
239 add_host_to_known_hosts (hostId);
240 host = lookup_host_entry (hostId);
241 }
242 GNUNET_assert (host != NULL);
243 if (value > 0)
244 {
245 if (host->trust + value < host->trust)
246 {
247 value = ((uint32_t) - 1) - host->trust;
248 host->trust = (uint32_t) - 1; /* maximized */
249 }
250 else
251 host->trust += value;
252 }
253 else
254 {
255 if (host->trust < -value)
256 {
257 value = -host->trust;
258 host->trust = 0;
259 }
260 else
261 host->trust += value;
262 }
263 return value;
264}
265
266
267/**
268 * Remove a file that should not be there. LOG
269 * success or failure.
270 */
271static void
272remove_garbage (const char *fullname)
273{
274 if (0 == UNLINK (fullname))
275 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
276 _
277 ("File `%s' in directory `%s' does not match naming convention. "
278 "Removed.\n"), fullname, networkIdDirectory);
279 else
280 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
281 GNUNET_ERROR_TYPE_BULK, "unlink", fullname);
282}
283
284
285static int
286hosts_directory_scan_callback (void *cls, const char *fullname)
287{
288 unsigned int *matched = cls;
289 struct GNUNET_PeerIdentity identity;
290 const char *filename;
291
292 if (GNUNET_DISK_file_test (fullname) != GNUNET_YES)
293 return GNUNET_OK; /* ignore non-files */
294 if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
295 {
296 remove_garbage (fullname);
297 return GNUNET_OK;
298 }
299 filename =
300 &fullname[strlen (fullname) -
301 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1];
302 if (filename[-1] != DIR_SEPARATOR)
303 {
304 remove_garbage (fullname);
305 return GNUNET_OK;
306 }
307 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (filename,
308 &identity.hashPubKey))
309 {
310 remove_garbage (fullname);
311 return GNUNET_OK;
312 }
313 (*matched)++;
314 add_host_to_known_hosts (&identity);
315 return GNUNET_OK;
316}
317
318
319/**
320 * Call this method periodically to scan data/hosts for new hosts.
321 */
322static void
323cron_scan_directory_data_hosts (void *cls,
324 const struct GNUNET_SCHEDULER_TaskContext *tc)
325{
326 static unsigned int retries;
327 unsigned int count;
328
329 count = 0;
330 GNUNET_DISK_directory_scan (networkIdDirectory,
331 &hosts_directory_scan_callback, &count);
332 if ((0 == count) && (0 == (++retries & 31)))
333 GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
334 GNUNET_ERROR_TYPE_BULK,
335 _("Still no peers found in `%s'!\n"), networkIdDirectory);
336 GNUNET_SCHEDULER_add_delayed (tc->sched,
337 GNUNET_NO,
338 GNUNET_SCHEDULER_PRIORITY_KEEP,
339 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
340 DATA_HOST_FREQ,
341 &cron_scan_directory_data_hosts, NULL);
342}
343
344
345/**
346 * Bind a host address (hello) to a hostId.
347 *
348 * @param peer the peer for which this is a hello
349 * @param hello the verified (!) hello message
350 */
351static void
352bind_address (const struct GNUNET_PeerIdentity *peer,
353 const struct GNUNET_HELLO_Message *hello)
354{
355 char *fn;
356 struct HostEntry *host;
357 struct GNUNET_HELLO_Message *mrg;
358
359 add_host_to_known_hosts (peer);
360 host = lookup_host_entry (peer);
361 GNUNET_assert (host != NULL);
362 if (host->hello == NULL)
363 {
364 host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
365 memcpy (host->hello, hello, GNUNET_HELLO_size (hello));
366 }
367 else
368 {
369 mrg = GNUNET_HELLO_merge (host->hello, hello);
370 GNUNET_free (host->hello);
371 host->hello = mrg;
372 }
373 fn = get_host_filename (peer);
374 GNUNET_DISK_file_write (fn, host->hello, GNUNET_HELLO_size (hello), "644");
375 GNUNET_free (fn);
376}
377
378
379/**
380 * Do transmit info either for only the host matching the given
381 * argument or for all known hosts and change their trust values by
382 * the given delta.
383 *
384 * @param only NULL to hit all hosts
385 */
386static void
387send_to_each_host (const struct GNUNET_PeerIdentity *only,
388 int trust_change,
389 struct GNUNET_SERVER_Client *client,
390 struct GNUNET_SERVER_Handle *server)
391{
392 struct HostEntry *pos;
393 struct InfoMessage *im;
394 const struct GNUNET_MessageHeader *end;
395 uint16_t hs;
396 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
397 struct GNUNET_SERVER_TransmitContext *tc;
398
399 tc = GNUNET_SERVER_transmit_context_create (client);
400 pos = hosts;
401 while (pos != NULL)
402 {
403 if ((only == NULL) ||
404 (0 ==
405 memcmp (only, &pos->identity,
406 sizeof (struct GNUNET_PeerIdentity))))
407 {
408 change_host_trust (&pos->identity, trust_change);
409 hs = 0;
410 im = (struct InfoMessage *) buf;
411 if (pos->hello != NULL)
412 {
413 hs = GNUNET_HELLO_size (pos->hello);
414 GNUNET_assert (hs <
415 GNUNET_SERVER_MAX_MESSAGE_SIZE -
416 sizeof (struct InfoMessage));
417 memcpy (&im[1], pos->hello, hs);
418 }
419 im->trust = htonl (pos->trust);
420 im->peer = pos->identity;
421 end = &im->header;
422 GNUNET_SERVER_transmit_context_append (tc,
423 &end[1],
424 hs +
425 sizeof (struct InfoMessage) -
426 sizeof (struct
427 GNUNET_MessageHeader),
428 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
429 }
430 pos = pos->next;
431 }
432 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
433 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
434 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
435}
436
437
438/**
439 * Write host-trust information to a file - flush the buffer entry!
440 * Assumes synchronized access.
441 */
442static void
443flush_trust (struct HostEntry *host)
444{
445 char *fn;
446 uint32_t trust;
447
448 if (host->trust == host->disk_trust)
449 return; /* unchanged */
450 fn = get_trust_filename (&host->identity);
451 if (host->trust == 0)
452 {
453 if ((0 != UNLINK (fn)) && (errno != ENOENT))
454 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
455 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
456 }
457 else
458 {
459 trust = htonl (host->trust);
460 if (GNUNET_OK ==
461 GNUNET_DISK_file_write (fn, &trust, sizeof (uint32_t), "644"))
462 host->disk_trust = host->trust;
463 }
464 GNUNET_free (fn);
465}
466
467/**
468 * Call this method periodically to scan data/hosts for new hosts.
469 */
470static void
471cron_flush_trust (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
472{
473 struct HostEntry *pos;
474
475 pos = hosts;
476 while (pos != NULL)
477 {
478 flush_trust (pos);
479 pos = pos->next;
480 }
481 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
482 GNUNET_SCHEDULER_add_delayed (tc->sched,
483 GNUNET_YES,
484 GNUNET_SCHEDULER_PRIORITY_KEEP,
485 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
486 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
487}
488
489
490/**
491 * @brief delete expired HELLO entries in data/hosts/
492 */
493static int
494discard_hosts_helper (void *cls, const char *fn)
495{
496 struct GNUNET_TIME_Absolute *now = cls;
497 char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
498 const struct GNUNET_HELLO_Message *hello;
499 struct GNUNET_HELLO_Message *new_hello;
500 int size;
501
502 size = GNUNET_DISK_file_read (fn, sizeof (buffer), buffer);
503 if ((size < sizeof (struct GNUNET_MessageHeader)) && (0 != UNLINK (fn)))
504 {
505 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
506 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
507 return GNUNET_OK;
508 }
509 hello = (const struct GNUNET_HELLO_Message *) buffer;
510 new_hello = GNUNET_HELLO_iterate_addresses (hello,
511 GNUNET_YES,
512 &discard_expired, now);
513 if ((new_hello == NULL) && (0 != UNLINK (fn)))
514 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
515 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
516 if (new_hello != NULL)
517 {
518 GNUNET_DISK_file_write (fn,
519 new_hello,
520 GNUNET_HELLO_size (new_hello), "644");
521 GNUNET_free (new_hello);
522 }
523 return GNUNET_OK;
524}
525
526
527/**
528 * Call this method periodically to scan data/hosts for new hosts.
529 */
530static void
531cron_clean_data_hosts (void *cls,
532 const struct GNUNET_SCHEDULER_TaskContext *tc)
533{
534 struct GNUNET_TIME_Absolute now;
535
536 now = GNUNET_TIME_absolute_get ();
537 GNUNET_DISK_directory_scan (networkIdDirectory,
538 &discard_hosts_helper, &now);
539
540 GNUNET_SCHEDULER_add_delayed (tc->sched,
541 GNUNET_NO,
542 GNUNET_SCHEDULER_PRIORITY_KEEP,
543 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
544 DATA_HOST_CLEAN_FREQ,
545 &cron_clean_data_hosts, NULL);
546}
547
548
549/**
550 * Handle ADD-message.
551 *
552 * @param cls closure
553 * @param server the server handling the message
554 * @param client identification of the client
555 * @param message the actual message
556 */
557static void
558handle_add (void *cls,
559 struct GNUNET_SERVER_Handle *server,
560 struct GNUNET_SERVER_Client *client,
561 const struct GNUNET_MessageHeader *message)
562{
563 const struct PeerAddMessage *pam;
564 const struct GNUNET_MessageHeader *hello;
565 uint16_t size;
566
567 size = ntohs (message->size);
568 if (size <
569 sizeof (struct PeerAddMessage) + sizeof (struct GNUNET_MessageHeader))
570 {
571 GNUNET_break (0);
572 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
573 return;
574 }
575 pam = (const struct PeerAddMessage *) message;
576 hello = (const struct GNUNET_MessageHeader *) &pam[1];
577 if (size != sizeof (struct PeerAddMessage) + ntohs (hello->size))
578 {
579 GNUNET_break (0);
580 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
581 return;
582 }
583 bind_address (&pam->peer, (const struct GNUNET_HELLO_Message *) hello);
584 GNUNET_SERVER_receive_done (client, GNUNET_OK);
585}
586
587
588/**
589 * Handle GET-message.
590 *
591 * @param cls closure
592 * @param server the server handling the message
593 * @param client identification of the client
594 * @param message the actual message
595 */
596static void
597handle_get (void *cls,
598 struct GNUNET_SERVER_Handle *server,
599 struct GNUNET_SERVER_Client *client,
600 const struct GNUNET_MessageHeader *message)
601{
602 const struct ListPeerMessage *lpm;
603
604 lpm = (const struct ListPeerMessage *) message;
605 send_to_each_host (&lpm->peer, ntohl (lpm->trust_change), client, server);
606}
607
608
609/**
610 * Handle GET-ALL-message.
611 *
612 * @param cls closure
613 * @param server the server handling the message
614 * @param client identification of the client
615 * @param message the actual message
616 */
617static void
618handle_get_all (void *cls,
619 struct GNUNET_SERVER_Handle *server,
620 struct GNUNET_SERVER_Client *client,
621 const struct GNUNET_MessageHeader *message)
622{
623 const struct ListAllPeersMessage *lpm;
624
625 lpm = (const struct ListAllPeersMessage *) message;
626 send_to_each_host (NULL, ntohl (lpm->trust_change), client, server);
627}
628
629
630/**
631 * List of handlers for the messages understood by this
632 * service.
633 */
634static struct GNUNET_SERVER_MessageHandler handlers[] = {
635 {&handle_add, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_ADD, 0},
636 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
637 sizeof (struct ListPeerMessage)},
638 {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
639 sizeof (struct ListAllPeersMessage)},
640 {NULL, NULL, 0, 0}
641};
642
643
644/**
645 * Process statistics requests.
646 *
647 * @param cls closure
648 * @param sched scheduler to use
649 * @param server the initialized server
650 * @param cfg configuration to use
651 */
652static void
653run (void *cls,
654 struct GNUNET_SCHEDULER_Handle *sched,
655 struct GNUNET_SERVER_Handle *server,
656 struct GNUNET_CONFIGURATION_Handle *cfg)
657{
658 GNUNET_assert (GNUNET_OK ==
659 GNUNET_CONFIGURATION_get_value_filename (cfg,
660 "peerinfo",
661 "HOSTS",
662 &networkIdDirectory));
663 GNUNET_assert (GNUNET_OK ==
664 GNUNET_CONFIGURATION_get_value_filename (cfg,
665 "peerinfo",
666 "TRUST",
667 &trustDirectory));
668 GNUNET_DISK_directory_create (networkIdDirectory);
669 GNUNET_DISK_directory_create (trustDirectory);
670 GNUNET_SCHEDULER_add_delayed (sched,
671 GNUNET_NO,
672 GNUNET_SCHEDULER_PRIORITY_IDLE,
673 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
674 GNUNET_TIME_UNIT_MILLISECONDS,
675 &cron_scan_directory_data_hosts, NULL);
676 GNUNET_SCHEDULER_add_delayed (sched,
677 GNUNET_YES,
678 GNUNET_SCHEDULER_PRIORITY_HIGH,
679 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
680 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
681 GNUNET_SCHEDULER_add_delayed (sched,
682 GNUNET_NO,
683 GNUNET_SCHEDULER_PRIORITY_IDLE,
684 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
685 DATA_HOST_CLEAN_FREQ,
686 &cron_clean_data_hosts, NULL);
687 GNUNET_SERVER_add_handlers (server, handlers);
688}
689
690
691/**
692 * The main function for the statistics service.
693 *
694 * @param argc number of arguments from the command line
695 * @param argv command line arguments
696 * @return 0 ok, 1 on error
697 */
698int
699main (int argc, char *const *argv)
700{
701 return (GNUNET_OK ==
702 GNUNET_SERVICE_run (argc,
703 argv,
704 "peerinfo", &run, NULL, NULL, NULL)) ? 0 : 1;
705}
706
707
708/* end of gnunet-service-peerinfo.c */
diff --git a/src/peerinfo/peerinfo.h b/src/peerinfo/peerinfo.h
new file mode 100644
index 000000000..040b084e4
--- /dev/null
+++ b/src/peerinfo/peerinfo.h
@@ -0,0 +1,135 @@
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 peerinfo/peerinfo.h
23 * @brief common internal definitions for peerinfo service
24 * @author Christian Grothoff
25 */
26#include "gnunet_crypto_lib.h"
27#include "gnunet_time_lib.h"
28#include "gnunet_peerinfo_service.h"
29
30
31/**
32 * Add the given peer to the list. This message
33 * is always followed by a verified HELLO message.
34 */
35struct PeerAddMessage
36{
37
38 /**
39 * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_ADD
40 */
41 struct GNUNET_MessageHeader header;
42
43 /**
44 * Always zero.
45 */
46 uint32_t reserved GNUNET_PACKED;
47
48 /**
49 * For which peer do we provide a HELLO message here?
50 */
51 struct GNUNET_PeerIdentity peer;
52
53};
54
55
56/**
57 * Message requesting a listing of all known peers,
58 * possibly modified by the specified trust value
59 * and restricted to the specified peer identity.
60 */
61struct ListPeerMessage
62{
63
64 /**
65 * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_GET
66 */
67 struct GNUNET_MessageHeader header;
68
69 /**
70 * How much to change the trust in each returned peer,
71 * in network byte order.
72 */
73 int32_t trust_change GNUNET_PACKED;
74
75 /**
76 * Restrict to peers with this identity (optional
77 * field, check header.size!).
78 */
79 struct GNUNET_PeerIdentity peer;
80
81};
82
83
84/**
85 * Message requesting a listing of all known peers,
86 * possibly modified by the specified trust value
87 * and restricted to the specified peer identity.
88 */
89struct ListAllPeersMessage
90{
91
92 /**
93 * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_GET
94 */
95 struct GNUNET_MessageHeader header;
96
97 /**
98 * How much to change the trust in each returned peer,
99 * in network byte order.
100 */
101 int32_t trust_change GNUNET_PACKED;
102
103};
104
105
106/**
107 * Message used to inform the client about
108 * a particular peer; this message is optionally followed
109 * by a HELLO message for the respective peer (if available).
110 * Check the header.size field to see if a HELLO is
111 * present.
112 */
113struct InfoMessage
114{
115
116 /**
117 * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_INFO
118 */
119 struct GNUNET_MessageHeader header;
120
121 /**
122 * Amount of trust we now have in the peer,
123 * in network byte order.
124 */
125 uint32_t trust GNUNET_PACKED;
126
127 /**
128 * About which peer are we talking here?
129 */
130 struct GNUNET_PeerIdentity peer;
131
132};
133
134
135/* end of peerinfo.h */
diff --git a/src/peerinfo/peerinfo_api.c b/src/peerinfo/peerinfo_api.c
new file mode 100644
index 000000000..ba5ade199
--- /dev/null
+++ b/src/peerinfo/peerinfo_api.c
@@ -0,0 +1,302 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2004, 2005, 2007, 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 peerinfo/peerinfo_api.c
23 * @brief API to access peerinfo service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_client_lib.h"
28#include "gnunet_peerinfo_service.h"
29#include "gnunet_protocols.h"
30#include "gnunet_time_lib.h"
31#include "peerinfo.h"
32
33#define ADD_PEER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
34
35
36struct CAFContext
37{
38 struct GNUNET_CLIENT_Connection *client;
39 struct GNUNET_MessageHeader *msg;
40};
41
42
43static size_t
44copy_and_free (void *cls, size_t size, void *buf)
45{
46 struct CAFContext *cc = cls;
47 struct GNUNET_MessageHeader *msg = cc->msg;
48 uint16_t msize;
49
50 if (buf == NULL)
51 {
52 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
53 _
54 ("Failed to transmit message of type %u to `%s' service.\n"),
55 ntohs (msg->type), "peerinfo");
56 GNUNET_free (msg);
57 GNUNET_CLIENT_disconnect (cc->client);
58 GNUNET_free (cc);
59 return 0;
60 }
61 msize = ntohs (msg->size);
62 GNUNET_assert (size >= msize);
63 memcpy (buf, msg, msize);
64 GNUNET_free (msg);
65 GNUNET_CLIENT_disconnect (cc->client);
66 GNUNET_free (cc);
67 return msize;
68}
69
70
71
72/**
73 * Add a host to the persistent list.
74 *
75 * @param cfg configuration to use
76 * @param sched scheduler to use
77 * @param peer identity of the peer
78 * @param hello the verified (!) HELLO message
79 * @param expiration when the HELLO will expire
80 */
81void
82GNUNET_PEERINFO_add_peer (struct GNUNET_CONFIGURATION_Handle *cfg,
83 struct GNUNET_SCHEDULER_Handle *sched,
84 const struct GNUNET_PeerIdentity *peer,
85 const struct GNUNET_HELLO_Message *hello)
86{
87 struct GNUNET_CLIENT_Connection *client;
88 struct PeerAddMessage *pam;
89 uint16_t hs;
90 struct CAFContext *cc;
91
92 client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
93 if (client == NULL)
94 {
95 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
96 _("Could not connect to `%s' service.\n"), "peerinfo");
97 return;
98 }
99 hs = GNUNET_HELLO_size (hello);
100 pam = GNUNET_malloc (sizeof (struct PeerAddMessage) + hs);
101 pam->header.size = htons (hs + sizeof (struct PeerAddMessage));
102 pam->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_ADD);
103 memcpy (&pam->peer, peer, sizeof (struct GNUNET_PeerIdentity));
104 memcpy (&pam[1], hello, hs);
105 cc = GNUNET_malloc (sizeof (struct CAFContext));
106 cc->client = client;
107 cc->msg = &pam->header;
108 GNUNET_CLIENT_notify_transmit_ready (client,
109 ntohs (pam->header.size),
110 ADD_PEER_TIMEOUT, &copy_and_free, cc);
111}
112
113
114/**
115 * Context for the info handler.
116 */
117struct InfoContext
118{
119
120 /**
121 * Our connection to the PEERINFO service.
122 */
123 struct GNUNET_CLIENT_Connection *client;
124
125 /**
126 * Function to call with information.
127 */
128 GNUNET_PEERINFO_Processor callback;
129
130 /**
131 * Closure for callback.
132 */
133 void *callback_cls;
134
135 /**
136 * When should we time out?
137 */
138 struct GNUNET_TIME_Absolute timeout;
139
140};
141
142
143/**
144 * Type of a function to call when we receive a message
145 * from the service.
146 *
147 * @param cls closure
148 * @param msg message received, NULL on timeout or fatal error
149 */
150static void
151info_handler (void *cls, const struct GNUNET_MessageHeader *msg)
152{
153 struct InfoContext *ic = cls;
154 const struct InfoMessage *im;
155 const struct GNUNET_HELLO_Message *hello;
156 uint16_t ms;
157
158 if (msg == NULL)
159 {
160 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
161 _("Failed to receive response from `%s' service.\n"),
162 "peerinfo");
163 ic->callback (ic->callback_cls, NULL, NULL, 1);
164 GNUNET_CLIENT_disconnect (ic->client);
165 GNUNET_free (ic);
166 return;
167 }
168 if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
169 {
170 ic->callback (ic->callback_cls, NULL, NULL, 0);
171 GNUNET_CLIENT_disconnect (ic->client);
172 GNUNET_free (ic);
173 return;
174 }
175 ms = ntohs (msg->size);
176 if ((ms < sizeof (struct InfoMessage)) ||
177 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
178 {
179 GNUNET_break (0);
180 ic->callback (ic->callback_cls, NULL, NULL, 2);
181 GNUNET_CLIENT_disconnect (ic->client);
182 GNUNET_free (ic);
183 return;
184 }
185 im = (const struct InfoMessage *) msg;
186 hello = NULL;
187 if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
188 {
189 hello = (const struct GNUNET_HELLO_Message *) &im[1];
190 if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
191 {
192 GNUNET_break (0);
193 ic->callback (ic->callback_cls, NULL, NULL, 2);
194 GNUNET_CLIENT_disconnect (ic->client);
195 GNUNET_free (ic);
196 return;
197 }
198 }
199 ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust));
200 GNUNET_CLIENT_receive (ic->client,
201 &info_handler,
202 ic,
203 GNUNET_TIME_absolute_get_remaining (ic->timeout));
204}
205
206
207static size_t
208copy_then_receive (void *cls, size_t size, void *buf)
209{
210 struct InfoContext *ic = cls;
211 const struct GNUNET_MessageHeader *msg =
212 (const struct GNUNET_MessageHeader *) &ic[1];
213 uint16_t msize;
214
215 if (buf == NULL)
216 {
217 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
218 _
219 ("Failed to transmit message of type %u to `%s' service.\n"),
220 ntohs (msg->type), "peerinfo");
221 ic->callback (ic->callback_cls, NULL, NULL, 1);
222 GNUNET_CLIENT_disconnect (ic->client);
223 GNUNET_free (ic);
224 return 0;
225 }
226 msize = ntohs (msg->size);
227 GNUNET_assert (size >= msize);
228 memcpy (buf, msg, msize);
229 GNUNET_CLIENT_receive (ic->client,
230 &info_handler,
231 ic,
232 GNUNET_TIME_absolute_get_remaining (ic->timeout));
233 return msize;
234}
235
236
237/**
238 * Call a method for each known matching host and change
239 * its trust value. The method will be invoked once for
240 * each host and then finally once with a NULL pointer.
241 * Note that the last call can be triggered by timeout or
242 * by simply being done; however, the trust argument will
243 * be set to zero if we are done and to 1 if we timed out.
244 *
245 * @param cfg configuration to use
246 * @param sched scheduler to use
247 * @param peer restrict iteration to this peer only (can be NULL)
248 * @param trust_delta how much to change the trust in all matching peers
249 * @param timeout how long to wait until timing out
250 * @param callback the method to call for each peer
251 * @param callback_cls closure for callback
252 */
253void
254GNUNET_PEERINFO_for_all (struct GNUNET_CONFIGURATION_Handle *cfg,
255 struct GNUNET_SCHEDULER_Handle *sched,
256 const struct GNUNET_PeerIdentity *peer,
257 int trust_delta,
258 struct GNUNET_TIME_Relative timeout,
259 GNUNET_PEERINFO_Processor callback,
260 void *callback_cls)
261{
262 struct GNUNET_CLIENT_Connection *client;
263 struct ListAllPeersMessage *lapm;
264 struct ListPeerMessage *lpm;
265 size_t hs;
266 struct InfoContext *ihc;
267
268 client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
269 if (client == NULL)
270 {
271 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
272 _("Could not connect to `%s' service.\n"), "peerinfo");
273 callback (callback_cls, NULL, NULL, 2);
274 return;
275 }
276 ihc = GNUNET_malloc (sizeof (struct InfoContext) +
277 sizeof (struct ListPeerMessage));
278 ihc->client = client;
279 ihc->callback = callback;
280 ihc->callback_cls = callback_cls;
281 ihc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
282 hs = 0;
283 if (peer == NULL)
284 {
285 lapm = (struct ListAllPeersMessage *) &ihc[1];
286 lapm->header.size = htons (hs = sizeof (struct ListAllPeersMessage));
287 lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
288 lapm->trust_change = htonl (trust_delta);
289 }
290 else
291 {
292 lpm = (struct ListPeerMessage *) &ihc[1];
293 lpm->header.size = htons (hs = sizeof (struct ListPeerMessage));
294 lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
295 lpm->trust_change = htonl (trust_delta);
296 memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
297 }
298 GNUNET_CLIENT_notify_transmit_ready (client,
299 hs, timeout, &copy_then_receive, ihc);
300}
301
302/* end of peerinfo_api.c */
diff --git a/src/peerinfo/test_peerinfo_api.c b/src/peerinfo/test_peerinfo_api.c
new file mode 100644
index 000000000..e7d632fd5
--- /dev/null
+++ b/src/peerinfo/test_peerinfo_api.c
@@ -0,0 +1,175 @@
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/**
22 * @file peerinfo/test_peerinfo_api.c
23 * @brief testcase for peerinfo_api.c
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - test merging of HELLOs (add same peer twice...)
28 */
29
30#include "platform.h"
31#include "gnunet_hello_lib.h"
32#include "gnunet_getopt_lib.h"
33#include "gnunet_os_lib.h"
34#include "gnunet_peerinfo_service.h"
35#include "gnunet_program_lib.h"
36#include "gnunet_time_lib.h"
37
38
39static int
40check_it (void *cls,
41 const char *tname,
42 struct GNUNET_TIME_Absolute expiration,
43 const void *addr, size_t addrlen)
44{
45 unsigned int *agc = cls;
46
47 if (addrlen > 0)
48 {
49 GNUNET_assert (0 == strcmp ("peerinfotest", tname));
50 GNUNET_assert (0 == strncmp ("Address", addr, addrlen));
51 (*agc) -= (1 << (addrlen - 1));
52 }
53 return GNUNET_OK;
54}
55
56
57static void
58process (void *cls,
59 const struct GNUNET_PeerIdentity *peer,
60 const struct GNUNET_HELLO_Message *hello, uint32_t trust)
61{
62 int *ok = cls;
63 unsigned int agc;
64
65 if (peer == NULL)
66 {
67 GNUNET_assert (peer == NULL);
68 GNUNET_assert (2 == *ok);
69 GNUNET_assert (trust == 0);
70 *ok = 0;
71 return;
72 }
73
74 if (hello != NULL)
75 {
76 GNUNET_assert (3 == *ok);
77 agc = 3;
78 GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &check_it, &agc);
79 GNUNET_assert (agc == 0);
80 *ok = 2;
81 }
82}
83
84
85static size_t
86address_generator (void *cls, size_t max, void *buf)
87{
88 size_t *agc = cls;
89 size_t ret;
90
91 if (0 == *agc)
92 return 0;
93 ret = GNUNET_HELLO_add_address ("peerinfotest",
94 GNUNET_TIME_relative_to_absolute
95 (GNUNET_TIME_UNIT_HOURS), "Address", *agc,
96 buf, max);
97 (*agc)--;
98 return ret;
99}
100
101
102static void
103run (void *cls,
104 struct GNUNET_SCHEDULER_Handle *sched,
105 char *const *args,
106 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
107{
108 struct GNUNET_HELLO_Message *hello;
109 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
110 size_t agc;
111 struct GNUNET_PeerIdentity pid;
112
113 memset (&pkey, 32, sizeof (pkey));
114 GNUNET_CRYPTO_hash (&pkey, sizeof (pkey), &pid.hashPubKey);
115 agc = 2;
116 hello = GNUNET_HELLO_create (&pkey, &address_generator, &agc);
117 GNUNET_assert (hello != NULL);
118 GNUNET_PEERINFO_add_peer (cfg, sched, &pid, hello);
119 GNUNET_PEERINFO_for_all (cfg,
120 sched,
121 NULL,
122 0,
123 GNUNET_TIME_relative_multiply
124 (GNUNET_TIME_UNIT_SECONDS, 15), &process, cls);
125 GNUNET_free (hello);
126}
127
128
129static int
130check ()
131{
132 int ok = 3;
133 pid_t pid;
134 char *const argv[] = { "test-peerinfo-api",
135 "-c",
136 "test_peerinfo_api_data.conf",
137#if DEBUG_PEERINFO
138 "-L", "DEBUG",
139#endif
140 NULL
141 };
142 struct GNUNET_GETOPT_CommandLineOption options[] = {
143 GNUNET_GETOPT_OPTION_END
144 };
145 pid = GNUNET_OS_start_process ("gnunet-service-peerinfo",
146 "gnunet-service-peerinfo",
147#if DEBUG_PEERINFO
148 "-L", "DEBUG",
149#endif
150 "-c", "test_peerinfo_api_data.conf", NULL);
151 sleep (1);
152 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
153 argv, "test-peerinfo-api", "nohelp",
154 options, &run, &ok);
155 if (0 != PLIBC_KILL (pid, SIGTERM))
156 {
157 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
158 ok = 1;
159 }
160 waitpid (pid, NULL, 0);
161 return ok;
162}
163
164
165int
166main (int argc, char *argv[])
167{
168 int ret = 0;
169
170 ret = check ();
171
172 return ret;
173}
174
175/* end of test_peerinfo_api.c */
diff --git a/src/peerinfo/test_peerinfo_api_data.conf b/src/peerinfo/test_peerinfo_api_data.conf
new file mode 100644
index 000000000..a81ffccb9
--- /dev/null
+++ b/src/peerinfo/test_peerinfo_api_data.conf
@@ -0,0 +1,5 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-peerinfo/
3
4[peerinfo]
5PORT = 22354
diff --git a/src/resolver/Makefile.am b/src/resolver/Makefile.am
new file mode 100644
index 000000000..20c07f4da
--- /dev/null
+++ b/src/resolver/Makefile.am
@@ -0,0 +1,46 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11
12lib_LTLIBRARIES = libgnunetresolver.la
13
14libgnunetresolver_la_SOURCES = \
15 resolver_api.c resolver.h
16libgnunetresolver_la_LIBADD = \
17 $(top_builddir)/src/util/libgnunetutil.la \
18 $(GN_LIBINTL)
19libgnunetresolver_la_LDFLAGS = \
20 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
21 -version-info 0:0:0
22
23
24bin_PROGRAMS = \
25 gnunet-service-resolver
26
27gnunet_service_resolver_SOURCES = \
28 gnunet-service-resolver.c
29gnunet_service_resolver_LDADD = \
30 $(top_builddir)/src/util/libgnunetutil.la \
31 $(GN_LIBINTL)
32
33
34check_PROGRAMS = \
35 test_resolver_api
36
37TESTS = $(check_PROGRAMS)
38
39test_resolver_api_SOURCES = \
40 test_resolver_api.c
41test_resolver_api_LDADD = \
42 $(top_builddir)/src/resolver/libgnunetresolver.la \
43 $(top_builddir)/src/util/libgnunetutil.la
44
45EXTRA_DIST = \
46 test_resolver_api_data.conf
diff --git a/src/resolver/gnunet-service-resolver.c b/src/resolver/gnunet-service-resolver.c
new file mode 100644
index 000000000..dbdecdfe5
--- /dev/null
+++ b/src/resolver/gnunet-service-resolver.c
@@ -0,0 +1,483 @@
1/*
2 This file is part of GNUnet.
3 (C) 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 resolver/gnunet-service-resolver.c
23 * @brief code to do DNS resolution
24 * @author Christian Grothoff
25 */
26
27#include <stdlib.h>
28#include "platform.h"
29#include "gnunet_disk_lib.h"
30#include "gnunet_getopt_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_service_lib.h"
33#include "gnunet_statistics_service.h"
34#include "gnunet_strings_lib.h"
35#include "gnunet_time_lib.h"
36#include "resolver.h"
37
38
39struct IPCache
40{
41 struct IPCache *next;
42 char *addr;
43 struct sockaddr *sa;
44 struct GNUNET_TIME_Absolute last_refresh;
45 struct GNUNET_TIME_Absolute last_request;
46 unsigned int salen;
47};
48
49
50static struct IPCache *head;
51
52
53
54
55#if HAVE_GETNAMEINFO
56static void
57getnameinfo_resolve (struct IPCache *cache)
58{
59 char hostname[256];
60
61 if (0 == getnameinfo (cache->sa, cache->salen, hostname, 255, NULL, 0, 0))
62 cache->addr = GNUNET_strdup (hostname);
63}
64#endif
65
66
67#if HAVE_GETHOSTBYADDR
68static void
69gethostbyaddr_resolve (struct IPCache *cache)
70{
71 struct hostent *ent;
72
73 switch (cache->sa->sa_family)
74 {
75 case AF_INET:
76 ent = gethostbyaddr (&((struct sockaddr_in *) cache->sa)->sin_addr,
77 sizeof (struct in_addr), AF_INET);
78 break;
79 case AF_INET6:
80 ent = gethostbyaddr (&((struct sockaddr_in6 *) cache->sa)->sin6_addr,
81 sizeof (struct in6_addr), AF_INET6);
82 break;
83 default:
84 ent = NULL;
85 }
86 if (ent != NULL)
87 cache->addr = GNUNET_strdup (ent->h_name);
88}
89#endif
90
91
92static void
93cache_resolve (struct IPCache *cache)
94{
95#if HAVE_GETNAMEINFO
96 if (cache->addr == NULL)
97 getnameinfo_resolve (cache);
98#endif
99#if HAVE_GETHOSTBYADDR
100 if (cache->addr == NULL)
101 gethostbyaddr_resolve (cache);
102#endif
103}
104
105
106
107/**
108 * Get an IP address as a string (works for both IPv4 and IPv6). Note
109 * that the resolution happens asynchronously and that the first call
110 * may not immediately result in the FQN (but instead in a
111 * human-readable IP address).
112 *
113 * @param sa should be of type "struct sockaddr*"
114 */
115static void
116get_ip_as_string (struct GNUNET_SERVER_Client *client,
117 const struct sockaddr *sav, socklen_t salen)
118{
119 struct IPCache *cache;
120 struct IPCache *prev;
121 struct GNUNET_TIME_Absolute now;
122 struct GNUNET_SERVER_TransmitContext *tc;
123
124 if (salen < sizeof (struct sockaddr))
125 {
126 GNUNET_break (0);
127 return;
128 }
129 now = GNUNET_TIME_absolute_get ();
130 cache = head;
131 prev = NULL;
132 while ((cache != NULL) &&
133 ((cache->salen != salen) || (0 != memcmp (cache->sa, sav, salen))))
134 {
135 if (GNUNET_TIME_absolute_get_duration (cache->last_request).value <
136 60 * 60 * 1000)
137 {
138 if (prev != NULL)
139 {
140 prev->next = cache->next;
141 GNUNET_free_non_null (cache->addr);
142 GNUNET_free (cache->sa);
143 GNUNET_free (cache);
144 cache = prev->next;
145 }
146 else
147 {
148 head = cache->next;
149 GNUNET_free_non_null (cache->addr);
150 GNUNET_free (cache->sa);
151 GNUNET_free (cache);
152 cache = head;
153 }
154 continue;
155 }
156 prev = cache;
157 cache = cache->next;
158 }
159 if (cache != NULL)
160 {
161 cache->last_request = now;
162 if (GNUNET_TIME_absolute_get_duration (cache->last_request).value <
163 60 * 60 * 1000)
164 {
165 GNUNET_free_non_null (cache->addr);
166 cache->addr = NULL;
167 cache->salen = 0;
168 cache_resolve (cache);
169 }
170 }
171 else
172 {
173 cache = GNUNET_malloc (sizeof (struct IPCache));
174 cache->next = head;
175 cache->salen = salen;
176 cache->sa = GNUNET_malloc (salen);
177 memcpy (cache->sa, sav, salen);
178 cache->last_request = GNUNET_TIME_absolute_get ();
179 cache->last_refresh = GNUNET_TIME_absolute_get ();
180 cache->addr = NULL;
181 cache_resolve (cache);
182 head = cache;
183 }
184 tc = GNUNET_SERVER_transmit_context_create (client);
185 if (cache->addr != NULL)
186 GNUNET_SERVER_transmit_context_append (tc,
187 cache->addr,
188 strlen (cache->addr) + 1,
189 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
190 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
191 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
192 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
193}
194
195
196#if HAVE_GETADDRINFO
197static int
198getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc,
199 const char *hostname, int domain)
200{
201 int s;
202 struct addrinfo hints;
203 struct addrinfo *result;
204 struct addrinfo *pos;
205
206 memset (&hints, 0, sizeof (struct addrinfo));
207// FIXME in PlibC
208#ifndef MINGW
209 hints.ai_family = domain;
210#else
211 hints.ai_family = AF_INET;
212#endif
213 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
214
215 if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result)))
216 {
217 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
218 _("Could not resolve `%s' (%s): %s\n"), hostname,
219 (domain ==
220 AF_INET) ? "IPv4" : ((domain ==
221 AF_INET6) ? "IPv6" : "any"),
222 gai_strerror (s));
223 if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY) || (s == EAI_SYSTEM))
224 return GNUNET_NO; /* other function may still succeed */
225 return GNUNET_SYSERR;
226 }
227 if (result == NULL)
228 return GNUNET_SYSERR;
229 pos = result;
230 while (pos != NULL)
231 {
232 GNUNET_SERVER_transmit_context_append (tc,
233 result->ai_addr,
234 result->ai_addrlen,
235 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
236 pos = pos->ai_next;
237 }
238 freeaddrinfo (result);
239 return GNUNET_OK;
240}
241#endif
242
243#if HAVE_GETHOSTBYNAME2
244static int
245gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
246 const char *hostname, int domain)
247{
248 struct hostent *hp;
249 struct sockaddr_in a4;
250 struct sockaddr_in6 a6;
251 int ret1;
252 int ret2;
253
254 if (domain == AF_UNSPEC)
255 {
256 ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
257 ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
258 if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
259 return GNUNET_OK;
260 if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
261 return GNUNET_SYSERR;
262 return GNUNET_NO;
263 }
264 hp = gethostbyname2 (hostname, domain);
265 if (hp == NULL)
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
268 _("Could not find IP of host `%s': %s\n"),
269 hostname, hstrerror (h_errno));
270 return GNUNET_SYSERR;
271 }
272 GNUNET_assert (hp->h_addrtype == domain);
273 if (domain == AF_INET)
274 {
275 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
276 memset (&a4, 0, sizeof (a4));
277 a4.sin_family = AF_INET;
278 memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length);
279 GNUNET_SERVER_transmit_context_append (tc,
280 &a4,
281 sizeof (a4),
282 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
283 }
284 else
285 {
286 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
287 memset (&a6, 0, sizeof (a6));
288 a6.sin6_family = AF_INET6;
289 memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length);
290 GNUNET_SERVER_transmit_context_append (tc,
291 &a6,
292 sizeof (a6),
293 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
294 }
295 return GNUNET_OK;
296}
297#endif
298
299#if HAVE_GETHOSTBYNAME
300static int
301gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
302 const char *hostname)
303{
304 struct hostent *hp;
305 struct sockaddr_in addr;
306
307 hp = GETHOSTBYNAME (hostname);
308 if (hp == NULL)
309 {
310 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
311 _("Could not find IP of host `%s': %s\n"),
312 hostname, hstrerror (h_errno));
313 return GNUNET_SYSERR;
314 }
315 if (hp->h_addrtype != AF_INET)
316 {
317 GNUNET_break (0);
318 return GNUNET_SYSERR;
319 }
320 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
321 memset (&addr, 0, sizeof (addr));
322 addr.sin_family = AF_INET;
323 memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length);
324 GNUNET_SERVER_transmit_context_append (tc,
325 &addr,
326 sizeof (addr),
327 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
328 return GNUNET_OK;
329}
330#endif
331
332
333/**
334 * Convert a string to an IP address.
335 *
336 * @param client where to send the IP address
337 * @param hostname the hostname to resolve
338 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
339 */
340static void
341get_ip_from_hostname (struct GNUNET_SERVER_Client *client,
342 const char *hostname, int domain)
343{
344 int ret;
345 struct GNUNET_SERVER_TransmitContext *tc;
346
347 tc = GNUNET_SERVER_transmit_context_create (client);
348 ret = GNUNET_NO;
349#if HAVE_GETADDRINFO
350 if (ret == GNUNET_NO)
351 ret = getaddrinfo_resolve (tc, hostname, domain);
352#endif
353#if HAVE_GETHOSTBYNAME2
354 if (ret == GNUNET_NO)
355 ret = gethostbyname2_resolve (tc, hostname, domain);
356#endif
357#if HAVE_GETHOSTBYNAME
358 if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET)))
359 gethostbyname_resolve (tc, hostname);
360#endif
361 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
362 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
363 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
364}
365
366
367/**
368 * Handle GET-message.
369 *
370 * @param cls closure
371 * @param server the server handling the message
372 * @param client identification of the client
373 * @param message the actual message
374 */
375static void
376handle_get (void *cls,
377 struct GNUNET_SERVER_Handle *server,
378 struct GNUNET_SERVER_Client *client,
379 const struct GNUNET_MessageHeader *message)
380{
381 uint16_t msize;
382 const struct GNUNET_RESOLVER_GetMessage *msg;
383 const char *hostname;
384 uint16_t size;
385 int direction;
386 int domain;
387
388 msize = ntohs (message->size);
389 if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
390 {
391 GNUNET_break (0);
392 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
393 return;
394 }
395 msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
396 size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
397 direction = ntohl (msg->direction);
398 domain = ntohl (msg->domain);
399 if (direction == GNUNET_NO)
400 {
401 /* IP from hostname */
402 hostname = (const char *) &msg[1];
403 if (hostname[size - 1] != '\0')
404 {
405 GNUNET_break (0);
406 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
407 return;
408 }
409#if DEBUG_RESOLVER
410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411 _("Resolver asked to look up `%s'.\n"), hostname);
412#endif
413 get_ip_from_hostname (client, hostname, domain);
414 }
415 else
416 {
417#if DEBUG_RESOLVER
418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419 _("Resolver asked to look up IP address.\n"));
420#endif
421 get_ip_as_string (client, (const struct sockaddr *) &msg[1], size);
422 }
423}
424
425
426/**
427 * List of handlers for the messages understood by this
428 * service.
429 */
430static struct GNUNET_SERVER_MessageHandler handlers[] = {
431 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
432 {NULL, NULL, 0, 0}
433};
434
435
436/**
437 * Process statistics requests.
438 *
439 * @param cls closure
440 * @param sched scheduler to use
441 * @param server the initialized server
442 * @param cfg configuration to use
443 */
444static void
445run (void *cls,
446 struct GNUNET_SCHEDULER_Handle *sched,
447 struct GNUNET_SERVER_Handle *server,
448 struct GNUNET_CONFIGURATION_Handle *cfg)
449{
450 GNUNET_SERVER_add_handlers (server, handlers);
451}
452
453
454/**
455 * The main function for the resolver service.
456 *
457 * @param argc number of arguments from the command line
458 * @param argv command line arguments
459 * @return 0 ok, 1 on error
460 */
461int
462main (int argc, char *const *argv)
463{
464 int ret;
465 struct IPCache *pos;
466
467 ret = (GNUNET_OK ==
468 GNUNET_SERVICE_run (argc,
469 argv,
470 "resolver", &run, NULL, NULL, NULL)) ? 0 : 1;
471
472 while (head != NULL)
473 {
474 pos = head->next;
475 GNUNET_free_non_null (head->addr);
476 GNUNET_free (head->sa);
477 GNUNET_free (head);
478 head = pos;
479 }
480 return ret;
481}
482
483/* end of gnunet-service-resolver.c */
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
new file mode 100644
index 000000000..31637f01d
--- /dev/null
+++ b/src/resolver/resolver.h
@@ -0,0 +1,63 @@
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 * @author Christian Grothoff
23 * @file resolver/resolver.h
24 */
25#ifndef RESOLVER_H
26#define RESOLVER_H
27
28#include "gnunet_common.h"
29
30#define DEBUG_RESOLVER GNUNET_NO
31
32/**
33 * Request for the resolver. Followed by either
34 * the "struct sockaddr" or the 0-terminated hostname.
35 *
36 * The response will be one or more messages of type
37 * RESOLVER_RESPONSE, each with the message header
38 * immediately followed by the requested data
39 * (hostname or struct sockaddr, depending on direction).
40 * The last RESOLVER_RESPONSE will just be a header
41 * without any data (used to indicate the end of the list).
42 */
43struct GNUNET_RESOLVER_GetMessage
44{
45 /**
46 * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE
47 */
48 struct GNUNET_MessageHeader header;
49
50 /**
51 * GNUNET_YES to get hostname from IP,
52 * GNUNET_NO to get IP from hostname.
53 */
54 int32_t direction GNUNET_PACKED;
55
56 /**
57 * Domain to use (AF_INET, AF_INET6 or AF_UNSPEC).
58 */
59 int32_t domain GNUNET_PACKED;
60
61};
62
63#endif
diff --git a/src/resolver/resolver_api.c b/src/resolver/resolver_api.c
new file mode 100644
index 000000000..27358996a
--- /dev/null
+++ b/src/resolver/resolver_api.c
@@ -0,0 +1,468 @@
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 resolver/resolver_api.c
23 * @brief resolver for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_client_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_resolver_service.h"
31#include "gnunet_server_lib.h"
32#include "resolver.h"
33
34
35struct GetAddressContext
36{
37 GNUNET_RESOLVER_AddressCallback callback;
38 void *cls;
39 struct GNUNET_RESOLVER_GetMessage *msg;
40 struct GNUNET_CLIENT_Connection *client;
41 struct GNUNET_TIME_Absolute timeout;
42};
43
44
45
46/**
47 * Convert IP address to string without DNS resolution.
48 */
49static char *
50no_resolve (const struct sockaddr *sa, socklen_t salen)
51{
52 char *ret;
53 char inet4[INET_ADDRSTRLEN];
54 char inet6[INET6_ADDRSTRLEN];
55
56 if (salen < sizeof (struct sockaddr))
57 return NULL;
58 switch (sa->sa_family)
59 {
60 case AF_INET:
61 if (salen != sizeof (struct sockaddr_in))
62 return NULL;
63 inet_ntop (AF_INET,
64 &((struct sockaddr_in *) sa)->sin_addr,
65 inet4, INET_ADDRSTRLEN);
66 ret = GNUNET_strdup (inet4);
67 break;
68 case AF_INET6:
69 if (salen != sizeof (struct sockaddr_in6))
70 return NULL;
71 inet_ntop (AF_INET6,
72 &((struct sockaddr_in6 *) sa)->sin6_addr,
73 inet6, INET6_ADDRSTRLEN);
74 ret = GNUNET_strdup (inet6);
75 break;
76 default:
77 ret = NULL;
78 break;
79 }
80 return ret;
81}
82
83
84static void
85handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
86{
87 struct GetAddressContext *gac = cls;
88 uint16_t size;
89 const struct sockaddr *sa;
90 socklen_t salen;
91
92
93 if (msg == NULL)
94 {
95 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
96 _("Timeout trying to resolve hostname.\n"));
97 gac->callback (gac->cls, NULL, 0);
98 GNUNET_CLIENT_disconnect (gac->client);
99 GNUNET_free (gac);
100 return;
101 }
102 if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
103 {
104 GNUNET_break (0);
105 gac->callback (gac->cls, NULL, 0);
106 GNUNET_CLIENT_disconnect (gac->client);
107 GNUNET_free (gac);
108 return;
109 }
110
111 size = ntohs (msg->size);
112 if (size == sizeof (struct GNUNET_MessageHeader))
113 {
114#if DEBUG_RESOLVER
115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
116 _("Received end message resolving hostname.\n"));
117#endif
118 gac->callback (gac->cls, NULL, 0);
119 GNUNET_CLIENT_disconnect (gac->client);
120 GNUNET_free (gac);
121 return;
122 }
123 sa = (const struct sockaddr *) &msg[1];
124 salen = size - sizeof (struct GNUNET_MessageHeader);
125 if (salen < sizeof (struct sockaddr))
126 {
127 GNUNET_break (0);
128 gac->callback (gac->cls, NULL, 0);
129 GNUNET_CLIENT_disconnect (gac->client);
130 GNUNET_free (gac);
131 return;
132 }
133#if DEBUG_RESOLVER
134 {
135 char *ips = no_resolve (sa, salen);
136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), ips);
137 GNUNET_free (ips);
138 }
139#endif
140 gac->callback (gac->cls, sa, salen);
141 GNUNET_CLIENT_receive (gac->client,
142 &handle_address_response,
143 gac,
144 GNUNET_TIME_absolute_get_remaining (gac->timeout));
145}
146
147
148static size_t
149transmit_get_ip (void *cls, size_t size, void *buf)
150{
151 struct GetAddressContext *actx = cls;
152 uint16_t ms;
153
154 if (buf == NULL)
155 {
156 /* timeout / error */
157 GNUNET_free (actx->msg);
158 actx->callback (actx->cls, NULL, 0);
159 GNUNET_CLIENT_disconnect (actx->client);
160 GNUNET_free (actx);
161 return 0;
162 }
163 ms = ntohs (actx->msg->header.size);
164 GNUNET_assert (size >= ms);
165 memcpy (buf, actx->msg, ms);
166 GNUNET_free (actx->msg);
167 actx->msg = NULL;
168 GNUNET_CLIENT_receive (actx->client,
169 &handle_address_response,
170 actx,
171 GNUNET_TIME_absolute_get_remaining (actx->timeout));
172 return ms;
173}
174
175
176
177/**
178 * Convert a string to one or more IP addresses.
179 *
180 * @param sched scheduler to use
181 * @param cfg configuration to use
182 * @param hostname the hostname to resolve
183 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
184 * @param callback function to call with addresses
185 * @param cls closure for callback
186 * @param timeout how long to try resolving
187 */
188void
189GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
190 struct GNUNET_CONFIGURATION_Handle *cfg,
191 const char *hostname,
192 int domain,
193 struct GNUNET_TIME_Relative timeout,
194 GNUNET_RESOLVER_AddressCallback callback, void *cls)
195{
196 struct GNUNET_CLIENT_Connection *client;
197 struct GNUNET_RESOLVER_GetMessage *msg;
198 struct GetAddressContext *actx;
199 size_t slen;
200
201 slen = strlen (hostname) + 1;
202 if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
203 GNUNET_SERVER_MAX_MESSAGE_SIZE)
204 {
205 GNUNET_break (0);
206 callback (cls, NULL, 0);
207 return;
208 }
209 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
210 if (client == NULL)
211 {
212 callback (cls, NULL, 0);
213 return;
214 }
215 msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
216 msg->header.size =
217 htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
218 msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
219 msg->direction = htonl (GNUNET_NO);
220 msg->domain = htonl (domain);
221 memcpy (&msg[1], hostname, slen);
222 actx = GNUNET_malloc (sizeof (struct GetAddressContext));
223 actx->callback = callback;
224 actx->cls = cls;
225 actx->client = client;
226 actx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
227 actx->msg = msg;
228
229#if DEBUG_RESOLVER
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231 _("Resolver requests DNS resolution of hostname `%s'.\n"),
232 hostname);
233#endif
234 if (NULL ==
235 GNUNET_CLIENT_notify_transmit_ready (client,
236 slen +
237 sizeof (struct
238 GNUNET_RESOLVER_GetMessage),
239 timeout, &transmit_get_ip, actx))
240 {
241 GNUNET_free (msg);
242 GNUNET_free (actx);
243 callback (cls, NULL, 0);
244 GNUNET_CLIENT_disconnect (client);
245 return;
246 }
247}
248
249
250struct GetHostnameContext
251{
252 GNUNET_RESOLVER_HostnameCallback callback;
253 void *cls;
254 struct GNUNET_RESOLVER_GetMessage *msg;
255 struct GNUNET_CLIENT_Connection *client;
256 struct GNUNET_TIME_Absolute timeout;
257};
258
259
260static void
261handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg)
262{
263 struct GetHostnameContext *ghc = cls;
264 uint16_t size;
265 const char *hostname;
266
267 if (msg == NULL)
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
270 _("Timeout trying to resolve IP address.\n"));
271 ghc->callback (ghc->cls, NULL);
272 GNUNET_CLIENT_disconnect (ghc->client);
273 GNUNET_free (ghc);
274 return;
275 }
276 size = ntohs (msg->size);
277 if (size == sizeof (struct GNUNET_MessageHeader))
278 {
279#if DEBUG_RESOLVER
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 _("Received end message resolving IP address.\n"));
282#endif
283 ghc->callback (ghc->cls, NULL);
284 GNUNET_CLIENT_disconnect (ghc->client);
285 GNUNET_free (ghc);
286 return;
287 }
288 hostname = (const char *) &msg[1];
289 if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
290 {
291 GNUNET_break (0);
292 ghc->callback (ghc->cls, NULL);
293 GNUNET_CLIENT_disconnect (ghc->client);
294 GNUNET_free (ghc);
295 return;
296 }
297#if DEBUG_RESOLVER
298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299 _("Resolver returns `%s'.\n"), hostname);
300#endif
301 ghc->callback (ghc->cls, hostname);
302 GNUNET_CLIENT_receive (ghc->client,
303 &handle_hostname_response,
304 ghc,
305 GNUNET_TIME_absolute_get_remaining (ghc->timeout));
306}
307
308
309static size_t
310transmit_get_hostname (void *cls, size_t size, void *buf)
311{
312 struct GetHostnameContext *hctx = cls;
313 uint16_t msize;
314
315 if (buf == NULL)
316 {
317 GNUNET_free (hctx->msg);
318 hctx->callback (hctx->cls, NULL);
319 GNUNET_CLIENT_disconnect (hctx->client);
320 GNUNET_free (hctx);
321 return 0;
322 }
323 msize = ntohs (hctx->msg->header.size);
324 GNUNET_assert (size >= msize);
325 memcpy (buf, hctx->msg, msize);
326 GNUNET_free (hctx->msg);
327 hctx->msg = NULL;
328 GNUNET_CLIENT_receive (hctx->client,
329 &handle_hostname_response,
330 hctx,
331 GNUNET_TIME_absolute_get_remaining (hctx->timeout));
332 return msize;
333}
334
335
336
337
338/**
339 * Get an IP address as a string.
340 *
341 * @param sched scheduler to use
342 * @param cfg configuration to use
343 * @param sa host address
344 * @param salen length of host address
345 * @param do_resolve use GNUNET_NO to return numeric hostname
346 * @param timeout how long to try resolving
347 * @param callback function to call with hostnames
348 * @param cls closure for callback
349 */
350void
351GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
352 struct GNUNET_CONFIGURATION_Handle *cfg,
353 const struct sockaddr *sa,
354 socklen_t salen,
355 int do_resolve,
356 struct GNUNET_TIME_Relative timeout,
357 GNUNET_RESOLVER_HostnameCallback callback,
358 void *cls)
359{
360 char *result;
361 struct GNUNET_CLIENT_Connection *client;
362 struct GNUNET_RESOLVER_GetMessage *msg;
363 struct GetHostnameContext *hctx;
364
365 if (GNUNET_NO == do_resolve)
366 {
367 result = no_resolve (sa, salen);
368#if DEBUG_RESOLVER
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 _("Resolver returns `%s'.\n"), result);
371#endif
372 callback (cls, result);
373 if (result != NULL)
374 {
375 GNUNET_free (result);
376 callback (cls, NULL);
377 }
378 return;
379 }
380 if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
381 GNUNET_SERVER_MAX_MESSAGE_SIZE)
382 {
383 GNUNET_break (0);
384 callback (cls, NULL);
385 return;
386 }
387 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
388 if (client == NULL)
389 {
390 callback (cls, NULL);
391 return;
392 }
393 msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
394 msg->header.size =
395 htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
396 msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
397 msg->direction = htonl (GNUNET_YES);
398 msg->domain = htonl (sa->sa_family);
399 memcpy (&msg[1], sa, salen);
400#if DEBUG_RESOLVER
401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402 _("Resolver requests DNS resolution of IP address.\n"));
403#endif
404 hctx = GNUNET_malloc (sizeof (struct GetHostnameContext));
405 hctx->callback = callback;
406 hctx->cls = cls;
407 hctx->client = client;
408 hctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
409 hctx->msg = msg;
410 if (NULL ==
411 GNUNET_CLIENT_notify_transmit_ready (client,
412 sizeof (struct
413 GNUNET_RESOLVER_GetMessage)
414 + salen, timeout,
415 &transmit_get_hostname, hctx))
416 {
417 GNUNET_free (msg);
418 callback (cls, NULL);
419 GNUNET_CLIENT_disconnect (client);
420 GNUNET_free (hctx);
421 }
422}
423
424/**
425 * Maximum supported length of hostname
426 */
427#define MAX_HOSTNAME 1024
428
429
430/**
431 * Resolve our hostname to an IP address.
432 *
433 * @param sched scheduler to use
434 * @param cfg configuration to use
435 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
436 * @param callback function to call with addresses
437 * @param cls closure for callback
438 * @param timeout how long to try resolving
439 */
440void
441GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
442 struct GNUNET_CONFIGURATION_Handle *cfg,
443 int domain,
444 struct GNUNET_TIME_Relative timeout,
445 GNUNET_RESOLVER_AddressCallback callback,
446 void *cls)
447{
448 char hostname[MAX_HOSTNAME];
449
450 if (0 != gethostname (hostname, sizeof (hostname) - 1))
451 {
452 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
453 GNUNET_ERROR_TYPE_BULK, "gethostname");
454 callback (cls, NULL, 0);
455 return;
456 }
457#if DEBUG_RESOLVER
458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
459 _("Resolving our hostname `%s'\n"), hostname);
460#endif
461 GNUNET_RESOLVER_ip_get (sched,
462 cfg, hostname, domain, timeout, callback, cls);
463}
464
465
466
467
468/* end of resolver_api.c */
diff --git a/src/resolver/test_resolver_api.c b/src/resolver/test_resolver_api.c
new file mode 100644
index 000000000..240879d73
--- /dev/null
+++ b/src/resolver/test_resolver_api.c
@@ -0,0 +1,229 @@
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 resolver/test_resolver_api.c
22 * @brief testcase for resolver_api.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_getopt_lib.h"
27#include "gnunet_os_lib.h"
28#include "gnunet_program_lib.h"
29#include "gnunet_scheduler_lib.h"
30#include "gnunet_resolver_service.h"
31#include "resolver.h"
32
33#define VERBOSE GNUNET_NO
34
35
36static void
37check_hostname (void *cls, const struct sockaddr *sa, socklen_t salen)
38{
39 char buf[INET6_ADDRSTRLEN];
40 int *ok = cls;
41
42 if (salen == 0)
43 {
44 (*ok) &= ~8;
45 return;
46 }
47 if (salen == sizeof (struct sockaddr_in))
48 {
49 struct sockaddr_in *in = (struct sockaddr_in *) sa;
50 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
51 _("Got IP address `%s' for our host.\n"),
52 inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)));
53 }
54 else if (salen == sizeof (struct sockaddr_in6))
55 {
56 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) sa;
57 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
58 _("Got IP address `%s' for our host.\n"),
59 inet_ntop (AF_INET6, &in6->sin6_addr, buf, sizeof (buf)));
60 }
61 else
62 {
63 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
64 _("Got address of bogus length %u\n"), salen);
65 GNUNET_assert (0);
66 }
67}
68
69
70static void
71check_localhost_num (void *cls, const char *hostname)
72{
73 int *ok = cls;
74 if (hostname == NULL)
75 return;
76 if (0 == strcmp (hostname, "127.0.0.1"))
77 {
78#if DEBUG_RESOLVER
79 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
80 "Received correct hostname `%s'.\n", hostname);
81#endif
82 (*ok) &= ~4;
83 }
84 else
85 {
86#if DEBUG_RESOLVER
87 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
88 "Received invalid hostname `%s'.\n", hostname);
89#endif
90 GNUNET_break (0);
91 }
92}
93
94static void
95check_localhost (void *cls, const char *hostname)
96{
97 int *ok = cls;
98 if (hostname == NULL)
99 return;
100 if (0 == strcmp (hostname, "localhost"))
101 {
102#if DEBUG_RESOLVER
103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
104 "Received correct hostname `%s'.\n", hostname);
105#endif
106 (*ok) &= ~2;
107 }
108 else
109 {
110#if DEBUG_RESOLVER
111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
112 "Received invalid hostname `%s'.\n", hostname);
113#endif
114 GNUNET_break (0);
115 }
116}
117
118static void
119check_127 (void *cls, const struct sockaddr *sa, socklen_t salen)
120{
121 int *ok = cls;
122 const struct sockaddr_in *sai = (const struct sockaddr_in *) sa;
123
124 if (sa == NULL)
125 return;
126 GNUNET_assert (sizeof (struct sockaddr_in) == salen);
127 if (sai->sin_addr.s_addr == htonl (INADDR_LOOPBACK))
128 {
129#if DEBUG_RESOLVER
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received correct address.\n");
131#endif
132 (*ok) &= ~1;
133 }
134 else
135 {
136#if DEBUG_RESOLVER
137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received incorrect address.\n");
138#endif
139 GNUNET_break (0);
140 }
141}
142
143static void
144run (void *cls,
145 struct GNUNET_SCHEDULER_Handle *sched,
146 char *const *args,
147 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
148{
149 struct sockaddr_in sa;
150 struct GNUNET_TIME_Relative timeout =
151 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
152 2500);
153 memset (&sa, 0, sizeof (sa));
154 sa.sin_family = AF_INET;
155 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
156 GNUNET_RESOLVER_ip_get (sched,
157 cfg,
158 "localhost", AF_INET, timeout, &check_127, cls);
159 GNUNET_RESOLVER_hostname_get (sched,
160 cfg,
161 (const struct sockaddr *) &sa,
162 sizeof (struct sockaddr),
163 GNUNET_YES, timeout, &check_localhost, cls);
164 GNUNET_RESOLVER_hostname_get (sched,
165 cfg,
166 (const struct sockaddr *) &sa,
167 sizeof (struct sockaddr),
168 GNUNET_NO,
169 timeout, &check_localhost_num, cls);
170 GNUNET_RESOLVER_hostname_resolve (sched,
171 cfg,
172 AF_UNSPEC, timeout, &check_hostname, cls);
173}
174
175static int
176check ()
177{
178 int ok = 1 + 2 + 4 + 8;
179 pid_t pid;
180 char *const argv[] = { "test-resolver-api",
181 "-c",
182 "test_resolver_api_data.conf",
183#if VERBOSE
184 "-L", "DEBUG",
185#endif
186 NULL
187 };
188 struct GNUNET_GETOPT_CommandLineOption options[] = {
189 GNUNET_GETOPT_OPTION_END
190 };
191 pid = GNUNET_OS_start_process ("gnunet-service-resolver",
192 "gnunet-service-resolver",
193#if VERBOSE
194 "-L", "DEBUG",
195#endif
196 "-c", "test_resolver_api_data.conf", NULL);
197 sleep (1);
198 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
199 argv, "test-resolver-api", "nohelp",
200 options, &run, &ok);
201 if (0 != PLIBC_KILL (pid, SIGTERM))
202 {
203 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
204 ok = 1;
205 }
206 waitpid (pid, NULL, 0);
207 if (ok != 0)
208 fprintf (stderr, "Missed some resolutions: %u\n", ok);
209 return ok;
210}
211
212int
213main (int argc, char *argv[])
214{
215 int ret;
216
217 GNUNET_log_setup ("test-resolver-api",
218#if VERBOSE
219 "DEBUG",
220#else
221 "WARNING",
222#endif
223 NULL);
224 ret = check ();
225
226 return ret;
227}
228
229/* end of test_resolver_api.c */
diff --git a/src/resolver/test_resolver_api_data.conf b/src/resolver/test_resolver_api_data.conf
new file mode 100644
index 000000000..c31668117
--- /dev/null
+++ b/src/resolver/test_resolver_api_data.conf
@@ -0,0 +1,5 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-statistics/
3
4[resolver]
5PORT = 22354
diff --git a/src/statistics/Makefile.am b/src/statistics/Makefile.am
new file mode 100644
index 000000000..2ae254cbf
--- /dev/null
+++ b/src/statistics/Makefile.am
@@ -0,0 +1,59 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11
12lib_LTLIBRARIES = libgnunetstatistics.la
13
14libgnunetstatistics_la_SOURCES = \
15 statistics_api.c statistics.h
16libgnunetstatistics_la_LIBADD = \
17 $(top_builddir)/src/util/libgnunetutil.la \
18 $(GN_LIBINTL)
19libgnunetstatistics_la_LDFLAGS = \
20 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
21 -version-info 0:0:0
22
23
24bin_PROGRAMS = \
25 gnunet-statistics \
26 gnunet-service-statistics
27
28gnunet_statistics_SOURCES = \
29 gnunet-statistics.c
30gnunet_statistics_LDADD = \
31 $(top_builddir)/src/statistics/libgnunetstatistics.la \
32 $(top_builddir)/src/util/libgnunetutil.la \
33 $(GN_LIBINTL)
34
35gnunet_service_statistics_SOURCES = \
36 gnunet-service-statistics.c
37gnunet_service_statistics_LDADD = \
38 $(top_builddir)/src/statistics/libgnunetstatistics.la \
39 $(top_builddir)/src/util/libgnunetutil.la \
40 $(GN_LIBINTL)
41
42
43check_PROGRAMS = \
44 test_statistics_api
45
46TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
47
48test_statistics_api_SOURCES = \
49 test_statistics_api.c
50test_statistics_api_LDADD = \
51 $(top_builddir)/src/statistics/libgnunetstatistics.la \
52 $(top_builddir)/src/util/libgnunetutil.la
53
54EXTRA_DIST = \
55 test_statistics_api_data.conf
56
57check_SCRIPTS = \
58 test_gnunet_statistics.sh
59
diff --git a/src/statistics/gnunet-service-statistics.c b/src/statistics/gnunet-service-statistics.c
new file mode 100644
index 000000000..0e7b4853d
--- /dev/null
+++ b/src/statistics/gnunet-service-statistics.c
@@ -0,0 +1,470 @@
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 statistics/gnunet-service-statistics.c
23 * @brief program that tracks statistics
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_disk_lib.h"
28#include "gnunet_getopt_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_service_lib.h"
31#include "gnunet_statistics_service.h"
32#include "gnunet_strings_lib.h"
33#include "gnunet_time_lib.h"
34#include "statistics.h"
35
36/**
37 * Entry in the statistics list.
38 */
39struct StatsEntry
40{
41 /**
42 * This is a linked list.
43 */
44 struct StatsEntry *next;
45
46 /**
47 * Name of the service, points into the
48 * middle of msg.
49 */
50 const char *service;
51
52 /**
53 * Name for the value, points into
54 * the middle of msg.
55 */
56 const char *name;
57
58 /**
59 * Message that can be used to set this value,
60 * stored at the end of the memory used by
61 * this struct.
62 */
63 struct GNUNET_STATISTICS_SetMessage *msg;
64
65 /**
66 * Our value.
67 */
68 uint64_t value;
69
70 /**
71 * Unique ID.
72 */
73 uint32_t uid;
74
75 /**
76 * Is this value persistent?
77 */
78 int persistent;
79
80};
81
82/**
83 * Linked list of our active statistics.
84 */
85static struct StatsEntry *start;
86
87/**
88 * Counter used to generate unique values.
89 */
90static uint32_t uidgen;
91
92/**
93 * Load persistent values from disk. Disk format is
94 * exactly the same format that we also use for
95 * setting the values over the network.
96 */
97static void
98load (struct GNUNET_SERVER_Handle *server,
99 struct GNUNET_CONFIGURATION_Handle *cfg)
100{
101 char *fn;
102 int fd;
103 struct stat sb;
104 char *buf;
105 size_t off;
106 const struct GNUNET_MessageHeader *msg;
107
108 fn = GNUNET_DISK_get_home_filename (cfg,
109 "statistics", "statistics.data", NULL);
110 if (fn == NULL)
111 return;
112 if ((0 != stat (fn, &sb)) || (sb.st_size == 0))
113 {
114 GNUNET_free (fn);
115 return;
116 }
117 fd = GNUNET_DISK_file_open (fn, O_RDONLY);
118 if (fd == -1)
119 {
120 GNUNET_free (fn);
121 return;
122 }
123 buf = mmap (NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
124 if (MAP_FAILED == buf)
125 {
126 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "mmap", fn);
127 GNUNET_break (0 == CLOSE (fd));
128 GNUNET_free (fn);
129 return;
130 }
131 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
132 _("Loading %llu bytes of statistics from `%s'\n"),
133 (unsigned long long) sb.st_size, fn);
134 off = 0;
135 while (off + sizeof (struct GNUNET_MessageHeader) < sb.st_size)
136 {
137 msg = (const struct GNUNET_MessageHeader *) &buf[off];
138 if ((ntohs (msg->size) + off > sb.st_size) ||
139 (GNUNET_OK != GNUNET_SERVER_inject (server, NULL, msg)))
140 {
141 GNUNET_break (0);
142 break;
143 }
144 off += ntohs (msg->size);
145 }
146 GNUNET_break (0 == munmap (buf, sb.st_size));
147 GNUNET_break (0 == CLOSE (fd));
148 GNUNET_free (fn);
149}
150
151
152/**
153 * Write persistent statistics to disk.
154 *
155 * @param cls closure
156 * @param cfg configuration to use
157 */
158static void
159save (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
160{
161 struct StatsEntry *pos;
162 char *fn;
163 int fd;
164 uint16_t size;
165 unsigned long long total;
166
167 fd = -1;
168 fn = GNUNET_DISK_get_home_filename (cfg,
169 "statistics", "statistics.data", NULL);
170 if (fn != NULL)
171 fd =
172 GNUNET_DISK_file_open (fn, O_WRONLY | O_CREAT | O_TRUNC,
173 S_IRUSR | S_IWUSR);
174 total = 0;
175 while (NULL != (pos = start))
176 {
177 start = pos->next;
178 if ((pos->persistent) && (fd != -1))
179 {
180 size = htons (pos->msg->header.size);
181 if (size != WRITE (fd, pos->msg, size))
182 {
183 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
184 "write", fn);
185 GNUNET_DISK_file_close (fn, fd);
186 fd = -1;
187 }
188 else
189 total += size;
190 }
191 GNUNET_free (pos);
192 }
193 if (fd != -1)
194 {
195 GNUNET_DISK_file_close (fn, fd);
196 if (total == 0)
197 GNUNET_break (0 == UNLINK (fn));
198 else
199 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
200 _("Wrote %llu bytes of statistics to `%s'\n"), total, fn);
201 }
202 GNUNET_free_non_null (fn);
203}
204
205
206/**
207 * Transmit the given stats value.
208 */
209static void
210transmit (struct GNUNET_SERVER_TransmitContext *tc,
211 const struct StatsEntry *e)
212{
213 struct GNUNET_STATISTICS_ReplyMessage *m;
214 struct GNUNET_MessageHeader *h;
215 size_t size;
216 uint16_t msize;
217
218 size =
219 sizeof (struct GNUNET_STATISTICS_ReplyMessage) + strlen (e->service) + 1 +
220 strlen (e->name) + 1;
221 GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
222 msize = size - sizeof (struct GNUNET_MessageHeader);
223 m = GNUNET_malloc (size);
224 m->uid = htonl (e->uid);
225 if (e->persistent)
226 m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT);
227 m->value = GNUNET_htonll (e->value);
228 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
229 GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1],
230 size,
231 2, e->service, e->name));
232#if DEBUG_STATISTICS
233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
234 "Transmitting value for `%s:%s': %llu\n",
235 e->service, e->name, e->value);
236#endif
237 h = &m->header;
238 GNUNET_SERVER_transmit_context_append (tc,
239 &h[1],
240 msize,
241 GNUNET_MESSAGE_TYPE_STATISTICS_VALUE);
242 GNUNET_free (m);
243}
244
245
246/**
247 * Does this entry match the request?
248 */
249static int
250matches (const struct StatsEntry *e, const char *service, const char *name)
251{
252 return ((0 == strlen (service)) ||
253 (0 == strcmp (service, e->service)))
254 && ((0 == strlen (name)) || (0 == strcmp (name, e->name)));
255}
256
257
258/**
259 * Handle GET-message.
260 *
261 * @param cls closure
262 * @param server the server handling the message
263 * @param client identification of the client
264 * @param message the actual message
265 * @return GNUNET_OK to keep the connection open,
266 * GNUNET_SYSERR to close it (signal serious error)
267 */
268static void
269handle_get (void *cls,
270 struct GNUNET_SERVER_Handle *server,
271 struct GNUNET_SERVER_Client *client,
272 const struct GNUNET_MessageHeader *message)
273{
274 char *service;
275 char *name;
276 struct StatsEntry *pos;
277 struct GNUNET_SERVER_TransmitContext *tc;
278 size_t size;
279
280 size = ntohs (message->size) - sizeof (struct GNUNET_MessageHeader);
281 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
282 size, 2, &service, &name))
283 {
284 GNUNET_break (0);
285 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
286 return;
287 }
288#if DEBUG_STATISTICS
289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
290 "Received request for statistics on `%s:%s'\n",
291 strlen (service) ? service : "*", strlen (name) ? name : "*");
292#endif
293 tc = GNUNET_SERVER_transmit_context_create (client);
294 pos = start;
295 while (pos != NULL)
296 {
297 if (matches (pos, service, name))
298 transmit (tc, pos);
299 pos = pos->next;
300 }
301 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
302 GNUNET_MESSAGE_TYPE_STATISTICS_END);
303 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
304}
305
306
307/**
308 * Handle SET-message.
309 *
310 * @param cls closure
311 * @param server the server handling the message
312 * @param client identification of the client
313 * @param message the actual message
314 */
315static void
316handle_set (void *cls,
317 struct GNUNET_SERVER_Handle *server,
318 struct GNUNET_SERVER_Client *client,
319 const struct GNUNET_MessageHeader *message)
320{
321 char *service;
322 char *name;
323 uint16_t msize;
324 uint16_t size;
325 const struct GNUNET_STATISTICS_SetMessage *msg;
326 struct StatsEntry *pos;
327 struct StatsEntry *prev;
328 uint32_t flags;
329 uint64_t value;
330 int64_t delta;
331
332 msize = ntohs (message->size);
333 if (msize < sizeof (struct GNUNET_STATISTICS_SetMessage))
334 {
335 GNUNET_break (0);
336 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
337 return;
338 }
339 size = msize - sizeof (struct GNUNET_STATISTICS_SetMessage);
340 msg = (const struct GNUNET_STATISTICS_SetMessage *) message;
341
342 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1],
343 size, 2, &service, &name))
344 {
345 GNUNET_break (0);
346 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
347 return;
348 }
349#if DEBUG_STATISTICS
350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351 "Received request to update statistic on `%s:%s'\n",
352 service, name);
353#endif
354 flags = ntohl (msg->flags);
355 value = GNUNET_ntohll (msg->value);
356 pos = start;
357 prev = NULL;
358 while (pos != NULL)
359 {
360 if (matches (pos, service, name))
361 {
362 if ((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0)
363 {
364 pos->value = value;
365 }
366 else
367 {
368 delta = (int64_t) value;
369 if ((delta < 0) && (pos->value < -delta))
370 {
371 pos->value = 0;
372 }
373 else
374 {
375 GNUNET_break ((delta <= 0) ||
376 (pos->value + delta > pos->value));
377 pos->value += delta;
378 }
379 }
380 pos->msg->value = GNUNET_htonll (pos->value);
381 pos->msg->flags = msg->flags;
382 pos->persistent =
383 (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
384 if (prev != NULL)
385 {
386 /* move to front for faster setting next time! */
387 prev->next = pos->next;
388 pos->next = start;
389 start = pos;
390 }
391#if DEBUG_STATISTICS
392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393 "Statistic `%s:%s' updated to value %llu.\n",
394 service, name, pos->value);
395#endif
396 GNUNET_SERVER_receive_done (client, GNUNET_OK);
397 return;
398 }
399 prev = pos;
400 pos = pos->next;
401 }
402 pos = GNUNET_malloc (sizeof (struct StatsEntry) + msize);
403 pos->next = start;
404 if (((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0) ||
405 (0 < (int64_t) GNUNET_ntohll (msg->value)))
406 pos->value = GNUNET_ntohll (msg->value);
407 pos->uid = uidgen++;
408 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
409 pos->msg = (void *) &pos[1];
410 memcpy (pos->msg, message, ntohs (message->size));
411 pos->service = (const char *) &pos->msg[1];
412 pos->name = &pos->service[strlen (pos->service) + 1];
413
414 start = pos;
415#if DEBUG_STATISTICS
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "New statistic on `%s:%s' with value %llu created.\n",
418 service, name, pos->value);
419#endif
420 GNUNET_SERVER_receive_done (client, GNUNET_OK);
421}
422
423
424/**
425 * List of handlers for the messages understood by this
426 * service.
427 */
428static struct GNUNET_SERVER_MessageHandler handlers[] = {
429 {&handle_set, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_SET, 0},
430 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_GET, 0},
431 {NULL, NULL, 0, 0}
432};
433
434
435/**
436 * Process statistics requests.
437 *
438 * @param cls closure
439 * @param sched scheduler to use
440 * @param server the initialized server
441 * @param cfg configuration to use
442 */
443static void
444run (void *cls,
445 struct GNUNET_SCHEDULER_Handle *sched,
446 struct GNUNET_SERVER_Handle *server,
447 struct GNUNET_CONFIGURATION_Handle *cfg)
448{
449 GNUNET_SERVER_add_handlers (server, handlers);
450 load (server, cfg);
451}
452
453
454/**
455 * The main function for the statistics service.
456 *
457 * @param argc number of arguments from the command line
458 * @param argv command line arguments
459 * @return 0 ok, 1 on error
460 */
461int
462main (int argc, char *const *argv)
463{
464 return (GNUNET_OK ==
465 GNUNET_SERVICE_run (argc,
466 argv,
467 "statistics", &run, NULL, &save, NULL)) ? 0 : 1;
468}
469
470/* end of gnunet-service-statistics.c */
diff --git a/src/statistics/gnunet-statistics.c b/src/statistics/gnunet-statistics.c
new file mode 100644
index 000000000..bafb77c66
--- /dev/null
+++ b/src/statistics/gnunet-statistics.c
@@ -0,0 +1,179 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2004, 2005, 2006, 2007, 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 statistics/gnunet-statistics.c
23 * @brief tool to obtain statistics
24 * @author Christian Grothoff
25 * @author Igor Wronsky
26 */
27#include "platform.h"
28#include "gnunet_getopt_lib.h"
29#include "gnunet_program_lib.h"
30#include "gnunet_statistics_service.h"
31#include "statistics.h"
32
33#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
34
35/**
36 * Final status code.
37 */
38static int ret;
39
40/**
41 * Set to subsystem that we're going to get stats for (or NULL for all).
42 */
43static char *subsystem;
44
45/**
46 * Set to the specific stat value that we are after (or NULL for all).
47 */
48static char *name;
49
50/**
51 * Make the value that is being set persistent.
52 */
53static int persistent;
54
55/**
56 * Callback function to process statistic values.
57 *
58 * @param cls closure
59 * @param subsystem name of subsystem that created the statistic
60 * @param name the name of the datum
61 * @param value the current value
62 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
63 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
64 */
65static int
66printer (void *cls,
67 const char *subsystem,
68 const char *sname, unsigned long long value, int is_persistent)
69{
70 FPRINTF (stdout,
71 "%s%-20s %-40s: %16llu\n",
72 is_persistent ? "!" : " ", subsystem, _(sname), value);
73 return GNUNET_OK;
74}
75
76
77/**
78 * Function called last by the statistics code.
79 *
80 * @param cls closure
81 * @param success GNUNET_OK if statistics were
82 * successfully obtained, GNUNET_SYSERR if not.
83 */
84static void
85cleanup (void *cls, int success)
86{
87 struct GNUNET_STATISTICS_Handle *h = cls;
88
89 if (success != GNUNET_OK)
90 ret = 1;
91 if (h != NULL)
92 GNUNET_STATISTICS_destroy (h);
93}
94
95
96/**
97 * Main function that will be run by the scheduler.
98 *
99 * @param cls closure
100 * @param sched the scheduler to use
101 * @param args remaining command-line arguments
102 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
103 * @param cfg configuration
104 */
105static void
106run (void *cls,
107 struct GNUNET_SCHEDULER_Handle *sched,
108 char *const *args,
109 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
110{
111 struct GNUNET_STATISTICS_Handle *h;
112 unsigned long long val;
113
114 if (args[0] != NULL)
115 {
116 if ((1 != SSCANF (args[0], "%llu", &val)) ||
117 (subsystem == NULL) || (name == NULL))
118 {
119 FPRINTF (stderr, _("Invalid argument `%s'\n"), args[0]);
120 ret = 1;
121 return;
122 }
123 h = GNUNET_STATISTICS_create (sched, subsystem, cfg);
124 if (h == NULL)
125 {
126 ret = 1;
127 return;
128 }
129 GNUNET_STATISTICS_set (h, name, val, persistent);
130 GNUNET_STATISTICS_destroy (h);
131 return;
132 }
133 h = GNUNET_STATISTICS_create (sched, "gnunet-statistics", cfg);
134 if (h == NULL)
135 {
136 ret = 1;
137 return;
138 }
139 GNUNET_STATISTICS_get (h,
140 subsystem, name, GET_TIMEOUT, &cleanup, &printer, h);
141}
142
143/**
144 * gnunet-statistics command line options
145 */
146static struct GNUNET_GETOPT_CommandLineOption options[] = {
147 {'n', "name", "NAME",
148 gettext_noop ("limit output to statistcs for the given NAME"), 1,
149 &GNUNET_GETOPT_set_string, &name},
150 {'p', "persistent", NULL,
151 gettext_noop ("make the value being set persistent"), 0,
152 &GNUNET_GETOPT_set_one, &persistent},
153 {'s', "subsystem", "SUBSYSTEM",
154 gettext_noop ("limit output to the given SUBSYSTEM"), 1,
155 &GNUNET_GETOPT_set_string, &subsystem},
156 GNUNET_GETOPT_OPTION_END
157};
158
159
160/**
161 * The main function to obtain statistics in GNUnet.
162 *
163 * @param argc number of arguments from the command line
164 * @param argv command line arguments
165 * @return 0 ok, 1 on error
166 */
167int
168main (int argc, char *const *argv)
169{
170 return (GNUNET_OK ==
171 GNUNET_PROGRAM_run (argc,
172 argv,
173 "gnunet-statistics",
174 gettext_noop
175 ("Print statistics about GNUnet operations."),
176 options, &run, NULL)) ? ret : 1;
177}
178
179/* end of gnunet-statistics.c */
diff --git a/src/statistics/statistics.h b/src/statistics/statistics.h
new file mode 100644
index 000000000..6eedd4d34
--- /dev/null
+++ b/src/statistics/statistics.h
@@ -0,0 +1,94 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 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/**
22 * @author Christian Grothoff
23 * @file statistics/statistics.h
24 */
25#ifndef STATISTICS_H
26#define STATISTICS_H
27
28#include "gnunet_common.h"
29
30#define DEBUG_STATISTICS 0
31
32/**
33 * Statistics message. Contains how long the system is up
34 * and one value.
35 *
36 * The struct is be followed by the service name and
37 * name of the statistic, both 0-terminated.
38 */
39struct GNUNET_STATISTICS_ReplyMessage
40{
41 /**
42 * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE
43 */
44 struct GNUNET_MessageHeader header;
45
46 /**
47 * Unique numerical identifier for the value (will
48 * not change during the same client-session). Highest
49 * bit will be set for persistent values.
50 */
51 uint32_t uid GNUNET_PACKED;
52
53 /**
54 * The value.
55 */
56 uint64_t value GNUNET_PACKED;
57
58};
59
60#define GNUNET_STATISTICS_PERSIST_BIT (1<<31)
61
62#define GNUNET_STATISTICS_SETFLAG_ABSOLUTE 0
63
64#define GNUNET_STATISTICS_SETFLAG_RELATIVE 1
65
66#define GNUNET_STATISTICS_SETFLAG_PERSISTENT 2
67
68/**
69 * Message to set a statistic. Followed
70 * by the subsystem name and the name of
71 * the statistic (each 0-terminated).
72 */
73struct GNUNET_STATISTICS_SetMessage
74{
75 /**
76 * Type: GNUNET_MESSAGE_TYPE_STATISTICS_SET
77 */
78 struct GNUNET_MessageHeader header;
79
80 /**
81 * 0 for absolute value, 1 for relative value; 2 to make persistent
82 * (see GNUNET_STATISTICS_SETFLAG_*).
83 */
84 uint32_t flags GNUNET_PACKED;
85
86 /**
87 * Value. Note that if this is a relative value, it will
88 * be signed even though the type given here is unsigned.
89 */
90 uint64_t value GNUNET_PACKED;
91
92};
93
94#endif
diff --git a/src/statistics/statistics_api.c b/src/statistics/statistics_api.c
new file mode 100644
index 000000000..f68cf9396
--- /dev/null
+++ b/src/statistics/statistics_api.c
@@ -0,0 +1,688 @@
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 statistics/statistics_api.c
23 * @brief API of the statistics service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_client_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_server_lib.h"
30#include "gnunet_statistics_service.h"
31#include "gnunet_strings_lib.h"
32#include "statistics.h"
33
34#define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
35
36
37/**
38 * Types of actions.
39 */
40enum ActionType
41{
42 ACTION_GET,
43 ACTION_SET,
44 ACTION_UPDATE
45};
46
47
48/**
49 * Linked list of things we still need to do.
50 */
51struct ActionItem
52{
53 /**
54 * This is a linked list.
55 */
56 struct ActionItem *next;
57
58 /**
59 * What subsystem is this action about? (can be NULL)
60 */
61 char *subsystem;
62
63 /**
64 * What value is this action about? (can be NULL)
65 */
66 char *name;
67
68 /**
69 * Continuation to call once action is complete.
70 */
71 GNUNET_STATISTICS_Callback cont;
72
73 /**
74 * Function to call (for GET actions only).
75 */
76 GNUNET_STATISTICS_Iterator proc;
77
78 /**
79 * Closure for proc and cont.
80 */
81 void *cls;
82
83 /**
84 * Timeout for this action.
85 */
86 struct GNUNET_TIME_Absolute timeout;
87
88 /**
89 * Associated value.
90 */
91 unsigned long long value;
92
93 /**
94 * Flag for SET/UPDATE actions.
95 */
96 int make_persistent;
97
98 /**
99 * Has the current iteration been aborted; for GET actions.
100 */
101 int aborted;
102
103 /**
104 * Is this a GET, SET or UPDATE?
105 */
106 enum ActionType type;
107
108 /**
109 * Size of the message that we will be transmitting.
110 */
111 uint16_t msize;
112
113};
114
115
116/**
117 * Handle for the service.
118 */
119struct GNUNET_STATISTICS_Handle
120{
121 /**
122 * Our scheduler.
123 */
124 struct GNUNET_SCHEDULER_Handle *sched;
125
126 /**
127 * Name of our subsystem.
128 */
129 char *subsystem;
130
131 /**
132 * Configuration to use.
133 */
134 struct GNUNET_CONFIGURATION_Handle *cfg;
135
136 /**
137 * Socket (if available).
138 */
139 struct GNUNET_CLIENT_Connection *client;
140
141 /**
142 * Head of the linked list of pending actions (first action
143 * to be performed).
144 */
145 struct ActionItem *action_head;
146
147 /**
148 * Tail of the linked list of actions (for fast append).
149 */
150 struct ActionItem *action_tail;
151
152 /**
153 * Action we are currently busy with (action request has been
154 * transmitted, we're now receiving the response from the
155 * service).
156 */
157 struct ActionItem *current;
158
159 /**
160 * Should this handle be destroyed once we've processed
161 * all actions?
162 */
163 int do_destroy;
164
165};
166
167
168/**
169 * Try to (re)connect to the statistics service.
170 *
171 * @return GNUNET_YES on success, GNUNET_NO on failure.
172 */
173static int
174try_connect (struct GNUNET_STATISTICS_Handle *ret)
175{
176 if (ret->client != NULL)
177 return GNUNET_OK;
178 ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg);
179 if (ret->client != NULL)
180 return GNUNET_YES;
181 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
182 _("Failed to connect to statistics service!\n"));
183 return GNUNET_NO;
184}
185
186
187/**
188 * Free memory associated with the given action item.
189 */
190static void
191free_action_item (struct ActionItem *ai)
192{
193 GNUNET_free_non_null (ai->subsystem);
194 GNUNET_free_non_null (ai->name);
195 GNUNET_free (ai);
196}
197
198
199/**
200 * Get handle for the statistics service.
201 *
202 * @param subsystem name of subsystem using the service
203 * @param cfg services configuration in use
204 * @return handle to use
205 */
206struct GNUNET_STATISTICS_Handle *
207GNUNET_STATISTICS_create (struct GNUNET_SCHEDULER_Handle *sched,
208 const char *subsystem,
209 struct GNUNET_CONFIGURATION_Handle *cfg)
210{
211 struct GNUNET_STATISTICS_Handle *ret;
212
213 GNUNET_assert (subsystem != NULL);
214 GNUNET_assert (sched != NULL);
215 GNUNET_assert (cfg != NULL);
216 ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle));
217 ret->sched = sched;
218 ret->cfg = cfg;
219 ret->subsystem = GNUNET_strdup (subsystem);
220 try_connect (ret);
221 return ret;
222}
223
224
225/**
226 * Actually free the handle.
227 */
228static void
229do_destroy (struct GNUNET_STATISTICS_Handle *h)
230{
231 GNUNET_assert (h->action_head == NULL);
232 GNUNET_assert (h->current == NULL);
233 if (h->client != NULL)
234 {
235 GNUNET_CLIENT_disconnect (h->client);
236 h->client = NULL;
237 }
238 GNUNET_free (h->subsystem);
239 GNUNET_free (h);
240}
241
242
243/**
244 * Destroy a handle (free all state associated with
245 * it).
246 */
247void
248GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *handle)
249{
250 GNUNET_assert (handle->do_destroy == GNUNET_NO);
251 if ((handle->action_head != NULL) || (handle->current != NULL))
252 {
253 handle->do_destroy = GNUNET_YES;
254 return;
255 }
256 do_destroy (handle);
257}
258
259
260/**
261 * Process the message.
262 *
263 * @return GNUNET_OK if the message was well-formed
264 */
265static int
266process_message (struct GNUNET_STATISTICS_Handle *h,
267 const struct GNUNET_MessageHeader *msg)
268{
269 char *service;
270 char *name;
271 const struct GNUNET_STATISTICS_ReplyMessage *smsg;
272 uint16_t size;
273
274 if (h->current->aborted)
275 return GNUNET_OK; /* don't bother */
276 size = ntohs (msg->size);
277 if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage))
278 {
279 GNUNET_break (0);
280 return GNUNET_SYSERR;
281 }
282 smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg;
283 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
284 if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1],
285 size, 2, &service, &name))
286 {
287 GNUNET_break (0);
288 return GNUNET_SYSERR;
289 }
290#if DEBUG_STATISTICS
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "Received valid statistic on `%s:%s': %llu\n",
293 service, name, GNUNET_ntohll (smsg->value));
294#endif
295 if (GNUNET_OK !=
296 h->current->proc (h->current->cls,
297 service,
298 name,
299 GNUNET_ntohll (smsg->value),
300 0 !=
301 (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)))
302 {
303#if DEBUG_STATISTICS
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
305 "Processing of remaining statistics aborted by client.\n");
306#endif
307 h->current->aborted = GNUNET_YES;
308 }
309 return GNUNET_OK;
310}
311
312
313
314/**
315 * Schedule the next action to be performed.
316 */
317static void schedule_action (struct GNUNET_STATISTICS_Handle *h);
318
319
320/**
321 * GET processing is complete, tell client about it.
322 */
323static void
324finish (struct GNUNET_STATISTICS_Handle *h, int code)
325{
326 struct ActionItem *pos = h->current;
327 h->current = NULL;
328 schedule_action (h);
329 if (pos->cont != NULL)
330 pos->cont (pos->cls, code);
331 free_action_item (pos);
332}
333
334
335/**
336 * Function called with messages from stats service.
337 *
338 * @param cls closure
339 * @param msg message received, NULL on timeout or fatal error
340 */
341static void
342receive_stats (void *cls, const struct GNUNET_MessageHeader *msg)
343{
344 struct GNUNET_STATISTICS_Handle *h = cls;
345
346 if (msg == NULL)
347 {
348 GNUNET_CLIENT_disconnect (h->client);
349 h->client = NULL;
350 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
351 _
352 ("Error receiving statistics from service, is the service running?\n"));
353 finish (h, GNUNET_SYSERR);
354 return;
355 }
356 switch (ntohs (msg->type))
357 {
358 case GNUNET_MESSAGE_TYPE_STATISTICS_END:
359#if DEBUG_STATISTICS
360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361 "Received end of statistics marker\n");
362#endif
363 finish (h, GNUNET_OK);
364 return;
365 case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE:
366 if (GNUNET_OK == process_message (h, msg))
367 {
368 /* finally, look for more! */
369 GNUNET_CLIENT_receive (h->client,
370 &receive_stats,
371 h,
372 GNUNET_TIME_absolute_get_remaining (h->
373 current->
374 timeout));
375 return;
376 }
377 GNUNET_break (0);
378 break;
379 default:
380 GNUNET_break (0);
381 break;
382 }
383 GNUNET_CLIENT_disconnect (h->client);
384 h->client = NULL;
385 finish (h, GNUNET_SYSERR);
386}
387
388
389/**
390 * Transmit a GET request (and if successful, start to receive
391 * the response).
392 */
393static size_t
394transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
395{
396 struct GNUNET_MessageHeader *hdr;
397 size_t slen1;
398 size_t slen2;
399 uint16_t msize;
400
401 if (buf == NULL)
402 {
403 /* timeout / error */
404 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
405 _("Transmission of request for statistics failed!\n"));
406 finish (handle, GNUNET_SYSERR);
407 return 0;
408 }
409 slen1 = strlen (handle->current->subsystem) + 1;
410 slen2 = strlen (handle->current->name) + 1;
411 msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
412 GNUNET_assert (msize <= size);
413 hdr = (struct GNUNET_MessageHeader *) buf;
414 hdr->size = htons (msize);
415 hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET);
416 GNUNET_assert (slen1 + slen2 ==
417 GNUNET_STRINGS_buffer_fill ((char *) &hdr[1],
418 slen1 + slen2,
419 2,
420 handle->current->subsystem,
421 handle->current->name));
422 GNUNET_CLIENT_receive (handle->client,
423 &receive_stats,
424 handle,
425 GNUNET_TIME_absolute_get_remaining (handle->current->
426 timeout));
427 return msize;
428}
429
430
431
432/**
433 * Transmit a SET/UPDATE request.
434 */
435static size_t
436transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf)
437{
438 struct GNUNET_STATISTICS_SetMessage *r;
439 size_t slen;
440 size_t nlen;
441 size_t nsize;
442
443 if (NULL == buf)
444 {
445 finish (handle, GNUNET_SYSERR);
446 return 0;
447 }
448
449 slen = strlen (handle->current->subsystem) + 1;
450 nlen = strlen (handle->current->name) + 1;
451 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
452 if (size < nsize)
453 {
454 GNUNET_break (0);
455 finish (handle, GNUNET_SYSERR);
456 return 0;
457 }
458 r = buf;
459 r->header.size = htons (nsize);
460 r->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
461 r->flags = 0;
462 r->value = GNUNET_htonll (handle->current->value);
463 if (handle->current->make_persistent)
464 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT);
465 if (handle->current->type == ACTION_UPDATE)
466 r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE);
467 GNUNET_assert (slen + nlen ==
468 GNUNET_STRINGS_buffer_fill ((char *) &r[1],
469 slen + nlen,
470 2,
471 handle->current->subsystem,
472 handle->current->name));
473 finish (handle, GNUNET_OK);
474 return nsize;
475}
476
477
478static size_t
479transmit_action (void *cls, size_t size, void *buf)
480{
481 struct GNUNET_STATISTICS_Handle *handle = cls;
482 size_t ret;
483
484 switch (handle->current->type)
485 {
486 case ACTION_GET:
487 ret = transmit_get (handle, size, buf);
488 break;
489 case ACTION_SET:
490 case ACTION_UPDATE:
491 ret = transmit_set (handle, size, buf);
492 break;
493 }
494 return ret;
495}
496
497
498/**
499 * Schedule the next action to be performed.
500 */
501static void
502schedule_action (struct GNUNET_STATISTICS_Handle *h)
503{
504 struct GNUNET_TIME_Relative timeout;
505
506 if (h->current != NULL)
507 return; /* action already pending */
508 if (GNUNET_YES != try_connect (h))
509 {
510 finish (h, GNUNET_SYSERR);
511 return;
512 }
513
514 /* schedule next action */
515 h->current = h->action_head;
516 if (NULL == h->current)
517 {
518 /* no pending network action, check destroy! */
519 if (h->do_destroy != GNUNET_YES)
520 return;
521 do_destroy (h);
522 return;
523 }
524 h->action_head = h->action_head->next;
525 if (NULL == h->action_head)
526 h->action_tail = NULL;
527 h->current->next = NULL;
528
529 timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout);
530 if (NULL ==
531 GNUNET_CLIENT_notify_transmit_ready (h->client,
532 h->current->msize,
533 timeout, &transmit_action, h))
534 {
535 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
536 "Failed to transmit request to statistics service.\n");
537 finish (h, GNUNET_SYSERR);
538 }
539}
540
541
542static void
543insert_ai (struct GNUNET_STATISTICS_Handle *h, struct ActionItem *ai)
544{
545 if (h->action_tail == NULL)
546 {
547 h->action_head = ai;
548 h->action_tail = ai;
549 schedule_action (h);
550 }
551 else
552 {
553 h->action_tail->next = ai;
554 h->action_tail = ai;
555 }
556}
557
558
559/**
560 * Get statistic from the peer.
561 *
562 * @param handle identification of the statistics service
563 * @param subsystem limit to the specified subsystem, NULL for our subsystem
564 * @param name name of the statistic value, NULL for all values
565 * @param timeout after how long should we give up (and call
566 * cont with an error code)?
567 * @param cont continuation to call when done (can be NULL)
568 * @param proc function to call on each value
569 * @param cls closure for cont and proc
570 */
571void
572GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle,
573 const char *subsystem,
574 const char *name,
575 struct GNUNET_TIME_Relative timeout,
576 GNUNET_STATISTICS_Callback cont,
577 GNUNET_STATISTICS_Iterator proc, void *cls)
578{
579 size_t slen1;
580 size_t slen2;
581 struct ActionItem *ai;
582
583 GNUNET_assert (handle != NULL);
584 GNUNET_assert (proc != NULL);
585 if (GNUNET_YES != try_connect (handle))
586 {
587 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
588 "Failed to connect to statistics service, can not get value `%s:%s'.\n",
589 strlen (subsystem) ? subsystem : "*",
590 strlen (name) ? name : "*");
591 cont (cls, GNUNET_SYSERR);
592 return;
593 }
594 if (subsystem == NULL)
595 subsystem = "";
596 if (name == NULL)
597 name = "";
598 slen1 = strlen (subsystem);
599 slen2 = strlen (name);
600 GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) <
601 GNUNET_SERVER_MAX_MESSAGE_SIZE);
602 ai = GNUNET_malloc (sizeof (struct ActionItem));
603 ai->subsystem = GNUNET_strdup (subsystem);
604 ai->name = GNUNET_strdup (name);
605 ai->cont = cont;
606 ai->proc = proc;
607 ai->cls = cls;
608 ai->timeout = GNUNET_TIME_relative_to_absolute (timeout);
609 ai->type = ACTION_GET;
610 ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader);
611 insert_ai (handle, ai);
612}
613
614
615static void
616add_setter_action (struct GNUNET_STATISTICS_Handle *h,
617 const char *name,
618 int make_persistent,
619 unsigned long long value, enum ActionType type)
620{
621 struct ActionItem *ai;
622 size_t slen;
623 size_t nlen;
624 size_t nsize;
625
626 GNUNET_assert (h != NULL);
627 GNUNET_assert (name != NULL);
628 if (GNUNET_YES != try_connect (h))
629 return;
630 slen = strlen (h->subsystem) + 1;
631 nlen = strlen (name) + 1;
632 nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen;
633 if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
634 {
635 GNUNET_break (0);
636 return;
637 }
638 ai = GNUNET_malloc (sizeof (struct ActionItem));
639 ai->subsystem = GNUNET_strdup (h->subsystem);
640 ai->name = GNUNET_strdup (name);
641 ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT);
642 ai->make_persistent = make_persistent;
643 ai->msize = nsize;
644 ai->value = value;
645 ai->type = type;
646 insert_ai (h, ai);
647 schedule_action (h);
648}
649
650
651/**
652 * Set statistic value for the peer. Will always use our
653 * subsystem (the argument used when "handle" was created).
654 *
655 * @param handle identification of the statistics service
656 * @param name name of the statistic value
657 * @param value new value to set
658 * @param make_persistent should the value be kept across restarts?
659 */
660void
661GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle,
662 const char *name,
663 unsigned long long value, int make_persistent)
664{
665 add_setter_action (handle, name, make_persistent, value, ACTION_SET);
666}
667
668
669/**
670 * Set statistic value for the peer. Will always use our
671 * subsystem (the argument used when "handle" was created).
672 *
673 * @param handle identification of the statistics service
674 * @param name name of the statistic value
675 * @param delta change in value (added to existing value)
676 * @param make_persistent should the value be kept across restarts?
677 */
678void
679GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle,
680 const char *name,
681 long long delta, int make_persistent)
682{
683 add_setter_action (handle, name, make_persistent,
684 (unsigned long long) delta, ACTION_UPDATE);
685}
686
687
688/* end of statistics_api.c */
diff --git a/src/statistics/test_gnunet_statistics.sh b/src/statistics/test_gnunet_statistics.sh
new file mode 100755
index 000000000..43f629f36
--- /dev/null
+++ b/src/statistics/test_gnunet_statistics.sh
@@ -0,0 +1,177 @@
1#!/bin/bash
2
3rm -rf /tmp/test-gnunetd-statistics/
4exe="./gnunet-statistics -c test_statistics_api_data.conf"
5base=/tmp/gnunet-test-statistics
6#DEBUG="-L DEBUG"
7# -----------------------------------
8echo -n "Preparing: Starting service..."
9./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf &
10sleep 1
11echo "DONE"
12
13# ----------------------------------------------------------------------------------
14echo -n "TEST: Bad argument checking..."
15
16if $exe -x 2> /dev/null; then
17 echo "FAIL: error running $exe"
18 kill %%
19 exit 1
20fi
21echo "PASS"
22
23# ----------------------------------------------------------------------------------
24echo -n "TEST: Set value..."
25
26if ! $exe $DEBUG -n test -s subsystem 42 ; then
27 echo "FAIL: error running $exe"
28 kill %%
29 exit 1
30fi
31echo "PASS"
32
33# ----------------------------------------------------------------------------------
34echo -n "TEST: Set another value..."
35
36if ! $exe $DEBUG -n other -s osystem 43 ; then
37 echo "FAIL: error running $exe"
38 kill %%
39 exit 1
40fi
41echo "PASS"
42
43# ----------------------------------------------------------------------------------
44echo -n "TEST: viewing all stats..."
45
46if ! $exe $DEBUG > $base.out; then
47 echo "FAIL: error running $exe"
48 kill %%
49 exit 1
50fi
51LINES=`cat $base.out | wc -l`
52if test $LINES -ne 2; then
53 echo "FAIL: unexpected output"
54 kill %%
55 exit 1
56fi
57echo "PASS"
58
59# ----------------------------------------------------------------------------------
60echo -n "TEST: viewing stats by name..."
61
62if ! $exe $DEBUG -n other > $base.out; then
63 echo "FAIL: error running $exe"
64 kill %%
65 exit 1
66fi
67LINES=`cat $base.out | grep 43 | wc -l`
68if test $LINES -ne 1; then
69 echo "FAIL: unexpected output"
70 kill %%
71 exit 1
72fi
73echo "PASS"
74
75# ----------------------------------------------------------------------------------
76echo -n "TEST: viewing stats by subsystem..."
77
78if ! $exe $DEBUG -s subsystem > $base.out; then
79 echo "FAIL: error running $exe"
80 kill %%
81 exit 1
82fi
83LINES=`cat $base.out | grep 42 | wc -l`
84if test $LINES -ne 1; then
85 echo "FAIL: unexpected output"
86 kill %%
87 exit 1
88fi
89echo "PASS"
90
91
92# ----------------------------------------------------------------------------------
93echo -n "TEST: Set persistent value..."
94
95if ! $exe $DEBUG -n lasting -s subsystem 40 -p; then
96 echo "FAIL: error running $exe"
97 kill %%
98 exit 1
99fi
100echo "PASS"
101
102# -----------------------------------
103echo -n "Restarting service..."
104sleep 1
105if ! kill %%;
106then
107 echo "FAIL: could not kill service"
108 kill %%
109 exit 1
110fi
111sleep 1
112./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf &
113sleep 1
114
115
116# ----------------------------------------------------------------------------------
117echo -n "TEST: checking persistence..."
118
119if ! $exe $DEBUG > $base.out; then
120 echo "FAIL: error running $exe"
121 kill %%
122 exit 1
123fi
124LINES=`cat $base.out | grep 40 | wc -l`
125if test $LINES -ne 1; then
126 echo "FAIL: unexpected output"
127 kill %%
128 exit 1
129fi
130echo "PASS"
131
132
133
134# ----------------------------------------------------------------------------------
135echo -n "TEST: Removing persistence..."
136
137if ! $exe $DEBUG -n lasting -s subsystem 40; then
138 echo "FAIL: error running $exe"
139 kill %%
140 exit 1
141fi
142echo "PASS"
143
144
145# -----------------------------------
146echo -n "Restarting service..."
147sleep 1
148if ! kill %%;
149then
150 echo "FAIL: could not kill service"
151 kill %%
152 exit 1
153fi
154sleep 1
155./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf &
156sleep 1
157
158
159# ----------------------------------------------------------------------------------
160echo -n "TEST: checking removed persistence..."
161
162if ! $exe $DEBUG > $base.out; then
163 echo "FAIL: error running $exe"
164 kill %%
165 exit 1
166fi
167LINES=`cat $base.out | grep 40 | wc -l`
168if test $LINES -ne 0; then
169 echo "FAIL: unexpected output"
170 kill %%
171 exit 1
172fi
173echo "PASS"
174
175kill %%
176rm -f $base.out
177rm -rf /tmp/test-gnunetd-statistics/
diff --git a/src/statistics/test_statistics_api.c b/src/statistics/test_statistics_api.c
new file mode 100644
index 000000000..7a39f54b6
--- /dev/null
+++ b/src/statistics/test_statistics_api.c
@@ -0,0 +1,177 @@
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 statistics/test_statistics_api.c
22 * @brief testcase for statistics_api.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_getopt_lib.h"
27#include "gnunet_os_lib.h"
28#include "gnunet_program_lib.h"
29#include "gnunet_scheduler_lib.h"
30#include "gnunet_statistics_service.h"
31
32#define VERBOSE GNUNET_NO
33
34static int
35check_1 (void *cls,
36 const char *subsystem,
37 const char *name, unsigned long long value, int is_persistent)
38{
39 GNUNET_assert (0 == strcmp (name, "test-1"));
40 GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api"));
41 GNUNET_assert (value == 1);
42 GNUNET_assert (is_persistent == GNUNET_NO);
43 return GNUNET_OK;
44}
45
46static int
47check_2 (void *cls,
48 const char *subsystem,
49 const char *name, unsigned long long value, int is_persistent)
50{
51 GNUNET_assert (0 == strcmp (name, "test-2"));
52 GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api"));
53 GNUNET_assert (value == 2);
54 GNUNET_assert (is_persistent == GNUNET_NO);
55 return GNUNET_OK;
56}
57
58static int
59check_3 (void *cls,
60 const char *subsystem,
61 const char *name, unsigned long long value, int is_persistent)
62{
63 GNUNET_assert (0 == strcmp (name, "test-3"));
64 GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api"));
65 GNUNET_assert (value == 3);
66 GNUNET_assert (is_persistent == GNUNET_YES);
67 return GNUNET_OK;
68}
69
70static struct GNUNET_STATISTICS_Handle *h;
71
72static void
73next_fin (void *cls, int success)
74{
75 int *ok = cls;
76
77 GNUNET_STATISTICS_destroy (h);
78 GNUNET_assert (success == GNUNET_OK);
79 *ok = 0;
80}
81
82static void
83next (void *cls, int success)
84{
85 GNUNET_assert (success == GNUNET_OK);
86 GNUNET_STATISTICS_get (h, NULL, "test-2",
87 GNUNET_TIME_UNIT_SECONDS, &next_fin, &check_2, cls);
88}
89
90static void
91run (void *cls,
92 struct GNUNET_SCHEDULER_Handle *sched,
93 char *const *args,
94 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
95{
96
97 h = GNUNET_STATISTICS_create (sched, "test-statistics-api", cfg);
98 GNUNET_STATISTICS_set (h, "test-1", 1, GNUNET_NO);
99 GNUNET_STATISTICS_set (h, "test-2", 2, GNUNET_NO);
100 GNUNET_STATISTICS_set (h, "test-3", 2, GNUNET_NO);
101 GNUNET_STATISTICS_update (h, "test-3", 1, GNUNET_YES);
102 GNUNET_STATISTICS_get (h, NULL, "test-1",
103 GNUNET_TIME_UNIT_SECONDS, &next, &check_1, cls);
104}
105
106static void
107run_more (void *cls,
108 struct GNUNET_SCHEDULER_Handle *sched,
109 char *const *args,
110 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
111{
112 h = GNUNET_STATISTICS_create (sched, "test-statistics-api", cfg);
113 GNUNET_STATISTICS_get (h, NULL, "test-3",
114 GNUNET_TIME_UNIT_SECONDS, &next_fin, &check_3, cls);
115}
116
117static int
118check ()
119{
120 int ok = 1;
121 pid_t pid;
122 char *const argv[] = { "test-statistics-api",
123 "-c",
124 "test_statistics_api_data.conf",
125 NULL
126 };
127 struct GNUNET_GETOPT_CommandLineOption options[] = {
128 GNUNET_GETOPT_OPTION_END
129 };
130 pid = GNUNET_OS_start_process ("gnunet-service-statistics",
131 "gnunet-service-statistics",
132#if DEBUG_STATISTICS
133 "-L", "DEBUG",
134#endif
135 "-c", "test_statistics_api_data.conf", NULL);
136 sleep (1);
137 GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp",
138 options, &run, &ok);
139 if (0 != PLIBC_KILL (pid, SIGTERM))
140 {
141 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
142 ok = 1;
143 }
144 waitpid (pid, NULL, 0);
145 if (ok != 0)
146 return ok;
147 ok = 1;
148 /* restart to check persistence! */
149 pid = GNUNET_OS_start_process ("gnunet-service-statistics",
150 "gnunet-service-statistics",
151#if DEBUG_STATISTICS
152 "-L", "DEBUG",
153#endif
154 "-c", "test_statistics_api_data.conf", NULL);
155 sleep (1);
156 GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp",
157 options, &run_more, &ok);
158 if (0 != PLIBC_KILL (pid, SIGTERM))
159 {
160 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
161 ok = 1;
162 }
163 waitpid (pid, NULL, 0);
164 return ok;
165}
166
167int
168main (int argc, char *argv[])
169{
170 int ret;
171
172 ret = check ();
173
174 return ret;
175}
176
177/* end of test_statistics_api.c */
diff --git a/src/statistics/test_statistics_api_data.conf b/src/statistics/test_statistics_api_data.conf
new file mode 100644
index 000000000..571a9b3e4
--- /dev/null
+++ b/src/statistics/test_statistics_api_data.conf
@@ -0,0 +1,5 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-statistics/
3
4[statistics]
5PORT = 22353
diff --git a/src/template/Makefile.am b/src/template/Makefile.am
new file mode 100644
index 000000000..47d6b4165
--- /dev/null
+++ b/src/template/Makefile.am
@@ -0,0 +1,37 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11bin_PROGRAMS = \
12 gnunet-template \
13 gnunet-service-template
14
15gnunet_template_SOURCES = \
16 gnunet-template.c
17gnunet_template_LDADD = \
18 $(top_builddir)/src/util/libgnunetutil.la \
19 $(GN_LIBINTL)
20
21gnunet_service_template_SOURCES = \
22 gnunet-service-template.c
23gnunet_service_template_LDADD = \
24 $(top_builddir)/src/util/libgnunetutil.la \
25 $(GN_LIBINTL)
26
27
28check_PROGRAMS = \
29 test_template_api
30
31TESTS = $(check_PROGRAMS)
32
33test_template_api_SOURCES = \
34 test_template_api.c
35test_template_api_LDADD = \
36 $(top_builddir)/src/util/libgnunetutil.la
37
diff --git a/src/template/gnunet-service-template.c b/src/template/gnunet-service-template.c
new file mode 100644
index 000000000..c43d681d7
--- /dev/null
+++ b/src/template/gnunet-service-template.c
@@ -0,0 +1,87 @@
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 template/gnunet-service-template.c
23 * @brief program that tracks template
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_service_lib.h"
29
30/**
31 * Do cleanup here.
32 *
33 * @param cls closure
34 * @param cfg configuration to use
35 */
36static void
37finish (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
38{
39 /* FIXME */
40}
41
42
43/**
44 * List of handlers for the messages understood by this
45 * service.
46 */
47static struct GNUNET_SERVER_MessageHandler handlers[] = {
48 /* FIXME: add handlers here! */
49 {NULL, NULL, 0, 0}
50};
51
52/**
53 * Process template requests.
54 *
55 * @param cls closure
56 * @param sched scheduler to use
57 * @param server the initialized server
58 * @param cfg configuration to use
59 */
60static void
61run (void *cls,
62 struct GNUNET_SCHEDULER_Handle *sched,
63 struct GNUNET_SERVER_Handle *server,
64 struct GNUNET_CONFIGURATION_Handle *cfg)
65{
66 /* FIXME: do setup here */
67 GNUNET_SERVER_add_handlers (server, handlers);
68}
69
70
71/**
72 * The main function for the template service.
73 *
74 * @param argc number of arguments from the command line
75 * @param argv command line arguments
76 * @return 0 ok, 1 on error
77 */
78int
79main (int argc, char *const *argv)
80{
81 return (GNUNET_OK ==
82 GNUNET_SERVICE_run (argc,
83 argv,
84 "template", &run, NULL, &finish, NULL)) ? 0 : 1;
85}
86
87/* end of gnunet-service-template.c */
diff --git a/src/template/gnunet-template.c b/src/template/gnunet-template.c
new file mode 100644
index 000000000..ea47c7f45
--- /dev/null
+++ b/src/template/gnunet-template.c
@@ -0,0 +1,81 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2004, 2005, 2006, 2007, 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 template/gnunet-template.c
23 * @brief template for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_program_lib.h"
29/* #include "gnunet_template_service.h" */
30
31/**
32 * Final status code.
33 */
34static int ret;
35
36/**
37 * Main function that will be run by the scheduler.
38 *
39 * @param cls closure
40 * @param sched the scheduler to use
41 * @param args remaining command-line arguments
42 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
43 * @param cfg configuration
44 */
45static void
46run (void *cls,
47 struct GNUNET_SCHEDULER_Handle *sched,
48 char *const *args,
49 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
50{
51 /* main code here */
52}
53
54/**
55 * gnunet-template command line options
56 */
57static struct GNUNET_GETOPT_CommandLineOption options[] = {
58 /* FIMXE: add options here */
59 GNUNET_GETOPT_OPTION_END
60};
61
62
63/**
64 * The main function to obtain template from gnunetd.
65 *
66 * @param argc number of arguments from the command line
67 * @param argv command line arguments
68 * @return 0 ok, 1 on error
69 */
70int
71main (int argc, char *const *argv)
72{
73 return (GNUNET_OK ==
74 GNUNET_PROGRAM_run (argc,
75 argv,
76 "gnunet-template",
77 gettext_noop ("help text"),
78 options, &run, NULL)) ? ret : 1;
79}
80
81/* end of gnunet-template.c */
diff --git a/src/template/test_template_api.c b/src/template/test_template_api.c
new file mode 100644
index 000000000..8d53a15da
--- /dev/null
+++ b/src/template/test_template_api.c
@@ -0,0 +1,45 @@
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 template/test_template.c
22 * @brief testcase for template.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26
27#define VERBOSE GNUNET_NO
28
29static int
30check ()
31{
32 return 0;
33}
34
35int
36main (int argc, char *argv[])
37{
38 int ret;
39
40 ret = check ();
41
42 return ret;
43}
44
45/* end of test_template.c */
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am
new file mode 100644
index 000000000..236dec7c4
--- /dev/null
+++ b/src/transport/Makefile.am
@@ -0,0 +1,84 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3plugindir = $(libdir)/gnunet
4
5if MINGW
6 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
7endif
8
9if USE_COVERAGE
10 AM_CFLAGS = -fprofile-arcs -ftest-coverage
11endif
12
13
14lib_LTLIBRARIES = \
15 libgnunettransport.la
16
17libgnunettransport_la_SOURCES = \
18 transport_api.c transport.h
19libgnunettransport_la_LIBADD = \
20 $(top_builddir)/src/arm/libgnunetarm.la \
21 $(top_builddir)/src/hello/libgnunethello.la \
22 $(top_builddir)/src/util/libgnunetutil.la \
23 $(GN_LIBINTL)
24libgnunettransport_la_LDFLAGS = \
25 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
26 -version-info 0:0:0
27
28
29bin_PROGRAMS = \
30 gnunet-transport \
31 gnunet-service-transport
32
33gnunet_transport_SOURCES = \
34 gnunet-transport.c
35gnunet_transport_LDADD = \
36 $(top_builddir)/src/transport/libgnunettransport.la \
37 $(top_builddir)/src/util/libgnunetutil.la \
38 $(GN_LIBINTL)
39
40gnunet_service_transport_SOURCES = \
41 gnunet-service-transport.c
42gnunet_service_transport_LDADD = \
43 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
44 $(top_builddir)/src/util/libgnunetutil.la \
45 $(GN_LIBINTL)
46
47
48
49plugin_LTLIBRARIES = \
50 libgnunet_plugin_transport_tcp.la \
51 libgnunet_plugin_transport_template.la
52# TODO: add udp, http, nat, etc.
53
54libgnunet_plugin_transport_tcp_la_SOURCES = \
55 plugin_transport_tcp.c
56libgnunet_plugin_transport_tcp_la_LIBADD = \
57 $(top_builddir)/src/resolver/libgnunetresolver.la \
58 $(top_builddir)/src/util/libgnunetutil.la
59libgnunet_plugin_transport_tcp_la_LDFLAGS = \
60 $(GN_PLUGIN_LDFLAGS)
61
62libgnunet_plugin_transport_template_la_SOURCES = \
63 plugin_transport_template.c
64libgnunet_plugin_transport_template_la_LDFLAGS = \
65 $(GN_PLUGIN_LDFLAGS)
66
67
68check_PROGRAMS = \
69 test_transport_api
70# TODO: add tests for tcp, udp, http, nat, etc.
71
72TESTS = $(check_PROGRAMS)
73
74test_transport_api_SOURCES = \
75 test_transport_api.c
76test_transport_api_LDADD = \
77 $(top_builddir)/src/transport/libgnunettransport.la \
78 $(top_builddir)/src/util/libgnunetutil.la
79
80
81EXTRA_DIST = \
82 test_transport_api_data.conf \
83 test_transport_api_peer1.conf \
84 test_transport_api_peer2.conf
diff --git a/src/transport/NOTES b/src/transport/NOTES
new file mode 100644
index 000000000..41404e1f9
--- /dev/null
+++ b/src/transport/NOTES
@@ -0,0 +1,46 @@
1KEY DESIGN CHOICES:
2 - who decides which connections to keep/create?
3 => higher level session/key management!
4 - who enforces things like F2F topology, etc?
5 => higher level session/key management!
6 - who tracks all known HELLOs & validates?
7 => We validate, PEERINFO tracks!
8 - who advertises our HELLO?
9 => us! (need background job; previously: advertising)
10 - who advertises other peers HELLOs?
11 => higher level (core?)
12 - who does bootstrapping?
13 => bootstrap service (external!)
14 - who enforces inbound bandwidth limits?
15 => transport-service and plugins! (previously: core);
16 either by limiting reads (TCP) or discarding packets
17 (transport-service)
18 - who enforces outbound bandwidth limits?
19 => transport_api!
20 - who decides outbound bandwidth limits?
21 => other peer, via core (need authenticated limits!)
22 - who decides inbound bandwidth limits?
23 => core / apps above core (need trust info)
24 - cost function for transports is latency estimate in ms
25 => plugin provides latency data, transport-service
26 selects plugin(s) for transmission
27 - who is responsible for fragmentation?
28 => plugins! (may use common shared library)
29 - should we require UDP to be reliable?
30 => NO. There are other places that may (rarely)
31 use messages that we can not fix
32 - how do we access the 50% of service that we need for TCP/UDP
33 from service.c without code replication or getting 50%
34 that we do not want (i.e. shutdown, pid-file-writing, etc.)
35 => use GNUNET_SERVICE_start/stop functions!
36 - At what level do we manage timeouts?
37 => At the plugin (TCP connections),
38 transport-service (neighbours) and
39 core (sessions) level!
40 => All plugins have to disconnect before service-level
41 disconnect occurs
42 => We can have a plugin-connection die, but the session
43 survives!
44 => We can have a session die (no further authenticated
45 communication) even if the plugin thinks it is still
46 up!
diff --git a/src/transport/gnunet-service-transport.c b/src/transport/gnunet-service-transport.c
new file mode 100644
index 000000000..08745c378
--- /dev/null
+++ b/src/transport/gnunet-service-transport.c
@@ -0,0 +1,2852 @@
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 transport/gnunet-service-transport.c
23 * @brief low-level P2P messaging
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - if we do not receive an ACK in response to our
28 * HELLO, retransmit HELLO!
29 */
30#include "platform.h"
31#include "gnunet_client_lib.h"
32#include "gnunet_getopt_lib.h"
33#include "gnunet_hello_lib.h"
34#include "gnunet_os_lib.h"
35#include "gnunet_peerinfo_service.h"
36#include "gnunet_plugin_lib.h"
37#include "gnunet_protocols.h"
38#include "gnunet_service_lib.h"
39#include "gnunet_signatures.h"
40#include "plugin_transport.h"
41#include "transport.h"
42
43/**
44 * How many messages can we have pending for a given client process
45 * before we start to drop incoming messages? We typically should
46 * have only one client and so this would be the primary buffer for
47 * messages, so the number should be chosen rather generously.
48 *
49 * The expectation here is that most of the time the queue is large
50 * enough so that a drop is virtually never required.
51 */
52#define MAX_PENDING 128
53
54/**
55 * How often should we try to reconnect to a peer using a particular
56 * transport plugin before giving up? Note that the plugin may be
57 * added back to the list after PLUGIN_RETRY_FREQUENCY expires.
58 */
59#define MAX_CONNECT_RETRY 3
60
61/**
62 * How often must a peer violate bandwidth quotas before we start
63 * to simply drop its messages?
64 */
65#define QUOTA_VIOLATION_DROP_THRESHOLD 100
66
67/**
68 * How long until a HELLO verification attempt should time out?
69 */
70#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_UNIT_MINUTES
71
72/**
73 * How often do we re-add (cheaper) plugins to our list of plugins
74 * to try for a given connected peer?
75 */
76#define PLUGIN_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
77
78/**
79 * After how long do we expire an address in a HELLO
80 * that we just validated? This value is also used
81 * for our own addresses when we create a HELLO.
82 */
83#define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
84
85/**
86 * After how long do we consider a connection to a peer dead
87 * if we don't receive messages from the peer?
88 */
89#define IDLE_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
90
91
92/**
93 * Entry in linked list of network addresses.
94 */
95struct AddressList
96{
97 /**
98 * This is a linked list.
99 */
100 struct AddressList *next;
101
102 /**
103 * The address, actually a pointer to the end
104 * of this struct. Do not free!
105 */
106 void *addr;
107
108 /**
109 * How long until we auto-expire this address (unless it is
110 * re-confirmed by the transport)?
111 */
112 struct GNUNET_TIME_Absolute expires;
113
114 /**
115 * Length of addr.
116 */
117 size_t addrlen;
118
119};
120
121
122/**
123 * Entry in linked list of all of our plugins.
124 */
125struct TransportPlugin
126{
127
128 /**
129 * This is a linked list.
130 */
131 struct TransportPlugin *next;
132
133 /**
134 * API of the transport as returned by the plugin's
135 * initialization function.
136 */
137 struct GNUNET_TRANSPORT_PluginFunctions *api;
138
139 /**
140 * Short name for the plugin (i.e. "tcp").
141 */
142 char *short_name;
143
144 /**
145 * Name of the library (i.e. "gnunet_plugin_transport_tcp").
146 */
147 char *lib_name;
148
149 /**
150 * List of our known addresses for this transport.
151 */
152 struct AddressList *addresses;
153
154 /**
155 * Environment this transport service is using
156 * for this plugin.
157 */
158 struct GNUNET_TRANSPORT_PluginEnvironment env;
159
160 /**
161 * ID of task that is used to clean up expired addresses.
162 */
163 GNUNET_SCHEDULER_TaskIdentifier address_update_task;
164
165
166 /**
167 * Set to GNUNET_YES if we need to scrap the existing
168 * list of "addresses" and start fresh when we receive
169 * the next address update from a transport. Set to
170 * GNUNET_NO if we should just add the new address
171 * to the list and wait for the commit call.
172 */
173 int rebuild;
174};
175
176struct NeighbourList;
177
178/**
179 * For each neighbour we keep a list of messages
180 * that we still want to transmit to the neighbour.
181 */
182struct MessageQueue
183{
184
185 /**
186 * This is a linked list.
187 */
188 struct MessageQueue *next;
189
190 /**
191 * The message we want to transmit.
192 */
193 struct GNUNET_MessageHeader *message;
194
195 /**
196 * Client responsible for queueing the message;
197 * used to check that a client has not two messages
198 * pending for the same target. Can be NULL.
199 */
200 struct TransportClient *client;
201
202 /**
203 * Neighbour this entry belongs to.
204 */
205 struct NeighbourList *neighbour;
206
207 /**
208 * Plugin that we used for the transmission.
209 * NULL until we scheduled a transmission.
210 */
211 struct TransportPlugin *plugin;
212
213 /**
214 * Internal message of the transport system that should not be
215 * included in the usual SEND-SEND_OK transmission confirmation
216 * traffic management scheme. Typically, "internal_msg" will
217 * be set whenever "client" is NULL (but it is not strictly
218 * required).
219 */
220 int internal_msg;
221
222};
223
224
225/**
226 * For a given Neighbour, which plugins are available
227 * to talk to this peer and what are their costs?
228 */
229struct ReadyList
230{
231
232 /**
233 * This is a linked list.
234 */
235 struct ReadyList *next;
236
237 /**
238 * Which of our transport plugins does this entry
239 * represent?
240 */
241 struct TransportPlugin *plugin;
242
243 /**
244 * Neighbour this entry belongs to.
245 */
246 struct NeighbourList *neighbour;
247
248 /**
249 * Opaque handle (specific to the plugin) for the
250 * connection to our target; can be NULL.
251 */
252 void *plugin_handle;
253
254 /**
255 * What was the last latency observed for this plugin
256 * and peer? Invalid if connected is GNUNET_NO.
257 */
258 struct GNUNET_TIME_Relative latency;
259
260 /**
261 * If we did not successfully transmit a message to the
262 * given peer via this connection during the specified
263 * time, we should consider the connection to be dead.
264 * This is used in the case that a TCP transport simply
265 * stalls writing to the stream but does not formerly
266 * get a signal that the other peer died.
267 */
268 struct GNUNET_TIME_Absolute timeout;
269
270 /**
271 * Is this plugin currently connected? The first time
272 * we transmit or send data to a peer via a particular
273 * plugin, we set this to GNUNET_YES. If we later get
274 * an error (disconnect notification or transmission
275 * failure), we set it back to GNUNET_NO. Each time the
276 * value is set to GNUNET_YES, we increment the
277 * "connect_attempts" counter. If that one reaches a
278 * particular threshold, we consider the plugin to not
279 * be working properly at this time for the given peer
280 * and remove it from the eligible list.
281 */
282 int connected;
283
284 /**
285 * How often have we tried to connect using this plugin?
286 */
287 unsigned int connect_attempts;
288
289 /**
290 * Is this plugin ready to transmit to the specific
291 * target? GNUNET_NO if not. Initially, all plugins
292 * are marked ready. If a transmission is in progress,
293 * "transmit_ready" is set to GNUNET_NO.
294 */
295 int transmit_ready;
296
297};
298
299
300/**
301 * Entry in linked list of all of our current neighbours.
302 */
303struct NeighbourList
304{
305
306 /**
307 * This is a linked list.
308 */
309 struct NeighbourList *next;
310
311 /**
312 * Which of our transports is connected to this peer
313 * and what is their status?
314 */
315 struct ReadyList *plugins;
316
317 /**
318 * List of messages we would like to send to this peer;
319 * must contain at most one message per client.
320 */
321 struct MessageQueue *messages;
322
323 /**
324 * Identity of this neighbour.
325 */
326 struct GNUNET_PeerIdentity id;
327
328 /**
329 * ID of task scheduled to run when this peer is about to
330 * time out (will free resources associated with the peer).
331 */
332 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
333
334 /**
335 * How long until we should consider this peer dead
336 * (if we don't receive another message in the
337 * meantime)?
338 */
339 struct GNUNET_TIME_Absolute peer_timeout;
340
341 /**
342 * At what time did we reset last_received last?
343 */
344 struct GNUNET_TIME_Absolute last_quota_update;
345
346 /**
347 * At what time should we try to again add plugins to
348 * our ready list?
349 */
350 struct GNUNET_TIME_Absolute retry_plugins_time;
351
352 /**
353 * How many bytes have we received since the "last_quota_update"
354 * timestamp?
355 */
356 uint64_t last_received;
357
358 /**
359 * Global quota for outbound traffic for the neighbour in bytes/ms.
360 */
361 uint32_t quota_in;
362
363 /**
364 * What is the latest version of our HELLO that we have
365 * sent to this neighbour?
366 */
367 unsigned int hello_version_sent;
368
369 /**
370 * How often has the other peer (recently) violated the
371 * inbound traffic limit? Incremented by 10 per violation,
372 * decremented by 1 per non-violation (for each
373 * time interval).
374 */
375 unsigned int quota_violation_count;
376
377 /**
378 * Have we seen an ACK from this neighbour in the past?
379 * (used to make up a fake ACK for clients connecting after
380 * the neighbour connected to us).
381 */
382 int saw_ack;
383
384};
385
386
387/**
388 * Linked list of messages to be transmitted to
389 * the client. Each entry is followed by the
390 * actual message.
391 */
392struct ClientMessageQueueEntry
393{
394 /**
395 * This is a linked list.
396 */
397 struct ClientMessageQueueEntry *next;
398};
399
400
401/**
402 * Client connected to the transport service.
403 */
404struct TransportClient
405{
406
407 /**
408 * This is a linked list.
409 */
410 struct TransportClient *next;
411
412 /**
413 * Handle to the client.
414 */
415 struct GNUNET_SERVER_Client *client;
416
417 /**
418 * Linked list of messages yet to be transmitted to
419 * the client.
420 */
421 struct ClientMessageQueueEntry *message_queue_head;
422
423 /**
424 * Tail of linked list of messages yet to be transmitted to the
425 * client.
426 */
427 struct ClientMessageQueueEntry *message_queue_tail;
428
429 /**
430 * Is a call to "transmit_send_continuation" pending? If so, we
431 * must not free this struct (even if the corresponding client
432 * disconnects) and instead only remove it from the linked list and
433 * set the "client" field to NULL.
434 */
435 int tcs_pending;
436
437 /**
438 * Length of the list of messages pending for this client.
439 */
440 unsigned int message_count;
441
442};
443
444
445/**
446 * Message used to ask a peer to validate receipt (to check an address
447 * from a HELLO). Followed by the address used. Note that the
448 * recipients response does not affirm that he has this address,
449 * only that he got the challenge message.
450 */
451struct ValidationChallengeMessage
452{
453
454 /**
455 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING
456 */
457 struct GNUNET_MessageHeader header;
458
459 /**
460 * What are we signing and why?
461 */
462 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
463
464 /**
465 * Random challenge number (in network byte order).
466 */
467 uint32_t challenge GNUNET_PACKED;
468
469 /**
470 * Who is the intended recipient?
471 */
472 struct GNUNET_PeerIdentity target;
473};
474
475
476/**
477 * Message used to validate a HELLO. If this was
478 * the right recipient, the response is a signature
479 * of the original validation request. The
480 * challenge is included in the confirmation to make
481 * matching of replies to requests possible.
482 */
483struct ValidationChallengeResponse
484{
485
486 /**
487 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
488 */
489 struct GNUNET_MessageHeader header;
490
491 /**
492 * Random challenge number (in network byte order).
493 */
494 uint32_t challenge GNUNET_PACKED;
495
496 /**
497 * Who signed this message?
498 */
499 struct GNUNET_PeerIdentity sender;
500
501 /**
502 * Signature.
503 */
504 struct GNUNET_CRYPTO_RsaSignature signature;
505
506};
507
508
509/**
510 * For each HELLO, we may have to validate multiple addresses;
511 * each address gets its own request entry.
512 */
513struct ValidationAddress
514{
515 /**
516 * This is a linked list.
517 */
518 struct ValidationAddress *next;
519
520 /**
521 * Our challenge message. Points to after this
522 * struct, so this field should not be freed.
523 */
524 struct ValidationChallengeMessage *msg;
525
526 /**
527 * Name of the transport.
528 */
529 char *transport_name;
530
531 /**
532 * When should this validated address expire?
533 */
534 struct GNUNET_TIME_Absolute expiration;
535
536 /**
537 * Length of the address we are validating.
538 */
539 size_t addr_len;
540
541 /**
542 * Set to GNUNET_YES if the challenge was met,
543 * GNUNET_SYSERR if we know it failed, GNUNET_NO
544 * if we are waiting on a response.
545 */
546 int ok;
547};
548
549
550/**
551 * Entry in linked list of all HELLOs awaiting validation.
552 */
553struct ValidationList
554{
555
556 /**
557 * This is a linked list.
558 */
559 struct ValidationList *next;
560
561 /**
562 * Linked list with one entry per address from the HELLO
563 * that needs to be validated.
564 */
565 struct ValidationAddress *addresses;
566
567 /**
568 * The public key of the peer.
569 */
570 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
571
572 /**
573 * When does this record time-out? (assuming the
574 * challenge goes unanswered)
575 */
576 struct GNUNET_TIME_Absolute timeout;
577
578};
579
580
581/**
582 * HELLOs awaiting validation.
583 */
584static struct ValidationList *pending_validations;
585
586/**
587 * Our HELLO message.
588 */
589static struct GNUNET_HELLO_Message *our_hello;
590
591/**
592 * "version" of "our_hello". Used to see if a given
593 * neighbour has already been sent the latest version
594 * of our HELLO message.
595 */
596static unsigned int our_hello_version;
597
598/**
599 * Our public key.
600 */
601static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
602
603/**
604 * Our identity.
605 */
606static struct GNUNET_PeerIdentity my_identity;
607
608/**
609 * Our private key.
610 */
611static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
612
613/**
614 * Our scheduler.
615 */
616struct GNUNET_SCHEDULER_Handle *sched;
617
618/**
619 * Our configuration.
620 */
621struct GNUNET_CONFIGURATION_Handle *cfg;
622
623/**
624 * Linked list of all clients to this service.
625 */
626static struct TransportClient *clients;
627
628/**
629 * All loaded plugins.
630 */
631static struct TransportPlugin *plugins;
632
633/**
634 * Our server.
635 */
636static struct GNUNET_SERVER_Handle *server;
637
638/**
639 * All known neighbours and their HELLOs.
640 */
641static struct NeighbourList *neighbours;
642
643/**
644 * Default bandwidth quota for receiving for new peers in bytes/ms.
645 */
646static uint32_t default_quota_in;
647
648/**
649 * Default bandwidth quota for sending for new peers in bytes/ms.
650 */
651static uint32_t default_quota_out;
652
653/**
654 * Number of neighbours we'd like to have.
655 */
656static uint32_t max_connect_per_transport;
657
658
659/**
660 * Find an entry in the neighbour list for a particular peer.
661 *
662 * @return NULL if not found.
663 */
664static struct NeighbourList *
665find_neighbour (const struct GNUNET_PeerIdentity *key)
666{
667 struct NeighbourList *head = neighbours;
668 while ((head != NULL) &&
669 (0 != memcmp (key, &head->id, sizeof (struct GNUNET_PeerIdentity))))
670 head = head->next;
671 return head;
672}
673
674
675/**
676 * Find an entry in the transport list for a particular transport.
677 *
678 * @return NULL if not found.
679 */
680static struct TransportPlugin *
681find_transport (const char *short_name)
682{
683 struct TransportPlugin *head = plugins;
684 while ((head != NULL) && (0 != strcmp (short_name, head->short_name)))
685 head = head->next;
686 return head;
687}
688
689
690/**
691 * Update the quota values for the given neighbour now.
692 */
693static void
694update_quota (struct NeighbourList *n)
695{
696 struct GNUNET_TIME_Relative delta;
697 uint64_t allowed;
698 uint64_t remaining;
699
700 delta = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
701 if (delta.value < MIN_QUOTA_REFRESH_TIME)
702 return; /* not enough time passed for doing quota update */
703 allowed = delta.value * n->quota_in;
704 if (n->last_received < allowed)
705 {
706 remaining = allowed - n->last_received;
707 if (n->quota_in > 0)
708 remaining /= n->quota_in;
709 else
710 remaining = 0;
711 if (remaining > MAX_BANDWIDTH_CARRY)
712 remaining = MAX_BANDWIDTH_CARRY;
713 n->last_received = 0;
714 n->last_quota_update = GNUNET_TIME_absolute_get ();
715 n->last_quota_update.value -= remaining;
716 if (n->quota_violation_count > 0)
717 n->quota_violation_count--;
718 }
719 else
720 {
721 n->last_received -= allowed;
722 n->last_quota_update = GNUNET_TIME_absolute_get ();
723 if (n->last_received > allowed)
724 {
725 /* more than twice the allowed rate! */
726 n->quota_violation_count += 10;
727 }
728 }
729}
730
731
732/**
733 * Function called to notify a client about the socket
734 * being ready to queue more data. "buf" will be
735 * NULL and "size" zero if the socket was closed for
736 * writing in the meantime.
737 *
738 * @param cls closure
739 * @param size number of bytes available in buf
740 * @param buf where the callee should write the message
741 * @return number of bytes written to buf
742 */
743static size_t
744transmit_to_client_callback (void *cls, size_t size, void *buf)
745{
746 struct TransportClient *client = cls;
747 struct ClientMessageQueueEntry *q;
748 uint16_t msize;
749 size_t tsize;
750 const struct GNUNET_MessageHeader *msg;
751 struct GNUNET_NETWORK_TransmitHandle *th;
752 char *cbuf;
753
754 if (buf == NULL)
755 {
756 /* fatal error with client, free message queue! */
757 while (NULL != (q = client->message_queue_head))
758 {
759 client->message_queue_head = q->next;
760 GNUNET_free (q);
761 }
762 client->message_queue_tail = NULL;
763 client->message_count = 0;
764 return 0;
765 }
766 cbuf = buf;
767 tsize = 0;
768 while (NULL != (q = client->message_queue_head))
769 {
770 msg = (const struct GNUNET_MessageHeader *) &q[1];
771 msize = ntohs (msg->size);
772 if (msize + tsize > size)
773 break;
774 client->message_queue_head = q->next;
775 if (q->next == NULL)
776 client->message_queue_tail = NULL;
777 memcpy (&cbuf[tsize], msg, msize);
778 tsize += msize;
779 GNUNET_free (q);
780 client->message_count--;
781 }
782 GNUNET_assert (tsize > 0);
783 if (NULL != q)
784 {
785 th = GNUNET_SERVER_notify_transmit_ready (client->client,
786 msize,
787 GNUNET_TIME_UNIT_FOREVER_REL,
788 &transmit_to_client_callback,
789 client);
790 GNUNET_assert (th != NULL);
791 }
792 return tsize;
793}
794
795
796/**
797 * Send the specified message to the specified client. Since multiple
798 * messages may be pending for the same client at a time, this code
799 * makes sure that no message is lost.
800 *
801 * @param client client to transmit the message to
802 * @param msg the message to send
803 * @param may_drop can this message be dropped if the
804 * message queue for this client is getting far too large?
805 */
806static void
807transmit_to_client (struct TransportClient *client,
808 const struct GNUNET_MessageHeader *msg, int may_drop)
809{
810 struct ClientMessageQueueEntry *q;
811 uint16_t msize;
812 struct GNUNET_NETWORK_TransmitHandle *th;
813
814 if ((client->message_count >= MAX_PENDING) && (GNUNET_YES == may_drop))
815 {
816 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
817 _
818 ("Dropping message, have %u messages pending (%u is the soft limit)\n"),
819 client->message_count, MAX_PENDING);
820 /* TODO: call to statistics... */
821 return;
822 }
823 client->message_count++;
824 msize = ntohs (msg->size);
825 q = GNUNET_malloc (sizeof (struct ClientMessageQueueEntry) + msize);
826 memcpy (&q[1], msg, msize);
827 /* append to message queue */
828 if (client->message_queue_tail == NULL)
829 {
830 client->message_queue_tail = q;
831 }
832 else
833 {
834 client->message_queue_tail->next = q;
835 client->message_queue_tail = q;
836 }
837 if (client->message_queue_head == NULL)
838 {
839 client->message_queue_head = q;
840 th = GNUNET_SERVER_notify_transmit_ready (client->client,
841 msize,
842 GNUNET_TIME_UNIT_FOREVER_REL,
843 &transmit_to_client_callback,
844 client);
845 GNUNET_assert (th != NULL);
846 }
847}
848
849
850/**
851 * Find alternative plugins for communication.
852 *
853 * @param neighbour for which neighbour should we try to find
854 * more plugins?
855 */
856static void
857try_alternative_plugins (struct NeighbourList *neighbour)
858{
859 struct ReadyList *rl;
860
861 if ((neighbour->plugins != NULL) &&
862 (neighbour->retry_plugins_time.value >
863 GNUNET_TIME_absolute_get ().value))
864 return; /* don't try right now */
865 neighbour->retry_plugins_time
866 = GNUNET_TIME_relative_to_absolute (PLUGIN_RETRY_FREQUENCY);
867
868 rl = neighbour->plugins;
869 while (rl != NULL)
870 {
871 if (rl->connect_attempts > 0)
872 rl->connect_attempts--; /* amnesty */
873 rl = rl->next;
874 }
875
876}
877
878
879/**
880 * Check the ready list for the given neighbour and
881 * if a plugin is ready for transmission (and if we
882 * have a message), do so!
883 *
884 * @param neighbour target peer for which to check the plugins
885 */
886static void try_transmission_to_peer (struct NeighbourList *neighbour);
887
888
889/**
890 * Function called by the GNUNET_TRANSPORT_TransmitFunction
891 * upon "completion" of a send request. This tells the API
892 * that it is now legal to send another message to the given
893 * peer.
894 *
895 * @param cls closure, identifies the entry on the
896 * message queue that was transmitted and the
897 * client responsible for queueing the message
898 * @param rl identifies plugin used for the transmission for
899 * this neighbour; needs to be re-enabled for
900 * future transmissions
901 * @param target the peer receiving the message
902 * @param result GNUNET_OK on success, if the transmission
903 * failed, we should not tell the client to transmit
904 * more messages
905 */
906static void
907transmit_send_continuation (void *cls,
908 struct ReadyList *rl,
909 const struct GNUNET_PeerIdentity *target,
910 int result)
911{
912 struct MessageQueue *mq = cls;
913 struct SendOkMessage send_ok_msg;
914 struct NeighbourList *n;
915
916 GNUNET_assert (mq != NULL);
917 n = mq->neighbour;
918 GNUNET_assert (0 ==
919 memcmp (&n->id, target,
920 sizeof (struct GNUNET_PeerIdentity)));
921 if (rl == NULL)
922 {
923 rl = n->plugins;
924 while ((rl != NULL) && (rl->plugin != mq->plugin))
925 rl = rl->next;
926 GNUNET_assert (rl != NULL);
927 }
928 if (result == GNUNET_OK)
929 rl->timeout = GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
930 else
931 rl->connected = GNUNET_NO;
932 if (!mq->internal_msg)
933 rl->transmit_ready = GNUNET_YES;
934 if (mq->client != NULL)
935 {
936 send_ok_msg.header.size = htons (sizeof (send_ok_msg));
937 send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK);
938 send_ok_msg.success = htonl (result);
939 send_ok_msg.peer = n->id;
940 transmit_to_client (mq->client, &send_ok_msg.header, GNUNET_NO);
941 }
942 GNUNET_free (mq->message);
943 GNUNET_free (mq);
944 /* one plugin just became ready again, try transmitting
945 another message (if available) */
946 try_transmission_to_peer (n);
947}
948
949
950
951
952/**
953 * We could not use an existing (or validated) connection to
954 * talk to a peer. Try addresses that have not yet been
955 * validated.
956 *
957 * @param n neighbour we want to communicate with
958 * @return plugin ready to talk, or NULL if none is available
959 */
960static struct ReadyList *
961try_unvalidated_addresses (struct NeighbourList *n)
962{
963 struct ValidationList *vl;
964 struct ValidationAddress *va;
965 struct GNUNET_PeerIdentity id;
966 struct GNUNET_TIME_Absolute now;
967 unsigned int total;
968 unsigned int cnt;
969 struct ReadyList *rl;
970 struct TransportPlugin *plugin;
971
972#if DEBUG_TRANSPORT
973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974 "Trying to connect to `%4s' using unvalidated addresses\n",
975 GNUNET_i2s (&n->id));
976#endif
977 /* NOTE: this function needs to not only identify the
978 plugin but also setup "plugin_handle", binding it to the
979 right address using the plugin's "send_to" API */
980 now = GNUNET_TIME_absolute_get ();
981 vl = pending_validations;
982 while (vl != NULL)
983 {
984 GNUNET_CRYPTO_hash (&vl->publicKey,
985 sizeof (struct
986 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
987 &id.hashPubKey);
988 if (0 == memcmp (&id, &n->id, sizeof (struct GNUNET_PeerIdentity)))
989 break;
990 vl = vl->next;
991 }
992 if (vl == NULL)
993 {
994#if DEBUG_TRANSPORT
995 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
996 "No unvalidated address found for peer `%4s'\n",
997 GNUNET_i2s (&n->id));
998#endif
999 return NULL;
1000 }
1001 total = 0;
1002 cnt = 0;
1003 va = vl->addresses;
1004 while (va != NULL)
1005 {
1006 cnt++;
1007 if (va->expiration.value > now.value)
1008 total++;
1009 va = va->next;
1010 }
1011 if (total == 0)
1012 {
1013#if DEBUG_TRANSPORT
1014 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1015 "All %u unvalidated addresses for peer have expired\n",
1016 cnt);
1017#endif
1018 return NULL;
1019 }
1020 total = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
1021 for (va = vl->addresses; va != NULL; va = va->next)
1022 {
1023 if (va->expiration.value <= now.value)
1024 continue;
1025 if (total > 0)
1026 {
1027 total--;
1028 continue;
1029 }
1030#if DEBUG_TRANSPORT
1031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1032 "Trying unvalidated address of `%s' transport\n",
1033 va->transport_name);
1034#endif
1035 plugin = find_transport (va->transport_name);
1036 if (plugin == NULL)
1037 {
1038 GNUNET_break (0);
1039 break;
1040 }
1041 rl = GNUNET_malloc (sizeof (struct ReadyList));
1042 rl->next = n->plugins;
1043 n->plugins = rl;
1044 rl->plugin = plugin;
1045 rl->plugin_handle = plugin->api->send_to (plugin->api->cls,
1046 &n->id,
1047 NULL,
1048 NULL,
1049 GNUNET_TIME_UNIT_ZERO,
1050 &va->msg[1], va->addr_len);
1051 rl->transmit_ready = GNUNET_YES;
1052 return rl;
1053 }
1054 return NULL;
1055}
1056
1057
1058/**
1059 * Check the ready list for the given neighbour and
1060 * if a plugin is ready for transmission (and if we
1061 * have a message), do so!
1062 */
1063static void
1064try_transmission_to_peer (struct NeighbourList *neighbour)
1065{
1066 struct ReadyList *pos;
1067 struct GNUNET_TIME_Relative min_latency;
1068 struct ReadyList *rl;
1069 struct MessageQueue *mq;
1070 struct GNUNET_TIME_Absolute now;
1071
1072 if (neighbour->messages == NULL)
1073 return; /* nothing to do */
1074 try_alternative_plugins (neighbour);
1075 min_latency = GNUNET_TIME_UNIT_FOREVER_REL;
1076 rl = NULL;
1077 mq = neighbour->messages;
1078 now = GNUNET_TIME_absolute_get ();
1079 pos = neighbour->plugins;
1080 while (pos != NULL)
1081 {
1082 /* set plugins that are inactive for a long time back to disconnected */
1083 if ((pos->timeout.value < now.value) && (pos->connected == GNUNET_YES))
1084 {
1085#if DEBUG_TRANSPORT
1086 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1087 "Marking long-time inactive connection to `%4s' as down.\n",
1088 GNUNET_i2s (&neighbour->id));
1089#endif
1090 pos->connected = GNUNET_NO;
1091 }
1092 if (((GNUNET_YES == pos->transmit_ready) ||
1093 (mq->internal_msg)) &&
1094 (pos->connect_attempts < MAX_CONNECT_RETRY) &&
1095 ((rl == NULL) || (min_latency.value > pos->latency.value)))
1096 {
1097 rl = pos;
1098 min_latency = pos->latency;
1099 }
1100 pos = pos->next;
1101 }
1102 if (rl == NULL)
1103 rl = try_unvalidated_addresses (neighbour);
1104 if (rl == NULL)
1105 {
1106#if DEBUG_TRANSPORT
1107 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1108 "No plugin ready to transmit message\n");
1109#endif
1110 return; /* nobody ready */
1111 }
1112 if (GNUNET_NO == rl->connected)
1113 {
1114 rl->connect_attempts++;
1115 rl->connected = GNUNET_YES;
1116 }
1117 neighbour->messages = mq->next;
1118 mq->plugin = rl->plugin;
1119 if (!mq->internal_msg)
1120 rl->transmit_ready = GNUNET_NO;
1121#if DEBUG_TRANSPORT
1122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123 "Giving message of type `%u' for `%4s' to plugin `%s'\n",
1124 ntohs (mq->message->type),
1125 GNUNET_i2s (&neighbour->id), rl->plugin->short_name);
1126#endif
1127 rl->plugin_handle
1128 = rl->plugin->api->send (rl->plugin->api->cls,
1129 rl->plugin_handle,
1130 rl,
1131 &neighbour->id,
1132 mq->message,
1133 IDLE_CONNECTION_TIMEOUT,
1134 &transmit_send_continuation, mq);
1135}
1136
1137
1138/**
1139 * Send the specified message to the specified peer.
1140 *
1141 * @param client source of the transmission request (can be NULL)
1142 * @param msg message to send
1143 * @param is_internal is this an internal message
1144 * @param neighbour handle to the neighbour for transmission
1145 */
1146static void
1147transmit_to_peer (struct TransportClient *client,
1148 const struct GNUNET_MessageHeader *msg,
1149 int is_internal, struct NeighbourList *neighbour)
1150{
1151 struct MessageQueue *mq;
1152 struct MessageQueue *mqe;
1153 struct GNUNET_MessageHeader *m;
1154
1155#if DEBUG_TRANSPORT
1156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157 _("Sending message of type %u to peer `%4s'\n"),
1158 ntohs (msg->type), GNUNET_i2s (&neighbour->id));
1159#endif
1160 if (client != NULL)
1161 {
1162 /* check for duplicate submission */
1163 mq = neighbour->messages;
1164 while (NULL != mq)
1165 {
1166 if (mq->client == client)
1167 {
1168 /* client transmitted to same peer twice
1169 before getting SendOk! */
1170 GNUNET_break (0);
1171 return;
1172 }
1173 mq = mq->next;
1174 }
1175 }
1176 mq = GNUNET_malloc (sizeof (struct MessageQueue));
1177 mq->client = client;
1178 m = GNUNET_malloc (ntohs (msg->size));
1179 memcpy (m, msg, ntohs (msg->size));
1180 mq->message = m;
1181 mq->neighbour = neighbour;
1182 mq->internal_msg = is_internal;
1183
1184 /* find tail */
1185 mqe = neighbour->messages;
1186 if (mqe != NULL)
1187 while (mqe->next != NULL)
1188 mqe = mqe->next;
1189 if (mqe == NULL)
1190 {
1191 /* new head */
1192 neighbour->messages = mq;
1193 try_transmission_to_peer (neighbour);
1194 }
1195 else
1196 {
1197 /* append */
1198 mqe->next = mq;
1199 }
1200}
1201
1202
1203struct GeneratorContext
1204{
1205 struct TransportPlugin *plug_pos;
1206 struct AddressList *addr_pos;
1207 struct GNUNET_TIME_Absolute expiration;
1208};
1209
1210
1211static size_t
1212address_generator (void *cls, size_t max, void *buf)
1213{
1214 struct GeneratorContext *gc = cls;
1215 size_t ret;
1216
1217 while ((gc->addr_pos == NULL) && (gc->plug_pos != NULL))
1218 {
1219 gc->plug_pos = gc->plug_pos->next;
1220 gc->addr_pos = (gc->plug_pos != NULL) ? gc->plug_pos->addresses : NULL;
1221 }
1222 if (NULL == gc->plug_pos)
1223 return 0;
1224 ret = GNUNET_HELLO_add_address (gc->plug_pos->short_name,
1225 gc->expiration,
1226 gc->addr_pos->addr,
1227 gc->addr_pos->addrlen, buf, max);
1228 gc->addr_pos = gc->addr_pos->next;
1229 return ret;
1230}
1231
1232
1233/**
1234 * Construct our HELLO message from all of the addresses of
1235 * all of the transports.
1236 */
1237static void
1238refresh_hello ()
1239{
1240 struct GNUNET_HELLO_Message *hello;
1241 struct TransportClient *cpos;
1242 struct NeighbourList *npos;
1243 struct GeneratorContext gc;
1244
1245#if DEBUG_TRANSPORT
1246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1247 "Refreshing my HELLO\n");
1248#endif
1249 gc.plug_pos = plugins;
1250 gc.addr_pos = plugins != NULL ? plugins->addresses : NULL;
1251 gc.expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
1252 hello = GNUNET_HELLO_create (&my_public_key, &address_generator, &gc);
1253 cpos = clients;
1254 while (cpos != NULL)
1255 {
1256 transmit_to_client (cpos,
1257 (const struct GNUNET_MessageHeader *) hello,
1258 GNUNET_NO);
1259 cpos = cpos->next;
1260 }
1261
1262 GNUNET_free_non_null (our_hello);
1263 our_hello = hello;
1264 our_hello_version++;
1265 npos = neighbours;
1266 while (npos != NULL)
1267 {
1268 transmit_to_peer (NULL,
1269 (const struct GNUNET_MessageHeader *) our_hello,
1270 GNUNET_YES, npos);
1271 npos = npos->next;
1272 }
1273}
1274
1275
1276/**
1277 * Task used to clean up expired addresses for a plugin.
1278 *
1279 * @param cls closure
1280 * @param tc context
1281 */
1282static void
1283expire_address_task (void *cls,
1284 const struct GNUNET_SCHEDULER_TaskContext *tc);
1285
1286
1287/**
1288 * Update the list of addresses for this plugin,
1289 * expiring those that are past their expiration date.
1290 *
1291 * @param plugin addresses of which plugin should be recomputed?
1292 * @param fresh set to GNUNET_YES if a new address was added
1293 * and we need to regenerate the HELLO even if nobody
1294 * expired
1295 */
1296static void
1297update_addresses (struct TransportPlugin *plugin, int fresh)
1298{
1299 struct GNUNET_TIME_Relative min_remaining;
1300 struct GNUNET_TIME_Relative remaining;
1301 struct GNUNET_TIME_Absolute now;
1302 struct AddressList *pos;
1303 struct AddressList *prev;
1304 struct AddressList *next;
1305 int expired;
1306
1307 if (plugin->address_update_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1308 GNUNET_SCHEDULER_cancel (plugin->env.sched, plugin->address_update_task);
1309 plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1310 now = GNUNET_TIME_absolute_get ();
1311 min_remaining = GNUNET_TIME_UNIT_FOREVER_REL;
1312 expired = GNUNET_NO;
1313 prev = NULL;
1314 pos = plugin->addresses;
1315 while (pos != NULL)
1316 {
1317 next = pos->next;
1318 if (pos->expires.value < now.value)
1319 {
1320 expired = GNUNET_YES;
1321 if (prev == NULL)
1322 plugin->addresses = pos->next;
1323 else
1324 prev->next = pos->next;
1325 GNUNET_free (pos);
1326 }
1327 else
1328 {
1329 remaining = GNUNET_TIME_absolute_get_remaining (pos->expires);
1330 if (remaining.value < min_remaining.value)
1331 min_remaining = remaining;
1332 prev = pos;
1333 }
1334 pos = next;
1335 }
1336
1337 if (expired || fresh)
1338 refresh_hello ();
1339 if (min_remaining.value < GNUNET_TIME_UNIT_FOREVER_REL.value)
1340 plugin->address_update_task
1341 = GNUNET_SCHEDULER_add_delayed (plugin->env.sched,
1342 GNUNET_NO,
1343 GNUNET_SCHEDULER_PRIORITY_IDLE,
1344 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1345 min_remaining,
1346 &expire_address_task, plugin);
1347
1348}
1349
1350
1351/**
1352 * Task used to clean up expired addresses for a plugin.
1353 *
1354 * @param cls closure
1355 * @param tc context
1356 */
1357static void
1358expire_address_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1359{
1360 struct TransportPlugin *plugin = cls;
1361 plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1362 update_addresses (plugin, GNUNET_NO);
1363}
1364
1365
1366/**
1367 * Function that must be called by each plugin to notify the
1368 * transport service about the addresses under which the transport
1369 * provided by the plugin can be reached.
1370 *
1371 * @param cls closure
1372 * @param name name of the transport that generated the address
1373 * @param addr one of the addresses of the host, NULL for the last address
1374 * the specific address format depends on the transport
1375 * @param addrlen length of the address
1376 * @param expires when should this address automatically expire?
1377 */
1378static void
1379plugin_env_notify_address (void *cls,
1380 const char *name,
1381 const void *addr,
1382 size_t addrlen,
1383 struct GNUNET_TIME_Relative expires)
1384{
1385 struct TransportPlugin *p = cls;
1386 struct AddressList *al;
1387 struct GNUNET_TIME_Absolute abex;
1388
1389#if DEBUG_TRANSPORT
1390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1391 "Plugin `%s' informs us about a new address\n", name);
1392#endif
1393 abex = GNUNET_TIME_relative_to_absolute (expires);
1394 GNUNET_assert (p == find_transport (name));
1395
1396 al = p->addresses;
1397 while (al != NULL)
1398 {
1399 if ((addrlen == al->addrlen) && (0 == memcmp (addr, &al[1], addrlen)))
1400 {
1401 if (al->expires.value < abex.value)
1402 al->expires = abex;
1403 return;
1404 }
1405 al = al->next;
1406 }
1407 al = GNUNET_malloc (sizeof (struct AddressList) + addrlen);
1408 al->addr = &al[1];
1409 al->next = p->addresses;
1410 p->addresses = al;
1411 al->expires = abex;
1412 al->addrlen = addrlen;
1413 memcpy (&al[1], addr, addrlen);
1414 update_addresses (p, GNUNET_YES);
1415}
1416
1417
1418struct LookupHelloContext
1419{
1420 GNUNET_TRANSPORT_AddressCallback iterator;
1421
1422 void *iterator_cls;
1423};
1424
1425
1426static int
1427lookup_address_callback (void *cls,
1428 const char *tname,
1429 struct GNUNET_TIME_Absolute expiration,
1430 const void *addr, size_t addrlen)
1431{
1432 struct LookupHelloContext *lhc = cls;
1433 lhc->iterator (lhc->iterator_cls, tname, addr, addrlen);
1434 return GNUNET_OK;
1435}
1436
1437
1438static void
1439lookup_hello_callback (void *cls,
1440 const struct GNUNET_PeerIdentity *peer,
1441 const struct GNUNET_HELLO_Message *h, uint32_t trust)
1442{
1443 struct LookupHelloContext *lhc = cls;
1444
1445 if (peer == NULL)
1446 {
1447 lhc->iterator (lhc->iterator_cls, NULL, NULL, 0);
1448 GNUNET_free (lhc);
1449 return;
1450 }
1451 if (h == NULL)
1452 return;
1453 GNUNET_HELLO_iterate_addresses (h,
1454 GNUNET_NO, &lookup_address_callback, lhc);
1455}
1456
1457
1458/**
1459 * Function that allows a transport to query the known
1460 * network addresses for a given peer.
1461 *
1462 * @param cls closure
1463 * @param timeout after how long should we time out?
1464 * @param target which peer are we looking for?
1465 * @param iter function to call for each known address
1466 * @param iter_cls closure for iter
1467 */
1468static void
1469plugin_env_lookup_address (void *cls,
1470 struct GNUNET_TIME_Relative timeout,
1471 const struct GNUNET_PeerIdentity *target,
1472 GNUNET_TRANSPORT_AddressCallback iter,
1473 void *iter_cls)
1474{
1475 struct LookupHelloContext *lhc;
1476
1477 lhc = GNUNET_malloc (sizeof (struct LookupHelloContext));
1478 lhc->iterator = iter;
1479 lhc->iterator_cls = iter_cls;
1480 GNUNET_PEERINFO_for_all (cfg,
1481 sched,
1482 target, 0, timeout, &lookup_hello_callback, &lhc);
1483}
1484
1485
1486/**
1487 * Notify all of our clients about a peer connecting.
1488 */
1489static void
1490notify_clients_connect (const struct GNUNET_PeerIdentity *peer,
1491 struct GNUNET_TIME_Relative latency)
1492{
1493 struct ConnectInfoMessage cim;
1494 struct TransportClient *cpos;
1495
1496#if DEBUG_TRANSPORT
1497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1498 "Informing clients about peer `%4s' connecting to us\n",
1499 GNUNET_i2s (peer));
1500#endif
1501 cim.header.size = htons (sizeof (struct ConnectInfoMessage));
1502 cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
1503 cim.quota_out = htonl (default_quota_out);
1504 cim.latency = GNUNET_TIME_relative_hton (latency);
1505 memcpy (&cim.id, peer, sizeof (struct GNUNET_PeerIdentity));
1506 cpos = clients;
1507 while (cpos != NULL)
1508 {
1509 transmit_to_client (cpos, &cim.header, GNUNET_NO);
1510 cpos = cpos->next;
1511 }
1512}
1513
1514
1515/**
1516 * Notify all of our clients about a peer disconnecting.
1517 */
1518static void
1519notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
1520{
1521 struct DisconnectInfoMessage dim;
1522 struct TransportClient *cpos;
1523
1524#if DEBUG_TRANSPORT
1525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1526 "Informing clients about peer `%4s' disconnecting\n",
1527 GNUNET_i2s (peer));
1528#endif
1529 dim.header.size = htons (sizeof (struct DisconnectInfoMessage));
1530 dim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
1531 dim.reserved = htonl (0);
1532 memcpy (&dim.peer, peer, sizeof (struct GNUNET_PeerIdentity));
1533 cpos = clients;
1534 while (cpos != NULL)
1535 {
1536 transmit_to_client (cpos, &dim.header, GNUNET_NO);
1537 cpos = cpos->next;
1538 }
1539}
1540
1541
1542/**
1543 * Copy any validated addresses to buf.
1544 *
1545 * @return 0 once all addresses have been
1546 * returned
1547 */
1548static size_t
1549list_validated_addresses (void *cls, size_t max, void *buf)
1550{
1551 struct ValidationAddress **va = cls;
1552 size_t ret;
1553
1554 while ((NULL != *va) && ((*va)->ok != GNUNET_YES))
1555 *va = (*va)->next;
1556 if (NULL == *va)
1557 return 0;
1558 ret = GNUNET_HELLO_add_address ((*va)->transport_name,
1559 (*va)->expiration,
1560 &(*va)->msg[1], (*va)->addr_len, buf, max);
1561 *va = (*va)->next;
1562 return ret;
1563}
1564
1565
1566/**
1567 * HELLO validation cleanup task.
1568 */
1569static void
1570cleanup_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1571{
1572 struct ValidationAddress *va;
1573 struct ValidationList *pos;
1574 struct ValidationList *prev;
1575 struct GNUNET_TIME_Absolute now;
1576 struct GNUNET_HELLO_Message *hello;
1577 struct GNUNET_PeerIdentity pid;
1578
1579#if DEBUG_TRANSPORT
1580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1581 "HELLO validation cleanup background task running...\n");
1582#endif
1583 now = GNUNET_TIME_absolute_get ();
1584 prev = NULL;
1585 pos = pending_validations;
1586 while (pos != NULL)
1587 {
1588 if (pos->timeout.value < now.value)
1589 {
1590 if (prev == NULL)
1591 pending_validations = pos->next;
1592 else
1593 prev->next = pos->next;
1594 va = pos->addresses;
1595 hello = GNUNET_HELLO_create (&pos->publicKey,
1596 &list_validated_addresses, &va);
1597 GNUNET_CRYPTO_hash (&pos->publicKey,
1598 sizeof (struct
1599 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1600 &pid.hashPubKey);
1601#if DEBUG_TRANSPORT
1602 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1603 "Creating persistent `%s' message for peer `%4s' based on confirmed addresses.\n",
1604 "HELLO", GNUNET_i2s (&pid));
1605#endif
1606 GNUNET_PEERINFO_add_peer (cfg, sched, &pid, hello);
1607 GNUNET_free (hello);
1608 while (NULL != (va = pos->addresses))
1609 {
1610 pos->addresses = va->next;
1611 GNUNET_free (va->transport_name);
1612 GNUNET_free (va);
1613 }
1614 GNUNET_free (pos);
1615 if (prev == NULL)
1616 pos = pending_validations;
1617 else
1618 pos = prev->next;
1619 continue;
1620 }
1621 prev = pos;
1622 pos = pos->next;
1623 }
1624
1625 /* finally, reschedule cleanup if needed; list is
1626 ordered by timeout, so we need the last element... */
1627 pos = pending_validations;
1628 while ((pos != NULL) && (pos->next != NULL))
1629 pos = pos->next;
1630 if (NULL != pos)
1631 GNUNET_SCHEDULER_add_delayed (sched,
1632 GNUNET_NO,
1633 GNUNET_SCHEDULER_PRIORITY_IDLE,
1634 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1635 GNUNET_TIME_absolute_get_remaining (pos->
1636 timeout),
1637 &cleanup_validation, NULL);
1638}
1639
1640
1641struct CheckHelloValidatedContext
1642{
1643 /**
1644 * Plugin for which we are validating.
1645 */
1646 struct TransportPlugin *plugin;
1647
1648 /**
1649 * Hello that we are validating.
1650 */
1651 struct GNUNET_HELLO_Message *hello;
1652
1653 /**
1654 * Validation list being build.
1655 */
1656 struct ValidationList *e;
1657};
1658
1659
1660/**
1661 * Append the given address to the list of entries
1662 * that need to be validated.
1663 */
1664static int
1665run_validation (void *cls,
1666 const char *tname,
1667 struct GNUNET_TIME_Absolute expiration,
1668 const void *addr, size_t addrlen)
1669{
1670 struct ValidationList *e = cls;
1671 struct TransportPlugin *tp;
1672 struct ValidationAddress *va;
1673 struct ValidationChallengeMessage *vcm;
1674
1675 tp = find_transport (tname);
1676 if (tp == NULL)
1677 {
1678 GNUNET_log (GNUNET_ERROR_TYPE_INFO |
1679 GNUNET_ERROR_TYPE_BULK,
1680 _
1681 ("Transport `%s' not loaded, will not try to validate peer address using this transport.\n"),
1682 tname);
1683 return GNUNET_OK;
1684 }
1685 va = GNUNET_malloc (sizeof (struct ValidationAddress) +
1686 sizeof (struct ValidationChallengeMessage) + addrlen);
1687 va->next = e->addresses;
1688 e->addresses = va;
1689 vcm = (struct ValidationChallengeMessage *) &va[1];
1690 va->msg = vcm;
1691 va->transport_name = GNUNET_strdup (tname);
1692 va->addr_len = addrlen;
1693 vcm->header.size =
1694 htons (sizeof (struct ValidationChallengeMessage) + addrlen);
1695 vcm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
1696 vcm->purpose.size =
1697 htonl (sizeof (struct ValidationChallengeMessage) + addrlen -
1698 sizeof (struct GNUNET_MessageHeader));
1699 vcm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO);
1700 vcm->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1701 (unsigned int) -1);
1702 /* Note: vcm->target is set in check_hello_validated */
1703 memcpy (&vcm[1], addr, addrlen);
1704 return GNUNET_OK;
1705}
1706
1707
1708/**
1709 * Check if addresses in validated hello "h" overlap with
1710 * those in "chvc->hello" and update "chvc->hello" accordingly,
1711 * removing those addresses that have already been validated.
1712 */
1713static void
1714check_hello_validated (void *cls,
1715 const struct GNUNET_PeerIdentity *peer,
1716 const struct GNUNET_HELLO_Message *h, uint32_t trust)
1717{
1718 struct CheckHelloValidatedContext *chvc = cls;
1719 struct ValidationAddress *va;
1720 struct TransportPlugin *tp;
1721 int first_call;
1722
1723 first_call = GNUNET_NO;
1724 if (chvc->e == NULL)
1725 {
1726 first_call = GNUNET_YES;
1727 chvc->e = GNUNET_malloc (sizeof (struct ValidationList));
1728 GNUNET_HELLO_get_key (h != NULL ? h : chvc->hello, &chvc->e->publicKey);
1729 chvc->e->timeout =
1730 GNUNET_TIME_relative_to_absolute (HELLO_VERIFICATION_TIMEOUT);
1731 chvc->e->next = pending_validations;
1732 pending_validations = chvc->e;
1733 }
1734 if (h != NULL)
1735 {
1736 GNUNET_HELLO_iterate_new_addresses (chvc->hello,
1737 h,
1738 GNUNET_TIME_absolute_get (),
1739 &run_validation, chvc->e);
1740 }
1741 else if (GNUNET_YES == first_call)
1742 {
1743 /* no existing HELLO, all addresses are new */
1744 GNUNET_HELLO_iterate_addresses (chvc->hello,
1745 GNUNET_NO, &run_validation, chvc->e);
1746 }
1747 if (h != NULL)
1748 return; /* wait for next call */
1749 /* finally, transmit validation attempts */
1750 va = chvc->e->addresses;
1751 while (va != NULL)
1752 {
1753 GNUNET_CRYPTO_hash (&chvc->e->publicKey,
1754 sizeof (struct
1755 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1756 &va->msg->target.hashPubKey);
1757#if DEBUG_TRANSPORT
1758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1759 "Establishing `%s' connection to validate `%s' of `%4s' (sending our `%s')\n",
1760 va->transport_name,
1761 "HELLO", GNUNET_i2s (&va->msg->target), "HELLO");
1762#endif
1763 tp = find_transport (va->transport_name);
1764 GNUNET_assert (tp != NULL);
1765 if (NULL ==
1766 tp->api->send_to (tp->api->cls,
1767 &va->msg->target,
1768 (const struct GNUNET_MessageHeader *) our_hello,
1769 &va->msg->header,
1770 HELLO_VERIFICATION_TIMEOUT,
1771 &va->msg[1], va->addr_len))
1772 va->ok = GNUNET_SYSERR;
1773 va = va->next;
1774 }
1775 if (chvc->e->next == NULL)
1776 GNUNET_SCHEDULER_add_delayed (sched,
1777 GNUNET_NO,
1778 GNUNET_SCHEDULER_PRIORITY_IDLE,
1779 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1780 GNUNET_TIME_absolute_get_remaining (chvc->
1781 e->
1782 timeout),
1783 &cleanup_validation, NULL);
1784 GNUNET_free (chvc);
1785}
1786
1787
1788/**
1789 * Process HELLO-message.
1790 *
1791 * @param plugin transport involved, may be NULL
1792 * @param message the actual message
1793 * @return GNUNET_OK if the HELLO was well-formed, GNUNET_SYSERR otherwise
1794 */
1795static int
1796process_hello (struct TransportPlugin *plugin,
1797 const struct GNUNET_MessageHeader *message)
1798{
1799 struct ValidationList *e;
1800 uint16_t hsize;
1801 struct GNUNET_PeerIdentity target;
1802 const struct GNUNET_HELLO_Message *hello;
1803 struct CheckHelloValidatedContext *chvc;
1804 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
1805
1806 hsize = ntohs (message->size);
1807 if ((ntohs (message->type) != GNUNET_MESSAGE_TYPE_HELLO) ||
1808 (hsize < sizeof (struct GNUNET_MessageHeader)))
1809 {
1810 GNUNET_break (0);
1811 return GNUNET_SYSERR;
1812 }
1813 /* first, check if load is too high */
1814 if (GNUNET_OS_load_cpu_get (cfg) > 100)
1815 {
1816 /* TODO: call to stats? */
1817 return GNUNET_OK;
1818 }
1819 hello = (const struct GNUNET_HELLO_Message *) message;
1820 if (GNUNET_OK != GNUNET_HELLO_get_key (hello, &publicKey))
1821 {
1822 GNUNET_break_op (0);
1823 return GNUNET_SYSERR;
1824 }
1825 GNUNET_CRYPTO_hash (&publicKey,
1826 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1827 &target.hashPubKey);
1828#if DEBUG_TRANSPORT
1829 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1830 "Processing `%s' message for `%4s'\n",
1831 "HELLO", GNUNET_i2s (&target));
1832#endif
1833 /* check if a HELLO for this peer is already on the validation list */
1834 e = pending_validations;
1835 while (e != NULL)
1836 {
1837 if (0 == memcmp (&e->publicKey,
1838 &publicKey,
1839 sizeof (struct
1840 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
1841 {
1842 /* TODO: call to stats? */
1843 return GNUNET_OK;
1844 }
1845 e = e->next;
1846 }
1847 chvc = GNUNET_malloc (sizeof (struct CheckHelloValidatedContext) + hsize);
1848 chvc->plugin = plugin;
1849 chvc->hello = (struct GNUNET_HELLO_Message *) &chvc[1];
1850 memcpy (chvc->hello, hello, hsize);
1851 /* finally, check if HELLO was previously validated
1852 (continuation will then schedule actual validation) */
1853 GNUNET_PEERINFO_for_all (cfg,
1854 sched,
1855 &target,
1856 0,
1857 HELLO_VERIFICATION_TIMEOUT,
1858 &check_hello_validated, chvc);
1859 return GNUNET_OK;
1860}
1861
1862
1863/**
1864 * Handle PING-message. If the plugin that gave us the message is
1865 * able to queue the PONG immediately, we only queue one PONG.
1866 * Otherwise we send at most TWO PONG messages, one via an unconfirmed
1867 * transport and one via a confirmed transport. Both addresses are
1868 * selected randomly among those available.
1869 *
1870 * @param plugin plugin that gave us the message
1871 * @param sender claimed sender of the PING
1872 * @param plugin_context context that might be used to send response
1873 * @param message the actual message
1874 */
1875static void
1876process_ping (struct TransportPlugin *plugin,
1877 const struct GNUNET_PeerIdentity *sender,
1878 void *plugin_context,
1879 const struct GNUNET_MessageHeader *message)
1880{
1881 const struct ValidationChallengeMessage *vcm;
1882 struct ValidationChallengeResponse vcr;
1883 uint16_t msize;
1884 struct NeighbourList *n;
1885
1886#if DEBUG_TRANSPORT
1887 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1888 "Processing PING\n");
1889#endif
1890 msize = ntohs (message->size);
1891 if (msize < sizeof (struct ValidationChallengeMessage))
1892 {
1893 GNUNET_break_op (0);
1894 return;
1895 }
1896 vcm = (const struct ValidationChallengeMessage *) message;
1897 if (0 != memcmp (&vcm->target,
1898 &my_identity, sizeof (struct GNUNET_PeerIdentity)))
1899 {
1900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1901 _("Received `%s' message not destined for me!\n"), "PING");
1902 /* TODO: call statistics */
1903 return;
1904 }
1905 if ((ntohl (vcm->purpose.size) !=
1906 msize - sizeof (struct GNUNET_MessageHeader))
1907 || (ntohl (vcm->purpose.purpose) !=
1908 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO))
1909 {
1910 GNUNET_break_op (0);
1911 return;
1912 }
1913 msize -= sizeof (struct ValidationChallengeMessage);
1914 if (GNUNET_OK !=
1915 plugin->api->address_suggested (plugin->api->cls, &vcm[1], msize))
1916 {
1917 GNUNET_break_op (0);
1918 return;
1919 }
1920 vcr.header.size = htons (sizeof (struct ValidationChallengeResponse));
1921 vcr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
1922 vcr.challenge = vcm->challenge;
1923 vcr.sender = my_identity;
1924 GNUNET_assert (GNUNET_OK ==
1925 GNUNET_CRYPTO_rsa_sign (my_private_key,
1926 &vcm->purpose, &vcr.signature));
1927#if EXTRA_CHECKS
1928 GNUNET_assert (GNUNET_OK ==
1929 GNUNET_CRYPTO_rsa_verify
1930 (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &vcm->purpose,
1931 &vcr.signature, &my_public_key));
1932#endif
1933#if DEBUG_TRANSPORT
1934 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1935 "Trying to transmit PONG using inbound connection\n");
1936#endif
1937 n = find_neighbour (sender);
1938 transmit_to_peer (NULL, &vcr.header, GNUNET_YES, n);
1939}
1940
1941
1942/**
1943 * Handle PONG-message.
1944 *
1945 * @param message the actual message
1946 */
1947static void
1948process_pong (struct TransportPlugin *plugin,
1949 const struct GNUNET_MessageHeader *message)
1950{
1951 const struct ValidationChallengeResponse *vcr;
1952 struct ValidationList *pos;
1953 struct GNUNET_PeerIdentity peer;
1954 struct ValidationAddress *va;
1955 int all_done;
1956 int matched;
1957
1958#if DEBUG_TRANSPORT
1959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1960 "Processing PONG\n");
1961#endif
1962 vcr = (const struct ValidationChallengeResponse *) message;
1963 pos = pending_validations;
1964 while (pos != NULL)
1965 {
1966 GNUNET_CRYPTO_hash (&pos->publicKey,
1967 sizeof (struct
1968 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1969 &peer.hashPubKey);
1970 if (0 ==
1971 memcmp (&peer, &vcr->sender, sizeof (struct GNUNET_PeerIdentity)))
1972 break;
1973 pos = pos->next;
1974 }
1975 if (pos == NULL)
1976 {
1977 /* TODO: call statistics (unmatched PONG) */
1978 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1979 _
1980 ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"),
1981 "PONG", "PING");
1982 return;
1983 }
1984 all_done = GNUNET_YES;
1985 matched = GNUNET_NO;
1986 va = pos->addresses;
1987 while (va != NULL)
1988 {
1989 if (va->msg->challenge == vcr->challenge)
1990 {
1991 if (GNUNET_OK !=
1992 GNUNET_CRYPTO_rsa_verify
1993 (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &va->msg->purpose,
1994 &vcr->signature, &pos->publicKey))
1995 {
1996 /* this could rarely happen if we used the same
1997 challenge number for the peer for two different
1998 transports / addresses, but the likelihood is
1999 very small... */
2000 GNUNET_break_op (0);
2001 }
2002 else
2003 {
2004#if DEBUG_TRANSPORT
2005 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2006 "Confirmed validity of peer address.\n");
2007#endif
2008 va->ok = GNUNET_YES;
2009 va->expiration =
2010 GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
2011 matched = GNUNET_YES;
2012 }
2013 }
2014 if (va->ok != GNUNET_YES)
2015 all_done = GNUNET_NO;
2016 va = va->next;
2017 }
2018 if (GNUNET_NO == matched)
2019 {
2020 /* TODO: call statistics (unmatched PONG) */
2021 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2022 _
2023 ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"),
2024 "PONG", "PING");
2025 }
2026 if (GNUNET_YES == all_done)
2027 {
2028 pos->timeout.value = 0;
2029 GNUNET_SCHEDULER_add_delayed (sched,
2030 GNUNET_NO,
2031 GNUNET_SCHEDULER_PRIORITY_IDLE,
2032 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
2033 GNUNET_TIME_UNIT_ZERO,
2034 &cleanup_validation, NULL);
2035 }
2036}
2037
2038
2039/**
2040 * The peer specified by the given neighbour has timed-out. Update
2041 * our state and do the necessary notifications. Also notifies
2042 * our clients that the neighbour is now officially gone.
2043 *
2044 * @param n the neighbour list entry for the peer
2045 */
2046static void
2047disconnect_neighbour (struct NeighbourList *n)
2048{
2049 struct ReadyList *rpos;
2050 struct NeighbourList *npos;
2051 struct NeighbourList *nprev;
2052 struct MessageQueue *mq;
2053
2054#if DEBUG_TRANSPORT
2055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2056 "Disconnecting from neighbour\n");
2057#endif
2058 /* remove n from neighbours list */
2059 nprev = NULL;
2060 npos = neighbours;
2061 while ((npos != NULL) && (npos != n))
2062 {
2063 nprev = npos;
2064 npos = npos->next;
2065 }
2066 GNUNET_assert (npos != NULL);
2067 if (nprev == NULL)
2068 neighbours = n->next;
2069 else
2070 nprev->next = n->next;
2071
2072 /* notify all clients about disconnect */
2073 notify_clients_disconnect (&n->id);
2074
2075 /* clean up all plugins, cancel connections & pending transmissions */
2076 while (NULL != (rpos = n->plugins))
2077 {
2078 n->plugins = rpos->next;
2079 GNUNET_assert (rpos->neighbour == n);
2080 rpos->plugin->api->cancel (rpos->plugin->api->cls,
2081 rpos->plugin_handle, rpos, &n->id);
2082 GNUNET_free (rpos);
2083 }
2084
2085 /* free all messages on the queue */
2086 while (NULL != (mq = n->messages))
2087 {
2088 n->messages = mq->next;
2089 GNUNET_assert (mq->neighbour == n);
2090 GNUNET_free (mq);
2091 }
2092
2093 /* finally, free n itself */
2094 GNUNET_free (n);
2095}
2096
2097
2098/**
2099 * Add an entry for each of our transport plugins
2100 * (that are able to send) to the list of plugins
2101 * for this neighbour.
2102 *
2103 * @param neighbour to initialize
2104 */
2105static void
2106add_plugins (struct NeighbourList *neighbour)
2107{
2108 struct TransportPlugin *tp;
2109 struct ReadyList *rl;
2110
2111 neighbour->retry_plugins_time
2112 = GNUNET_TIME_relative_to_absolute (PLUGIN_RETRY_FREQUENCY);
2113 tp = plugins;
2114 while (tp != NULL)
2115 {
2116 if (tp->api->send != NULL)
2117 {
2118 rl = GNUNET_malloc (sizeof (struct ReadyList));
2119 rl->next = neighbour->plugins;
2120 neighbour->plugins = rl;
2121 rl->plugin = tp;
2122 rl->neighbour = neighbour;
2123 rl->transmit_ready = GNUNET_YES;
2124 }
2125 tp = tp->next;
2126 }
2127}
2128
2129
2130static void
2131neighbour_timeout_task (void *cls,
2132 const struct GNUNET_SCHEDULER_TaskContext *tc)
2133{
2134 struct NeighbourList *n = cls;
2135
2136#if DEBUG_TRANSPORT
2137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2138 "Neighbour has timed out!\n");
2139#endif
2140 n->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
2141 disconnect_neighbour (n);
2142}
2143
2144
2145
2146/**
2147 * Create a fresh entry in our neighbour list for the given peer.
2148 * Will try to transmit our current HELLO to the new neighbour. Also
2149 * notifies our clients about the new "connection".
2150 *
2151 * @param peer the peer for which we create the entry
2152 * @return the new neighbour list entry
2153 */
2154static struct NeighbourList *
2155setup_new_neighbour (const struct GNUNET_PeerIdentity *peer)
2156{
2157 struct NeighbourList *n;
2158
2159#if DEBUG_TRANSPORT
2160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2161 "Setting up new neighbour `%4s', sending our HELLO to introduce ourselves\n",
2162 GNUNET_i2s (peer));
2163#endif
2164 GNUNET_assert (our_hello != NULL);
2165 n = GNUNET_malloc (sizeof (struct NeighbourList));
2166 n->next = neighbours;
2167 neighbours = n;
2168 n->id = *peer;
2169 n->last_quota_update = GNUNET_TIME_absolute_get ();
2170 n->peer_timeout =
2171 GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
2172 n->quota_in = default_quota_in;
2173 add_plugins (n);
2174 n->hello_version_sent = our_hello_version;
2175 n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
2176 GNUNET_NO,
2177 GNUNET_SCHEDULER_PRIORITY_IDLE,
2178 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
2179 IDLE_CONNECTION_TIMEOUT,
2180 &neighbour_timeout_task, n);
2181 transmit_to_peer (NULL,
2182 (const struct GNUNET_MessageHeader *) our_hello,
2183 GNUNET_YES, n);
2184 notify_clients_connect (peer, GNUNET_TIME_UNIT_FOREVER_REL);
2185 return n;
2186}
2187
2188
2189/**
2190 * Function called by the plugin for each received message.
2191 * Update data volumes, possibly notify plugins about
2192 * reducing the rate at which they read from the socket
2193 * and generally forward to our receive callback.
2194 *
2195 * @param plugin_context value to pass to this plugin
2196 * to respond to the given peer (use is optional,
2197 * but may speed up processing)
2198 * @param service_context value passed to the transport-service
2199 * to identify the neighbour; will be NULL on the first
2200 * call for a given peer
2201 * @param latency estimated latency for communicating with the
2202 * given peer
2203 * @param peer (claimed) identity of the other peer
2204 * @param message the message, NULL if peer was disconnected
2205 * @return the new service_context that the plugin should use
2206 * for future receive calls for messages from this
2207 * particular peer
2208 */
2209static struct ReadyList *
2210plugin_env_receive (void *cls,
2211 void *plugin_context,
2212 struct ReadyList *service_context,
2213 struct GNUNET_TIME_Relative latency,
2214 const struct GNUNET_PeerIdentity *peer,
2215 const struct GNUNET_MessageHeader *message)
2216{
2217 const struct GNUNET_MessageHeader ack = {
2218 htons (sizeof (struct GNUNET_MessageHeader)),
2219 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ACK)
2220 };
2221 struct TransportPlugin *plugin = cls;
2222 struct TransportClient *cpos;
2223 struct InboundMessage *im;
2224 uint16_t msize;
2225 struct NeighbourList *n;
2226
2227 if (service_context != NULL)
2228 {
2229 n = service_context->neighbour;
2230 GNUNET_assert (n != NULL);
2231 }
2232 else
2233 {
2234 n = find_neighbour (peer);
2235 if (n == NULL)
2236 {
2237 if (message == NULL)
2238 return NULL; /* disconnect of peer already marked down */
2239 n = setup_new_neighbour (peer);
2240 }
2241 service_context = n->plugins;
2242 while ((service_context != NULL) && (plugin != service_context->plugin))
2243 service_context = service_context->next;
2244 GNUNET_assert ((plugin->api->send == NULL) ||
2245 (service_context != NULL));
2246 }
2247 if (message == NULL)
2248 {
2249 if ((service_context != NULL) &&
2250 (service_context->plugin_handle == plugin_context))
2251 {
2252 service_context->connected = GNUNET_NO;
2253 service_context->plugin_handle = NULL;
2254 }
2255 /* TODO: call stats */
2256 return NULL;
2257 }
2258#if DEBUG_TRANSPORT
2259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2260 "Processing message of type `%u' received by plugin...\n",
2261 ntohs (message->type));
2262#endif
2263 if (service_context != NULL)
2264 {
2265 if (service_context->connected == GNUNET_NO)
2266 {
2267 service_context->connected = GNUNET_YES;
2268 service_context->transmit_ready = GNUNET_YES;
2269 service_context->connect_attempts++;
2270 }
2271 service_context->timeout
2272 = GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
2273 service_context->plugin_handle = plugin_context;
2274 service_context->latency = latency;
2275 }
2276 /* update traffic received amount ... */
2277 msize = ntohs (message->size);
2278 n->last_received += msize;
2279 GNUNET_SCHEDULER_cancel (sched, n->timeout_task);
2280 n->peer_timeout =
2281 GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT);
2282 n->timeout_task =
2283 GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO,
2284 GNUNET_SCHEDULER_PRIORITY_IDLE,
2285 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
2286 IDLE_CONNECTION_TIMEOUT,
2287 &neighbour_timeout_task, n);
2288 update_quota (n);
2289 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
2290 {
2291 /* dropping message due to frequent inbound volume violations! */
2292 GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
2293 GNUNET_ERROR_TYPE_BULK,
2294 _
2295 ("Dropping incoming message due to repeated bandwidth quota violations.\n"));
2296 /* TODO: call stats */
2297 return service_context;
2298 }
2299 switch (ntohs (message->type))
2300 {
2301 case GNUNET_MESSAGE_TYPE_HELLO:
2302#if DEBUG_TRANSPORT
2303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2304 "Receiving `%s' message from other peer.\n", "HELLO");
2305#endif
2306 process_hello (plugin, message);
2307#if DEBUG_TRANSPORT
2308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2309 "Sending `%s' message to connecting peer.\n", "ACK");
2310#endif
2311 transmit_to_peer (NULL, &ack, GNUNET_YES, n);
2312 break;
2313 case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
2314 process_ping (plugin, peer, plugin_context, message);
2315 break;
2316 case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
2317 process_pong (plugin, message);
2318 break;
2319 case GNUNET_MESSAGE_TYPE_TRANSPORT_ACK:
2320 n->saw_ack = GNUNET_YES;
2321 /* intentional fall-through! */
2322 default:
2323#if DEBUG_TRANSPORT
2324 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2325 "Received message of type %u from other peer, sending to all clients.\n",
2326 ntohs (message->type));
2327#endif
2328 /* transmit message to all clients */
2329 im = GNUNET_malloc (sizeof (struct InboundMessage) + msize);
2330 im->header.size = htons (sizeof (struct InboundMessage) + msize);
2331 im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
2332 im->latency = GNUNET_TIME_relative_hton (latency);
2333 im->peer = *peer;
2334 memcpy (&im[1], message, msize);
2335
2336 cpos = clients;
2337 while (cpos != NULL)
2338 {
2339 transmit_to_client (cpos, &im->header, GNUNET_YES);
2340 cpos = cpos->next;
2341 }
2342 GNUNET_free (im);
2343 }
2344 return service_context;
2345}
2346
2347
2348/**
2349 * Handle START-message. This is the first message sent to us
2350 * by any client which causes us to add it to our list.
2351 *
2352 * @param cls closure (always NULL)
2353 * @param server the server handling the message
2354 * @param client identification of the client
2355 * @param message the actual message
2356 */
2357static void
2358handle_start (void *cls,
2359 struct GNUNET_SERVER_Handle *server,
2360 struct GNUNET_SERVER_Client *client,
2361 const struct GNUNET_MessageHeader *message)
2362{
2363 struct TransportClient *c;
2364 struct ConnectInfoMessage cim;
2365 struct NeighbourList *n;
2366 struct InboundMessage *im;
2367 struct GNUNET_MessageHeader *ack;
2368
2369#if DEBUG_TRANSPORT
2370 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2371 "Received `%s' request from client\n", "START");
2372#endif
2373 c = clients;
2374 while (c != NULL)
2375 {
2376 if (c->client == client)
2377 {
2378 /* client already on our list! */
2379 GNUNET_break (0);
2380 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
2381 return;
2382 }
2383 c = c->next;
2384 }
2385 c = GNUNET_malloc (sizeof (struct TransportClient));
2386 c->next = clients;
2387 clients = c;
2388 c->client = client;
2389 if (our_hello != NULL)
2390 {
2391#if DEBUG_TRANSPORT
2392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2393 "Sending our own HELLO to new client\n");
2394#endif
2395 transmit_to_client (c,
2396 (const struct GNUNET_MessageHeader *) our_hello,
2397 GNUNET_NO);
2398 /* tell new client about all existing connections */
2399 cim.header.size = htons (sizeof (struct ConnectInfoMessage));
2400 cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
2401 cim.quota_out = htonl (default_quota_out);
2402 cim.latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); /* FIXME? */
2403 im = GNUNET_malloc (sizeof (struct InboundMessage) +
2404 sizeof (struct GNUNET_MessageHeader));
2405 im->header.size = htons (sizeof (struct InboundMessage) +
2406 sizeof (struct GNUNET_MessageHeader));
2407 im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
2408 im->latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); /* FIXME? */
2409 ack = (struct GNUNET_MessageHeader *) &im[1];
2410 ack->size = htons (sizeof (struct GNUNET_MessageHeader));
2411 ack->type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ACK);
2412 for (n = neighbours; n != NULL; n = n->next)
2413 {
2414 cim.id = n->id;
2415 transmit_to_client (c, &cim.header, GNUNET_NO);
2416 if (n->saw_ack)
2417 {
2418 im->peer = n->id;
2419 transmit_to_client (c, &im->header, GNUNET_NO);
2420 }
2421 }
2422 GNUNET_free (im);
2423 }
2424 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2425}
2426
2427
2428/**
2429 * Handle HELLO-message.
2430 *
2431 * @param cls closure (always NULL)
2432 * @param server the server handling the message
2433 * @param client identification of the client
2434 * @param message the actual message
2435 */
2436static void
2437handle_hello (void *cls,
2438 struct GNUNET_SERVER_Handle *server,
2439 struct GNUNET_SERVER_Client *client,
2440 const struct GNUNET_MessageHeader *message)
2441{
2442 int ret;
2443
2444#if DEBUG_TRANSPORT
2445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2446 "Received `%s' request from client\n", "HELLO");
2447#endif
2448 ret = process_hello (NULL, message);
2449 GNUNET_SERVER_receive_done (client, ret);
2450}
2451
2452
2453/**
2454 * Handle SEND-message.
2455 *
2456 * @param cls closure (always NULL)
2457 * @param server the server handling the message
2458 * @param client identification of the client
2459 * @param message the actual message
2460 */
2461static void
2462handle_send (void *cls,
2463 struct GNUNET_SERVER_Handle *server,
2464 struct GNUNET_SERVER_Client *client,
2465 const struct GNUNET_MessageHeader *message)
2466{
2467 struct TransportClient *tc;
2468 struct NeighbourList *n;
2469 const struct OutboundMessage *obm;
2470 const struct GNUNET_MessageHeader *obmm;
2471 uint16_t size;
2472 uint16_t msize;
2473
2474 size = ntohs (message->size);
2475 if (size <
2476 sizeof (struct OutboundMessage) + sizeof (struct GNUNET_MessageHeader))
2477 {
2478 GNUNET_break (0);
2479 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
2480 return;
2481 }
2482 obm = (const struct OutboundMessage *) message;
2483#if DEBUG_TRANSPORT
2484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2485 "Received `%s' request from client with target `%4s'\n",
2486 "SEND", GNUNET_i2s (&obm->peer));
2487#endif
2488 obmm = (const struct GNUNET_MessageHeader *) &obm[1];
2489 msize = ntohs (obmm->size);
2490 if (size != msize + sizeof (struct OutboundMessage))
2491 {
2492 GNUNET_break (0);
2493 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
2494 return;
2495 }
2496 n = find_neighbour (&obm->peer);
2497 if (n == NULL)
2498 n = setup_new_neighbour (&obm->peer);
2499 tc = clients;
2500 while ((tc != NULL) && (tc->client != client))
2501 tc = tc->next;
2502
2503#if DEBUG_TRANSPORT
2504 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2505 "Client asked to transmit %u-byte message of type %u to `%4s'\n",
2506 ntohs (obmm->size),
2507 ntohs (obmm->type), GNUNET_i2s (&obm->peer));
2508#endif
2509 transmit_to_peer (tc, obmm, GNUNET_NO, n);
2510 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2511}
2512
2513
2514/**
2515 * Handle SET_QUOTA-message.
2516 *
2517 * @param cls closure (always NULL)
2518 * @param server the server handling the message
2519 * @param client identification of the client
2520 * @param message the actual message
2521 */
2522static void
2523handle_set_quota (void *cls,
2524 struct GNUNET_SERVER_Handle *server,
2525 struct GNUNET_SERVER_Client *client,
2526 const struct GNUNET_MessageHeader *message)
2527{
2528 const struct QuotaSetMessage *qsm =
2529 (const struct QuotaSetMessage *) message;
2530 struct NeighbourList *n;
2531 struct TransportPlugin *p;
2532 struct ReadyList *rl;
2533
2534#if DEBUG_TRANSPORT
2535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2536 "Received `%s' request from client for peer `%4s'\n",
2537 "SET_QUOTA", GNUNET_i2s (&qsm->peer));
2538#endif
2539 n = find_neighbour (&qsm->peer);
2540 if (n == NULL)
2541 {
2542 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2543 return;
2544 }
2545 update_quota (n);
2546 if (n->quota_in < ntohl (qsm->quota_in))
2547 n->last_quota_update = GNUNET_TIME_absolute_get ();
2548 n->quota_in = ntohl (qsm->quota_in);
2549 rl = n->plugins;
2550 while (rl != NULL)
2551 {
2552 p = rl->plugin;
2553 p->api->set_receive_quota (p->api->cls,
2554 &qsm->peer, ntohl (qsm->quota_in));
2555 rl = rl->next;
2556 }
2557 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2558}
2559
2560
2561/**
2562 * Handle TRY_CONNECT-message.
2563 *
2564 * @param cls closure (always NULL)
2565 * @param server the server handling the message
2566 * @param client identification of the client
2567 * @param message the actual message
2568 */
2569static void
2570handle_try_connect (void *cls,
2571 struct GNUNET_SERVER_Handle *server,
2572 struct GNUNET_SERVER_Client *client,
2573 const struct GNUNET_MessageHeader *message)
2574{
2575 const struct TryConnectMessage *tcm;
2576
2577 tcm = (const struct TryConnectMessage *) message;
2578#if DEBUG_TRANSPORT
2579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2580 "Received `%s' request from client asking to connect to `%4s'\n",
2581 "TRY_CONNECT", GNUNET_i2s (&tcm->peer));
2582#endif
2583 if (NULL == find_neighbour (&tcm->peer))
2584 setup_new_neighbour (&tcm->peer);
2585 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2586}
2587
2588
2589/**
2590 * List of handlers for the messages understood by this
2591 * service.
2592 */
2593static struct GNUNET_SERVER_MessageHandler handlers[] = {
2594 {&handle_start, NULL,
2595 GNUNET_MESSAGE_TYPE_TRANSPORT_START, 0},
2596 {&handle_hello, NULL,
2597 GNUNET_MESSAGE_TYPE_HELLO, 0},
2598 {&handle_send, NULL,
2599 GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, 0},
2600 {&handle_set_quota, NULL,
2601 GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA, sizeof (struct QuotaSetMessage)},
2602 {&handle_try_connect, NULL,
2603 GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT,
2604 sizeof (struct TryConnectMessage)},
2605 {NULL, NULL, 0, 0}
2606};
2607
2608
2609/**
2610 * Setup the environment for this plugin.
2611 */
2612static void
2613create_environment (struct TransportPlugin *plug)
2614{
2615 plug->env.cfg = cfg;
2616 plug->env.sched = sched;
2617 plug->env.my_public_key = &my_public_key;
2618 plug->env.cls = plug;
2619 plug->env.receive = &plugin_env_receive;
2620 plug->env.lookup = &plugin_env_lookup_address;
2621 plug->env.notify_address = &plugin_env_notify_address;
2622 plug->env.default_quota_in = default_quota_in;
2623 plug->env.max_connections = max_connect_per_transport;
2624}
2625
2626
2627/**
2628 * Start the specified transport (load the plugin).
2629 */
2630static void
2631start_transport (struct GNUNET_SERVER_Handle *server, const char *name)
2632{
2633 struct TransportPlugin *plug;
2634 char *libname;
2635
2636 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2637 _("Loading `%s' transport plugin\n"), name);
2638 GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", name);
2639 plug = GNUNET_malloc (sizeof (struct TransportPlugin));
2640 create_environment (plug);
2641 plug->short_name = GNUNET_strdup (name);
2642 plug->lib_name = libname;
2643 plug->next = plugins;
2644 plugins = plug;
2645 plug->api = GNUNET_PLUGIN_load (libname, &plug->env);
2646 if (plug->api == NULL)
2647 {
2648 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2649 _("Failed to load transport plugin for `%s'\n"), name);
2650 GNUNET_free (plug->short_name);
2651 plugins = plug->next;
2652 GNUNET_free (libname);
2653 GNUNET_free (plug);
2654 }
2655}
2656
2657
2658/**
2659 * Called whenever a client is disconnected. Frees our
2660 * resources associated with that client.
2661 *
2662 * @param cls closure
2663 * @param client identification of the client
2664 */
2665static void
2666client_disconnect_notification (void *cls,
2667 struct GNUNET_SERVER_Client *client)
2668{
2669 struct TransportClient *pos;
2670 struct TransportClient *prev;
2671 struct ClientMessageQueueEntry *mqe;
2672
2673#if DEBUG_TRANSPORT
2674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2675 "Client disconnected, cleaning up.\n");
2676#endif
2677 prev = NULL;
2678 pos = clients;
2679 while ((pos != NULL) && (pos->client != client))
2680 {
2681 prev = pos;
2682 pos = pos->next;
2683 }
2684 if (pos == NULL)
2685 return;
2686 while (NULL != (mqe = pos->message_queue_head))
2687 {
2688 pos->message_queue_head = mqe->next;
2689 GNUNET_free (mqe);
2690 }
2691 pos->message_queue_head = NULL;
2692 if (prev == NULL)
2693 clients = pos->next;
2694 else
2695 prev->next = pos->next;
2696 if (GNUNET_YES == pos->tcs_pending)
2697 {
2698 pos->client = NULL;
2699 return;
2700 }
2701 GNUNET_free (pos);
2702}
2703
2704
2705/**
2706 * Initiate transport service.
2707 *
2708 * @param cls closure
2709 * @param s scheduler to use
2710 * @param serv the initialized server
2711 * @param c configuration to use
2712 */
2713static void
2714run (void *cls,
2715 struct GNUNET_SCHEDULER_Handle *s,
2716 struct GNUNET_SERVER_Handle *serv, struct GNUNET_CONFIGURATION_Handle *c)
2717{
2718 char *plugs;
2719 char *pos;
2720 int no_transports;
2721 unsigned long long qin;
2722 unsigned long long qout;
2723 unsigned long long tneigh;
2724 char *keyfile;
2725
2726 sched = s;
2727 cfg = c;
2728 /* parse configuration */
2729 if ((GNUNET_OK !=
2730 GNUNET_CONFIGURATION_get_value_number (c,
2731 "TRANSPORT",
2732 "DEFAULT_QUOTA_IN",
2733 &qin)) ||
2734 (GNUNET_OK !=
2735 GNUNET_CONFIGURATION_get_value_number (c,
2736 "TRANSPORT",
2737 "DEFAULT_QUOTA_OUT",
2738 &qout)) ||
2739 (GNUNET_OK !=
2740 GNUNET_CONFIGURATION_get_value_number (c,
2741 "TRANSPORT",
2742 "NEIGHBOUR_LIMIT",
2743 &tneigh)) ||
2744 (GNUNET_OK !=
2745 GNUNET_CONFIGURATION_get_value_filename (c,
2746 "GNUNETD",
2747 "HOSTKEY", &keyfile)))
2748 {
2749 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2750 _
2751 ("Transport service is lacking key configuration settings. Exiting.\n"));
2752 GNUNET_SCHEDULER_shutdown (s);
2753 return;
2754 }
2755 max_connect_per_transport = (uint32_t) tneigh;
2756 default_quota_in = (uint32_t) qin;
2757 default_quota_out = (uint32_t) qout;
2758 my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2759 GNUNET_free (keyfile);
2760 if (my_private_key == NULL)
2761 {
2762 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2763 _
2764 ("Transport service could not access hostkey. Exiting.\n"));
2765 GNUNET_SCHEDULER_shutdown (s);
2766 return;
2767 }
2768 GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
2769 GNUNET_CRYPTO_hash (&my_public_key,
2770 sizeof (my_public_key), &my_identity.hashPubKey);
2771 /* setup notification */
2772 server = serv;
2773 GNUNET_SERVER_disconnect_notify (server,
2774 &client_disconnect_notification, NULL);
2775 /* load plugins... */
2776 no_transports = 1;
2777 if (GNUNET_OK ==
2778 GNUNET_CONFIGURATION_get_value_string (c,
2779 "TRANSPORT", "PLUGINS", &plugs))
2780 {
2781 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2782 _("Starting transport plugins `%s'\n"), plugs);
2783 pos = strtok (plugs, " ");
2784 while (pos != NULL)
2785 {
2786 start_transport (server, pos);
2787 no_transports = 0;
2788 pos = strtok (NULL, " ");
2789 }
2790 GNUNET_free (plugs);
2791 }
2792 if (no_transports)
2793 refresh_hello ();
2794 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport service ready.\n"));
2795 /* process client requests */
2796 GNUNET_SERVER_add_handlers (server, handlers);
2797}
2798
2799
2800/**
2801 * Function called when the service shuts
2802 * down. Unloads our plugins.
2803 *
2804 * @param cls closure
2805 * @param cfg configuration to use
2806 */
2807static void
2808unload_plugins (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
2809{
2810 struct TransportPlugin *plug;
2811 struct AddressList *al;
2812
2813#if DEBUG_TRANSPORT
2814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2815 "Transport service is unloading plugins...\n");
2816#endif
2817 while (NULL != (plug = plugins))
2818 {
2819 plugins = plug->next;
2820 GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
2821 GNUNET_free (plug->lib_name);
2822 GNUNET_free (plug->short_name);
2823 while (NULL != (al = plug->addresses))
2824 {
2825 plug->addresses = al->next;
2826 GNUNET_free (al);
2827 }
2828 GNUNET_free (plug);
2829 }
2830 if (my_private_key != NULL)
2831 GNUNET_CRYPTO_rsa_key_free (my_private_key);
2832}
2833
2834
2835/**
2836 * The main function for the transport service.
2837 *
2838 * @param argc number of arguments from the command line
2839 * @param argv command line arguments
2840 * @return 0 ok, 1 on error
2841 */
2842int
2843main (int argc, char *const *argv)
2844{
2845 return (GNUNET_OK ==
2846 GNUNET_SERVICE_run (argc,
2847 argv,
2848 "transport",
2849 &run, NULL, &unload_plugins, NULL)) ? 0 : 1;
2850}
2851
2852/* end of gnunet-service-transport.c */
diff --git a/src/transport/gnunet-transport.c b/src/transport/gnunet-transport.c
new file mode 100644
index 000000000..52475e3eb
--- /dev/null
+++ b/src/transport/gnunet-transport.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/**
22 * @file server/gnunet-transport.c
23 * @brief Tool to help configure the transports.
24 * @author Christian Grothoff
25 *
26 * This utility can be used to test if a transport mechanism for
27 * GNUnet is properly configured.
28 */
29
30#include "platform.h"
31#include "gnunet_program_lib.h"
32#include "gnunet_protocols.h"
33#include "gnunet_transport_service.h"
34
35int
36main (int argc, char *const *argv)
37{
38 return 0;
39}
40
41
42/* end of gnunet-transport.c */
diff --git a/src/transport/plugin_transport.h b/src/transport/plugin_transport.h
new file mode 100644
index 000000000..57df9affe
--- /dev/null
+++ b/src/transport/plugin_transport.h
@@ -0,0 +1,468 @@
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 transport/plugin_transport.h
23 * @brief API for the transport services. This header
24 * specifies the struct that is given to the plugin's entry
25 * method and the other struct that must be returned.
26 * Note that the destructors of transport plugins will
27 * be given the value returned by the constructor
28 * and is expected to return a NULL pointer.
29 *
30 * TODO:
31 * - consider moving DATA message (latency measurement)
32 * to service; avoids encapsulation overheads and
33 * would enable latency measurements for non-bidi
34 * transports.
35 * -
36 *
37 * @author Christian Grothoff
38 */
39#ifndef PLUGIN_TRANSPORT_H
40#define PLUGIN_TRANSPORT_H
41
42#include "gnunet_configuration_lib.h"
43#include "gnunet_scheduler_lib.h"
44#include "gnunet_transport_service.h"
45
46/**
47 * Opaque internal context for a particular peer of the transport
48 * service. Plugins will be given a pointer to this type and, if
49 * cheaply possible, should pass this pointer back to the transport
50 * service whenever additional messages from the same peer are
51 * received.
52 */
53struct ReadyList;
54
55/**
56 * Function called by the transport for each received message.
57 * This function should also be called with "NULL" for the
58 * message to signal that the other peer disconnected.
59 *
60 * @param cls closure
61 * @param plugin_context value to pass to this plugin
62 * to respond to the given peer (use is optional,
63 * but may speed up processing)
64 * @param service_context value passed to the transport-service
65 * to identify the neighbour; will be NULL on the first
66 * call for a given peer
67 * @param latency estimated latency for communicating with the
68 * given peer; should be set to GNUNET_TIME_UNIT_FOREVER_REL
69 * until the transport has seen messages transmitted in
70 * BOTH directions (and hence been able to do an actual
71 * round-trip observation); a non-FOREVER latency is also used
72 * by the transport to know that communication in both directions
73 * using this one plugin actually works
74 * @param peer (claimed) identity of the other peer
75 * @param message the message, NULL if peer was disconnected
76 * @return the new service_context that the plugin should use
77 * for future receive calls for messages from this
78 * particular peer
79 */
80typedef struct ReadyList *
81 (*GNUNET_TRANSPORT_PluginReceiveCallback) (void *cls,
82 void *plugin_context,
83 struct ReadyList *
84 service_context,
85 struct GNUNET_TIME_Relative
86 latency,
87 const struct GNUNET_PeerIdentity
88 * peer,
89 const struct GNUNET_MessageHeader
90 * message);
91
92
93/**
94 * Function that will be called for each address the transport
95 * is aware that it might be reachable under.
96 *
97 * @param cls closure
98 * @param name name of the transport that generated the address
99 * @param addr one of the addresses of the host, NULL for the last address
100 * the specific address format depends on the transport
101 * @param addrlen length of the address
102 * @param expires when should this address automatically expire?
103 */
104typedef void (*GNUNET_TRANSPORT_AddressNotification) (void *cls,
105 const char *name,
106 const void *addr,
107 size_t addrlen,
108 struct
109 GNUNET_TIME_Relative
110 expires);
111
112
113/**
114 * Function that will be called for each address obtained from the HELLO.
115 *
116 * @param cls closure
117 * @param name name of the transport that generated the address
118 * @param addr one of the addresses of the host, NULL for the last address
119 * the specific address format depends on the transport
120 * @param addrlen length of the address
121 */
122typedef void (*GNUNET_TRANSPORT_AddressCallback) (void *cls,
123 const char *name,
124 const void *addr,
125 size_t addrlen);
126
127
128/**
129 * Function that allows a transport to query the known
130 * network addresses for a given peer.
131 *
132 * @param cls closure
133 * @param timeout after how long should we time out?
134 * @param target which peer are we looking for?
135 * @param iter function to call for each known address
136 * @param iter_cls closure for iter
137 */
138typedef void (*GNUNET_TRANSPORT_LookupAddress) (void *cls,
139 struct GNUNET_TIME_Relative
140 timeout,
141 const struct
142 GNUNET_PeerIdentity * target,
143 GNUNET_TRANSPORT_AddressCallback
144 iter, void *iter_cls);
145
146
147/**
148 * The transport service will pass a pointer to a struct
149 * of this type as the first and only argument to the
150 * entry point of each transport plugin.
151 */
152struct GNUNET_TRANSPORT_PluginEnvironment
153{
154 /**
155 * Configuration to use.
156 */
157 struct GNUNET_CONFIGURATION_Handle *cfg;
158
159 /**
160 * Scheduler to use.
161 */
162 struct GNUNET_SCHEDULER_Handle *sched;
163
164 /**
165 * Our public key.
166 */
167 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *my_public_key;
168
169 /**
170 * Closure for the various callbacks.
171 */
172 void *cls;
173
174 /**
175 * Function that should be called by the transport plugin
176 * whenever a message is received.
177 */
178 GNUNET_TRANSPORT_PluginReceiveCallback receive;
179
180 /**
181 * Address lookup function.
182 */
183 GNUNET_TRANSPORT_LookupAddress lookup;
184
185 /**
186 * Function that must be called by each plugin to notify the
187 * transport service about the addresses under which the transport
188 * provided by the plugin can be reached.
189 */
190 GNUNET_TRANSPORT_AddressNotification notify_address;
191
192 /**
193 * What is the default quota (in terms of incoming bytes per
194 * ms) for new connections?
195 */
196 uint32_t default_quota_in;
197
198 /**
199 * What is the maximum number of connections that this transport
200 * should allow? Transports that do not have sessions (such as
201 * UDP) can ignore this value.
202 */
203 uint32_t max_connections;
204
205};
206
207
208/**
209 * Function that can be used by the transport service to transmit
210 * a message using the plugin using a fresh connection (even if
211 * we already have a connection to this peer, this function is
212 * required to establish a new one).
213 *
214 * @param cls closure
215 * @param target who should receive this message
216 * @param msg1 first message to transmit
217 * @param msg2 second message to transmit (can be NULL)
218 * @param timeout how long should we try to transmit these?
219 * @param addrlen length of the address
220 * @param addr the address
221 * @return session instance if the transmission has been scheduled
222 * NULL if the address format is invalid
223 */
224typedef void *
225 (*GNUNET_TRANSPORT_TransmitToAddressFunction) (void *cls,
226 const struct
227 GNUNET_PeerIdentity * target,
228 const struct
229 GNUNET_MessageHeader * msg1,
230 const struct
231 GNUNET_MessageHeader * msg2,
232 struct GNUNET_TIME_Relative
233 timeout, const void *addr,
234 size_t addrlen);
235
236/**
237 * Function called by the GNUNET_TRANSPORT_TransmitFunction
238 * upon "completion".
239 *
240 * @param cls closure
241 * @param service_context value passed to the transport-service
242 * to identify the neighbour
243 * @param target who was the recipient of the message?
244 * @param result GNUNET_OK on success
245 * GNUNET_SYSERR if the target disconnected;
246 * disconnect will ALSO be signalled using
247 * the ReceiveCallback.
248 */
249typedef void
250 (*GNUNET_TRANSPORT_TransmitContinuation) (void *cls,
251 struct ReadyList *
252 service_context,
253 const struct GNUNET_PeerIdentity *
254 target, int result);
255
256/**
257 * Function that can be used by the transport service to transmit
258 * a message using the plugin. Note that in the case of a
259 * peer disconnecting, the continuation MUST be called
260 * prior to the disconnect notification itself. This function
261 * will be called with this peer's HELLO message to initiate
262 * a fresh connection to another peer.
263 *
264 * @param cls closure
265 * @param plugin_context value we were asked to pass to this plugin
266 * to respond to the given peer (use is optional,
267 * but may speed up processing), can be NULL
268 * @param service_context value passed to the transport-service
269 * to identify the neighbour; NULL is used to indicate
270 * an urgent message. If the urgent message can not be
271 * scheduled for immediate transmission, the plugin is to
272 * call the continuation with failure immediately
273 * @param target who should receive this message
274 * @param msg the message to transmit
275 * @param timeout how long to wait at most for the transmission
276 * @param cont continuation to call once the message has
277 * been transmitted (or if the transport is ready
278 * for the next transmission call; or if the
279 * peer disconnected...); can be NULL
280 * @param cont_cls closure for cont
281 * @return plugin_context that should be used next time for
282 * sending messages to the specified peer
283 */
284typedef void *
285 (*GNUNET_TRANSPORT_TransmitFunction) (void *cls,
286 void *plugin_context,
287 struct ReadyList * service_context,
288 const struct GNUNET_PeerIdentity *
289 target,
290 const struct GNUNET_MessageHeader *
291 msg,
292 struct GNUNET_TIME_Relative timeout,
293 GNUNET_TRANSPORT_TransmitContinuation
294 cont, void *cont_cls);
295
296
297/**
298 * Function that can be called to force a disconnect from the
299 * specified neighbour. This should also cancel all previously
300 * scheduled transmissions. Obviously the transmission may have been
301 * partially completed already, which is OK. The plugin is supposed
302 * to close the connection (if applicable) and no longer call the
303 * transmit continuation(s).
304 *
305 * Finally, plugin MUST NOT call the services's receive function to
306 * notify the service that the connection to the specified target was
307 * closed after a getting this call.
308 *
309 * @param cls closure
310 * @param plugin_context value we were asked to pass to this plugin
311 * to respond to the given peer (use is optional,
312 * but may speed up processing), can be NULL (if
313 * NULL was returned from the transmit function)
314 * @param service_context must correspond to the service context
315 * of the corresponding Transmit call; the plugin should
316 * not cancel a send call made with a different service
317 * context pointer! Never NULL.
318 * @param target peer for which the last transmission is
319 * to be cancelled
320 */
321typedef void
322 (*GNUNET_TRANSPORT_CancelFunction) (void *cls,
323 void *plugin_context,
324 struct ReadyList * service_context,
325 const struct GNUNET_PeerIdentity *
326 target);
327
328
329/**
330 * Function called by the pretty printer for the resolved address for
331 * each human-readable address obtained.
332 *
333 * @param cls closure
334 * @param hostname one of the names for the host, NULL
335 * on the last call to the callback
336 */
337typedef void (*GNUNET_TRANSPORT_AddressStringCallback) (void *cls,
338 const char *address);
339
340
341/**
342 * Convert the transports address to a nice, human-readable
343 * format.
344 *
345 * @param cls closure
346 * @param name name of the transport that generated the address
347 * @param addr one of the addresses of the host, NULL for the last address
348 * the specific address format depends on the transport
349 * @param addrlen length of the address
350 * @param numeric should (IP) addresses be displayed in numeric form?
351 * @param timeout after how long should we give up?
352 * @param asc function to call on each string
353 * @param asc_cls closure for asc
354 */
355typedef void
356 (*GNUNET_TRANSPORT_AddressPrettyPrinter) (void *cls,
357 const char *type,
358 const void *addr,
359 size_t addrlen,
360 int numeric,
361 struct GNUNET_TIME_Relative
362 timeout,
363 GNUNET_TRANSPORT_AddressStringCallback
364 asc, void *asc_cls);
365
366
367/**
368 * Set a quota for receiving data from the given peer; this is a
369 * per-transport limit. The transport should limit its read/select
370 * calls to stay below the quota (in terms of incoming data).
371 *
372 * @param cls closure
373 * @param peer the peer for whom the quota is given
374 * @param quota_in quota for receiving/sending data in bytes per ms
375 */
376typedef void
377 (*GNUNET_TRANSPORT_SetQuota) (void *cls,
378 const struct GNUNET_PeerIdentity * target,
379 uint32_t quota_in);
380
381
382/**
383 * Another peer has suggested an address for this
384 * peer and transport plugin. Check that this could be a valid
385 * address. If so, consider adding it to the list
386 * of addresses.
387 *
388 * @param addr pointer to the address
389 * @param addrlen length of addr
390 * @return GNUNET_OK if this is a plausible address for this peer
391 * and transport
392 */
393typedef int
394 (*GNUNET_TRANSPORT_SuggestAddress) (void *cls,
395 const void *addr, size_t addrlen);
396
397/**
398 * Each plugin is required to return a pointer to a struct of this
399 * type as the return value from its entry point.
400 */
401struct GNUNET_TRANSPORT_PluginFunctions
402{
403
404 /**
405 * Closure for all of the callbacks.
406 */
407 void *cls;
408
409 /**
410 * Function used to send a single message to a particular
411 * peer using the specified address. Used to validate
412 * HELLOs.
413 */
414 GNUNET_TRANSPORT_TransmitToAddressFunction send_to;
415
416 /**
417 * Function that the transport service will use to transmit data to
418 * another peer. May be null for plugins that only support
419 * receiving data. After this call, the plugin call the specified
420 * continuation with success or error before notifying us about the
421 * target having disconnected.
422 */
423 GNUNET_TRANSPORT_TransmitFunction send;
424
425 /**
426 * Function that can be used to force the plugin to disconnect
427 * from the given peer and cancel all previous transmissions
428 * (and their continuationc).
429 */
430 GNUNET_TRANSPORT_CancelFunction cancel;
431
432 /**
433 * Function to pretty-print addresses. NOTE: this function is not
434 * yet used by transport-service, but will be used in the future
435 * once the transport-API has been completed.
436 */
437 GNUNET_TRANSPORT_AddressPrettyPrinter address_pretty_printer;
438
439 /**
440 * Function that the transport service can use to try to enforce a
441 * quota for the number of bytes received via this transport.
442 * Transports that can not refuse incoming data (such as UDP)
443 * are free to ignore these calls.
444 */
445 GNUNET_TRANSPORT_SetQuota set_receive_quota;
446
447 /**
448 * Function that will be called if another peer suggested that
449 * we should use a particular address (since he is reaching
450 * us at that address) for this transport.
451 */
452 GNUNET_TRANSPORT_SuggestAddress address_suggested;
453
454 /**
455 * Relative cost of this transport compared to others. This
456 * is supposed to be a static cost estimate which determines
457 * which plugins should not even be attempted if other,
458 * cheaper transports are already working. The idea is that
459 * the costs have roughly this relationship:
460 * <pre>
461 * TCP < UDP < HTTP == HTTPS < SMTP
462 * </pre>
463 */
464 unsigned int cost_estimate;
465};
466
467
468#endif
diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c
new file mode 100644
index 000000000..eb69f4386
--- /dev/null
+++ b/src/transport/plugin_transport_http.c
@@ -0,0 +1,2085 @@
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 transports/http.c
23 * @brief Implementation of the HTTP transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "gnunet_protocols.h"
30#include "gnunet_transport.h"
31#include "gnunet_stats_service.h"
32#include "gnunet_upnp_service.h"
33#include <stdint.h>
34#include <microhttpd.h>
35#include <curl/curl.h>
36#include "ip.h"
37
38#define DEBUG_HTTP GNUNET_NO
39
40/**
41 * Disable GET (for debugging only!). Must be GNUNET_YES
42 * in production use!
43 */
44#define DO_GET GNUNET_YES
45
46/**
47 * After how much time of the core not being associated with a http
48 * connection anymore do we close it?
49 *
50 * Needs to be larger than SECONDS_INACTIVE_DROP in
51 * core's connection.s
52 */
53#define HTTP_TIMEOUT (600 * GNUNET_CRON_SECONDS)
54
55/**
56 * How often do we re-issue GET requests?
57 */
58#define HTTP_GET_REFRESH (5 * GNUNET_CRON_SECONDS)
59
60/**
61 * Default maximum size of the HTTP read and write buffer.
62 */
63#define HTTP_BUF_SIZE (64 * 1024)
64
65/**
66 * Text of the response sent back after the last bytes of a PUT
67 * request have been received (just to formally obey the HTTP
68 * protocol).
69 */
70#define HTTP_PUT_RESPONSE "Thank you!"
71
72#define MY_TRANSPORT_NAME "HTTP"
73#include "common.c"
74
75/**
76 * Client-side data per PUT request.
77 */
78struct HTTPPutData
79{
80 /**
81 * This is a linked list.
82 */
83 struct HTTPPutData *next;
84
85 /**
86 * Handle to our CURL request.
87 */
88 CURL *curl_put;
89
90 /**
91 * Last time we made progress with the PUT.
92 */
93 GNUNET_CronTime last_activity;
94
95 /**
96 * The message we are sending.
97 */
98 char *msg;
99
100 /**
101 * Size of msg.
102 */
103 unsigned int size;
104
105 /**
106 * Current position in msg.
107 */
108 unsigned int pos;
109
110 /**
111 * Are we done sending? Set to 1 after we
112 * completed sending and started to receive
113 * a response ("Thank you!") or once the
114 * timeout has been reached.
115 */
116 int done;
117
118};
119
120/**
121 * Server-side data per PUT request.
122 */
123struct MHDPutData
124{
125 /**
126 * This is a linked list.
127 */
128 struct MHDPutData *next;
129
130 /**
131 * MHD connection handle for this request.
132 */
133 struct MHD_Connection *session;
134
135 /**
136 * Last time we received data on this PUT
137 * connection.
138 */
139 GNUNET_CronTime last_activity;
140
141 /**
142 * Read buffer for the header (from PUT)
143 */
144 char rbuff1[sizeof (GNUNET_MessageHeader)];
145
146 /**
147 * The read buffer (used only receiving PUT data).
148 */
149 char *rbuff2;
150
151 /**
152 * Number of valid bytes in rbuff1
153 */
154 unsigned int rpos1;
155
156 /**
157 * Number of valid bytes in rbuff2
158 */
159 unsigned int rpos2;
160
161
162 /**
163 * Size of the rbuff2 buffer.
164 */
165 unsigned int rsize2;
166
167 /**
168 * Should we sent a response for this PUT yet?
169 */
170 int ready;
171
172 /**
173 * Have we sent a response for this PUT yet?
174 */
175 int done;
176
177};
178
179/**
180 * Server-side data for a GET request.
181 */
182struct MHDGetData
183{
184
185 /**
186 * This is a linked list.
187 */
188 struct MHDGetData *next;
189
190 /**
191 * MHD connection handle for this request.
192 */
193 struct MHD_Connection *session;
194
195 /**
196 * GET session response handle
197 */
198 struct MHD_Response *get;
199
200 /**
201 * My HTTP session.
202 */
203 struct HTTPSession *httpsession;
204
205 /**
206 * The write buffer (for sending GET response)
207 */
208 char *wbuff;
209
210 /**
211 * What was the last time we were able to
212 * transmit data using the current get handle?
213 */
214 GNUNET_CronTime last_get_activity;
215
216 /**
217 * Current write position in wbuff
218 */
219 unsigned int woff;
220
221 /**
222 * Number of valid bytes in wbuff (starting at woff)
223 */
224 unsigned int wpos;
225
226 /**
227 * Size of the write buffer.
228 */
229 unsigned int wsize;
230
231};
232
233/**
234 * Transport Session handle.
235 */
236typedef struct HTTPSession
237{
238
239 /**
240 * GNUNET_TSession for this session.
241 */
242 GNUNET_TSession *tsession;
243
244 /**
245 * To whom are we talking to.
246 */
247 GNUNET_PeerIdentity sender;
248
249 /**
250 * number of users of this session
251 */
252 unsigned int users;
253
254 /**
255 * Has this session been destroyed?
256 */
257 int destroyed;
258
259 /**
260 * Are we client or server? Determines which of the
261 * structs in the union below is being used for this
262 * connection!
263 */
264 int is_client;
265
266 /**
267 * Is MHD still using this session handle?
268 */
269 int is_mhd_active;
270
271 /**
272 * Data maintained for the http client-server connection
273 * (depends on if we are client or server).
274 */
275 union
276 {
277
278 struct
279 {
280 /**
281 * Active PUT requests (linked list).
282 */
283 struct MHDPutData *puts;
284
285#if DO_GET
286 /**
287 * Active GET requests (linked list; most
288 * recent received GET is the head of the list).
289 */
290 struct MHDGetData *gets;
291#endif
292
293 } server;
294
295 struct
296 {
297
298 /**
299 * Address of the other peer.
300 */
301 HostAddress address;
302
303#if DO_GET
304 /**
305 * Last time the GET was active.
306 */
307 GNUNET_CronTime last_get_activity;
308
309 /**
310 * What was the last time we were able to
311 * transmit data using the current get handle?
312 */
313 GNUNET_CronTime last_get_initiated;
314
315 /**
316 * GET operation
317 */
318 CURL *get;
319
320 /**
321 * Read buffer for the header (from GET).
322 */
323 char rbuff1[sizeof (GNUNET_MessageHeader)];
324
325 /**
326 * The read buffer (used only receiving GET data).
327 */
328 char *rbuff2;
329
330 /**
331 * Number of valid bytes in rbuff1
332 */
333 unsigned int rpos1;
334
335 /**
336 * Number of valid bytes in rbuff2
337 */
338 unsigned int rpos2;
339
340 /**
341 * Current size of the read buffer rbuff2.
342 */
343 unsigned int rsize2;
344#endif
345
346 /**
347 * URL of the get and put operations.
348 */
349 char *url;
350
351 /**
352 * Linked list of PUT operations.
353 */
354 struct HTTPPutData *puts;
355
356 } client;
357
358 } cs;
359
360} HTTPSession;
361
362/* *********** globals ************* */
363
364static int stat_bytesReceived;
365
366static int stat_bytesSent;
367
368static int stat_bytesDropped;
369
370static int stat_get_issued;
371
372static int stat_get_received;
373
374static int stat_put_issued;
375
376static int stat_put_received;
377
378static int stat_select_calls;
379
380static int stat_send_calls;
381
382static int stat_connect_calls;
383
384static int stat_curl_send_callbacks;
385
386static int stat_curl_receive_callbacks;
387
388static int stat_mhd_access_callbacks;
389
390static int stat_mhd_read_callbacks;
391
392static int stat_mhd_close_callbacks;
393
394static int stat_connect_calls;
395
396/**
397 * How many requests do we have currently pending
398 * (with libcurl)?
399 */
400static unsigned int http_requests_pending;
401
402static int signal_pipe[2];
403
404static char *proxy;
405
406/**
407 * Daemon for listening for new connections.
408 */
409static struct MHD_Daemon *mhd_daemon;
410
411/**
412 * Curl multi for managing client operations.
413 */
414static CURLM *curl_multi;
415
416/**
417 * Set to GNUNET_YES while the transport is running.
418 */
419static int http_running;
420
421/**
422 * Thread running libcurl activities.
423 */
424static struct GNUNET_ThreadHandle *curl_thread;
425
426/**
427 * Array of currently active HTTP sessions.
428 */
429static GNUNET_TSession **tsessions;
430
431/**
432 * Number of valid entries in tsessions.
433 */
434static unsigned int tsessionCount;
435
436/**
437 * Sie of the tsessions array.
438 */
439static unsigned int tsessionArrayLength;
440
441/**
442 * Lock for concurrent access to all structures used
443 * by http, including CURL.
444 */
445static struct GNUNET_Mutex *lock;
446
447
448/**
449 * Signal select thread that its selector
450 * set may have changed.
451 */
452static void
453signal_select ()
454{
455 static char c;
456 WRITE (signal_pipe[1], &c, sizeof (c));
457}
458
459/**
460 * Check if we are allowed to connect to the given IP.
461 */
462static int
463acceptPolicyCallback (void *cls,
464 const struct sockaddr *addr, socklen_t addr_len)
465{
466 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
467 return MHD_NO;
468 return MHD_YES;
469}
470
471/**
472 * Disconnect from a remote node. May only be called
473 * on sessions that were acquired by the caller first.
474 * For the core, aquiration means to call associate or
475 * connect. The number of disconnects must match the
476 * number of calls to connect+associate.
477 *
478 * Sessions are actually discarded in cleanup_connections.
479 *
480 *
481 * @param tsession the session that is closed
482 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
483 */
484static int
485httpDisconnect (GNUNET_TSession * tsession)
486{
487 HTTPSession *httpsession = tsession->internal;
488 if (httpsession == NULL)
489 {
490 GNUNET_free (tsession);
491 return GNUNET_OK;
492 }
493 GNUNET_mutex_lock (lock);
494 httpsession->users--;
495 GNUNET_mutex_unlock (lock);
496 return GNUNET_OK;
497}
498
499static void
500destroy_tsession (GNUNET_TSession * tsession)
501{
502 HTTPSession *httpsession = tsession->internal;
503 struct HTTPPutData *pos;
504 struct HTTPPutData *next;
505#if DO_GET
506 struct MHDGetData *gpos;
507 struct MHDGetData *gnext;
508#endif
509 struct MHD_Response *r;
510 int i;
511
512 GNUNET_mutex_lock (lock);
513 for (i = 0; i < tsessionCount; i++)
514 {
515 if (tsessions[i] == tsession)
516 {
517 tsessions[i] = tsessions[--tsessionCount];
518 break;
519 }
520 }
521 if (httpsession->is_client)
522 {
523#if DO_GET
524 curl_multi_remove_handle (curl_multi, httpsession->cs.client.get);
525 http_requests_pending--;
526 signal_select ();
527 curl_easy_cleanup (httpsession->cs.client.get);
528 GNUNET_array_grow (httpsession->cs.client.rbuff2,
529 httpsession->cs.client.rsize2, 0);
530#endif
531 GNUNET_free_non_null (httpsession->cs.client.url);
532 pos = httpsession->cs.client.puts;
533 while (pos != NULL)
534 {
535 next = pos->next;
536 curl_multi_remove_handle (curl_multi, pos->curl_put);
537 http_requests_pending--;
538 signal_select ();
539 curl_easy_cleanup (pos->curl_put);
540 GNUNET_free (pos->msg);
541 GNUNET_free (pos);
542 pos = next;
543 }
544 GNUNET_free (httpsession);
545 GNUNET_free (tsession);
546 }
547 else
548 {
549 httpsession->destroyed = GNUNET_YES;
550 GNUNET_GE_BREAK (NULL, httpsession->cs.server.puts == NULL);
551#if DO_GET
552 gpos = httpsession->cs.server.gets;
553 while (gpos != NULL)
554 {
555 GNUNET_array_grow (gpos->wbuff, gpos->wsize, 0);
556 r = gpos->get;
557 gpos->get = NULL;
558 gnext = gpos->next;
559 MHD_destroy_response (r);
560 gpos = gnext;
561 }
562 httpsession->cs.server.gets = NULL;
563#endif
564 GNUNET_free (httpsession->tsession);
565 GNUNET_free (httpsession);
566 }
567 GNUNET_mutex_unlock (lock);
568}
569
570/**
571 * MHD is done handling a request. Cleanup
572 * the respective transport state.
573 */
574static void
575requestCompletedCallback (void *unused,
576 struct MHD_Connection *session,
577 void **httpSessionCache)
578{
579 HTTPSession *httpsession = *httpSessionCache;
580 struct MHDPutData *pprev;
581 struct MHDPutData *ppos;
582#if DO_GET
583 struct MHDGetData *gprev;
584 struct MHDGetData *gpos;
585#endif
586
587 if (stats != NULL)
588 stats->change (stat_mhd_close_callbacks, 1);
589 if (httpsession == NULL)
590 return; /* oops */
591 GNUNET_GE_ASSERT (NULL, !httpsession->is_client);
592 pprev = NULL;
593 ppos = httpsession->cs.server.puts;
594 while (ppos != NULL)
595 {
596 if (ppos->session == session)
597 {
598 ppos->last_activity = 0;
599 signal_select ();
600 return;
601 }
602 pprev = ppos;
603 ppos = ppos->next;
604 }
605#if DO_GET
606 gprev = NULL;
607 gpos = httpsession->cs.server.gets;
608 while (gpos != NULL)
609 {
610 if (gpos->session == session)
611 {
612 gpos->last_get_activity = 0;
613 signal_select ();
614 return;
615 }
616 gprev = gpos;
617 gpos = gpos->next;
618 }
619#endif
620 httpsession->is_mhd_active--;
621}
622
623/**
624 * A (core) Session is to be associated with a transport session. The
625 * transport service may want to know in order to call back on the
626 * core if the connection is being closed. Associate can also be
627 * called to test if it would be possible to associate the session
628 * later, in this case the argument session is NULL. This can be used
629 * to test if the connection must be closed by the core or if the core
630 * can assume that it is going to be self-managed (if associate
631 * returns GNUNET_OK and session was NULL, the transport layer is responsible
632 * for eventually freeing resources associated with the tesession). If
633 * session is not NULL, the core takes responsbility for eventually
634 * calling disconnect.
635 *
636 * @param tsession the session handle passed along
637 * from the call to receive that was made by the transport
638 * layer
639 * @return GNUNET_OK if the session could be associated,
640 * GNUNET_SYSERR if not.
641 */
642static int
643httpAssociate (GNUNET_TSession * tsession)
644{
645 HTTPSession *httpSession;
646
647 if (tsession == NULL)
648 {
649 GNUNET_GE_BREAK (NULL, 0);
650 return GNUNET_SYSERR;
651 }
652 httpSession = tsession->internal;
653 GNUNET_mutex_lock (lock);
654 if (httpSession->destroyed == GNUNET_YES)
655 {
656 GNUNET_mutex_unlock (lock);
657 return GNUNET_SYSERR;
658 }
659 httpSession->users++;
660 GNUNET_mutex_unlock (lock);
661 return GNUNET_OK;
662}
663
664/**
665 * Add a new session to the array watched by the select thread. Grows
666 * the array if needed. If the caller wants to do anything useful
667 * with the return value, it must have the lock before
668 * calling. It is ok to call this function without holding lock if
669 * the return value is ignored.
670 */
671static unsigned int
672addTSession (GNUNET_TSession * tsession)
673{
674 unsigned int i;
675
676 GNUNET_mutex_lock (lock);
677 if (tsessionCount == tsessionArrayLength)
678 GNUNET_array_grow (tsessions, tsessionArrayLength,
679 tsessionArrayLength * 2);
680 i = tsessionCount;
681 tsessions[tsessionCount++] = tsession;
682 GNUNET_mutex_unlock (lock);
683 return i;
684}
685
686#if DO_GET
687/**
688 * Callback for processing GET requests if our side is the
689 * MHD HTTP server.
690 *
691 * @param cls the HTTP session
692 * @param pos read-offset in the stream
693 * @param buf where to write the data
694 * @param max how much data to write (at most)
695 * @return number of bytes written, 0 is allowed!
696 */
697static int
698contentReaderCallback (void *cls, uint64_t pos, char *buf, int max)
699{
700 struct MHDGetData *mgd = cls;
701
702 if (stats != NULL)
703 stats->change (stat_mhd_read_callbacks, 1);
704 GNUNET_mutex_lock (lock);
705 if (mgd->wpos < max)
706 max = mgd->wpos;
707 memcpy (buf, &mgd->wbuff[mgd->woff], max);
708 mgd->wpos -= max;
709 mgd->woff += max;
710 if (max > 0)
711 mgd->last_get_activity = GNUNET_get_time ();
712 if (mgd->wpos == 0)
713 mgd->woff = 0;
714 GNUNET_mutex_unlock (lock);
715#if DEBUG_HTTP
716 GNUNET_GE_LOG (coreAPI->ectx,
717 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
718 "HTTP returns %u bytes in MHD's GET handler.\n", max);
719#endif
720 if (stats != NULL)
721 stats->change (stat_bytesSent, max);
722 if ((max == 0) && (mgd->httpsession->cs.server.gets != mgd))
723 return -1; /* end of response (another GET replaces this one) */
724 return max;
725}
726#endif
727
728#if DO_GET
729/**
730 * Notification that libmicrohttpd no longer needs the
731 * response object.
732 */
733static void
734contentReaderFreeCallback (void *cls)
735{
736 struct MHDGetData *mgd = cls;
737
738 GNUNET_GE_ASSERT (NULL, mgd->get == NULL);
739 GNUNET_array_grow (mgd->wbuff, mgd->wsize, 0);
740 GNUNET_free (mgd);
741}
742#endif
743
744/**
745 * Process GET or PUT request received via MHD. For
746 * GET, queue response that will send back our pending
747 * messages. For PUT, process incoming data and send
748 * to GNUnet core. In either case, check if a session
749 * already exists and create a new one if not.
750 */
751static int
752accessHandlerCallback (void *cls,
753 struct MHD_Connection *session,
754 const char *url,
755 const char *method,
756 const char *version,
757 const char *upload_data,
758 size_t * upload_data_size, void **httpSessionCache)
759{
760 GNUNET_TSession *tsession;
761 struct MHDPutData *put;
762 struct MHDGetData *get;
763 HTTPSession *httpSession;
764 struct MHD_Response *response;
765 GNUNET_HashCode client;
766 int i;
767 unsigned int have;
768 GNUNET_MessageHeader *hdr;
769 GNUNET_TransportPacket *mp;
770 unsigned int cpy;
771 unsigned int poff;
772
773 if (stats != NULL)
774 stats->change (stat_mhd_access_callbacks, 1);
775#if DEBUG_HTTP
776 GNUNET_GE_LOG (coreAPI->ectx,
777 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
778 "HTTP/MHD receives `%s' request.\n", method);
779#endif
780 /* convert URL to sender peer id */
781 if ((strlen (url) < 2)
782 || (GNUNET_OK != GNUNET_enc_to_hash (&url[1], &client)))
783 {
784 /* invalid request */
785 /* GNUNET_GE_BREAK_OP (NULL, 0); -- this happens a lot, most likely
786 somebody scanning for MyDoom.X-opened backdoors */
787 return MHD_NO;
788 }
789
790 /* check if we already have a session for this */
791 httpSession = *httpSessionCache;
792 if (httpSession == NULL)
793 {
794 /* new http connection */
795 if (stats != NULL)
796 {
797 if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method))
798 stats->change (stat_put_received, 1);
799 else
800 stats->change (stat_get_received, 1);
801 }
802 GNUNET_mutex_lock (lock);
803 for (i = 0; i < tsessionCount; i++)
804 {
805 tsession = tsessions[i];
806 httpSession = tsession->internal;
807 if ((0 ==
808 memcmp (&httpSession->sender, &client,
809 sizeof (GNUNET_HashCode)))
810 && (httpSession->is_client == GNUNET_NO))
811 break;
812 tsession = NULL;
813 httpSession = NULL;
814 }
815 GNUNET_mutex_unlock (lock);
816 }
817 /* create new session if necessary */
818 if (httpSession == NULL)
819 {
820#if DEBUG_HTTP
821 GNUNET_GE_LOG (coreAPI->ectx,
822 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
823 "HTTP/MHD creates new session for request from `%s'.\n",
824 &url[1]);
825#endif
826 httpSession = GNUNET_malloc (sizeof (HTTPSession));
827 memset (httpSession, 0, sizeof (HTTPSession));
828 httpSession->sender.hashPubKey = client;
829 httpSession->users = 0; /* MHD */
830 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
831 memset (tsession, 0, sizeof (GNUNET_TSession));
832 tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
833 tsession->internal = httpSession;
834 tsession->peer.hashPubKey = client;
835 httpSession->tsession = tsession;
836 addTSession (tsession);
837 }
838 if (*httpSessionCache == NULL)
839 {
840 httpSession->is_mhd_active++;
841 *httpSessionCache = httpSession;
842 }
843 GNUNET_mutex_lock (lock);
844#if DO_GET
845 if (0 == strcasecmp (MHD_HTTP_METHOD_GET, method))
846 {
847#if DEBUG_HTTP
848 GNUNET_GE_LOG (coreAPI->ectx,
849 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
850 "HTTP/MHD receives GET request from `%s'.\n", &url[1]);
851#endif
852
853 /* handle get; create response object if we do not
854 have one already */
855 get = GNUNET_malloc (sizeof (struct MHDGetData));
856 memset (get, 0, sizeof (struct MHDGetData));
857 get->next = httpSession->cs.server.gets;
858 httpSession->cs.server.gets = get;
859 get->session = session;
860 get->httpsession = httpSession;
861 get->last_get_activity = GNUNET_get_time ();
862 get->get = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
863 64 * 1024,
864 contentReaderCallback,
865 get,
866 contentReaderFreeCallback);
867 MHD_queue_response (session, MHD_HTTP_OK, get->get);
868 GNUNET_mutex_unlock (lock);
869 return MHD_YES;
870 }
871#endif
872 if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method))
873 {
874#if DEBUG_HTTP
875 GNUNET_GE_LOG (coreAPI->ectx,
876 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
877 "HTTP/MHD receives PUT request from `%s' with %u bytes.\n",
878 &url[1], *upload_data_size);
879#endif
880 put = httpSession->cs.server.puts;
881 while ((put != NULL) && (put->session != session))
882 put = put->next;
883 if (put == NULL)
884 {
885 put = GNUNET_malloc (sizeof (struct MHDPutData));
886 memset (put, 0, sizeof (struct MHDPutData));
887 put->next = httpSession->cs.server.puts;
888 httpSession->cs.server.puts = put;
889 put->session = session;
890 }
891 put->last_activity = GNUNET_get_time ();
892
893 /* handle put (upload_data!) */
894 poff = 0;
895 have = *upload_data_size;
896 if (stats != NULL)
897 stats->change (stat_bytesReceived, have);
898 *upload_data_size = 0; /* we will always process everything */
899 if ((have == 0) && (put->done == GNUNET_NO)
900 && (put->ready == GNUNET_YES))
901 {
902 put->done = GNUNET_YES;
903 /* end of upload, send response! */
904#if DEBUG_HTTP
905 GNUNET_GE_LOG (coreAPI->ectx,
906 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
907 "HTTP/MHD queues dummy response to completed PUT request.\n");
908#endif
909 response =
910 MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),
911 HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
912 MHD_queue_response (session, MHD_HTTP_OK, response);
913 MHD_destroy_response (response);
914 GNUNET_mutex_unlock (lock);
915 return MHD_YES;
916 }
917 while (have > 0)
918 {
919 put->ready = GNUNET_NO;
920 if (put->rpos1 < sizeof (GNUNET_MessageHeader))
921 {
922 cpy = sizeof (GNUNET_MessageHeader) - put->rpos1;
923 if (cpy > have)
924 cpy = have;
925 memcpy (&put->rbuff1[put->rpos1], &upload_data[poff], cpy);
926 put->rpos1 += cpy;
927 have -= cpy;
928 poff += cpy;
929 put->rpos2 = 0;
930 }
931 if (put->rpos1 < sizeof (GNUNET_MessageHeader))
932 break;
933 hdr = (GNUNET_MessageHeader *) put->rbuff1;
934 GNUNET_array_grow (put->rbuff2,
935 put->rsize2,
936 ntohs (hdr->size) -
937 sizeof (GNUNET_MessageHeader));
938 if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
939 {
940 cpy =
941 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) -
942 put->rpos2;
943 if (cpy > have)
944 cpy = have;
945 memcpy (&put->rbuff2[put->rpos2], &upload_data[poff], cpy);
946 have -= cpy;
947 poff += cpy;
948 put->rpos2 += cpy;
949 }
950 if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
951 break;
952 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
953 mp->msg = put->rbuff2;
954 mp->sender = httpSession->sender;
955 mp->tsession = httpSession->tsession;
956 mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader);
957#if DEBUG_HTTP
958 GNUNET_GE_LOG (coreAPI->ectx,
959 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
960 "HTTP/MHD passes %u bytes to core (received via PUT request).\n",
961 mp->size);
962#endif
963 coreAPI->receive (mp);
964 put->rbuff2 = NULL;
965 put->rpos2 = 0;
966 put->rsize2 = 0;
967 put->rpos1 = 0;
968 put->ready = GNUNET_YES;
969 }
970 GNUNET_mutex_unlock (lock);
971 return MHD_YES;
972 }
973 GNUNET_mutex_unlock (lock);
974 GNUNET_GE_BREAK_OP (NULL, 0); /* invalid request */
975 return MHD_NO;
976}
977
978#if DO_GET
979/**
980 * Process downloaded bits (from GET via CURL).
981 */
982static size_t
983receiveContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx)
984{
985 HTTPSession *httpSession = ctx;
986 const char *inbuf = ptr;
987 size_t have = size * nmemb;
988 size_t poff = 0;
989 size_t cpy;
990 GNUNET_MessageHeader *hdr;
991 GNUNET_TransportPacket *mp;
992
993 if (stats != NULL)
994 stats->change (stat_curl_receive_callbacks, 1);
995 httpSession->cs.client.last_get_activity = GNUNET_get_time ();
996#if DEBUG_HTTP
997 GNUNET_GE_LOG (coreAPI->ectx,
998 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
999 "HTTP/CURL receives %u bytes as response to GET.\n",
1000 size * nmemb);
1001#endif
1002 while (have > 0)
1003 {
1004 if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader))
1005 {
1006 cpy = sizeof (GNUNET_MessageHeader) - httpSession->cs.client.rpos1;
1007 if (cpy > have)
1008 cpy = have;
1009 memcpy (&httpSession->cs.client.
1010 rbuff1[httpSession->cs.client.rpos1], &inbuf[poff], cpy);
1011 httpSession->cs.client.rpos1 += cpy;
1012 have -= cpy;
1013 poff += cpy;
1014 httpSession->cs.client.rpos2 = 0;
1015 }
1016 if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader))
1017 break;
1018 hdr = (GNUNET_MessageHeader *) httpSession->cs.client.rbuff1;
1019 GNUNET_array_grow (httpSession->cs.client.rbuff2,
1020 httpSession->cs.client.rsize2,
1021 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader));
1022 if (httpSession->cs.client.rpos2 <
1023 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
1024 {
1025 cpy =
1026 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) -
1027 httpSession->cs.client.rpos2;
1028 if (cpy > have)
1029 cpy = have;
1030 memcpy (&httpSession->cs.client.
1031 rbuff2[httpSession->cs.client.rpos2], &inbuf[poff], cpy);
1032 have -= cpy;
1033 poff += cpy;
1034 httpSession->cs.client.rpos2 += cpy;
1035 }
1036 if (httpSession->cs.client.rpos2 <
1037 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
1038 break;
1039 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
1040 mp->msg = httpSession->cs.client.rbuff2;
1041 mp->sender = httpSession->sender;
1042 mp->tsession = httpSession->tsession;
1043 mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader);
1044 coreAPI->receive (mp);
1045 httpSession->cs.client.rbuff2 = NULL;
1046 httpSession->cs.client.rpos2 = 0;
1047 httpSession->cs.client.rsize2 = 0;
1048 httpSession->cs.client.rpos1 = 0;
1049 }
1050 if (stats != NULL)
1051 stats->change (stat_bytesReceived, size * nmemb);
1052 return size * nmemb;
1053}
1054#endif
1055
1056/**
1057 * Provide bits for upload: we're using CURL for a PUT request
1058 * and now need to provide data from the message we are transmitting.
1059 */
1060static size_t
1061sendContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx)
1062{
1063 struct HTTPPutData *put = ctx;
1064 size_t max = size * nmemb;
1065
1066 if (stats != NULL)
1067 stats->change (stat_curl_send_callbacks, 1);
1068 put->last_activity = GNUNET_get_time ();
1069 if (max > put->size - put->pos)
1070 max = put->size - put->pos;
1071 memcpy (ptr, &put->msg[put->pos], max);
1072 put->pos += max;
1073#if DEBUG_HTTP
1074 GNUNET_GE_LOG (coreAPI->ectx,
1075 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1076 "HTTP/CURL sends %u bytes in PUT request.\n", max);
1077#endif
1078 if (stats != NULL)
1079 stats->change (stat_bytesSent, max);
1080 return max;
1081}
1082
1083#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_GE_LOG(coreAPI->ectx, GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
1084#define IP_BUF_LEN 128
1085
1086static void
1087create_session_url (HTTPSession * httpSession)
1088{
1089 char buf[IP_BUF_LEN];
1090 char *url;
1091 GNUNET_EncName enc;
1092 unsigned short available;
1093 const char *obr;
1094 const char *cbr;
1095 const HostAddress *haddr =
1096 (const HostAddress *) &httpSession->cs.client.address;
1097
1098 url = httpSession->cs.client.url;
1099 if (url == NULL)
1100 {
1101 GNUNET_hash_to_enc (&coreAPI->my_identity->hashPubKey, &enc);
1102 available = ntohs (haddr->availability) & available_protocols;
1103 if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6))
1104 {
1105 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
1106 available = VERSION_AVAILABLE_IPV4;
1107 else
1108 available = VERSION_AVAILABLE_IPV6;
1109 }
1110 if ((available & VERSION_AVAILABLE_IPV4) > 0)
1111 {
1112 if (NULL == inet_ntop (AF_INET, &haddr->ipv4, buf, IP_BUF_LEN))
1113 {
1114 /* log? */
1115 return;
1116 }
1117 obr = "";
1118 cbr = "";
1119 }
1120 else if ((available & VERSION_AVAILABLE_IPV6) > 0)
1121 {
1122 if (NULL == inet_ntop (AF_INET6, &haddr->ipv6, buf, IP_BUF_LEN))
1123 {
1124 /* log? */
1125 return;
1126 }
1127 obr = "[";
1128 cbr = "]";
1129 }
1130 else
1131 return; /* error */
1132 url = GNUNET_malloc (64 + sizeof (GNUNET_EncName) + strlen (buf));
1133 GNUNET_snprintf (url,
1134 64 + sizeof (GNUNET_EncName),
1135 "http://%s%s%s:%u/%s", obr, buf, cbr,
1136 ntohs (haddr->port), &enc);
1137 httpSession->cs.client.url = url;
1138 }
1139}
1140
1141#if DO_GET
1142/**
1143 * Try to do a GET on the other peer of the given
1144 * http session.
1145 *
1146 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1147 */
1148static int
1149create_curl_get (HTTPSession * httpSession)
1150{
1151 CURL *curl_get;
1152 CURLcode ret;
1153 CURLMcode mret;
1154 GNUNET_CronTime now;
1155
1156 if (httpSession->cs.client.url == NULL)
1157 return GNUNET_SYSERR;
1158 curl_get = httpSession->cs.client.get;
1159 if (curl_get != NULL)
1160 {
1161 GNUNET_mutex_lock (lock);
1162 curl_multi_remove_handle (curl_multi, curl_get);
1163 http_requests_pending--;
1164 signal_select ();
1165 curl_easy_cleanup (curl_get);
1166 GNUNET_mutex_unlock (lock);
1167 httpSession->cs.client.get = NULL;
1168 }
1169 curl_get = curl_easy_init ();
1170 if (curl_get == NULL)
1171 return GNUNET_SYSERR;
1172 /* create GET */
1173 CURL_EASY_SETOPT (curl_get, CURLOPT_FAILONERROR, 1);
1174 CURL_EASY_SETOPT (curl_get, CURLOPT_URL, httpSession->cs.client.url);
1175 if (strlen (proxy) > 0)
1176 CURL_EASY_SETOPT (curl_get, CURLOPT_PROXY, proxy);
1177 CURL_EASY_SETOPT (curl_get, CURLOPT_BUFFERSIZE, 32 * 1024);
1178 if (0 == strncmp (httpSession->cs.client.url, "http", 4))
1179 CURL_EASY_SETOPT (curl_get, CURLOPT_USERAGENT, "GNUnet-http");
1180#if 0
1181 CURL_EASY_SETOPT (curl_get, CURLOPT_VERBOSE, 1);
1182#endif
1183 CURL_EASY_SETOPT (curl_get, CURLOPT_CONNECTTIMEOUT, 150L);
1184 /* NOTE: use of CONNECTTIMEOUT without also
1185 setting NOSIGNAL results in really weird
1186 crashes on my system! */
1187 CURL_EASY_SETOPT (curl_get, CURLOPT_NOSIGNAL, 1);
1188 CURL_EASY_SETOPT (curl_get, CURLOPT_TIMEOUT, 150L);
1189 CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEFUNCTION, &receiveContentCallback);
1190 CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEDATA, httpSession);
1191 CURL_EASY_SETOPT (curl_get, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1192 if (ret != CURLE_OK)
1193 {
1194 curl_easy_cleanup (curl_get);
1195 return GNUNET_SYSERR;
1196 }
1197 GNUNET_mutex_lock (lock);
1198 mret = curl_multi_add_handle (curl_multi, curl_get);
1199 http_requests_pending++;
1200 GNUNET_mutex_unlock (lock);
1201 if (stats != NULL)
1202 stats->change (stat_get_issued, 1);
1203 if (mret != CURLM_OK)
1204 {
1205 GNUNET_GE_LOG (coreAPI->ectx,
1206 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1207 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1208 "curl_multi_add_handle", __FILE__, __LINE__,
1209 curl_multi_strerror (mret));
1210 curl_easy_cleanup (curl_get);
1211 return GNUNET_SYSERR;
1212 }
1213 signal_select ();
1214 now = GNUNET_get_time ();
1215 httpSession->cs.client.last_get_activity = now;
1216 httpSession->cs.client.get = curl_get;
1217 httpSession->cs.client.last_get_initiated = now;
1218#if DEBUG_HTTP
1219 GNUNET_GE_LOG (coreAPI->ectx,
1220 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1221 "HTTP/CURL initiated GET request.\n");
1222#endif
1223 return GNUNET_OK;
1224}
1225#endif
1226
1227/**
1228 * Establish a connection to a remote node.
1229 *
1230 * @param hello the hello-Message for the target node
1231 * @param tsessionPtr the session handle that is set
1232 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1233 */
1234static int
1235httpConnect (const GNUNET_MessageHello * hello,
1236 GNUNET_TSession ** tsessionPtr, int may_reuse)
1237{
1238 const HostAddress *haddr = (const HostAddress *) &hello[1];
1239 GNUNET_TSession *tsession;
1240 HTTPSession *httpSession;
1241 int i;
1242
1243 if (stats != NULL)
1244 stats->change (stat_connect_calls, 1);
1245 /* check if we have a session pending for this peer */
1246 tsession = NULL;
1247 if (may_reuse)
1248 {
1249 GNUNET_mutex_lock (lock);
1250 for (i = 0; i < tsessionCount; i++)
1251 {
1252 if (0 == memcmp (&hello->senderIdentity,
1253 &tsessions[i]->peer, sizeof (GNUNET_PeerIdentity)))
1254 {
1255 tsession = tsessions[i];
1256 break;
1257 }
1258 }
1259 if ((tsession != NULL) && (GNUNET_OK == httpAssociate (tsession)))
1260 {
1261 *tsessionPtr = tsession;
1262 GNUNET_mutex_unlock (lock);
1263 return GNUNET_OK;
1264 }
1265 GNUNET_mutex_unlock (lock);
1266 }
1267 /* no session pending, initiate a new one! */
1268 httpSession = GNUNET_malloc (sizeof (HTTPSession));
1269 memset (httpSession, 0, sizeof (HTTPSession));
1270 httpSession->sender = hello->senderIdentity;
1271 httpSession->users = 1; /* us only, core has not seen this tsession! */
1272 httpSession->is_client = GNUNET_YES;
1273 httpSession->cs.client.address = *haddr;
1274 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
1275 memset (tsession, 0, sizeof (GNUNET_TSession));
1276 httpSession->tsession = tsession;
1277 tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
1278 tsession->internal = httpSession;
1279 tsession->peer = hello->senderIdentity;
1280 create_session_url (httpSession);
1281#if DO_GET
1282 if (GNUNET_OK != create_curl_get (httpSession))
1283 {
1284 GNUNET_free (tsession);
1285 GNUNET_free (httpSession);
1286 return GNUNET_SYSERR;
1287 }
1288#endif
1289 /* PUTs will be created as needed */
1290 addTSession (tsession);
1291 *tsessionPtr = tsession;
1292#if DEBUG_HTTP
1293 GNUNET_GE_LOG (coreAPI->ectx,
1294 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1295 "HTTP/CURL initiated connection to `%s'.\n",
1296 httpSession->cs.client.url);
1297#endif
1298 return GNUNET_OK;
1299}
1300
1301/**
1302 * We received the "Thank you!" response to a PUT.
1303 * Discard the data (not useful) and mark the PUT
1304 * operation as completed.
1305 */
1306static size_t
1307discardContentCallback (void *data, size_t size, size_t nmemb, void *put_cls)
1308{
1309 struct HTTPPutData *put = put_cls;
1310 /* this condition should pretty much always be
1311 true; just checking here in case the PUT
1312 response comes early somehow */
1313 if (put->pos == put->size)
1314 put->done = GNUNET_YES;
1315 return size * nmemb;
1316}
1317
1318/**
1319 * Create a new PUT request for the given PUT data.
1320 */
1321static int
1322create_curl_put (HTTPSession * httpSession, struct HTTPPutData *put)
1323{
1324 CURL *curl_put;
1325 CURLcode ret;
1326 CURLMcode mret;
1327 long size;
1328
1329 /* we should have initiated a GET earlier,
1330 so URL must not be NULL here */
1331 if (httpSession->cs.client.url == NULL)
1332 return GNUNET_SYSERR;
1333 curl_put = curl_easy_init ();
1334 if (curl_put == NULL)
1335 return GNUNET_SYSERR;
1336 CURL_EASY_SETOPT (curl_put, CURLOPT_FAILONERROR, 1);
1337 CURL_EASY_SETOPT (curl_put, CURLOPT_URL, httpSession->cs.client.url);
1338 if (strlen (proxy) > 0)
1339 CURL_EASY_SETOPT (curl_put, CURLOPT_PROXY, proxy);
1340 CURL_EASY_SETOPT (curl_put, CURLOPT_BUFFERSIZE, put->size);
1341 if (0 == strncmp (httpSession->cs.client.url, "http", 4))
1342 CURL_EASY_SETOPT (curl_put, CURLOPT_USERAGENT, "GNUnet-http");
1343 CURL_EASY_SETOPT (curl_put, CURLOPT_UPLOAD, 1);
1344#if 0
1345 CURL_EASY_SETOPT (curl_put, CURLOPT_VERBOSE, 1);
1346#endif
1347 CURL_EASY_SETOPT (curl_put, CURLOPT_CONNECTTIMEOUT, 150L);
1348 /* NOTE: use of CONNECTTIMEOUT without also
1349 setting NOSIGNAL results in really weird
1350 crashes on my system! */
1351 CURL_EASY_SETOPT (curl_put, CURLOPT_NOSIGNAL, 1);
1352 CURL_EASY_SETOPT (curl_put, CURLOPT_TIMEOUT, 150L);
1353 size = put->size;
1354 CURL_EASY_SETOPT (curl_put, CURLOPT_INFILESIZE, size);
1355 CURL_EASY_SETOPT (curl_put, CURLOPT_READFUNCTION, &sendContentCallback);
1356 CURL_EASY_SETOPT (curl_put, CURLOPT_READDATA, put);
1357 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEFUNCTION, &discardContentCallback);
1358 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEDATA, put);
1359 CURL_EASY_SETOPT (curl_put, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1360 if (ret != CURLE_OK)
1361 {
1362 curl_easy_cleanup (curl_put);
1363 return GNUNET_SYSERR;
1364 }
1365 GNUNET_mutex_lock (lock);
1366 mret = curl_multi_add_handle (curl_multi, curl_put);
1367 http_requests_pending++;
1368 GNUNET_mutex_unlock (lock);
1369 if (stats != NULL)
1370 stats->change (stat_put_issued, 1);
1371 if (mret != CURLM_OK)
1372 {
1373 GNUNET_GE_LOG (coreAPI->ectx,
1374 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1375 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1376 "curl_multi_add_handle", __FILE__, __LINE__,
1377 curl_multi_strerror (mret));
1378 return GNUNET_SYSERR;
1379 }
1380 signal_select ();
1381 put->curl_put = curl_put;
1382#if DEBUG_HTTP
1383 GNUNET_GE_LOG (coreAPI->ectx,
1384 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1385 "HTTP/CURL initiated PUT request to `%s'.\n",
1386 httpSession->cs.client.url);
1387#endif
1388 return GNUNET_OK;
1389}
1390
1391
1392/**
1393 * Test if the transport would even try to send
1394 * a message of the given size and importance
1395 * for the given session.<br>
1396 * This function is used to check if the core should
1397 * even bother to construct (and encrypt) this kind
1398 * of message.
1399 *
1400 * @return GNUNET_YES if the transport would try (i.e. queue
1401 * the message or call the OS to send),
1402 * GNUNET_NO if the transport would just drop the message,
1403 * GNUNET_SYSERR if the size/session is invalid
1404 */
1405static int
1406httpTestWouldTry (GNUNET_TSession * tsession, const unsigned int size,
1407 int important)
1408{
1409 HTTPSession *httpSession = tsession->internal;
1410 struct MHDGetData *get;
1411 int ret;
1412
1413 if (size >= GNUNET_MAX_BUFFER_SIZE - sizeof (GNUNET_MessageHeader))
1414 {
1415 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1416 return GNUNET_SYSERR;
1417 }
1418 if (size == 0)
1419 {
1420 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1421 return GNUNET_SYSERR;
1422 }
1423 if (httpSession->is_client)
1424 {
1425 /* client */
1426 if ((important != GNUNET_YES) && (httpSession->cs.client.puts != NULL))
1427 return GNUNET_NO;
1428 return GNUNET_YES;
1429 }
1430 else
1431 {
1432 /* server */
1433 GNUNET_mutex_lock (lock);
1434 get = httpSession->cs.server.gets;
1435 if (get == NULL)
1436 ret = GNUNET_NO;
1437 else
1438 {
1439 if (get->wsize == 0)
1440 ret = GNUNET_YES;
1441 else if ((get->wpos + size > get->wsize)
1442 && (important != GNUNET_YES))
1443 ret = GNUNET_NO;
1444 else
1445 ret = GNUNET_YES;
1446 }
1447 GNUNET_mutex_unlock (lock);
1448 return ret;
1449 }
1450}
1451
1452
1453/**
1454 * Send a message to the specified remote node.
1455 *
1456 * @param tsession the GNUNET_MessageHello identifying the remote node
1457 * @param msg the message
1458 * @param size the size of the message
1459 * @return GNUNET_SYSERR on error, GNUNET_OK on success, GNUNET_NO if queue is full
1460 */
1461static int
1462httpSend (GNUNET_TSession * tsession,
1463 const void *msg, unsigned int size, int important)
1464{
1465 HTTPSession *httpSession = tsession->internal;
1466 struct HTTPPutData *putData;
1467 GNUNET_MessageHeader *hdr;
1468#if DO_GET
1469 struct MHDGetData *getData;
1470 char *tmp;
1471#endif
1472
1473 if (stats != NULL)
1474 stats->change (stat_send_calls, 1);
1475 if (httpSession->is_client)
1476 {
1477 /* we need to do a PUT (we are the client) */
1478 if (size >= GNUNET_MAX_BUFFER_SIZE)
1479 return GNUNET_SYSERR;
1480 if (size == 0)
1481 {
1482 GNUNET_GE_BREAK (NULL, 0);
1483 return GNUNET_SYSERR;
1484 }
1485 if (important != GNUNET_YES)
1486 {
1487 GNUNET_mutex_lock (lock);
1488 if (httpSession->cs.client.puts != NULL)
1489 {
1490 /* do not queue more than one unimportant PUT at a time */
1491 signal_select (); /* do clean up now! */
1492 GNUNET_mutex_unlock (lock);
1493 if (stats != NULL)
1494 stats->change (stat_bytesDropped, size);
1495
1496 return GNUNET_NO;
1497 }
1498 GNUNET_mutex_unlock (lock);
1499 }
1500 putData = GNUNET_malloc (sizeof (struct HTTPPutData));
1501 memset (putData, 0, sizeof (struct HTTPPutData));
1502 putData->msg = GNUNET_malloc (size + sizeof (GNUNET_MessageHeader));
1503 hdr = (GNUNET_MessageHeader *) putData->msg;
1504 hdr->size = htons (size + sizeof (GNUNET_MessageHeader));
1505 hdr->type = htons (0);
1506 memcpy (&putData->msg[sizeof (GNUNET_MessageHeader)], msg, size);
1507 putData->size = size + sizeof (GNUNET_MessageHeader);
1508 putData->last_activity = GNUNET_get_time ();
1509 if (GNUNET_OK != create_curl_put (httpSession, putData))
1510 {
1511 GNUNET_free (putData->msg);
1512 GNUNET_free (putData);
1513 return GNUNET_SYSERR;
1514 }
1515 GNUNET_mutex_lock (lock);
1516 putData->next = httpSession->cs.client.puts;
1517 httpSession->cs.client.puts = putData;
1518 GNUNET_mutex_unlock (lock);
1519 return GNUNET_OK;
1520 }
1521
1522 /* httpSession->isClient == false, respond to a GET (we
1523 hopefully have one or will have one soon) */
1524#if DEBUG_HTTP
1525 GNUNET_GE_LOG (coreAPI->ectx,
1526 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1527 "HTTP/MHD queues %u bytes to be sent as response to GET as soon as possible.\n",
1528 size);
1529#endif
1530#if DO_GET
1531 GNUNET_mutex_lock (lock);
1532 getData = httpSession->cs.server.gets;
1533 if (getData == NULL)
1534 {
1535 GNUNET_mutex_unlock (lock);
1536 return GNUNET_SYSERR;
1537 }
1538 if (getData->wsize == 0)
1539 GNUNET_array_grow (getData->wbuff, getData->wsize, HTTP_BUF_SIZE);
1540 size += sizeof (GNUNET_MessageHeader);
1541 if (getData->wpos + size > getData->wsize)
1542 {
1543 /* need to grow or discard */
1544 if (!important)
1545 {
1546 GNUNET_mutex_unlock (lock);
1547 return GNUNET_NO;
1548 }
1549 tmp = GNUNET_malloc (getData->wpos + size);
1550 memcpy (tmp, &getData->wbuff[getData->woff], getData->wpos);
1551 hdr = (GNUNET_MessageHeader *) & tmp[getData->wpos];
1552 hdr->type = htons (0);
1553 hdr->size = htons (size);
1554 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1555 GNUNET_free (getData->wbuff);
1556 getData->wbuff = tmp;
1557 getData->wsize = getData->wpos + size;
1558 getData->woff = 0;
1559 getData->wpos = getData->wpos + size;
1560 }
1561 else
1562 {
1563 /* fits without growing */
1564 if (getData->wpos + getData->woff + size > getData->wsize)
1565 {
1566 /* need to compact first */
1567 memmove (getData->wbuff,
1568 &getData->wbuff[getData->woff], getData->wpos);
1569 getData->woff = 0;
1570 }
1571 /* append */
1572 hdr =
1573 (GNUNET_MessageHeader *) & getData->wbuff[getData->woff +
1574 getData->wpos];
1575 hdr->size = htons (size);
1576 hdr->type = htons (0);
1577 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1578 getData->wpos += size;
1579 }
1580 signal_select ();
1581 GNUNET_mutex_unlock (lock);
1582#endif
1583 return GNUNET_OK;
1584}
1585
1586/**
1587 * Function called to cleanup dead connections
1588 * (completed PUTs, GETs that have timed out,
1589 * etc.). Also re-vives GETs that have timed out
1590 * if we are still interested in the connection.
1591 */
1592static void
1593cleanup_connections ()
1594{
1595 int i;
1596 HTTPSession *s;
1597 struct HTTPPutData *prev;
1598 struct HTTPPutData *pos;
1599 struct MHDPutData *mpos;
1600 struct MHDPutData *mprev;
1601#if DO_GET
1602 struct MHD_Response *r;
1603 struct MHDGetData *gpos;
1604 struct MHDGetData *gnext;
1605#endif
1606 GNUNET_CronTime now;
1607
1608 GNUNET_mutex_lock (lock);
1609 now = GNUNET_get_time ();
1610 for (i = 0; i < tsessionCount; i++)
1611 {
1612 s = tsessions[i]->internal;
1613 if (s->is_client)
1614 {
1615 if ((s->cs.client.puts == NULL) && (s->users == 0)
1616#if DO_GET
1617 && (s->cs.client.last_get_activity + HTTP_TIMEOUT < now)
1618#endif
1619 )
1620 {
1621#if DO_GET
1622#if DEBUG_HTTP
1623 GNUNET_GE_LOG (coreAPI->ectx,
1624 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1625 GNUNET_GE_USER,
1626 "HTTP transport destroys old (%llu ms) unused client session\n",
1627 now - s->cs.client.last_get_activity);
1628#endif
1629#endif
1630 destroy_tsession (tsessions[i]);
1631 i--;
1632 continue;
1633 }
1634
1635 prev = NULL;
1636 pos = s->cs.client.puts;
1637 while (pos != NULL)
1638 {
1639 if (pos->last_activity + HTTP_TIMEOUT < now)
1640 pos->done = GNUNET_YES;
1641 if (pos->done)
1642 {
1643 if (prev == NULL)
1644 s->cs.client.puts = pos->next;
1645 else
1646 prev->next = pos->next;
1647 GNUNET_free (pos->msg);
1648 curl_multi_remove_handle (curl_multi, pos->curl_put);
1649 http_requests_pending--;
1650 signal_select ();
1651 curl_easy_cleanup (pos->curl_put);
1652 GNUNET_free (pos);
1653 if (prev == NULL)
1654 pos = s->cs.client.puts;
1655 else
1656 pos = prev->next;
1657 continue;
1658 }
1659 prev = pos;
1660 pos = pos->next;
1661 }
1662#if DO_GET
1663 if ((s->cs.client.last_get_activity + HTTP_TIMEOUT < now) &&
1664 ((s->users > 0) || (s->cs.client.puts != NULL)) &&
1665 ((s->cs.client.last_get_initiated + HTTP_GET_REFRESH > now) ||
1666 (s->cs.client.get == NULL)) &&
1667 ((s->cs.client.get == NULL) ||
1668 (s->cs.client.last_get_activity + HTTP_GET_REFRESH / 2 < now)))
1669 create_curl_get (s);
1670#endif
1671 }
1672 else
1673 {
1674 mpos = s->cs.server.puts;
1675 mprev = NULL;
1676 while (mpos != NULL)
1677 {
1678 if (mpos->last_activity == 0)
1679 {
1680 if (mprev == NULL)
1681 s->cs.server.puts = mpos->next;
1682 else
1683 mprev->next = mpos->next;
1684 GNUNET_array_grow (mpos->rbuff2, mpos->rsize2, 0);
1685 GNUNET_free (mpos);
1686 if (mprev == NULL)
1687 mpos = s->cs.server.puts;
1688 else
1689 mpos = mprev->next;
1690 continue;
1691 }
1692 mprev = mpos;
1693 mpos = mpos->next;
1694 }
1695
1696 /* ! s->is_client */
1697#if DO_GET
1698 gpos = s->cs.server.gets;
1699 while (gpos != NULL)
1700 {
1701 gnext = gpos->next;
1702 gpos->next = NULL;
1703 if ((gpos->last_get_activity + HTTP_TIMEOUT < now) ||
1704 (gpos != s->cs.server.gets))
1705 {
1706 if (gpos == s->cs.server.gets)
1707 s->cs.server.gets = NULL;
1708 r = gpos->get;
1709 gpos->get = NULL;
1710 MHD_destroy_response (r);
1711 }
1712 gpos = gnext;
1713 }
1714#endif
1715 if (
1716#if DO_GET
1717 (s->cs.server.gets == NULL) &&
1718#endif
1719 (s->is_mhd_active == 0) && (s->users == 0))
1720 {
1721#if DO_GET
1722#if DEBUG_HTTP
1723 GNUNET_GE_LOG (coreAPI->ectx,
1724 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1725 GNUNET_GE_USER,
1726 "HTTP transport destroys unused server session\n");
1727#endif
1728#endif
1729 destroy_tsession (tsessions[i]);
1730 i--;
1731 continue;
1732 }
1733 }
1734 }
1735 GNUNET_mutex_unlock (lock);
1736}
1737
1738/**
1739 * Thread that runs the CURL and MHD requests.
1740 */
1741static void *
1742curl_runner (void *unused)
1743{
1744 CURLMcode mret;
1745 fd_set rs;
1746 fd_set ws;
1747 fd_set es;
1748 int max;
1749 struct timeval tv;
1750 int running;
1751 unsigned long long timeout;
1752 long ms;
1753 int have_tv;
1754 char buf[128]; /* for reading from pipe */
1755 int ret;
1756
1757#if DEBUG_HTTP
1758 GNUNET_GE_LOG (coreAPI->ectx,
1759 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1760 "HTTP transport select thread started\n");
1761#endif
1762 while (GNUNET_YES == http_running)
1763 {
1764 max = 0;
1765 FD_ZERO (&rs);
1766 FD_ZERO (&ws);
1767 FD_ZERO (&es);
1768 GNUNET_mutex_lock (lock);
1769 mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1770 GNUNET_mutex_unlock (lock);
1771 if (mret != CURLM_OK)
1772 {
1773 GNUNET_GE_LOG (coreAPI->ectx,
1774 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1775 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1776 "curl_multi_fdset", __FILE__, __LINE__,
1777 curl_multi_strerror (mret));
1778 break;
1779 }
1780 if (mhd_daemon != NULL)
1781 MHD_get_fdset (mhd_daemon, &rs, &ws, &es, &max);
1782 timeout = 0;
1783 have_tv = MHD_NO;
1784 if (mhd_daemon != NULL)
1785 have_tv = MHD_get_timeout (mhd_daemon, &timeout);
1786 GNUNET_mutex_lock (lock);
1787 if ((CURLM_OK == curl_multi_timeout (curl_multi, &ms)) &&
1788 (ms != -1) && ((ms < timeout) || (have_tv == MHD_NO)))
1789 {
1790 timeout = ms;
1791 have_tv = MHD_YES;
1792 }
1793 GNUNET_mutex_unlock (lock);
1794 FD_SET (signal_pipe[0], &rs);
1795 if (max < signal_pipe[0])
1796 max = signal_pipe[0];
1797 tv.tv_sec = timeout / 1000;
1798 tv.tv_usec = (timeout % 1000) * 1000;
1799 if (stats != NULL)
1800 stats->change (stat_select_calls, 1);
1801 ret =
1802 SELECT (max + 1, &rs, &ws, &es, (have_tv == MHD_YES) ? &tv : NULL);
1803 if (ret == -1)
1804 {
1805 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
1806 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1807 GNUNET_GE_DEVELOPER, "select");
1808 }
1809 if (GNUNET_YES != http_running)
1810 break;
1811 running = 0;
1812 do
1813 {
1814 GNUNET_mutex_lock (lock);
1815 mret = curl_multi_perform (curl_multi, &running);
1816 GNUNET_mutex_unlock (lock);
1817 }
1818 while ((mret == CURLM_CALL_MULTI_PERFORM)
1819 && (http_running == GNUNET_YES));
1820 if (FD_ISSET (signal_pipe[0], &rs))
1821 read (signal_pipe[0], buf, sizeof (buf));
1822 if ((mret != CURLM_OK) && (mret != CURLM_CALL_MULTI_PERFORM))
1823 GNUNET_GE_LOG (coreAPI->ectx,
1824 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1825 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1826 "curl_multi_perform", __FILE__, __LINE__,
1827 curl_multi_strerror (mret));
1828 if (mhd_daemon != NULL)
1829 MHD_run (mhd_daemon);
1830 cleanup_connections ();
1831 }
1832#if DEBUG_HTTP
1833 GNUNET_GE_LOG (coreAPI->ectx,
1834 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1835 "HTTP transport select thread exits.\n");
1836#endif
1837 return NULL;
1838}
1839
1840
1841/**
1842 * Start the server process to receive inbound traffic.
1843 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1844 */
1845static int
1846startTransportServer ()
1847{
1848 unsigned short port;
1849
1850 if ((curl_multi != NULL) || (http_running == GNUNET_YES))
1851 return GNUNET_SYSERR;
1852 curl_multi = curl_multi_init ();
1853 if (curl_multi == NULL)
1854 return GNUNET_SYSERR;
1855 port = get_port ();
1856 if ((mhd_daemon == NULL) && (port != 0))
1857 {
1858 if (GNUNET_YES !=
1859 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1860 "DISABLE-IPV6",
1861 GNUNET_YES))
1862 {
1863 mhd_daemon = MHD_start_daemon (MHD_USE_IPv6,
1864 port,
1865 &acceptPolicyCallback,
1866 NULL, &accessHandlerCallback, NULL,
1867 MHD_OPTION_CONNECTION_TIMEOUT,
1868 (unsigned int) HTTP_TIMEOUT,
1869 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1870 (unsigned int) 1024 * 128,
1871 MHD_OPTION_CONNECTION_LIMIT,
1872 (unsigned int) 128,
1873 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1874 (unsigned int) 8,
1875 MHD_OPTION_NOTIFY_COMPLETED,
1876 &requestCompletedCallback, NULL,
1877 MHD_OPTION_END);
1878 }
1879 if (mhd_daemon == NULL)
1880 {
1881 /* try without IPv6 */
1882 mhd_daemon = MHD_start_daemon (MHD_NO_FLAG,
1883 port,
1884 &acceptPolicyCallback,
1885 NULL, &accessHandlerCallback, NULL,
1886 MHD_OPTION_CONNECTION_TIMEOUT,
1887 (unsigned int) HTTP_TIMEOUT,
1888 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1889 (unsigned int) 1024 * 128,
1890 MHD_OPTION_CONNECTION_LIMIT,
1891 (unsigned int) 128,
1892 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1893 (unsigned int) 8,
1894 MHD_OPTION_NOTIFY_COMPLETED,
1895 &requestCompletedCallback, NULL,
1896 MHD_OPTION_END);
1897 }
1898 else
1899 {
1900 available_protocols |= VERSION_AVAILABLE_IPV6;
1901 }
1902 if (mhd_daemon != NULL)
1903 available_protocols |= VERSION_AVAILABLE_IPV4;
1904 }
1905 if (port == 0)
1906 {
1907 /* NAT */
1908 available_protocols |= VERSION_AVAILABLE_IPV4;
1909 if (GNUNET_YES !=
1910 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1911 "DISABLE-IPV6",
1912 GNUNET_YES))
1913 available_protocols |= VERSION_AVAILABLE_IPV6;
1914 }
1915 if (0 != PIPE (signal_pipe))
1916 {
1917 MHD_stop_daemon (mhd_daemon);
1918 curl_multi_cleanup (curl_multi);
1919 curl_multi = NULL;
1920 mhd_daemon = NULL;
1921 return GNUNET_SYSERR;
1922 }
1923 GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[0]);
1924 GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[1]);
1925 http_running = GNUNET_YES;
1926 curl_thread = GNUNET_thread_create (&curl_runner, NULL, 32 * 1024);
1927 if (curl_thread == NULL)
1928 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
1929 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
1930 GNUNET_GE_IMMEDIATE, "pthread_create");
1931 return GNUNET_OK;
1932}
1933
1934/**
1935 * Shutdown the server process (stop receiving inbound
1936 * traffic). May be restarted later!
1937 */
1938static int
1939stopTransportServer ()
1940{
1941 void *unused;
1942 int i;
1943 HTTPSession *s;
1944
1945 if ((http_running == GNUNET_NO) || (curl_multi == NULL))
1946 return GNUNET_SYSERR;
1947 http_running = GNUNET_NO;
1948 signal_select ();
1949 GNUNET_thread_stop_sleep (curl_thread);
1950 GNUNET_thread_join (curl_thread, &unused);
1951 CLOSE (signal_pipe[0]);
1952 CLOSE (signal_pipe[1]);
1953 if (mhd_daemon != NULL)
1954 {
1955 MHD_stop_daemon (mhd_daemon);
1956 mhd_daemon = NULL;
1957 }
1958 cleanup_connections ();
1959 for (i = 0; i < tsessionCount; i++)
1960 {
1961 s = tsessions[i]->internal;
1962 if (s->users == 0)
1963 {
1964 destroy_tsession (tsessions[i]);
1965 i--;
1966 }
1967 }
1968 curl_multi_cleanup (curl_multi);
1969 curl_multi = NULL;
1970 return GNUNET_OK;
1971}
1972
1973/* ******************** public API ******************** */
1974
1975/**
1976 * The exported method. Makes the core api available
1977 * via a global and returns the udp transport API.
1978 */
1979GNUNET_TransportAPI *
1980inittransport_http (GNUNET_CoreAPIForTransport * core)
1981{
1982 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
1983 coreAPI = core;
1984 cfg = coreAPI->cfg;
1985 lock = GNUNET_mutex_create (GNUNET_YES);
1986 if (0 != GNUNET_GC_attach_change_listener (coreAPI->cfg,
1987 &reload_configuration, NULL))
1988 {
1989 GNUNET_mutex_destroy (lock);
1990 lock = NULL;
1991 return NULL;
1992 }
1993 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1994 {
1995 GNUNET_GE_BREAK (NULL, 0);
1996 GNUNET_GC_detach_change_listener (coreAPI->cfg, &reload_configuration,
1997 NULL);
1998 GNUNET_mutex_destroy (lock);
1999 lock = NULL;
2000 return NULL;
2001 }
2002 tsessionCount = 0;
2003 tsessionArrayLength = 0;
2004 GNUNET_array_grow (tsessions, tsessionArrayLength, 32);
2005 if (GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg,
2006 "HTTP", "UPNP",
2007 GNUNET_YES) == GNUNET_YES)
2008 {
2009 upnp = coreAPI->service_request ("upnp");
2010
2011 if (upnp == NULL)
2012 {
2013 GNUNET_GE_LOG (coreAPI->ectx,
2014 GNUNET_GE_ERROR | GNUNET_GE_USER |
2015 GNUNET_GE_IMMEDIATE,
2016 _
2017 ("The UPnP service could not be loaded. To disable UPnP, set the "
2018 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n"),
2019 "HTTP");
2020 }
2021 }
2022 stats = coreAPI->service_request ("stats");
2023 if (stats != NULL)
2024 {
2025 stat_bytesReceived
2026 = stats->create (gettext_noop ("# bytes received via HTTP"));
2027 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via HTTP"));
2028 stat_bytesDropped
2029 = stats->create (gettext_noop ("# bytes dropped by HTTP (outgoing)"));
2030 stat_get_issued = stats->create (gettext_noop ("# HTTP GET issued"));
2031 stat_get_received
2032 = stats->create (gettext_noop ("# HTTP GET received"));
2033 stat_put_issued = stats->create (gettext_noop ("# HTTP PUT issued"));
2034 stat_put_received
2035 = stats->create (gettext_noop ("# HTTP PUT received"));
2036 stat_select_calls
2037 = stats->create (gettext_noop ("# HTTP select calls"));
2038
2039 stat_send_calls = stats->create (gettext_noop ("# HTTP send calls"));
2040
2041 stat_curl_send_callbacks
2042 = stats->create (gettext_noop ("# HTTP curl send callbacks"));
2043 stat_curl_receive_callbacks
2044 = stats->create (gettext_noop ("# HTTP curl receive callbacks"));
2045 stat_mhd_access_callbacks
2046 = stats->create (gettext_noop ("# HTTP mhd access callbacks"));
2047 stat_mhd_read_callbacks
2048 = stats->create (gettext_noop ("# HTTP mhd read callbacks"));
2049 stat_mhd_close_callbacks
2050 = stats->create (gettext_noop ("# HTTP mhd close callbacks"));
2051 stat_connect_calls
2052 = stats->create (gettext_noop ("# HTTP connect calls"));
2053 }
2054 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
2055 "GNUNETD", "HTTP-PROXY", "",
2056 &proxy);
2057
2058 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
2059 myAPI.mtu = 0;
2060 myAPI.cost = 20000; /* about equal to udp */
2061 myAPI.hello_verify = &verify_hello;
2062 myAPI.hello_create = &create_hello;
2063 myAPI.connect = &httpConnect;
2064 myAPI.associate = &httpAssociate;
2065 myAPI.send = &httpSend;
2066 myAPI.disconnect = &httpDisconnect;
2067 myAPI.server_start = &startTransportServer;
2068 myAPI.server_stop = &stopTransportServer;
2069 myAPI.hello_to_address = &hello_to_address;
2070 myAPI.send_now_test = &httpTestWouldTry;
2071
2072 return &myAPI;
2073}
2074
2075void
2076donetransport_http ()
2077{
2078 curl_global_cleanup ();
2079 GNUNET_free_non_null (proxy);
2080 proxy = NULL;
2081 GNUNET_array_grow (tsessions, tsessionArrayLength, 0);
2082 do_shutdown ();
2083}
2084
2085/* end of http.c */
diff --git a/src/transport/plugin_transport_smtp.c b/src/transport/plugin_transport_smtp.c
new file mode 100644
index 000000000..f7cc530e4
--- /dev/null
+++ b/src/transport/plugin_transport_smtp.c
@@ -0,0 +1,906 @@
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/**
22 * @file transports/smtp.c
23 * @brief Implementation of the SMTP transport service
24 * @author Christian Grothoff
25 * @author Renaldo Ferreira
26 */
27
28#include "platform.h"
29#include "gnunet_util.h"
30#include "gnunet_directories.h"
31#include "gnunet_protocols.h"
32#include "gnunet_transport.h"
33#include "gnunet_stats_service.h"
34#include <libesmtp.h>
35#include <signal.h>
36
37
38/**
39 * The default maximum size of each outbound SMTP message.
40 */
41#define SMTP_MESSAGE_SIZE 65528
42
43#define DEBUG_SMTP GNUNET_NO
44
45#define FILTER_STRING_SIZE 64
46
47/* how long can a line in base64 encoded
48 mime text be? (in characters, excluding "\n") */
49#define MAX_CHAR_PER_LINE 76
50
51#define EBUF_LEN 128
52
53/**
54 * Host-Address in a SMTP network.
55 */
56typedef struct
57{
58
59 /**
60 * Filter line that every sender must include in the E-mails such
61 * that the receiver can effectively filter out the GNUnet traffic
62 * from the E-mail.
63 */
64 char filter[FILTER_STRING_SIZE];
65
66 /**
67 * Claimed E-mail address of the sender.
68 * Format is "foo@bar.com" with null termination, padded to be
69 * of a multiple of 8 bytes long.
70 */
71 char senderAddress[0];
72
73} EmailAddress;
74
75/**
76 * Encapsulation of a GNUnet message in the SMTP mail body (before
77 * base64 encoding).
78 */
79typedef struct
80{
81 GNUNET_MessageHeader header;
82
83 /**
84 * What is the identity of the sender (GNUNET_hash of public key)
85 */
86 GNUNET_PeerIdentity sender;
87
88} SMTPMessage;
89
90/* *********** globals ************* */
91
92/**
93 * apis (our advertised API and the core api )
94 */
95static GNUNET_CoreAPIForTransport *coreAPI;
96
97static struct GNUNET_GE_Context *ectx;
98
99/**
100 * Thread that listens for inbound messages
101 */
102static struct GNUNET_ThreadHandle *dispatchThread;
103
104/**
105 * Flag to indicate that server has been shut down.
106 */
107static int smtp_shutdown = GNUNET_YES;
108
109/**
110 * Set to the SMTP server hostname (and port) for outgoing messages.
111 */
112static char *smtp_server_name;
113
114static char *pipename;
115
116/**
117 * Lock for uses of libesmtp (not thread-safe).
118 */
119static struct GNUNET_Mutex *lock;
120
121/**
122 * Old handler for SIGPIPE (kept to be able to restore).
123 */
124static struct sigaction old_handler;
125
126static char *email;
127
128static GNUNET_TransportAPI smtpAPI;
129
130static GNUNET_Stats_ServiceAPI *stats;
131
132static int stat_bytesReceived;
133
134static int stat_bytesSent;
135
136static int stat_bytesDropped;
137
138/**
139 * How many e-mails are we allowed to send per hour?
140 */
141static unsigned long long rate_limit;
142
143static GNUNET_CronTime last_transmission;
144
145/** ******************** Base64 encoding ***********/
146
147#define FILLCHAR '='
148static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
149 "abcdefghijklmnopqrstuvwxyz" "0123456789+/";
150
151/**
152 * Encode into Base64.
153 *
154 * @param data the data to encode
155 * @param len the length of the input
156 * @param output where to write the output (*output should be NULL,
157 * is allocated)
158 * @return the size of the output
159 */
160static unsigned int
161base64_encode (const char *data, unsigned int len, char **output)
162{
163 unsigned int i;
164 char c;
165 unsigned int ret;
166 char *opt;
167
168/* (*output)[ret++] = '\r'; \*/
169#define CHECKLINE \
170 if ( (ret % MAX_CHAR_PER_LINE) == 0) { \
171 (*output)[ret++] = '\n'; \
172 }
173 ret = 0;
174 opt = GNUNET_malloc (2 + (((len * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2)) /
175 MAX_CHAR_PER_LINE);
176 /* message must start with \r\n for libesmtp */
177 *output = opt;
178 opt[0] = '\r';
179 opt[1] = '\n';
180 ret += 2;
181 for (i = 0; i < len; ++i)
182 {
183 c = (data[i] >> 2) & 0x3f;
184 opt[ret++] = cvt[(int) c];
185 CHECKLINE;
186 c = (data[i] << 4) & 0x3f;
187 if (++i < len)
188 c |= (data[i] >> 4) & 0x0f;
189 opt[ret++] = cvt[(int) c];
190 CHECKLINE;
191 if (i < len)
192 {
193 c = (data[i] << 2) & 0x3f;
194 if (++i < len)
195 c |= (data[i] >> 6) & 0x03;
196 opt[ret++] = cvt[(int) c];
197 CHECKLINE;
198 }
199 else
200 {
201 ++i;
202 opt[ret++] = FILLCHAR;
203 CHECKLINE;
204 }
205 if (i < len)
206 {
207 c = data[i] & 0x3f;
208 opt[ret++] = cvt[(int) c];
209 CHECKLINE;
210 }
211 else
212 {
213 opt[ret++] = FILLCHAR;
214 CHECKLINE;
215 }
216 }
217 opt[ret++] = FILLCHAR;
218 return ret;
219}
220
221#define cvtfind(a)( (((a) >= 'A')&&((a) <= 'Z'))? (a)-'A'\
222 :(((a)>='a')&&((a)<='z')) ? (a)-'a'+26\
223 :(((a)>='0')&&((a)<='9')) ? (a)-'0'+52\
224 :((a) == '+') ? 62\
225 :((a) == '/') ? 63 : -1)
226/**
227 * Decode from Base64.
228 *
229 * @param data the data to encode
230 * @param len the length of the input
231 * @param output where to write the output (*output should be NULL,
232 * is allocated)
233 * @return the size of the output
234 */
235static unsigned int
236base64_decode (const char *data, unsigned int len, char **output)
237{
238 unsigned int i;
239 char c;
240 char c1;
241 unsigned int ret = 0;
242
243#define CHECK_CRLF while (data[i] == '\r' || data[i] == '\n') {\
244 GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, "ignoring CR/LF\n"); \
245 i++; \
246 if (i >= len) goto END; \
247 }
248
249 *output = GNUNET_malloc ((len * 3 / 4) + 8);
250#if DEBUG_SMTP
251 GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
252 "base64_decode decoding len=%d\n", len);
253#endif
254 for (i = 0; i < len; ++i)
255 {
256 CHECK_CRLF;
257 if (data[i] == FILLCHAR)
258 break;
259 c = (char) cvtfind (data[i]);
260 ++i;
261 CHECK_CRLF;
262 c1 = (char) cvtfind (data[i]);
263 c = (c << 2) | ((c1 >> 4) & 0x3);
264 (*output)[ret++] = c;
265 if (++i < len)
266 {
267 CHECK_CRLF;
268 c = data[i];
269 if (FILLCHAR == c)
270 break;
271 c = (char) cvtfind (c);
272 c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
273 (*output)[ret++] = c1;
274 }
275 if (++i < len)
276 {
277 CHECK_CRLF;
278 c1 = data[i];
279 if (FILLCHAR == c1)
280 break;
281
282 c1 = (char) cvtfind (c1);
283 c = ((c << 6) & 0xc0) | c1;
284 (*output)[ret++] = c;
285 }
286 }
287END:
288 return ret;
289}
290
291/* ********************* the real stuff ******************* */
292
293#define strAUTOncmp(a,b) strncmp(a,b,strlen(b))
294
295/**
296 * Listen to the pipe, decode messages and send to core.
297 */
298static void *
299listenAndDistribute (void *unused)
300{
301 char *line;
302 unsigned int linesize;
303 SMTPMessage *mp;
304 FILE *fdes;
305 char *retl;
306 char *out;
307 unsigned int size;
308 GNUNET_TransportPacket *coreMP;
309 int fd;
310 unsigned int pos;
311
312 linesize = ((GNUNET_MAX_BUFFER_SIZE * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2) / MAX_CHAR_PER_LINE; /* maximum size of a line supported */
313 line = GNUNET_malloc (linesize + 2); /* 2 bytes for off-by-one errors, just to be safe... */
314
315#define READLINE(l,limit) \
316 do { retl = fgets(l, (limit), fdes); \
317 if ( (retl == NULL) || (smtp_shutdown == GNUNET_YES)) {\
318 goto END; \
319 }\
320 if (coreAPI->load_monitor != NULL) \
321 GNUNET_network_monitor_notify_transmission(coreAPI->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \
322 } while (0)
323
324
325 while (smtp_shutdown == GNUNET_NO)
326 {
327 fd = OPEN (pipename, O_RDONLY | O_ASYNC);
328 if (fd == -1)
329 {
330 if (smtp_shutdown == GNUNET_NO)
331 GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS);
332 continue;
333 }
334 fdes = fdopen (fd, "r");
335 while (smtp_shutdown == GNUNET_NO)
336 {
337 /* skip until end of header */
338 do
339 {
340 READLINE (line, linesize);
341 }
342 while ((line[0] != '\r') && (line[0] != '\n')); /* expect newline */
343 READLINE (line, linesize); /* read base64 encoded message; decode, process */
344 pos = 0;
345 while (1)
346 {
347 pos = strlen (line) - 1; /* ignore new line */
348 READLINE (&line[pos], linesize - pos); /* read base64 encoded message; decode, process */
349 if ((line[pos] == '\r') || (line[pos] == '\n'))
350 break; /* empty line => end of message! */
351 }
352 size = base64_decode (line, pos, &out);
353 if (size < sizeof (SMTPMessage))
354 {
355 GNUNET_GE_BREAK (ectx, 0);
356 GNUNET_free (out);
357 goto END;
358 }
359
360 mp = (SMTPMessage *) & out[size - sizeof (SMTPMessage)];
361 if (ntohs (mp->header.size) != size)
362 {
363 GNUNET_GE_LOG (ectx,
364 GNUNET_GE_WARNING | GNUNET_GE_BULK |
365 GNUNET_GE_USER,
366 _
367 ("Received malformed message via %s. Ignored.\n"),
368 "SMTP");
369#if DEBUG_SMTP
370 GNUNET_GE_LOG (ectx,
371 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
372 GNUNET_GE_USER,
373 "Size returned by base64=%d, in the msg=%d.\n",
374 size, ntohl (mp->size));
375#endif
376 GNUNET_free (out);
377 goto END;
378 }
379 if (stats != NULL)
380 stats->change (stat_bytesReceived, size);
381 coreMP = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
382 coreMP->msg = out;
383 coreMP->size = size - sizeof (SMTPMessage);
384 coreMP->tsession = NULL;
385 coreMP->sender = mp->sender;
386#if DEBUG_SMTP
387 GNUNET_GE_LOG (ectx,
388 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
389 "SMTP message passed to the core.\n");
390#endif
391
392 coreAPI->receive (coreMP);
393 }
394 END:
395#if DEBUG_SMTP
396 GNUNET_GE_LOG (ectx,
397 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
398 "SMTP message processed.\n");
399#endif
400 if (fdes != NULL)
401 fclose (fdes);
402 }
403 GNUNET_free (line);
404 return NULL;
405}
406
407/* *************** API implementation *************** */
408
409/**
410 * Verify that a hello-Message is correct (a node is reachable at that
411 * address). Since the reply will be asynchronous, a method must be
412 * called on success.
413 *
414 * @param hello the hello message to verify
415 * (the signature/crc have been verified before)
416 * @return GNUNET_OK on success, GNUNET_SYSERR on error
417 */
418static int
419api_verify_hello (const GNUNET_MessageHello * hello)
420{
421 const EmailAddress *maddr;
422
423 maddr = (const EmailAddress *) &hello[1];
424 if ((ntohs (hello->header.size) !=
425 sizeof (GNUNET_MessageHello) + ntohs (hello->senderAddressSize)) ||
426 (maddr->senderAddress[ntohs (hello->senderAddressSize) - 1 -
427 FILTER_STRING_SIZE] != '\0'))
428 {
429 GNUNET_GE_BREAK (ectx, 0);
430 return GNUNET_SYSERR; /* obviously invalid */
431 }
432 if (NULL == strstr (maddr->filter, ": "))
433 return GNUNET_SYSERR;
434 return GNUNET_OK;
435}
436
437/**
438 * Create a hello-Message for the current node. The hello is created
439 * without signature and without a timestamp. The GNUnet core will
440 * GNUNET_RSA_sign the message and add an expiration time.
441 *
442 * @return hello on success, NULL on error
443 */
444static GNUNET_MessageHello *
445api_create_hello ()
446{
447 GNUNET_MessageHello *msg;
448 char *filter;
449 EmailAddress *haddr;
450 int i;
451
452 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
453 "SMTP", "FILTER",
454 "X-mailer: GNUnet", &filter);
455 if (NULL == strstr (filter, ": "))
456 {
457 GNUNET_GE_LOG (ectx,
458 GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
459 _("SMTP filter string to invalid, lacks ': '\n"));
460 GNUNET_free (filter);
461 return NULL;
462 }
463
464 if (strlen (filter) > FILTER_STRING_SIZE)
465 {
466 filter[FILTER_STRING_SIZE] = '\0';
467 GNUNET_GE_LOG (ectx,
468 GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
469 _("SMTP filter string to long, capped to `%s'\n"),
470 filter);
471 }
472 i = (strlen (email) + 8) & (~7); /* make multiple of 8 */
473 msg =
474 GNUNET_malloc (sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i);
475 memset (msg, 0, sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i);
476 haddr = (EmailAddress *) & msg[1];
477 memset (&haddr->filter[0], 0, FILTER_STRING_SIZE);
478 strcpy (&haddr->filter[0], filter);
479 memcpy (&haddr->senderAddress[0], email, strlen (email) + 1);
480 msg->senderAddressSize = htons (strlen (email) + 1 + sizeof (EmailAddress));
481 msg->protocol = htons (GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP);
482 msg->MTU = htonl (smtpAPI.mtu);
483 msg->header.size = htons (GNUNET_sizeof_hello (msg));
484 if (api_verify_hello (msg) == GNUNET_SYSERR)
485 GNUNET_GE_ASSERT (ectx, 0);
486 GNUNET_free (filter);
487 return msg;
488}
489
490struct GetMessageClosure
491{
492 unsigned int esize;
493 unsigned int pos;
494 char *ebody;
495};
496
497static const char *
498get_message (void **buf, int *len, void *cls)
499{
500 struct GetMessageClosure *gmc = cls;
501
502 *buf = NULL;
503 if (len == NULL)
504 {
505 gmc->pos = 0;
506 return NULL;
507 }
508 if (gmc->pos == gmc->esize)
509 return NULL; /* done */
510 *len = gmc->esize;
511 gmc->pos = gmc->esize;
512 return gmc->ebody;
513}
514
515/**
516 * Send a message to the specified remote node.
517 *
518 * @param tsession the GNUNET_MessageHello identifying the remote node
519 * @param message what to send
520 * @param size the size of the message
521 * @return GNUNET_SYSERR on error, GNUNET_OK on success
522 */
523static int
524api_send (GNUNET_TSession * tsession,
525 const void *msg, const unsigned int size, int important)
526{
527 const GNUNET_MessageHello *hello;
528 const EmailAddress *haddr;
529 char *m;
530 char *filter;
531 char *fvalue;
532 SMTPMessage *mp;
533 struct GetMessageClosure gm_cls;
534 smtp_session_t session;
535 smtp_message_t message;
536 smtp_recipient_t recipient;
537#define EBUF_LEN 128
538 char ebuf[EBUF_LEN];
539 GNUNET_CronTime now;
540
541 if (smtp_shutdown == GNUNET_YES)
542 return GNUNET_SYSERR;
543 if ((size == 0) || (size > smtpAPI.mtu))
544 {
545 GNUNET_GE_BREAK (ectx, 0);
546 return GNUNET_SYSERR;
547 }
548 now = GNUNET_get_time ();
549 if ((important != GNUNET_YES) &&
550 ((now - last_transmission) * rate_limit) < GNUNET_CRON_HOURS)
551 return GNUNET_NO; /* rate too high */
552 last_transmission = now;
553
554 hello = (const GNUNET_MessageHello *) tsession->internal;
555 if (hello == NULL)
556 return GNUNET_SYSERR;
557 GNUNET_mutex_lock (lock);
558 session = smtp_create_session ();
559 if (session == NULL)
560 {
561 GNUNET_GE_LOG (ectx,
562 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
563 GNUNET_GE_IMMEDIATE,
564 _("SMTP: `%s' failed: %s.\n"),
565 "smtp_create_session",
566 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
567 GNUNET_mutex_unlock (lock);
568 return GNUNET_SYSERR;
569 }
570 if (0 == smtp_set_server (session, smtp_server_name))
571 {
572 GNUNET_GE_LOG (ectx,
573 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
574 GNUNET_GE_IMMEDIATE,
575 _("SMTP: `%s' failed: %s.\n"),
576 "smtp_set_server",
577 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
578 smtp_destroy_session (session);
579 GNUNET_mutex_unlock (lock);
580 return GNUNET_SYSERR;
581 }
582 haddr = (const EmailAddress *) &hello[1];
583 message = smtp_add_message (session);
584 if (message == NULL)
585 {
586 GNUNET_GE_LOG (ectx,
587 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
588 GNUNET_GE_BULK,
589 _("SMTP: `%s' failed: %s.\n"),
590 "smtp_add_message",
591 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
592 smtp_destroy_session (session);
593 GNUNET_mutex_unlock (lock);
594 return GNUNET_SYSERR;
595 }
596 smtp_set_header (message, "To", NULL, haddr->senderAddress);
597 smtp_set_header (message, "From", NULL, email);
598
599 filter = GNUNET_strdup (haddr->filter);
600 fvalue = strstr (filter, ": ");
601 GNUNET_GE_ASSERT (NULL, NULL != fvalue);
602 fvalue[0] = '\0';
603 fvalue += 2;
604 if (0 == smtp_set_header (message, filter, fvalue))
605 {
606 GNUNET_GE_LOG (ectx,
607 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
608 GNUNET_GE_BULK,
609 _("SMTP: `%s' failed: %s.\n"),
610 "smtp_set_header",
611 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
612 smtp_destroy_session (session);
613 GNUNET_mutex_unlock (lock);
614 GNUNET_free (filter);
615 return GNUNET_SYSERR;
616 }
617 GNUNET_free (filter);
618 m = GNUNET_malloc (size + sizeof (SMTPMessage));
619 memcpy (m, msg, size);
620 mp = (SMTPMessage *) & m[size];
621 mp->header.size = htons (size + sizeof (SMTPMessage));
622 mp->header.type = htons (0);
623 mp->sender = *coreAPI->my_identity;
624 gm_cls.ebody = NULL;
625 gm_cls.pos = 0;
626 gm_cls.esize =
627 base64_encode (m, size + sizeof (SMTPMessage), &gm_cls.ebody);
628 GNUNET_free (m);
629 if (0 == smtp_size_set_estimate (message, gm_cls.esize))
630 {
631 GNUNET_GE_LOG (ectx,
632 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
633 GNUNET_GE_BULK,
634 _("SMTP: `%s' failed: %s.\n"),
635 "smtp_size_set_estimate",
636 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
637 }
638 if (0 == smtp_set_messagecb (message, &get_message, &gm_cls))
639 {
640 GNUNET_GE_LOG (ectx,
641 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
642 GNUNET_GE_BULK,
643 _("SMTP: `%s' failed: %s.\n"),
644 "smtp_set_messagecb",
645 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
646 smtp_destroy_session (session);
647 GNUNET_mutex_unlock (lock);
648 GNUNET_free (gm_cls.ebody);
649 return GNUNET_SYSERR;
650 }
651 recipient = smtp_add_recipient (message, haddr->senderAddress);
652 if (recipient == NULL)
653 {
654 GNUNET_GE_LOG (ectx,
655 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
656 GNUNET_GE_BULK,
657 _("SMTP: `%s' failed: %s.\n"),
658 "smtp_add_recipient",
659 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
660 smtp_destroy_session (session);
661 GNUNET_mutex_unlock (lock);
662 return GNUNET_SYSERR;
663 }
664 if (0 == smtp_start_session (session))
665 {
666 GNUNET_GE_LOG (ectx,
667 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
668 GNUNET_GE_BULK,
669 _("SMTP: `%s' failed: %s.\n"),
670 "smtp_start_session",
671 smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
672 smtp_destroy_session (session);
673 GNUNET_mutex_unlock (lock);
674 GNUNET_free (gm_cls.ebody);
675 return GNUNET_SYSERR;
676 }
677 if (stats != NULL)
678 stats->change (stat_bytesSent, size);
679 if (coreAPI->load_monitor != NULL)
680 GNUNET_network_monitor_notify_transmission (coreAPI->load_monitor,
681 GNUNET_ND_UPLOAD,
682 gm_cls.esize);
683 smtp_message_reset_status (message); /* this is needed to plug a 28-byte/message memory leak in libesmtp */
684 smtp_destroy_session (session);
685 GNUNET_mutex_unlock (lock);
686 GNUNET_free (gm_cls.ebody);
687 return GNUNET_OK;
688}
689
690/**
691 * Establish a connection to a remote node.
692 * @param helo the hello-Message for the target node
693 * @param tsessionPtr the session handle that is to be set
694 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
695 */
696static int
697api_connect (const GNUNET_MessageHello * hello,
698 GNUNET_TSession ** tsessionPtr, int may_reuse)
699{
700 GNUNET_TSession *tsession;
701
702 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
703 tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello));
704 tsession->peer = hello->senderIdentity;
705 memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello));
706 tsession->ttype = smtpAPI.protocol_number;
707 (*tsessionPtr) = tsession;
708 return GNUNET_OK;
709}
710
711/**
712 * Disconnect from a remote node.
713 *
714 * @param tsession the session that is closed
715 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
716 */
717static int
718api_disconnect (GNUNET_TSession * tsession)
719{
720 if (tsession != NULL)
721 {
722 if (tsession->internal != NULL)
723 GNUNET_free (tsession->internal);
724 GNUNET_free (tsession);
725 }
726 return GNUNET_OK;
727}
728
729/**
730 * Start the server process to receive inbound traffic.
731 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
732 */
733static int
734api_start_transport_server ()
735{
736 smtp_shutdown = GNUNET_NO;
737 /* initialize SMTP network */
738 dispatchThread =
739 GNUNET_thread_create (&listenAndDistribute, NULL, 1024 * 4);
740 if (dispatchThread == NULL)
741 {
742 GNUNET_GE_DIE_STRERROR (ectx,
743 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
744 GNUNET_GE_FATAL, "pthread_create");
745 return GNUNET_SYSERR;
746 }
747 return GNUNET_OK;
748}
749
750/**
751 * Shutdown the server process (stop receiving inbound traffic). Maybe
752 * restarted later!
753 */
754static int
755api_stop_transport_server ()
756{
757 void *unused;
758
759 smtp_shutdown = GNUNET_YES;
760 GNUNET_thread_stop_sleep (dispatchThread);
761 GNUNET_thread_join (dispatchThread, &unused);
762 return GNUNET_OK;
763}
764
765/**
766 * Convert SMTP hello to an IP address (always fails).
767 */
768static int
769api_hello_to_address (const GNUNET_MessageHello * hello,
770 void **sa, unsigned int *sa_len)
771{
772 return GNUNET_SYSERR;
773}
774
775/**
776 * Always fails.
777 */
778static int
779api_associate (GNUNET_TSession * tsession)
780{
781 return GNUNET_SYSERR; /* SMTP connections can never be associated */
782}
783
784/**
785 * Always succeeds (for now; we should look at adding
786 * frequency limits to SMTP in the future!).
787 */
788static int
789api_test_would_try (GNUNET_TSession * tsession, const unsigned int size,
790 int important)
791{
792 return GNUNET_OK; /* we always try... */
793}
794
795/**
796 * The exported method. Makes the core api available via a global and
797 * returns the smtp transport API.
798 */
799GNUNET_TransportAPI *
800inittransport_smtp (GNUNET_CoreAPIForTransport * core)
801{
802
803
804 unsigned long long mtu;
805 struct sigaction sa;
806
807 coreAPI = core;
808 ectx = core->ectx;
809 if (!GNUNET_GC_have_configuration_value (coreAPI->cfg, "SMTP", "EMAIL"))
810 {
811 GNUNET_GE_LOG (ectx,
812 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
813 _
814 ("No email-address specified, can not start SMTP transport.\n"));
815 return NULL;
816 }
817 GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
818 "SMTP",
819 "MTU",
820 1200,
821 SMTP_MESSAGE_SIZE,
822 SMTP_MESSAGE_SIZE, &mtu);
823 GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
824 "SMTP",
825 "RATELIMIT",
826 0, 0, 1024 * 1024, &rate_limit);
827 stats = coreAPI->service_request ("stats");
828 if (stats != NULL)
829 {
830 stat_bytesReceived
831 = stats->create (gettext_noop ("# bytes received via SMTP"));
832 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via SMTP"));
833 stat_bytesDropped
834 = stats->create (gettext_noop ("# bytes dropped by SMTP (outgoing)"));
835 }
836 GNUNET_GC_get_configuration_value_filename (coreAPI->cfg,
837 "SMTP",
838 "PIPE",
839 GNUNET_DEFAULT_DAEMON_VAR_DIRECTORY
840 "/smtp-pipe", &pipename);
841 UNLINK (pipename);
842 if (0 != mkfifo (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
843 {
844 GNUNET_GE_LOG_STRERROR (ectx,
845 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
846 GNUNET_GE_FATAL, "mkfifo");
847 GNUNET_free (pipename);
848 coreAPI->service_release (stats);
849 stats = NULL;
850 return NULL;
851 }
852 /* we need to allow the mailer program to send us messages;
853 easiest done by giving it write permissions (see Mantis #1142) */
854 if (0 != chmod (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
855 GNUNET_GE_LOG_STRERROR (ectx,
856 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
857 GNUNET_GE_WARNING, "chmod");
858 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
859 "SMTP", "EMAIL", NULL, &email);
860 lock = GNUNET_mutex_create (GNUNET_NO);
861 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
862 "SMTP",
863 "SERVER",
864 "localhost:25",
865 &smtp_server_name);
866 sa.sa_handler = SIG_IGN;
867 sigemptyset (&sa.sa_mask);
868 sa.sa_flags = 0;
869 sigaction (SIGPIPE, &sa, &old_handler);
870
871 smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP;
872 smtpAPI.mtu = mtu - sizeof (SMTPMessage);
873 smtpAPI.cost = 50;
874 smtpAPI.hello_verify = &api_verify_hello;
875 smtpAPI.hello_create = &api_create_hello;
876 smtpAPI.connect = &api_connect;
877 smtpAPI.send = &api_send;
878 smtpAPI.associate = &api_associate;
879 smtpAPI.disconnect = &api_disconnect;
880 smtpAPI.server_start = &api_start_transport_server;
881 smtpAPI.server_stop = &api_stop_transport_server;
882 smtpAPI.hello_to_address = &api_hello_to_address;
883 smtpAPI.send_now_test = &api_test_would_try;
884 return &smtpAPI;
885}
886
887void
888donetransport_smtp ()
889{
890 sigaction (SIGPIPE, &old_handler, NULL);
891 GNUNET_free (smtp_server_name);
892 if (stats != NULL)
893 {
894 coreAPI->service_release (stats);
895 stats = NULL;
896 }
897 GNUNET_mutex_destroy (lock);
898 lock = NULL;
899 UNLINK (pipename);
900 GNUNET_free (pipename);
901 pipename = NULL;
902 GNUNET_free (email);
903 email = NULL;
904}
905
906/* end of smtp.c */
diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c
new file mode 100644
index 000000000..c87056e71
--- /dev/null
+++ b/src/transport/plugin_transport_tcp.c
@@ -0,0 +1,1782 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 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 transport/plugin_transport_tcp.c
23 * @brief Implementation of the TCP transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_hello_lib.h"
29#include "gnunet_network_lib.h"
30#include "gnunet_os_lib.h"
31#include "gnunet_peerinfo_service.h"
32#include "gnunet_protocols.h"
33#include "gnunet_resolver_service.h"
34#include "gnunet_server_lib.h"
35#include "gnunet_service_lib.h"
36#include "gnunet_statistics_service.h"
37#include "gnunet_transport_service.h"
38#include "plugin_transport.h"
39#include "transport.h"
40
41#define DEBUG_TCP GNUNET_NO
42
43/**
44 * After how long do we expire an address that we
45 * learned from another peer if it is not reconfirmed
46 * by anyone?
47 */
48#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
49
50/**
51 * How long until we give up on transmitting the welcome message?
52 */
53#define WELCOME_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
54
55/**
56 * How long until we give up on transmitting the welcome message?
57 */
58#define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
59
60/**
61 * For how many messages back to we keep transmission times?
62 */
63#define ACK_LOG_SIZE 32
64
65/**
66 * Initial handshake message for a session. This header
67 * is followed by the address that the other peer used to
68 * connect to us (so that we may learn it) or the address
69 * that the other peer got from the accept call.
70 */
71struct WelcomeMessage
72{
73 struct GNUNET_MessageHeader header;
74
75 /**
76 * Identity of the node connecting (TCP client)
77 */
78 struct GNUNET_PeerIdentity clientIdentity;
79
80};
81
82
83/**
84 * Encapsulation for normal TCP traffic.
85 */
86struct DataMessage
87{
88 struct GNUNET_MessageHeader header;
89
90 /**
91 * For alignment.
92 */
93 uint32_t reserved GNUNET_PACKED;
94
95 /**
96 * Number of the last message that was received from the other peer.
97 */
98 uint64_t ack_in GNUNET_PACKED;
99
100 /**
101 * Number of this outgoing message.
102 */
103 uint64_t ack_out GNUNET_PACKED;
104
105 /**
106 * How long was sending this ack delayed by the other peer
107 * (estimate). The receiver of this message can use the delay
108 * between sending his message number 'ack' and receiving this ack
109 * minus the delay as an estimate of the round-trip time.
110 */
111 struct GNUNET_TIME_RelativeNBO delay;
112
113};
114
115
116/**
117 * Encapsulation of all of the state of the plugin.
118 */
119struct Plugin;
120
121
122/**
123 * Information kept for each message that is yet to
124 * be transmitted.
125 */
126struct PendingMessage
127{
128
129 /**
130 * This is a linked list.
131 */
132 struct PendingMessage *next;
133
134 /**
135 * The pending message, pointer to the end
136 * of this struct, do not free!
137 */
138 struct GNUNET_MessageHeader *msg;
139
140
141 /**
142 * Continuation function to call once the message
143 * has been sent. Can be NULL if there is no
144 * continuation to call.
145 */
146 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
147
148 /**
149 * Closure for transmit_cont.
150 */
151 void *transmit_cont_cls;
152
153 /**
154 * Timeout value for the pending message.
155 */
156 struct GNUNET_TIME_Absolute timeout;
157
158 /**
159 * GNUNET_YES if this is a welcome message;
160 * otherwise this should be a DATA message.
161 */
162 int is_welcome;
163
164};
165
166
167/**
168 * Session handle for TCP connections.
169 */
170struct Session
171{
172
173 /**
174 * Stored in a linked list.
175 */
176 struct Session *next;
177
178 /**
179 * Pointer to the global plugin struct.
180 */
181 struct Plugin *plugin;
182
183 /**
184 * The client (used to identify this connection)
185 */
186 struct GNUNET_SERVER_Client *client;
187
188 /**
189 * gnunet-service-transport context for this connection.
190 */
191 struct ReadyList *service_context;
192
193 /**
194 * Messages currently pending for transmission
195 * to this peer, if any.
196 */
197 struct PendingMessage *pending_messages;
198
199 /**
200 * Handle for pending transmission request.
201 */
202 struct GNUNET_NETWORK_TransmitHandle *transmit_handle;
203
204 /**
205 * To whom are we talking to (set to our identity
206 * if we are still waiting for the welcome message)
207 */
208 struct GNUNET_PeerIdentity target;
209
210 /**
211 * At what time did we reset last_received last?
212 */
213 struct GNUNET_TIME_Absolute last_quota_update;
214
215 /**
216 * Address of the other peer if WE initiated the connection
217 * (and hence can be sure what it is), otherwise NULL.
218 */
219 void *connect_addr;
220
221 /**
222 * How many bytes have we received since the "last_quota_update"
223 * timestamp?
224 */
225 uint64_t last_received;
226
227 /**
228 * Our current latency estimate (in ms).
229 */
230 double latency_estimate;
231
232 /**
233 * Time when we generated the last ACK_LOG_SIZE acks.
234 * (the "last" refers to the "out_msg_counter" here)
235 */
236 struct GNUNET_TIME_Absolute gen_time[ACK_LOG_SIZE];
237
238 /**
239 * Our current sequence number.
240 */
241 uint64_t out_msg_counter;
242
243 /**
244 * Highest received incoming sequence number.
245 */
246 uint64_t max_in_msg_counter;
247
248 /**
249 * Number of bytes per ms that this peer is allowed
250 * to send to us.
251 */
252 uint32_t quota_in;
253
254 /**
255 * Length of connect_addr, can be 0.
256 */
257 size_t connect_alen;
258
259 /**
260 * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
261 */
262 int expecting_welcome;
263
264 /**
265 * Are we still trying to connect?
266 */
267 int still_connecting;
268
269};
270
271
272/**
273 * Encapsulation of all of the state of the plugin.
274 */
275struct Plugin
276{
277 /**
278 * Our environment.
279 */
280 struct GNUNET_TRANSPORT_PluginEnvironment *env;
281
282 /**
283 * The listen socket.
284 */
285 struct GNUNET_NETWORK_SocketHandle *lsock;
286
287 /**
288 * List of open TCP sessions.
289 */
290 struct Session *sessions;
291
292 /**
293 * Handle for the statistics service.
294 */
295 struct GNUNET_STATISTICS_Handle *statistics;
296
297 /**
298 * Handle to the network service.
299 */
300 struct GNUNET_SERVICE_Context *service;
301
302 /**
303 * Handle to the server for this service.
304 */
305 struct GNUNET_SERVER_Handle *server;
306
307 /**
308 * Copy of the handler array where the closures are
309 * set to this struct's instance.
310 */
311 struct GNUNET_SERVER_MessageHandler *handlers;
312
313 /**
314 * ID of task used to update our addresses when one expires.
315 */
316 GNUNET_SCHEDULER_TaskIdentifier address_update_task;
317
318 /**
319 * Port that we are actually listening on.
320 */
321 uint16_t open_port;
322
323 /**
324 * Port that the user said we would have visible to the
325 * rest of the world.
326 */
327 uint16_t adv_port;
328
329};
330
331
332/**
333 * Find the session handle for the given peer.
334 */
335static struct Session *
336find_session_by_target (struct Plugin *plugin,
337 const struct GNUNET_PeerIdentity *target)
338{
339 struct Session *ret;
340
341 ret = plugin->sessions;
342 while ((ret != NULL) &&
343 (0 != memcmp (target,
344 &ret->target, sizeof (struct GNUNET_PeerIdentity))))
345 ret = ret->next;
346 return ret;
347}
348
349
350/**
351 * Find the session handle for the given peer.
352 */
353static struct Session *
354find_session_by_client (struct Plugin *plugin,
355 const struct GNUNET_SERVER_Client *client)
356{
357 struct Session *ret;
358
359 ret = plugin->sessions;
360 while ((ret != NULL) && (client != ret->client))
361 ret = ret->next;
362 return ret;
363}
364
365
366/**
367 * Create a welcome message.
368 */
369static struct PendingMessage *
370create_welcome (size_t addrlen, const void *addr, struct Plugin *plugin)
371{
372 struct PendingMessage *pm;
373 struct WelcomeMessage *welcome;
374
375 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
376 sizeof (struct WelcomeMessage) + addrlen);
377 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
378 welcome = (struct WelcomeMessage *) &pm[1];
379 welcome->header.size = htons (sizeof (struct WelcomeMessage) + addrlen);
380 welcome->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME);
381 GNUNET_CRYPTO_hash (plugin->env->my_public_key,
382 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
383 &welcome->clientIdentity.hashPubKey);
384 memcpy (&welcome[1], addr, addrlen);
385 pm->timeout = GNUNET_TIME_relative_to_absolute (WELCOME_TIMEOUT);
386 pm->is_welcome = GNUNET_YES;
387 return pm;
388}
389
390
391/**
392 * Create a new session using the specified address
393 * for the welcome message.
394 *
395 * @param plugin us
396 * @param target peer to connect to
397 * @param client client to use
398 * @param addrlen IPv4 or IPv6
399 * @param addr either struct sockaddr_in or struct sockaddr_in6
400 * @return NULL connection failed / invalid address
401 */
402static struct Session *
403create_session (struct Plugin *plugin,
404 const struct GNUNET_PeerIdentity *target,
405 struct GNUNET_SERVER_Client *client,
406 const void *addr, size_t addrlen)
407{
408 struct Session *ret;
409
410 ret = GNUNET_malloc (sizeof (struct Session));
411 ret->plugin = plugin;
412 ret->next = plugin->sessions;
413 plugin->sessions = ret;
414 ret->client = client;
415 ret->target = *target;
416 ret->last_quota_update = GNUNET_TIME_absolute_get ();
417 ret->quota_in = plugin->env->default_quota_in;
418 ret->expecting_welcome = GNUNET_YES;
419 ret->pending_messages = create_welcome (addrlen, addr, plugin);
420 return ret;
421}
422
423
424/**
425 * Create a new session connecting to the specified
426 * target at the specified address.
427 *
428 * @param plugin us
429 * @param target peer to connect to
430 * @param addrlen IPv4 or IPv6
431 * @param addr either struct sockaddr_in or struct sockaddr_in6
432 * @return NULL connection failed / invalid address
433 */
434static struct Session *
435connect_and_create_session (struct Plugin *plugin,
436 const struct GNUNET_PeerIdentity *target,
437 const void *addr, size_t addrlen)
438{
439 struct GNUNET_SERVER_Client *client;
440 struct GNUNET_NETWORK_SocketHandle *conn;
441 struct Session *session;
442 int af;
443 char buf[INET6_ADDRSTRLEN];
444 uint16_t port;
445
446 session = plugin->sessions;
447 while (session != NULL)
448 {
449 if ((0 == memcmp (target,
450 &session->target,
451 sizeof (struct GNUNET_PeerIdentity))) &&
452 (session->connect_alen == addrlen) &&
453 (0 == memcmp (session->connect_addr, addr, addrlen)))
454 return session; /* already exists! */
455 session = session->next;
456 }
457
458 if (addrlen == sizeof (struct sockaddr_in))
459 {
460 af = AF_INET;
461 inet_ntop (af,
462 &((struct sockaddr_in *) addr)->sin_addr, buf, sizeof (buf));
463 port = ntohs (((struct sockaddr_in *) addr)->sin_port);
464 }
465 else if (addrlen == sizeof (struct sockaddr_in6))
466 {
467 af = AF_INET6;
468 inet_ntop (af,
469 &((struct sockaddr_in6 *) addr)->sin6_addr,
470 buf, sizeof (buf));
471 port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
472 }
473 else
474 {
475 GNUNET_break_op (0);
476 return NULL; /* invalid address */
477 }
478 conn = GNUNET_NETWORK_socket_create_from_sockaddr (plugin->env->sched,
479 af,
480 addr,
481 addrlen,
482 GNUNET_SERVER_MAX_MESSAGE_SIZE);
483 if (conn == NULL)
484 {
485#if DEBUG_TCP
486 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
487 "tcp",
488 "Failed to create connection to peer at `%s:%u'.\n",
489 buf, port);
490#endif
491 return NULL;
492 }
493 client = GNUNET_SERVER_connect_socket (plugin->server, conn);
494 GNUNET_assert (client != NULL);
495 session = create_session (plugin, target, client, addr, addrlen);
496 session->connect_alen = addrlen;
497 session->connect_addr = GNUNET_malloc (addrlen);
498 memcpy (session->connect_addr, addr, addrlen);
499#if DEBUG_TCP
500 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
501 "tcp",
502 "Creating new session %p with `%s:%u' based on `%s' request.\n",
503 session, buf, port, "send_to");
504#endif
505 return session;
506}
507
508
509/**
510 * If we have pending messages, ask the server to
511 * transmit them (schedule the respective tasks, etc.)
512 *
513 * @param session for which session should we do this
514 */
515static void process_pending_messages (struct Session *session);
516
517
518/**
519 * Function called to notify a client about the socket
520 * begin ready to queue more data. "buf" will be
521 * NULL and "size" zero if the socket was closed for
522 * writing in the meantime.
523 *
524 * @param cls closure
525 * @param size number of bytes available in buf
526 * @param buf where the callee should write the message
527 * @return number of bytes written to buf
528 */
529static size_t
530do_transmit (void *cls, size_t size, void *buf)
531{
532 struct Session *session = cls;
533 struct PendingMessage *pm;
534 char *cbuf;
535 uint16_t msize;
536 size_t ret;
537 struct DataMessage *dm;
538
539 session->transmit_handle = NULL;
540 if (buf == NULL)
541 {
542#if DEBUG_TCP
543 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
544 "tcp", "Timeout trying to transmit\n");
545#endif
546 /* timeout */
547 while (NULL != (pm = session->pending_messages))
548 {
549 session->pending_messages = pm->next;
550 if (pm->transmit_cont != NULL)
551 pm->transmit_cont (pm->transmit_cont_cls,
552 session->service_context,
553 &session->target, GNUNET_SYSERR);
554 GNUNET_free (pm);
555 }
556 return 0;
557 }
558 ret = 0;
559 cbuf = buf;
560 while (NULL != (pm = session->pending_messages))
561 {
562 if (pm->is_welcome)
563 {
564 if (size < (msize = htons (pm->msg->size)))
565 break;
566 memcpy (cbuf, pm->msg, msize);
567 cbuf += msize;
568 ret += msize;
569 size -= msize;
570 }
571 else
572 {
573 if (size <
574 sizeof (struct DataMessage) + (msize = htons (pm->msg->size)))
575 break;
576 dm = (struct DataMessage *) cbuf;
577 dm->header.size = htons (sizeof (struct DataMessage) + msize);
578 dm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA);
579 dm->ack_out = GNUNET_htonll (++session->out_msg_counter);
580 dm->ack_in = GNUNET_htonll (session->max_in_msg_counter);
581 cbuf += sizeof (struct DataMessage);
582 ret += sizeof (struct DataMessage);
583 size -= sizeof (struct DataMessage);
584 memcpy (cbuf, pm->msg, msize);
585 cbuf += msize;
586 ret += msize;
587 size -= msize;
588 }
589 session->pending_messages = pm->next;
590 if (pm->transmit_cont != NULL)
591 pm->transmit_cont (pm->transmit_cont_cls,
592 session->service_context,
593 &session->target, GNUNET_OK);
594 GNUNET_free (pm);
595 session->gen_time[session->out_msg_counter % ACK_LOG_SIZE]
596 = GNUNET_TIME_absolute_get ();
597 }
598 process_pending_messages (session);
599#if DEBUG_TCP || 1
600 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
601 "tcp", "Transmitting %u bytes\n", ret);
602#endif
603 return ret;
604}
605
606
607/**
608 * If we have pending messages, ask the server to
609 * transmit them (schedule the respective tasks, etc.)
610 *
611 * @param session for which session should we do this
612 */
613static void
614process_pending_messages (struct Session *session)
615{
616 GNUNET_assert (session->client != NULL);
617 if (session->pending_messages == NULL)
618 return;
619 if (session->transmit_handle != NULL)
620 return;
621 session->transmit_handle
622 = GNUNET_SERVER_notify_transmit_ready (session->client,
623 htons (session->pending_messages->
624 msg->size) +
625 (session->pending_messages->
626 is_welcome ? 0 : sizeof (struct
627 DataMessage)),
628 GNUNET_TIME_absolute_get_remaining
629 (session->pending_messages[0].
630 timeout), &do_transmit, session);
631}
632
633
634/**
635 * Function that can be used by the transport service to transmit
636 * a message using the plugin using a fresh connection (even if
637 * we already have a connection to this peer, this function is
638 * required to establish a new one).
639 *
640 * @param cls closure
641 * @param target who should receive this message
642 * @param msg1 first message to transmit
643 * @param msg2 second message to transmit (can be NULL)
644 * @param timeout how long should we try to transmit these?
645 * @param addrlen length of the address
646 * @param addr the address
647 * @return session if the transmission has been scheduled
648 * NULL if the address format is invalid
649 */
650static void *
651tcp_plugin_send_to (void *cls,
652 const struct GNUNET_PeerIdentity *target,
653 const struct GNUNET_MessageHeader *msg1,
654 const struct GNUNET_MessageHeader *msg2,
655 struct GNUNET_TIME_Relative timeout,
656 const void *addr, size_t addrlen)
657{
658 struct Plugin *plugin = cls;
659 struct Session *session;
660 struct PendingMessage *pl;
661 struct PendingMessage *pm;
662
663 session = connect_and_create_session (plugin, target, addr, addrlen);
664 if (session == NULL)
665 {
666#if DEBUG_TCP
667 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
668 "tcp", "Failed to create fresh session.\n");
669#endif
670 return NULL;
671 }
672 pl = NULL;
673 if (msg2 != NULL)
674 {
675 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
676 ntohs (msg2->size));
677 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
678 memcpy (pm->msg, msg2, ntohs (msg2->size));
679 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
680 pm->is_welcome = GNUNET_NO;
681 pl = pm;
682 }
683 if (msg1 != NULL)
684 {
685 pm = GNUNET_malloc (sizeof (struct PendingMessage) +
686 ntohs (msg1->size));
687 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
688 memcpy (pm->msg, msg1, ntohs (msg1->size));
689 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
690 pm->is_welcome = GNUNET_NO;
691 pm->next = pl;
692 pl = pm;
693 }
694 /* append */
695 if (session->pending_messages != NULL)
696 {
697 pm = session->pending_messages;
698 while (pm->next != NULL)
699 pm = pm->next;
700 pm->next = pl;
701 }
702 else
703 {
704 session->pending_messages = pl;
705 }
706 process_pending_messages (session);
707 return session;
708}
709
710
711/**
712 * Functions with this signature are called whenever we need
713 * to close a session due to a disconnect or failure to
714 * establish a connection.
715 *
716 * @param session session to close down
717 */
718static void
719disconnect_session (struct Session *session)
720{
721 struct Session *prev;
722 struct Session *pos;
723 struct PendingMessage *pm;
724
725#if DEBUG_TCP
726 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
727 "tcp",
728 "Disconnecting from other peer (session %p).\n", session);
729#endif
730 /* remove from session list */
731 prev = NULL;
732 pos = session->plugin->sessions;
733 while (pos != session)
734 {
735 prev = pos;
736 pos = pos->next;
737 }
738 if (prev == NULL)
739 session->plugin->sessions = session->next;
740 else
741 prev->next = session->next;
742 /* clean up state */
743 if (session->client != NULL)
744 {
745#if DEBUG_TCP
746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747 "Disconnecting from client address %p\n", session->client);
748#endif
749 GNUNET_SERVER_client_drop (session->client);
750 session->client = NULL;
751 }
752 if (session->transmit_handle != NULL)
753 {
754 GNUNET_NETWORK_notify_transmit_ready_cancel (session->transmit_handle);
755 session->transmit_handle = NULL;
756 }
757 while (NULL != (pm = session->pending_messages))
758 {
759 session->pending_messages = pm->next;
760 if (NULL != pm->transmit_cont)
761 pm->transmit_cont (pm->transmit_cont_cls,
762 session->service_context,
763 &session->target, GNUNET_SYSERR);
764 GNUNET_free (pm);
765 }
766 /* notify transport service about disconnect */
767 session->plugin->env->receive (session->plugin->env->cls,
768 session,
769 session->service_context,
770 GNUNET_TIME_UNIT_ZERO,
771 &session->target, NULL);
772 GNUNET_free_non_null (session->connect_addr);
773 GNUNET_free (session);
774}
775
776
777/**
778 * Iterator callback to go over all addresses. If we get
779 * a TCP address, increment the counter
780 *
781 * @param cls closure, points to the counter
782 * @param tname name of the transport
783 * @param expiration expiration time
784 * @param addr the address
785 * @param addrlen length of the address
786 * @return GNUNET_OK to keep the address,
787 * GNUNET_NO to delete it from the HELLO
788 * GNUNET_SYSERR to stop iterating (but keep current address)
789 */
790static int
791count_tcp_addresses (void *cls,
792 const char *tname,
793 struct GNUNET_TIME_Absolute expiration,
794 const void *addr, size_t addrlen)
795{
796 unsigned int *counter = cls;
797
798 if (0 != strcmp (tname, "tcp"))
799 return GNUNET_OK; /* not one of ours */
800 (*counter)++;
801 return GNUNET_OK; /* failed to connect */
802}
803
804
805struct ConnectContext
806{
807 struct Plugin *plugin;
808
809 struct GNUNET_NETWORK_SocketHandle *sa;
810
811 struct PendingMessage *welcome;
812
813 unsigned int pos;
814};
815
816
817/**
818 * Iterator callback to go over all addresses. If we get
819 * the "pos" TCP address, try to connect to it.
820 *
821 * @param cls closure
822 * @param tname name of the transport
823 * @param expiration expiration time
824 * @param addrlen length of the address
825 * @param addr the address
826 * @return GNUNET_OK to keep the address,
827 * GNUNET_NO to delete it from the HELLO
828 * GNUNET_SYSERR to stop iterating (but keep current address)
829 */
830static int
831try_connect_to_address (void *cls,
832 const char *tname,
833 struct GNUNET_TIME_Absolute expiration,
834 const void *addr, size_t addrlen)
835{
836 struct ConnectContext *cc = cls;
837 int af;
838
839 if (0 != strcmp (tname, "tcp"))
840 return GNUNET_OK; /* not one of ours */
841 if (sizeof (struct sockaddr_in) == addrlen)
842 af = AF_INET;
843 else if (sizeof (struct sockaddr_in6) == addrlen)
844 af = AF_INET6;
845 else
846 {
847 /* not a valid address */
848 GNUNET_break (0);
849 return GNUNET_NO;
850 }
851 if (0 == cc->pos--)
852 {
853 cc->welcome = create_welcome (addrlen, addr, cc->plugin);
854 cc->sa =
855 GNUNET_NETWORK_socket_create_from_sockaddr (cc->plugin->env->sched,
856 af, addr, addrlen,
857 GNUNET_SERVER_MAX_MESSAGE_SIZE);
858#if DEBUG_TCP
859 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
860 "tcp", "Connected to other peer.\n");
861#endif
862 return GNUNET_SYSERR;
863 }
864 return GNUNET_OK; /* failed to connect */
865}
866
867
868/**
869 * Type of an iterator over the hosts. Note that each
870 * host will be called with each available protocol.
871 *
872 * @param cls closure
873 * @param peer id of the peer, NULL for last call
874 * @param hello hello message for the peer (can be NULL)
875 * @param trust amount of trust we have in the peer
876 */
877static void
878session_try_connect (void *cls,
879 const struct GNUNET_PeerIdentity *peer,
880 const struct GNUNET_HELLO_Message *hello, uint32_t trust)
881{
882 struct Session *session = cls;
883 unsigned int count;
884 struct ConnectContext cctx;
885 struct PendingMessage *pm;
886
887 if (peer == NULL)
888 {
889 /* last call, destroy session if we are still not
890 connected */
891 if (session->still_connecting == GNUNET_NO)
892 {
893#if DEBUG_TCP
894 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
895 "tcp",
896 "Connected to other peer, now processing messages.\n");
897#endif
898 process_pending_messages (session);
899 }
900 else
901 {
902#if DEBUG_TCP
903 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
904 "tcp",
905 "Failed to connect to other peer, now closing session.\n");
906#endif
907 disconnect_session (session);
908 }
909 return;
910 }
911 if ((hello == NULL) || (session->client != NULL))
912 {
913 GNUNET_break (0); /* should this ever happen!? */
914 return;
915 }
916 count = 0;
917 GNUNET_HELLO_iterate_addresses (hello,
918 GNUNET_NO, &count_tcp_addresses, &count);
919 if (count == 0)
920 {
921#if DEBUG_TCP
922 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
923 "tcp",
924 "Asked to connect, but have no addresses to try.\n");
925#endif
926 return;
927 }
928 cctx.plugin = session->plugin;
929 cctx.sa = NULL;
930 cctx.welcome = NULL;
931 cctx.pos = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, count);
932 GNUNET_HELLO_iterate_addresses (hello,
933 GNUNET_NO, &try_connect_to_address, &cctx);
934 if (cctx.sa == NULL)
935 {
936#if DEBUG_TCP
937 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
938 "tcp",
939 "Asked to connect, but all addresses failed.\n");
940#endif
941 GNUNET_free_non_null (cctx.welcome);
942 return;
943 }
944 session->client = GNUNET_SERVER_connect_socket (session->plugin->server,
945 cctx.sa);
946#if DEBUG_TCP
947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948 "Connected getting client address %p\n", session->client);
949#endif
950 if (session->client == NULL)
951 {
952 GNUNET_break (0); /* how could this happen? */
953 GNUNET_free_non_null (cctx.welcome);
954 return;
955 }
956 pm = cctx.welcome;
957 /* prepend (!) */
958 pm->next = session->pending_messages;
959 session->pending_messages = pm;
960 session->still_connecting = GNUNET_NO;
961#if DEBUG_TCP
962 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
963 "tcp",
964 "Connected to other peer, now sending `%s' message.\n",
965 "WELCOME");
966#endif
967}
968
969
970/**
971 * Function that can be used by the transport service to transmit
972 * a message using the plugin.
973 *
974 * @param cls closure
975 * @param plugin_context value we were asked to pass to this plugin
976 * to respond to the given peer (use is optional,
977 * but may speed up processing), can be NULL
978 * @param service_context value passed to the transport-service
979 * to identify the neighbour
980 * @param target who should receive this message
981 * @param msg the message to transmit
982 * @param cont continuation to call once the message has
983 * been transmitted (or if the transport is ready
984 * for the next transmission call; or if the
985 * peer disconnected...)
986 * @param cont_cls closure for cont
987 * @return plugin_context that should be used next time for
988 * sending messages to the specified peer
989 */
990static void *
991tcp_plugin_send (void *cls,
992 void *plugin_context,
993 struct ReadyList *service_context,
994 const struct GNUNET_PeerIdentity *target,
995 const struct GNUNET_MessageHeader *msg,
996 struct GNUNET_TIME_Relative timeout,
997 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
998{
999 struct Plugin *plugin = cls;
1000 struct Session *session = plugin_context;
1001 struct PendingMessage *pm;
1002 struct PendingMessage *pme;
1003
1004 if (session == NULL)
1005 session = find_session_by_target (plugin, target);
1006 pm = GNUNET_malloc (sizeof (struct PendingMessage) + ntohs (msg->size));
1007 pm->msg = (struct GNUNET_MessageHeader *) &pm[1];
1008 memcpy (pm->msg, msg, ntohs (msg->size));
1009 pm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1010 pm->transmit_cont = cont;
1011 pm->transmit_cont_cls = cont_cls;
1012 if (session == NULL)
1013 {
1014 session = GNUNET_malloc (sizeof (struct Session));
1015#if DEBUG_TCP
1016 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1017 "tcp",
1018 "Asked to transmit, creating fresh session %p.\n",
1019 session);
1020#endif
1021 session->next = plugin->sessions;
1022 plugin->sessions = session;
1023 session->plugin = plugin;
1024 session->target = *target;
1025 session->last_quota_update = GNUNET_TIME_absolute_get ();
1026 session->quota_in = plugin->env->default_quota_in;
1027 session->expecting_welcome = GNUNET_YES;
1028 session->still_connecting = GNUNET_YES;
1029 session->pending_messages = pm;
1030 GNUNET_PEERINFO_for_all (plugin->env->cfg,
1031 plugin->env->sched,
1032 target,
1033 0, timeout, &session_try_connect, session);
1034 return session;
1035 }
1036 GNUNET_assert (session != NULL);
1037 GNUNET_assert (session->still_connecting == GNUNET_NO);
1038 /* append pm to pending_messages list */
1039 pme = session->pending_messages;
1040 if (pme == NULL)
1041 {
1042 session->pending_messages = pm;
1043 }
1044 else
1045 {
1046 while (NULL != pme->next)
1047 pme = pme->next;
1048 pme->next = pm;
1049 }
1050#if DEBUG_TCP
1051 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1052 "tcp", "Asked to transmit, added message to list.\n");
1053#endif
1054 process_pending_messages (session);
1055 return session;
1056}
1057
1058
1059
1060/**
1061 * Function that can be called to force a disconnect from the
1062 * specified neighbour. This should also cancel all previously
1063 * scheduled transmissions. Obviously the transmission may have been
1064 * partially completed already, which is OK. The plugin is supposed
1065 * to close the connection (if applicable) and no longer call the
1066 * transmit continuation(s).
1067 *
1068 * Finally, plugin MUST NOT call the services's receive function to
1069 * notify the service that the connection to the specified target was
1070 * closed after a getting this call.
1071 *
1072 * @param cls closure
1073 * @param plugin_context value we were asked to pass to this plugin
1074 * to respond to the given peer (use is optional,
1075 * but may speed up processing), can be NULL (if
1076 * NULL was returned from the transmit function)
1077 * @param service_context must correspond to the service context
1078 * of the corresponding Transmit call; the plugin should
1079 * not cancel a send call made with a different service
1080 * context pointer! Never NULL.
1081 * @param target peer for which the last transmission is
1082 * to be cancelled
1083 */
1084static void
1085tcp_plugin_cancel (void *cls,
1086 void *plugin_context,
1087 struct ReadyList *service_context,
1088 const struct GNUNET_PeerIdentity *target)
1089{
1090 struct Plugin *plugin = cls;
1091 struct PendingMessage *pm;
1092 struct Session *session;
1093 struct Session *next;
1094
1095 session = plugin->sessions;
1096 while (session != NULL)
1097 {
1098 next = session->next;
1099 if (0 == memcmp (target,
1100 &session->target, sizeof (struct GNUNET_PeerIdentity)))
1101 {
1102 pm = session->pending_messages;
1103 while (pm != NULL)
1104 {
1105 pm->transmit_cont = NULL;
1106 pm->transmit_cont_cls = NULL;
1107 pm = pm->next;
1108 }
1109 session->service_context = NULL;
1110 GNUNET_SERVER_client_disconnect (session->client);
1111 /* rest of the clean-up of the session will be done as part of
1112 disconnect_notify which should be triggered any time now */
1113 }
1114 session = next;
1115 }
1116}
1117
1118
1119struct PrettyPrinterContext
1120{
1121 GNUNET_TRANSPORT_AddressStringCallback asc;
1122 void *asc_cls;
1123 uint16_t port;
1124};
1125
1126
1127/**
1128 * Append our port and forward the result.
1129 */
1130static void
1131append_port (void *cls, const char *hostname)
1132{
1133 struct PrettyPrinterContext *ppc = cls;
1134 char *ret;
1135
1136 if (hostname == NULL)
1137 {
1138 ppc->asc (ppc->asc_cls, NULL);
1139 GNUNET_free (ppc);
1140 return;
1141 }
1142 GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
1143 ppc->asc (ppc->asc_cls, ret);
1144 GNUNET_free (ret);
1145}
1146
1147
1148/**
1149 * Convert the transports address to a nice, human-readable
1150 * format.
1151 *
1152 * @param cls closure
1153 * @param name name of the transport that generated the address
1154 * @param addr one of the addresses of the host, NULL for the last address
1155 * the specific address format depends on the transport
1156 * @param addrlen length of the address
1157 * @param numeric should (IP) addresses be displayed in numeric form?
1158 * @param timeout after how long should we give up?
1159 * @param asc function to call on each string
1160 * @param asc_cls closure for asc
1161 */
1162static void
1163tcp_plugin_address_pretty_printer (void *cls,
1164 const char *type,
1165 const void *addr,
1166 size_t addrlen,
1167 int numeric,
1168 struct GNUNET_TIME_Relative timeout,
1169 GNUNET_TRANSPORT_AddressStringCallback asc,
1170 void *asc_cls)
1171{
1172 struct Plugin *plugin = cls;
1173 const struct sockaddr_in *v4;
1174 const struct sockaddr_in6 *v6;
1175 struct PrettyPrinterContext *ppc;
1176
1177 if ((addrlen != sizeof (struct sockaddr_in)) &&
1178 (addrlen != sizeof (struct sockaddr_in6)))
1179 {
1180 /* invalid address */
1181 GNUNET_break_op (0);
1182 asc (asc_cls, NULL);
1183 return;
1184 }
1185 ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
1186 ppc->asc = asc;
1187 ppc->asc_cls = asc_cls;
1188 if (addrlen == sizeof (struct sockaddr_in))
1189 {
1190 v4 = (const struct sockaddr_in *) addr;
1191 ppc->port = ntohs (v4->sin_port);
1192 }
1193 else
1194 {
1195 v6 = (const struct sockaddr_in6 *) addr;
1196 ppc->port = ntohs (v6->sin6_port);
1197
1198 }
1199 GNUNET_RESOLVER_hostname_get (plugin->env->sched,
1200 plugin->env->cfg,
1201 addr,
1202 addrlen,
1203 !numeric, timeout, &append_port, ppc);
1204}
1205
1206
1207/**
1208 * Update the last-received and bandwidth quota values
1209 * for this session.
1210 *
1211 * @param session session to update
1212 * @param force set to GNUNET_YES if we should update even
1213 * though the minimum refresh time has not yet expired
1214 */
1215static void
1216update_quota (struct Session *session, int force)
1217{
1218 struct GNUNET_TIME_Absolute now;
1219 unsigned long long delta;
1220 unsigned long long total_allowed;
1221 unsigned long long total_remaining;
1222
1223 now = GNUNET_TIME_absolute_get ();
1224 delta = now.value - session->last_quota_update.value;
1225 if ((delta < MIN_QUOTA_REFRESH_TIME) && (!force))
1226 return; /* too early, not enough data */
1227
1228 total_allowed = session->quota_in * delta;
1229 if (total_allowed > session->last_received)
1230 {
1231 /* got less than acceptable */
1232 total_remaining = total_allowed - session->last_received;
1233 session->last_received = 0;
1234 delta = total_remaining / session->quota_in; /* bonus seconds */
1235 if (delta > MAX_BANDWIDTH_CARRY)
1236 delta = MAX_BANDWIDTH_CARRY; /* limit amount of carry-over */
1237 }
1238 else
1239 {
1240 /* got more than acceptable */
1241 total_remaining = 0;
1242 session->last_received -= total_allowed;
1243 delta = 0;
1244 }
1245 session->last_quota_update.value = now.value - delta;
1246}
1247
1248
1249/**
1250 * Set a quota for receiving data from the given peer; this is a
1251 * per-transport limit. The transport should limit its read/select
1252 * calls to stay below the quota (in terms of incoming data).
1253 *
1254 * @param cls closure
1255 * @param peer the peer for whom the quota is given
1256 * @param quota_in quota for receiving/sending data in bytes per ms
1257 */
1258static void
1259tcp_plugin_set_receive_quota (void *cls,
1260 const struct GNUNET_PeerIdentity *target,
1261 uint32_t quota_in)
1262{
1263 struct Plugin *plugin = cls;
1264 struct Session *session;
1265
1266 session = find_session_by_target (plugin, target);
1267 if (session->quota_in != quota_in)
1268 {
1269 update_quota (session, GNUNET_YES);
1270 if (session->quota_in > quota_in)
1271 session->last_quota_update = GNUNET_TIME_absolute_get ();
1272 session->quota_in = quota_in;
1273 }
1274}
1275
1276
1277/**
1278 * Check if the given port is plausible (must be either
1279 * our listen port or our advertised port). If it is
1280 * neither, we return one of these two ports at random.
1281 *
1282 * @return either in_port or a more plausible port
1283 */
1284static uint16_t
1285check_port (struct Plugin *plugin, uint16_t in_port)
1286{
1287 if ((in_port == plugin->adv_port) || (in_port == plugin->open_port))
1288 return in_port;
1289 return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1290 2) == 0)
1291 ? plugin->open_port : plugin->adv_port;
1292}
1293
1294
1295/**
1296 * Another peer has suggested an address for this
1297 * peer and transport plugin. Check that this could be a valid
1298 * address. If so, consider adding it to the list
1299 * of addresses.
1300 *
1301 * @param cls closure
1302 * @param addr pointer to the address
1303 * @param addrlen length of addr
1304 * @return GNUNET_OK if this is a plausible address for this peer
1305 * and transport
1306 */
1307static int
1308tcp_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
1309{
1310 struct Plugin *plugin = cls;
1311 char buf[sizeof (struct sockaddr_in6)];
1312 struct sockaddr_in *v4;
1313 struct sockaddr_in6 *v6;
1314 char dst[INET6_ADDRSTRLEN];
1315 uint16_t port;
1316
1317 if ((addrlen != sizeof (struct sockaddr_in)) &&
1318 (addrlen != sizeof (struct sockaddr_in6)))
1319 {
1320 GNUNET_break_op (0);
1321 return GNUNET_SYSERR;
1322 }
1323 memcpy (buf, addr, sizeof (struct sockaddr_in6));
1324 if (addrlen == sizeof (struct sockaddr_in))
1325 {
1326 v4 = (struct sockaddr_in *) buf;
1327 v4->sin_port = htons (check_port (plugin, ntohs (v4->sin_port)));
1328 inet_ntop (AF_INET, &v4->sin_addr, dst, sizeof (dst));
1329 port = ntohs (v4->sin_port);
1330 }
1331 else
1332 {
1333 v6 = (struct sockaddr_in6 *) buf;
1334 v6->sin6_port = htons (check_port (plugin, ntohs (v6->sin6_port)));
1335 inet_ntop (AF_INET6, &v6->sin6_addr, dst, sizeof (dst));
1336 port = ntohs (v6->sin6_port);
1337 }
1338#if DEBUG_TCP
1339 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1340 "tcp",
1341 "Informing transport service about my address `%s:%u'.\n",
1342 dst, port);
1343#endif
1344 plugin->env->notify_address (plugin->env->cls,
1345 "tcp",
1346 buf, addrlen, LEARNED_ADDRESS_EXPIRATION);
1347 return GNUNET_OK;
1348}
1349
1350
1351/**
1352 * We've received a welcome from this peer via TCP.
1353 * Possibly create a fresh client record and send back
1354 * our welcome.
1355 *
1356 * @param cls closure
1357 * @param server the server handling the message
1358 * @param client identification of the client
1359 * @param message the actual message
1360 */
1361static void
1362handle_tcp_welcome (void *cls,
1363 struct GNUNET_SERVER_Handle *server,
1364 struct GNUNET_SERVER_Client *client,
1365 const struct GNUNET_MessageHeader *message)
1366{
1367 struct Plugin *plugin = cls;
1368 struct Session *session_c;
1369 const struct WelcomeMessage *wm;
1370 uint16_t msize;
1371 uint32_t addrlen;
1372 size_t alen;
1373 void *vaddr;
1374 const struct sockaddr *addr;
1375
1376#if DEBUG_TCP
1377 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1378 "tcp",
1379 "Received `%s' message from %p.\n", "WELCOME", client);
1380#endif
1381 msize = ntohs (message->size);
1382 if (msize < sizeof (struct WelcomeMessage))
1383 {
1384 GNUNET_break_op (0);
1385 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1386 return;
1387 }
1388 wm = (const struct WelcomeMessage *) message;
1389 session_c = find_session_by_client (plugin, client);
1390 if (session_c == NULL)
1391 {
1392 vaddr = NULL;
1393 GNUNET_SERVER_client_get_address (client, &vaddr, &alen);
1394 GNUNET_SERVER_client_keep (client);
1395 session_c = create_session (plugin,
1396 &wm->clientIdentity, client, vaddr, alen);
1397#if DEBUG_TCP
1398 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1399 "tcp",
1400 "Creating new session %p for incoming `%s' message.\n",
1401 session_c, "WELCOME");
1402#endif
1403 GNUNET_free_non_null (vaddr);
1404 process_pending_messages (session_c);
1405 }
1406 session_c->expecting_welcome = GNUNET_NO;
1407 if (0 < (addrlen = msize - sizeof (struct WelcomeMessage)))
1408 {
1409 addr = (const struct sockaddr *) &wm[1];
1410 tcp_plugin_address_suggested (plugin, addr, addrlen);
1411 }
1412 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1413}
1414
1415
1416/**
1417 * Calculate how long we should delay reading from the TCP socket to
1418 * ensure that we stay within our bandwidth limits (push back).
1419 *
1420 * @param session for which client should this be calculated
1421 */
1422static struct GNUNET_TIME_Relative
1423calculate_throttle_delay (struct Session *session)
1424{
1425 struct GNUNET_TIME_Relative ret;
1426 struct GNUNET_TIME_Absolute now;
1427 uint64_t del;
1428 uint64_t avail;
1429 uint64_t excess;
1430
1431 now = GNUNET_TIME_absolute_get ();
1432 del = now.value - session->last_quota_update.value;
1433 if (del > MAX_BANDWIDTH_CARRY)
1434 {
1435 update_quota (session, GNUNET_YES);
1436 del = now.value - session->last_quota_update.value;
1437 GNUNET_assert (del <= MAX_BANDWIDTH_CARRY);
1438 }
1439 if (session->quota_in == 0)
1440 session->quota_in = 1; /* avoid divison by zero */
1441 avail = del * session->quota_in;
1442 if (avail > session->last_received)
1443 return GNUNET_TIME_UNIT_ZERO; /* can receive right now */
1444 excess = session->last_received - avail;
1445 ret.value = excess / session->quota_in;
1446 return ret;
1447}
1448
1449
1450/**
1451 * Task to signal the server that we can continue
1452 * receiving from the TCP client now.
1453 */
1454static void
1455delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1456{
1457 struct Session *session = cls;
1458 GNUNET_SERVER_receive_done (session->client, GNUNET_OK);
1459}
1460
1461
1462/**
1463 * We've received data for this peer via TCP. Unbox,
1464 * compute latency and forward.
1465 *
1466 * @param cls closure
1467 * @param server the server handling the message
1468 * @param client identification of the client
1469 * @param message the actual message
1470 */
1471static void
1472handle_tcp_data (void *cls,
1473 struct GNUNET_SERVER_Handle *server,
1474 struct GNUNET_SERVER_Client *client,
1475 const struct GNUNET_MessageHeader *message)
1476{
1477 struct Plugin *plugin = cls;
1478 struct Session *session;
1479 const struct DataMessage *dm;
1480 uint16_t msize;
1481 const struct GNUNET_MessageHeader *msg;
1482 struct GNUNET_TIME_Relative latency;
1483 struct GNUNET_TIME_Absolute ttime;
1484 struct GNUNET_TIME_Absolute now;
1485 struct GNUNET_TIME_Relative delay;
1486 uint64_t ack_in;
1487
1488#if DEBUG_TCP
1489 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1490 "tcp", "Receiving data from other peer.\n");
1491#endif
1492 msize = ntohs (message->size);
1493 if ((msize <
1494 sizeof (struct DataMessage) + sizeof (struct GNUNET_MessageHeader)))
1495 {
1496 GNUNET_break_op (0);
1497 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1498 return;
1499 }
1500 session = find_session_by_client (plugin, client);
1501 if ((NULL == session) || (GNUNET_YES == session->expecting_welcome))
1502 {
1503 GNUNET_break_op (0);
1504 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1505 return;
1506 }
1507 dm = (const struct DataMessage *) message;
1508 session->max_in_msg_counter = GNUNET_MAX (session->max_in_msg_counter,
1509 GNUNET_ntohll (dm->ack_out));
1510 msg = (const struct GNUNET_MessageHeader *) &dm[1];
1511 if (msize != sizeof (struct DataMessage) + ntohs (msg->size))
1512 {
1513 GNUNET_break_op (0);
1514 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1515 return;
1516 }
1517 /* estimate latency */
1518 ack_in = GNUNET_ntohll (dm->ack_in);
1519 if ((ack_in <= session->out_msg_counter) &&
1520 (session->out_msg_counter - ack_in < ACK_LOG_SIZE))
1521 {
1522 delay = GNUNET_TIME_relative_ntoh (dm->delay);
1523 ttime = session->gen_time[ack_in % ACK_LOG_SIZE];
1524 now = GNUNET_TIME_absolute_get ();
1525 if (delay.value > now.value - ttime.value)
1526 delay.value = 0; /* not plausible */
1527 /* update (round-trip) latency using ageing; we
1528 use 7:1 so that we can reasonably quickly react
1529 to changes, but not so fast that latency is largely
1530 jitter... */
1531 session->latency_estimate
1532 = ((7 * session->latency_estimate) +
1533 (now.value - ttime.value - delay.value)) / 8;
1534 }
1535 latency.value = (uint64_t) session->latency_estimate;
1536 /* deliver on */
1537#if DEBUG_TCP
1538 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1539 "tcp",
1540 "Forwarding data of type %u to transport service.\n",
1541 ntohs (msg->type));
1542#endif
1543 session->service_context
1544 = plugin->env->receive (plugin->env->cls,
1545 session,
1546 session->service_context,
1547 latency, &session->target, msg);
1548 /* update bandwidth used */
1549 session->last_received += msize;
1550 update_quota (session, GNUNET_NO);
1551
1552 delay = calculate_throttle_delay (session);
1553 if (delay.value == 0)
1554 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1555 else
1556 GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched,
1557 GNUNET_NO,
1558 GNUNET_SCHEDULER_PRIORITY_HIGH,
1559 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1560 delay, &delayed_done, session);
1561}
1562
1563
1564/**
1565 * Handlers for the various TCP messages.
1566 */
1567static struct GNUNET_SERVER_MessageHandler my_handlers[] = {
1568 {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME, 0},
1569 {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA, 0},
1570 {NULL, NULL, 0, 0}
1571};
1572
1573
1574static void
1575create_tcp_handlers (struct Plugin *plugin)
1576{
1577 unsigned int i;
1578 plugin->handlers = GNUNET_malloc (sizeof (my_handlers));
1579 memcpy (plugin->handlers, my_handlers, sizeof (my_handlers));
1580 for (i = 0;
1581 i <
1582 sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler);
1583 i++)
1584 plugin->handlers[i].callback_cls = plugin;
1585 GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers);
1586}
1587
1588
1589/**
1590 * Functions with this signature are called whenever a peer
1591 * is disconnected on the network level.
1592 *
1593 * @param cls closure
1594 * @param client identification of the client
1595 */
1596static void
1597disconnect_notify (void *cls, struct GNUNET_SERVER_Client *client)
1598{
1599 struct Plugin *plugin = cls;
1600 struct Session *session;
1601
1602#if DEBUG_TCP
1603 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1604 "tcp",
1605 "Notified about network-level disconnect of client %p.\n",
1606 client);
1607#endif
1608 session = find_session_by_client (plugin, client);
1609 if (session == NULL)
1610 return; /* unknown, nothing to do */
1611#if DEBUG_TCP
1612 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1613 "tcp", "Will now destroy session %p.\n", session);
1614#endif
1615 disconnect_session (session);
1616}
1617
1618
1619/**
1620 * Add the IP of our network interface to the list of
1621 * our external IP addresses.
1622 */
1623static int
1624process_interfaces (void *cls,
1625 const char *name,
1626 int isDefault,
1627 const struct sockaddr *addr, socklen_t addrlen)
1628{
1629 struct Plugin *plugin = cls;
1630 char dst[INET6_ADDRSTRLEN];
1631 int af;
1632 struct sockaddr_in *v4;
1633 struct sockaddr_in6 *v6;
1634
1635 af = addr->sa_family;
1636 if (af == AF_INET)
1637 {
1638 v4 = (struct sockaddr_in *) addr;
1639 inet_ntop (AF_INET, &v4->sin_addr, dst, sizeof (dst));
1640 v4->sin_port = htons (plugin->adv_port);
1641 }
1642 else
1643 {
1644 GNUNET_assert (af == AF_INET6);
1645 v6 = (struct sockaddr_in6 *) addr;
1646 inet_ntop (AF_INET6, &v6->sin6_addr, dst, sizeof (dst));
1647 v6->sin6_port = htons (plugin->adv_port);
1648 }
1649 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO |
1650 GNUNET_ERROR_TYPE_BULK,
1651 "tcp", _("Found address `%s' (%s)\n"), dst, name);
1652 plugin->env->notify_address (plugin->env->cls,
1653 "tcp",
1654 addr, addrlen, GNUNET_TIME_UNIT_FOREVER_REL);
1655 return GNUNET_OK;
1656}
1657
1658
1659/**
1660 * Function called by the resolver for each address obtained from DNS
1661 * for our own hostname. Add the addresses to the list of our
1662 * external IP addresses.
1663 *
1664 * @param cls closure
1665 * @param addr one of the addresses of the host, NULL for the last address
1666 * @param addrlen length of the address
1667 */
1668static void
1669process_hostname_ips (void *cls,
1670 const struct sockaddr *addr, socklen_t addrlen)
1671{
1672 struct Plugin *plugin = cls;
1673
1674 if (addr == NULL)
1675 return;
1676 plugin->env->notify_address (plugin->env->cls,
1677 "tcp",
1678 addr, addrlen, GNUNET_TIME_UNIT_FOREVER_REL);
1679}
1680
1681
1682/**
1683 * Entry point for the plugin.
1684 */
1685void *
1686libgnunet_plugin_transport_tcp_init (void *cls)
1687{
1688 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1689 struct GNUNET_TRANSPORT_PluginFunctions *api;
1690 struct Plugin *plugin;
1691 struct GNUNET_SERVICE_Context *service;
1692 unsigned long long aport;
1693 unsigned long long bport;
1694
1695 service = GNUNET_SERVICE_start ("tcp", env->sched, env->cfg);
1696 if (service == NULL)
1697 {
1698 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1699 "tcp",
1700 _
1701 ("Failed to start service for `%s' transport plugin.\n"),
1702 "tcp");
1703 return NULL;
1704 }
1705 aport = 0;
1706 if ((GNUNET_OK !=
1707 GNUNET_CONFIGURATION_get_value_number (env->cfg,
1708 "tcp",
1709 "PORT",
1710 &bport)) ||
1711 (bport > 65535) ||
1712 ((GNUNET_OK ==
1713 GNUNET_CONFIGURATION_get_value_number (env->cfg,
1714 "tcp",
1715 "ADVERTISED-PORT",
1716 &aport)) && (aport > 65535)))
1717 {
1718 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1719 "tcp",
1720 _
1721 ("Require valid port number for service `%s' in configuration!\n"),
1722 "tcp");
1723 GNUNET_SERVICE_stop (service);
1724 return NULL;
1725 }
1726 if (aport == 0)
1727 aport = bport;
1728 plugin = GNUNET_malloc (sizeof (struct Plugin));
1729 plugin->open_port = bport;
1730 plugin->adv_port = aport;
1731 plugin->env = env;
1732 plugin->lsock = NULL;
1733 plugin->statistics = NULL;
1734 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1735 api->cls = plugin;
1736 api->send_to = &tcp_plugin_send_to;
1737 api->send = &tcp_plugin_send;
1738 api->cancel = &tcp_plugin_cancel;
1739 api->address_pretty_printer = &tcp_plugin_address_pretty_printer;
1740 api->set_receive_quota = &tcp_plugin_set_receive_quota;
1741 api->address_suggested = &tcp_plugin_address_suggested;
1742 api->cost_estimate = 42; /* TODO: ATS */
1743 plugin->service = service;
1744 plugin->server = GNUNET_SERVICE_get_server (service);
1745 create_tcp_handlers (plugin);
1746 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1747 "tcp", _("TCP transport listening on port %u\n"), bport);
1748 if (aport != bport)
1749 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1750 "tcp",
1751 _
1752 ("TCP transport advertises itself as being on port %u\n"),
1753 aport);
1754 GNUNET_SERVER_disconnect_notify (plugin->server, &disconnect_notify,
1755 plugin);
1756 GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1757 GNUNET_RESOLVER_hostname_resolve (env->sched,
1758 env->cfg,
1759 AF_UNSPEC,
1760 HOSTNAME_RESOLVE_TIMEOUT,
1761 &process_hostname_ips, plugin);
1762 return api;
1763}
1764
1765
1766/**
1767 * Exit point from the plugin.
1768 */
1769void *
1770libgnunet_plugin_transport_tcp_done (void *cls)
1771{
1772 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1773 struct Plugin *plugin = api->cls;
1774
1775 GNUNET_SERVICE_stop (plugin->service);
1776 GNUNET_free (plugin->handlers);
1777 GNUNET_free (plugin);
1778 GNUNET_free (api);
1779 return NULL;
1780}
1781
1782/* end of plugin_transport_tcp.c */
diff --git a/src/transport/plugin_transport_template.c b/src/transport/plugin_transport_template.c
new file mode 100644
index 000000000..1c8b06c61
--- /dev/null
+++ b/src/transport/plugin_transport_template.c
@@ -0,0 +1,335 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 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 transport/plugin_transport_template.c
23 * @brief template for a new transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_protocols.h"
29#include "gnunet_network_lib.h"
30#include "gnunet_server_lib.h"
31#include "gnunet_service_lib.h"
32#include "gnunet_statistics_service.h"
33#include "gnunet_transport_service.h"
34#include "plugin_transport.h"
35
36#define DEBUG_TEMPLATE GNUNET_NO
37
38/**
39 * After how long do we expire an address that we
40 * learned from another peer if it is not reconfirmed
41 * by anyone?
42 */
43#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
44
45
46/**
47 * Encapsulation of all of the state of the plugin.
48 */
49struct Plugin;
50
51
52/**
53 * Session handle for connections.
54 */
55struct Session
56{
57
58 /**
59 * Stored in a linked list.
60 */
61 struct Session *next;
62
63 /**
64 * Pointer to the global plugin struct.
65 */
66 struct Plugin *plugin;
67
68 /**
69 * The client (used to identify this connection)
70 */
71 /* void *client; */
72
73 /**
74 * Continuation function to call once the transmission buffer
75 * has again space available. NULL if there is no
76 * continuation to call.
77 */
78 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
79
80 /**
81 * Closure for transmit_cont.
82 */
83 void *transmit_cont_cls;
84
85 /**
86 * To whom are we talking to (set to our identity
87 * if we are still waiting for the welcome message)
88 */
89 struct GNUNET_PeerIdentity sender;
90
91 /**
92 * At what time did we reset last_received last?
93 */
94 struct GNUNET_TIME_Absolute last_quota_update;
95
96 /**
97 * How many bytes have we received since the "last_quota_update"
98 * timestamp?
99 */
100 uint64_t last_received;
101
102 /**
103 * Number of bytes per ms that this peer is allowed
104 * to send to us.
105 */
106 uint32_t quota;
107
108};
109
110/**
111 * Encapsulation of all of the state of the plugin.
112 */
113struct Plugin
114{
115 /**
116 * Our environment.
117 */
118 struct GNUNET_TRANSPORT_PluginEnvironment *env;
119
120 /**
121 * List of open sessions.
122 */
123 struct Session *sessions;
124
125 /**
126 * Handle for the statistics service.
127 */
128 struct GNUNET_STATISTICS_Handle *statistics;
129
130};
131
132
133
134/**
135 * Function that can be used by the transport service to transmit
136 * a message using the plugin using a fresh connection (even if
137 * we already have a connection to this peer, this function is
138 * required to establish a new one).
139 *
140 * @param cls closure
141 * @param target who should receive this message
142 * @param msg1 first message to transmit
143 * @param msg2 second message to transmit (can be NULL)
144 * @param timeout how long until we give up?
145 * @param addr the address
146 * @param addrlen length of the address
147 * @return non-null session if the transmission has been scheduled
148 * NULL if the address format is invalid
149 */
150static void *
151template_plugin_send_to (void *cls,
152 const struct GNUNET_PeerIdentity *target,
153 const struct GNUNET_MessageHeader *msg1,
154 const struct GNUNET_MessageHeader *msg2,
155 struct GNUNET_TIME_Relative timeout,
156 const void *addr, size_t addrlen)
157{
158 // FIXME
159 return NULL;
160}
161
162
163/**
164 * Function that can be used by the transport service to transmit
165 * a message using the plugin.
166 *
167 * @param cls closure
168 * @param plugin_context value we were asked to pass to this plugin
169 * to respond to the given peer (use is optional,
170 * but may speed up processing), can be NULL
171 * @param service_context value passed to the transport-service
172 * to identify the neighbour
173 * @param target who should receive this message
174 * @param msg the message to transmit
175 * @param cont continuation to call once the message has
176 * been transmitted (or if the transport is ready
177 * for the next transmission call; or if the
178 * peer disconnected...)
179 * @param cont_cls closure for cont
180 * @return plugin_context that should be used next time for
181 * sending messages to the specified peer
182 */
183static void *
184template_plugin_send (void *cls,
185 void *plugin_context,
186 struct ReadyList *service_context,
187 const struct GNUNET_PeerIdentity *target,
188 const struct GNUNET_MessageHeader *msg,
189 struct GNUNET_TIME_Relative timeout,
190 GNUNET_TRANSPORT_TransmitContinuation cont,
191 void *cont_cls)
192{
193 // struct Plugin *plugin = cls;
194 return NULL;
195}
196
197
198
199/**
200 *
201 * @param cls closure
202 * @param plugin_context value we were asked to pass to this plugin
203 * to respond to the given peer (use is optional,
204 * but may speed up processing), can be NULL (if
205 * NULL was returned from the transmit function)
206 * @param service_context must correspond to the service context
207 * of the corresponding Transmit call; the plugin should
208 * not cancel a send call made with a different service
209 * context pointer! Never NULL.
210 * @param target peer for which the last transmission is
211 * to be cancelled
212 */
213static void
214template_plugin_cancel (void *cls,
215 void *plugin_context,
216 struct ReadyList *service_context,
217 const struct GNUNET_PeerIdentity *target)
218{
219 // struct Plugin *plugin = cls;
220 // FIXME
221}
222
223
224/**
225 * Convert the transports address to a nice, human-readable
226 * format.
227 *
228 * @param cls closure
229 * @param name name of the transport that generated the address
230 * @param addr one of the addresses of the host, NULL for the last address
231 * the specific address format depends on the transport
232 * @param addrlen length of the address
233 * @param numeric should (IP) addresses be displayed in numeric form?
234 * @param timeout after how long should we give up?
235 * @param asc function to call on each string
236 * @param asc_cls closure for asc
237 */
238static void
239template_plugin_address_pretty_printer (void *cls,
240 const char *type,
241 const void *addr,
242 size_t addrlen,
243 int numeric,
244 struct GNUNET_TIME_Relative timeout,
245 GNUNET_TRANSPORT_AddressStringCallback
246 asc, void *asc_cls)
247{
248 asc (asc_cls, NULL);
249}
250
251/**
252 * Set a quota for receiving data from the given peer; this is a
253 * per-transport limit. The transport should limit its read/select
254 * calls to stay below the quota (in terms of incoming data).
255 *
256 * @param cls closure
257 * @param peer the peer for whom the quota is given
258 * @param quota_in quota for receiving/sending data in bytes per ms
259 */
260static void
261template_plugin_set_receive_quota (void *cls,
262 const struct GNUNET_PeerIdentity *target,
263 uint32_t quota_in)
264{
265 // struct Plugin *plugin = cls;
266 // FIXME!
267}
268
269
270/**
271 * Another peer has suggested an address for this
272 * peer and transport plugin. Check that this could be a valid
273 * address. If so, consider adding it to the list
274 * of addresses.
275 *
276 * @param cls closure
277 * @param addr pointer to the address
278 * @param addrlen length of addr
279 * @return GNUNET_OK if this is a plausible address for this peer
280 * and transport
281 */
282static int
283template_plugin_address_suggested (void *cls,
284 const void *addr, size_t addrlen)
285{
286 // struct Plugin *plugin = cls;
287
288 /* check if the address is plausible; if so,
289 add it to our list! */
290 // FIXME!
291 return GNUNET_OK;
292}
293
294
295/**
296 * Entry point for the plugin.
297 */
298void *
299gnunet_plugin_transport_template_init (void *cls)
300{
301 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
302 struct GNUNET_TRANSPORT_PluginFunctions *api;
303 struct Plugin *plugin;
304
305 plugin = GNUNET_malloc (sizeof (struct Plugin));
306 plugin->env = env;
307 plugin->statistics = NULL;
308 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
309 api->cls = plugin;
310 api->send_to = &template_plugin_send_to;
311 api->send = &template_plugin_send;
312 api->cancel = &template_plugin_cancel;
313 api->address_pretty_printer = &template_plugin_address_pretty_printer;
314 api->set_receive_quota = &template_plugin_set_receive_quota;
315 api->address_suggested = &template_plugin_address_suggested;
316 api->cost_estimate = 42; // FIXME
317 return api;
318}
319
320
321/**
322 * Exit point from the plugin.
323 */
324void *
325gnunet_plugin_transport_template_done (void *cls)
326{
327 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
328 struct Plugin *plugin = api->cls;
329
330 GNUNET_free (plugin);
331 GNUNET_free (api);
332 return NULL;
333}
334
335/* end of plugin_transport_template.c */
diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c
new file mode 100644
index 000000000..ccaf9fbd1
--- /dev/null
+++ b/src/transport/plugin_transport_udp.c
@@ -0,0 +1,592 @@
1/*
2 This file is part of GNUnet
3 (C) 2001, 2002, 2003, 2004, 2005, 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 transports/udp.c
23 * @brief Implementation of the UDP transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "gnunet_protocols.h"
30#include "gnunet_transport.h"
31#include "gnunet_stats_service.h"
32#include "gnunet_upnp_service.h"
33#include "ip.h"
34
35#define DEBUG_UDP GNUNET_YES
36
37/**
38 * The default maximum size of each outbound UDP message,
39 * optimal value for Ethernet (10 or 100 MBit).
40 */
41#define MESSAGE_SIZE 1472
42
43/**
44 * Message-Packet header.
45 */
46typedef struct
47{
48 /**
49 * size of the message, in bytes, including this header.
50 */
51 GNUNET_MessageHeader header;
52
53 /**
54 * What is the identity of the sender (GNUNET_hash of public key)
55 */
56 GNUNET_PeerIdentity sender;
57
58} UDPMessage;
59
60#define MY_TRANSPORT_NAME "UDP"
61#include "common.c"
62
63/* *********** globals ************* */
64
65static int stat_bytesReceived;
66
67static int stat_bytesSent;
68
69static int stat_bytesDropped;
70
71static int stat_udpConnected;
72
73/**
74 * thread that listens for inbound messages
75 */
76static struct GNUNET_SelectHandle *selector;
77
78/**
79 * the socket that we transmit all data with
80 */
81static struct GNUNET_SocketHandle *udp_sock;
82
83static struct GNUNET_LoadMonitor *load_monitor;
84
85
86/**
87 * The socket of session has data waiting, process!
88 *
89 * This function may only be called if the tcplock is
90 * already held by the caller.
91 */
92static int
93select_message_handler (void *mh_cls,
94 struct GNUNET_SelectHandle *sh,
95 struct GNUNET_SocketHandle *sock,
96 void *sock_ctx, const GNUNET_MessageHeader * msg)
97{
98 unsigned int len;
99 GNUNET_TransportPacket *mp;
100 const UDPMessage *um;
101
102 len = ntohs (msg->size);
103 if (len <= sizeof (UDPMessage))
104 {
105 GNUNET_GE_LOG (coreAPI->ectx,
106 GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK,
107 _("Received malformed message via %s. Ignored.\n"),
108 "UDP");
109 return GNUNET_SYSERR;
110 }
111 um = (const UDPMessage *) msg;
112 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
113 mp->msg = GNUNET_malloc (len - sizeof (UDPMessage));
114 memcpy (mp->msg, &um[1], len - sizeof (UDPMessage));
115 mp->sender = um->sender;
116 mp->size = len - sizeof (UDPMessage);
117 mp->tsession = NULL;
118 coreAPI->receive (mp);
119 if (stats != NULL)
120 stats->change (stat_bytesReceived, len);
121 return GNUNET_OK;
122}
123
124static void *
125select_accept_handler (void *ah_cls,
126 struct GNUNET_SelectHandle *sh,
127 struct GNUNET_SocketHandle *sock,
128 const void *addr, unsigned int addr_len)
129{
130 static int nonnullpointer;
131
132 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
133 return NULL;
134 return &nonnullpointer;
135}
136
137/**
138 * Select has been forced to close a connection.
139 * Free the associated context.
140 */
141static void
142select_close_handler (void *ch_cls,
143 struct GNUNET_SelectHandle *sh,
144 struct GNUNET_SocketHandle *sock, void *sock_ctx)
145{
146 /* do nothing */
147}
148
149/**
150 * Establish a connection to a remote node.
151 *
152 * @param hello the hello-Message for the target node
153 * @param tsessionPtr the session handle that is to be set
154 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
155 */
156static int
157udp_connect (const GNUNET_MessageHello * hello,
158 GNUNET_TSession ** tsessionPtr, int may_reuse)
159{
160 GNUNET_TSession *tsession;
161
162 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
163 memset (tsession, 0, sizeof (GNUNET_TSession));
164 tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello));
165 memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello));
166 tsession->ttype = myAPI.protocol_number;
167 tsession->peer = hello->senderIdentity;
168 *tsessionPtr = tsession;
169 if (stats != NULL)
170 stats->change (stat_udpConnected, 1);
171 return GNUNET_OK;
172}
173
174/**
175 * A (core) Session is to be associated with a transport session. The
176 * transport service may want to know in order to call back on the
177 * core if the connection is being closed.
178 *
179 * @param tsession the session handle passed along
180 * from the call to receive that was made by the transport
181 * layer
182 * @return GNUNET_OK if the session could be associated,
183 * GNUNET_SYSERR if not.
184 */
185int
186udp_associate (GNUNET_TSession * tsession)
187{
188 return GNUNET_SYSERR; /* UDP connections can never be associated */
189}
190
191/**
192 * Disconnect from a remote node.
193 *
194 * @param tsession the session that is closed
195 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
196 */
197static int
198udp_disconnect (GNUNET_TSession * tsession)
199{
200 if (tsession != NULL)
201 {
202 if (tsession->internal != NULL)
203 GNUNET_free (tsession->internal);
204 GNUNET_free (tsession);
205 if (stats != NULL)
206 stats->change (stat_udpConnected, -1);
207 }
208 return GNUNET_OK;
209}
210
211/**
212 * Shutdown the server process (stop receiving inbound traffic). Maybe
213 * restarted later!
214 */
215static int
216udp_transport_server_stop ()
217{
218 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
219 if (selector != NULL)
220 {
221 GNUNET_select_destroy (selector);
222 selector = NULL;
223 }
224 GNUNET_socket_destroy (udp_sock);
225 udp_sock = NULL;
226 return GNUNET_OK;
227}
228
229/**
230 * Test if the transport would even try to send
231 * a message of the given size and importance
232 * for the given session.<br>
233 * This function is used to check if the core should
234 * even bother to construct (and encrypt) this kind
235 * of message.
236 *
237 * @return GNUNET_YES if the transport would try (i.e. queue
238 * the message or call the OS to send),
239 * GNUNET_NO if the transport would just drop the message,
240 * GNUNET_SYSERR if the size/session is invalid
241 */
242static int
243udp_test_would_try (GNUNET_TSession * tsession, unsigned int size,
244 int important)
245{
246 const GNUNET_MessageHello *hello;
247
248 if (udp_sock == NULL)
249 return GNUNET_SYSERR;
250 if (size == 0)
251 {
252 GNUNET_GE_BREAK (coreAPI->ectx, 0);
253 return GNUNET_SYSERR;
254 }
255 if (size > myAPI.mtu)
256 {
257 GNUNET_GE_BREAK (coreAPI->ectx, 0);
258 return GNUNET_SYSERR;
259 }
260 hello = (const GNUNET_MessageHello *) tsession->internal;
261 if (hello == NULL)
262 return GNUNET_SYSERR;
263 return GNUNET_YES;
264}
265
266/**
267 * Create a UDP socket. If possible, use IPv6, otherwise
268 * try IPv4. Update available_protocols accordingly.
269 */
270static int
271udp_create_socket ()
272{
273 int s;
274
275 available_protocols = VERSION_AVAILABLE_NONE;
276 s = -1;
277 if (GNUNET_YES !=
278 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", "DISABLE-IPV6",
279 GNUNET_YES))
280 {
281#ifndef MINGW
282 s = SOCKET (PF_INET6, SOCK_DGRAM, 17);
283#else
284 s = win_ols_socket (PF_INET6, SOCK_DGRAM, 17);
285#endif
286 }
287 if (s < 0)
288 {
289#ifndef MINGW
290 s = SOCKET (PF_INET, SOCK_DGRAM, 17);
291#else
292 s = win_ols_socket (PF_INET, SOCK_DGRAM, 17);
293#endif
294 if (s < 0)
295 {
296 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
297 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
298 GNUNET_GE_BULK, "socket");
299 return GNUNET_SYSERR;
300 }
301 available_protocols = VERSION_AVAILABLE_IPV4;
302 }
303 else
304 {
305 available_protocols = VERSION_AVAILABLE_IPV6 | VERSION_AVAILABLE_IPV4;
306 }
307 return s;
308}
309
310/**
311 * Send a message to the specified remote node.
312 *
313 * @param tsession the GNUNET_MessageHello identifying the remote node
314 * @param message what to send
315 * @param size the size of the message
316 * @return GNUNET_SYSERR on error, GNUNET_OK on success
317 */
318static int
319udp_send (GNUNET_TSession * tsession,
320 const void *message, const unsigned int size, int important)
321{
322 const GNUNET_MessageHello *hello;
323 const HostAddress *haddr;
324 UDPMessage *mp;
325 struct sockaddr_in serverAddrv4;
326 struct sockaddr_in6 serverAddrv6;
327 struct sockaddr *serverAddr;
328 socklen_t addrlen;
329 unsigned short available;
330 int ok;
331 int ssize;
332 size_t sent;
333
334 GNUNET_GE_ASSERT (NULL, tsession != NULL);
335 if (udp_sock == NULL)
336 return GNUNET_SYSERR;
337 if (size == 0)
338 {
339 GNUNET_GE_BREAK (coreAPI->ectx, 0);
340 return GNUNET_SYSERR;
341 }
342 if (size > myAPI.mtu)
343 {
344 GNUNET_GE_BREAK (coreAPI->ectx, 0);
345 return GNUNET_SYSERR;
346 }
347 hello = (const GNUNET_MessageHello *) tsession->internal;
348 if (hello == NULL)
349 return GNUNET_SYSERR;
350
351 haddr = (const HostAddress *) &hello[1];
352 available = ntohs (haddr->availability) & available_protocols;
353 if (available == VERSION_AVAILABLE_NONE)
354 return GNUNET_SYSERR;
355 if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6))
356 {
357 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
358 available = VERSION_AVAILABLE_IPV4;
359 else
360 available = VERSION_AVAILABLE_IPV6;
361 }
362 ssize = size + sizeof (UDPMessage);
363 mp = GNUNET_malloc (ssize);
364 mp->header.size = htons (ssize);
365 mp->header.type = 0;
366 mp->sender = *(coreAPI->my_identity);
367 memcpy (&mp[1], message, size);
368 ok = GNUNET_SYSERR;
369
370 if ((available & VERSION_AVAILABLE_IPV4) > 0)
371 {
372 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
373 serverAddrv4.sin_family = AF_INET;
374 serverAddrv4.sin_port = haddr->port;
375 memcpy (&serverAddrv4.sin_addr, &haddr->ipv4, sizeof (struct in_addr));
376 addrlen = sizeof (serverAddrv4);
377 serverAddr = (struct sockaddr *) &serverAddrv4;
378 }
379 else
380 {
381 memset (&serverAddrv6, 0, sizeof (serverAddrv6));
382 serverAddrv6.sin6_family = AF_INET;
383 serverAddrv6.sin6_port = haddr->port;
384 memcpy (&serverAddrv6.sin6_addr, &haddr->ipv6,
385 sizeof (struct in6_addr));
386 addrlen = sizeof (serverAddrv6);
387 serverAddr = (struct sockaddr *) &serverAddrv6;
388 }
389#ifndef MINGW
390 if (GNUNET_YES == GNUNET_socket_send_to (udp_sock,
391 GNUNET_NC_NONBLOCKING,
392 mp,
393 ssize, &sent,
394 (const char *) serverAddr,
395 addrlen))
396#else
397 sent =
398 win_ols_sendto (udp_sock, mp, ssize, (const char *) serverAddr, addrlen);
399 if (sent != SOCKET_ERROR)
400#endif
401 {
402 ok = GNUNET_OK;
403 if (stats != NULL)
404 stats->change (stat_bytesSent, sent);
405 }
406 else
407 {
408 if (stats != NULL)
409 stats->change (stat_bytesDropped, ssize);
410 }
411 GNUNET_free (mp);
412 return ok;
413}
414
415/**
416 * Start the server process to receive inbound traffic.
417 *
418 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
419 */
420static int
421udp_transport_server_start ()
422{
423 struct sockaddr_in serverAddrv4;
424 struct sockaddr_in6 serverAddrv6;
425 struct sockaddr *serverAddr;
426 socklen_t addrlen;
427 int sock;
428 const int on = 1;
429 unsigned short port;
430
431 GNUNET_GE_ASSERT (coreAPI->ectx, selector == NULL);
432 /* initialize UDP network */
433 port = get_port ();
434 if (port != 0)
435 {
436 sock = udp_create_socket ();
437 if (sock < 0)
438 return GNUNET_SYSERR;
439 if (SETSOCKOPT (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
440 {
441 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
442 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
443 GNUNET_GE_IMMEDIATE, "setsockopt");
444 return GNUNET_SYSERR;
445 }
446 if (available_protocols == VERSION_AVAILABLE_IPV4)
447 {
448 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
449 serverAddrv4.sin_family = AF_INET;
450 serverAddrv4.sin_addr.s_addr = INADDR_ANY;
451 serverAddrv4.sin_port = htons (port);
452 addrlen = sizeof (serverAddrv4);
453 serverAddr = (struct sockaddr *) &serverAddrv4;
454 }
455 else
456 {
457 memset (&serverAddrv6, 0, sizeof (serverAddrv6));
458 serverAddrv6.sin6_family = AF_INET6;
459 serverAddrv6.sin6_addr = in6addr_any;
460 serverAddrv6.sin6_port = htons (port);
461 addrlen = sizeof (serverAddrv6);
462 serverAddr = (struct sockaddr *) &serverAddrv6;
463 }
464 if (BIND (sock, serverAddr, addrlen) < 0)
465 {
466 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
467 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
468 GNUNET_GE_IMMEDIATE, "bind");
469 GNUNET_GE_LOG (coreAPI->ectx,
470 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
471 GNUNET_GE_IMMEDIATE,
472 _("Failed to bind to %s port %d.\n"),
473 MY_TRANSPORT_NAME, port);
474 if (0 != CLOSE (sock))
475 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
476 GNUNET_GE_ERROR | GNUNET_GE_USER |
477 GNUNET_GE_ADMIN | GNUNET_GE_BULK,
478 "close");
479 return GNUNET_SYSERR;
480 }
481 selector = GNUNET_select_create ("udp", GNUNET_YES, coreAPI->ectx, load_monitor, sock, addrlen, 0, /* timeout */
482 &select_message_handler,
483 NULL,
484 &select_accept_handler,
485 NULL,
486 &select_close_handler,
487 NULL, 64 * 1024,
488 16 /* max sockets */ );
489 if (selector == NULL)
490 return GNUNET_SYSERR;
491 }
492 sock = udp_create_socket ();
493 if (sock == -1)
494 {
495 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
496 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
497 GNUNET_GE_BULK, "socket");
498 GNUNET_select_destroy (selector);
499 selector = NULL;
500 return GNUNET_SYSERR;
501 }
502 udp_sock = GNUNET_socket_create (coreAPI->ectx, load_monitor, sock);
503 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
504 return GNUNET_OK;
505}
506
507/**
508 * The exported method. Makes the core api available via a global and
509 * returns the udp transport API.
510 */
511GNUNET_TransportAPI *
512inittransport_udp (GNUNET_CoreAPIForTransport * core)
513{
514 unsigned long long mtu;
515
516 cfg = core->cfg;
517 load_monitor = core->load_monitor;
518 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (UDPMessage) == 68);
519 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
520 coreAPI = core;
521 if (-1 == GNUNET_GC_get_configuration_value_number (cfg,
522 "UDP",
523 "MTU",
524 sizeof (UDPMessage)
525 +
526 GNUNET_P2P_MESSAGE_OVERHEAD
527 +
528 sizeof
529 (GNUNET_MessageHeader) +
530 32, 65500,
531 MESSAGE_SIZE, &mtu))
532 {
533 return NULL;
534 }
535 if (mtu < 1200)
536 GNUNET_GE_LOG (coreAPI->ectx,
537 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
538 _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP");
539 lock = GNUNET_mutex_create (GNUNET_NO);
540 if (0 !=
541 GNUNET_GC_attach_change_listener (cfg, &reload_configuration, NULL))
542 {
543 GNUNET_mutex_destroy (lock);
544 lock = NULL;
545 return NULL;
546 }
547 if (GNUNET_GC_get_configuration_value_yesno (cfg, "UDP", "UPNP", GNUNET_YES)
548 == GNUNET_YES)
549 {
550 upnp = coreAPI->service_request ("upnp");
551 if (upnp == NULL)
552 GNUNET_GE_LOG (coreAPI->ectx,
553 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
554 "The UPnP service could not be loaded. To disable UPnP, set the "
555 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n",
556 "UDP");
557 }
558 stats = coreAPI->service_request ("stats");
559 if (stats != NULL)
560 {
561 stat_bytesReceived
562 = stats->create (gettext_noop ("# bytes received via UDP"));
563 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via UDP"));
564 stat_bytesDropped
565 = stats->create (gettext_noop ("# bytes dropped by UDP (outgoing)"));
566 stat_udpConnected
567 = stats->create (gettext_noop ("# UDP connections (right now)"));
568 }
569 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_UDP;
570 myAPI.mtu = mtu - sizeof (UDPMessage);
571 myAPI.cost = 20000;
572 myAPI.hello_verify = &verify_hello;
573 myAPI.hello_create = &create_hello;
574 myAPI.connect = &udp_connect;
575 myAPI.send = &udp_send;
576 myAPI.associate = &udp_associate;
577 myAPI.disconnect = &udp_disconnect;
578 myAPI.server_start = &udp_transport_server_start;
579 myAPI.server_stop = &udp_transport_server_stop;
580 myAPI.hello_to_address = &hello_to_address;
581 myAPI.send_now_test = &udp_test_would_try;
582
583 return &myAPI;
584}
585
586void
587donetransport_udp ()
588{
589 do_shutdown ();
590}
591
592/* end of udp.c */
diff --git a/src/transport/test_transport_api.c b/src/transport/test_transport_api.c
new file mode 100644
index 000000000..02c28b09c
--- /dev/null
+++ b/src/transport/test_transport_api.c
@@ -0,0 +1,305 @@
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 transport/test_transport_api.c
22 * @brief testcase for transport_api.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_hello_lib.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_os_lib.h"
29#include "gnunet_program_lib.h"
30#include "gnunet_scheduler_lib.h"
31#include "gnunet_transport_service.h"
32#include "transport.h"
33
34#define VERBOSE GNUNET_NO
35
36#define START_ARM GNUNET_YES
37
38/**
39 * How long until we give up on transmitting the message?
40 */
41#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
42
43#define MTYPE 12345
44
45struct PeerContext
46{
47 struct GNUNET_CONFIGURATION_Handle *cfg;
48 struct GNUNET_TRANSPORT_Handle *th;
49 struct GNUNET_PeerIdentity id;
50#if START_ARM
51 pid_t arm_pid;
52#endif
53};
54
55static struct PeerContext p1;
56
57static struct PeerContext p2;
58
59static struct GNUNET_SCHEDULER_Handle *sched;
60
61static int ok;
62
63#if VERBOSE
64#define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
65#else
66#define OKPP do { ok++; } while (0)
67#endif
68
69
70static void
71end ()
72{
73 /* do work here */
74 GNUNET_assert (ok == 8);
75 GNUNET_TRANSPORT_disconnect (p1.th);
76 GNUNET_TRANSPORT_disconnect (p2.th);
77 ok = 0;
78}
79
80
81/**
82 * Function called by the transport for each received message.
83 *
84 * @param cls closure
85 * @param latency estimated latency for communicating with the
86 * given peer
87 * @param peer (claimed) identity of the other peer
88 * @param message the message
89 */
90static void
91notify_receive (void *cls,
92 struct GNUNET_TIME_Relative latency,
93 const struct GNUNET_PeerIdentity *peer,
94 const struct GNUNET_MessageHeader *message)
95{
96 GNUNET_assert (ok == 7);
97 OKPP;
98 GNUNET_assert (MTYPE == ntohs (message->type));
99 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) ==
100 ntohs (message->size));
101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received message from peer (%p)!\n",
102 cls);
103 end ();
104}
105
106
107/**
108 * Function called to notify transport users that another
109 * peer connected to us.
110 *
111 * @param cls closure
112 * @param transport the transport service handle
113 * @param peer the peer that disconnected
114 * @param latency current latency of the connection
115 */
116static void
117notify_connect (void *cls,
118 const struct GNUNET_PeerIdentity *peer,
119 struct GNUNET_TIME_Relative latency)
120{
121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer connected to us (%p)!\n", cls);
122 GNUNET_assert ((ok >= 1) && (ok <= 6));
123 OKPP;
124}
125
126
127/**
128 * Function called to notify transport users that another
129 * peer disconnected from us.
130 *
131 * @param cls closure
132 * @param transport the transport service handle
133 * @param peer the peer that disconnected
134 */
135static void
136notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
137{
138 GNUNET_assert (0);
139}
140
141
142static void
143setup_peer (struct PeerContext *p, const char *cfgname)
144{
145 p->cfg = GNUNET_CONFIGURATION_create ();
146#if START_ARM
147 p->arm_pid = GNUNET_OS_start_process ("gnunet-service-arm",
148 "gnunet-service-arm",
149#if VERBOSE
150 "-L", "DEBUG",
151#endif
152 "-c", cfgname, NULL);
153 sleep (1); /* allow ARM to start */
154#endif
155 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
156 p->th = GNUNET_TRANSPORT_connect (sched, p->cfg,
157 p,
158 &notify_receive,
159 &notify_connect, &notify_disconnect);
160 GNUNET_assert (p->th != NULL);
161}
162
163
164static size_t
165notify_ready (void *cls, size_t size, void *buf)
166{
167 struct GNUNET_MessageHeader *hdr;
168
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
170 "Transmitting message to peer (%p) - %u!\n", cls, size);
171 GNUNET_assert (size >= 256);
172 GNUNET_assert ((ok >= 5) && (ok <= 6));
173 OKPP;
174 hdr = buf;
175 hdr->size = htons (sizeof (struct GNUNET_MessageHeader));
176 hdr->type = htons (MTYPE);
177 return sizeof (struct GNUNET_MessageHeader);
178}
179
180
181static void
182exchange_hello_last (void *cls,
183 struct GNUNET_TIME_Relative latency,
184 const struct GNUNET_PeerIdentity *peer,
185 const struct GNUNET_MessageHeader *message)
186{
187 struct PeerContext *me = cls;
188 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
189
190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191 "Exchanging HELLO with peer (%p)!\n", cls);
192 GNUNET_assert (ok >= 3);
193 OKPP;
194 GNUNET_assert (message != NULL);
195 GNUNET_assert (GNUNET_OK ==
196 GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *)
197 message, &pk));
198 GNUNET_CRYPTO_hash (&pk,
199 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
200 &me->id.hashPubKey);
201 GNUNET_TRANSPORT_offer_hello (p1.th, message);
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203 "Finished exchanging HELLOs, now waiting for transmission!\n");
204 /* both HELLOs exchanged, get ready to test transmission! */
205 GNUNET_TRANSPORT_notify_transmit_ready (p1.th,
206 &p2.id,
207 256, TIMEOUT, &notify_ready, &p1);
208}
209
210
211static void
212exchange_hello (void *cls,
213 struct GNUNET_TIME_Relative latency,
214 const struct GNUNET_PeerIdentity *peer,
215 const struct GNUNET_MessageHeader *message)
216{
217 struct PeerContext *me = cls;
218 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
219
220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221 "Exchanging HELLO with peer (%p)!\n", cls);
222 GNUNET_assert (ok >= 2);
223 OKPP;
224 GNUNET_assert (message != NULL);
225 GNUNET_assert (GNUNET_OK ==
226 GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *)
227 message, &pk));
228 GNUNET_CRYPTO_hash (&pk,
229 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
230 &me->id.hashPubKey);
231 GNUNET_TRANSPORT_get_hello (p2.th, GNUNET_TIME_UNIT_MINUTES,
232 &exchange_hello_last, &p2);
233}
234
235
236static void
237run (void *cls,
238 struct GNUNET_SCHEDULER_Handle *s,
239 char *const *args,
240 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
241{
242 GNUNET_assert (ok == 1);
243 OKPP;
244 sched = s;
245 setup_peer (&p1, "test_transport_api_peer1.conf");
246 setup_peer (&p2, "test_transport_api_peer2.conf");
247 GNUNET_TRANSPORT_get_hello (p1.th,
248 GNUNET_TIME_UNIT_MINUTES, &exchange_hello, &p1);
249}
250
251
252static void
253stop_arm (struct PeerContext *p)
254{
255#if START_ARM
256 if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
257 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
258 waitpid (p->arm_pid, NULL, 0);
259#endif
260 GNUNET_CONFIGURATION_destroy (p->cfg);
261}
262
263
264static int
265check ()
266{
267 char *const argv[] = { "test-transport-api",
268 "-c",
269 "test_transport_api_data.conf",
270#if VERBOSE
271 "-L", "DEBUG",
272#endif
273 NULL
274 };
275 struct GNUNET_GETOPT_CommandLineOption options[] = {
276 GNUNET_GETOPT_OPTION_END
277 };
278
279 ok = 1;
280 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
281 argv, "test-transport-api", "nohelp",
282 options, &run, &ok);
283 stop_arm (&p1);
284 stop_arm (&p2);
285 return ok;
286}
287
288int
289main (int argc, char *argv[])
290{
291 int ret;
292
293 GNUNET_log_setup ("test-transport-api",
294#if VERBOSE
295 "DEBUG",
296#else
297 "WARNING",
298#endif
299 NULL);
300 ret = check ();
301
302 return ret;
303}
304
305/* end of test_transport_api.c */
diff --git a/src/transport/test_transport_api_data.conf b/src/transport/test_transport_api_data.conf
new file mode 100644
index 000000000..0fa611350
--- /dev/null
+++ b/src/transport/test_transport_api_data.conf
@@ -0,0 +1,24 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-transport-master/
3
4[resolver]
5PORT = 2364
6
7[transport]
8PORT = 2365
9PLUGINS = tcp
10
11[arm]
12PORT = 2366
13
14[statistics]
15PORT = 2367
16
17[tcp]
18PORT = 2368
19
20[peerinfo]
21PORT = 2369
22
23[testing]
24WEAKRANDOM = YES
diff --git a/src/transport/test_transport_api_peer1.conf b/src/transport/test_transport_api_peer1.conf
new file mode 100644
index 000000000..dcc0ab4cf
--- /dev/null
+++ b/src/transport/test_transport_api_peer1.conf
@@ -0,0 +1,25 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-transport-peer-1/
3DEFAULTCONFIG = test_transport_api_peer1.conf
4
5[resolver]
6PORT = 12364
7
8[transport]
9PORT = 12365
10PLUGINS = tcp
11
12[arm]
13PORT = 12366
14
15[statistics]
16PORT = 12367
17
18[tcp]
19PORT = 12368
20
21[peerinfo]
22PORT = 12369
23
24[testing]
25WEAKRANDOM = YES
diff --git a/src/transport/test_transport_api_peer2.conf b/src/transport/test_transport_api_peer2.conf
new file mode 100644
index 000000000..8567c6ac8
--- /dev/null
+++ b/src/transport/test_transport_api_peer2.conf
@@ -0,0 +1,25 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/
3DEFAULTCONFIG = test_transport_api_peer2.conf
4
5[resolver]
6PORT = 22364
7
8[transport]
9PORT = 22365
10PLUGINS = tcp
11
12[arm]
13PORT = 22366
14
15[statistics]
16PORT = 22367
17
18[tcp]
19PORT = 22368
20
21[peerinfo]
22PORT = 22369
23
24[testing]
25WEAKRANDOM = YES
diff --git a/src/transport/transport.h b/src/transport/transport.h
new file mode 100644
index 000000000..8e1291005
--- /dev/null
+++ b/src/transport/transport.h
@@ -0,0 +1,238 @@
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 transport/transport.h
23 * @brief common internal definitions for transport service
24 * @author Christian Grothoff
25 */
26#include "gnunet_crypto_lib.h"
27#include "gnunet_time_lib.h"
28#include "gnunet_transport_service.h"
29
30#define DEBUG_TRANSPORT GNUNET_NO
31
32/**
33 * For how long do we allow unused bandwidth
34 * from the past to carry over into the future? (in ms)
35 */
36#define MAX_BANDWIDTH_CARRY 5000
37
38/**
39 * How often do we (at most) do a full quota
40 * recalculation? (in ms)
41 */
42#define MIN_QUOTA_REFRESH_TIME 2000
43
44/**
45 * Message from the transport service to the library
46 * informing about neighbors.
47 */
48struct ConnectInfoMessage
49{
50
51 /**
52 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT
53 */
54 struct GNUNET_MessageHeader header;
55
56 /**
57 * Current quota for outbound traffic in bytes/ms.
58 * (should be equal to system default)
59 */
60 uint32_t quota_out GNUNET_PACKED;
61
62 /**
63 * Latency estimate.
64 */
65 struct GNUNET_TIME_RelativeNBO latency;
66
67 /**
68 * Identity of the new neighbour.
69 */
70 struct GNUNET_PeerIdentity id;
71
72};
73
74
75/**
76 * Message from the transport service to the library
77 * informing about disconnects.
78 */
79struct DisconnectInfoMessage
80{
81
82 /**
83 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT
84 */
85 struct GNUNET_MessageHeader header;
86
87 /**
88 * Reserved, always zero.
89 */
90 uint32_t reserved GNUNET_PACKED;
91
92 /**
93 * Who got disconnected?
94 */
95 struct GNUNET_PeerIdentity peer;
96
97};
98
99
100/**
101 * Message used to set a particular bandwidth quota. Send
102 * TO the service to set an incoming quota, send FROM the
103 * service to update an outgoing quota.
104 */
105struct QuotaSetMessage
106{
107
108 /**
109 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_NEIGHBOUR_INFO
110 */
111 struct GNUNET_MessageHeader header;
112
113 /**
114 * Quota in bytes per ms, 0 to drop everything;
115 * in network byte order.
116 */
117 uint32_t quota_in GNUNET_PACKED;
118
119 /**
120 * About which peer are we talking here?
121 */
122 struct GNUNET_PeerIdentity peer;
123
124};
125
126
127/**
128 * Message used to ask the transport service to connect
129 * to a particular peer.
130 */
131struct TryConnectMessage
132{
133
134 /**
135 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT.
136 */
137 struct GNUNET_MessageHeader header;
138
139 /**
140 * Always zero.
141 */
142 uint32_t reserved GNUNET_PACKED;
143
144 /**
145 * About which peer are we talking here?
146 */
147 struct GNUNET_PeerIdentity peer;
148
149};
150
151/**
152 * Message used to notify the transport API about a message
153 * received from the network. The actual message follows.
154 */
155struct InboundMessage
156{
157
158 /**
159 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_RECV
160 */
161 struct GNUNET_MessageHeader header;
162
163 /**
164 * Always zero.
165 */
166 uint32_t reserved GNUNET_PACKED;
167
168 /**
169 * Latency estimate.
170 */
171 struct GNUNET_TIME_RelativeNBO latency;
172
173 /**
174 * Which peer sent the message?
175 */
176 struct GNUNET_PeerIdentity peer;
177
178};
179
180
181/**
182 * Message used to notify the transport API that it can
183 * send another message to the transport service.
184 */
185struct SendOkMessage
186{
187
188 /**
189 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK
190 */
191 struct GNUNET_MessageHeader header;
192
193 /**
194 * GNUNET_OK if the transmission succeeded,
195 * GNUNET_SYSERR if it failed (i.e. network disconnect);
196 * in either case, it is now OK for this client to
197 * send us another message for the given peer.
198 */
199 uint32_t success GNUNET_PACKED;
200
201 /**
202 * Which peer can send more now?
203 */
204 struct GNUNET_PeerIdentity peer;
205
206};
207
208
209/**
210 * Message used to notify the transport service about a message
211 * to be transmitted to another peer. The actual message follows.
212 */
213struct OutboundMessage
214{
215
216 /**
217 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_SEND
218 */
219 struct GNUNET_MessageHeader header;
220
221 /**
222 * Always zero.
223 */
224 uint32_t reserved GNUNET_PACKED;
225
226 /**
227 * Which peer should receive the message?
228 */
229 struct GNUNET_PeerIdentity peer;
230
231};
232
233
234
235
236
237
238/* end of transport.h */
diff --git a/src/transport/transport_api.c b/src/transport/transport_api.c
new file mode 100644
index 000000000..d6d4e2a96
--- /dev/null
+++ b/src/transport/transport_api.c
@@ -0,0 +1,1863 @@
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 transport/transport_api.c
23 * @brief library to access the low-level P2P IO service
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - set_quota with low bandwidth should cause peer
28 * disconnects (currently never does that) (MINOR)
29 */
30#include "platform.h"
31#include "gnunet_client_lib.h"
32#include "gnunet_arm_service.h"
33#include "gnunet_hello_lib.h"
34#include "gnunet_protocols.h"
35#include "gnunet_server_lib.h"
36#include "gnunet_time_lib.h"
37#include "gnunet_transport_service.h"
38#include "transport.h"
39
40/**
41 * After how long do we give up on transmitting a HELLO
42 * to the service?
43 */
44#define OFFER_HELLO_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
45
46/**
47 * How long should ARM wait when starting up the
48 * transport service before reporting back?
49 */
50#define START_SERVICE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
51
52/**
53 * How long should ARM wait when stopping the
54 * transport service before reporting back?
55 */
56#define STOP_SERVICE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
57
58/**
59 * Entry in linked list of all of our current neighbours.
60 */
61struct NeighbourList
62{
63
64 /**
65 * This is a linked list.
66 */
67 struct NeighbourList *next;
68
69 /**
70 * Active transmit handle, can be NULL. Used to move
71 * from ready to wait list on disconnect and to block
72 * two transmissions to the same peer from being scheduled
73 * at the same time.
74 */
75 struct GNUNET_TRANSPORT_TransmitHandle *transmit_handle;
76
77
78 /**
79 * Identity of this neighbour.
80 */
81 struct GNUNET_PeerIdentity id;
82
83 /**
84 * At what time did we reset last_sent last?
85 */
86 struct GNUNET_TIME_Absolute last_quota_update;
87
88 /**
89 * How many bytes have we sent since the "last_quota_update"
90 * timestamp?
91 */
92 uint64_t last_sent;
93
94 /**
95 * Global quota for outbound traffic to the neighbour in bytes/ms.
96 */
97 uint32_t quota_out;
98
99 /**
100 * Set to GNUNET_YES if we are currently allowed to
101 * transmit a message to the transport service for this
102 * peer, GNUNET_NO otherwise.
103 */
104 int transmit_ok;
105
106 /**
107 * Set to GNUNET_YES if we have received an ACK for the
108 * given peer. Peers that receive our HELLO always respond
109 * with an ACK to let us know that we are successfully
110 * communicating. Note that a PING can not be used for this
111 * since PINGs are only send if a HELLO address requires
112 * confirmation (and also, PINGs are not passed to the
113 * transport API itself).
114 */
115 int received_ack;
116
117};
118
119
120/**
121 * Linked list of requests from clients for our HELLO
122 * that were deferred.
123 */
124struct HelloWaitList
125{
126
127 /**
128 * This is a linked list.
129 */
130 struct HelloWaitList *next;
131
132 /**
133 * Reference back to our transport handle.
134 */
135 struct GNUNET_TRANSPORT_Handle *handle;
136
137 /**
138 * Callback to call once we got our HELLO.
139 */
140 GNUNET_TRANSPORT_ReceiveCallback rec;
141
142 /**
143 * Closure for rec.
144 */
145 void *rec_cls;
146
147 /**
148 * When to time out (call rec with NULL).
149 */
150 struct GNUNET_TIME_Absolute timeout;
151
152 /**
153 * Timeout task (used to trigger timeout,
154 * cancel if we get the HELLO in time).
155 */
156 GNUNET_SCHEDULER_TaskIdentifier task;
157
158
159};
160
161
162/**
163 * Opaque handle for a transmission-ready request.
164 */
165struct GNUNET_TRANSPORT_TransmitHandle
166{
167
168 /**
169 * We keep the transmit handles that are waiting for
170 * a transport-level connection in a doubly linked list.
171 */
172 struct GNUNET_TRANSPORT_TransmitHandle *next;
173
174 /**
175 * We keep the transmit handles that are waiting for
176 * a transport-level connection in a doubly linked list.
177 */
178 struct GNUNET_TRANSPORT_TransmitHandle *prev;
179
180 /**
181 * Handle of the main transport data structure.
182 */
183 struct GNUNET_TRANSPORT_Handle *handle;
184
185 /**
186 * Neighbour for this handle, can be NULL if the service
187 * is not yet connected to the target.
188 */
189 struct NeighbourList *neighbour;
190
191 /**
192 * Which peer is this transmission going to be for? All
193 * zeros if it is control-traffic to the service.
194 */
195 struct GNUNET_PeerIdentity target;
196
197 /**
198 * Function to call when notify_size bytes are available
199 * for transmission.
200 */
201 GNUNET_NETWORK_TransmitReadyNotify notify;
202
203 /**
204 * Closure for notify.
205 */
206 void *notify_cls;
207
208 /**
209 * transmit_ready task Id. The task is used to introduce
210 * the artificial delay that may be required to maintain
211 * the bandwidth limits.
212 */
213 GNUNET_SCHEDULER_TaskIdentifier notify_delay_task;
214
215 /**
216 * Timeout for this request.
217 */
218 struct GNUNET_TIME_Absolute timeout;
219
220 /**
221 * How many bytes is our notify callback waiting for?
222 */
223 size_t notify_size;
224
225};
226
227
228/**
229 * Handle for the transport service (includes all of the
230 * state for the transport service).
231 */
232struct GNUNET_TRANSPORT_Handle
233{
234
235 /**
236 * Closure for the callbacks.
237 */
238 void *cls;
239
240 /**
241 * Function to call for received data.
242 */
243 GNUNET_TRANSPORT_ReceiveCallback rec;
244
245 /**
246 * function to call on connect events
247 */
248 GNUNET_TRANSPORT_NotifyConnect nc_cb;
249
250 /**
251 * function to call on disconnect events
252 */
253 GNUNET_TRANSPORT_NotifyDisconnect nd_cb;
254
255 /**
256 * The current HELLO message for this peer. Updated
257 * whenever transports change their addresses.
258 */
259 struct GNUNET_HELLO_Message *my_hello;
260
261 /**
262 * My client connection to the transport service.
263 */
264 struct GNUNET_CLIENT_Connection *client;
265
266 /**
267 * Handle to our registration with the client for notification.
268 */
269 struct GNUNET_NETWORK_TransmitHandle *network_handle;
270
271 /**
272 * Linked list of transmit handles that are waiting for the
273 * transport to connect to the respective peer. When we
274 * receive notification that the transport connected to a
275 * peer, we go over this list and check if someone has already
276 * requested a transmission to the new peer; if so, we trigger
277 * the next step.
278 */
279 struct GNUNET_TRANSPORT_TransmitHandle *connect_wait_head;
280
281 /**
282 * Linked list of transmit handles that are waiting for the
283 * transport to be ready for transmission to the respective
284 * peer. When we
285 * receive notification that the transport disconnected from
286 * a peer, we go over this list and move the entry back to
287 * the connect_wait list.
288 */
289 struct GNUNET_TRANSPORT_TransmitHandle *connect_ready_head;
290
291 /**
292 * Linked list of pending requests for our HELLO.
293 */
294 struct HelloWaitList *hwl_head;
295
296 /**
297 * My scheduler.
298 */
299 struct GNUNET_SCHEDULER_Handle *sched;
300
301 /**
302 * My configuration.
303 */
304 struct GNUNET_CONFIGURATION_Handle *cfg;
305
306 /**
307 * Linked list of the current neighbours of this peer.
308 */
309 struct NeighbourList *neighbours;
310
311 /**
312 * ID of the task trying to reconnect to the
313 * service.
314 */
315 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
316
317 /**
318 * Delay until we try to reconnect.
319 */
320 struct GNUNET_TIME_Relative reconnect_delay;
321
322 /**
323 * Do we currently have a transmission pending?
324 * (schedule transmission was called but has not
325 * yet succeeded)?
326 */
327 int transmission_scheduled;
328};
329
330
331static struct NeighbourList *
332find_neighbour (struct GNUNET_TRANSPORT_Handle *h,
333 const struct GNUNET_PeerIdentity *peer)
334{
335 struct NeighbourList *pos;
336
337 pos = h->neighbours;
338 while ((pos != NULL) &&
339 (0 != memcmp (peer, &pos->id, sizeof (struct GNUNET_PeerIdentity))))
340 pos = pos->next;
341 return pos;
342}
343
344
345/**
346 * Schedule the task to send one message from the
347 * connect_ready list to the service.
348 */
349static void schedule_transmission (struct GNUNET_TRANSPORT_Handle *h);
350
351
352/**
353 * Transmit message to client...
354 */
355static size_t
356transport_notify_ready (void *cls, size_t size, void *buf)
357{
358 struct GNUNET_TRANSPORT_Handle *h = cls;
359 struct GNUNET_TRANSPORT_TransmitHandle *th;
360 struct NeighbourList *n;
361 size_t ret;
362 char *cbuf;
363
364#if DEBUG_TRANSPORT
365 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366 "Ready to transmit %u bytes to transport service\n", size);
367#endif
368 h->network_handle = NULL;
369 h->transmission_scheduled = GNUNET_NO;
370 if (buf == NULL)
371 {
372 th = h->connect_ready_head;
373 if (th->next != NULL)
374 th->next->prev = NULL;
375 h->connect_ready_head = th->next;
376 if (NULL != (n = th->neighbour))
377 {
378 GNUNET_assert (n->transmit_handle == th);
379 n->transmit_handle = NULL;
380 }
381 GNUNET_assert (0 == th->notify (th->notify_cls, 0, NULL));
382 GNUNET_free (th);
383 return 0;
384 }
385 cbuf = buf;
386 ret = 0;
387 h->network_handle = NULL;
388 h->transmission_scheduled = GNUNET_NO;
389 do
390 {
391 th = h->connect_ready_head;
392 GNUNET_assert (th->notify_size <= size);
393 if (th->next != NULL)
394 th->next->prev = NULL;
395 h->connect_ready_head = th->next;
396 if (NULL != (n = th->neighbour))
397 {
398 GNUNET_assert (n->transmit_handle == th);
399 n->transmit_handle = NULL;
400 }
401 ret += th->notify (th->notify_cls, size, &cbuf[ret]);
402 GNUNET_free (th);
403 if (n != NULL)
404 n->last_sent += ret;
405 size -= ret;
406 }
407 while ((h->connect_ready_head != NULL) &&
408 (h->connect_ready_head->notify_size <= size));
409 if (h->connect_ready_head != NULL)
410 schedule_transmission (h);
411#if DEBUG_TRANSPORT
412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413 "Transmitting %u bytes to transport service\n", ret);
414#endif
415 return ret;
416}
417
418
419/**
420 * Schedule the task to send one message from the
421 * connect_ready list to the service.
422 */
423static void
424schedule_transmission (struct GNUNET_TRANSPORT_Handle *h)
425{
426 struct GNUNET_TRANSPORT_TransmitHandle *th;
427
428 GNUNET_assert (NULL == h->network_handle);
429 if (h->client == NULL)
430 {
431#if DEBUG_TRANSPORT
432 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
433 "Not yet connected to transport service, need to wait.\n");
434#endif
435 return;
436 }
437 th = h->connect_ready_head;
438 if (th == NULL)
439 {
440#if DEBUG_TRANSPORT
441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442 "Schedule transmission called, but no request is pending.\n");
443#endif
444 return;
445 }
446 h->transmission_scheduled = GNUNET_YES;
447#if DEBUG_TRANSPORT
448 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449 "Asking client API for transmission of %u bytes\n",
450 th->notify_size);
451#endif
452 h->network_handle = GNUNET_CLIENT_notify_transmit_ready (h->client,
453 th->notify_size,
454 GNUNET_TIME_absolute_get_remaining
455 (th->timeout),
456 &transport_notify_ready,
457 h);
458 GNUNET_assert (NULL != h->network_handle);
459}
460
461
462/**
463 * Insert the given transmit handle in the given sorted
464 * doubly linked list based on timeout.
465 *
466 * @param head pointer to the head of the linked list
467 * @param th element to insert into the list
468 */
469static void
470insert_transmit_handle (struct GNUNET_TRANSPORT_TransmitHandle **head,
471 struct GNUNET_TRANSPORT_TransmitHandle *th)
472{
473 struct GNUNET_TRANSPORT_TransmitHandle *pos;
474 struct GNUNET_TRANSPORT_TransmitHandle *prev;
475
476 pos = *head;
477 prev = NULL;
478 while ((pos != NULL) && (pos->timeout.value < th->timeout.value))
479 {
480 prev = pos;
481 pos = pos->next;
482 }
483 if (prev == NULL)
484 {
485 th->next = *head;
486 if (th->next != NULL)
487 th->next->prev = th;
488 *head = th;
489 }
490 else
491 {
492 th->next = pos;
493 th->prev = prev;
494 prev->next = th;
495 if (pos != NULL)
496 pos->prev = th;
497 }
498}
499
500
501/**
502 * Queue control request for transmission to the transport
503 * service.
504 *
505 * @param size number of bytes to be transmitted
506 * @param at_head request must be added to the head of the queue
507 * (otherwise request will be appended)
508 * @param timeout how long this transmission can wait (at most)
509 * @param notify function to call to get the content
510 * @param notify_cls closure for notify
511 */
512static void
513schedule_control_transmit (struct GNUNET_TRANSPORT_Handle *h,
514 size_t size,
515 int at_head,
516 struct GNUNET_TIME_Relative timeout,
517 GNUNET_NETWORK_TransmitReadyNotify notify,
518 void *notify_cls)
519{
520 struct GNUNET_TRANSPORT_TransmitHandle *th;
521#if DEBUG_TRANSPORT
522 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
523 "Queueing %u bytes control transmission request.\n", size);
524#endif
525 th = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TransmitHandle));
526 th->handle = h;
527 th->notify = notify;
528 th->notify_cls = notify_cls;
529 th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
530 th->notify_size = size;
531 if (at_head)
532 {
533 th->next = h->connect_ready_head;
534 h->connect_ready_head = th;
535 if (th->next != NULL)
536 th->next->prev = th;
537 }
538 else
539 {
540 insert_transmit_handle (&h->connect_ready_head, th);
541 }
542 if (GNUNET_NO == h->transmission_scheduled)
543 schedule_transmission (h);
544}
545
546
547/**
548 * Update the quota values for the given neighbour now.
549 */
550static void
551update_quota (struct NeighbourList *n)
552{
553 struct GNUNET_TIME_Relative delta;
554 uint64_t allowed;
555 uint64_t remaining;
556
557 delta = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
558 allowed = delta.value * n->quota_out;
559 if (n->last_sent < allowed)
560 {
561 remaining = allowed - n->last_sent;
562 if (n->quota_out > 0)
563 remaining /= n->quota_out;
564 else
565 remaining = 0;
566 if (remaining > MAX_BANDWIDTH_CARRY)
567 remaining = MAX_BANDWIDTH_CARRY;
568 n->last_sent = 0;
569 n->last_quota_update = GNUNET_TIME_absolute_get ();
570 n->last_quota_update.value -= remaining;
571 }
572 else
573 {
574 n->last_sent -= allowed;
575 n->last_quota_update = GNUNET_TIME_absolute_get ();
576 }
577}
578
579
580struct SetQuotaContext
581{
582 struct GNUNET_TRANSPORT_Handle *handle;
583
584 struct GNUNET_PeerIdentity target;
585
586 GNUNET_SCHEDULER_Task cont;
587
588 void *cont_cls;
589
590 struct GNUNET_TIME_Absolute timeout;
591
592 uint32_t quota_in;
593};
594
595
596static size_t
597send_set_quota (void *cls, size_t size, void *buf)
598{
599 struct SetQuotaContext *sqc = cls;
600 struct QuotaSetMessage *msg;
601
602 if (buf == NULL)
603 {
604 GNUNET_SCHEDULER_add_continuation (sqc->handle->sched,
605 GNUNET_NO,
606 sqc->cont,
607 sqc->cont_cls,
608 GNUNET_SCHEDULER_REASON_TIMEOUT);
609 GNUNET_free (sqc);
610 return 0;
611 }
612#if DEBUG_TRANSPORT
613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614 "Transmitting `%s' request with respect to `%4s'.\n",
615 "SET_QUOTA", GNUNET_i2s (&sqc->target));
616#endif
617 GNUNET_assert (size >= sizeof (struct QuotaSetMessage));
618 msg = buf;
619 msg->header.size = htons (sizeof (struct QuotaSetMessage));
620 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
621 msg->quota_in = htonl (sqc->quota_in);
622 memcpy (&msg->peer, &sqc->target, sizeof (struct GNUNET_PeerIdentity));
623 if (sqc->cont != NULL)
624 GNUNET_SCHEDULER_add_continuation (sqc->handle->sched,
625 GNUNET_NO,
626 sqc->cont,
627 sqc->cont_cls,
628 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
629 GNUNET_free (sqc);
630 return sizeof (struct QuotaSetMessage);
631}
632
633
634/**
635 * Set the share of incoming bandwidth for the given
636 * peer to the specified amount.
637 *
638 * @param handle connection to transport service
639 * @param target who's bandwidth quota is being changed
640 * @param quota_in incoming bandwidth quota in bytes per ms; 0 can
641 * be used to force all traffic to be discarded
642 * @param quota_out outgoing bandwidth quota in bytes per ms; 0 can
643 * be used to force all traffic to be discarded
644 * @param timeout how long to wait until signaling failure if
645 * we can not communicate the quota change
646 * @param cont continuation to call when done, will be called
647 * either with reason "TIMEOUT" or with reason "PREREQ_DONE"
648 * @param cont_cls closure for continuation
649 */
650void
651GNUNET_TRANSPORT_set_quota (struct GNUNET_TRANSPORT_Handle *handle,
652 const struct GNUNET_PeerIdentity *target,
653 uint32_t quota_in,
654 uint32_t quota_out,
655 struct GNUNET_TIME_Relative timeout,
656 GNUNET_SCHEDULER_Task cont, void *cont_cls)
657{
658 struct NeighbourList *n;
659 struct SetQuotaContext *sqc;
660
661 n = find_neighbour (handle, target);
662 if (n != NULL)
663 {
664 update_quota (n);
665 if (n->quota_out < quota_out)
666 n->last_quota_update = GNUNET_TIME_absolute_get ();
667 n->quota_out = quota_out;
668 }
669 sqc = GNUNET_malloc (sizeof (struct SetQuotaContext));
670 sqc->handle = handle;
671 sqc->target = *target;
672 sqc->cont = cont;
673 sqc->cont_cls = cont_cls;
674 sqc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
675 sqc->quota_in = quota_in;
676 schedule_control_transmit (handle,
677 sizeof (struct QuotaSetMessage),
678 GNUNET_NO, timeout, &send_set_quota, sqc);
679}
680
681
682/**
683 * A "get_hello" request has timed out. Signal the client
684 * and clean up.
685 */
686static void
687hello_wait_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
688{
689 struct HelloWaitList *hwl = cls;
690 struct HelloWaitList *pos;
691 struct HelloWaitList *prev;
692
693 prev = NULL;
694 pos = hwl->handle->hwl_head;
695 while (pos != hwl)
696 {
697 GNUNET_assert (pos != NULL);
698 prev = pos;
699 pos = pos->next;
700 }
701 if (prev == NULL)
702 hwl->handle->hwl_head = hwl->next;
703 else
704 prev->next = hwl->next;
705 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
706 _("Timeout trying to obtain `%s' from transport service.\n"),
707 "HELLO");
708 /* signal timeout */
709 if (hwl->rec != NULL)
710 hwl->rec (hwl->rec_cls, GNUNET_TIME_UNIT_ZERO, NULL, NULL);
711 GNUNET_free (hwl);
712}
713
714
715/**
716 * Obtain the HELLO message for this peer.
717 *
718 * @param handle connection to transport service
719 * @param timeout how long to wait for the HELLO
720 * @param rec function to call with the HELLO, sender will be our peer
721 * identity; message and sender will be NULL on timeout
722 * (handshake with transport service pending/failed).
723 * cost estimate will be 0.
724 * @param rec_cls closure for rec
725 */
726void
727GNUNET_TRANSPORT_get_hello (struct GNUNET_TRANSPORT_Handle *handle,
728 struct GNUNET_TIME_Relative timeout,
729 GNUNET_TRANSPORT_ReceiveCallback rec,
730 void *rec_cls)
731{
732 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
733 struct GNUNET_PeerIdentity me;
734 struct HelloWaitList *hwl;
735
736 if (handle->my_hello == NULL)
737 {
738 hwl = GNUNET_malloc (sizeof (struct HelloWaitList));
739 hwl->next = handle->hwl_head;
740 handle->hwl_head = hwl;
741 hwl->handle = handle;
742 hwl->rec = rec;
743 hwl->rec_cls = rec_cls;
744 hwl->timeout = GNUNET_TIME_relative_to_absolute (timeout);
745 hwl->task = GNUNET_SCHEDULER_add_delayed (handle->sched,
746 GNUNET_YES,
747 GNUNET_SCHEDULER_PRIORITY_KEEP,
748 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
749 timeout,
750 &hello_wait_timeout, hwl);
751 return;
752 }
753 GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_key (handle->my_hello, &pk));
754 GNUNET_CRYPTO_hash (&pk,
755 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
756 &me.hashPubKey);
757
758 rec (rec_cls,
759 GNUNET_TIME_UNIT_ZERO,
760 &me, (const struct GNUNET_MessageHeader *) handle->my_hello);
761}
762
763
764static size_t
765send_hello (void *cls, size_t size, void *buf)
766{
767 struct GNUNET_MessageHeader *hello = cls;
768 uint16_t msize;
769
770 if (buf == NULL)
771 {
772#if DEBUG_TRANSPORT
773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
774 "Timeout while trying to transmit `%s' request.\n",
775 "HELLO");
776#endif
777 GNUNET_free (hello);
778 return 0;
779 }
780#if DEBUG_TRANSPORT
781 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782 "Transmitting `%s' request.\n", "HELLO");
783#endif
784 msize = ntohs (hello->size);
785 GNUNET_assert (size >= msize);
786 memcpy (buf, hello, msize);
787 GNUNET_free (hello);
788 return msize;
789}
790
791
792/**
793 * Offer the transport service the HELLO of another peer. Note that
794 * the transport service may just ignore this message if the HELLO is
795 * malformed or useless due to our local configuration.
796 *
797 * @param handle connection to transport service
798 * @param hello the hello message
799 */
800void
801GNUNET_TRANSPORT_offer_hello (struct GNUNET_TRANSPORT_Handle *handle,
802 const struct GNUNET_MessageHeader *hello)
803{
804 struct GNUNET_MessageHeader *hc;
805 uint16_t size;
806
807 if (handle->client == NULL)
808 {
809#if DEBUG_TRANSPORT
810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
811 "Not connected to transport service, dropping offered HELLO\n");
812#endif
813 return;
814 }
815 GNUNET_break (ntohs (hello->type) == GNUNET_MESSAGE_TYPE_HELLO);
816 size = ntohs (hello->size);
817 GNUNET_break (size >= sizeof (struct GNUNET_MessageHeader));
818 hc = GNUNET_malloc (size);
819 memcpy (hc, hello, size);
820 schedule_control_transmit (handle,
821 size,
822 GNUNET_NO, OFFER_HELLO_TIMEOUT, &send_hello, hc);
823}
824
825
826/**
827 * Function we use for handling incoming messages.
828 */
829static void demultiplexer (void *cls, const struct GNUNET_MessageHeader *msg);
830
831
832static size_t
833send_start (void *cls, size_t size, void *buf)
834{
835 struct GNUNET_MessageHeader *s = buf;
836
837 if (buf == NULL)
838 return 0;
839#if DEBUG_TRANSPORT
840 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
841 "Transmitting `%s' request.\n", "START");
842#endif
843 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
844 s->size = htons (sizeof (struct GNUNET_MessageHeader));
845 s->type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_START);
846 return sizeof (struct GNUNET_MessageHeader);
847}
848
849
850/**
851 * Try again to connect to transport service.
852 */
853static void
854reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
855{
856 struct GNUNET_TRANSPORT_Handle *h = cls;
857 struct GNUNET_TRANSPORT_TransmitHandle *pos;
858 struct NeighbourList *n;
859
860 while (NULL != (n = h->neighbours))
861 {
862 h->neighbours = n->next;
863 pos = n->transmit_handle;
864 if (pos != NULL)
865 {
866 pos->neighbour = NULL;
867 pos->next = h->connect_wait_head;
868 h->connect_wait_head = pos;
869 if (pos->next != NULL)
870 pos->next->prev = pos;
871 pos->prev = NULL;
872 }
873 GNUNET_free (n);
874 }
875 h->connect_ready_head = NULL;
876#if DEBUG_TRANSPORT
877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n");
878#endif
879 GNUNET_assert (h->client == NULL);
880 h->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
881 h->client = GNUNET_CLIENT_connect (h->sched, "transport", h->cfg);
882 GNUNET_assert (h->client != NULL);
883 /* make sure we don't send "START" twice,
884 remove existing entry from queue (if present) */
885 pos = h->connect_ready_head;
886 while (pos != NULL)
887 {
888 if (pos->notify == &send_start)
889 {
890 if (pos->prev == NULL)
891 h->connect_ready_head = pos->next;
892 else
893 pos->prev->next = pos->next;
894 if (pos->next != NULL)
895 pos->next->prev = pos->prev;
896 GNUNET_assert (pos->neighbour == NULL);
897 GNUNET_free (pos);
898 break;
899 }
900 pos = pos->next;
901 }
902 schedule_control_transmit (h,
903 sizeof (struct GNUNET_MessageHeader),
904 GNUNET_YES,
905 GNUNET_TIME_UNIT_FOREVER_REL, &send_start, NULL);
906 GNUNET_CLIENT_receive (h->client,
907 &demultiplexer, h, GNUNET_TIME_UNIT_FOREVER_REL);
908}
909
910
911/**
912 * Function that will schedule the job that will try
913 * to connect us again to the client.
914 */
915static void
916schedule_reconnect (struct GNUNET_TRANSPORT_Handle *h)
917{
918#if DEBUG_TRANSPORT
919 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920 "Scheduling task to reconnect to transport service in %llu ms.\n",
921 h->reconnect_delay.value);
922#endif
923 GNUNET_assert (h->client == NULL);
924 GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
925 h->reconnect_task
926 = GNUNET_SCHEDULER_add_delayed (h->sched,
927 GNUNET_NO,
928 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
929 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
930 h->reconnect_delay, &reconnect, h);
931 h->reconnect_delay = GNUNET_TIME_UNIT_SECONDS;
932}
933
934
935/**
936 * Remove the given transmit handle from the wait list. Does NOT free
937 * it.
938 */
939static void
940remove_from_wait_list (struct GNUNET_TRANSPORT_TransmitHandle *th)
941{
942 if (th->prev == NULL)
943 th->handle->connect_wait_head = th->next;
944 else
945 th->prev->next = th->next;
946 if (th->next != NULL)
947 th->next->prev = th->prev;
948}
949
950
951/**
952 * We are connected to the respective peer, check the
953 * bandwidth limits and schedule the transmission.
954 */
955static void schedule_request (struct GNUNET_TRANSPORT_TransmitHandle *th);
956
957
958/**
959 * Function called by the scheduler when the timeout
960 * for bandwidth availablility for the target
961 * neighbour is reached.
962 */
963static void
964transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
965{
966 struct GNUNET_TRANSPORT_TransmitHandle *th = cls;
967
968 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
969 schedule_request (th);
970}
971
972
973/**
974 * Called when our transmit request timed out before any transport
975 * reported success connecting to the desired peer or before the
976 * transport was ready to receive. Signal error and free
977 * TransmitHandle.
978 */
979static void
980transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
981{
982 struct GNUNET_TRANSPORT_TransmitHandle *th = cls;
983
984 if (th->neighbour != NULL)
985 th->neighbour->transmit_handle = NULL;
986#if DEBUG_TRANSPORT
987 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission request timed out.\n");
988#endif
989 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
990 remove_from_wait_list (th);
991 th->notify (th->notify_cls, 0, NULL);
992 GNUNET_free (th);
993}
994
995
996/**
997 * We are connected to the respective peer, check the
998 * bandwidth limits and schedule the transmission.
999 */
1000static void
1001schedule_request (struct GNUNET_TRANSPORT_TransmitHandle *th)
1002{
1003 struct GNUNET_TRANSPORT_Handle *h;
1004 struct GNUNET_TIME_Relative duration;
1005 struct NeighbourList *n;
1006 uint64_t available;
1007
1008 h = th->handle;
1009 n = th->neighbour;
1010 if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1011 {
1012 GNUNET_SCHEDULER_cancel (h->sched, th->notify_delay_task);
1013 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1014 }
1015 /* check outgoing quota */
1016 duration = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
1017 if (duration.value > MIN_QUOTA_REFRESH_TIME)
1018 {
1019 update_quota (n);
1020 duration = GNUNET_TIME_absolute_get_duration (n->last_quota_update);
1021 }
1022 available = duration.value * n->quota_out;
1023 if (available < n->last_sent + th->notify_size)
1024 {
1025 /* calculate how much bandwidth we'd still need to
1026 accumulate and based on that how long we'll have
1027 to wait... */
1028 available = n->last_sent + th->notify_size - available;
1029 duration = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
1030 available / n->quota_out);
1031 if (th->timeout.value <
1032 GNUNET_TIME_relative_to_absolute (duration).value)
1033 {
1034 /* signal timeout! */
1035#if DEBUG_TRANSPORT
1036 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037 "Would need %llu ms before bandwidth is available for delivery, that is too long. Signaling timeout.\n",
1038 duration.value);
1039#endif
1040 remove_from_wait_list (th);
1041 th->notify (th->notify_cls, 0, NULL);
1042 GNUNET_free (th);
1043 return;
1044 }
1045#if DEBUG_TRANSPORT
1046 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1047 "Need more bandwidth, delaying delivery by %llu ms\n",
1048 duration.value);
1049#endif
1050 th->notify_delay_task
1051 = GNUNET_SCHEDULER_add_delayed (h->sched,
1052 GNUNET_NO,
1053 GNUNET_SCHEDULER_PRIORITY_KEEP,
1054 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1055 duration, &transmit_ready, th);
1056 return;
1057 }
1058#if DEBUG_TRANSPORT
1059 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1060 "Bandwidth available for transmission to `%4s'\n",
1061 GNUNET_i2s (&n->id));
1062#endif
1063 if (GNUNET_NO == n->transmit_ok)
1064 {
1065 /* we may be ready, but transport service is not;
1066 wait for SendOkMessage or timeout */
1067#if DEBUG_TRANSPORT
1068 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1069 "Need to wait for transport service `%s' message\n",
1070 "SEND_OK");
1071#endif
1072 th->notify_delay_task
1073 = GNUNET_SCHEDULER_add_delayed (h->sched,
1074 GNUNET_NO,
1075 GNUNET_SCHEDULER_PRIORITY_KEEP,
1076 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1077 GNUNET_TIME_absolute_get_remaining
1078 (th->timeout), &transmit_timeout, th);
1079 return;
1080 }
1081 n->transmit_ok = GNUNET_NO;
1082 remove_from_wait_list (th);
1083#if DEBUG_TRANSPORT
1084 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving message to ready list\n");
1085#endif
1086 insert_transmit_handle (&h->connect_ready_head, th);
1087 if (GNUNET_NO == h->transmission_scheduled)
1088 schedule_transmission (h);
1089}
1090
1091
1092/**
1093 * Add neighbour to our list
1094 */
1095static void
1096add_neighbour (struct GNUNET_TRANSPORT_Handle *h,
1097 uint32_t quota_out,
1098 struct GNUNET_TIME_Relative latency,
1099 const struct GNUNET_PeerIdentity *pid)
1100{
1101 struct NeighbourList *n;
1102 struct GNUNET_TRANSPORT_TransmitHandle *prev;
1103 struct GNUNET_TRANSPORT_TransmitHandle *pos;
1104 struct GNUNET_TRANSPORT_TransmitHandle *next;
1105
1106 /* check for duplicates */
1107 if (NULL != find_neighbour (h, pid))
1108 {
1109 GNUNET_break (0);
1110 return;
1111 }
1112#if DEBUG_TRANSPORT
1113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1114 "Creating entry for new neighbour `%4s'.\n", GNUNET_i2s (pid));
1115#endif
1116 n = GNUNET_malloc (sizeof (struct NeighbourList));
1117 n->id = *pid;
1118 n->last_quota_update = GNUNET_TIME_absolute_get ();
1119 n->quota_out = quota_out;
1120 n->next = h->neighbours;
1121 n->transmit_ok = GNUNET_YES;
1122 h->neighbours = n;
1123 if (h->nc_cb != NULL)
1124 h->nc_cb (h->cls, &n->id, latency);
1125 prev = NULL;
1126 pos = h->connect_wait_head;
1127 while (pos != NULL)
1128 {
1129 next = pos->next;
1130#if DEBUG_TRANSPORT
1131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1132 "Found entry in connect_wait_head for `%4s'.\n",
1133 GNUNET_i2s (&pos->target));
1134#endif
1135 if (0 == memcmp (pid,
1136 &pos->target, sizeof (struct GNUNET_PeerIdentity)))
1137 {
1138#if DEBUG_TRANSPORT
1139 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1140 "Found pending request for new connection, will trigger now.\n");
1141#endif
1142 pos->neighbour = n;
1143 if (pos->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1144 {
1145 GNUNET_SCHEDULER_cancel (h->sched, pos->notify_delay_task);
1146 pos->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1147 }
1148 GNUNET_assert (NULL == n->transmit_handle);
1149 n->transmit_handle = pos;
1150 if (GNUNET_YES == n->received_ack)
1151 {
1152#if DEBUG_TRANSPORT
1153 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1154 "`%s' already received, scheduling request\n",
1155 "ACK");
1156#endif
1157 schedule_request (pos);
1158 }
1159 else
1160 {
1161#if DEBUG_TRANSPORT
1162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163 "Still need to wait to receive `%s' message\n",
1164 "ACK");
1165#endif
1166 pos->notify_delay_task
1167 = GNUNET_SCHEDULER_add_delayed (h->sched,
1168 GNUNET_NO,
1169 GNUNET_SCHEDULER_PRIORITY_KEEP,
1170 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1171 GNUNET_TIME_absolute_get_remaining
1172 (pos->timeout),
1173 &transmit_timeout, pos);
1174 }
1175 if (prev == NULL)
1176 h->connect_wait_head = next;
1177 else
1178 prev->next = next;
1179 break;
1180 }
1181 prev = pos;
1182 pos = next;
1183 }
1184}
1185
1186
1187/**
1188 * Connect to the transport service. Note that the connection may
1189 * complete (or fail) asynchronously.
1190 *
1191
1192 * @param sched scheduler to use
1193 * @param cfg configuration to use
1194 * @param cls closure for the callbacks
1195 * @param rec receive function to call
1196 * @param nc function to call on connect events
1197 * @param dc function to call on disconnect events
1198 */
1199struct GNUNET_TRANSPORT_Handle *
1200GNUNET_TRANSPORT_connect (struct GNUNET_SCHEDULER_Handle *sched,
1201 struct GNUNET_CONFIGURATION_Handle *cfg,
1202 void *cls,
1203 GNUNET_TRANSPORT_ReceiveCallback rec,
1204 GNUNET_TRANSPORT_NotifyConnect nc,
1205 GNUNET_TRANSPORT_NotifyDisconnect nd)
1206{
1207 struct GNUNET_TRANSPORT_Handle *ret;
1208
1209 GNUNET_ARM_start_service ("peerinfo",
1210 cfg, sched, START_SERVICE_TIMEOUT, NULL, NULL);
1211 GNUNET_ARM_start_service ("transport",
1212 cfg, sched, START_SERVICE_TIMEOUT, NULL, NULL);
1213 ret = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_Handle));
1214 ret->sched = sched;
1215 ret->cfg = cfg;
1216 ret->cls = cls;
1217 ret->rec = rec;
1218 ret->nc_cb = nc;
1219 ret->nd_cb = nd;
1220 ret->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
1221 schedule_reconnect (ret);
1222 return ret;
1223}
1224
1225
1226/**
1227 * These stop activities must be run in a fresh
1228 * scheduler that is NOT in shutdown mode.
1229 */
1230static void
1231stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1232{
1233 struct GNUNET_TRANSPORT_Handle *handle = cls;
1234 GNUNET_ARM_stop_service ("transport",
1235 handle->cfg,
1236 tc->sched, STOP_SERVICE_TIMEOUT, NULL, NULL);
1237 GNUNET_ARM_stop_service ("peerinfo",
1238 handle->cfg,
1239 tc->sched, STOP_SERVICE_TIMEOUT, NULL, NULL);
1240}
1241
1242
1243/**
1244 * Disconnect from the transport service.
1245 */
1246void
1247GNUNET_TRANSPORT_disconnect (struct GNUNET_TRANSPORT_Handle *handle)
1248{
1249 struct GNUNET_TRANSPORT_TransmitHandle *th;
1250 struct NeighbourList *n;
1251 struct HelloWaitList *hwl;
1252 struct GNUNET_CLIENT_Connection *client;
1253
1254#if DEBUG_TRANSPORT
1255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transport disconnect called!\n");
1256#endif
1257 while (NULL != (th = handle->connect_ready_head))
1258 {
1259 handle->connect_ready_head = th->next;
1260 th->notify (th->notify_cls, 0, NULL);
1261 GNUNET_free (th);
1262 }
1263
1264 while (NULL != (th = handle->connect_wait_head))
1265 {
1266 handle->connect_wait_head = th->next;
1267 if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1268 {
1269 GNUNET_SCHEDULER_cancel (handle->sched, th->notify_delay_task);
1270 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1271 }
1272 th->notify (th->notify_cls, 0, NULL);
1273 GNUNET_free (th);
1274 }
1275 while (NULL != (n = handle->neighbours))
1276 {
1277 handle->neighbours = n->next;
1278 GNUNET_free (n);
1279 }
1280 while (NULL != (hwl = handle->hwl_head))
1281 {
1282 handle->hwl_head = hwl->next;
1283 GNUNET_SCHEDULER_cancel (handle->sched, hwl->task);
1284 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1285 _
1286 ("Disconnect while trying to obtain HELLO from transport service.\n"));
1287 if (hwl->rec != NULL)
1288 hwl->rec (hwl->rec_cls, GNUNET_TIME_UNIT_ZERO, NULL, NULL);
1289 GNUNET_free (hwl);
1290 }
1291 if (handle->reconnect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1292 {
1293 GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task);
1294 handle->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1295 }
1296 GNUNET_free_non_null (handle->my_hello);
1297 handle->my_hello = NULL;
1298 GNUNET_SCHEDULER_run (&stop_task, handle);
1299 if (NULL != (client = handle->client))
1300 {
1301#if DEBUG_TRANSPORT
1302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1303 "Disconnecting from transport service for good.\n");
1304#endif
1305 handle->client = NULL;
1306 GNUNET_CLIENT_disconnect (client);
1307 }
1308 if (client == NULL)
1309 GNUNET_free (handle);
1310}
1311
1312
1313/**
1314 * We're ready to transmit the request that the transport service
1315 * should connect to a new peer. In addition to sending the
1316 * request, schedule the next phase for the transmission processing
1317 * that caused the connect request in the first place.
1318 */
1319static size_t
1320request_connect (void *cls, size_t size, void *buf)
1321{
1322 struct GNUNET_TRANSPORT_TransmitHandle *th = cls;
1323 struct TryConnectMessage *tcm;
1324 struct GNUNET_TRANSPORT_Handle *h;
1325
1326 h = th->handle;
1327 if (buf == NULL)
1328 {
1329#if DEBUG_TRANSPORT
1330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1331 "Failed to transmit connect request to service.\n");
1332#endif
1333 th->notify (th->notify_cls, 0, NULL);
1334 GNUNET_free (th);
1335 return 0;
1336 }
1337#if DEBUG_TRANSPORT
1338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1339 "Transmitting `%s' message for `%4s'.\n",
1340 "TRY_CONNECT", GNUNET_i2s (&th->target));
1341#endif
1342 GNUNET_assert (size >= sizeof (struct TryConnectMessage));
1343 tcm = buf;
1344 tcm->header.size = htons (sizeof (struct TryConnectMessage));
1345 tcm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT);
1346 tcm->reserved = htonl (0);
1347 memcpy (&tcm->peer, &th->target, sizeof (struct GNUNET_PeerIdentity));
1348 th->notify_delay_task
1349 = GNUNET_SCHEDULER_add_delayed (h->sched,
1350 GNUNET_NO,
1351 GNUNET_SCHEDULER_PRIORITY_KEEP,
1352 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1353 GNUNET_TIME_absolute_get_remaining (th->
1354 timeout),
1355 &transmit_timeout, th);
1356 insert_transmit_handle (&h->connect_wait_head, th);
1357 return sizeof (struct TryConnectMessage);
1358}
1359
1360
1361/**
1362 * Schedule a request to connect to the given
1363 * neighbour (and if successful, add the specified
1364 * handle to the wait list).
1365 */
1366static void
1367try_connect (struct GNUNET_TRANSPORT_TransmitHandle *th)
1368{
1369 schedule_control_transmit (th->handle,
1370 sizeof (struct TryConnectMessage),
1371 GNUNET_NO,
1372 GNUNET_TIME_absolute_get_remaining (th->timeout),
1373 &request_connect, th);
1374}
1375
1376
1377/**
1378 * Cancel a pending notify transmit task
1379 * and also remove the given transmit handle
1380 * from whatever list is on.
1381 */
1382static void
1383remove_from_any_list (struct GNUNET_TRANSPORT_TransmitHandle *th)
1384{
1385 struct GNUNET_TRANSPORT_Handle *h;
1386
1387 h = th->handle;
1388 if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1389 {
1390 GNUNET_SCHEDULER_cancel (h->sched, th->notify_delay_task);
1391 th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1392 }
1393 if (th->prev == NULL)
1394 {
1395 if (th == h->connect_wait_head)
1396 h->connect_wait_head = th->next;
1397 else
1398 h->connect_ready_head = th->next;
1399 }
1400 else
1401 th->prev->next = th->next;
1402 if (th->next != NULL)
1403 th->next->prev = th->prev;
1404}
1405
1406
1407/**
1408 * Remove neighbour from our list
1409 */
1410static void
1411remove_neighbour (struct GNUNET_TRANSPORT_Handle *h,
1412 const struct GNUNET_PeerIdentity *peer)
1413{
1414 struct NeighbourList *prev;
1415 struct NeighbourList *pos;
1416 struct GNUNET_TRANSPORT_TransmitHandle *th;
1417
1418 prev = NULL;
1419 pos = h->neighbours;
1420 while ((pos != NULL) &&
1421 (0 != memcmp (peer, &pos->id, sizeof (struct GNUNET_PeerIdentity))))
1422 {
1423 prev = pos;
1424 pos = pos->next;
1425 }
1426 if (pos == NULL)
1427 {
1428 GNUNET_break (0);
1429 return;
1430 }
1431 if (prev == NULL)
1432 h->neighbours = pos->next;
1433 else
1434 prev->next = pos->next;
1435 if (NULL != (th = pos->transmit_handle))
1436 {
1437 pos->transmit_handle = NULL;
1438 th->neighbour = NULL;
1439 remove_from_any_list (th);
1440 try_connect (th);
1441 }
1442 if (h->nc_cb != NULL)
1443 h->nd_cb (h->cls, peer);
1444 GNUNET_free (pos);
1445}
1446
1447
1448/**
1449 * Type of a function to call when we receive a message
1450 * from the service.
1451 *
1452 * @param cls closure
1453 * @param msg message received, NULL on timeout or fatal error
1454 */
1455static void
1456demultiplexer (void *cls, const struct GNUNET_MessageHeader *msg)
1457{
1458 struct GNUNET_TRANSPORT_Handle *h = cls;
1459 const struct DisconnectInfoMessage *dim;
1460 const struct ConnectInfoMessage *cim;
1461 const struct InboundMessage *im;
1462 const struct GNUNET_MessageHeader *imm;
1463 const struct SendOkMessage *okm;
1464 struct HelloWaitList *hwl;
1465 struct NeighbourList *n;
1466 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
1467 struct GNUNET_PeerIdentity me;
1468 uint16_t size;
1469
1470 if ((msg == NULL) || (h->client == NULL))
1471 {
1472 if (h->client != NULL)
1473 {
1474#if DEBUG_TRANSPORT
1475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1476 "Error receiving from transport service, disconnecting temporarily.\n");
1477#endif
1478 if (h->network_handle != NULL)
1479 {
1480 GNUNET_NETWORK_notify_transmit_ready_cancel (h->network_handle);
1481 h->network_handle = NULL;
1482 h->transmission_scheduled = GNUNET_NO;
1483 }
1484 GNUNET_CLIENT_disconnect (h->client);
1485 h->client = NULL;
1486 schedule_reconnect (h);
1487 }
1488 else
1489 {
1490 /* shutdown initiated from 'GNUNET_TRANSPORT_disconnect',
1491 finish clean up work! */
1492 GNUNET_free (h);
1493 }
1494 return;
1495 }
1496 GNUNET_CLIENT_receive (h->client,
1497 &demultiplexer, h, GNUNET_TIME_UNIT_FOREVER_REL);
1498 size = ntohs (msg->size);
1499 switch (ntohs (msg->type))
1500 {
1501 case GNUNET_MESSAGE_TYPE_HELLO:
1502 if (GNUNET_OK !=
1503 GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *) msg,
1504 &pkey))
1505 {
1506 GNUNET_break (0);
1507 break;
1508 }
1509 GNUNET_CRYPTO_hash (&pkey,
1510 sizeof (struct
1511 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1512 &me.hashPubKey);
1513#if DEBUG_TRANSPORT
1514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1515 "Receiving (my own) `%s' message, I am `%4s'.\n",
1516 "HELLO", GNUNET_i2s (&me));
1517#endif
1518 GNUNET_free_non_null (h->my_hello);
1519 h->my_hello = NULL;
1520 if (size < sizeof (struct GNUNET_MessageHeader))
1521 {
1522 GNUNET_break (0);
1523 break;
1524 }
1525 h->my_hello = GNUNET_malloc (size);
1526 memcpy (h->my_hello, msg, size);
1527 while (NULL != (hwl = h->hwl_head))
1528 {
1529 h->hwl_head = hwl->next;
1530 GNUNET_SCHEDULER_cancel (h->sched, hwl->task);
1531 GNUNET_TRANSPORT_get_hello (h,
1532 GNUNET_TIME_UNIT_ZERO,
1533 hwl->rec, hwl->rec_cls);
1534 GNUNET_free (hwl);
1535 }
1536 break;
1537 case GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT:
1538 if (size != sizeof (struct ConnectInfoMessage))
1539 {
1540 GNUNET_break (0);
1541 break;
1542 }
1543 cim = (const struct ConnectInfoMessage *) msg;
1544#if DEBUG_TRANSPORT
1545 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1546 "Receiving `%s' message for `%4s'.\n",
1547 "CONNECT", GNUNET_i2s (&cim->id));
1548#endif
1549 add_neighbour (h,
1550 ntohl (cim->quota_out),
1551 GNUNET_TIME_relative_ntoh (cim->latency), &cim->id);
1552 break;
1553 case GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT:
1554 if (size != sizeof (struct DisconnectInfoMessage))
1555 {
1556 GNUNET_break (0);
1557 break;
1558 }
1559 dim = (const struct DisconnectInfoMessage *) msg;
1560#if DEBUG_TRANSPORT
1561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1562 "Receiving `%s' message for `%4s'.\n",
1563 "DISCONNECT", GNUNET_i2s (&dim->peer));
1564#endif
1565 remove_neighbour (h, &dim->peer);
1566 break;
1567 case GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK:
1568#if DEBUG_TRANSPORT
1569 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1570 "Receiving `%s' message.\n", "SEND_OK");
1571#endif
1572 if (size != sizeof (struct SendOkMessage))
1573 {
1574 GNUNET_break (0);
1575 break;
1576 }
1577 okm = (const struct SendOkMessage *) msg;
1578 n = find_neighbour (h, &okm->peer);
1579 GNUNET_assert (n != NULL);
1580 n->transmit_ok = GNUNET_YES;
1581 if (n->transmit_handle != NULL)
1582 {
1583#if DEBUG_TRANSPORT
1584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1585 "Processing pending message\n");
1586#endif
1587 GNUNET_SCHEDULER_cancel (h->sched,
1588 n->transmit_handle->notify_delay_task);
1589 n->transmit_handle->notify_delay_task =
1590 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1591 GNUNET_assert (GNUNET_YES == n->received_ack);
1592 schedule_request (n->transmit_handle);
1593 }
1594 break;
1595 case GNUNET_MESSAGE_TYPE_TRANSPORT_RECV:
1596#if DEBUG_TRANSPORT
1597 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1598 "Receiving `%s' message.\n", "RECV");
1599#endif
1600 if (size <
1601 sizeof (struct InboundMessage) +
1602 sizeof (struct GNUNET_MessageHeader))
1603 {
1604 GNUNET_break (0);
1605 break;
1606 }
1607 im = (const struct InboundMessage *) msg;
1608 imm = (const struct GNUNET_MessageHeader *) &im[1];
1609 if (ntohs (imm->size) + sizeof (struct InboundMessage) != size)
1610 {
1611 GNUNET_break (0);
1612 break;
1613 }
1614 switch (ntohs (imm->type))
1615 {
1616 case GNUNET_MESSAGE_TYPE_TRANSPORT_ACK:
1617#if DEBUG_TRANSPORT
1618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1619 "Receiving `%s' message from `%4s'.\n",
1620 "ACK", GNUNET_i2s (&im->peer));
1621#endif
1622 n = find_neighbour (h, &im->peer);
1623 if (n == NULL)
1624 {
1625 GNUNET_break (0);
1626 break;
1627 }
1628 if (n->received_ack == GNUNET_NO)
1629 {
1630 n->received_ack = GNUNET_YES;
1631 if (NULL != n->transmit_handle)
1632 {
1633#if DEBUG_TRANSPORT
1634 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1635 "Peer connected, scheduling delayed message for deliverery now.\n");
1636#endif
1637 schedule_request (n->transmit_handle);
1638 }
1639 }
1640 break;
1641 default:
1642#if DEBUG_TRANSPORT
1643 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1644 "Received message of type %u from `%4s'.\n",
1645 ntohs (imm->type), GNUNET_i2s (&im->peer));
1646#endif
1647 if (h->rec != NULL)
1648 h->rec (h->cls,
1649 GNUNET_TIME_relative_ntoh (im->latency), &im->peer, imm);
1650 break;
1651 }
1652 break;
1653 default:
1654 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1655 _
1656 ("Received unexpected message of type %u from `%4s' in %s:%u\n"),
1657 ntohs (msg->type), GNUNET_i2s (&im->peer), __FILE__,
1658 __LINE__);
1659 GNUNET_break (0);
1660 break;
1661 }
1662}
1663
1664
1665struct ClientTransmitWrapper
1666{
1667 GNUNET_NETWORK_TransmitReadyNotify notify;
1668 void *notify_cls;
1669 struct GNUNET_TRANSPORT_TransmitHandle *th;
1670};
1671
1672
1673/**
1674 * Transmit message of a client destined for another
1675 * peer to the service.
1676 */
1677static size_t
1678client_notify_wrapper (void *cls, size_t size, void *buf)
1679{
1680 struct ClientTransmitWrapper *ctw = cls;
1681 struct OutboundMessage *obm;
1682 struct GNUNET_MessageHeader *hdr;
1683 size_t ret;
1684
1685 if (size == 0)
1686 {
1687#if DEBUG_TRANSPORT
1688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1689 "Transmission request could not be satisfied.\n");
1690#endif
1691 ret = ctw->notify (ctw->notify_cls, 0, NULL);
1692 GNUNET_assert (ret == 0);
1693 GNUNET_free (ctw);
1694 return 0;
1695 }
1696 GNUNET_assert (size >= sizeof (struct OutboundMessage));
1697 obm = buf;
1698 ret = ctw->notify (ctw->notify_cls,
1699 size - sizeof (struct OutboundMessage),
1700 (void *) &obm[1]);
1701 if (ret == 0)
1702 {
1703 /* Need to reset flag, no SEND means no SEND_OK! */
1704 ctw->th->neighbour->transmit_ok = GNUNET_YES;
1705 GNUNET_free (ctw);
1706 return 0;
1707 }
1708 GNUNET_assert (ret >= sizeof (struct GNUNET_MessageHeader));
1709 hdr = (struct GNUNET_MessageHeader *) &obm[1];
1710 GNUNET_assert (ntohs (hdr->size) == ret);
1711 GNUNET_assert (ret + sizeof (struct OutboundMessage) <
1712 GNUNET_SERVER_MAX_MESSAGE_SIZE);
1713#if DEBUG_TRANSPORT
1714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1715 "Transmitting `%s' message with data for `%4s'\n",
1716 "SEND", GNUNET_i2s (&ctw->th->target));
1717#endif
1718 ret += sizeof (struct OutboundMessage);
1719 obm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND);
1720 obm->header.size = htons (ret);
1721 obm->reserved = htonl (0);
1722 obm->peer = ctw->th->target;
1723 GNUNET_free (ctw);
1724 return ret;
1725}
1726
1727
1728
1729/**
1730 * Check if we could queue a message of the given size for
1731 * transmission. The transport service will take both its
1732 * internal buffers and bandwidth limits imposed by the
1733 * other peer into consideration when answering this query.
1734 *
1735 * @param handle connection to transport service
1736 * @param target who should receive the message
1737 * @param size how big is the message we want to transmit?
1738 * @param timeout after how long should we give up (and call
1739 * notify with buf NULL and size 0)?
1740 * @param notify function to call when we are ready to
1741 * send such a message
1742 * @param notify_cls closure for notify
1743 * @return NULL if someone else is already waiting to be notified
1744 * non-NULL if the notify callback was queued (can be used to cancel
1745 * using GNUNET_TRANSPORT_notify_transmit_ready_cancel)
1746 */
1747struct GNUNET_TRANSPORT_TransmitHandle *
1748GNUNET_TRANSPORT_notify_transmit_ready (struct GNUNET_TRANSPORT_Handle
1749 *handle,
1750 const struct GNUNET_PeerIdentity
1751 *target, size_t size,
1752 struct GNUNET_TIME_Relative timeout,
1753 GNUNET_NETWORK_TransmitReadyNotify
1754 notify, void *notify_cls)
1755{
1756 struct GNUNET_TRANSPORT_TransmitHandle *pos;
1757 struct GNUNET_TRANSPORT_TransmitHandle *th;
1758 struct NeighbourList *n;
1759 struct ClientTransmitWrapper *ctw;
1760
1761 if (size + sizeof (struct OutboundMessage) >=
1762 GNUNET_SERVER_MAX_MESSAGE_SIZE)
1763 return NULL;
1764#if DEBUG_TRANSPORT
1765 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1766 "Asking transport service for transmission of %u bytes to peer `%4s'.\n",
1767 size, GNUNET_i2s (target));
1768#endif
1769 n = find_neighbour (handle, target);
1770 ctw = GNUNET_malloc (sizeof (struct ClientTransmitWrapper));
1771 th = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TransmitHandle));
1772 ctw->notify = notify;
1773 ctw->notify_cls = notify_cls;
1774 ctw->th = th;
1775 th->handle = handle;
1776 th->target = *target;
1777 th->notify = &client_notify_wrapper;
1778 th->notify_cls = ctw;
1779 th->notify_size = size + sizeof (struct OutboundMessage);
1780 th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1781 th->neighbour = n;
1782 if (NULL == n)
1783 {
1784#if DEBUG_TRANSPORT
1785 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1786 "Transmission request could not be satisfied (not yet connected), adding it to pending request list.\n");
1787#endif
1788 pos = handle->connect_wait_head;
1789 while (pos != NULL)
1790 {
1791 GNUNET_assert (0 != memcmp (target,
1792 &pos->target,
1793 sizeof (struct GNUNET_PeerIdentity)));
1794 pos = pos->next;
1795 }
1796#if DEBUG_TRANSPORT
1797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1798 "Will now try to connect to `%4s'.\n", GNUNET_i2s (target));
1799#endif
1800 try_connect (th);
1801 }
1802 else
1803 {
1804#if DEBUG_TRANSPORT
1805 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1806 "Transmission request queued for transmission to transport service.\n");
1807#endif
1808 GNUNET_assert (NULL == n->transmit_handle);
1809 n->transmit_handle = th;
1810 if (GNUNET_YES == n->received_ack)
1811 {
1812#if DEBUG_TRANSPORT
1813 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1814 "Peer `%4s' is connected, scheduling for delivery now.\n",
1815 GNUNET_i2s (target));
1816#endif
1817 schedule_request (th);
1818 }
1819 else
1820 {
1821#if DEBUG_TRANSPORT
1822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1823 "Connection to `%4s' is not yet confirmed connected, scheduling timeout (%llums) only.\n",
1824 GNUNET_i2s (target), timeout.value);
1825#endif
1826 th->notify_delay_task
1827 = GNUNET_SCHEDULER_add_delayed (handle->sched,
1828 GNUNET_NO,
1829 GNUNET_SCHEDULER_PRIORITY_KEEP,
1830 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1831 timeout, &transmit_timeout, th);
1832 }
1833 }
1834 return th;
1835}
1836
1837
1838/**
1839 * Cancel the specified transmission-ready
1840 * notification.
1841 */
1842void
1843GNUNET_TRANSPORT_notify_transmit_ready_cancel (struct
1844 GNUNET_TRANSPORT_TransmitHandle
1845 *th)
1846{
1847 struct GNUNET_TRANSPORT_Handle *h;
1848
1849 GNUNET_assert (th->notify == &client_notify_wrapper);
1850 remove_from_any_list (th);
1851 h = th->handle;
1852 if ((h->connect_ready_head == NULL) && (h->network_handle != NULL))
1853 {
1854 GNUNET_NETWORK_notify_transmit_ready_cancel (h->network_handle);
1855 h->network_handle = NULL;
1856 h->transmission_scheduled = GNUNET_NO;
1857 }
1858 GNUNET_free (th->notify_cls);
1859 GNUNET_free (th);
1860}
1861
1862
1863/* end of transport_api.c */
diff --git a/src/upnp/Makefile.am b/src/upnp/Makefile.am
new file mode 100644
index 000000000..02a2b139d
--- /dev/null
+++ b/src/upnp/Makefile.am
@@ -0,0 +1,59 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11lib_LTLIBRARIES = libgnunetupnp.la
12
13libgnunetupnp_la_SOURCES = \
14 upnp.c upnp.h \
15 upnp_ip.c upnp_ip.h \
16 upnp_util.c upnp_util.h \
17 upnp_xmlnode.c upnp_xmlnode.h
18libgnunetupnp_la_CFLAGS = \
19 -I$(top_scrdir)/include \
20 @LIBCURL_CPPFLAGS@ @XML_CPPFLAGS@
21libgnunetupnp_la_LIBADD = \
22 $(top_builddir)/src/util/libgnunetutil.la \
23 $(GN_LIBINTL) @EXT_LIBS@ @XML_LIBS@ @LIBCURL@
24libgnunetupnp_la_LDFLAGS = \
25 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
26 -version-info 0:0:0
27
28
29bin_PROGRAMS = \
30 gnunet-upnp \
31 gnunet-service-upnp
32
33gnunet_upnp_SOURCES = \
34 gnunet-upnp.c
35gnunet_upnp_LDADD = \
36 $(top_builddir)/src/upnp/libgnunetupnp.la \
37 $(top_builddir)/src/util/libgnunetutil.la \
38 $(GN_LIBINTL)
39
40gnunet_service_upnp_SOURCES = \
41 gnunet-service-upnp.c
42gnunet_service_upnp_LDADD = \
43 $(top_builddir)/src/util/libgnunetutil.la \
44 $(GN_LIBINTL)
45
46
47check_PROGRAMS = \
48 test_upnp
49
50TESTS = $(check_PROGRAMS)
51
52test_upnp_SOURCES = \
53 test_upnp.c
54test_upnp_LDADD = \
55 $(top_builddir)/src/transport/libgnunetupnp.la \
56 $(top_builddir)/src/util/libgnunetutil.la
57
58
59# EXTRA_DIST = test_upnp_data.conf
diff --git a/src/upnp/draft-cheshire-nat-pmp.txt b/src/upnp/draft-cheshire-nat-pmp.txt
new file mode 100644
index 000000000..727b5fad7
--- /dev/null
+++ b/src/upnp/draft-cheshire-nat-pmp.txt
@@ -0,0 +1,1160 @@
1Document: draft-cheshire-nat-pmp-02.txt Stuart Cheshire
2Internet-Draft Marc Krochmal
3Category: Standards Track Apple Computer, Inc.
4Expires 14th March 2007 Kiren Sekar
5 Sharpcast, Inc.
6 14th September 2006
7
8 NAT Port Mapping Protocol (NAT-PMP)
9
10 <draft-cheshire-nat-pmp-02.txt>
11
12Status of this Memo
13
14 By submitting this Internet-Draft, each author represents that any
15 applicable patent or other IPR claims of which he or she is aware
16 have been or will be disclosed, and any of which he or she becomes
17 aware will be disclosed, in accordance with Section 6 of BCP 79.
18 For the purposes of this document, the term "BCP 79" refers
19 exclusively to RFC 3979, "Intellectual Property Rights in IETF
20 Technology", published March 2005.
21
22 Internet-Drafts are working documents of the Internet Engineering
23 Task Force (IETF), its areas, and its working groups. Note that
24 other groups may also distribute working documents as Internet-
25 Drafts.
26
27 Internet-Drafts are draft documents valid for a maximum of six months
28 and may be updated, replaced, or obsoleted by other documents at any
29 time. It is inappropriate to use Internet-Drafts as reference
30 material or to cite them other than as "work in progress."
31
32 The list of current Internet-Drafts can be accessed at
33 http://www.ietf.org/1id-abstracts.html
34
35 The list of Internet-Draft Shadow Directories can be accessed at
36 http://www.ietf.org/shadow.html
37
38
39Abstract
40
41 This document describes a protocol for automating the process of
42 creating Network Address Translation (NAT) port mappings. Included
43 in the protocol is a method for retrieving the public IP address of
44 a NAT gateway, thus allowing a client to make this public IP address
45 and port number known to peers that may wish to communicate with it.
46 This protocol is implemented in current Apple products including
47 Mac OS X, Bonjour for Windows, and AirPort wireless base stations.
48
49
50
51
52
53
54
55
56
57
58Expires 14th March 2007 Cheshire, et al. [Page 1]
59
60Internet Draft NAT Port Mapping Protocol 14th September 2006
61
62
631. Introduction
64
65 Network Address Translation (NAT) is a method of sharing one public
66 internet address with a number of devices. This document is focused
67 on what "IP Network Address Translator (NAT) Terminology and
68 Considerations" [RFC 2663] calls "NAPTs" (Network Address/Port
69 Translators). A full description of NAT is beyond the scope of this
70 document. The following brief overview will cover the aspects
71 relevant to this port mapping protocol. For more information on
72 NAT, see "Traditional IP Network Address Translator" [RFC 3022].
73
74 NATs have one or more public IP addresses. A private network is set
75 up behind the NAT. Devices behind the NAT are assigned private
76 addresses and the private address of the NAT device is used as the
77 gateway.
78
79 When a packet from any device behind the NAT is sent to an address on
80 the public internet, the packet first passes through the NAT box. The
81 NAT box looks at the source port and address. In some cases, a NAT
82 will also keep track of the destination port and address. The NAT
83 then creates a mapping from the private address and private port to a
84 public address and public port if a mapping does not already exist.
85 The NAT box replaces the private address and port number in the
86 packet with the public entries from the mapping and sends the packet
87 on to the next gateway.
88
89 When a packet from any address on the internet is received on the
90 NAT's public side, the NAT will look up the destination port (public
91 port) in the list of mappings. If an entry is found, it will contain
92 the private address and port that the packet should be sent to. The
93 NAT gateway will then rewrite the destination address and port with
94 those from the mapping. The packet will then be forwarded to the new
95 destination addresses. If the packet did not match any mapping, the
96 packet will most likely be dropped. Various NATs implement different
97 strategies to handle this. The important thing to note is that if
98 there is no mapping, the NAT does not know which private address the
99 packet should be sent to.
100
101 Mappings are usually created automatically as a result of observing
102 outbound traffic. There are a few exceptions. Some NATs may allow
103 manually-created permanent mappings that map a public port to a
104 specific private IP address and port. Such a mapping allows incoming
105 connections to the device with that private address. Some NATs also
106 implement a default mapping where any inbound traffic that does not
107 match a mapping will always be forwarded to a specific private
108 address. Both types of mappings are usually set up manually through
109 some configuration tool.
110
111 Without these manually-created inbound port mappings, clients behind
112 the NAT would be unable to receive inbound connections, which
113 represents a loss of connectivity when compared to the original
114
115
116Expires 14th March 2007 Cheshire, et al. [Page 2]
117
118Internet Draft NAT Port Mapping Protocol 14th September 2006
119
120
121 Internet architecture [ETEAISD]. For those who view this loss of
122 connectivity as a bad thing, NAT-PMP allows clients to operate much
123 more like a host directly connected to the unrestricted public
124 Internet, with an unrestricted public IP address. NAT-PMP allows
125 client hosts to communicate with the NAT gateway to request the
126 creation of inbound mappings on demand. Having created a NAT mapping
127 to allow inbound connections, the client can then record its public
128 IP address and public port number in a public registry (e.g. the
129 world-wide Domain Name System) or otherwise make it accessible to
130 peers that wish to communicate with it.
131
132
1332. Conventions and Terminology Used in this Document
134
135 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
136 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
137 document are to be interpreted as described in "Key words for use in
138 RFCs to Indicate Requirement Levels" [RFC 2119].
139
140
1413. Protocol and Packet Format
142
143 NAT Port Mapping Protocol runs over UDP. Every packet starts with an
144 8 bit version followed by an 8 bit operation code.
145
146 This document specifies version 0 of the protocol. Any NAT-PMP
147 gateway implementing this version of the protocol, receiving a
148 packet with a version number other than 0, MUST return result code 1
149 (Unsupported Version).
150
151 Opcodes between 0 and 127 are client requests. Opcodes from 128 to
152 255 are server responses. Responses always contain a 16 bit result
153 code in network byte order. A result code of zero indicates success.
154 Responses also contain a 32 bit unsigned integer corresponding to the
155 number of seconds since the NAT gateway was rebooted or since its
156 port mapping state was reset.
157
158 This protocol SHOULD only be used when the client determines that
159 its primary IPv4 address is in one of the private IP address ranges
160 defined in "Address Allocation for Private Internets" [RFC 1918].
161 This includes the address ranges 10/8, 172.16/12, and 192.168/16.
162
163 Clients always send their Port Mapping Protocol requests to their
164 default gateway, as learned via DHCP [RFC 2131], or similar means.
165 This protocol is designed for small home networks, with a single
166 logical link (subnet) where the client's default gateway is also the
167 NAT translator for that network. For more complicated networks where
168 the NAT translator is some device other than the client's default
169 gateway, this protocol is not appropriate.
170
171
172
173
174Expires 14th March 2007 Cheshire, et al. [Page 3]
175
176Internet Draft NAT Port Mapping Protocol 14th September 2006
177
178
1793.1 Requests and Responses
180
181 NAT gateways are often low-cost devices, with limited memory and
182 CPU speed. For this reason, to avoid making excessive demands on
183 the NAT gateway, clients machines SHOULD NOT issue multiple requests
184 simultaneously in parallel. If a client needs to perform multiple
185 requests (e.g. on boot, wake from sleep, network connection, etc.)
186 it SHOULD queue them and issue them serially one at a time. Once the
187 NAT gateway responds to one request the client machine may issue the
188 next. In the case of a fast NAT gateway, the client may be able to
189 complete requests at a rate of hundreds per second. In the case of
190 a slow NAT gateway that takes perhaps half a second to respond to
191 a NAT-PMP request, the client SHOULD respect this and allow the
192 NAT gateway to operate at the pace it can manage, and not overload
193 it by issuing requests faster than the rate it's answering them.
194
195 To determine the puclic IP address or request a port mapping,
196 a NAT-PMP client sends its request packet to port 5351 of its
197 configured gateway address, and waits 250ms for a response. If no
198 NAT-PMP response is received from the gateway after 250ms, the client
199 retransmits its request and waits 500ms. The client SHOULD repeat
200 this process with the interval between attempts doubling each time.
201 If, after sending its 9th attempt (and then waiting for 64 seconds),
202 the client has still received no response, then it SHOULD conclude
203 that this gateway does not support NAT Port Mapping Protocol and
204 MAY log an error message indicating this fact. In addition, if the
205 NAT-PMP client receives an "ICMP Port Unreachable" message from the
206 gateway for port 5351 then it can skip any remaining retransmissions
207 and conclude immediately that the gateway does not support NAT-PMP.
208
209 As a performance optimization the client MAY record this information
210 and use it to suppress further attempts to use NAT-PMP, but the
211 client should not retain this information for too long. In
212 particular, any event that may indicate a potential change of gateway
213 or a change in gateway configuration (hardware link change
214 indication, change of gateway MAC address, acquisition of new DHCP
215 lease, receipt of NAT-PMP announcement packet from gateway, etc.)
216 should cause the client to discard its previous information regarding
217 the gateway's lack of NAT-PMP support, and send its next NAT-PMP
218 request packet normally.
219
220
2213.2 Determining the Public Address
222
223 To determine the public address, the client behind the NAT sends the
224 following UDP payload to port 5351 of the configured gateway address:
225
226 0 1
227 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
228 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
229 | Vers = 0 | OP = 0 |
230 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
231
232Expires 14th March 2007 Cheshire, et al. [Page 4]
233
234Internet Draft NAT Port Mapping Protocol 14th September 2006
235
236
237 A compatible NAT gateway MUST generate a response with the following
238 format:
239
240 0 1 2 3
241 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
242 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
243 | Vers = 0 | OP = 128 + 0 | Result Code |
244 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
245 | Seconds Since Start of Epoch |
246 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247 | Public IP Address (a.b.c.d) |
248 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
249
250 This response indicates that the NAT gateway implements this version
251 of the protocol and returns the public IP address of the NAT gateway.
252 If the result code is non-zero, the value of Public IP Address is
253 undefined (MUST be set to zero on transmission, and MUST be ignored
254 on reception).
255
256 The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field
257 with the time elapsed since its port mapping table was initialized on
258 startup or reset for any other reason (see Section 3.6 "Seconds Since
259 Start of Epoch").
260
261 Upon receiving the response packet, the client MUST check the source
262 IP address, and silently discard the packet if the address is not the
263 address of the gateway to which the request was sent.
264
265
2663.2.1 Announcing Address Changes
267
268 When the public IP address of the NAT changes, the NAT gateway MUST
269 send a gratuitous response to the link-local multicast address
270 224.0.0.1, port 5351 with the packet format above to notify clients
271 of the new public IP address. To accommodate packet loss, the
272 NAT gateway SHOULD multicast 10 address change notifications.
273 The interval between the first two notifications SHOULD be 250ms,
274 and the interval between each subsequent notification SHOULD double.
275
276 Upon receiving a gratuitous address change announcement packet,
277 the client MUST check the source IP address, and silently discard
278 the packet if the address is not the address of the client's
279 current configured gateway. This is to guard against inadvertent
280 misconfigurations where there may be more than one NAT gateway
281 active on the network.
282
283
284
285
286
287
288
289
290Expires 14th March 2007 Cheshire, et al. [Page 5]
291
292Internet Draft NAT Port Mapping Protocol 14th September 2006
293
294
2953.3 Creating a Mapping
296
297 To create a mapping, the client sends a UDP packet to port 5351
298 of the gateway's private IP address with the following format:
299
300 0 1 2 3
301 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
302 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
303 | Vers = 0 | OP = x | Reserved (MUST be zero) |
304 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
305 | Private Port | Requested Public Port |
306 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307 | Requested Port Mapping Lifetime in Seconds |
308 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309
310 Opcodes supported:
311 1 - Map UDP
312 2 - Map TCP
313
314 The Reserved field MUST be set to zero on transmission and MUST
315 be ignored on reception.
316
317 The Private Port is set to the local port on which the client is
318 listening.
319
320 The Requested Public Port SHOULD usually be set to the same value as
321 the local Private Port, or zero if the client has no preference for
322 what port is assigned. However, the gateway is not obliged to assign
323 the port requested, and may choose not to, either for policy reasons
324 (e.g. port 80 is reserved and clients may not request it) or because
325 that port has already been assigned to some other client. Because
326 of this, some product developers have questioned the value of having
327 the Requested Public Port field at all. The reason is for failure
328 recovery. Most low-cost home NAT gateways do not record temporary
329 port mappings in persistent storage, so if the gateway crashes or is
330 rebooted, all the mappings are lost. A renewal packet is formatted
331 identically to an initial mapping request packet, except that for
332 renewals the client sets the Requested Public Port field to the
333 port the gateway actually assigned, rather than the port the client
334 originally wanted. When a freshly-rebooted NAT gateway receives a
335 renewal packet from a client, it appears to the gateway just like
336 an ordinary initial request for a port mapping, except that in this
337 case the Requested Public Port is likely to be one that the NAT
338 gateway *is* willing to allocate (it allocated it to this client
339 right before the reboot, so it should presumably be willing to
340 allocate it again).
341
342 The RECOMMENDED Port Mapping Lifetime is 3600 seconds.
343
344
345
346
347
348Expires 14th March 2007 Cheshire, et al. [Page 6]
349
350Internet Draft NAT Port Mapping Protocol 14th September 2006
351
352
353 After sending the port mapping request, the client then waits for the
354 NAT gateway to respond. If after 250ms, the gateway doesn't respond,
355 the client SHOULD re-issue its request as described above in Section
356 3.1 "Requests and Responses".
357
358 The NAT gateway responds with the following packet format:
359
360 0 1 2 3
361 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
362 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363 | Vers = 0 | OP = 128 + x | Result Code |
364 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 | Seconds Since Start of Epoch |
366 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 | Private Port | Mapped Public Port |
368 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 | Port Mapping Lifetime in Seconds |
370 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371
372 The 'x' in the OP field MUST match what the client requested. Some
373 NAT gateways are incapable of creating a UDP port mapping without
374 also creating a corresponding TCP port mapping, and vice versa, and
375 these gateways MUST NOT implement NAT Port Mapping Protocol until
376 this deficiency is fixed. A NAT gateway which implements this
377 protocol MUST be able to create TCP-only and UDP-only port mappings.
378
379 If a NAT gateway silently creates a pair of mappings for a client
380 that only requested one mapping, then it may expose that client to
381 receiving inbound UDP packets or inbound TCP connection requests
382 that it did not ask for and does not want.
383
384 While a NAT gateway MUST NOT automatically create mappings for TCP
385 when the client requests UDP, and vice versa, the NAT gateway MUST
386 reserve the companion port so the same client can choose to map it
387 in the future. For example, if a client requests to map TCP port 80,
388 as long as the client maintains the lease for that TCP port mapping,
389 another client with a different IP address MUST NOT be able to
390 successfully acquire the mapping for UDP port 80.
391
392 The client normally requests the public port matching the private
393 port. If that public port is not available, the NAT gateway MUST
394 return a public port that is available or return an error code if
395 no ports are available.
396
397 The source address of the packet MUST be used for the private address
398 in the mapping. This protocol is not intended to facilitate one
399 device behind a NAT creating mappings for other devices. If there
400 are legacy devices that require inbound mappings, permanent mappings
401 can be created manually by the administrator, just as they are today.
402
403
404
405
406Expires 14th March 2007 Cheshire, et al. [Page 7]
407
408Internet Draft NAT Port Mapping Protocol 14th September 2006
409
410
411 If a mapping already exists for a given private port on a given local
412 client (whether that mapping was created explicitly using NAT-PMP,
413 implicitly as a result of an outgoing TCP SYN packet, or manually by
414 a human administrator) and that client requests another mapping for
415 the same private port (possibly requesting a different public port)
416 then the mapping request should succeed, returning the already-
417 assigned public port. This is necessary to handle the case where
418 a client requests a mapping with requested public port X, and is
419 granted a mapping with actual public port Y, but the acknowledgement
420 packet gets lost. When the client retransmits its mapping request,
421 it should get back the same positive acknowledgement as was sent (and
422 lost) the first time.
423
424 The NAT gateway SHOULD NOT accept mapping requests destined to the
425 NAT gateway's public IP address or received on its public network
426 interface. Only packets received on the private interface(s) with
427 a destination address matching the private address(es) of the NAT
428 gateway should be allowed.
429
430 The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field
431 with the time elapsed since its port mapping table was initialized on
432 startup or reset for any other reason (see Section 3.6 "Seconds Since
433 Start of Epoch").
434
435 The Port Mapping Lifetime is an unsigned integer in seconds. The NAT
436 gateway MAY reduce the lifetime from what the client requested. The
437 NAT gateway SHOULD NOT offer a lease lifetime greater than that
438 requested by the client.
439
440 Upon receiving the response packet, the client MUST check the source
441 IP address, and silently discard the packet if the address is not the
442 address of the gateway to which the request was sent.
443
444 The client SHOULD begin trying to renew the mapping halfway to expiry
445 time, like DHCP. The renewal packet should look exactly the same as
446 a request packet, except that the client SHOULD set the requested
447 public port to what the NAT gateway previously mapped, not what the
448 client originally requested. As described above, this enables the
449 gateway to automatically recover its mapping state after a crash or
450 reboot.
451
452
4533.4 Destroying a Mapping
454
455 A mapping may be destroyed in a variety of ways. If a client fails
456 to renew a mapping, then when its lifetime expires the mapping MUST
457 be automatically deleted. In the common case where the gateway
458 device is a combined DHCP server and NAT gateway, when a client's
459 DHCP address lease expires, the gateway device MAY automatically
460 delete any mappings belonging to that client. Otherwise a new client
461 being assigned the same IP address could receive unexpected inbound
462
463
464Expires 14th March 2007 Cheshire, et al. [Page 8]
465
466Internet Draft NAT Port Mapping Protocol 14th September 2006
467
468
469 UDP packets or inbound TCP connection requests that it did not ask
470 for and does not want.
471
472 A client MAY also send an explicit packet to request deletion of a
473 mapping that is no longer needed. A client requests explicit
474 deletion of a mapping by sending a message to the NAT gateway
475 requesting the mapping, with the Requested Lifetime in Seconds set
476 to 0. The requested public port MUST be set to zero by the client
477 on sending, and MUST be ignored by the gateway on reception.
478
479 When a mapping is destroyed successfully as a result of the client
480 explicitly requesting the deletion, the NAT gateway MUST send a
481 response packet which is formatted as defined in section 3.3
482 "Creating a Mapping". The response MUST contain a result code of 0,
483 the private port as indicated in the deletion request, a public port
484 of 0, and a lifetime of 0. The NAT gateway MUST respond to a request
485 to destroy a mapping that does not exist as if the request were
486 successful. This is because of the case where the acknowledgement is
487 lost, and the client retransmits its request to delete the mapping.
488 In this case the second request to delete the mapping MUST return the
489 same response packet as the first request.
490
491 If the deletion request was unsuccessful, the response MUST contain a
492 non-zero result code and the requested mapping; the lifetime is
493 undefined (MUST be set to zero on transmission, and MUST be ignored
494 on reception). If the client attempts to delete a port mapping which
495 was manually assigned by some kind of configuration tool, the NAT
496 gateway MUST respond with a 'Not Authorized' error, result code 2.
497
498 When a mapping is destroyed as a result of its lifetime expiring or
499 for any other reason, if the NAT gateway's internal state indicates
500 that there are still active TCP connections traversing that now-
501 defunct mapping, then the NAT gateway SHOULD send appropriately-
502 constructed TCP RST (reset) packets both to the local client and to
503 the remote peer on the Internet to terminate that TCP connection.
504
505 A client can request the explicit deletion of all its UDP or TCP
506 mappings by sending the same deletion request to the NAT gateway
507 with public port, private port, and lifetime set to 0. A client MAY
508 choose to do this when it first acquires a new IP address in order to
509 protect itself from port mappings that were performed by a previous
510 owner of the IP address. After receiving such a deletion request,
511 the gateway MUST delete all its UDP or TCP port mappings (depending
512 on the opcode). The gateway responds to such a deletion request with
513 a response as described above, with the private port set to zero. If
514 the gateway is unable to delete a port mapping, for example, because
515 the mapping was manually configured by the administrator, the gateway
516 MUST still delete as many port mappings as possible, but respond with
517 a non-zero result code. The exact result code to return depends on
518 the cause of the failure. If the gateway is able to successfully
519 delete all port mappings as requested, it MUST respond with a result
520 code of 0.
521
522Expires 14th March 2007 Cheshire, et al. [Page 9]
523
524Internet Draft NAT Port Mapping Protocol 14th September 2006
525
526
5273.5 Result Codes
528
529 Currently defined result codes:
530 0 - Success
531 1 - Unsupported Version
532 2 - Not Authorized/Refused
533 (e.g. box supports mapping, but user has turned feature off)
534 3 - Network Failure
535 (e.g. NAT box itself has not obtained a DHCP lease)
536 4 - Out of resources
537 (NAT box cannot create any more mappings at this time)
538 5 - Unsupported opcode
539
540 If the result code is non-zero, the format of the packet following
541 the result code may be truncated. For example, if the client sends a
542 request to the server with an opcode of 17 and the server does not
543 recognize that opcode, the server SHOULD respond with a message where
544 the opcode is 17 + 128 and the result code is 5 (opcode not
545 supported). Since the server does not understand the format of
546 opcode 17, it may not know what to place after the result code. In
547 some cases, relevant data may follow the opcode to identify the
548 operation that failed. For example, a client may request a mapping
549 but that mapping may fail due to resource exhaustion. The server
550 SHOULD respond with the result code to indicate resource exhaustion
551 (4) followed by the requested port mapping so the client may identify
552 which operation failed.
553
554 Clients MUST be able to properly handle result codes not defined in
555 this document. Undefined results codes MUST be treated as fatal
556 errors of the request.
557
558
5593.6 Seconds Since Start of Epoch
560
561 Every packet sent by the NAT gateway includes a "Seconds since start
562 of epoch" field (SSSOE). If the NAT gateway resets or loses the
563 state of its port mapping table, due to reboot, power failure, or any
564 other reason, it MUST reset its epoch time and begin counting SSSOE
565 from 0 again. Whenever a client receives any packet from the NAT
566 gateway, either gratuitously or in response to a client request, the
567 client computes its own conservative estimate of the expected SSSOE
568 value by taking the SSSOE value in the last packet it received from
569 the gateway and adding 7/8 (87.5%) of the time elapsed since that
570 packet was received. If the SSSOE in the newly received packet is
571 less than the client's conservative estimate by more than one second,
572 then the client concludes that the NAT gateway has undergone a reboot
573 or other loss of port mapping state, and the client MUST immediately
574 renew all its active port mapping leases as described in Section 3.7
575 "Recreating Mappings On NAT Gateway Reboot".
576
577
578
579
580Expires 14th March 2007 Cheshire, et al. [Page 10]
581
582Internet Draft NAT Port Mapping Protocol 14th September 2006
583
584
5853.7 Recreating Mappings On NAT Gateway Reboot
586
587 The NAT gateway MAY store mappings in persistent storage so when it
588 is powered off or rebooted, it remembers the port mapping state of
589 the network.
590
591 However, maintaining this state is not essential for correct
592 operation. When the NAT gateway powers on or clears its port mapping
593 state as the result of a configuration change, it MUST reset the
594 epoch time and re-announce its IP address as described in Section
595 3.2.1 "Announcing Address Changes". Reception of this packet where
596 time has apparently gone backwards serves as a hint to clients
597 on the network that they SHOULD immediately send renewal packets
598 (to immediately recreate their mappings) instead of waiting until
599 the originally scheduled time for those renewals. Clients who miss
600 receiving those gateway announcement packets for any reason will
601 still renew their mappings at the originally scheduled time and cause
602 their mappings to be recreated; it will just take a little longer for
603 these clients.
604
605 A mapping renewal packet is formatted identically to an original
606 mapping request; from the point of view of the client it is a
607 renewal of an existing mapping, but from the point of view of the
608 freshly-rebooted NAT gateway it appears as a new mapping request.
609
610 This self-healing property of the protocol is very important.
611
612 The remarkable reliability of the Internet as a whole derives
613 in large part from the fact that important state is held in the
614 endpoints, not in the network itself [ETEAISD]. Power-cycling an
615 Ethernet switch results only in a brief interruption in the flow
616 of packets; established TCP connections through that switch are not
617 broken, merely delayed for a few seconds. Indeed, an old Ethernet
618 switch can even be replaced with a new one, and as long as the cables
619 are transferred over reasonably quickly, after the upgrade all the
620 TCP connections that were previously going though the old switch will
621 be unbroken and now going through the new one. The same is true of
622 IP routers, wireless base stations, etc. The one exception is NAT
623 gateways. Because the port mapping state is required for the NAT
624 gateway to know where to forward inbound packets, loss of that state
625 breaks connectivity through the NAT gateway. By allowing clients to
626 detect when this loss of NAT gateway state has occurred, and recreate
627 it on demand, we turn hard state in the network into soft state, and
628 allow it to be recovered automatically when needed.
629
630 Without this automatic recreation of soft state in the NAT gateway,
631 reliable long-term networking would not be achieved. As mentioned
632 above, the reliability of the Internet does not come from trying
633 to build a perfect network in which errors never happen, but from
634 accepting that in any sufficiently large system there will always be
635 some component somewhere that's failing, and designing mechanisms
636
637
638Expires 14th March 2007 Cheshire, et al. [Page 11]
639
640Internet Draft NAT Port Mapping Protocol 14th September 2006
641
642
643 that can handle those failures and recover. To illustrate this point
644 with an example, consider the following scenario: Imagine a network
645 security camera that has a web interface and accepts incoming
646 connections from web browser clients. Imagine this network security
647 camera uses NAT-PMP or a similar protocol to set up an inbound
648 port mapping in the NAT gateway so that it can receive incoming
649 connections from clients the other side of the NAT gateway.
650 Now, this camera may well operate for weeks, months, or even years.
651 During that time it's possible that the NAT gateway could experience
652 a power failure or be rebooted. The user could upgrade the NAT
653 gateway's firmware, or even replace the entire NAT gateway device
654 with a newer model. The general point is that if the camera operates
655 for a long enough period of time, some kind of disruption to the NAT
656 gateway becomes inevitable. The question is not whether the NAT
657 gateway will lose its port mappings, but when, and how often.
658 If the network camera and devices like it on the network can detect
659 when the NAT gateway has lost its port mappings, and recreate them
660 automatically, then these disruptions are self-correcting and
661 invisible to the end user. If, on the other hand, the disruptions are
662 not self-correcting, and after a NAT gateway reboot the user has to
663 manually reset or reboot all the other devices on the network too,
664 then these disruptions are *very* visible to the end user. This
665 aspect of the design is what makes the difference between a protocol
666 that keeps on working indefinitely over a time scale of months or
667 years, and a protocol that works in brief testing, but in the real
668 world is continually failing and requiring manual intervention to get
669 it going again.
670
671 When a client renews its port mappings as the result of receiving
672 a packet where the "Seconds since start of epoch" field (SSSOE)
673 indicates that a reboot or similar loss of state has occurred,
674 the client MUST first delay by a random amount of time selected
675 with uniform random distribution in the range 0 to 5 seconds, and
676 then send its first port mapping request. After that request is
677 acknowledged by the gateway, the client may then send its second
678 request, and so on, as rapidly as the gateway allows. The requests
679 SHOULD be issued serially, one at a time; the client SHOULD NOT issue
680 multiple requests simultaneously in parallel.
681
682 The discussion in this section focusses on recreating inbound port
683 mappings after loss of NAT gateway state, because that is the more
684 serious problem. Losing port mappings for outgoing connections
685 destroys those currently active connections, but does not prevent
686 clients from establishing new outgoing connections. In contrast,
687 losing inbound port mappings not only destroys all existing inbound
688 connections, but also prevents the reception of any new inbound
689 connections until the port mapping is recreated. Accordingly,
690 we consider recovery of inbound port mappings the more important
691 priority. However, clients that want outgoing connections to survive
692 a NAT gateway reboot can also achieve that using NAT-PMP. After
693 initiating an outbound TCP connection (which will cause the NAT
694
695
696Expires 14th March 2007 Cheshire, et al. [Page 12]
697
698Internet Draft NAT Port Mapping Protocol 14th September 2006
699
700
701 gateway to establish an implicit port mapping) the client should send
702 the NAT gateway a port mapping request for the source port of its TCP
703 connection, which will cause the NAT gateway to send a response
704 giving the public port it allocated for that mapping. The client can
705 then store this information, and use later to recreate the mapping
706 if it determines that the NAT gateway has lost its mapping state.
707
708
7093.8 NAT Gateways with NAT Function Disabled
710
711 Note that *only* devices currently acting in the role of NAT gateway
712 should participate in NAT-PMP protocol exchanges with clients.
713 A network device that is capable of NAT (and NAT-PMP), but is
714 currently configured not to perform that function, (e.g. it is
715 acting as a traditional IP router, forwarding packets without
716 modifying them), MUST NOT respond to NAT-PMP requests from clients,
717 or send spontaneous NAT-PMP address-change announcements.
718
719 In particular, a network device not currently acting in the role of
720 NAT gateway should not even respond to NAT-PMP requests by returning
721 an error code such as "2 - Not Authorized/Refused", since to do so
722 is misleading to clients -- it suggests that NAT port mapping is
723 necessary on this network for the client to successfully receive
724 inbound connections, but is not available because the administrator
725 has chosen to disable that functionality.
726
727 Clients should also be careful to avoid making unfounded assumptions,
728 such as the assumption that if the client has an IPv4 address in
729 one of the RFC 1918 private IP address ranges then that means
730 NAT necessarily must be in use. Net 10/8 has enough addresses
731 to build a private network with millions of hosts and thousands
732 of interconnected subnets, all without any use of NAT. Many
733 organizations have built such private networks that benefit from
734 using standard TCP/IP technology, but by choice do not connect
735 to the public Internet. The purpose of NAT-PMP is to mitigate some
736 of the damage caused by NAT. It would be an ironic and unwanted
737 side-effect of this protocol if it were to lead well-meaning but
738 misguided developers to create products that refuse to work on a
739 private network *unless* they can find a NAT gateway to talk to.
740 Consequently, a client finding that NAT-PMP is not available on its
741 network should not give up, but should proceed on the assumption
742 that the network may be a traditional routed IP network, with no
743 address translation being used. This assumption may not always be
744 true, but it is better than the alternative of falsely assuming
745 the worst and not even trying to use normal (non-NAT) IP networking.
746
747 If a network device not currently acting in the role of NAT gateway
748 receives UDP packets addressed to port 5351, it SHOULD respond
749 immediately with an "ICMP Port Unreachable" message to tell the
750 client that it needn't continue with timeouts and retransmissions,
751 and it should assume that NAT-PMP is not available and not needed
752 on this network.
753
754Expires 14th March 2007 Cheshire, et al. [Page 13]
755
756Internet Draft NAT Port Mapping Protocol 14th September 2006
757
758
7594. UNSAF Considerations
760
761 The document "IAB Considerations for UNSAF Across NAT" [RFC 3424]
762 covers a number of issues when working with NATs. RFC 3424 outlines
763 some requirements for any document that attempts to work around
764 problems associated with NATs. This section addresses those
765 requirements.
766
767
7684.1 Scope
769
770 This protocol addresses the needs of TCP and UDP transport peers that
771 are separated from the public internet by exactly one NAT. Such
772 peers must have access to some form of directory server for
773 registering the public IP address and port at which they can be
774 reached.
775
776
7774.2 Transition Plan
778
779 Any client making use of this protocol SHOULD implement IPv6 support.
780 If a client supports IPv6 and is running on a device with a global
781 IPv6 address, that IPv6 address SHOULD be preferred to the IPv4
782 public address using this NAT mapping protocol. In case other
783 clients do not have IPv6 connectivity, both the IPv4 and IPv6
784 addresses SHOULD be registered with whatever form of directory server
785 is used. Preference SHOULD be given to IPv6 addresses when
786 available. By implementing support for IPv6 and using this protocol
787 for IPv4, vendors can ship products today that will work under both
788 scenarios. As IPv6 is more widely deployed, clients of this protocol
789 following these recommendations will transparently make use of IPv6.
790
791
7924.3 Failure Cases
793
794 Aside from NATs that do not implement this protocol, there are a
795 number of situations where this protocol may not work.
796
797
7984.3.1 NAT Behind NAT
799
800 Some people's primary IP address, assigned by their ISP, may itself
801 be a NAT address. In addition, some people may have a public IP
802 address, but may then double NAT themselves, perhaps by choice or
803 perhaps by accident. Although it might be possible in principle for
804 one NAT gateway to recursively request a mapping from the next one,
805 this document does not advocate that and does not try to prescribe
806 how it would be done.
807
808 It would be a lot of work to implement nested NAT port mapping
809 correctly, and there are a number of reasons why the end result might
810
811
812Expires 14th March 2007 Cheshire, et al. [Page 14]
813
814Internet Draft NAT Port Mapping Protocol 14th September 2006
815
816
817 not be as useful as we might hope. Consider the case of an ISP that
818 offers each of its customers only a single NAT address. This ISP
819 could instead have chosen to provide each customer with a single
820 public IP address, or, if the ISP insists on running NAT, it could
821 have chosen to allow each customer a reasonable number of addresses,
822 enough for each customer device to have its own NAT address directly
823 from the ISP. If instead this ISP chooses to allow each customer
824 just one and only one NAT address, forcing said customer to run
825 nested NAT in order to use more than one device, it seems unlikely
826 that such an ISP would be so obliging as to provide a NAT service
827 that supports NAT Port Mapping Protocol. Supposing that such an ISP
828 did wish to offer its customers NAT service with NAT-PMP so as to
829 give them the ability to receive inbound connections, this ISP could
830 easily choose to allow each client to request a reasonable number of
831 DHCP addresses from that gateway. Remember that Net 10/8 [RFC 1918]
832 allows for over 16 million addresses, so NAT addresses are not in any
833 way in short supply. A single NAT gateway with 16 million available
834 addresses is likely to run out of packet forwarding capacity before
835 it runs out of private addresses to hand out. In this way the ISP
836 could offer single-level NAT with NAT-PMP, obviating the need to
837 support nested NAT-PMP. In addition, an ISP that is motivated to
838 provide their customers with unhindered access to the Internet by
839 allowing incoming as well as outgoing connections has better ways
840 to offer this service. Such an ISP could offer its customers real
841 public IP addresses instead of NAT addresses, or could even choose
842 to offer its customers full IPv6 connectivity, where no mapping or
843 translation is required at all.
844
845
8464.3.2 NATs with Multiple Public IP Addresses
847
848 If a NAT maps private addresses to multiple public addresses,
849 then it SHOULD pick one of those addresses as the one it will
850 support for inbound connections, and for the purposes of this
851 protocol it SHOULD act as if that address were its only address.
852
853
8544.3.3 NATs and Routed Private Networks
855
856 In some cases, a large network may be subnetted. Some sites
857 may install a NAT gateway and subnet the private network.
858 Such subnetting breaks this protocol because the router address
859 is not necessarily the address of the device performing NAT.
860
861 Addressing this problem is not a high priority. Any site with the
862 resources to set up such a configuration should have the resources to
863 add manual mappings or attain a range of globally unique addresses.
864
865 Not all NATs will support this protocol. In the case where a client
866 is run behind a NAT that does not support this protocol, the software
867 relying on the functionality of this protocol may be unusable.
868
869
870Expires 14th March 2007 Cheshire, et al. [Page 15]
871
872Internet Draft NAT Port Mapping Protocol 14th September 2006
873
874
8754.3.4 Communication Between Hosts Behind the Same NAT
876
877 NAT gateways supporting NAT-PMP should also implement "hairpin
878 translation". Hairpin translation means supporting communication
879 between two local clients being served by the same NAT gateway.
880
881 Suppose device A is listening on private address and port 10.0.0.2:80
882 for incoming connections. Using NAT-PMP, device A has obtained a
883 mapping to public address and port x.x.x.x:80, and has recorded this
884 public address and port in a public directory of some kind. For
885 example, it could have created a DNS SRV record containing this
886 information, and recorded it, using DNS Dynamic Update [RFC 3007], in
887 a publicly accessible DNS server. Suppose then that device B, behind
888 the same NAT gateway as device A, but unknowing or uncaring of this
889 fact, retrieves device A's DNS SRV record and attempts to open a TCP
890 connection to x.x.x.x:80. The outgoing packets addressed to this
891 public Internet address will be sent to the NAT gateway for
892 translation and forwarding. Having translated the source address and
893 port number on the outgoing packet, the NAT gateway needs to be smart
894 enough to recognize that the destination address is in fact itself,
895 and then feed this packet back into its packet reception engine, to
896 perform the destination port mapping lookup to translate and forward
897 this packet to device A at address and port 10.0.0.2:80.
898
8994.3.5 Non UDP/TCP Transport Traffic
900
901 Any communication over transport protocols other than TCP and UDP
902 will not be served by this protocol. Examples are Generic Routing
903 Encapsulation (GRE), Authentication Header (AH) and Encapsulating
904 Security Payload (ESP).
905
9064.4 Long Term Solution
907
908 As IPv6 is deployed, clients of this protocol supporting IPv6 will be
909 able to bypass this protocol and the NAT when communicating with
910 other IPv6 devices. In order to ensure this transition, any client
911 implementing this protocol SHOULD also implement IPv6 and use this
912 solution only when IPv6 is not available to both peers.
913
9144.5 Existing Deployed NATs
915
916 Existing deployed NATs will not support this protocol. This protocol
917 will only work with NATs that are upgraded to support it.
918
919
920
921
922
923
924
925
926
927
928Expires 14th March 2007 Cheshire, et al. [Page 16]
929
930Internet Draft NAT Port Mapping Protocol 14th September 2006
931
932
9335. Security Considerations
934
935 As discussed in section 3.2 "Determining the Public Address", only
936 clients on the private side of the NAT may create port mappings, and
937 only on behalf of themselves. By using IP address spoofing, it's
938 possible for one client to delete the port mappings of another
939 client. It's also possible for one client to create port mappings on
940 behalf of another client. The best way to deal with this
941 vulnerability is to use IPSec [RFC 2401].
942
943 Since allowing incoming connections is often a policy decision, any
944 NAT gateway implementing this protocol SHOULD have an administrative
945 mechanism to disable it.
946
947 Some people view the property that NATs block inbound connections as
948 a security benefit which is undermined by this protocol. The authors
949 of this document have a different point of view. In the days before
950 NAT, all hosts had unique public IP addresses, and had unhindered
951 ability to communicate with any other host on the Internet. When NAT
952 came along it broke this unhindered connectivity, relegating many
953 hosts to second-class status, unable to receive inbound connections.
954 This protocol goes some way to undo some of that damage. The purpose
955 of a NAT gateway should be to allow several hosts to share a single
956 address, not to simultaneously impede those host's ability to
957 communicate freely. Security is most properly provided by end-to-end
958 cryptographic security, and/or by explicit firewall functionality, as
959 appropriate. Blocking of certain connections should occur only as a
960 result of explicit and intentional firewall policy, not as an
961 accidental side-effect of some other technology.
962
963
9646. IANA Considerations
965
966 No IANA services are required by this document.
967
968
9697. Acknowledgments
970
971 The concepts described in this document have been explored, developed
972 and implemented with help from Bob Bradley, Josh Graessley, Rob
973 Newberry, Roger Pantos, John Saxton, and James Woodyatt.
974
975
9768. Deployment History
977
978 NAT-PMP client software first became available to the public
979 through Apple's Darwin Open Source code in August 2004.
980 NAT-PMP implementations began shipping to end users in large
981 volumes (i.e. millions) with the launch of Mac OS X 10.4 Tiger
982 and Bonjour for Windows 1.0 in April 2005.
983
984
985
986Expires 14th March 2007 Cheshire, et al. [Page 17]
987
988Internet Draft NAT Port Mapping Protocol 14th September 2006
989
990
991 The NAT-PMP client in Mac OS X 10.4 Tiger and Bonjour for Windows
992 exists as part of the mDNSResponder system service. When a client
993 advertises a service using Wide Area Bonjour [DNS-SD], and the
994 machine is behind a NAT-PMP-capable NAT gateway, then if the machine
995 is so configured, the mDNSResponder system service automatically uses
996 NAT-PMP to set up an inbound port mapping, and then records the
997 public IP address and port in the global DNS. Existing client
998 software using the existing Bonjour programming APIs [Bonjour]
999 gets this functionality automatically. The logic is that if client
1000 software publishes its information into the global DNS via Wide Area
1001 Bonjour service advertising, then it's reasonable to infer an
1002 expectation that this information should be usable by the peers
1003 retrieving it. Generally speaking, recording a private IP address
1004 like 10.0.0.2 in the public DNS is completely pointless because that
1005 address is not reachable from clients on the other side of the NAT
1006 gateway. In the case of a home user with a single computer directly
1007 connected to their Cable or DSL modem, with a single global IPv4
1008 address and no NAT gateway (a surprisingly common configuration),
1009 publishing that IP address into the global DNS is useful because that
1010 IP address is reachable. In contrast, a home user using a NAT gateway
1011 to share a single global IPv4 address between several computers loses
1012 this ability to receive inbound connections easily. This breaks many
1013 peer-to-peer collaborative applications, like the multi-user text
1014 editor SubEthaEdit [SEE]. Automatically creating the necessary
1015 inbound port mappings helps remedy this unintended side-effect of
1016 NAT.
1017
1018 The server side of the NAT-PMP protocol is implemented in Apple's
1019 "AirPort Extreme" and "AirPort Express" wireless base stations.
1020
1021
10229. Copyright Notice
1023
1024 Copyright (C) The Internet Society (2006).
1025
1026 This document is subject to the rights, licenses and restrictions
1027 contained in BCP 78, and except as set forth therein, the authors
1028 retain all their rights. For the purposes of this document,
1029 the term "BCP 78" refers exclusively to RFC 3978, "IETF Rights
1030 in Contributions", published March 2005.
1031
1032 This document and the information contained herein are provided on
1033 an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE
1034 REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE
1035 INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR
1036 IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
1037 THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
1038 WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
1039
1040
1041
1042
1043
1044Expires 14th March 2007 Cheshire, et al. [Page 18]
1045
1046Internet Draft NAT Port Mapping Protocol 14th September 2006
1047
1048
104910. Normative References
1050
1051 [RFC 1918] Y. Rekhter et.al., "Address Allocation for Private
1052 Internets", RFC 1918, February 1996.
1053
1054 [RFC 2119] RFC 2119 - Key words for use in RFCs to Indicate
1055 Requirement Levels
1056
1057
105811. Informative References
1059
1060 [Bonjour] Apple "Bonjour" <http://developer.apple.com/bonjour/>
1061
1062 [ETEAISD] J. Saltzer, D. Reed and D. Clark: "End-to-end arguments in
1063 system design", ACM Trans. Comp. Sys., 2(4):277-88, Nov.
1064 1984
1065
1066 [DNS-SD] Cheshire, S., and M. Krochmal, "DNS-Based Service
1067 Discovery", Internet-Draft (work in progress),
1068 draft-cheshire-dnsext-dns-sd-04.txt, August 2006.
1069
1070 [mDNS] Cheshire, S., and M. Krochmal, "Multicast DNS",
1071 Internet-Draft (work in progress),
1072 draft-cheshire-dnsext-multicastdns-06.txt, August 2006.
1073
1074 [RFC 2131] R. Droms, "Dynamic Host Configuration Protocol", RFC 2131,
1075 March 1997.
1076
1077 [RFC 2401] Atkinson, R. and S. Kent, "Security Architecture for the
1078 Internet Protocol", RFC 2401, November 1998.
1079
1080 [RFC 2663] Srisuresh, P. and M. Holdrege, "IP Network Address
1081 Translator (NAT) Terminology and Considerations", RFC
1082 2663, August 1999.
1083
1084 [RFC 3007] Wellington, B., "Simple Secure Domain Name System
1085 (DNS) Dynamic Update", RFC 3007, November 2000.
1086
1087 [SEE] <http://www.codingmonkeys.de/subethaedit/>
1088
1089 [RFC 3022] RFC 3022 - Network Address Translator
1090
1091 [RFC 3424] RFC 3424 - IAB Considerations for UNilateral Self-Address
1092 Fixing (UNSAF) Across Network Address Translation
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102Expires 14th March 2007 Cheshire, et al. [Page 19]
1103
1104Internet Draft NAT Port Mapping Protocol 14th September 2006
1105
1106
110712. Authors' Addresses
1108
1109 Stuart Cheshire
1110 Apple Computer, Inc.
1111 1 Infinite Loop
1112 Cupertino
1113 California 95014
1114 USA
1115
1116 Phone: +1 408 974 3207
1117 EMail: rfc [at] stuartcheshire [dot] org
1118
1119
1120 Marc Krochmal
1121 Apple Computer, Inc.
1122 1 Infinite Loop
1123 Cupertino
1124 California 95014
1125 USA
1126
1127 Phone: +1 408 974 4368
1128 EMail: marc [at] apple [dot] com
1129
1130
1131 Kiren Sekar
1132 Sharpcast, Inc.
1133 250 Cambridge Ave, Suite 101
1134 Palo Alto
1135 California 94306
1136 USA
1137
1138 Phone: +1 650 323 1960
1139 EMail: ksekar [at] sharpcast [dot] com
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160Expires 14th March 2007 Cheshire, et al. [Page 20]
diff --git a/src/upnp/test_upnp.c b/src/upnp/test_upnp.c
new file mode 100644
index 000000000..628b40d7c
--- /dev/null
+++ b/src/upnp/test_upnp.c
@@ -0,0 +1,110 @@
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 * @file src/transports/upnp/upnptest.c
22 * @brief Testcase for UPnP
23 * @author Christian Grothoff
24 */
25
26#include "gnunet_util.h"
27#include "gnunet_upnp_service.h"
28#include "gnunet_core.h"
29#include "platform.h"
30
31
32
33int
34main (int argc, const char *argv[])
35{
36 static GNUNET_CoreAPIForPlugins capi;
37 struct GNUNET_GE_Context *ectx;
38 struct GNUNET_GC_Configuration *cfg;
39 struct in_addr addr;
40 int i;
41 GNUNET_UPnP_ServiceAPI *upnp;
42 struct GNUNET_PluginHandle *plug;
43 GNUNET_ServicePluginInitializationMethod init;
44 GNUNET_ServicePluginShutdownMethod done;
45 char ntop_buf[INET_ADDRSTRLEN];
46
47 ectx = GNUNET_GE_create_context_stderr (GNUNET_NO,
48 GNUNET_GE_WARNING | GNUNET_GE_ERROR
49 | GNUNET_GE_FATAL | GNUNET_GE_USER |
50 GNUNET_GE_ADMIN |
51 GNUNET_GE_DEVELOPER |
52 GNUNET_GE_IMMEDIATE |
53 GNUNET_GE_BULK);
54 GNUNET_GE_setDefaultContext (ectx);
55 cfg = GNUNET_GC_create ();
56 GNUNET_GE_ASSERT (ectx, cfg != NULL);
57 GNUNET_os_init (ectx);
58 capi.ectx = ectx;
59 capi.cfg = cfg;
60 plug = GNUNET_plugin_load (ectx, "libgnunet", "module_upnp");
61 if (plug == NULL)
62 {
63 GNUNET_GC_free (cfg);
64 GNUNET_GE_free_context (ectx);
65 return 1;
66 }
67 init = GNUNET_plugin_resolve_function (plug, "provide_", GNUNET_YES);
68 if (init == NULL)
69 {
70 GNUNET_plugin_unload (plug);
71 GNUNET_GC_free (cfg);
72 GNUNET_GE_free_context (ectx);
73 return 1;
74 }
75 upnp = init (&capi);
76 if (upnp == NULL)
77 {
78 GNUNET_plugin_unload (plug);
79 GNUNET_GC_free (cfg);
80 GNUNET_GE_free_context (ectx);
81 return 1;
82 }
83 for (i = 0; i < 10; i++)
84 {
85 if (GNUNET_shutdown_test () != GNUNET_NO)
86 break;
87 if (GNUNET_OK == upnp->get_ip (2086, "TCP", &addr))
88 {
89 printf ("UPnP returned external IP %s\n",
90 inet_ntop (AF_INET, &addr, ntop_buf, INET_ADDRSTRLEN));
91 }
92 else
93 {
94 /* we cannot be sure that there is a UPnP-capable
95 NAT-box out there, so test should not fail
96 just because of this! */
97 printf ("No UPnP response (yet).\n");
98 }
99 GNUNET_thread_sleep (2 * GNUNET_CRON_SECONDS);
100 }
101 done = GNUNET_plugin_resolve_function (plug, "release_", GNUNET_YES);
102 if (done != NULL)
103 done ();
104 GNUNET_plugin_unload (plug);
105 GNUNET_GC_free (cfg);
106 GNUNET_GE_free_context (ectx);
107 return 0;
108}
109
110/* end of upnptest.c */
diff --git a/src/upnp/upnp.c b/src/upnp/upnp.c
new file mode 100644
index 000000000..6dc4338a0
--- /dev/null
+++ b/src/upnp/upnp.c
@@ -0,0 +1,721 @@
1/**
2 * @file upnp.c UPnP Implementation
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include "platform.h"
27#include "upnp_xmlnode.h"
28#include "upnp_util.h"
29#include "upnp.h"
30
31#include <curl/curl.h>
32
33#define TRUE GNUNET_YES
34#define FALSE GNUNET_NO
35#define g_return_if_fail(a) if(!(a)) return;
36#define g_return_val_if_fail(a, val) if(!(a)) return (val);
37
38#define HTTP_OK "200 OK"
39#define NUM_UDP_ATTEMPTS 2
40#define HTTPMU_HOST_ADDRESS "239.255.255.250"
41#define HTTPMU_HOST_PORT 1900
42#define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s"
43#define SEARCH_REQUEST_STRING \
44 "M-SEARCH * HTTP/1.1\r\n" \
45 "MX: 2\r\n" \
46 "HOST: 239.255.255.250:1900\r\n" \
47 "MAN: \"ssdp:discover\"\r\n" \
48 "ST: urn:schemas-upnp-org:service:%s\r\n" \
49 "\r\n"
50#define WAN_IP_CONN_SERVICE "WANIPConnection:1"
51#define WAN_PPP_CONN_SERVICE "WANPPPConnection:1"
52#define HTTP_POST_SOAP_HEADER \
53 "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\""
54#define HTTP_POST_SIZE_HEADER "CONTENT-LENGTH: %u"
55#define SOAP_ACTION \
56 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
57 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
58 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
59 "<s:Body>\r\n" \
60 "<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s\">\r\n" \
61 "%s" \
62 "</u:%s>\r\n" \
63 "</s:Body>\r\n" \
64 "</s:Envelope>"
65#define PORT_MAPPING_LEASE_TIME "0"
66#define PORT_MAPPING_DESCRIPTION "GNUNET_UPNP_PORT_FORWARD"
67#define ADD_PORT_MAPPING_PARAMS \
68 "<NewRemoteHost></NewRemoteHost>\r\n" \
69 "<NewExternalPort>%i</NewExternalPort>\r\n" \
70 "<NewProtocol>%s</NewProtocol>\r\n" \
71 "<NewInternalPort>%i</NewInternalPort>\r\n" \
72 "<NewInternalClient>%s</NewInternalClient>\r\n" \
73 "<NewEnabled>1</NewEnabled>\r\n" \
74 "<NewPortMappingDescription>" \
75 PORT_MAPPING_DESCRIPTION \
76 "</NewPortMappingDescription>\r\n" \
77 "<NewLeaseDuration>" \
78 PORT_MAPPING_LEASE_TIME \
79 "</NewLeaseDuration>\r\n"
80#define DELETE_PORT_MAPPING_PARAMS \
81 "<NewRemoteHost></NewRemoteHost>\r\n" \
82 "<NewExternalPort>%i</NewExternalPort>\r\n" \
83 "<NewProtocol>%s</NewProtocol>\r\n"
84
85typedef enum
86{
87 GAIM_UPNP_STATUS_UNDISCOVERED = -1,
88 GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER,
89 GAIM_UPNP_STATUS_DISCOVERING,
90 GAIM_UPNP_STATUS_DISCOVERED
91} GaimUPnPStatus;
92
93typedef struct
94{
95 GaimUPnPStatus status;
96 char *control_url;
97 const char *service_type;
98 char publicip[16];
99} GaimUPnPControlInfo;
100
101typedef struct
102{
103 const char *service_type;
104 char *full_url;
105 char *buf;
106 unsigned int buf_len;
107 int sock;
108} UPnPDiscoveryData;
109
110static GaimUPnPControlInfo control_info = {
111 GAIM_UPNP_STATUS_UNDISCOVERED,
112 NULL,
113 NULL,
114 "",
115};
116
117/**
118 * This is the signature used for functions that act as a callback
119 * to CURL.
120 */
121typedef size_t (*GaimUtilFetchUrlCallback) (void *url_data,
122 size_t size,
123 size_t nmemb, void *user_data);
124
125
126
127static char *
128g_strstr_len (const char *haystack, int haystack_len, const char *needle)
129{
130 int i;
131
132 g_return_val_if_fail (haystack != NULL, NULL);
133 g_return_val_if_fail (needle != NULL, NULL);
134
135 if (haystack_len < 0)
136 return strstr (haystack, needle);
137 else
138 {
139 const char *p = haystack;
140 int needle_len = strlen (needle);
141 const char *end = haystack + haystack_len - needle_len;
142
143 if (needle_len == 0)
144 return (char *) haystack;
145
146 while (*p && p <= end)
147 {
148 for (i = 0; i < needle_len; i++)
149 if (p[i] != needle[i])
150 goto next;
151
152 return (char *) p;
153
154 next:
155 p++;
156 }
157 }
158
159 return NULL;
160}
161
162static int
163gaim_upnp_compare_device (const xmlnode * device, const char *deviceType)
164{
165 xmlnode *deviceTypeNode = xmlnode_get_child (device, "deviceType");
166 char *tmp;
167 int ret;
168
169 if (deviceTypeNode == NULL)
170 return FALSE;
171 tmp = xmlnode_get_data (deviceTypeNode);
172 ret = !strcasecmp (tmp, deviceType);
173 GNUNET_free (tmp);
174 return ret;
175}
176
177static int
178gaim_upnp_compare_service (const xmlnode * service, const char *serviceType)
179{
180 xmlnode *serviceTypeNode;
181 char *tmp;
182 int ret;
183
184 if (service == NULL)
185 return FALSE;
186 serviceTypeNode = xmlnode_get_child (service, "serviceType");
187 if (serviceTypeNode == NULL)
188 return FALSE;
189 tmp = xmlnode_get_data (serviceTypeNode);
190 ret = !strcasecmp (tmp, serviceType);
191 GNUNET_free (tmp);
192 return ret;
193}
194
195static char *
196gaim_upnp_parse_description_response (const char *httpResponse,
197 size_t len,
198 const char *httpURL,
199 const char *serviceType)
200{
201 char *xmlRoot, *baseURL, *controlURL, *service;
202 xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
203 char *tmp;
204
205 /* find the root of the xml document */
206 xmlRoot = g_strstr_len (httpResponse, len, "<root");
207 if (xmlRoot == NULL)
208 return NULL;
209 if (g_strstr_len (httpResponse, len, "</root") == NULL)
210 return NULL;
211
212 /* create the xml root node */
213 xmlRootNode = xmlnode_from_str (xmlRoot, len - (xmlRoot - httpResponse));
214 if (xmlRootNode == NULL)
215 return NULL;
216
217 /* get the baseURL of the device */
218 baseURLNode = xmlnode_get_child (xmlRootNode, "URLBase");
219 if (baseURLNode != NULL)
220 {
221 baseURL = xmlnode_get_data (baseURLNode);
222 }
223 else
224 {
225 baseURL = GNUNET_strdup (httpURL);
226 }
227
228 /* get the serviceType child that has the service type as its data */
229 /* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its devicelist */
230 serviceTypeNode = xmlnode_get_child (xmlRootNode, "device");
231 while (!gaim_upnp_compare_device (serviceTypeNode,
232 "urn:schemas-upnp-org:device:InternetGatewayDevice:1")
233 && serviceTypeNode != NULL)
234 {
235 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
236 }
237 if (serviceTypeNode == NULL)
238 {
239 GNUNET_free (baseURL);
240 xmlnode_free (xmlRootNode);
241 return NULL;
242 }
243 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
244 if (serviceTypeNode == NULL)
245 {
246 GNUNET_free (baseURL);
247 xmlnode_free (xmlRootNode);
248 return NULL;
249 }
250
251 /* get urn:schemas-upnp-org:device:WANDevice:1 and its devicelist */
252 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
253 while (!gaim_upnp_compare_device (serviceTypeNode,
254 "urn:schemas-upnp-org:device:WANDevice:1")
255 && serviceTypeNode != NULL)
256 {
257 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
258 }
259 if (serviceTypeNode == NULL)
260 {
261 GNUNET_free (baseURL);
262 xmlnode_free (xmlRootNode);
263 return NULL;
264 }
265 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
266 if (serviceTypeNode == NULL)
267 {
268 GNUNET_free (baseURL);
269 xmlnode_free (xmlRootNode);
270 return NULL;
271 }
272
273 /* get urn:schemas-upnp-org:device:WANConnectionDevice:1 and its servicelist */
274 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
275 while (serviceTypeNode && !gaim_upnp_compare_device (serviceTypeNode,
276 "urn:schemas-upnp-org:device:WANConnectionDevice:1"))
277 {
278 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
279 }
280 if (serviceTypeNode == NULL)
281 {
282 GNUNET_free (baseURL);
283 xmlnode_free (xmlRootNode);
284 return NULL;
285 }
286 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "serviceList");
287 if (serviceTypeNode == NULL)
288 {
289 GNUNET_free (baseURL);
290 xmlnode_free (xmlRootNode);
291 return NULL;
292 }
293
294 /* get the serviceType variable passed to this function */
295 service = g_strdup_printf (SEARCH_REQUEST_DEVICE, serviceType);
296 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "service");
297 while (!gaim_upnp_compare_service (serviceTypeNode, service) &&
298 serviceTypeNode != NULL)
299 {
300 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
301 }
302
303 GNUNET_free (service);
304 if (serviceTypeNode == NULL)
305 {
306 GNUNET_free (baseURL);
307 xmlnode_free (xmlRootNode);
308 return NULL;
309 }
310
311 /* get the controlURL of the service */
312 if ((controlURLNode = xmlnode_get_child (serviceTypeNode,
313 "controlURL")) == NULL)
314 {
315 GNUNET_free (baseURL);
316 xmlnode_free (xmlRootNode);
317 return NULL;
318 }
319
320 tmp = xmlnode_get_data (controlURLNode);
321 if (baseURL && !gaim_str_has_prefix (tmp, "http://") &&
322 !gaim_str_has_prefix (tmp, "HTTP://"))
323 {
324 if (tmp[0] == '/')
325 {
326 size_t len;
327 const char *end;
328 /* absolute path */
329 end = strstr (&baseURL[strlen ("http://")], "/");
330 if (end == NULL)
331 len = strlen (&baseURL[strlen ("http://")]);
332 else
333 len = end - &baseURL[strlen ("http://")];
334 controlURL = g_strdup_printf ("http://%.*s%s",
335 len,
336 &baseURL[strlen ("http://")], tmp);
337 }
338 else
339 {
340 controlURL = g_strdup_printf ("%s%s", baseURL, tmp);
341 }
342 GNUNET_free (tmp);
343 }
344 else
345 {
346 controlURL = tmp;
347 }
348 GNUNET_free (baseURL);
349 xmlnode_free (xmlRootNode);
350
351 return controlURL;
352}
353
354#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
355
356/**
357 * Do the generic curl setup.
358 */
359static int
360setup_curl (const char *proxy, CURL * curl)
361{
362 int ret;
363
364 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
365 if (strlen (proxy) > 0)
366 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
367 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, 1024); /* a bit more than one HELLO */
368 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 150L);
369 /* NOTE: use of CONNECTTIMEOUT without also
370 setting NOSIGNAL results in really weird
371 crashes on my system! */
372 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
373 return GNUNET_OK;
374}
375
376static int
377gaim_upnp_generate_action_message_and_send (const char *proxy,
378 const char *actionName,
379 const char *actionParams,
380 GaimUtilFetchUrlCallback cb,
381 void *cb_data)
382{
383 CURL *curl;
384 int ret;
385 char *soapHeader;
386 char *sizeHeader;
387 char *soapMessage;
388 struct curl_slist *headers = NULL;
389
390 GNUNET_assert (cb != NULL);
391 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
392 return GNUNET_SYSERR;
393 /* set the soap message */
394 soapMessage = g_strdup_printf (SOAP_ACTION,
395 actionName,
396 control_info.service_type,
397 actionParams, actionName);
398 soapHeader = g_strdup_printf (HTTP_POST_SOAP_HEADER,
399 control_info.service_type, actionName);
400 sizeHeader = g_strdup_printf (HTTP_POST_SIZE_HEADER, strlen (soapMessage));
401 curl = curl_easy_init ();
402 setup_curl (proxy, curl);
403 CURL_EASY_SETOPT (curl, CURLOPT_URL, control_info.control_url);
404 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, cb);
405 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cb_data);
406 CURL_EASY_SETOPT (curl, CURLOPT_POST, 1);
407 headers = curl_slist_append (headers,
408 "CONTENT-TYPE: text/xml ; charset=\"utf-8\"");
409 headers = curl_slist_append (headers, soapHeader);
410 headers = curl_slist_append (headers, sizeHeader);
411 CURL_EASY_SETOPT (curl, CURLOPT_HTTPHEADER, headers);
412 CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDS, soapMessage);
413 CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDSIZE, strlen (soapMessage));
414 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
415 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
416 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
417 /* NOTE: use of CONNECTTIMEOUT without also
418 setting NOSIGNAL results in really weird
419 crashes on my system! */
420 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
421 if (ret == CURLE_OK)
422 ret = curl_easy_perform (curl);
423#if 0
424 if (ret != CURLE_OK)
425 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
426 _
427 ("%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n"),
428 "curl_easy_perform", control_info.control_url, soapMessage,
429 __FILE__, __LINE__, curl_easy_strerror (ret));
430#endif
431 curl_slist_free_all (headers);
432 curl_easy_cleanup (curl);
433 curl_global_cleanup ();
434 GNUNET_free (sizeHeader);
435 GNUNET_free (soapMessage);
436 GNUNET_free (soapHeader);
437 if (ret != CURLE_OK)
438 return GNUNET_SYSERR;
439 return GNUNET_OK;
440}
441
442
443static size_t
444looked_up_public_ip_cb (void *url_data,
445 size_t size, size_t nmemb, void *user_data)
446{
447 UPnPDiscoveryData *dd = user_data;
448 size_t len = size * nmemb;
449 const char *temp;
450 const char *temp2;
451
452 if (len + dd->buf_len > 1024 * 1024 * 4)
453 return 0; /* refuse to process - too big! */
454 GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
455 memcpy (&dd->buf[dd->buf_len - len], url_data, len);
456 if (dd->buf_len == 0)
457 return len;
458 /* extract the ip, or see if there is an error */
459 if ((temp = g_strstr_len (dd->buf,
460 dd->buf_len, "<NewExternalIPAddress")) == NULL)
461 return len;
462 if (!(temp = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), ">")))
463 return len;
464 if (!(temp2 = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), "<")))
465 return len;
466 memset (control_info.publicip, 0, sizeof (control_info.publicip));
467 if (temp2 - temp >= sizeof (control_info.publicip))
468 temp2 = temp + sizeof (control_info.publicip) - 1;
469 memcpy (control_info.publicip, temp + 1, temp2 - (temp + 1));
470 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
471 _("upnp: NAT Returned IP: %s\n"), control_info.publicip);
472 return len;
473}
474
475
476static size_t
477ignore_response (void *url_data, size_t size, size_t nmemb, void *user_data)
478{
479 return size * nmemb;
480}
481
482/**
483 * Process downloaded bits of service description.
484 */
485static size_t
486upnp_parse_description_cb (void *httpResponse,
487 size_t size, size_t nmemb, void *user_data)
488{
489 UPnPDiscoveryData *dd = user_data;
490 size_t len = size * nmemb;
491 char *control_url = NULL;
492
493 if (len + dd->buf_len > 1024 * 1024 * 4)
494 return len; /* refuse to process - too big! */
495 GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
496 memcpy (&dd->buf[dd->buf_len - len], httpResponse, len);
497 if (dd->buf_len > 0)
498 control_url = gaim_upnp_parse_description_response (dd->buf,
499 dd->buf_len,
500 dd->full_url,
501 dd->service_type);
502 control_info.status = control_url ? GAIM_UPNP_STATUS_DISCOVERED
503 : GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER;
504 GNUNET_free_non_null (control_info.control_url);
505 control_info.control_url = control_url;
506 control_info.service_type = dd->service_type;
507 return len;
508}
509
510static int
511gaim_upnp_parse_description (char *proxy, UPnPDiscoveryData * dd)
512{
513 CURL *curl;
514 int ret;
515
516 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
517 return GNUNET_SYSERR;
518 curl = curl_easy_init ();
519 setup_curl (proxy, curl);
520 ret = CURLE_OK;
521 CURL_EASY_SETOPT (curl, CURLOPT_URL, dd->full_url);
522 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &upnp_parse_description_cb);
523 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, dd);
524 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
525 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
526 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
527
528 /* NOTE: use of CONNECTTIMEOUT without also
529 setting NOSIGNAL results in really weird
530 crashes on my system! */
531 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
532 ret = curl_easy_perform (curl);
533 if (ret != CURLE_OK)
534 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
535 _("%s failed at %s:%d: `%s'\n"),
536 "curl_easy_perform", __FILE__, __LINE__,
537 curl_easy_strerror (ret));
538 curl_easy_cleanup (curl);
539 curl_global_cleanup ();
540 if (control_info.control_url == NULL)
541 return GNUNET_SYSERR;
542 return GNUNET_OK;
543}
544
545int
546gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, int sock)
547{
548 char *proxy;
549 socklen_t avail;
550 struct sockaddr_in server;
551 int retry_count;
552 char *sendMessage;
553 size_t totalSize;
554 int sentSuccess;
555 char buf[65536];
556 int buf_len;
557 const char *startDescURL;
558 const char *endDescURL;
559 int ret;
560 UPnPDiscoveryData dd;
561 struct sockaddr *sa;
562
563 memset (&dd, 0, sizeof (UPnPDiscoveryData));
564 if (control_info.status == GAIM_UPNP_STATUS_DISCOVERING)
565 return GNUNET_NO;
566 dd.sock = sock;
567 memset (&server, 0, sizeof (struct sockaddr_in));
568 server.sin_family = AF_INET;
569 avail = sizeof (struct sockaddr_in);
570 sa = (struct sockaddr *) &server;
571 if (GNUNET_OK !=
572 GNUNET_get_ip_from_hostname (HTTPMU_HOST_ADDRESS, AF_INET, &sa, &avail))
573 {
574 return GNUNET_SYSERR;
575 }
576 server.sin_port = htons (HTTPMU_HOST_PORT);
577 control_info.status = GAIM_UPNP_STATUS_DISCOVERING;
578
579 /* because we are sending over UDP, if there is a failure
580 we should retry the send NUM_UDP_ATTEMPTS times. Also,
581 try different requests for WANIPConnection and WANPPPConnection */
582 for (retry_count = 0; retry_count < NUM_UDP_ATTEMPTS; retry_count++)
583 {
584 sentSuccess = FALSE;
585 if ((retry_count % 2) == 0)
586 dd.service_type = WAN_IP_CONN_SERVICE;
587 else
588 dd.service_type = WAN_PPP_CONN_SERVICE;
589 sendMessage = g_strdup_printf (SEARCH_REQUEST_STRING, dd.service_type);
590 totalSize = strlen (sendMessage);
591 do
592 {
593 if (SENDTO (dd.sock,
594 sendMessage,
595 totalSize,
596 0,
597 (struct sockaddr *) &server,
598 sizeof (struct sockaddr_in)) == totalSize)
599 {
600 sentSuccess = TRUE;
601 break;
602 }
603 }
604 while (((errno == EINTR) || (errno == EAGAIN)) &&
605 (GNUNET_shutdown_test () == GNUNET_NO));
606 GNUNET_free (sendMessage);
607 if (sentSuccess)
608 break;
609 }
610 if (sentSuccess == FALSE)
611 return GNUNET_SYSERR;
612
613 /* try to read response */
614 do
615 {
616 buf_len = recv (dd.sock, buf, sizeof (buf) - 1, 0);
617 if (buf_len > 0)
618 {
619 buf[buf_len] = '\0';
620 break;
621 }
622 else if (errno != EINTR)
623 {
624 continue;
625 }
626 }
627 while ((errno == EINTR) && (GNUNET_shutdown_test () == GNUNET_NO));
628
629 /* parse the response, and see if it was a success */
630 if (g_strstr_len (buf, buf_len, HTTP_OK) == NULL)
631 return GNUNET_SYSERR;
632 if ((startDescURL = g_strstr_len (buf, buf_len, "http://")) == NULL)
633 return GNUNET_SYSERR;
634
635 endDescURL = g_strstr_len (startDescURL,
636 buf_len - (startDescURL - buf), "\r");
637 if (endDescURL == NULL)
638 endDescURL = g_strstr_len (startDescURL,
639 buf_len - (startDescURL - buf), "\n");
640 if (endDescURL == NULL)
641 return GNUNET_SYSERR;
642 if (endDescURL == startDescURL)
643 return GNUNET_SYSERR;
644 dd.full_url = GNUNET_strdup (startDescURL);
645 dd.full_url[endDescURL - startDescURL] = '\0';
646 proxy = NULL;
647 GNUNET_CONFIGURATION_get_value_string (cfg,
648 "GNUNETD", "HTTP-PROXY", &proxy);
649 ret = gaim_upnp_parse_description (proxy, &dd);
650 GNUNET_free (dd.full_url);
651 GNUNET_array_grow (dd.buf, dd.buf_len, 0);
652 if (ret == GNUNET_OK)
653 {
654 ret = gaim_upnp_generate_action_message_and_send (proxy,
655 "GetExternalIPAddress",
656 "",
657 looked_up_public_ip_cb,
658 &dd);
659 GNUNET_array_grow (dd.buf, dd.buf_len, 0);
660 }
661 GNUNET_free (proxy);
662 return ret;
663}
664
665const char *
666gaim_upnp_get_public_ip ()
667{
668 if ((control_info.status == GAIM_UPNP_STATUS_DISCOVERED)
669 && (strlen (control_info.publicip) > 0))
670 return control_info.publicip;
671 return NULL;
672}
673
674int
675gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg,
676 int do_add,
677 unsigned short portmap, const char *protocol)
678{
679 const char *action_name;
680 char *action_params;
681 char *internal_ip;
682 char *proxy;
683 int ret;
684
685 if (control_info.status != GAIM_UPNP_STATUS_DISCOVERED)
686 return GNUNET_NO;
687 if (do_add)
688 {
689 internal_ip = GNUNET_upnp_get_internal_ip (cfg);
690 if (internal_ip == NULL)
691 {
692 gaim_debug_error ("upnp",
693 "gaim_upnp_set_port_mapping(): couldn't get local ip\n");
694 return GNUNET_NO;
695 }
696 action_name = "AddPortMapping";
697 action_params = g_strdup_printf (ADD_PORT_MAPPING_PARAMS,
698 portmap,
699 protocol, portmap, internal_ip);
700 GNUNET_free (internal_ip);
701 }
702 else
703 {
704 action_name = "DeletePortMapping";
705 action_params = g_strdup_printf (DELETE_PORT_MAPPING_PARAMS,
706 portmap, protocol);
707 }
708 proxy = NULL;
709 GNUNET_CONFIGURATION_get_value_string (cfg,
710 "GNUNETD", "HTTP-PROXY", &proxy);
711 ret =
712 gaim_upnp_generate_action_message_and_send (proxy, action_name,
713 action_params,
714 &ignore_response, NULL);
715
716 GNUNET_free (action_params);
717 GNUNET_free (proxy);
718 return ret;
719}
720
721/* end of upnp.c */
diff --git a/src/upnp/upnp.h b/src/upnp/upnp.h
new file mode 100644
index 000000000..1f99c5352
--- /dev/null
+++ b/src/upnp/upnp.h
@@ -0,0 +1,82 @@
1/**
2 * @file upnp.h Universal Plug N Play API
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#ifndef _GAIM_UPNP_H_
27#define _GAIM_UPNP_H_
28
29#include <libxml/parser.h>
30#include <string.h>
31#include "gnunet_util_lib.h"
32
33#ifdef __cplusplus
34extern "C"
35{
36#if 0 /* keep Emacsens' auto-indent happy */
37}
38#endif
39#endif
40
41/**
42 * Sends a discovery request to search for a UPnP enabled IGD that
43 * contains the WANIPConnection service that will allow us to receive the
44 * public IP address of the IGD, and control it for forwarding ports.
45 * The result will be cached for further use.
46 */
47int gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, int sock);
48
49/**
50 * Gets the IP address from a UPnP enabled IGD that sits on the local
51 * network, so when getting the network IP, instead of returning the
52 * local network IP, the public IP is retrieved. This is a cached value from
53 * the time of the UPnP discovery.
54 *
55 * @return The IP address of the network, or NULL if something went wrong
56 */
57const char *gaim_upnp_get_public_ip (void);
58
59/**
60 * Maps Ports in a UPnP enabled IGD that sits on the local network to
61 * this gaim client. Essentially, this function takes care of the port
62 * forwarding so things like file transfers can work behind NAT firewalls
63 *
64 * @param portmap The port to map to this client
65 * @param protocol The protocol to map, either "TCP" or "UDP"
66 * @param do_add TRUE/GNUNET_YES to add, FALSE/GNUNET_NO to remove
67 * @param cb an optional callback function to be notified when the mapping
68 * addition is complete
69 * @param cb_data Extra data to be passed to the callback
70 */
71int gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg,
72 int do_add,
73 uint16_t portmap, const char *protocol);
74
75#if 0 /* keep Emacsens' auto-indent happy */
76{
77#endif
78#ifdef __cplusplus
79}
80#endif
81
82#endif /* _GAIM_UPNP_H_ */
diff --git a/src/upnp/upnp_init.c b/src/upnp/upnp_init.c
new file mode 100644
index 000000000..3dc0d2b7c
--- /dev/null
+++ b/src/upnp/upnp_init.c
@@ -0,0 +1,208 @@
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/transports/upnp/init.c
23 * @brief API for UPnP access
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "upnp.h"
30#include "gnunet_upnp_service.h"
31#include "gnunet_core.h"
32
33static struct GNUNET_GE_Context *ectx;
34
35static struct GNUNET_GC_Configuration *cfg;
36
37static struct GNUNET_CronManager *cron;
38
39static struct GNUNET_Mutex *lock;
40
41typedef struct
42{
43 unsigned short port;
44 const char *proto;
45} PMap;
46
47static PMap *maps;
48
49static unsigned int maps_size;
50
51static struct GNUNET_ThreadHandle *discovery;
52
53static int discovery_socket;
54
55/**
56 * Obtain the public/external IP address.
57 *
58 * @return GNUNET_SYSERR on error, GNUNET_OK on success
59 */
60static int
61gnunet_upnp_get_public_ip (struct in_addr *address)
62{
63 const char *ip;
64 socklen_t socklen;
65 struct sockaddr *sa;
66 struct sockaddr_in s4;
67 int ret;
68
69 ip = gaim_upnp_get_public_ip ();
70 if (ip == NULL)
71 return GNUNET_SYSERR;
72 socklen = sizeof (struct sockaddr_in);
73 sa = (struct sockaddr *) &s4;
74 ret = GNUNET_get_ip_from_hostname (NULL, ip, AF_INET, &sa, &socklen);
75 if (ret == GNUNET_OK)
76 *address = s4.sin_addr;
77 return ret;
78}
79
80static void
81kill_discovery ()
82{
83 void *unused;
84
85 if (discovery != NULL)
86 {
87 SHUTDOWN (discovery_socket, SHUT_RDWR);
88 CLOSE (discovery_socket);
89 GNUNET_thread_join (discovery, &unused);
90 discovery = NULL;
91 }
92}
93
94static void *
95discover_thread ()
96{
97 gaim_upnp_discover (ectx, cfg, discovery_socket);
98 return NULL;
99}
100
101/**
102 * Periodically try to (re)discover UPnP access points.
103 */
104static void
105discover (void *unused)
106{
107 kill_discovery ();
108 discovery_socket = SOCKET (PF_INET, SOCK_DGRAM, 0);
109 if (discovery_socket == -1)
110 return;
111 discovery = GNUNET_thread_create (&discover_thread, NULL, 1024 * 128);
112}
113
114/**
115 * Periodically repeat our requests for port mappings.
116 */
117static void
118portmap (void *unused)
119{
120 unsigned int i;
121
122 GNUNET_mutex_lock (lock);
123 for (i = 0; i < maps_size; i++)
124 gaim_upnp_change_port_mapping (ectx,
125 cfg, GNUNET_NO, maps[i].port,
126 maps[i].proto);
127 GNUNET_mutex_unlock (lock);
128}
129
130
131/**
132 * Get the external IP address for the local machine.
133 *
134 * @return GNUNET_SYSERR on error, GNUNET_OK on success
135 */
136static int
137gnunet_upnp_get_ip (unsigned short port,
138 const char *protocol, struct in_addr *address)
139{
140 unsigned int i;
141
142 GNUNET_mutex_lock (lock);
143 for (i = 0; i < maps_size; i++)
144 if ((0 == strcmp (maps[i].proto, protocol)) && (maps[i].port == port))
145 break;
146 if (i == maps_size)
147 {
148 /* new entry! */
149 GNUNET_array_grow (maps, maps_size, maps_size + 1);
150 maps[i].proto = protocol;
151 maps[i].port = port;
152 gaim_upnp_change_port_mapping (ectx, cfg, GNUNET_YES, port, protocol);
153 }
154 GNUNET_mutex_unlock (lock);
155 return gnunet_upnp_get_public_ip (address);
156}
157
158
159/**
160 * Get the external IP address for the local machine.
161 */
162GNUNET_UPnP_ServiceAPI *
163provide_module_upnp (GNUNET_CoreAPIForPlugins * capi)
164{
165 static GNUNET_UPnP_ServiceAPI api;
166
167 ectx = capi->ectx;
168 cfg = capi->cfg;
169 cron = GNUNET_cron_create (ectx);
170 lock = GNUNET_mutex_create (GNUNET_NO);
171 GNUNET_cron_start (cron);
172 GNUNET_cron_add_job (cron, &discover, 0, 5 * GNUNET_CRON_MINUTES, NULL);
173 GNUNET_cron_add_job (cron, &portmap, 150 * GNUNET_CRON_SECONDS,
174 5 * GNUNET_CRON_MINUTES, NULL);
175 api.get_ip = gnunet_upnp_get_ip;
176 return &api;
177}
178
179/**
180 * Shutdown UPNP.
181 */
182int
183release_module_upnp ()
184{
185 unsigned int i;
186
187 if (cron == NULL)
188 return GNUNET_SYSERR; /* not loaded! */
189 for (i = 0; i < maps_size; i++)
190 gaim_upnp_change_port_mapping (ectx,
191 cfg, GNUNET_NO, maps[i].port,
192 maps[i].proto);
193 GNUNET_cron_stop (cron);
194 GNUNET_cron_del_job (cron, &discover, 5 * GNUNET_CRON_MINUTES, NULL);
195 GNUNET_cron_del_job (cron, &portmap, 5 * GNUNET_CRON_MINUTES, NULL);
196 GNUNET_cron_destroy (cron);
197 kill_discovery ();
198 cron = NULL;
199 GNUNET_mutex_destroy (lock);
200 lock = NULL;
201 GNUNET_array_grow (maps, maps_size, 0);
202 ectx = NULL;
203 cfg = NULL;
204 return GNUNET_OK;
205}
206
207
208/* end of init.c */
diff --git a/src/upnp/upnp_ip.c b/src/upnp/upnp_ip.c
new file mode 100644
index 000000000..3716fc671
--- /dev/null
+++ b/src/upnp/upnp_ip.c
@@ -0,0 +1,47 @@
1/*
2 This file is part of GNUnet.
3 (C) 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/**
22 * @file transports/upnp/ip.c
23 * @brief code to determine the IP of the local machine
24 *
25 * @author Christian Grothoff
26 */
27
28#include <stdlib.h>
29#include "platform.h"
30#include "gnunet_util.h"
31#include "ip.h"
32
33/**
34 * Get the IP address for the local machine.
35 * @return NULL on error
36 */
37char *
38GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg,
39 struct GNUNET_GE_Context *ectx)
40{
41 struct in_addr address;
42
43 return GNUNET_get_local_ip (cfg, ectx, &address);
44}
45
46
47/* end of ip.c */
diff --git a/src/upnp/upnp_ip.h b/src/upnp/upnp_ip.h
new file mode 100644
index 000000000..adc06bbd5
--- /dev/null
+++ b/src/upnp/upnp_ip.h
@@ -0,0 +1,40 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005 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 transports/upnp/ip.h
23 * @brief
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef IP_H
29#define IP_H
30
31
32/**
33 * Get the IP address for the local machine.
34 * @return NULL on error
35 */
36char *GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg,
37 struct GNUNET_GE_Context *ectx);
38
39
40#endif
diff --git a/src/upnp/upnp_util.c b/src/upnp/upnp_util.c
new file mode 100644
index 000000000..cef40b578
--- /dev/null
+++ b/src/upnp/upnp_util.c
@@ -0,0 +1,152 @@
1/*
2 * @file util.h Utility Functions
3 * @ingroup core
4 *
5 * Gaim is the legal property of its developers, whose names are too numerous
6 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * source distribution.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24#include "platform.h"
25#include "util.h"
26#include "gnunet_util.h"
27
28/* Returns a NULL-terminated string after unescaping an entity
29 * (eg. &amp;, &lt; &#38 etc.) starting at s. Returns NULL on failure.*/
30static char *
31detect_entity (const char *text, int *length)
32{
33 const char *pln;
34 int len;
35 int pound;
36 char b[7];
37 char *buf;
38
39 if (!text || *text != '&')
40 return NULL;
41
42#define IS_ENTITY(s) (!strncasecmp(text, s, (len = sizeof(s) - 1)))
43
44 if (IS_ENTITY ("&amp;"))
45 pln = "&";
46 else if (IS_ENTITY ("&lt;"))
47 pln = "<";
48 else if (IS_ENTITY ("&gt;"))
49 pln = ">";
50 else if (IS_ENTITY ("&nbsp;"))
51 pln = " ";
52 else if (IS_ENTITY ("&copy;"))
53 pln = "\302\251"; /* or use g_unichar_to_utf8(0xa9); */
54 else if (IS_ENTITY ("&quot;"))
55 pln = "\"";
56 else if (IS_ENTITY ("&reg;"))
57 pln = "\302\256"; /* or use g_unichar_to_utf8(0xae); */
58 else if (IS_ENTITY ("&apos;"))
59 pln = "\'";
60 else if (*(text + 1) == '#' && (sscanf (text, "&#%u;", &pound) == 1) &&
61 pound != 0 && *(text + 3 + (int) log10 (pound)) == ';')
62 {
63 buf = GNUNET_convert_string_to_utf8 (NULL,
64 (const char *) &pound,
65 2, "UNICODE");
66 if (strlen (buf) > 6)
67 buf[6] = '\0';
68 strcpy (b, buf);
69 pln = b;
70 GNUNET_free (buf);
71 len = 2;
72 while (isdigit ((int) text[len]))
73 len++;
74 if (text[len] == ';')
75 len++;
76 }
77 else
78 return NULL;
79
80 if (length)
81 *length = len;
82 return GNUNET_strdup (pln);
83}
84
85char *
86g_strdup_printf (const char *fmt, ...)
87{
88 size_t size;
89 char *buf;
90 va_list va;
91
92 va_start (va, fmt);
93 size = VSNPRINTF (NULL, 0, fmt, va) + 1;
94 va_end (va);
95 buf = GNUNET_malloc (size);
96 va_start (va, fmt);
97 VSNPRINTF (buf, size, fmt, va);
98 va_end (va);
99 return buf;
100}
101
102char *
103gaim_unescape_html (const char *html)
104{
105 if (html != NULL)
106 {
107 const char *c = html;
108 char *ret = GNUNET_strdup ("");
109 char *app;
110 while (*c)
111 {
112 int len;
113 char *ent;
114
115 if ((ent = detect_entity (c, &len)) != NULL)
116 {
117 app = g_strdup_printf ("%s%s", ret, ent);
118 GNUNET_free (ret);
119 ret = app;
120 c += len;
121 GNUNET_free (ent);
122 }
123 else if (!strncmp (c, "<br>", 4))
124 {
125 app = g_strdup_printf ("%s%s", ret, "\n");
126 GNUNET_free (ret);
127 ret = app;
128 c += 4;
129 }
130 else
131 {
132 app = g_strdup_printf ("%s%c", ret, *c);
133 GNUNET_free (ret);
134 ret = app;
135 c++;
136 }
137 }
138 return ret;
139 }
140 return NULL;
141}
142
143
144int
145gaim_str_has_prefix (const char *s, const char *p)
146{
147 if ((s == NULL) || (p == NULL))
148 return 0;
149 return !strncmp (s, p, strlen (p));
150}
151
152/* end of util.c */
diff --git a/src/upnp/upnp_util.h b/src/upnp/upnp_util.h
new file mode 100644
index 000000000..89a8d71cf
--- /dev/null
+++ b/src/upnp/upnp_util.h
@@ -0,0 +1,68 @@
1/**
2 * @file util.h Utility Functions
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 * @todo Rename the functions so that they live somewhere in the gaim
26 * namespace.
27 */
28#ifndef _GAIM_UTIL_H_
29#define _GAIM_UTIL_H_
30
31#include <stdio.h>
32#include <string.h>
33
34#ifdef __cplusplus
35extern "C"
36{
37#endif
38
39/**
40 * Unescapes HTML entities to their literal characters.
41 * For example "&amp;" is replaced by '&' and so on.
42 * Actually only "&amp;", "&quot;", "&lt;" and "&gt;" are currently
43 * supported.
44 *
45 * @param html The string in which to unescape any HTML entities
46 *
47 * @return the text with HTML entities literalized
48 */
49 char *gaim_unescape_html (const char *html);
50
51/**
52 * Compares two strings to see if the first contains the second as
53 * a proper prefix.
54 *
55 * @param s The string to check.
56 * @param p The prefix in question.
57 *
58 * @return TRUE if p is a prefix of s, otherwise FALSE.
59 */
60 int gaim_str_has_prefix (const char *s, const char *p);
61
62 char *g_strdup_printf (const char *fmt, ...);
63
64#ifdef __cplusplus
65}
66#endif
67
68#endif
diff --git a/src/upnp/upnp_xmlnode.c b/src/upnp/upnp_xmlnode.c
new file mode 100644
index 000000000..b37528f00
--- /dev/null
+++ b/src/upnp/upnp_xmlnode.c
@@ -0,0 +1,487 @@
1/**
2 * @file xmlnode.c XML DOM functions
3 *
4 * gaim
5 *
6 * Gaim is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25/* A lot of this code at least resembles the code in libxode, but since
26 * libxode uses memory pools that we simply have no need for, I decided to
27 * write my own stuff. Also, re-writing this lets me be as lightweight
28 * as I want to be. Thank you libxode for giving me a good starting point */
29
30#include "platform.h"
31
32#include "util.h"
33#include "gnunet_util.h"
34#include "xmlnode.h"
35
36#include <libxml/parser.h>
37#include <string.h>
38
39
40#ifdef _WIN32
41# define NEWLINE_S "\r\n"
42#else
43# define NEWLINE_S "\n"
44#endif
45
46#define TRUE GNUNET_YES
47#define FALSE GNUNET_NO
48
49#define g_return_if_fail(a) if(!(a)) return;
50#define g_return_val_if_fail(a, val) if(!(a)) return (val);
51
52/**
53 * The valid types for an xmlnode
54 */
55typedef enum _XMLNodeType
56{
57 XMLNODE_TYPE_TAG, /**< Just a tag */
58 XMLNODE_TYPE_ATTRIB, /**< Has attributes */
59 XMLNODE_TYPE_DATA /**< Has data */
60} XMLNodeType;
61
62typedef struct
63{
64 xmlnode *current;
65 xmlnode **nodes;
66 unsigned int pos;
67 unsigned int size;
68} XMLNodePool;
69
70struct _xmlnode
71{
72 char *name; /**< The name of the node. */
73 char *xmlns; /**< The namespace of the node */
74 XMLNodeType type; /**< The type of the node. */
75 char *data; /**< The data for the node. */
76 size_t data_sz; /**< The size of the data. */
77 struct _xmlnode *parent; /**< The parent node or @c NULL.*/
78 struct _xmlnode *child; /**< The child node or @c NULL.*/
79 struct _xmlnode *lastchild; /**< The last child node or @c NULL.*/
80 struct _xmlnode *next; /**< The next node or @c NULL. */
81 XMLNodePool *pool;
82 int free_pool; /* set to GNUNET_YES for the root node, which must free the pool */
83};
84
85
86static void *
87g_memdup (const void *data, size_t s)
88{
89 void *ret;
90
91 ret = GNUNET_malloc (s);
92 memcpy (ret, data, s);
93 return ret;
94}
95
96static char *
97g_string_append_len (char *prefix, const void *data, size_t s)
98{
99 char *ret;
100
101 ret = g_strdup_printf ("%s%.*s", prefix, s, data);
102 GNUNET_free (prefix);
103 return ret;
104}
105
106static xmlnode *
107new_node (const char *name, XMLNodeType type, void *user_data)
108{
109 xmlnode *node = GNUNET_malloc (sizeof (xmlnode));
110
111 node->name = name == NULL ? NULL : GNUNET_strdup (name);
112 node->type = type;
113 node->pool = user_data;
114 if (node->pool->size == node->pool->pos)
115 GNUNET_array_grow (node->pool->nodes, node->pool->size,
116 node->pool->size * 2 + 64);
117 node->pool->nodes[node->pool->pos++] = node;
118 node->free_pool = 0;
119 return node;
120}
121
122static xmlnode *
123xmlnode_new (const char *name, void *user_data)
124{
125 g_return_val_if_fail (name != NULL, NULL);
126 return new_node (name, XMLNODE_TYPE_TAG, user_data);
127}
128
129static void
130xmlnode_insert_child (xmlnode * parent, xmlnode * child)
131{
132 g_return_if_fail (parent != NULL);
133 g_return_if_fail (child != NULL);
134
135 child->parent = parent;
136 if (parent->lastchild)
137 parent->lastchild->next = child;
138 else
139 parent->child = child;
140 parent->lastchild = child;
141}
142
143static xmlnode *
144xmlnode_new_child (xmlnode * parent, const char *name, void *user_data)
145{
146 xmlnode *node;
147
148 g_return_val_if_fail (parent != NULL, NULL);
149 g_return_val_if_fail (name != NULL, NULL);
150 node = new_node (name, XMLNODE_TYPE_TAG, user_data);
151 xmlnode_insert_child (parent, node);
152 return node;
153}
154
155static void
156xmlnode_insert_data (xmlnode * node,
157 const char *data, int size, void *user_data)
158{
159 xmlnode *child;
160 size_t real_size;
161
162 g_return_if_fail (node != NULL);
163 g_return_if_fail (data != NULL);
164 g_return_if_fail (size != 0);
165 real_size = size == -1 ? strlen (data) : size;
166 child = new_node (NULL, XMLNODE_TYPE_DATA, user_data);
167 child->data = g_memdup (data, real_size);
168 child->data_sz = real_size;
169 xmlnode_insert_child (node, child);
170}
171
172static void
173xmlnode_remove_attrib (xmlnode * node, const char *attr)
174{
175 xmlnode *attr_node, *sibling = NULL;
176
177 g_return_if_fail (node != NULL);
178 g_return_if_fail (attr != NULL);
179
180 for (attr_node = node->child; attr_node; attr_node = attr_node->next)
181 {
182 if (attr_node->type == XMLNODE_TYPE_ATTRIB &&
183 !strcmp (attr_node->name, attr))
184 {
185 if (node->child == attr_node)
186 {
187 node->child = attr_node->next;
188 }
189 else
190 {
191 sibling->next = attr_node->next;
192 }
193 if (node->lastchild == attr_node)
194 {
195 node->lastchild = sibling;
196 }
197 xmlnode_free (attr_node);
198 return;
199 }
200 sibling = attr_node;
201 }
202}
203
204static void
205xmlnode_set_attrib (xmlnode * node,
206 const char *attr, const char *value, void *user_data)
207{
208 xmlnode *attrib_node;
209
210 g_return_if_fail (node != NULL);
211 g_return_if_fail (attr != NULL);
212 g_return_if_fail (value != NULL);
213 xmlnode_remove_attrib (node, attr);
214 attrib_node = new_node (attr, XMLNODE_TYPE_ATTRIB, user_data);
215 attrib_node->data = GNUNET_strdup (value);
216 xmlnode_insert_child (node, attrib_node);
217}
218
219static void
220xmlnode_set_namespace (xmlnode * node, const char *xmlns)
221{
222 g_return_if_fail (node != NULL);
223 GNUNET_free_non_null (node->xmlns);
224 node->xmlns = GNUNET_strdup (xmlns);
225}
226
227static const char *
228xmlnode_get_namespace (xmlnode * node)
229{
230 g_return_val_if_fail (node != NULL, NULL);
231 return node->xmlns;
232}
233
234static void
235freePool (XMLNodePool * pool)
236{
237 unsigned int i;
238 xmlnode *x;
239
240 for (i = 0; i < pool->pos; i++)
241 {
242 x = pool->nodes[i];
243 GNUNET_free_non_null (x->name);
244 GNUNET_free_non_null (x->data);
245 GNUNET_free_non_null (x->xmlns);
246 GNUNET_free (x);
247 }
248 GNUNET_array_grow (pool->nodes, pool->size, 0);
249 GNUNET_free (pool);
250}
251
252void
253xmlnode_free (xmlnode * node)
254{
255 g_return_if_fail (node != NULL);
256 if (node->free_pool != GNUNET_YES)
257 return;
258 freePool (node->pool);
259}
260
261static xmlnode *
262xmlnode_get_child_with_namespace (const xmlnode * parent,
263 const char *name, const char *ns)
264{
265 xmlnode *x;
266 xmlnode *ret = NULL;
267 char *parent_name;
268 char *child_name;
269
270 if (parent == NULL)
271 return NULL;
272 if (name == NULL)
273 return NULL;
274
275 parent_name = GNUNET_strdup (name);
276 child_name = strstr (parent_name, "/");
277 if (child_name != NULL)
278 {
279 child_name[0] = '\0';
280 child_name++;
281 }
282
283 for (x = parent->child; x; x = x->next)
284 {
285 const char *xmlns = NULL;
286 if (ns)
287 xmlns = xmlnode_get_namespace (x);
288
289 if (x->type == XMLNODE_TYPE_TAG && name
290 && !strcmp (parent_name, x->name) && (!ns
291 || (xmlns
292 && !strcmp (ns, xmlns))))
293 {
294 ret = x;
295 break;
296 }
297 }
298
299 if (child_name && ret)
300 ret = xmlnode_get_child (ret, child_name);
301
302 GNUNET_free (parent_name);
303 return ret;
304}
305
306xmlnode *
307xmlnode_get_child (const xmlnode * parent, const char *name)
308{
309 return xmlnode_get_child_with_namespace (parent, name, NULL);
310}
311
312char *
313xmlnode_get_data (xmlnode * node)
314{
315 char *str = NULL;
316 xmlnode *c;
317
318 if (node == NULL)
319 return NULL;
320 for (c = node->child; c; c = c->next)
321 {
322 if (c->type == XMLNODE_TYPE_DATA)
323 {
324 if (!str)
325 str = GNUNET_strdup ("");
326 str = g_string_append_len (str, c->data, c->data_sz);
327 }
328 }
329 if (str == NULL)
330 return NULL;
331
332 return str;
333}
334
335static void
336xmlnode_parser_element_start_libxml (void *user_data,
337 const xmlChar * element_name,
338 const xmlChar * prefix,
339 const xmlChar * xmlns,
340 int nb_namespaces,
341 const xmlChar ** namespaces,
342 int nb_attributes,
343 int nb_defaulted,
344 const xmlChar ** attributes)
345{
346 XMLNodePool *xpd = user_data;
347 xmlnode *node;
348 int i;
349
350 if (!element_name)
351 return;
352 if (xpd->current)
353 node =
354 xmlnode_new_child (xpd->current, (const char *) element_name,
355 user_data);
356 else
357 node = xmlnode_new ((const char *) element_name, user_data);
358
359 xmlnode_set_namespace (node, (const char *) xmlns);
360
361 for (i = 0; i < nb_attributes * 5; i += 5)
362 {
363 char *txt;
364 int attrib_len = attributes[i + 4] - attributes[i + 3];
365 char *attrib = GNUNET_malloc (attrib_len + 1);
366 memcpy (attrib, attributes[i + 3], attrib_len);
367 attrib[attrib_len] = '\0';
368 txt = attrib;
369 attrib = gaim_unescape_html (txt);
370 GNUNET_free (txt);
371 xmlnode_set_attrib (node, (const char *) attributes[i], attrib,
372 user_data);
373 GNUNET_free (attrib);
374 }
375 xpd->current = node;
376}
377
378static void
379xmlnode_parser_element_end_libxml (void *user_data,
380 const xmlChar * element_name,
381 const xmlChar * prefix,
382 const xmlChar * xmlns)
383{
384 XMLNodePool *xpd = user_data;
385
386 if (!element_name || !xpd->current)
387 return;
388 if (xpd->current->parent)
389 {
390 if (!xmlStrcmp ((xmlChar *) xpd->current->name, element_name))
391 xpd->current = xpd->current->parent;
392 }
393}
394
395static void
396xmlnode_parser_element_text_libxml (void *user_data,
397 const xmlChar * text, int text_len)
398{
399 XMLNodePool *xpd = user_data;
400
401 if (!xpd->current || !text || !text_len)
402 return;
403 xmlnode_insert_data (xpd->current,
404 (const char *) text, text_len, user_data);
405}
406
407static xmlSAXHandler xmlnode_parser_libxml = {
408 .internalSubset = NULL,
409 .isStandalone = NULL,
410 .hasInternalSubset = NULL,
411 .hasExternalSubset = NULL,
412 .resolveEntity = NULL,
413 .getEntity = NULL,
414 .entityDecl = NULL,
415 .notationDecl = NULL,
416 .attributeDecl = NULL,
417 .elementDecl = NULL,
418 .unparsedEntityDecl = NULL,
419 .setDocumentLocator = NULL,
420 .startDocument = NULL,
421 .endDocument = NULL,
422 .startElement = NULL,
423 .endElement = NULL,
424 .reference = NULL,
425 .characters = xmlnode_parser_element_text_libxml,
426 .ignorableWhitespace = NULL,
427 .processingInstruction = NULL,
428 .comment = NULL,
429 .warning = NULL,
430 .error = NULL,
431 .fatalError = NULL,
432 .getParameterEntity = NULL,
433 .cdataBlock = NULL,
434 .externalSubset = NULL,
435 .initialized = XML_SAX2_MAGIC,
436 ._private = NULL,
437 .startElementNs = xmlnode_parser_element_start_libxml,
438 .endElementNs = xmlnode_parser_element_end_libxml,
439 .serror = NULL
440};
441
442xmlnode *
443xmlnode_from_str (const char *str, int size)
444{
445 XMLNodePool *xpd;
446 xmlnode *ret;
447 size_t real_size;
448
449 g_return_val_if_fail (str != NULL, NULL);
450
451 real_size = size < 0 ? strlen (str) : size;
452 xpd = GNUNET_malloc (sizeof (XMLNodePool));
453 memset (xpd, 0, sizeof (XMLNodePool));
454 if (xmlSAXUserParseMemory (&xmlnode_parser_libxml, xpd, str, real_size) < 0)
455 {
456 freePool (xpd);
457 return NULL;
458 }
459 ret = xpd->current;
460 ret->free_pool = GNUNET_YES;
461 return ret;
462}
463
464xmlnode *
465xmlnode_get_next_twin (xmlnode * node)
466{
467 xmlnode *sibling;
468 const char *ns = xmlnode_get_namespace (node);
469
470 g_return_val_if_fail (node != NULL, NULL);
471 g_return_val_if_fail (node->type == XMLNODE_TYPE_TAG, NULL);
472
473 for (sibling = node->next; sibling; sibling = sibling->next)
474 {
475 const char *xmlns = NULL;
476 if (ns)
477 xmlns = xmlnode_get_namespace (sibling);
478
479 if (sibling->type == XMLNODE_TYPE_TAG
480 && !strcmp (node->name, sibling->name) && (!ns
481 || (xmlns
482 && !strcmp (ns,
483 xmlns))))
484 return sibling;
485 }
486 return NULL;
487}
diff --git a/src/upnp/upnp_xmlnode.h b/src/upnp/upnp_xmlnode.h
new file mode 100644
index 000000000..199975e37
--- /dev/null
+++ b/src/upnp/upnp_xmlnode.h
@@ -0,0 +1,92 @@
1/**
2 * @file xmlnode.h XML DOM functions
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25#ifndef _GGAIM_XMLNODE_H_
26#define _GGAIM_XMLNODE_H_
27
28
29#ifdef __cplusplus
30extern "C"
31{
32#endif
33
34/**
35 * An xmlnode.
36 */
37 typedef struct _xmlnode xmlnode;
38
39/**
40 * Gets a child node named name.
41 *
42 * @param parent The parent node.
43 * @param name The child's name.
44 *
45 * @return The child or NULL.
46 */
47 xmlnode *xmlnode_get_child (const xmlnode * parent, const char *name);
48
49/**
50 * Gets the next node with the same name as node.
51 *
52 * @param node The node of a twin to find.
53 *
54 * @return The twin of node or NULL.
55 */
56 xmlnode *xmlnode_get_next_twin (xmlnode * node);
57
58/**
59 * Gets data from a node.
60 *
61 * @param node The node to get data from.
62 *
63 * @return The data from the node. You must g_free
64 * this string when finished using it.
65 */
66 char *xmlnode_get_data (xmlnode * node);
67
68/**
69 * Creates a node from a string of XML. Calling this on the
70 * root node of an XML document will parse the entire document
71 * into a tree of nodes, and return the xmlnode of the root.
72 *
73 * @param str The string of xml.
74 * @param size The size of the string, or -1 if @a str is
75 * NUL-terminated.
76 *
77 * @return The new node.
78 */
79 xmlnode *xmlnode_from_str (const char *str, int size);
80
81/**
82 * Frees a node and all of it's children.
83 *
84 * @param node The node to free.
85 */
86 void xmlnode_free (xmlnode * node);
87
88#ifdef __cplusplus
89}
90#endif
91
92#endif /* _GAIM_XMLNODE_H_ */
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
new file mode 100644
index 000000000..a90b2b793
--- /dev/null
+++ b/src/util/Makefile.am
@@ -0,0 +1,308 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3plugindir = $(libdir)/gnunet
4
5if MINGW
6 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -luuid -liconv -lstdc++ -lcomdlg32 -lgdi32
7endif
8
9if USE_COVERAGE
10 AM_CFLAGS = --coverage
11endif
12
13lib_LTLIBRARIES = libgnunetutil.la
14
15libgnunetutil_la_SOURCES = \
16 client.c \
17 common_allocation.c \
18 common_endian.c \
19 common_gettext.c \
20 common_logging.c \
21 configuration.c \
22 container_bloomfilter.c \
23 container_meta_data.c \
24 container_multihashmap.c \
25 crypto_aes.c \
26 crypto_crc.c \
27 crypto_hash.c \
28 crypto_ksk.c \
29 crypto_random.c \
30 crypto_rsa.c \
31 disk.c \
32 getopt.c \
33 getopt_helpers.c \
34 network.c \
35 os_installation.c \
36 os_load.c \
37 os_network.c \
38 os_priority.c \
39 plugin.c \
40 program.c \
41 pseudonym.c \
42 scheduler.c \
43 server.c \
44 server_tc.c \
45 service.c \
46 signal.c \
47 strings.c \
48 time.c
49
50
51libgnunetutil_la_LIBADD = \
52 $(GCLIBADD) \
53 $(LIBGCRYPT_LIBS) \
54 -lgmp -lltdl -lz -lextractor
55
56libgnunetutil_la_LDFLAGS = \
57 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
58 -version-info 4:0:0
59
60
61plugin_LTLIBRARIES = \
62 libgnunet_plugin_test.la
63
64libgnunet_plugin_test_la_SOURCES = \
65 test_plugin_plug.c
66libgnunet_plugin_test_la_LDFLAGS = \
67 $(GN_PLUGIN_LDFLAGS)
68
69
70check_PROGRAMS = \
71 test_client \
72 test_common_allocation \
73 test_common_endian \
74 test_common_logging \
75 test_configuration \
76 test_container_bloomfilter \
77 test_container_meta_data \
78 test_container_multihashmap \
79 test_crypto_aes \
80 test_crypto_aes_weak \
81 test_crypto_crc \
82 test_crypto_hash \
83 test_crypto_ksk \
84 test_crypto_random \
85 test_crypto_rsa \
86 test_disk \
87 test_getopt \
88 test_network \
89 test_network_addressing \
90 test_network_receive_cancel \
91 test_network_timeout \
92 test_network_timeout_no_connect \
93 test_network_transmit_cancel \
94 test_os_load \
95 test_os_network \
96 test_os_priority \
97 test_plugin \
98 test_program \
99 test_pseudonym \
100 test_scheduler \
101 test_scheduler_delay \
102 test_server \
103 test_server_disconnect \
104 test_server_with_client \
105 test_service \
106 test_strings \
107 test_time \
108 perf_crypto_hash
109
110TESTS = $(check_PROGRAMS)
111
112test_client_SOURCES = \
113 test_client.c
114test_client_LDADD = \
115 $(top_builddir)/src/util/libgnunetutil.la
116
117test_common_allocation_SOURCES = \
118 test_common_allocation.c
119test_common_allocation_LDADD = \
120 $(top_builddir)/src/util/libgnunetutil.la
121
122test_common_endian_SOURCES = \
123 test_common_endian.c
124test_common_endian_LDADD = \
125 $(top_builddir)/src/util/libgnunetutil.la
126
127test_common_logging_SOURCES = \
128 test_common_logging.c
129test_common_logging_LDADD = \
130 $(top_builddir)/src/util/libgnunetutil.la
131
132test_configuration_SOURCES = \
133 test_configuration.c
134test_configuration_LDADD = \
135 $(top_builddir)/src/util/libgnunetutil.la
136
137test_container_bloomfilter_SOURCES = \
138 test_container_bloomfilter.c
139test_container_bloomfilter_LDADD = \
140 $(top_builddir)/src/util/libgnunetutil.la
141
142test_container_meta_data_SOURCES = \
143 test_container_meta_data.c
144test_container_meta_data_LDADD = \
145 $(top_builddir)/src/util/libgnunetutil.la
146
147test_container_multihashmap_SOURCES = \
148 test_container_multihashmap.c
149test_container_multihashmap_LDADD = \
150 $(top_builddir)/src/util/libgnunetutil.la
151
152test_crypto_aes_SOURCES = \
153 test_crypto_aes.c
154test_crypto_aes_LDADD = \
155 $(top_builddir)/src/util/libgnunetutil.la
156
157test_crypto_aes_weak_SOURCES = \
158 test_crypto_aes_weak.c
159test_crypto_aes_weak_LDADD = \
160 $(top_builddir)/src/util/libgnunetutil.la
161
162test_crypto_crc_SOURCES = \
163 test_crypto_crc.c
164test_crypto_crc_LDADD = \
165 $(top_builddir)/src/util/libgnunetutil.la
166
167test_crypto_hash_SOURCES = \
168 test_crypto_hash.c
169test_crypto_hash_LDADD = \
170 $(top_builddir)/src/util/libgnunetutil.la
171
172test_crypto_ksk_SOURCES = \
173 test_crypto_ksk.c
174test_crypto_ksk_LDADD = \
175 $(top_builddir)/src/util/libgnunetutil.la
176
177test_crypto_random_SOURCES = \
178 test_crypto_random.c
179test_crypto_random_LDADD = \
180 $(top_builddir)/src/util/libgnunetutil.la
181
182test_crypto_rsa_SOURCES = \
183 test_crypto_rsa.c
184test_crypto_rsa_LDADD = \
185 $(top_builddir)/src/util/libgnunetutil.la
186
187test_disk_SOURCES = \
188 test_disk.c
189test_disk_LDADD = \
190 $(top_builddir)/src/util/libgnunetutil.la
191
192test_getopt_SOURCES = \
193 test_getopt.c
194test_getopt_LDADD = \
195 $(top_builddir)/src/util/libgnunetutil.la
196
197test_network_SOURCES = \
198 test_network.c
199test_network_LDADD = \
200 $(top_builddir)/src/util/libgnunetutil.la
201
202test_network_addressing_SOURCES = \
203 test_network_addressing.c
204test_network_addressing_LDADD = \
205 $(top_builddir)/src/util/libgnunetutil.la
206
207test_network_receive_cancel_SOURCES = \
208 test_network_receive_cancel.c
209test_network_receive_cancel_LDADD = \
210 $(top_builddir)/src/util/libgnunetutil.la
211
212test_network_timeout_SOURCES = \
213 test_network_timeout.c
214test_network_timeout_LDADD = \
215 $(top_builddir)/src/util/libgnunetutil.la
216
217test_network_timeout_no_connect_SOURCES = \
218 test_network_timeout_no_connect.c
219test_network_timeout_no_connect_LDADD = \
220 $(top_builddir)/src/util/libgnunetutil.la
221
222test_network_transmit_cancel_SOURCES = \
223 test_network_transmit_cancel.c
224test_network_transmit_cancel_LDADD = \
225 $(top_builddir)/src/util/libgnunetutil.la
226
227test_os_load_SOURCES = \
228 test_os_load.c
229test_os_load_LDADD = \
230 $(top_builddir)/src/util/libgnunetutil.la
231
232test_os_network_SOURCES = \
233 test_os_network.c
234test_os_network_LDADD = \
235 $(top_builddir)/src/util/libgnunetutil.la
236
237test_os_priority_SOURCES = \
238 test_os_priority.c
239test_os_priority_LDADD = \
240 $(top_builddir)/src/util/libgnunetutil.la
241
242test_plugin_SOURCES = \
243 test_plugin.c
244test_plugin_LDADD = \
245 $(top_builddir)/src/util/libgnunetutil.la
246
247test_program_SOURCES = \
248 test_program.c
249test_program_LDADD = \
250 $(top_builddir)/src/util/libgnunetutil.la
251
252test_pseudonym_SOURCES = \
253 test_pseudonym.c
254test_pseudonym_LDADD = \
255 $(top_builddir)/src/util/libgnunetutil.la
256
257test_scheduler_SOURCES = \
258 test_scheduler.c
259test_scheduler_LDADD = \
260 $(top_builddir)/src/util/libgnunetutil.la
261
262test_scheduler_delay_SOURCES = \
263 test_scheduler_delay.c
264test_scheduler_delay_LDADD = \
265 $(top_builddir)/src/util/libgnunetutil.la
266
267test_server_SOURCES = \
268 test_server.c
269test_server_LDADD = \
270 $(top_builddir)/src/util/libgnunetutil.la
271
272test_server_disconnect_SOURCES = \
273 test_server_disconnect.c
274test_server_disconnect_LDADD = \
275 $(top_builddir)/src/util/libgnunetutil.la
276
277test_server_with_client_SOURCES = \
278 test_server_with_client.c
279test_server_with_client_LDADD = \
280 $(top_builddir)/src/util/libgnunetutil.la
281
282test_service_SOURCES = \
283 test_service.c
284test_service_LDADD = \
285 $(top_builddir)/src/util/libgnunetutil.la
286
287test_strings_SOURCES = \
288 test_strings.c
289test_strings_LDADD = \
290 $(top_builddir)/src/util/libgnunetutil.la
291
292test_time_SOURCES = \
293 test_time.c
294test_time_LDADD = \
295 $(top_builddir)/src/util/libgnunetutil.la
296
297perf_crypto_hash_SOURCES = \
298 perf_crypto_hash.c
299perf_crypto_hash_LDADD = \
300 $(top_builddir)/src/util/libgnunetutil.la
301
302
303EXTRA_DIST = \
304 test_configuration_data.conf \
305 test_container_meta_data_image.jpg \
306 test_program_data.conf \
307 test_pseudonym_data.conf \
308 test_service_data.conf
diff --git a/src/util/client.c b/src/util/client.c
new file mode 100644
index 000000000..9dd70f266
--- /dev/null
+++ b/src/util/client.c
@@ -0,0 +1,526 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/client.c
23 * @brief code for access to services
24 * @author Christian Grothoff
25 *
26 * Generic TCP code for reliable, record-oriented TCP
27 * connections between clients and service providers.
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_client_lib.h"
33#include "gnunet_protocols.h"
34#include "gnunet_server_lib.h"
35#include "gnunet_scheduler_lib.h"
36
37#define DEBUG_CLIENT GNUNET_NO
38
39/**
40 * Struct to refer to a GNUnet TCP connection.
41 * This is more than just a socket because if the server
42 * drops the connection, the client automatically tries
43 * to reconnect (and for that needs connection information).
44 */
45struct GNUNET_CLIENT_Connection
46{
47
48 /**
49 * the socket handle, NULL if not live
50 */
51 struct GNUNET_NETWORK_SocketHandle *sock;
52
53 /**
54 * Our scheduler.
55 */
56 struct GNUNET_SCHEDULER_Handle *sched;
57
58 /**
59 * Name of the service we interact with.
60 */
61 char *service_name;
62
63 /**
64 * ID of task used for receiving.
65 */
66 GNUNET_SCHEDULER_TaskIdentifier receiver_task;
67
68 /**
69 * Handler for current receiver task.
70 */
71 GNUNET_CLIENT_MessageHandler receiver_handler;
72
73 /**
74 * Closure for receiver_handler.
75 */
76 void *receiver_handler_cls;
77
78 /**
79 * Handler for service test completion (NULL unless in service_test)
80 */
81 GNUNET_SCHEDULER_Task test_cb;
82
83 /**
84 * Closure for test_cb (NULL unless in service_test)
85 */
86 void *test_cb_cls;
87
88 /**
89 * Buffer for received message.
90 */
91 char *received_buf;
92
93 /**
94 * Timeout for receiving a response (absolute time).
95 */
96 struct GNUNET_TIME_Absolute receive_timeout;
97
98 /**
99 * Number of bytes in received_buf that are valid.
100 */
101 size_t received_pos;
102
103 /**
104 * Size of received_buf.
105 */
106 size_t received_size;
107
108 /**
109 * Do we have a complete response in received_buf?
110 */
111 int msg_complete;
112
113};
114
115
116/**
117 * Get a connection with a service.
118 *
119 * @param sched scheduler to use
120 * @param service_name name of the service
121 * @param cfg configuration to use
122 * @return NULL on error (service unknown to configuration)
123 */
124struct GNUNET_CLIENT_Connection *
125GNUNET_CLIENT_connect (struct GNUNET_SCHEDULER_Handle *sched,
126 const char *service_name,
127 struct GNUNET_CONFIGURATION_Handle *cfg)
128{
129 struct GNUNET_CLIENT_Connection *ret;
130 struct GNUNET_NETWORK_SocketHandle *sock;
131 char *hostname;
132 unsigned long long port;
133
134 if ((GNUNET_OK !=
135 GNUNET_CONFIGURATION_get_value_number (cfg,
136 service_name,
137 "PORT",
138 &port)) ||
139 (port > 65535) ||
140 (GNUNET_OK !=
141 GNUNET_CONFIGURATION_get_value_string (cfg,
142 service_name,
143 "HOSTNAME", &hostname)))
144 {
145 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
146 "Could not determine valid hostname and port for service `%s' from configuration.\n",
147 service_name);
148 return NULL;
149 }
150 sock = GNUNET_NETWORK_socket_create_from_connect (sched,
151 hostname,
152 port,
153 GNUNET_SERVER_MAX_MESSAGE_SIZE);
154 GNUNET_free (hostname);
155 if (sock == NULL)
156 return NULL;
157 ret = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_Connection));
158 ret->sock = sock;
159 ret->sched = sched;
160 ret->service_name = GNUNET_strdup (service_name);
161 return ret;
162}
163
164
165/**
166 * Receiver task has completed, free rest of client
167 * data structures.
168 */
169static void
170finish_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
171{
172 struct GNUNET_CLIENT_Connection *sock = cls;
173
174 GNUNET_array_grow (sock->received_buf, sock->received_size, 0);
175 GNUNET_free (sock->service_name);
176 GNUNET_free (sock);
177}
178
179
180/**
181 * Destroy connection with the service.
182 */
183void
184GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock)
185{
186 GNUNET_assert (sock->sock != NULL);
187 GNUNET_NETWORK_socket_destroy (sock->sock);
188 sock->sock = NULL;
189 sock->receiver_handler = NULL;
190 GNUNET_SCHEDULER_add_after (sock->sched,
191 GNUNET_YES,
192 GNUNET_SCHEDULER_PRIORITY_KEEP,
193 sock->receiver_task, &finish_cleanup, sock);
194}
195
196
197/**
198 * check if message is complete
199 */
200static void
201check_complete (struct GNUNET_CLIENT_Connection *conn)
202{
203 if ((conn->received_pos >= sizeof (struct GNUNET_MessageHeader)) &&
204 (conn->received_pos >=
205 ntohs (((const struct GNUNET_MessageHeader *) conn->received_buf)->
206 size)))
207 conn->msg_complete = GNUNET_YES;
208}
209
210
211/**
212 * Callback function for data received from the network. Note that
213 * both "available" and "err" would be 0 if the read simply timed out.
214 *
215 * @param cls closure
216 * @param buf pointer to received data
217 * @param available number of bytes availabe in "buf",
218 * possibly 0 (on errors)
219 * @param addr address of the sender
220 * @param addrlen size of addr
221 * @param errCode value of errno (on errors receiving)
222 */
223static void
224receive_helper (void *cls,
225 const void *buf,
226 size_t available,
227 const struct sockaddr *addr, socklen_t addrlen, int errCode)
228{
229 struct GNUNET_CLIENT_Connection *conn = cls;
230 struct GNUNET_TIME_Relative remaining;
231
232 GNUNET_assert (conn->msg_complete == GNUNET_NO);
233 conn->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
234
235 if ((available == 0) || (conn->sock == NULL) || (errCode != 0))
236 {
237 /* signal timeout! */
238 if (conn->receiver_handler != NULL)
239 {
240 conn->receiver_handler (conn->receiver_handler_cls, NULL);
241 conn->receiver_handler = NULL;
242 }
243 return;
244 }
245
246 /* FIXME: optimize for common fast case where buf contains the
247 entire message and we need no copying... */
248
249
250 /* slow path: append to array */
251 if (conn->received_size < conn->received_pos + available)
252 GNUNET_array_grow (conn->received_buf,
253 conn->received_size, conn->received_pos + available);
254 memcpy (&conn->received_buf[conn->received_pos], buf, available);
255 conn->received_pos += available;
256 check_complete (conn);
257 /* check for timeout */
258 remaining = GNUNET_TIME_absolute_get_remaining (conn->receive_timeout);
259 if (remaining.value == 0)
260 {
261 /* signal timeout! */
262 conn->receiver_handler (conn->receiver_handler_cls, NULL);
263 return;
264 }
265 /* back to receive -- either for more data or to call callback! */
266 GNUNET_CLIENT_receive (conn,
267 conn->receiver_handler,
268 conn->receiver_handler_cls, remaining);
269}
270
271
272/**
273 * Continuation to call the receive callback.
274 */
275static void
276receive_task (void *scls, const struct GNUNET_SCHEDULER_TaskContext *tc)
277{
278 struct GNUNET_CLIENT_Connection *sock = scls;
279 const struct GNUNET_MessageHeader *cmsg;
280 struct GNUNET_MessageHeader *msg;
281 GNUNET_CLIENT_MessageHandler handler = sock->receiver_handler;
282 void *cls = sock->receiver_handler_cls;
283 uint16_t msize;
284
285 GNUNET_assert (GNUNET_YES == sock->msg_complete);
286 sock->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
287 cmsg = (const struct GNUNET_MessageHeader *) sock->received_buf;
288 msize = ntohs (cmsg->size);
289 GNUNET_assert (sock->received_pos >= msize);
290 msg = GNUNET_malloc (msize);
291 memcpy (msg, cmsg, msize);
292 memmove (sock->received_buf,
293 &sock->received_buf[msize], sock->received_pos - msize);
294 sock->received_pos -= msize;
295 sock->msg_complete = GNUNET_NO;
296 sock->receiver_handler = NULL;
297 check_complete (sock);
298 handler (cls, msg);
299 GNUNET_free (msg);
300}
301
302
303/**
304 * Read from the service.
305 *
306 * @param sched scheduler to use
307 * @param sock the service
308 * @param handler function to call with the message
309 * @param cls closure for handler
310 * @param timeout how long to wait until timing out
311 */
312void
313GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock,
314 GNUNET_CLIENT_MessageHandler handler,
315 void *cls, struct GNUNET_TIME_Relative timeout)
316{
317 if (sock->sock == NULL)
318 {
319 /* already disconnected, fail instantly! */
320 GNUNET_break (0); /* this should not happen in well-written code! */
321 handler (cls, NULL);
322 return;
323 }
324 GNUNET_assert (sock->receiver_task ==
325 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
326 sock->receiver_handler = handler;
327 sock->receiver_handler_cls = cls;
328 sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
329 if (GNUNET_YES == sock->msg_complete)
330 sock->receiver_task = GNUNET_SCHEDULER_add_after (sock->sched,
331 GNUNET_YES,
332 GNUNET_SCHEDULER_PRIORITY_KEEP,
333 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
334 &receive_task, sock);
335 else
336 sock->receiver_task = GNUNET_NETWORK_receive (sock->sock,
337 GNUNET_SERVER_MAX_MESSAGE_SIZE,
338 timeout,
339 &receive_helper, sock);
340}
341
342
343static size_t
344write_shutdown (void *cls, size_t size, void *buf)
345{
346 struct GNUNET_MessageHeader *msg;
347
348 if (size < sizeof (struct GNUNET_MessageHeader))
349 return 0; /* client disconnected */
350 msg = (struct GNUNET_MessageHeader *) buf;
351 msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN);
352 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
353 return sizeof (struct GNUNET_MessageHeader);
354}
355
356
357/**
358 * Request that the service should shutdown.
359 * Afterwards, the connection should be disconnected.
360 *
361 * @param sched scheduler to use
362 * @param sock the socket connected to the service
363 */
364void
365GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock)
366{
367 GNUNET_NETWORK_notify_transmit_ready (sock->sock,
368 sizeof (struct GNUNET_MessageHeader),
369 GNUNET_TIME_UNIT_FOREVER_REL,
370 &write_shutdown, NULL);
371}
372
373
374/**
375 * Report service unavailable.
376 */
377static void
378service_test_error (struct GNUNET_SCHEDULER_Handle *s,
379 GNUNET_SCHEDULER_Task task, void *task_cls)
380{
381 GNUNET_SCHEDULER_add_continuation (s,
382 GNUNET_YES,
383 task,
384 task_cls,
385 GNUNET_SCHEDULER_REASON_TIMEOUT);
386}
387
388
389/**
390 * Receive confirmation from test, service is up.
391 *
392 * @param cls closure
393 * @param msg message received, NULL on timeout or fatal error
394 */
395static void
396confirm_handler (void *cls, const struct GNUNET_MessageHeader *msg)
397{
398 struct GNUNET_CLIENT_Connection *conn = cls;
399 /* We may want to consider looking at the reply in more
400 detail in the future, for example, is this the
401 correct service? FIXME! */
402 if (msg != NULL)
403 {
404#if DEBUG_CLIENT
405 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406 "Received confirmation that service is running.\n");
407#endif
408 GNUNET_SCHEDULER_add_continuation (conn->sched,
409 GNUNET_YES,
410 conn->test_cb,
411 conn->test_cb_cls,
412 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
413 }
414 else
415 {
416 service_test_error (conn->sched, conn->test_cb, conn->test_cb_cls);
417 }
418 GNUNET_CLIENT_disconnect (conn);
419}
420
421
422static size_t
423write_test (void *cls, size_t size, void *buf)
424{
425 struct GNUNET_MessageHeader *msg;
426
427 if (size < sizeof (struct GNUNET_MessageHeader))
428 {
429#if DEBUG_CLIENT
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 _("Failure to transmit TEST request.\n"));
432#endif
433 return 0; /* client disconnected */
434 }
435#if DEBUG_CLIENT
436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Transmitting TEST request.\n"));
437#endif
438 msg = (struct GNUNET_MessageHeader *) buf;
439 msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
440 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
441 return sizeof (struct GNUNET_MessageHeader);
442}
443
444
445/**
446 * Wait until the service is running.
447 *
448 * @param sched scheduler to use
449 * @param service name of the service to wait for
450 * @param cfg configuration to use
451 * @param timeout how long to wait at most in ms
452 * @param task task to run if service is running
453 * (reason will be "PREREQ_DONE" (service running)
454 * or "TIMEOUT" (service not known to be running))
455 * @param task_cls closure for task
456 */
457void
458GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched,
459 const char *service,
460 struct GNUNET_CONFIGURATION_Handle *cfg,
461 struct GNUNET_TIME_Relative timeout,
462 GNUNET_SCHEDULER_Task task, void *task_cls)
463{
464 struct GNUNET_CLIENT_Connection *conn;
465
466#if DEBUG_CLIENT
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468 "Testing if service `%s' is running.\n", service);
469#endif
470 conn = GNUNET_CLIENT_connect (sched, service, cfg);
471 if (conn == NULL)
472 {
473 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
474 _
475 ("Could not connect to service `%s', must not be running.\n"),
476 service);
477 service_test_error (sched, task, task_cls);
478 return;
479 }
480 conn->test_cb = task;
481 conn->test_cb_cls = task_cls;
482 if (NULL ==
483 GNUNET_NETWORK_notify_transmit_ready (conn->sock,
484 sizeof (struct
485 GNUNET_MessageHeader),
486 timeout, &write_test, NULL))
487 {
488 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
489 _("Failure to transmit request to service `%s'\n"),
490 service);
491 service_test_error (sched, task, task_cls);
492 GNUNET_CLIENT_disconnect (conn);
493 return;
494 }
495 GNUNET_CLIENT_receive (conn, &confirm_handler, conn, timeout);
496}
497
498
499/**
500 * Ask the client to call us once the specified number of bytes
501 * are free in the transmission buffer. May call the notify
502 * method immediately if enough space is available.
503 *
504 * @param client connection to the service
505 * @param size number of bytes to send
506 * @param timeout after how long should we give up (and call
507 * notify with buf NULL and size 0)?
508 * @param notify function to call
509 * @param notify_cls closure for notify
510 * @return NULL if our buffer will never hold size bytes,
511 * a handle if the notify callback was queued (can be used to cancel)
512 */
513struct GNUNET_NETWORK_TransmitHandle *
514GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock,
515 size_t size,
516 struct GNUNET_TIME_Relative timeout,
517 GNUNET_NETWORK_TransmitReadyNotify
518 notify, void *notify_cls)
519{
520 return GNUNET_NETWORK_notify_transmit_ready (sock->sock,
521 size,
522 timeout, notify, notify_cls);
523}
524
525
526/* end of client.c */
diff --git a/src/util/common_allocation.c b/src/util/common_allocation.c
new file mode 100644
index 000000000..9fabb3a32
--- /dev/null
+++ b/src/util/common_allocation.c
@@ -0,0 +1,206 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/memory/common_allocation.c
23 * @brief wrapper around malloc/free
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29
30#ifndef INT_MAX
31#define INT_MAX 0x7FFFFFFF
32#endif
33
34/**
35 * Allocate memory. Checks the return value, aborts if no more
36 * memory is available.
37 *
38 * @param size how many bytes of memory to allocate, do NOT use
39 * this function (or GNUNET_malloc) to allocate more than several MB
40 * of memory, if you are possibly needing a very large chunk use
41 * GNUNET_xmalloc_unchecked_ instead.
42 * @param filename where in the code was the call to GNUNET_array_grow
43 * @param linenumber where in the code was the call to GNUNET_array_grow
44 * @return pointer to size bytes of memory
45 */
46void *
47GNUNET_xmalloc_ (size_t size, const char *filename, int linenumber)
48{
49 /* As a security precaution, we generally do not allow very large
50 allocations using the default 'GNUNET_malloc' macro */
51 GNUNET_assert_at (size <= GNUNET_MAX_GNUNET_MALLOC_CHECKED, filename,
52 linenumber);
53 return GNUNET_xmalloc_unchecked_ (size, filename, linenumber);
54}
55
56void *
57GNUNET_xmalloc_unchecked_ (size_t size, const char *filename, int linenumber)
58{
59 void *result;
60
61 GNUNET_assert_at (size < INT_MAX, filename, linenumber);
62 result = malloc (size);
63 if (result == NULL)
64 {
65 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc");
66 abort ();
67 }
68 memset (result, 0, size);
69 return result;
70}
71
72/**
73 * Reallocate memory. Checks the return value, aborts if no more
74 * memory is available.
75 *
76 * @ptr the pointer to reallocate
77 * @param size how many bytes of memory to allocate, do NOT use
78 * this function (or GNUNET_malloc) to allocate more than several MB
79 * of memory
80 * @param filename where in the code was the call to GNUNET_realloc
81 * @param linenumber where in the code was the call to GNUNET_realloc
82 * @return pointer to size bytes of memory
83 */
84void *
85GNUNET_xrealloc_ (void *ptr,
86 const size_t n, const char *filename, int linenumber)
87{
88 ptr = realloc (ptr, n);
89 if (!ptr)
90 {
91 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "realloc");
92 abort ();
93 }
94 return ptr;
95}
96
97/**
98 * Free memory. Merely a wrapper for the case that we
99 * want to keep track of allocations.
100 *
101 * @param ptr the pointer to free
102 * @param filename where in the code was the call to GNUNET_array_grow
103 * @param linenumber where in the code was the call to GNUNET_array_grow
104 */
105void
106GNUNET_xfree_ (void *ptr, const char *filename, int linenumber)
107{
108 GNUNET_assert_at (ptr != NULL, filename, linenumber);
109 free (ptr);
110}
111
112/**
113 * Dup a string (same semantics as strdup).
114 *
115 * @param str the string to dup
116 * @param filename where in the code was the call to GNUNET_array_grow
117 * @param linenumber where in the code was the call to GNUNET_array_grow
118 * @return strdup(str)
119 */
120char *
121GNUNET_xstrdup_ (const char *str, const char *filename, int linenumber)
122{
123 char *res;
124
125 GNUNET_assert_at (str != NULL, filename, linenumber);
126 res = GNUNET_xmalloc_ (strlen (str) + 1, filename, linenumber);
127 memcpy (res, str, strlen (str) + 1);
128 return res;
129}
130
131/**
132 * Grow an array. Grows old by (*oldCount-newCount)*elementSize bytes
133 * and sets *oldCount to newCount.
134 *
135 * @param old address of the pointer to the array
136 * *old may be NULL
137 * @param elementSize the size of the elements of the array
138 * @param oldCount address of the number of elements in the *old array
139 * @param newCount number of elements in the new array, may be 0
140 * @param filename where in the code was the call to GNUNET_array_grow
141 * @param linenumber where in the code was the call to GNUNET_array_grow
142 */
143void
144GNUNET_xgrow_ (void **old,
145 size_t elementSize,
146 unsigned int *oldCount,
147 unsigned int newCount, const char *filename, int linenumber)
148{
149 void *tmp;
150 size_t size;
151
152 GNUNET_assert_at (INT_MAX / elementSize > newCount, filename, linenumber);
153 size = newCount * elementSize;
154 if (size == 0)
155 {
156 tmp = NULL;
157 }
158 else
159 {
160 tmp = GNUNET_xmalloc_ (size, filename, linenumber);
161 memset (tmp, 0, size); /* client code should not rely on this, though... */
162 if (*oldCount > newCount)
163 *oldCount = newCount; /* shrink is also allowed! */
164 memcpy (tmp, *old, elementSize * (*oldCount));
165 }
166
167 if (*old != NULL)
168 {
169 GNUNET_xfree_ (*old, filename, linenumber);
170 }
171 *old = tmp;
172 *oldCount = newCount;
173}
174
175
176int
177GNUNET_asprintf (char **buf, const char *format, ...)
178{
179 int ret;
180 va_list args;
181
182 va_start (args, format);
183 ret = VSNPRINTF (NULL, 0, format, args);
184 va_end (args);
185 *buf = GNUNET_malloc (ret + 1);
186 va_start (args, format);
187 ret = VSPRINTF (*buf, format, args);
188 va_end (args);
189 return ret;
190}
191
192int
193GNUNET_snprintf (char *buf, size_t size, const char *format, ...)
194{
195 int ret;
196 va_list args;
197
198 va_start (args, format);
199 ret = VSNPRINTF (buf, size, format, args);
200 va_end (args);
201 GNUNET_assert (ret <= size);
202 return ret;
203}
204
205
206/* end of common_allocation.c */
diff --git a/src/util/common_endian.c b/src/util/common_endian.c
new file mode 100644
index 000000000..223144c40
--- /dev/null
+++ b/src/util/common_endian.c
@@ -0,0 +1,52 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/common_endian.c
23 * @brief endian conversion helpers
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29
30unsigned long long
31GNUNET_ntohll (unsigned long long n)
32{
33#if __BYTE_ORDER == __BIG_ENDIAN
34 return n;
35#else
36 return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
37#endif
38}
39
40unsigned long long
41GNUNET_htonll (unsigned long long n)
42{
43#if __BYTE_ORDER == __BIG_ENDIAN
44 return n;
45#else
46 return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
47#endif
48}
49
50
51
52/* end of common_endian.c */
diff --git a/src/util/common_gettext.c b/src/util/common_gettext.c
new file mode 100644
index 000000000..ad347e160
--- /dev/null
+++ b/src/util/common_gettext.c
@@ -0,0 +1,42 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/common_gettext.c
23 * @brief gettext init routine
24 * @author Heikki Lindholm
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_os_lib.h"
29
30void __attribute__ ((constructor)) GNUNET_util_generic_ltdl_init ()
31{
32#if ENABLE_NLS
33 char *path;
34
35 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
36 if (path != NULL)
37 {
38 BINDTEXTDOMAIN ("GNUnet", path);
39 GNUNET_free (path);
40 }
41#endif
42}
diff --git a/src/util/common_logging.c b/src/util/common_logging.c
new file mode 100644
index 000000000..4068ed94b
--- /dev/null
+++ b/src/util/common_logging.c
@@ -0,0 +1,401 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/common_logging.c
23 * @brief error handling API
24 *
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_crypto_lib.h"
30#include "gnunet_strings_lib.h"
31#include "gnunet_time_lib.h"
32
33/**
34 * After how many seconds do we always print
35 * that "message X was repeated N times"? Use 12h.
36 */
37#define BULK_DELAY_THRESHOLD (12 * 60 * 60 * 1000)
38
39/**
40 * After how many repetitions do we always print
41 * that "message X was repeated N times"? (even if
42 * we have not yet reached the delay threshold)
43 */
44#define BULK_REPEAT_THRESHOLD 1000
45
46/**
47 * How many characters do we use for matching of
48 * bulk messages?
49 */
50#define BULK_TRACK_SIZE 256
51
52/**
53 * How many characters can a date/time string
54 * be at most?
55 */
56#define DATE_STR_SIZE 64
57
58/**
59 * Linked list of active loggers.
60 */
61struct CustomLogger
62{
63 /**
64 * This is a linked list.
65 */
66 struct CustomLogger *next;
67
68 /**
69 * Log function.
70 */
71 GNUNET_Logger logger;
72
73 /**
74 * Closure for logger.
75 */
76 void *logger_cls;
77};
78
79/**
80 * The last "bulk" error message that we have been logging.
81 * Note that this message maybe truncated to the first BULK_TRACK_SIZE
82 * characters, in which case it is NOT 0-terminated!
83 */
84static char last_bulk[BULK_TRACK_SIZE];
85
86/**
87 * Type of the last bulk message.
88 */
89static enum GNUNET_ErrorType last_bulk_kind;
90
91/**
92 * Time of the last bulk error message (0 for none)
93 */
94static struct GNUNET_TIME_Absolute last_bulk_time;
95
96/**
97 * Number of times that bulk message has been repeated since.
98 */
99static unsigned int last_bulk_repeat;
100
101/**
102 * Component when the last bulk was logged.
103 */
104static const char *last_bulk_comp;
105
106/**
107 * Running component.
108 */
109static const char *component;
110
111/**
112 * Minimum log level.
113 */
114static enum GNUNET_ErrorType min_level;
115
116/**
117 * Linked list of our custom loggres.
118 */
119static struct CustomLogger *loggers;
120
121/**
122 * Number of log calls to ignore.
123 */
124static unsigned int skip_log;
125
126/**
127 * Convert a textual description of a loglevel
128 * to the respective GNUNET_GE_KIND.
129 * @returns GNUNET_GE_INVALID if log does not parse
130 */
131static enum GNUNET_ErrorType
132get_type (const char *log)
133{
134 if (0 == strcasecmp (log, _("DEBUG")))
135 return GNUNET_ERROR_TYPE_DEBUG;
136 if (0 == strcasecmp (log, _("INFO")))
137 return GNUNET_ERROR_TYPE_INFO;
138 if (0 == strcasecmp (log, _("WARNING")))
139 return GNUNET_ERROR_TYPE_WARNING;
140 if (0 == strcasecmp (log, _("ERROR")))
141 return GNUNET_ERROR_TYPE_ERROR;
142 return GNUNET_ERROR_TYPE_INVALID;
143}
144
145/**
146 * Setup logging.
147 *
148 * @param comp default component to use
149 * @param loglevel what types of messages should be logged
150 */
151int
152GNUNET_log_setup (const char *comp, const char *loglevel, const char *logfile)
153{
154 FILE *altlog;
155
156 component = comp;
157 min_level = get_type (loglevel);
158 if (logfile == NULL)
159 return GNUNET_OK;
160 altlog = fopen (logfile, "a");
161 if (altlog == NULL)
162 {
163 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", logfile);
164 return GNUNET_SYSERR;
165 }
166 if (stderr != NULL)
167 fclose (stderr);
168 stderr = altlog;
169 return GNUNET_OK;
170}
171
172/**
173 * Add a custom logger.
174 *
175 * @param logger log function
176 * @param logger_cls closure for logger
177 */
178void
179GNUNET_logger_add (GNUNET_Logger logger, void *logger_cls)
180{
181 struct CustomLogger *entry;
182
183 entry = GNUNET_malloc (sizeof (struct CustomLogger));
184 entry->logger = logger;
185 entry->logger_cls = logger_cls;
186 entry->next = loggers;
187 loggers = entry;
188}
189
190/**
191 * Remove a custom logger.
192 *
193 * @param logger log function
194 * @param logger_cls closure for logger
195 */
196void
197GNUNET_logger_remove (GNUNET_Logger logger, void *logger_cls)
198{
199 struct CustomLogger *pos;
200 struct CustomLogger *prev;
201
202 prev = NULL;
203 pos = loggers;
204 while ((pos != NULL) &&
205 ((pos->logger != logger) || (pos->logger_cls != logger_cls)))
206 {
207 prev = pos;
208 pos = pos->next;
209 }
210 GNUNET_assert (pos != NULL);
211 if (prev == NULL)
212 loggers = pos->next;
213 else
214 prev->next = pos->next;
215 GNUNET_free (pos);
216}
217
218static void
219output_message (enum GNUNET_ErrorType kind,
220 const char *comp, const char *datestr, const char *msg)
221{
222 struct CustomLogger *pos;
223 if (stderr != NULL)
224 fprintf (stderr, "%s %s %s %s", datestr, comp,
225 GNUNET_error_type_to_string (kind), msg);
226 pos = loggers;
227 while (pos != NULL)
228 {
229 pos->logger (pos->logger_cls, kind, comp, datestr, msg);
230 pos = pos->next;
231 }
232}
233
234static void
235flush_bulk (const char *datestr)
236{
237 char msg[DATE_STR_SIZE + BULK_TRACK_SIZE + 256];
238 int rev;
239 char *last;
240 char *ft;
241
242 if ((last_bulk_time.value == 0) || (last_bulk_repeat == 0))
243 return;
244 rev = 0;
245 last = memchr (last_bulk, '\0', BULK_TRACK_SIZE);
246 if (last == NULL)
247 last = &last_bulk[BULK_TRACK_SIZE - 1];
248 else if (last != last_bulk)
249 last--;
250 if (last[0] == '\n')
251 {
252 rev = 1;
253 last[0] = '\0';
254 }
255 ft =
256 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration
257 (last_bulk_time));
258 snprintf (msg, sizeof (msg),
259 _("Message `%.*s' repeated %u times in the last %s\n"),
260 BULK_TRACK_SIZE, last_bulk, last_bulk_repeat, ft);
261 GNUNET_free (ft);
262 if (rev == 1)
263 last[0] = '\n';
264 output_message (last_bulk_kind, last_bulk_comp, datestr, msg);
265 last_bulk_time = GNUNET_TIME_absolute_get ();
266 last_bulk_repeat = 0;
267}
268
269
270/**
271 * Ignore the next n calls to the log function.
272 *
273 * @param n number of log calls to ignore
274 */
275void
276GNUNET_log_skip (unsigned int n)
277{
278 int ok;
279
280 if (n == 0)
281 {
282 ok = (0 == skip_log);
283 skip_log = 0;
284 GNUNET_assert (ok);
285 }
286 skip_log += n;
287}
288
289
290static void
291mylog (enum GNUNET_ErrorType kind,
292 const char *comp, const char *message, va_list va)
293{
294 char date[DATE_STR_SIZE];
295 time_t timetmp;
296 struct tm *tmptr;
297 size_t size;
298 char *buf;
299 va_list vacp;
300
301 if (skip_log > 0)
302 {
303 skip_log--;
304 return;
305 }
306 if ((kind & (~GNUNET_ERROR_TYPE_BULK)) > min_level)
307 return;
308 va_copy (vacp, va);
309 size = VSNPRINTF (NULL, 0, message, vacp) + 1;
310 va_end (vacp);
311 buf = malloc (size);
312 if (buf == NULL)
313 return; /* oops */
314 VSNPRINTF (buf, size, message, va);
315 time (&timetmp);
316 memset (date, 0, DATE_STR_SIZE);
317 tmptr = localtime (&timetmp);
318 strftime (date, DATE_STR_SIZE, "%b %d %H:%M:%S", tmptr);
319 if ((0 != (kind & GNUNET_ERROR_TYPE_BULK)) &&
320 (last_bulk_time.value != 0) &&
321 (0 == strncmp (buf, last_bulk, sizeof (last_bulk))))
322 {
323 last_bulk_repeat++;
324 if ((GNUNET_TIME_absolute_get_duration (last_bulk_time).value >
325 BULK_DELAY_THRESHOLD)
326 || (last_bulk_repeat > BULK_REPEAT_THRESHOLD))
327 flush_bulk (date);
328 free (buf);
329 return;
330 }
331 flush_bulk (date);
332 strncpy (last_bulk, buf, sizeof (last_bulk));
333 last_bulk_repeat = 0;
334 last_bulk_kind = kind;
335 last_bulk_time = GNUNET_TIME_absolute_get ();
336 last_bulk_comp = comp;
337 output_message (kind, comp, date, buf);
338 free (buf);
339}
340
341
342void
343GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...)
344{
345 va_list va;
346 va_start (va, message);
347 mylog (kind, component, message, va);
348 va_end (va);
349}
350
351
352void
353GNUNET_log_from (enum GNUNET_ErrorType kind,
354 const char *comp, const char *message, ...)
355{
356 va_list va;
357 va_start (va, message);
358 mylog (kind, comp, message, va);
359 va_end (va);
360}
361
362
363/**
364 * Convert KIND to String
365 */
366const char *
367GNUNET_error_type_to_string (enum GNUNET_ErrorType kind)
368{
369 if ((kind & GNUNET_ERROR_TYPE_ERROR) > 0)
370 return _("ERROR");
371 if ((kind & GNUNET_ERROR_TYPE_WARNING) > 0)
372 return _("WARNING");
373 if ((kind & GNUNET_ERROR_TYPE_INFO) > 0)
374 return _("INFO");
375 if ((kind & GNUNET_ERROR_TYPE_DEBUG) > 0)
376 return _("DEBUG");
377 return _("INVALID");
378}
379
380
381/**
382 * Convert a peer identity to a string (for printing debug messages).
383 * This is one of the very few calls in the entire API that is
384 * NOT reentrant!
385 *
386 * @param pid the peer identity
387 * @return string form of the pid; will be overwritten by next
388 * call to GNUNET_i2s.
389 */
390const char *
391GNUNET_i2s (const struct GNUNET_PeerIdentity *pid)
392{
393 static struct GNUNET_CRYPTO_HashAsciiEncoded ret;
394 GNUNET_CRYPTO_hash_to_enc (&pid->hashPubKey, &ret);
395 ret.encoding[4] = '\0';
396 return (const char *) ret.encoding;
397}
398
399
400
401/* end of common_logging.c */
diff --git a/src/util/configuration.c b/src/util/configuration.c
new file mode 100644
index 000000000..4413b0377
--- /dev/null
+++ b/src/util/configuration.c
@@ -0,0 +1,848 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/util/configuration.c
23 * @brief configuration management
24 *
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_configuration_lib.h"
31#include "gnunet_crypto_lib.h"
32#include "gnunet_disk_lib.h"
33#include "gnunet_os_lib.h"
34#include "gnunet_strings_lib.h"
35
36/**
37 * @brief configuration entry
38 */
39struct ConfigEntry
40{
41
42 /**
43 * This is a linked list.
44 */
45 struct ConfigEntry *next;
46
47 /**
48 * key for this entry
49 */
50 char *key;
51
52 /**
53 * current, commited value
54 */
55 char *val;
56};
57
58/**
59 * @brief configuration section
60 */
61struct ConfigSection
62{
63 /**
64 * This is a linked list.
65 */
66 struct ConfigSection *next;
67
68 /**
69 * entries in the section
70 */
71 struct ConfigEntry *entries;
72
73 /**
74 * name of the section
75 */
76 char *name;
77};
78
79/**
80 * @brief configuration data
81 */
82struct GNUNET_CONFIGURATION_Handle
83{
84 /**
85 * Configuration sections.
86 */
87 struct ConfigSection *sections;
88
89 /**
90 * Modification indication since last save
91 * GNUNET_NO if clean, GNUNET_YES if dirty,
92 * GNUNET_SYSERR on error (i.e. last save failed)
93 */
94 int dirty;
95
96};
97
98/**
99 * Create a GNUNET_CONFIGURATION_Configuration.
100 */
101struct GNUNET_CONFIGURATION_Handle *
102GNUNET_CONFIGURATION_create ()
103{
104 return GNUNET_malloc (sizeof (struct GNUNET_CONFIGURATION_Handle));
105}
106
107void
108GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg)
109{
110 struct ConfigSection *sec;
111 struct ConfigEntry *ent;
112
113 while (NULL != (sec = cfg->sections))
114 {
115 cfg->sections = sec->next;
116 while (NULL != (ent = sec->entries))
117 {
118 sec->entries = ent->next;
119 GNUNET_free (ent->key);
120 GNUNET_free_non_null (ent->val);
121 GNUNET_free (ent);
122 }
123 GNUNET_free (sec->name);
124 GNUNET_free (sec);
125 }
126 GNUNET_free (cfg);
127}
128
129int
130GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg,
131 const char *filename)
132{
133 int dirty;
134 char line[256];
135 char tag[64];
136 char value[192];
137 FILE *fp;
138 unsigned int nr;
139 int i;
140 int emptyline;
141 int ret;
142 char *section;
143 char *fn;
144
145 fn = GNUNET_STRINGS_filename_expand (filename);
146 dirty = cfg->dirty; /* back up value! */
147 if (NULL == (fp = FOPEN (fn, "r")))
148 {
149 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", fn);
150 GNUNET_free (fn);
151 return GNUNET_SYSERR;
152 }
153 GNUNET_free (fn);
154 ret = GNUNET_OK;
155 section = GNUNET_strdup ("");
156 memset (line, 0, 256);
157 nr = 0;
158 while (NULL != fgets (line, 255, fp))
159 {
160 nr++;
161 for (i = 0; i < 255; i++)
162 if (line[i] == '\t')
163 line[i] = ' ';
164 if (line[0] == '\n' || line[0] == '#' || line[0] == '%' ||
165 line[0] == '\r')
166 continue;
167 emptyline = 1;
168 for (i = 0; (i < 255 && line[i] != 0); i++)
169 if (line[i] != ' ' && line[i] != '\n' && line[i] != '\r')
170 emptyline = 0;
171 if (emptyline == 1)
172 continue;
173 /* remove tailing whitespace */
174 for (i = strlen (line) - 1; (i >= 0) && (isspace (line[i])); i--)
175 line[i] = '\0';
176 if (1 == sscanf (line, "@INLINE@ %191[^\n]", value))
177 {
178 /* @INLINE@ value */
179 if (0 != GNUNET_CONFIGURATION_parse (cfg, value))
180 ret = GNUNET_SYSERR; /* failed to parse included config */
181 }
182 else if (1 == sscanf (line, "[%99[^]]]", value))
183 {
184 /* [value] */
185 GNUNET_free (section);
186 section = GNUNET_strdup (value);
187 }
188 else if (2 == sscanf (line, " %63[^= ] = %191[^\n]", tag, value))
189 {
190 /* tag = value */
191 /* Strip LF */
192 i = strlen (value) - 1;
193 while ((i >= 0) && (isspace (value[i])))
194 value[i--] = '\0';
195 /* remove quotes */
196 i = 0;
197 if (value[0] == '"')
198 {
199 i = 1;
200 while ((value[i] != '\0') && (value[i] != '"'))
201 i++;
202 if (value[i] == '"')
203 {
204 value[i] = '\0';
205 i = 1;
206 }
207 else
208 i = 0;
209 }
210 GNUNET_CONFIGURATION_set_value_string (cfg,
211 section, tag, &value[i]);
212 }
213 else if (1 == sscanf (line, " %63[^= ] =[^\n]", tag))
214 {
215 /* tag = */
216 GNUNET_CONFIGURATION_set_value_string (cfg, section, tag, "");
217 }
218 else
219 {
220 /* parse error */
221 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
222 _
223 ("Syntax error in configuration file `%s' at line %u.\n"),
224 filename, nr);
225 ret = GNUNET_SYSERR;
226 break;
227 }
228 }
229 GNUNET_assert (0 == fclose (fp));
230 /* restore dirty flag - anything we set in the meantime
231 came from disk */
232 cfg->dirty = dirty;
233 GNUNET_free (section);
234 return ret;
235}
236
237int
238GNUNET_CONFIGURATION_test_dirty (struct GNUNET_CONFIGURATION_Handle *cfg)
239{
240 return cfg->dirty;
241}
242
243int
244GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *data,
245 const char *filename)
246{
247 struct ConfigSection *sec;
248 struct ConfigEntry *ent;
249 FILE *fp;
250 int error;
251 char *fn;
252 char *val;
253 char *pos;
254
255 fn = GNUNET_STRINGS_filename_expand (filename);
256 GNUNET_DISK_directory_create_for_file (fn);
257 if (NULL == (fp = FOPEN (fn, "w")))
258 {
259 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", fn);
260 GNUNET_free (fn);
261 return GNUNET_SYSERR;
262 }
263 GNUNET_free (fn);
264 error = 0;
265 sec = data->sections;
266 while (sec != NULL)
267 {
268 if (0 > fprintf (fp, "[%s]\n", sec->name))
269 {
270 error = 1;
271 break;
272 }
273 ent = sec->entries;
274 while (ent != NULL)
275 {
276 if (ent->val != NULL)
277 {
278 val = GNUNET_malloc (strlen (ent->val) * 2 + 1);
279 strcpy (val, ent->val);
280 while (NULL != (pos = strstr (val, "\n")))
281 {
282 memmove (&pos[2], &pos[1], strlen (&pos[1]));
283 pos[0] = '\\';
284 pos[1] = 'n';
285 }
286 if (0 > fprintf (fp, "%s = %s\n", ent->key, val))
287 {
288 error = 1;
289 GNUNET_free (val);
290 break;
291 }
292 GNUNET_free (val);
293 }
294 ent = ent->next;
295 }
296 if (error != 0)
297 break;
298 if (0 > fprintf (fp, "\n"))
299 {
300 error = 1;
301 break;
302 }
303 sec = sec->next;
304 }
305 if (error != 0)
306 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fprintf", filename);
307 GNUNET_assert (0 == fclose (fp));
308 if (error != 0)
309 {
310 data->dirty = GNUNET_SYSERR; /* last write failed */
311 return GNUNET_SYSERR;
312 }
313 data->dirty = GNUNET_NO; /* last write succeeded */
314 return GNUNET_OK;
315}
316
317
318static struct ConfigSection *
319findSection (struct GNUNET_CONFIGURATION_Handle *data, const char *section)
320{
321 struct ConfigSection *pos;
322
323 pos = data->sections;
324 while ((pos != NULL) && (0 != strcasecmp (section, pos->name)))
325 pos = pos->next;
326 return pos;
327}
328
329
330static struct ConfigEntry *
331findEntry (struct GNUNET_CONFIGURATION_Handle *data,
332 const char *section, const char *key)
333{
334 struct ConfigSection *sec;
335 struct ConfigEntry *pos;
336
337 sec = findSection (data, section);
338 if (sec == NULL)
339 return NULL;
340 pos = sec->entries;
341 while ((pos != NULL) && (0 != strcasecmp (key, pos->key)))
342 pos = pos->next;
343 return pos;
344}
345
346void
347GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle
348 *data,
349 const char *section,
350 const char *option, const char *value)
351{
352 struct ConfigSection *sec;
353 struct ConfigEntry *e;
354
355 e = findEntry (data, section, option);
356 if (e != NULL)
357 {
358 GNUNET_free_non_null (e->val);
359 e->val = GNUNET_strdup (value);
360 return;
361 }
362 sec = findSection (data, section);
363 if (sec == NULL)
364 {
365 sec = GNUNET_malloc (sizeof (struct ConfigSection));
366 sec->name = GNUNET_strdup (section);
367 sec->next = data->sections;
368 data->sections = sec;
369 }
370 e = GNUNET_malloc (sizeof (struct ConfigEntry));
371 e->key = GNUNET_strdup (option);
372 e->val = GNUNET_strdup (value);
373 e->next = sec->entries;
374 sec->entries = e;
375}
376
377void
378GNUNET_CONFIGURATION_set_value_number (struct GNUNET_CONFIGURATION_Handle
379 *cfg, const char *section,
380 const char *option,
381 unsigned long long number)
382{
383 char s[64];
384 GNUNET_snprintf (s, 64, "%llu", number);
385 GNUNET_CONFIGURATION_set_value_string (cfg, section, option, s);
386}
387
388int
389GNUNET_CONFIGURATION_get_value_number (struct GNUNET_CONFIGURATION_Handle
390 *cfg, const char *section,
391 const char *option,
392 unsigned long long *number)
393{
394 struct ConfigEntry *e;
395
396 e = findEntry (cfg, section, option);
397 if (e == NULL)
398 return GNUNET_SYSERR;
399 if (1 != SSCANF (e->val, "%llu", number))
400 return GNUNET_SYSERR;
401 return GNUNET_OK;
402}
403
404int
405GNUNET_CONFIGURATION_get_value_string (struct GNUNET_CONFIGURATION_Handle
406 *cfg, const char *section,
407 const char *option, char **value)
408{
409 struct ConfigEntry *e;
410
411 e = findEntry (cfg, section, option);
412 if ((e == NULL) || (e->val == NULL))
413 {
414 *value = NULL;
415 return GNUNET_SYSERR;
416 }
417 *value = GNUNET_strdup (e->val);
418 return GNUNET_OK;
419}
420
421int
422GNUNET_CONFIGURATION_get_value_choice (struct GNUNET_CONFIGURATION_Handle
423 *cfg, const char *section,
424 const char *option,
425 const char **choices,
426 const char **value)
427{
428 struct ConfigEntry *e;
429 int i;
430
431 e = findEntry (cfg, section, option);
432 if (e == NULL)
433 return GNUNET_SYSERR;
434 i = 0;
435 while (choices[i] != NULL)
436 {
437 if (0 == strcasecmp (choices[i], e->val))
438 break;
439 i++;
440 }
441 if (choices[i] == NULL)
442 {
443 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
444 _("Configuration value '%s' for '%s'"
445 " in section '%s' is not in set of legal choices\n"),
446 e->val, option, section);
447 return GNUNET_SYSERR;
448 }
449 *value = choices[i];
450 return GNUNET_OK;
451}
452
453/**
454 * Test if we have a value for a particular option
455 * @return GNUNET_YES if so, GNUNET_NO if not.
456 */
457int
458GNUNET_CONFIGURATION_have_value (struct GNUNET_CONFIGURATION_Handle *cfg,
459 const char *section, const char *option)
460{
461 struct ConfigEntry *e;
462 if ((NULL == (e = findEntry (cfg, section, option))) || (e->val == NULL))
463 return GNUNET_NO;
464 return GNUNET_YES;
465}
466
467/**
468 * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR"
469 * where either in the "PATHS" section or the environtment
470 * "FOO" is set to "DIRECTORY".
471 *
472 * @param old string to $-expand (will be freed!)
473 * @return $-expanded string
474 */
475char *
476GNUNET_CONFIGURATION_expand_dollar (struct GNUNET_CONFIGURATION_Handle *cfg,
477 char *orig)
478{
479 int i;
480 char *prefix;
481 char *result;
482 const char *post;
483 const char *env;
484
485 if (orig[0] != '$')
486 return orig;
487 i = 0;
488 while ((orig[i] != '/') && (orig[i] != '\\') && (orig[i] != '\0'))
489 i++;
490 if (orig[i] == '\0')
491 {
492 post = "";
493 }
494 else
495 {
496 orig[i] = '\0';
497 post = &orig[i + 1];
498 }
499 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
500 "PATHS",
501 &orig[1], &prefix))
502 {
503 if (NULL == (env = getenv (&orig[1])))
504 {
505 orig[i] = DIR_SEPARATOR;
506 return orig;
507 }
508 prefix = GNUNET_strdup (env);
509 }
510 result = GNUNET_malloc (strlen (prefix) + strlen (post) + 2);
511 strcpy (result, prefix);
512 if ((strlen (prefix) == 0) ||
513 ((prefix[strlen (prefix) - 1] != DIR_SEPARATOR) && (strlen (post) > 0)))
514 strcat (result, DIR_SEPARATOR_STR);
515 strcat (result, post);
516 GNUNET_free (prefix);
517 GNUNET_free (orig);
518 return result;
519}
520
521/**
522 * Get a configuration value that should be a string.
523 * @param value will be set to a freshly allocated configuration
524 * value, or NULL if option is not specified
525 * @return GNUNET_OK on success, GNUNET_SYSERR on error
526 */
527int
528GNUNET_CONFIGURATION_get_value_filename (struct GNUNET_CONFIGURATION_Handle
529 *data, const char *section,
530 const char *option, char **value)
531{
532 int ret;
533 char *tmp;
534
535 tmp = NULL;
536 ret = GNUNET_CONFIGURATION_get_value_string (data, section, option, &tmp);
537 if (ret == GNUNET_SYSERR)
538 return ret;
539 if (tmp != NULL)
540 {
541 tmp = GNUNET_CONFIGURATION_expand_dollar (data, tmp);
542 *value = GNUNET_STRINGS_filename_expand (tmp);
543 GNUNET_free (tmp);
544 }
545 else
546 {
547 *value = NULL;
548 }
549 return ret;
550}
551
552/**
553 * Get a configuration value that should be in a set of
554 * "GNUNET_YES" or "GNUNET_NO".
555 *
556 * @return GNUNET_YES, GNUNET_NO or GNUNET_SYSERR
557 */
558int
559GNUNET_CONFIGURATION_get_value_yesno (struct GNUNET_CONFIGURATION_Handle *cfg,
560 const char *section, const char *option)
561{
562 static const char *yesno[] = { "YES", "NO", NULL };
563 const char *val;
564 int ret;
565
566 ret = GNUNET_CONFIGURATION_get_value_choice (cfg,
567 section, option, yesno, &val);
568 if (ret == GNUNET_SYSERR)
569 return ret;
570 if (val == yesno[0])
571 return GNUNET_YES;
572 return GNUNET_NO;
573}
574
575
576/**
577 * Iterate over the set of filenames stored in a configuration value.
578 *
579 * @return number of filenames iterated over, -1 on error
580 */
581int
582GNUNET_CONFIGURATION_iterate_value_filenames (struct
583 GNUNET_CONFIGURATION_Handle
584 *cfg, const char *section,
585 const char *option,
586 GNUNET_FileNameCallback cb,
587 void *cls)
588{
589 char *list;
590 char *pos;
591 char *end;
592 char old;
593 int ret;
594
595 if (GNUNET_OK !=
596 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list))
597 return 0;
598 GNUNET_assert (list != NULL);
599 ret = 0;
600 pos = list;
601 while (1)
602 {
603 while (pos[0] == ' ')
604 pos++;
605 if (strlen (pos) == 0)
606 break;
607 end = pos + 1;
608 while ((end[0] != ' ') && (end[0] != '\0'))
609 {
610 if (end[0] == '\\')
611 {
612 switch (end[1])
613 {
614 case '\\':
615 case ' ':
616 memmove (end, &end[1], strlen (&end[1]) + 1);
617 case '\0':
618 /* illegal, but just keep it */
619 break;
620 default:
621 /* illegal, but just ignore that there was a '/' */
622 break;
623 }
624 }
625 end++;
626 }
627 old = end[0];
628 end[0] = '\0';
629 if (strlen (pos) > 0)
630 {
631 ret++;
632 if ((cb != NULL) && (GNUNET_OK != cb (cls, pos)))
633 {
634 ret = GNUNET_SYSERR;
635 break;
636 }
637 }
638 if (old == '\0')
639 break;
640 pos = end + 1;
641 }
642 GNUNET_free (list);
643 return ret;
644}
645
646static char *
647escape_name (const char *value)
648{
649 char *escaped;
650 const char *rpos;
651 char *wpos;
652
653 escaped = GNUNET_malloc (strlen (value) * 2 + 1);
654 memset (escaped, 0, strlen (value) * 2 + 1);
655 rpos = value;
656 wpos = escaped;
657 while (rpos[0] != '\0')
658 {
659 switch (rpos[0])
660 {
661 case '\\':
662 case ' ':
663 wpos[0] = '\\';
664 wpos[1] = rpos[0];
665 wpos += 2;
666 break;
667 default:
668 wpos[0] = rpos[0];
669 wpos++;
670 }
671 rpos++;
672 }
673 return escaped;
674}
675
676static int
677test_match (void *cls, const char *fn)
678{
679 const char *of = cls;
680 return (0 == strcmp (of, fn)) ? GNUNET_SYSERR : GNUNET_OK;
681}
682
683/**
684 * Append a filename to a configuration value that
685 * represents a list of filenames
686 *
687 * @param value filename to append
688 * @return GNUNET_OK on success,
689 * GNUNET_NO if the filename already in the list
690 * GNUNET_SYSERR on error
691 */
692int
693GNUNET_CONFIGURATION_append_value_filename (struct GNUNET_CONFIGURATION_Handle
694 *cfg,
695 const char *section,
696 const char *option,
697 const char *value)
698{
699 char *escaped;
700 char *old;
701 char *nw;
702
703 if (GNUNET_SYSERR
704 == GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
705 section,
706 option,
707 &test_match,
708 (void *) value))
709 return GNUNET_NO; /* already exists */
710 if (GNUNET_OK !=
711 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &old))
712 old = GNUNET_strdup ("");
713 escaped = escape_name (value);
714 nw = GNUNET_malloc (strlen (old) + strlen (escaped) + 2);
715 strcpy (nw, old);
716 strcat (nw, " ");
717 strcat (nw, escaped);
718 GNUNET_CONFIGURATION_set_value_string (cfg, section, option, nw);
719 GNUNET_free (old);
720 GNUNET_free (nw);
721 GNUNET_free (escaped);
722 return GNUNET_OK;
723}
724
725
726/**
727 * Remove a filename from a configuration value that
728 * represents a list of filenames
729 *
730 * @param value filename to remove
731 * @return GNUNET_OK on success,
732 * GNUNET_NO if the filename is not in the list,
733 * GNUNET_SYSERR on error
734 */
735int
736GNUNET_CONFIGURATION_remove_value_filename (struct GNUNET_CONFIGURATION_Handle
737 *cfg,
738 const char *section,
739 const char *option,
740 const char *value)
741{
742 char *list;
743 char *pos;
744 char *end;
745 char *match;
746 char old;
747 int ret;
748
749 if (GNUNET_OK !=
750 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list))
751 return GNUNET_NO;
752 match = escape_name (value);
753 ret = 0;
754 pos = list;
755 while (1)
756 {
757 while (pos[0] == ' ')
758 pos++;
759 if (strlen (pos) == 0)
760 break;
761 end = pos + 1;
762 while ((end[0] != ' ') && (end[0] != '\0'))
763 {
764 if (end[0] == '\\')
765 {
766 switch (end[1])
767 {
768 case '\\':
769 case ' ':
770 end++;
771 break;
772 case '\0':
773 /* illegal, but just keep it */
774 break;
775 default:
776 /* illegal, but just ignore that there was a '/' */
777 break;
778 }
779 }
780 end++;
781 }
782 old = end[0];
783 end[0] = '\0';
784 if (strlen (pos) > 0)
785 {
786 if (0 == strcmp (pos, match))
787 {
788 memmove (pos, &end[1], strlen (&end[1]) + 1);
789
790 if (pos != list)
791 pos[-1] = ' '; /* previously changed to "\0" */
792 GNUNET_CONFIGURATION_set_value_string (cfg,
793 section, option, list);
794 GNUNET_free (list);
795 GNUNET_free (match);
796 return GNUNET_OK;
797 }
798 }
799 if (old == '\0')
800 break;
801 pos = end + 1;
802 }
803 GNUNET_free (list);
804 GNUNET_free (match);
805 return GNUNET_NO;
806}
807
808
809/**
810 * Load configuration (starts with defaults, then loads
811 * system-specific configuration).
812 */
813int
814GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
815 const char *cfgfn)
816{
817 char *baseconfig;
818 char *ipath;
819
820 ipath = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
821 if (ipath == NULL)
822 return GNUNET_SYSERR;
823 baseconfig = NULL;
824 GNUNET_asprintf (&baseconfig,
825 "%s%s%s", ipath, DIR_SEPARATOR_STR, "defaults.conf");
826 GNUNET_free (ipath);
827 if ((GNUNET_OK !=
828 GNUNET_CONFIGURATION_parse (cfg, baseconfig)) ||
829 (!((cfgfn == NULL) ||
830 (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, cfgfn)))))
831 {
832 GNUNET_free (baseconfig);
833 return GNUNET_SYSERR;
834 }
835 GNUNET_free (baseconfig);
836 if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg,
837 "TESTING",
838 "WEAKRANDOM")) &&
839 (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg,
840 "TESTING",
841 "WEAKRANDOM")))
842 GNUNET_CRYPTO_random_disable_entropy_gathering ();
843 return GNUNET_OK;
844}
845
846
847
848/* end of configuration.c */
diff --git a/src/util/container_bloomfilter.c b/src/util/container_bloomfilter.c
new file mode 100644
index 000000000..8b76ef8dc
--- /dev/null
+++ b/src/util/container_bloomfilter.c
@@ -0,0 +1,677 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006, 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/container_bloomfilter.c
22 * @brief data structure used to reduce disk accesses.
23 *
24 * The idea basically: Create a signature for each element in the
25 * database. Add those signatures to a bit array. When doing a lookup,
26 * check if the bit array matches the signature of the requested
27 * element. If yes, address the disk, otherwise return 'not found'.
28 *
29 * A property of the bloom filter is that sometimes we will have
30 * a match even if the element is not on the disk (then we do
31 * an unnecessary disk access), but what's most important is that
32 * we never get a single "false negative".
33 *
34 * To be able to delete entries from the bloom filter, we maintain
35 * a 4 bit counter in the file on the drive (we still use only one
36 * bit in memory).
37 *
38 * @author Igor Wronsky
39 * @author Christian Grothoff
40 */
41
42#include "platform.h"
43#include "gnunet_common.h"
44#include "gnunet_container_lib.h"
45#include "gnunet_disk_lib.h"
46
47struct GNUNET_CONTAINER_BloomFilter
48{
49
50 /**
51 * The actual bloomfilter bit array
52 */
53 char *bitArray;
54
55 /**
56 * Filename of the filter
57 */
58 char *filename;
59
60 /**
61 * The bit counter file on disk
62 */
63 int fd;
64
65 /**
66 * How many bits we set for each stored element
67 */
68 unsigned int addressesPerElement;
69
70 /**
71 * Size of bitArray in bytes
72 */
73 unsigned int bitArraySize;
74
75};
76
77
78/**
79 * Sets a bit active in the bitArray. Increment bit-specific
80 * usage counter on disk only if below 4bit max (==15).
81 *
82 * @param bitArray memory area to set the bit in
83 * @param bitIdx which bit to set
84 */
85static void
86setBit (char *bitArray, unsigned int bitIdx)
87{
88 unsigned int arraySlot;
89 unsigned int targetBit;
90
91 arraySlot = bitIdx / 8;
92 targetBit = (1L << (bitIdx % 8));
93 bitArray[arraySlot] |= targetBit;
94}
95
96/**
97 * Clears a bit from bitArray. Bit is cleared from the array
98 * only if the respective usage counter on the disk hits/is zero.
99 *
100 * @param bitArray memory area to set the bit in
101 * @param bitIdx which bit to unset
102 */
103static void
104clearBit (char *bitArray, unsigned int bitIdx)
105{
106 unsigned int slot;
107 unsigned int targetBit;
108
109 slot = bitIdx / 8;
110 targetBit = (1L << (bitIdx % 8));
111 bitArray[slot] = bitArray[slot] & (~targetBit);
112}
113
114/**
115 * Checks if a bit is active in the bitArray
116 *
117 * @param bitArray memory area to set the bit in
118 * @param bitIdx which bit to test
119 * @return GNUNET_YES if the bit is set, GNUNET_NO if not.
120 */
121static int
122testBit (char *bitArray, unsigned int bitIdx)
123{
124 unsigned int slot;
125 unsigned int targetBit;
126
127 slot = bitIdx / 8;
128 targetBit = (1L << (bitIdx % 8));
129 if (bitArray[slot] & targetBit)
130 return GNUNET_YES;
131 else
132 return GNUNET_NO;
133}
134
135/**
136 * Sets a bit active in the bitArray and increments
137 * bit-specific usage counter on disk (but only if
138 * the counter was below 4 bit max (==15)).
139 *
140 * @param bitArray memory area to set the bit in
141 * @param bitIdx which bit to test
142 * @param fd A file to keep the 4 bit address usage counters in
143 */
144static void
145incrementBit (char *bitArray, unsigned int bitIdx, int fd)
146{
147 unsigned int fileSlot;
148 unsigned char value;
149 unsigned int high;
150 unsigned int low;
151 unsigned int targetLoc;
152
153 setBit (bitArray, bitIdx);
154 if (fd == -1)
155 return;
156 /* Update the counter file on disk */
157 fileSlot = bitIdx / 2;
158 targetLoc = bitIdx % 2;
159
160 GNUNET_assert (fileSlot == (unsigned int) LSEEK (fd, fileSlot, SEEK_SET));
161 if (1 != READ (fd, &value, 1))
162 value = 0;
163 low = value & 0xF;
164 high = (value & (~0xF)) >> 4;
165
166 if (targetLoc == 0)
167 {
168 if (low < 0xF)
169 low++;
170 }
171 else
172 {
173 if (high < 0xF)
174 high++;
175 }
176 value = ((high << 4) | low);
177 GNUNET_assert (fileSlot == (unsigned int) LSEEK (fd, fileSlot, SEEK_SET));
178 GNUNET_assert (1 == WRITE (fd, &value, 1));
179}
180
181/**
182 * Clears a bit from bitArray if the respective usage
183 * counter on the disk hits/is zero.
184 *
185 * @param bitArray memory area to set the bit in
186 * @param bitIdx which bit to test
187 * @param fd A file to keep the 4bit address usage counters in
188 */
189static void
190decrementBit (char *bitArray, unsigned int bitIdx, int fd)
191{
192 unsigned int fileSlot;
193 unsigned char value;
194 unsigned int high;
195 unsigned int low;
196 unsigned int targetLoc;
197
198 if (fd == -1)
199 return; /* cannot decrement! */
200 /* Each char slot in the counter file holds two 4 bit counters */
201 fileSlot = bitIdx / 2;
202 targetLoc = bitIdx % 2;
203 LSEEK (fd, fileSlot, SEEK_SET);
204 if (1 != READ (fd, &value, 1))
205 value = 0;
206 low = value & 0xF;
207 high = (value & 0xF0) >> 4;
208
209 /* decrement, but once we have reached the max, never go back! */
210 if (targetLoc == 0)
211 {
212 if ((low > 0) && (low < 0xF))
213 low--;
214 if (low == 0)
215 {
216 clearBit (bitArray, bitIdx);
217 }
218 }
219 else
220 {
221 if ((high > 0) && (high < 0xF))
222 high--;
223 if (high == 0)
224 {
225 clearBit (bitArray, bitIdx);
226 }
227 }
228 value = ((high << 4) | low);
229 LSEEK (fd, fileSlot, SEEK_SET);
230 GNUNET_assert (1 == WRITE (fd, &value, 1));
231}
232
233#define BUFFSIZE 65536
234
235/**
236 * Creates a file filled with zeroes
237 *
238 * @param fd the file handle
239 * @param size the size of the file
240 * @return GNUNET_OK if created ok, GNUNET_SYSERR otherwise
241 */
242static int
243makeEmptyFile (int fd, unsigned int size)
244{
245 char *buffer;
246 unsigned int bytesleft = size;
247 int res = 0;
248
249 if (fd == -1)
250 return GNUNET_SYSERR;
251 buffer = GNUNET_malloc (BUFFSIZE);
252 memset (buffer, 0, BUFFSIZE);
253 LSEEK (fd, 0, SEEK_SET);
254
255 while (bytesleft > 0)
256 {
257 if (bytesleft > BUFFSIZE)
258 {
259 res = WRITE (fd, buffer, BUFFSIZE);
260 bytesleft -= BUFFSIZE;
261 }
262 else
263 {
264 res = WRITE (fd, buffer, bytesleft);
265 bytesleft = 0;
266 }
267 GNUNET_assert (res != -1);
268 }
269 GNUNET_free (buffer);
270 return GNUNET_OK;
271}
272
273/* ************** GNUNET_CONTAINER_BloomFilter GNUNET_CRYPTO_hash iterator ********* */
274
275/**
276 * Iterator (callback) method to be called by the
277 * bloomfilter iterator on each bit that is to be
278 * set or tested for the key.
279 *
280 * @param bf the filter to manipulate
281 * @param bit the current bit
282 * @param additional context specific argument
283 */
284typedef void (*BitIterator) (struct GNUNET_CONTAINER_BloomFilter * bf,
285 unsigned int bit, void *arg);
286
287/**
288 * Call an iterator for each bit that the bloomfilter
289 * must test or set for this element.
290 *
291 * @param bf the filter
292 * @param callback the method to call
293 * @param arg extra argument to callback
294 * @param key the key for which we iterate over the BF bits
295 */
296static void
297iterateBits (struct GNUNET_CONTAINER_BloomFilter *bf,
298 BitIterator callback, void *arg, const GNUNET_HashCode * key)
299{
300 GNUNET_HashCode tmp[2];
301 int bitCount;
302 int round;
303 unsigned int slot = 0;
304
305 bitCount = bf->addressesPerElement;
306 memcpy (&tmp[0], key, sizeof (GNUNET_HashCode));
307 round = 0;
308 while (bitCount > 0)
309 {
310 while (slot < (sizeof (GNUNET_HashCode) / sizeof (unsigned int)))
311 {
312 callback (bf,
313 (((unsigned int *) &tmp[round & 1])[slot]) &
314 ((bf->bitArraySize * 8) - 1), arg);
315 slot++;
316 bitCount--;
317 if (bitCount == 0)
318 break;
319 }
320 if (bitCount > 0)
321 {
322 GNUNET_CRYPTO_hash (&tmp[round & 1], sizeof (GNUNET_HashCode),
323 &tmp[(round + 1) & 1]);
324 round++;
325 slot = 0;
326 }
327 }
328}
329
330/**
331 * Callback: increment bit
332 *
333 * @param bf the filter to manipulate
334 * @param bit the bit to increment
335 * @param arg not used
336 */
337static void
338incrementBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf,
339 unsigned int bit, void *arg)
340{
341 incrementBit (bf->bitArray, bit, bf->fd);
342}
343
344/**
345 * Callback: decrement bit
346 *
347 * @param bf the filter to manipulate
348 * @param bit the bit to decrement
349 * @param arg not used
350 */
351static void
352decrementBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf,
353 unsigned int bit, void *arg)
354{
355 decrementBit (bf->bitArray, bit, bf->fd);
356}
357
358/**
359 * Callback: test if all bits are set
360 *
361 * @param bf the filter
362 * @param bit the bit to test
363 * @param arg pointer set to GNUNET_NO if bit is not set
364 */
365static void
366testBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf, unsigned int bit,
367 void *cls)
368{
369 int *arg = cls;
370 if (GNUNET_NO == testBit (bf->bitArray, bit))
371 *arg = GNUNET_NO;
372}
373
374/* *********************** INTERFACE **************** */
375
376/**
377 * Load a bloom-filter from a file.
378 *
379 * @param filename the name of the file (or the prefix)
380 * @param size the size of the bloom-filter (number of
381 * bytes of storage space to use)
382 * @param k the number of GNUNET_CRYPTO_hash-functions to apply per
383 * element (number of bits set per element in the set)
384 * @return the bloomfilter
385 */
386struct GNUNET_CONTAINER_BloomFilter *
387GNUNET_CONTAINER_bloomfilter_load (const char *filename, unsigned int size,
388 unsigned int k)
389{
390 struct GNUNET_CONTAINER_BloomFilter *bf;
391 char *rbuff;
392 unsigned int pos;
393 int i;
394 unsigned int ui;
395
396 if ((k == 0) || (size == 0))
397 return NULL;
398 if (size < BUFFSIZE)
399 size = BUFFSIZE;
400 ui = 1;
401 while (ui < size)
402 ui *= 2;
403 size = ui; /* make sure it's a power of 2 */
404
405 bf = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_BloomFilter));
406 /* Try to open a bloomfilter file */
407 if (filename != NULL)
408 {
409#ifndef _MSC_VER
410 bf->fd = GNUNET_DISK_file_open (filename, O_RDWR | O_CREAT,
411 S_IRUSR | S_IWUSR);
412#else
413 bf->fd = GNUNET_DISK_file_open (filename,
414 O_WRONLY | O_CREAT, S_IREAD | S_IWRITE);
415#endif
416 if (-1 == bf->fd)
417 {
418 GNUNET_free (bf);
419 return NULL;
420 }
421 bf->filename = GNUNET_strdup (filename);
422 }
423 else
424 {
425 bf->fd = -1;
426 bf->filename = NULL;
427 }
428 /* Alloc block */
429 bf->bitArray = GNUNET_malloc_large (size);
430 bf->bitArraySize = size;
431 bf->addressesPerElement = k;
432 memset (bf->bitArray, 0, bf->bitArraySize);
433
434 if (bf->fd != -1)
435 {
436 /* Read from the file what bits we can */
437 rbuff = GNUNET_malloc (BUFFSIZE);
438 pos = 0;
439 while (pos < size * 8)
440 {
441 int res;
442
443 res = READ (bf->fd, rbuff, BUFFSIZE);
444 if (res == 0)
445 break; /* is ok! we just did not use that many bits yet */
446 for (i = 0; i < res; i++)
447 {
448 if ((rbuff[i] & 0x0F) != 0)
449 setBit (bf->bitArray, pos + i * 2);
450 if ((rbuff[i] & 0xF0) != 0)
451 setBit (bf->bitArray, pos + i * 2 + 1);
452 }
453 if (res < BUFFSIZE)
454 break;
455 pos += BUFFSIZE * 2; /* 2 bits per byte in the buffer */
456 }
457 GNUNET_free (rbuff);
458 }
459 return bf;
460}
461
462
463/**
464 * Create a bloom filter from raw bits.
465 *
466 * @param data the raw bits in memory (maybe NULL,
467 * in which case all bits should be considered
468 * to be zero).
469 * @param size the size of the bloom-filter (number of
470 * bytes of storage space to use); also size of data
471 * -- unless data is NULL
472 * @param k the number of GNUNET_CRYPTO_hash-functions to apply per
473 * element (number of bits set per element in the set)
474 * @return the bloomfilter
475 */
476struct GNUNET_CONTAINER_BloomFilter *
477GNUNET_CONTAINER_bloomfilter_init (const char *data, unsigned int size,
478 unsigned int k)
479{
480 struct GNUNET_CONTAINER_BloomFilter *bf;
481 unsigned int ui;
482
483 if ((k == 0) || (size == 0))
484 return NULL;
485 ui = 1;
486 while (ui < size)
487 ui *= 2;
488 if (size != ui)
489 {
490 GNUNET_break (0);
491 return NULL;
492 }
493 bf = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_BloomFilter));
494 bf->fd = -1;
495 bf->filename = NULL;
496 bf->bitArray = GNUNET_malloc_large (size);
497 bf->bitArraySize = size;
498 bf->addressesPerElement = k;
499 if (data != NULL)
500 memcpy (bf->bitArray, data, size);
501 else
502 memset (bf->bitArray, 0, bf->bitArraySize);
503 return bf;
504}
505
506
507/**
508 * Copy the raw data of this bloomfilter into
509 * the given data array.
510 *
511 * @param data where to write the data
512 * @param size the size of the given data array
513 * @return GNUNET_SYSERR if the data array is not big enough
514 */
515int
516GNUNET_CONTAINER_bloomfilter_get_raw_data (struct GNUNET_CONTAINER_BloomFilter
517 *bf, char *data, unsigned int size)
518{
519 if (NULL == bf)
520 return GNUNET_SYSERR;
521
522 if (bf->bitArraySize != size)
523 return GNUNET_SYSERR;
524 memcpy (data, bf->bitArray, size);
525 return GNUNET_OK;
526}
527
528/**
529 * Free the space associated with a filter
530 * in memory, flush to drive if needed (do not
531 * free the space on the drive)
532 *
533 * @param bf the filter
534 */
535void
536GNUNET_CONTAINER_bloomfilter_free (struct GNUNET_CONTAINER_BloomFilter *bf)
537{
538 if (NULL == bf)
539 return;
540 if (bf->fd != -1)
541 GNUNET_DISK_file_close (bf->filename, bf->fd);
542 GNUNET_free_non_null (bf->filename);
543 GNUNET_free (bf->bitArray);
544 GNUNET_free (bf);
545}
546
547/**
548 * Reset a bloom filter to empty. Clears the file on disk.
549 *
550 * @param bf the filter
551 */
552void
553GNUNET_CONTAINER_bloomfilter_clear (struct GNUNET_CONTAINER_BloomFilter *bf)
554{
555 if (NULL == bf)
556 return;
557
558 memset (bf->bitArray, 0, bf->bitArraySize);
559 if (bf->fd != -1)
560 makeEmptyFile (bf->fd, bf->bitArraySize * 4);
561}
562
563
564/**
565 * Test if an element is in the filter.
566 *
567 * @param e the element
568 * @param bf the filter
569 * @return GNUNET_YES if the element is in the filter, GNUNET_NO if not
570 */
571int
572GNUNET_CONTAINER_bloomfilter_test (struct GNUNET_CONTAINER_BloomFilter *bf,
573 const GNUNET_HashCode * e)
574{
575 int res;
576
577 if (NULL == bf)
578 return GNUNET_YES;
579 res = GNUNET_YES;
580 iterateBits (bf, &testBitCallback, &res, e);
581 return res;
582}
583
584/**
585 * Add an element to the filter
586 *
587 * @param bf the filter
588 * @param e the element
589 */
590void
591GNUNET_CONTAINER_bloomfilter_add (struct GNUNET_CONTAINER_BloomFilter *bf,
592 const GNUNET_HashCode * e)
593{
594
595 if (NULL == bf)
596 return;
597 iterateBits (bf, &incrementBitCallback, NULL, e);
598}
599
600
601/**
602 * Or the entries of the given raw data array with the
603 * data of the given bloom filter. Assumes that
604 * the size of the data array and the current filter
605 * match.
606 * @param bf the filter
607 */
608int
609GNUNET_CONTAINER_bloomfilter_or (struct GNUNET_CONTAINER_BloomFilter *bf,
610 const char *data, unsigned int size)
611{
612 unsigned int i;
613
614 if (NULL == bf)
615 return GNUNET_YES;
616 if (bf->bitArraySize != size)
617 return GNUNET_SYSERR;
618 /* FIXME: we could do this 4-8x faster by
619 going over int/long arrays */
620 for (i = 0; i < size; i++)
621 bf->bitArray[i] |= data[i];
622 return GNUNET_OK;
623}
624
625/**
626 * Remove an element from the filter.
627 *
628 * @param bf the filter
629 * @param e the element to remove
630 */
631void
632GNUNET_CONTAINER_bloomfilter_remove (struct GNUNET_CONTAINER_BloomFilter *bf,
633 const GNUNET_HashCode * e)
634{
635 if (NULL == bf)
636 return;
637 if (bf->fd == -1)
638 return;
639 iterateBits (bf, &decrementBitCallback, NULL, e);
640}
641
642/**
643 * Resize a bloom filter. Note that this operation
644 * is pretty costly. Essentially, the bloom filter
645 * needs to be completely re-build.
646 *
647 * @param bf the filter
648 * @param iterator an iterator over all elements stored in the BF
649 * @param iterator_arg argument to the iterator function
650 * @param size the new size for the filter
651 * @param k the new number of GNUNET_CRYPTO_hash-function to apply per element
652 */
653void
654GNUNET_CONTAINER_bloomfilter_resize (struct GNUNET_CONTAINER_BloomFilter *bf,
655 GNUNET_HashCodeIterator iterator,
656 void *iterator_arg, unsigned int size,
657 unsigned int k)
658{
659 GNUNET_HashCode hc;
660 unsigned int i;
661
662 GNUNET_free (bf->bitArray);
663 i = 1;
664 while (i < size)
665 i *= 2;
666 size = i; /* make sure it's a power of 2 */
667
668 bf->bitArraySize = size;
669 bf->bitArray = GNUNET_malloc (size);
670 memset (bf->bitArray, 0, bf->bitArraySize);
671 if (bf->fd != -1)
672 makeEmptyFile (bf->fd, bf->bitArraySize * 4);
673 while (GNUNET_YES == iterator (&hc, iterator_arg))
674 GNUNET_CONTAINER_bloomfilter_add (bf, &hc);
675}
676
677/* end of container_bloomfilter.c */
diff --git a/src/util/container_heap.c b/src/util/container_heap.c
new file mode 100644
index 000000000..1e7077c80
--- /dev/null
+++ b/src/util/container_heap.c
@@ -0,0 +1,533 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @author Nathan Evans
23 * @file util/container_heap.c
24 * @brief Implementation of heap operations
25 */
26
27#include "platform.h"
28#include "gnunet_protocols.h"
29#include "gnunet_util.h"
30#include "gnunet_util_containers.h"
31
32/*
33 * Struct that is stored in hashmap, pointers to
34 * locations in min_heap and max_heap.
35 */
36struct GNUNET_CONTAINER_heap_info
37{
38 struct GNUNET_CONTAINER_heap_node *min_loc;
39
40 struct GNUNET_CONTAINER_heap_node *max_loc;
41
42};
43
44/*
45 * Generic heap node structure, contains pointer to parent
46 * left child, right child, and actual neighbor.
47 */
48struct GNUNET_CONTAINER_heap_node
49{
50 struct GNUNET_CONTAINER_heap_node *parent;
51
52 struct GNUNET_CONTAINER_heap_node *left_child;
53
54 struct GNUNET_CONTAINER_heap_node *right_child;
55
56 GNUNET_CONTAINER_HeapCost cost;
57
58 void *element;
59
60};
61
62struct GNUNET_CONTAINER_Heap
63{
64 unsigned int size;
65
66 unsigned int max_size;
67
68 enum type;
69
70 struct GNUNET_CONTAINER_heap_node *root;
71
72 struct GNUNET_CONTAINER_heap_node *traversal_pos;
73
74};
75
76void
77internal_print (struct GNUNET_CONTAINER_heap_node *root)
78{
79 fprintf (stdout, "%d\n", root->cost);
80 if (root->left_child != NULL)
81 {
82 fprintf (stdout, "LEFT of %d\n", root->cost);
83 internal_print (root->left_child);
84 }
85 if (root->right_child != NULL)
86 {
87 fprintf (stdout, "RIGHT of %d\n", root->cost);
88 internal_print (root->right_child);
89 }
90}
91
92void
93printTree (struct GNUNET_CONTAINER_Heap *root)
94{
95 internal_print (root->root);
96}
97
98struct GNUNET_CONTAINER_Heap *
99GNUNET_CONTAINER_heap_create (enum type)
100{
101 struct GNUNET_CONTAINER_Heap *heap;
102 heap = malloc (sizeof (struct GNUNET_CONTAINER_Heap));
103 heap->max_size = -1;
104 heap->type = type;
105 heap->root = NULL;
106 heap->traversal_pos = NULL;
107 heap->size = 0;
108
109 return heap;
110}
111
112void
113GNUNET_CONTAINER_heap_destroy (struct GNUNET_CONTAINER_Heap *heap)
114{
115 void *unused;
116 while (heap->size > 0)
117 {
118 unused = GNUNET_CONTAINER_heap_remove_root (heap);
119 }
120
121 GNUNET_free (heap);
122 return;
123}
124
125struct GNUNET_CONTAINER_heap_node *
126find_element (struct GNUNET_CONTAINER_heap_node *node, void *element)
127{
128 struct GNUNET_CONTAINER_heap_node *ret;
129 ret = NULL;
130 if ((node != NULL) && (node->element == element))
131 {
132 ret = node;
133 }
134
135 if ((ret == NULL) && (node->left_child != NULL))
136 {
137 ret = find_element (node->left_child, element);
138 }
139
140 if ((ret == NULL) && (node->right_child != NULL))
141 {
142 ret = find_element (node->right_child, element);
143 }
144
145 return ret;
146}
147
148static struct GNUNET_CONTAINER_heap_node *
149getNextPos (struct GNUNET_CONTAINER_Heap *root)
150{
151 struct GNUNET_CONTAINER_heap_node *ret;
152 struct GNUNET_CONTAINER_heap_node *parent;
153 int pos;
154 int depth;
155 int i;
156
157 ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_heap_node));
158 pos = root->size + 1;
159 depth = (int) log2 (pos);
160 ret->left_child = NULL;
161 ret->right_child = NULL;
162
163 if (depth == 0)
164 {
165 ret->parent = NULL;
166 root->root = ret;
167 }
168 else
169 {
170 parent = root->root;
171 for (i = depth; i > 1; i--)
172 {
173 if (((pos / (1 << (i - 1))) % 2) == 0)
174 parent = parent->left_child;
175 else
176 parent = parent->right_child;
177 }
178
179 ret->parent = parent;
180 if ((pos % 2) == 0)
181 parent->left_child = ret;
182 else
183 parent->right_child = ret;
184
185 }
186
187 return ret;
188
189}
190
191static struct GNUNET_CONTAINER_heap_node *
192getPos (struct GNUNET_CONTAINER_Heap *root, unsigned int pos)
193{
194 struct GNUNET_CONTAINER_heap_node *ret;
195
196 int depth;
197 int i;
198
199 depth = (int) log2 (pos);
200 ret = NULL;
201 if (pos > root->size)
202 {
203 return ret;
204 }
205 else
206 {
207 ret = root->root;
208 for (i = depth; i > 0; i--)
209 {
210 if (((pos / (1 << (i - 1))) % 2) == 0)
211 ret = ret->left_child;
212 else
213 ret = ret->right_child;
214 }
215 }
216
217 return ret;
218
219}
220
221void
222swapNodes (struct GNUNET_CONTAINER_heap_node *first,
223 struct GNUNET_CONTAINER_heap_node *second,
224 struct GNUNET_CONTAINER_Heap *root)
225{
226 void *temp_element;
227 GNUNET_CONTAINER_HeapCost temp_cost;
228
229 temp_element = first->element;
230 temp_cost = first->cost;
231 first->element = second->element;
232 first->cost = second->cost;
233 second->element = temp_element;
234 second->cost = temp_cost;
235
236/*
237 * I still worry that there is some good reason for
238 * elements being location aware... but it eludes me
239 * for the moment...
240 if ((root->type == GNUNET_DV_MAX_HEAP))
241 {
242 first->neighbor->max_loc = first;
243 second->neighbor->max_loc = second;
244 }
245 else if ((root->type == GNUNET_DV_MIN_HEAP))
246 {
247 first->neighbor->min_loc = first;
248 second->neighbor->min_loc = second;
249 }
250*/
251 return;
252}
253
254void
255percolateHeap (struct GNUNET_CONTAINER_heap_node *pos,
256 struct GNUNET_CONTAINER_Heap *root)
257{
258
259 while ((pos->parent != NULL) &&
260 (((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
261 && (pos->parent->cost < pos->cost))
262 || ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
263 && (pos->parent->cost > pos->cost))))
264 {
265 swapNodes (pos, pos->parent, root);
266 pos = pos->parent;
267 }
268
269 return;
270}
271
272
273
274void
275percolateDownHeap (struct GNUNET_CONTAINER_heap_node *pos,
276 struct GNUNET_CONTAINER_Heap *root)
277{
278 struct GNUNET_CONTAINER_heap_node *switchNeighbor;
279
280 switchNeighbor = pos;
281
282 if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX))
283 {
284 if ((pos->left_child != NULL)
285 && (pos->left_child->cost > switchNeighbor->cost))
286 {
287 switchNeighbor = pos->left_child;
288 }
289
290 if ((pos->right_child != NULL)
291 && (pos->right_child->cost > switchNeighbor->cost))
292 {
293 switchNeighbor = pos->right_child;
294 }
295 }
296 else if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN))
297 {
298 if ((pos->left_child != NULL)
299 && (pos->left_child->cost < switchNeighbor->cost))
300 {
301 switchNeighbor = pos->left_child;
302 }
303
304 if ((pos->right_child != NULL)
305 && (pos->right_child->cost < switchNeighbor->cost))
306 {
307 switchNeighbor = pos->right_child;
308 }
309 }
310
311 if (switchNeighbor != pos)
312 {
313 swapNodes (switchNeighbor, pos, root);
314 percolateDownHeap (switchNeighbor, root);
315 }
316
317 return;
318}
319
320void *
321GNUNET_CONTAINER_heap_remove_node (struct GNUNET_CONTAINER_Heap *root,
322 void *element)
323{
324 void *ret;
325 struct GNUNET_CONTAINER_heap_node *del_node;
326 struct GNUNET_CONTAINER_heap_node *last;
327 GNUNET_CONTAINER_HeapCost old_cost;
328
329 del_node = NULL;
330 del_node = find_element (root->root, element);
331
332 if (del_node == NULL)
333 return NULL;
334
335 ret = del_node->element;
336 last = getPos (root, root->size);
337
338 old_cost = del_node->cost;
339 del_node->element = last->element;
340 del_node->cost = last->cost;
341
342 if (last->parent->left_child == last)
343 last->parent->left_child = NULL;
344 if (last->parent->right_child == last)
345 last->parent->right_child = NULL;
346
347 if (root->traversal_pos == last)
348 {
349 root->traversal_pos = root->root;
350 }
351 GNUNET_free (last);
352 root->size--;
353
354 if (del_node->cost > old_cost)
355 {
356 if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
357 percolateHeap (del_node, root);
358 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
359 percolateDownHeap (del_node, root);
360 }
361 else if (del_node->cost < old_cost)
362 {
363 if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
364 percolateDownHeap (del_node, root);
365 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
366 percolateHeap (del_node, root);
367 }
368
369 return ret;
370}
371
372int
373GNUNET_CONTAINER_heap_insert (struct GNUNET_CONTAINER_Heap *root,
374 void *element, GNUNET_CONTAINER_HeapCost cost)
375{
376 struct GNUNET_CONTAINER_heap_node *new_pos;
377 int ret;
378 ret = GNUNET_YES;
379
380 if (root->max_size > root->size)
381 {
382 new_pos = getNextPos (root);
383 new_pos->element = element;
384 new_pos->cost = cost;
385 root->size++;
386 /*We no longer can tolerate pointers between heaps :( */
387 /*if (root->type == GNUNET_DV_MIN_HEAP)
388 new_pos->neighbor->min_loc = new_pos;
389 else if (root->type == GNUNET_DV_MAX_HEAP)
390 new_pos->neighbor->max_loc = new_pos; */
391
392 percolateHeap (new_pos, root);
393 }
394 else
395 {
396 ret = GNUNET_NO;
397 }
398
399 return ret;
400}
401
402void *
403GNUNET_CONTAINER_heap_remove_root (struct GNUNET_CONTAINER_Heap *root)
404{
405 void *ret;
406 struct GNUNET_CONTAINER_heap_node *root_node;
407 struct GNUNET_CONTAINER_heap_node *last;
408
409 root_node = root->root;
410 ret = root_node->element;
411 last = getPos (root, root->size);
412
413 if (last->parent->left_child == last)
414 last->parent->left_child = NULL;
415 else if (last->parent->right_child == last)
416 last->parent->right_child = NULL;
417
418 root_node->element = last->element;
419 root_node->cost = last->cost;
420
421 if (root->traversal_pos == last)
422 {
423 root->traversal_pos = root->root;
424 }
425
426 GNUNET_free (last);
427 root->size--;
428 percolateDownHeap (root->root, root);
429 return ret;
430}
431
432static int
433updatedCost (struct GNUNET_CONTAINER_Heap *root,
434 struct GNUNET_CONTAINER_heap_node *node)
435{
436 struct GNUNET_CONTAINER_heap_node *parent;
437
438 if (node == NULL)
439 return GNUNET_SYSERR;
440
441 parent = node->parent;
442
443 if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX) && (parent != NULL)
444 && (node->cost > parent->cost))
445 percolateHeap (node, root);
446 else if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN) && (parent != NULL)
447 && (node->cost < parent->cost))
448 percolateHeap (node, root);
449 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)
450 percolateDownHeap (node, root);
451 else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)
452 percolateDownHeap (node, root);
453
454 return GNUNET_YES;
455}
456
457
458int
459GNUNET_CONTAINER_heap_update_cost (struct GNUNET_CONTAINER_Heap *root,
460 void *element,
461 GNUNET_CONTAINER_HeapCost new_cost)
462{
463 struct GNUNET_CONTAINER_heap_node *node;
464 int ret = GNUNET_YES;
465 node = find_element (root->root, element);
466 if (node == NULL)
467 return GNUNET_NO;
468
469 node->cost = new_cost;
470 ret = updatedCost (root, node);
471 return ret;
472}
473
474void
475internal_iterator (struct GNUNET_CONTAINER_Heap *root,
476 struct GNUNET_CONTAINER_heap_node *node,
477 GNUNET_CONTAINER_HeapIterator iterator, void *cls)
478{
479 if (node == NULL)
480 return;
481 internal_iterator (root, node->left_child, iterator, cls);
482 internal_iterator (root, node->right_child, iterator, cls);
483 iterator (node->element, node->cost, root, cls);
484}
485
486int
487GNUNET_CONTAINER_heap_iterate (struct GNUNET_CONTAINER_Heap *heap,
488 GNUNET_CONTAINER_HeapIterator iterator,
489 void *cls)
490{
491 internal_iterator (heap, heap->root, iterator, cls);
492 return GNUNET_OK;
493}
494
495void *
496GNUNET_CONTAINER_heap_walk_get_next (struct GNUNET_CONTAINER_Heap *root)
497{
498 unsigned int choice;
499 void *element;
500
501 if ((root->traversal_pos == NULL) && (root->root != NULL))
502 {
503 root->traversal_pos = root->root;
504 }
505
506 if (root->traversal_pos == NULL)
507 return NULL;
508
509 element = root->traversal_pos->element;
510
511 choice = GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2);
512
513 switch (choice)
514 {
515 case 1:
516 root->traversal_pos = root->traversal_pos->right_child;
517 break;
518 case 0:
519 root->traversal_pos = root->traversal_pos->left_child;
520 break;
521 }
522
523 return element;
524
525}
526
527unsigned int
528GNUNET_CONTAINER_heap_get_size (struct GNUNET_CONTAINER_Heap *heap)
529{
530 return heap->size;
531}
532
533/* end of heap.c */
diff --git a/src/util/container_meta_data.c b/src/util/container_meta_data.c
new file mode 100644
index 000000000..b79de57d2
--- /dev/null
+++ b/src/util/container_meta_data.c
@@ -0,0 +1,721 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/container_meta_data.c
23 * @brief Storing of meta data
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30#include "gnunet_strings_lib.h"
31#include "gnunet_time_lib.h"
32#include <extractor.h>
33#include <zlib.h>
34
35#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS
36
37struct Item
38{
39 EXTRACTOR_KeywordType type;
40 char *data;
41};
42
43/**
44 * Meta data to associate with a file, directory or namespace.
45 */
46struct GNUNET_CONTAINER_MetaData
47{
48 uint32_t itemCount;
49 struct Item *items;
50};
51
52/**
53 * Create a fresh struct CONTAINER_MetaData token.
54 */
55struct GNUNET_CONTAINER_MetaData *
56GNUNET_CONTAINER_meta_data_create ()
57{
58 struct GNUNET_CONTAINER_MetaData *ret;
59 ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MetaData));
60 ret->items = NULL;
61 ret->itemCount = 0;
62 return ret;
63}
64
65/**
66 * Free meta data.
67 */
68void
69GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
70{
71 int i;
72
73 if (md == NULL)
74 return;
75 for (i = 0; i < md->itemCount; i++)
76 GNUNET_free (md->items[i].data);
77 GNUNET_array_grow (md->items, md->itemCount, 0);
78 GNUNET_free (md);
79}
80
81/**
82 * Add the current time as the publication date
83 * to the meta-data.
84 */
85void
86GNUNET_CONTAINER_meta_data_add_publication_date (struct
87 GNUNET_CONTAINER_MetaData
88 *md)
89{
90 char *dat;
91 struct GNUNET_TIME_Absolute t;
92
93 t = GNUNET_TIME_absolute_get ();
94 GNUNET_CONTAINER_meta_data_delete (md, EXTRACTOR_PUBLICATION_DATE, NULL);
95 dat = GNUNET_STRINGS_absolute_time_to_string (t);
96 GNUNET_CONTAINER_meta_data_insert (md, EXTRACTOR_PUBLICATION_DATE, dat);
97 GNUNET_free (dat);
98}
99
100/**
101 * Extend metadata.
102 * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists
103 */
104int
105GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
106 EXTRACTOR_KeywordType type,
107 const char *data)
108{
109 uint32_t idx;
110 char *p;
111
112 GNUNET_assert (data != NULL);
113 for (idx = 0; idx < md->itemCount; idx++)
114 {
115 if ((md->items[idx].type == type) &&
116 (0 == strcmp (md->items[idx].data, data)))
117 return GNUNET_SYSERR;
118 }
119 idx = md->itemCount;
120 GNUNET_array_grow (md->items, md->itemCount, md->itemCount + 1);
121 md->items[idx].type = type;
122 md->items[idx].data = p = GNUNET_strdup (data);
123
124 /* change OS native dir separators to unix '/' and others to '_' */
125 if (type == EXTRACTOR_FILENAME)
126 {
127 while (*p != '\0')
128 {
129 if (*p == DIR_SEPARATOR)
130 *p = '/';
131 else if (*p == '\\')
132 *p = '_';
133 p++;
134 }
135 }
136
137 return GNUNET_OK;
138}
139
140/**
141 * Remove an item.
142 * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md
143 */
144int
145GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
146 EXTRACTOR_KeywordType type,
147 const char *data)
148{
149 uint32_t idx;
150 int ret = GNUNET_SYSERR;
151 for (idx = 0; idx < md->itemCount; idx++)
152 {
153 if ((md->items[idx].type == type) &&
154 ((data == NULL) || (0 == strcmp (md->items[idx].data, data))))
155 {
156 GNUNET_free (md->items[idx].data);
157 md->items[idx] = md->items[md->itemCount - 1];
158 GNUNET_array_grow (md->items, md->itemCount, md->itemCount - 1);
159 if (data == NULL)
160 {
161 ret = GNUNET_OK;
162 continue;
163 }
164 return GNUNET_OK;
165 }
166 }
167 return ret;
168}
169
170/**
171 * Iterate over MD entries, excluding thumbnails.
172 *
173 * @return number of entries
174 */
175int
176GNUNET_CONTAINER_meta_data_get_contents (const struct
177 GNUNET_CONTAINER_MetaData *md,
178 GNUNET_CONTAINER_MetaDataProcessor
179 iterator, void *closure)
180{
181 uint32_t i;
182 uint32_t sub;
183
184 sub = 0;
185 for (i = 0; i < md->itemCount; i++)
186 {
187 if (!EXTRACTOR_isBinaryType (md->items[i].type))
188 {
189 if ((iterator != NULL) &&
190 (GNUNET_OK != iterator (md->items[i].type,
191 md->items[i].data, closure)))
192 return GNUNET_SYSERR;
193 }
194 else
195 sub++;
196 }
197 return (int) (md->itemCount - sub);
198}
199
200/**
201 * Iterate over MD entries
202 *
203 * @return number of entries
204 */
205char *
206GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
207 *md, EXTRACTOR_KeywordType type)
208{
209 uint32_t i;
210
211 for (i = 0; i < md->itemCount; i++)
212 if (type == md->items[i].type)
213 return GNUNET_strdup (md->items[i].data);
214 return NULL;
215}
216
217/**
218 * Iterate over MD entries
219 *
220 * @return number of entries
221 */
222char *
223GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
224 GNUNET_CONTAINER_MetaData *md,
225 ...)
226{
227 char *ret;
228 va_list args;
229 EXTRACTOR_KeywordType type;
230
231 ret = NULL;
232 va_start (args, md);
233 while (1)
234 {
235 type = va_arg (args, EXTRACTOR_KeywordType);
236 if (type == -1)
237 break;
238 ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type);
239 if (ret != NULL)
240 break;
241 }
242 va_end (args);
243 return ret;
244}
245
246/**
247 * Get a thumbnail from the meta-data (if present).
248 *
249 * @param thumb will be set to the thumbnail data. Must be
250 * freed by the caller!
251 * @return number of bytes in thumbnail, 0 if not available
252 */
253size_t
254GNUNET_CONTAINER_meta_data_get_thumbnail (const struct
255 GNUNET_CONTAINER_MetaData * md,
256 unsigned char **thumb)
257{
258 char *encoded;
259 int ret;
260 size_t size;
261
262 encoded =
263 GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_THUMBNAIL_DATA);
264 if (encoded == NULL)
265 return 0;
266 if (strlen (encoded) == 0)
267 {
268 GNUNET_free (encoded);
269 return 0; /* invalid */
270 }
271 *thumb = NULL;
272 ret = EXTRACTOR_binaryDecode (encoded, thumb, &size);
273 GNUNET_free (encoded);
274 if (ret != 0)
275 return 0;
276 return size;
277}
278
279/**
280 * Duplicate struct GNUNET_CONTAINER_MetaData.
281 */
282struct GNUNET_CONTAINER_MetaData *
283GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
284 *md)
285{
286 uint32_t i;
287 struct GNUNET_CONTAINER_MetaData *ret;
288
289 if (md == NULL)
290 return NULL;
291 ret = GNUNET_CONTAINER_meta_data_create ();
292 for (i = 0; i < md->itemCount; i++)
293 GNUNET_CONTAINER_meta_data_insert (ret, md->items[i].type,
294 md->items[i].data);
295 return ret;
296}
297
298/**
299 * Extract meta-data from a file.
300 *
301 * @return GNUNET_SYSERR on error, otherwise the number
302 * of meta-data items obtained
303 */
304int
305GNUNET_CONTAINER_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData
306 *md, const char *filename,
307 EXTRACTOR_ExtractorList *
308 extractors)
309{
310 EXTRACTOR_KeywordList *head;
311 EXTRACTOR_KeywordList *pos;
312 int ret;
313
314 if (filename == NULL)
315 return GNUNET_SYSERR;
316 if (extractors == NULL)
317 return 0;
318 head = EXTRACTOR_getKeywords (extractors, filename);
319 head = EXTRACTOR_removeDuplicateKeywords (head,
320 EXTRACTOR_DUPLICATES_REMOVE_UNKNOWN);
321 pos = head;
322 ret = 0;
323 while (pos != NULL)
324 {
325 if (GNUNET_OK ==
326 GNUNET_CONTAINER_meta_data_insert (md, pos->keywordType,
327 pos->keyword))
328 ret++;
329 pos = pos->next;
330 }
331 EXTRACTOR_freeKeywords (head);
332 return ret;
333}
334
335static unsigned int
336tryCompression (char *data, unsigned int oldSize)
337{
338 char *tmp;
339 uLongf dlen;
340
341#ifdef compressBound
342 dlen = compressBound (oldSize);
343#else
344 dlen = oldSize + (oldSize / 100) + 20;
345 /* documentation says 100.1% oldSize + 12 bytes, but we
346 should be able to overshoot by more to be safe */
347#endif
348 tmp = GNUNET_malloc (dlen);
349 if (Z_OK == compress2 ((Bytef *) tmp,
350 &dlen, (const Bytef *) data, oldSize, 9))
351 {
352 if (dlen < oldSize)
353 {
354 memcpy (data, tmp, dlen);
355 GNUNET_free (tmp);
356 return dlen;
357 }
358 }
359 GNUNET_free (tmp);
360 return oldSize;
361}
362
363/**
364 * Decompress input, return the decompressed data
365 * as output, set outputSize to the number of bytes
366 * that were found.
367 *
368 * @return NULL on error
369 */
370static char *
371decompress (const char *input,
372 unsigned int inputSize, unsigned int outputSize)
373{
374 char *output;
375 uLongf olen;
376
377 olen = outputSize;
378 output = GNUNET_malloc (olen);
379 if (Z_OK == uncompress ((Bytef *) output,
380 &olen, (const Bytef *) input, inputSize))
381 {
382 return output;
383 }
384 else
385 {
386 GNUNET_free (output);
387 return NULL;
388 }
389}
390
391/**
392 * Flag in 'version' that indicates compressed meta-data.
393 */
394#define HEADER_COMPRESSED 0x80000000
395
396/**
397 * Bits in 'version' that give the version number.
398 */
399#define HEADER_VERSION_MASK 0x7FFFFFFF
400
401struct MetaDataHeader
402{
403 /**
404 * The version of the MD serialization.
405 * The highest bit is used to indicate
406 * compression.
407 *
408 * Version 0 is the current version;
409 * Version is 1 for a NULL pointer.
410 * Other version numbers are not yet defined.
411 */
412 uint32_t version;
413
414 /**
415 * How many MD entries are there?
416 */
417 uint32_t entries;
418
419 /**
420 * Size of the MD (decompressed)
421 */
422 uint32_t size;
423
424 /**
425 * This is followed by 'entries' values of type 'unsigned int' that
426 * correspond to EXTRACTOR_KeywordTypes. After that, the meta-data
427 * keywords follow (0-terminated). The MD block always ends with
428 * 0-termination, padding with 0 until a multiple of 8 bytes.
429 */
430
431};
432
433/**
434 * Serialize meta-data to target.
435 *
436 * @param size maximum number of bytes available
437 * @param part is it ok to just write SOME of the
438 * meta-data to match the size constraint,
439 * possibly discarding some data?
440 * @return number of bytes written on success,
441 * GNUNET_SYSERR on error (typically: not enough
442 * space)
443 */
444int
445GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
446 *md, char *target, unsigned int max,
447 enum
448 GNUNET_CONTAINER_MetaDataSerializationOptions
449 part)
450{
451 struct MetaDataHeader *hdr;
452 size_t size;
453 size_t pos;
454 uint32_t i;
455 size_t len;
456 uint32_t ic;
457
458 if (max < sizeof (struct MetaDataHeader))
459 return GNUNET_SYSERR; /* far too small */
460 ic = md ? md->itemCount : 0;
461 hdr = NULL;
462 while (1)
463 {
464 size = sizeof (struct MetaDataHeader);
465 size += sizeof (unsigned int) * ic;
466 for (i = 0; i < ic; i++)
467 size += 1 + strlen (md->items[i].data);
468 while (size % 8 != 0)
469 size++;
470 hdr = GNUNET_malloc (size);
471 hdr->version = htonl (md == NULL ? 1 : 0);
472 hdr->entries = htonl (ic);
473 for (i = 0; i < ic; i++)
474 ((unsigned int *) &hdr[1])[i] =
475 htonl ((unsigned int) md->items[i].type);
476 pos = sizeof (struct MetaDataHeader);
477 pos += sizeof (unsigned int) * ic;
478 for (i = 0; i < ic; i++)
479 {
480 len = strlen (md->items[i].data) + 1;
481 memcpy (&((char *) hdr)[pos], md->items[i].data, len);
482 pos += len;
483 }
484
485 hdr->size = htonl (size);
486 if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
487 {
488 pos = tryCompression ((char *) &hdr[1],
489 size - sizeof (struct MetaDataHeader));
490 }
491 else
492 {
493 pos = size - sizeof (struct MetaDataHeader);
494 }
495 if (pos < size - sizeof (struct MetaDataHeader))
496 {
497 hdr->version = htonl (HEADER_COMPRESSED);
498 size = pos + sizeof (struct MetaDataHeader);
499 }
500 if (size <= max)
501 break;
502 GNUNET_free (hdr);
503 hdr = NULL;
504
505 if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART) == 0)
506 {
507 return GNUNET_SYSERR; /* does not fit! */
508 }
509 /* partial serialization ok, try again with less meta-data */
510 if (size > 2 * max)
511 ic = ic * 2 / 3; /* still far too big, make big reductions */
512 else
513 ic--; /* small steps, we're close */
514 }
515 GNUNET_assert (size <= max);
516 memcpy (target, hdr, size);
517 GNUNET_free (hdr);
518 /* extra check: deserialize! */
519#if EXTRA_CHECKS
520 {
521 struct GNUNET_CONTAINER_MetaData *mdx;
522 mdx = GNUNET_CONTAINER_meta_data_deserialize (target, size);
523 GNUNET_assert (NULL != mdx);
524 GNUNET_CONTAINER_meta_data_destroy (mdx);
525 }
526#endif
527 return size;
528}
529
530/**
531 * Estimate (!) the size of the meta-data in
532 * serialized form. The estimate MAY be higher
533 * than what is strictly needed.
534 */
535unsigned int
536GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
537 GNUNET_CONTAINER_MetaData *md,
538 enum
539 GNUNET_CONTAINER_MetaDataSerializationOptions
540 part)
541{
542 struct MetaDataHeader *hdr;
543 size_t size;
544 size_t pos;
545 uint32_t i;
546 size_t len;
547 uint32_t ic;
548
549 ic = md ? md->itemCount : 0;
550 size = sizeof (struct MetaDataHeader);
551 size += sizeof (unsigned int) * ic;
552 for (i = 0; i < ic; i++)
553 size += 1 + strlen (md->items[i].data);
554 while (size % 8 != 0)
555 size++;
556 hdr = GNUNET_malloc (size);
557 hdr->version = htonl (md == NULL ? 1 : 0);
558 hdr->entries = htonl (ic);
559 for (i = 0; i < ic; i++)
560 ((unsigned int *) &hdr[1])[i] = htonl ((unsigned int) md->items[i].type);
561 pos = sizeof (struct MetaDataHeader);
562 pos += sizeof (unsigned int) * ic;
563 for (i = 0; i < ic; i++)
564 {
565 len = strlen (md->items[i].data) + 1;
566 memcpy (&((char *) hdr)[pos], md->items[i].data, len);
567 pos += len;
568 }
569 if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
570 {
571 pos =
572 tryCompression ((char *) &hdr[1],
573 size - sizeof (struct MetaDataHeader));
574 }
575 else
576 {
577 pos = size - sizeof (struct MetaDataHeader);
578 }
579 if (pos < size - sizeof (struct MetaDataHeader))
580 size = pos + sizeof (struct MetaDataHeader);
581 GNUNET_free (hdr);
582 return size;
583}
584
585/**
586 * Deserialize meta-data. Initializes md.
587 * @param size number of bytes available
588 * @return MD on success, NULL on error (i.e.
589 * bad format)
590 */
591struct GNUNET_CONTAINER_MetaData *
592GNUNET_CONTAINER_meta_data_deserialize (const char *input, unsigned int size)
593{
594 struct GNUNET_CONTAINER_MetaData *md;
595 const struct MetaDataHeader *hdr;
596 uint32_t ic;
597 char *data;
598 const char *cdata;
599 uint32_t dataSize;
600 int compressed;
601 int i;
602 unsigned int pos;
603 int len;
604 uint32_t version;
605
606 if (size < sizeof (struct MetaDataHeader))
607 return NULL;
608 hdr = (const struct MetaDataHeader *) input;
609 version = ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_VERSION_MASK;
610 if (version == 1)
611 return NULL; /* null pointer */
612 if (version != 0)
613 {
614 GNUNET_break_op (0); /* unsupported version */
615 return NULL;
616 }
617 ic = ntohl (MAKE_UNALIGNED (hdr->entries));
618 compressed =
619 (ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_COMPRESSED) != 0;
620 if (compressed)
621 {
622 dataSize =
623 ntohl (MAKE_UNALIGNED (hdr->size)) - sizeof (struct MetaDataHeader);
624 if (dataSize > 2 * 1042 * 1024)
625 {
626 GNUNET_break (0);
627 return NULL; /* only 2 MB allowed [to make sure we don't blow
628 our memory limit because of a mal-formed
629 message... ] */
630 }
631 data =
632 decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
633 size - sizeof (struct MetaDataHeader), dataSize);
634 if (data == NULL)
635 {
636 GNUNET_break_op (0);
637 return NULL;
638 }
639 cdata = data;
640 }
641 else
642 {
643 data = NULL;
644 cdata = (const char *) &hdr[1];
645 dataSize = size - sizeof (struct MetaDataHeader);
646 if (size != ntohl (MAKE_UNALIGNED (hdr->size)))
647 {
648 GNUNET_break (0);
649 return NULL;
650 }
651 }
652
653 if ((sizeof (unsigned int) * ic + ic) > dataSize)
654 {
655 GNUNET_break (0);
656 goto FAILURE;
657 }
658 if ((ic > 0) && (cdata[dataSize - 1] != '\0'))
659 {
660 GNUNET_break (0);
661 goto FAILURE;
662 }
663
664 md = GNUNET_CONTAINER_meta_data_create ();
665 GNUNET_array_grow (md->items, md->itemCount, ic);
666 i = 0;
667 pos = sizeof (unsigned int) * ic;
668 while ((pos < dataSize) && (i < ic))
669 {
670 len = strlen (&cdata[pos]) + 1;
671 md->items[i].type = (EXTRACTOR_KeywordType)
672 ntohl (MAKE_UNALIGNED (((const unsigned int *) cdata)[i]));
673 md->items[i].data = GNUNET_strdup (&cdata[pos]);
674 pos += len;
675 i++;
676 }
677 if (i < ic)
678 { /* oops */
679 GNUNET_CONTAINER_meta_data_destroy (md);
680 goto FAILURE;
681 }
682 GNUNET_free_non_null (data);
683 return md;
684FAILURE:
685 GNUNET_free_non_null (data);
686 return NULL; /* size too small */
687}
688
689/**
690 * Test if two MDs are equal.
691 */
692int
693GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
694 *md1,
695 const struct GNUNET_CONTAINER_MetaData
696 *md2)
697{
698 uint32_t i;
699 uint32_t j;
700 int found;
701
702 if (md1->itemCount != md2->itemCount)
703 return GNUNET_NO;
704 for (i = 0; i < md1->itemCount; i++)
705 {
706 found = GNUNET_NO;
707 for (j = 0; j < md2->itemCount; j++)
708 if ((md1->items[i].type == md2->items[j].type) &&
709 (0 == strcmp (md1->items[i].data, md2->items[j].data)))
710 {
711 found = GNUNET_YES;
712 break;
713 }
714 if (found == GNUNET_NO)
715 return GNUNET_NO;
716 }
717 return GNUNET_YES;
718}
719
720
721/* end of container_meta_data.c */
diff --git a/src/util/container_multihashmap.c b/src/util/container_multihashmap.c
new file mode 100644
index 000000000..a84955eb3
--- /dev/null
+++ b/src/util/container_multihashmap.c
@@ -0,0 +1,334 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/container_multihashmap.c
22 * @brief hash map where the same key maybe present multiple times
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_container_lib.h"
29#include "gnunet_crypto_lib.h"
30
31struct MapEntry
32{
33 GNUNET_HashCode key;
34 void *value;
35 struct MapEntry *next;
36};
37
38struct GNUNET_CONTAINER_MultiHashMap
39{
40
41 struct MapEntry **map;
42
43 unsigned int size;
44
45 unsigned int map_length;
46};
47
48struct GNUNET_CONTAINER_MultiHashMap *
49GNUNET_CONTAINER_multihashmap_create (unsigned int len)
50{
51 struct GNUNET_CONTAINER_MultiHashMap *ret;
52
53 ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MultiHashMap));
54 ret->size = 0;
55 ret->map = GNUNET_malloc (len * sizeof (struct MapEntry *));
56 memset (ret->map, 0, len * sizeof (struct MapEntry *));
57 ret->map_length = len;
58 return ret;
59}
60
61void
62GNUNET_CONTAINER_multihashmap_destroy (struct GNUNET_CONTAINER_MultiHashMap
63 *map)
64{
65 unsigned int i;
66 struct MapEntry *e;
67
68 for (i = 0; i < map->map_length; i++)
69 {
70 while (NULL != (e = map->map[i]))
71 {
72 map->map[i] = e->next;
73 GNUNET_free (e);
74 }
75 }
76 GNUNET_free (map->map);
77 GNUNET_free (map);
78}
79
80static unsigned int
81idx_of (const struct GNUNET_CONTAINER_MultiHashMap *m,
82 const GNUNET_HashCode * key)
83{
84 return (*(unsigned int *) key) % m->map_length;
85}
86
87unsigned int
88GNUNET_CONTAINER_multihashmap_size (const struct GNUNET_CONTAINER_MultiHashMap
89 *map)
90{
91 return map->size;
92}
93
94void *
95GNUNET_CONTAINER_multihashmap_get (const struct GNUNET_CONTAINER_MultiHashMap
96 *map, const GNUNET_HashCode * key)
97{
98 struct MapEntry *e;
99
100 e = map->map[idx_of (map, key)];
101 while (e != NULL)
102 {
103 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
104 return e->value;
105 e = e->next;
106 }
107 return NULL;
108}
109
110int
111GNUNET_CONTAINER_multihashmap_iterate (const struct
112 GNUNET_CONTAINER_MultiHashMap *map,
113 GNUNET_CONTAINER_HashMapIterator it,
114 void *cls)
115{
116 int count;
117 unsigned int i;
118 struct MapEntry *e;
119
120 count = 0;
121 for (i = 0; i < map->map_length; i++)
122 {
123 e = map->map[i];
124 while (e != NULL)
125 {
126 if ((NULL != it) && (GNUNET_OK != it (&e->key, e->value, cls)))
127 return GNUNET_SYSERR;
128 count++;
129 e = e->next;
130 }
131 }
132 return count;
133}
134
135int
136GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap
137 *map, const GNUNET_HashCode * key,
138 void *value)
139{
140 struct MapEntry *e;
141 struct MapEntry *p;
142 unsigned int i;
143
144 i = idx_of (map, key);
145 p = NULL;
146 e = map->map[i];
147 while (e != NULL)
148 {
149 if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
150 (value == e->value))
151 {
152 if (p == NULL)
153 map->map[i] = e->next;
154 else
155 p->next = e->next;
156 GNUNET_free (e);
157 map->size--;
158 return GNUNET_YES;
159 }
160 p = e;
161 e = e->next;
162 }
163 return GNUNET_NO;
164}
165
166int
167GNUNET_CONTAINER_multihashmap_remove_all (struct GNUNET_CONTAINER_MultiHashMap
168 *map, const GNUNET_HashCode * key)
169{
170 struct MapEntry *e;
171 struct MapEntry *p;
172 unsigned int i;
173 int ret;
174
175 ret = 0;
176 i = idx_of (map, key);
177 p = NULL;
178 e = map->map[i];
179 while (e != NULL)
180 {
181 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
182 {
183 if (p == NULL)
184 map->map[i] = e->next;
185 else
186 p->next = e->next;
187 GNUNET_free (e);
188 map->size--;
189 if (p == NULL)
190 e = map->map[i];
191 else
192 e = p->next;
193 ret++;
194 }
195 else
196 {
197 p = e;
198 e = e->next;
199 }
200 }
201 return ret;
202}
203
204int
205GNUNET_CONTAINER_multihashmap_contains (const struct
206 GNUNET_CONTAINER_MultiHashMap *map,
207 const GNUNET_HashCode * key)
208{
209 struct MapEntry *e;
210 unsigned int i;
211
212 i = idx_of (map, key);
213 e = map->map[i];
214 while (e != NULL)
215 {
216 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
217 return GNUNET_YES;
218 e = e->next;
219 }
220 return GNUNET_NO;
221}
222
223static void
224grow (struct GNUNET_CONTAINER_MultiHashMap *map)
225{
226 struct MapEntry **old;
227 struct MapEntry *e;
228 unsigned int i;
229 unsigned int l;
230
231 old = map->map;
232 l = map->map_length;
233 map->map_length *= 2;
234 map->map = GNUNET_malloc (sizeof (struct MapEntry *) * map->map_length);
235 memset (map->map, 0, sizeof (struct MapEntry *) * map->map_length);
236 for (i = 0; i < l; i++)
237 {
238 while (NULL != (e = old[i]))
239 {
240 old[i] = e->next;
241 e->next = map->map[idx_of (map, &e->key)];
242 map->map[idx_of (map, &e->key)] = e;
243 }
244 }
245 GNUNET_free (old);
246}
247
248int
249GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap *map,
250 const GNUNET_HashCode * key,
251 void *value,
252 enum GNUNET_CONTAINER_MultiHashMapOption
253 opt)
254{
255 struct MapEntry *e;
256 unsigned int i;
257
258 i = idx_of (map, key);
259 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
260 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
261 {
262 e = map->map[i];
263 while (e != NULL)
264 {
265 if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
266 (value == e->value))
267 {
268 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
269 return GNUNET_SYSERR;
270 e->value = value;
271 return GNUNET_NO;
272 }
273 e = e->next;
274 }
275 }
276 if (map->size / 3 > map->map_length / 4)
277 grow (map);
278 e = GNUNET_malloc (sizeof (struct MapEntry));
279 e->key = *key;
280 e->value = value;
281 e->next = map->map[i];
282 map->map[i] = e;
283 map->size++;
284 return GNUNET_OK;
285}
286
287int
288GNUNET_CONTAINER_multihashmap_get_multiple (const struct
289 GNUNET_CONTAINER_MultiHashMap
290 *map, const GNUNET_HashCode * key,
291 GNUNET_CONTAINER_HashMapIterator
292 it, void *cls)
293{
294 int count;
295 struct MapEntry *e;
296
297 count = 0;
298 e = map->map[idx_of (map, key)];
299 while (e != NULL)
300 {
301 if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
302 {
303 if ((it != NULL) && (GNUNET_OK != it (&e->key, e->value, cls)))
304 return GNUNET_SYSERR;
305 count++;
306 }
307 e = e->next;
308 }
309 return count;
310}
311
312void *
313GNUNET_CONTAINER_multihashmap_get_random (const struct
314 GNUNET_CONTAINER_MultiHashMap *map)
315{
316 unsigned int rand;
317 struct MapEntry *e;
318 e = NULL;
319
320 if (map->size == 0)
321 return NULL;
322
323 while (e == NULL)
324 {
325 rand =
326 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
327 map->map_length);
328 e = map->map[rand];
329 }
330
331 return e->value;
332}
333
334/* end of multihashmap.c */
diff --git a/src/util/crypto_aes.c b/src/util/crypto_aes.c
new file mode 100644
index 000000000..28a65dfca
--- /dev/null
+++ b/src/util/crypto_aes.c
@@ -0,0 +1,148 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/crypto_aes.c
23 * @brief Symmetric encryption services.
24 * @author Christian Grothoff
25 * @author Ioana Patrascu
26 */
27
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_crypto_lib.h"
31#include <gcrypt.h>
32
33/**
34 * Create a new SessionKey (for AES-256).
35 */
36void
37GNUNET_CRYPTO_aes_create_session_key (struct GNUNET_CRYPTO_AesSessionKey *key)
38{
39 gcry_randomize (&key->key[0], GNUNET_CRYPTO_AES_KEY_LENGTH,
40 GCRY_STRONG_RANDOM);
41 key->crc32 =
42 htonl (GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH));
43}
44
45/**
46 * Check that a new session key is well-formed.
47 *
48 * @return GNUNET_OK if the key is valid
49 */
50int
51GNUNET_CRYPTO_aes_check_session_key (const struct GNUNET_CRYPTO_AesSessionKey
52 *key)
53{
54 uint32_t crc;
55
56 crc = GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH);
57 if (ntohl (key->crc32) == crc)
58 return GNUNET_OK;
59 GNUNET_break (0);
60 return GNUNET_SYSERR;
61}
62
63
64/**
65 * Encrypt a block with the public key of another
66 * host that uses the same cyper.
67 * @param block the block to encrypt
68 * @param len the size of the block
69 * @param sessionkey the key used to encrypt
70 * @param iv the initialization vector to use, use INITVALUE
71 * for streams.
72 * @param result the output parameter in which to store the encrypted result
73 * @returns the size of the encrypted block, -1 for errors
74 */
75int
76GNUNET_CRYPTO_aes_encrypt (const void *block,
77 uint16_t len,
78 const struct GNUNET_CRYPTO_AesSessionKey
79 *sessionkey,
80 const struct GNUNET_CRYPTO_AesInitializationVector
81 *iv, void *result)
82{
83 gcry_cipher_hd_t handle;
84 int rc;
85
86 if (sessionkey->crc32 !=
87 htonl (GNUNET_CRYPTO_crc32_n
88 (sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH)))
89 {
90 GNUNET_break (0);
91 return GNUNET_SYSERR;
92 }
93 GNUNET_assert (0 == gcry_cipher_open (&handle,
94 GCRY_CIPHER_AES256,
95 GCRY_CIPHER_MODE_CFB, 0));
96 rc = gcry_cipher_setkey (handle, sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH);
97 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
98 rc =
99 gcry_cipher_setiv (handle, iv,
100 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
101 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
102 GNUNET_assert (0 == gcry_cipher_encrypt (handle, result, len, block, len));
103 gcry_cipher_close (handle);
104 return len;
105}
106
107/**
108 * Decrypt a given block with the sessionkey.
109 * @param sessionkey the key used to decrypt
110 * @param block the data to decrypt, encoded as returned by encrypt
111 * @param size the size of the block to decrypt
112 * @param iv the initialization vector to use, use INITVALUE
113 * for streams.
114 * @param result address to store the result at
115 * @return -1 on failure, size of decrypted block on success
116 */
117int
118GNUNET_CRYPTO_aes_decrypt (const struct GNUNET_CRYPTO_AesSessionKey
119 *sessionkey, const void *block, uint16_t size,
120 const struct GNUNET_CRYPTO_AesInitializationVector
121 *iv, void *result)
122{
123 gcry_cipher_hd_t handle;
124 int rc;
125
126 if (sessionkey->crc32 !=
127 htonl (GNUNET_CRYPTO_crc32_n
128 (sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH)))
129 {
130 GNUNET_break (0);
131 return GNUNET_SYSERR;
132 }
133 GNUNET_assert (0 == gcry_cipher_open (&handle,
134 GCRY_CIPHER_AES256,
135 GCRY_CIPHER_MODE_CFB, 0));
136 rc = gcry_cipher_setkey (handle, sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH);
137 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
138 rc =
139 gcry_cipher_setiv (handle, iv,
140 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
141 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
142 GNUNET_assert (0 ==
143 gcry_cipher_decrypt (handle, result, size, block, size));
144 gcry_cipher_close (handle);
145 return size;
146}
147
148/* end of crypto_aes.c */
diff --git a/src/util/crypto_crc.c b/src/util/crypto_crc.c
new file mode 100644
index 000000000..35d1e2576
--- /dev/null
+++ b/src/util/crypto_crc.c
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20 For the actual CRC code:
21 Copyright abandoned; this code is in the public domain.
22 Provided to GNUnet by peter@horizon.com
23*/
24
25/**
26 * @file util/crypto_crc.c
27 * @brief implementation of CRC32
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_common.h"
33#include "gnunet_crypto_lib.h"
34
35/* Avoid wasting space on 8-byte longs. */
36#if UINT_MAX >= 0xffffffff
37typedef unsigned int uLong;
38#elif ULONG_MAX >= 0xffffffff
39typedef unsigned long uLong;
40#else
41#error This compiler is not ANSI-compliant!
42#endif
43
44#define Z_NULL 0
45
46
47#define POLYNOMIAL (uLong)0xedb88320
48static uLong crc_table[256];
49
50/*
51 * This routine writes each crc_table entry exactly once,
52 * with the ccorrect final value. Thus, it is safe to call
53 * even on a table that someone else is using concurrently.
54 */
55void __attribute__ ((constructor)) GNUNET_CRYPTO_crc_init ()
56{
57 unsigned int i, j;
58 uLong h = 1;
59 crc_table[0] = 0;
60 for (i = 128; i; i >>= 1)
61 {
62 h = (h >> 1) ^ ((h & 1) ? POLYNOMIAL : 0);
63 /* h is now crc_table[i] */
64 for (j = 0; j < 256; j += 2 * i)
65 crc_table[i + j] = crc_table[j] ^ h;
66 }
67}
68
69/*
70 * This computes the standard preset and inverted CRC, as used
71 * by most networking standards. Start by passing in an initial
72 * chaining value of 0, and then pass in the return value from the
73 * previous crc32() call. The final return value is the CRC.
74 * Note that this is a little-endian CRC, which is best used with
75 * data transmitted lsbit-first, and it should, itself, be appended
76 * to data in little-endian byte and bit order to preserve the
77 * property of detecting all burst errors of length 32 bits or less.
78 */
79static uLong
80crc32 (uLong crc, const char *buf, size_t len)
81{
82 GNUNET_assert (crc_table[255] != 0);
83 crc ^= 0xffffffff;
84 while (len--)
85 crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff];
86 return crc ^ 0xffffffff;
87}
88
89
90/**
91 * Compute the CRC32 checksum for the first len bytes of the buffer.
92 *
93 * @param buf the data over which we're taking the CRC
94 * @param len the length of the buffer
95 * @return the resulting CRC32 checksum
96 */
97int
98GNUNET_CRYPTO_crc32_n (const void *buf, unsigned int len)
99{
100 uLong crc;
101 crc = crc32 (0L, Z_NULL, 0);
102 crc = crc32 (crc, (char *) buf, len);
103 return crc;
104}
105
106/* end of crc32.c */
diff --git a/src/util/crypto_hash.c b/src/util/crypto_hash.c
new file mode 100644
index 000000000..139496eac
--- /dev/null
+++ b/src/util/crypto_hash.c
@@ -0,0 +1,791 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20 SHA-512 code by Jean-Luc Cooke <jlcooke@certainkey.com>
21
22 Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com>
23 Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
24 Copyright (c) 2003 Kyle McMartin <kyle@debian.org>
25*/
26
27/**
28 * @file util/crypto_hash.c
29 * @brief SHA-512 GNUNET_CRYPTO_hash related functions
30 * @author Christian Grothoff
31 */
32
33#include "platform.h"
34#include "gnunet_common.h"
35#include "gnunet_crypto_lib.h"
36#include "gnunet_disk_lib.h"
37
38#define SHA512_DIGEST_SIZE 64
39#define SHA512_HMAC_BLOCK_SIZE 128
40
41struct sha512_ctx
42{
43 unsigned long long state[8];
44 unsigned int count[4];
45 unsigned char buf[128];
46};
47
48static unsigned long long
49Ch (unsigned long long x, unsigned long long y, unsigned long long z)
50{
51 return z ^ (x & (y ^ z));
52}
53
54static unsigned long long
55Maj (unsigned long long x, unsigned long long y, unsigned long long z)
56{
57 return (x & y) | (z & (x | y));
58}
59
60static unsigned long long
61RORu64 (unsigned long long x, unsigned long long y)
62{
63 return (x >> y) | (x << (64 - y));
64}
65
66const unsigned long long sha512_K[80] = {
67 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,
68 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
69 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,
70 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
71 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
72 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
73 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,
74 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
75 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,
76 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
77 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,
78 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
79 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,
80 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
81 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
82 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
83 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,
84 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
85 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,
86 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
87 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,
88 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
89 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,
90 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
91 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
92 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
93 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL,
94};
95
96#define e0(x) (RORu64(x,28) ^ RORu64(x,34) ^ RORu64(x,39))
97#define e1(x) (RORu64(x,14) ^ RORu64(x,18) ^ RORu64(x,41))
98#define s0(x) (RORu64(x, 1) ^ RORu64(x, 8) ^ (x >> 7))
99#define s1(x) (RORu64(x,19) ^ RORu64(x,61) ^ (x >> 6))
100
101/* H* initial state for SHA-512 */
102#define H0 0x6a09e667f3bcc908ULL
103#define H1 0xbb67ae8584caa73bULL
104#define H2 0x3c6ef372fe94f82bULL
105#define H3 0xa54ff53a5f1d36f1ULL
106#define H4 0x510e527fade682d1ULL
107#define H5 0x9b05688c2b3e6c1fULL
108#define H6 0x1f83d9abfb41bd6bULL
109#define H7 0x5be0cd19137e2179ULL
110
111/* H'* initial state for SHA-384 */
112#define HP0 0xcbbb9d5dc1059ed8ULL
113#define HP1 0x629a292a367cd507ULL
114#define HP2 0x9159015a3070dd17ULL
115#define HP3 0x152fecd8f70e5939ULL
116#define HP4 0x67332667ffc00b31ULL
117#define HP5 0x8eb44a8768581511ULL
118#define HP6 0xdb0c2e0d64f98fa7ULL
119#define HP7 0x47b5481dbefa4fa4ULL
120
121#define LOAD_OP(t1, I, W, input) \
122 t1 = input[(8*I) ] & 0xff;\
123 t1 <<= 8;\
124 t1 |= input[(8*I)+1] & 0xff;\
125 t1 <<= 8;\
126 t1 |= input[(8*I)+2] & 0xff;\
127 t1 <<= 8;\
128 t1 |= input[(8*I)+3] & 0xff;\
129 t1 <<= 8;\
130 t1 |= input[(8*I)+4] & 0xff;\
131 t1 <<= 8;\
132 t1 |= input[(8*I)+5] & 0xff;\
133 t1 <<= 8;\
134 t1 |= input[(8*I)+6] & 0xff;\
135 t1 <<= 8;\
136 t1 |= input[(8*I)+7] & 0xff;\
137 W[I] = t1;
138
139
140#define BLEND_OP(I, W) \
141 W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16];
142
143static void
144sha512_transform (unsigned long long *state, const unsigned char *input)
145{
146 unsigned long long a, b, c, d, e, f, g, h, t1, t2;
147 unsigned long long W[80];
148 unsigned long long t0;
149 int i;
150
151 /* load the input */
152 for (i = 0; i < 16; i++)
153 {
154 LOAD_OP (t0, i, W, input);
155 }
156
157 for (i = 16; i < 80; i++)
158 {
159 BLEND_OP (i, W);
160 }
161
162 /* load the state into our registers */
163 a = state[0];
164 b = state[1];
165 c = state[2];
166 d = state[3];
167 e = state[4];
168 f = state[5];
169 g = state[6];
170 h = state[7];
171
172 /* now iterate */
173 for (i = 0; i < 80; i += 8)
174 {
175 t1 = h + e1 (e) + Ch (e, f, g) + sha512_K[i] + W[i];
176 t2 = e0 (a) + Maj (a, b, c);
177 d += t1;
178 h = t1 + t2;
179 t1 = g + e1 (d) + Ch (d, e, f) + sha512_K[i + 1] + W[i + 1];
180 t2 = e0 (h) + Maj (h, a, b);
181 c += t1;
182 g = t1 + t2;
183 t1 = f + e1 (c) + Ch (c, d, e) + sha512_K[i + 2] + W[i + 2];
184 t2 = e0 (g) + Maj (g, h, a);
185 b += t1;
186 f = t1 + t2;
187 t1 = e + e1 (b) + Ch (b, c, d) + sha512_K[i + 3] + W[i + 3];
188 t2 = e0 (f) + Maj (f, g, h);
189 a += t1;
190 e = t1 + t2;
191 t1 = d + e1 (a) + Ch (a, b, c) + sha512_K[i + 4] + W[i + 4];
192 t2 = e0 (e) + Maj (e, f, g);
193 h += t1;
194 d = t1 + t2;
195 t1 = c + e1 (h) + Ch (h, a, b) + sha512_K[i + 5] + W[i + 5];
196 t2 = e0 (d) + Maj (d, e, f);
197 g += t1;
198 c = t1 + t2;
199 t1 = b + e1 (g) + Ch (g, h, a) + sha512_K[i + 6] + W[i + 6];
200 t2 = e0 (c) + Maj (c, d, e);
201 f += t1;
202 b = t1 + t2;
203 t1 = a + e1 (f) + Ch (f, g, h) + sha512_K[i + 7] + W[i + 7];
204 t2 = e0 (b) + Maj (b, c, d);
205 e += t1;
206 a = t1 + t2;
207 }
208
209 state[0] += a;
210 state[1] += b;
211 state[2] += c;
212 state[3] += d;
213 state[4] += e;
214 state[5] += f;
215 state[6] += g;
216 state[7] += h;
217
218 /* erase our data */
219 a = b = c = d = e = f = g = h = t1 = t2 = 0;
220 memset (W, 0, 80 * sizeof (unsigned long long));
221}
222
223static void
224sha512_init (struct sha512_ctx *sctx)
225{
226 sctx->state[0] = H0;
227 sctx->state[1] = H1;
228 sctx->state[2] = H2;
229 sctx->state[3] = H3;
230 sctx->state[4] = H4;
231 sctx->state[5] = H5;
232 sctx->state[6] = H6;
233 sctx->state[7] = H7;
234 sctx->count[0] = sctx->count[1] = sctx->count[2] = sctx->count[3] = 0;
235 memset (sctx->buf, 0, sizeof (sctx->buf));
236}
237
238static void
239sha512_update (struct sha512_ctx *sctx,
240 const unsigned char *data, unsigned int len)
241{
242 unsigned int i, index, part_len;
243
244 /* Compute number of bytes mod 128 */
245 index = (unsigned int) ((sctx->count[0] >> 3) & 0x7F);
246
247 /* Update number of bits */
248 if ((sctx->count[0] += (len << 3)) < (len << 3))
249 {
250 if ((sctx->count[1] += 1) < 1)
251 if ((sctx->count[2] += 1) < 1)
252 sctx->count[3]++;
253 sctx->count[1] += (len >> 29);
254 }
255
256 part_len = 128 - index;
257
258 /* Transform as many times as possible. */
259 if (len >= part_len)
260 {
261 memcpy (&sctx->buf[index], data, part_len);
262 sha512_transform (sctx->state, sctx->buf);
263
264 for (i = part_len; i + 127 < len; i += 128)
265 sha512_transform (sctx->state, &data[i]);
266
267 index = 0;
268 }
269 else
270 {
271 i = 0;
272 }
273
274 /* Buffer remaining input */
275 memcpy (&sctx->buf[index], &data[i], len - i);
276}
277
278static void
279sha512_final (struct sha512_ctx *sctx, unsigned char *hash)
280{
281 static unsigned char padding[128] = { 0x80, };
282
283 unsigned int t;
284 unsigned long long t2;
285 unsigned char bits[128];
286 unsigned int index, pad_len;
287 int i, j;
288
289 index = pad_len = t = i = j = 0;
290 t2 = 0;
291
292 /* Save number of bits */
293 t = sctx->count[0];
294 bits[15] = t;
295 t >>= 8;
296 bits[14] = t;
297 t >>= 8;
298 bits[13] = t;
299 t >>= 8;
300 bits[12] = t;
301 t = sctx->count[1];
302 bits[11] = t;
303 t >>= 8;
304 bits[10] = t;
305 t >>= 8;
306 bits[9] = t;
307 t >>= 8;
308 bits[8] = t;
309 t = sctx->count[2];
310 bits[7] = t;
311 t >>= 8;
312 bits[6] = t;
313 t >>= 8;
314 bits[5] = t;
315 t >>= 8;
316 bits[4] = t;
317 t = sctx->count[3];
318 bits[3] = t;
319 t >>= 8;
320 bits[2] = t;
321 t >>= 8;
322 bits[1] = t;
323 t >>= 8;
324 bits[0] = t;
325
326 /* Pad out to 112 mod 128. */
327 index = (sctx->count[0] >> 3) & 0x7f;
328 pad_len = (index < 112) ? (112 - index) : ((128 + 112) - index);
329 sha512_update (sctx, padding, pad_len);
330
331 /* Append length (before padding) */
332 sha512_update (sctx, bits, 16);
333
334 /* Store state in digest */
335 for (i = j = 0; i < 8; i++, j += 8)
336 {
337 t2 = sctx->state[i];
338 hash[j + 7] = (char) t2 & 0xff;
339 t2 >>= 8;
340 hash[j + 6] = (char) t2 & 0xff;
341 t2 >>= 8;
342 hash[j + 5] = (char) t2 & 0xff;
343 t2 >>= 8;
344 hash[j + 4] = (char) t2 & 0xff;
345 t2 >>= 8;
346 hash[j + 3] = (char) t2 & 0xff;
347 t2 >>= 8;
348 hash[j + 2] = (char) t2 & 0xff;
349 t2 >>= 8;
350 hash[j + 1] = (char) t2 & 0xff;
351 t2 >>= 8;
352 hash[j] = (char) t2 & 0xff;
353 }
354
355 /* Zeroize sensitive information. */
356 memset (sctx, 0, sizeof (struct sha512_ctx));
357}
358
359/**
360 * Hash block of given size.
361 *
362 * @param block the data to GNUNET_CRYPTO_hash, length is given as a second argument
363 * @param size the length of the data to GNUNET_CRYPTO_hash
364 * @param ret pointer to where to write the hashcode
365 */
366void
367GNUNET_CRYPTO_hash (const void *block, unsigned int size,
368 GNUNET_HashCode * ret)
369{
370 struct sha512_ctx ctx;
371
372 sha512_init (&ctx);
373 sha512_update (&ctx, block, size);
374 sha512_final (&ctx, (unsigned char *) ret);
375}
376
377
378/**
379 * Context used when hashing a file.
380 */
381struct FileHashContext
382{
383
384 /**
385 * Function to call upon completion.
386 */
387 GNUNET_CRYPTO_HashCompletedCallback callback;
388
389 /**
390 * Closure for callback.
391 */
392 void *callback_cls;
393
394 /**
395 * IO buffer.
396 */
397 unsigned char *buffer;
398
399 /**
400 * Name of the file we are hashing.
401 */
402 char *filename;
403
404 /**
405 * Cummulated hash.
406 */
407 struct sha512_ctx hctx;
408
409 /**
410 * Blocksize.
411 */
412 size_t bsize;
413
414 /**
415 * Size of the file.
416 */
417 unsigned long long fsize;
418
419 /**
420 * Current offset.
421 */
422 unsigned long long offset;
423
424 /**
425 * Run on shutdown?
426 */
427 int run_on_shutdown;
428
429 /**
430 * File descriptor.
431 */
432 int fd;
433
434};
435
436
437/**
438 * Report result of hash computation to callback
439 * and free associated resources.
440 */
441static void
442file_hash_finish (struct FileHashContext *fhc, const GNUNET_HashCode * res)
443{
444 fhc->callback (fhc->callback_cls, res);
445 GNUNET_free (fhc->filename);
446 if (fhc->fd != -1)
447 GNUNET_break (0 == CLOSE (fhc->fd));
448 GNUNET_free (fhc); /* also frees fhc->buffer */
449}
450
451
452/**
453 * File hashing task.
454 *
455 * @param cls closure
456 * @param tc context
457 */
458static void
459file_hash_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
460{
461 struct FileHashContext *fhc = cls;
462 GNUNET_HashCode res;
463 size_t delta;
464
465 GNUNET_assert (fhc->offset < fhc->fsize);
466 delta = fhc->bsize;
467 if (fhc->fsize - fhc->offset < delta)
468 delta = fhc->fsize - fhc->offset;
469 if (delta != READ (fhc->fd, fhc->buffer, delta))
470 {
471 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
472 "read", fhc->filename);
473 file_hash_finish (fhc, NULL);
474 return;
475 }
476 sha512_update (&fhc->hctx, fhc->buffer, delta);
477 fhc->offset += delta;
478 if (fhc->offset == fhc->fsize)
479 {
480 sha512_final (&fhc->hctx, (unsigned char *) &res);
481 file_hash_finish (fhc, &res);
482 return;
483 }
484 GNUNET_SCHEDULER_add_after (tc->sched,
485 fhc->run_on_shutdown,
486 GNUNET_SCHEDULER_PRIORITY_KEEP,
487 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
488 &file_hash_task, fhc);
489}
490
491
492/**
493 * Compute the hash of an entire file.
494 *
495 * @param sched scheduler to use
496 * @param priority scheduling priority to use
497 * @param run_on_shutdown should we complete even on shutdown?
498 * @param filename name of file to hash
499 * @param blocksize number of bytes to process in one task
500 * @param callback function to call upon completion
501 * @param callback_cls closure for callback
502 */
503void
504GNUNET_CRYPTO_hash_file (struct GNUNET_SCHEDULER_Handle *sched,
505 enum GNUNET_SCHEDULER_Priority priority,
506 int run_on_shutdown,
507 const char *filename,
508 size_t blocksize,
509 GNUNET_CRYPTO_HashCompletedCallback callback,
510 void *callback_cls)
511{
512 struct FileHashContext *fhc;
513
514 GNUNET_assert (blocksize > 0);
515 fhc = GNUNET_malloc (sizeof (struct FileHashContext) + blocksize);
516 fhc->callback = callback;
517 fhc->callback_cls = callback_cls;
518 fhc->buffer = (unsigned char *) &fhc[1];
519 fhc->filename = GNUNET_strdup (filename);
520 fhc->fd = -1;
521 sha512_init (&fhc->hctx);
522 fhc->bsize = blocksize;
523 if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fhc->fsize, GNUNET_NO))
524 {
525 file_hash_finish (fhc, NULL);
526 return;
527 }
528 fhc->run_on_shutdown = run_on_shutdown;
529 fhc->fd = GNUNET_DISK_file_open (filename, O_RDONLY | O_LARGEFILE);
530 if (fhc->fd == -1)
531 {
532 file_hash_finish (fhc, NULL);
533 return;
534 }
535 GNUNET_SCHEDULER_add_after (sched,
536 run_on_shutdown,
537 priority,
538 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
539 &file_hash_task, fhc);
540}
541
542
543/* ***************** binary-ASCII encoding *************** */
544
545/**
546 * 32 characters for encoding (GNUNET_CRYPTO_hash => 32 characters)
547 */
548static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
549
550static unsigned int
551getValue__ (unsigned char a)
552{
553 if ((a >= '0') && (a <= '9'))
554 return a - '0';
555 if ((a >= 'A') && (a <= 'V'))
556 return (a - 'A' + 10);
557 return -1;
558}
559
560/**
561 * Convert GNUNET_CRYPTO_hash to ASCII encoding. The ASCII encoding is rather
562 * GNUnet specific. It was chosen such that it only uses characters
563 * in [0-9A-V], can be produced without complex arithmetics and uses a
564 * small number of characters. The GNUnet encoding uses 102
565 * characters plus a null terminator.
566 *
567 * @param block the GNUNET_CRYPTO_hash code
568 * @param result where to store the encoding (struct GNUNET_CRYPTO_HashAsciiEncoded can be
569 * safely cast to char*, a '\0' termination is set).
570 */
571void
572GNUNET_CRYPTO_hash_to_enc (const GNUNET_HashCode * block,
573 struct GNUNET_CRYPTO_HashAsciiEncoded *result)
574{
575 unsigned int wpos;
576 unsigned int rpos;
577 unsigned int bits;
578 unsigned int vbit;
579
580 GNUNET_assert (block != NULL);
581 GNUNET_assert (result != NULL);
582 vbit = 0;
583 wpos = 0;
584 rpos = 0;
585 bits = 0;
586 while ((rpos < sizeof (GNUNET_HashCode)) || (vbit > 0))
587 {
588 if ((rpos < sizeof (GNUNET_HashCode)) && (vbit < 5))
589 {
590 bits = (bits << 8) | ((unsigned char *) block)[rpos++]; /* eat 8 more bits */
591 vbit += 8;
592 }
593 if (vbit < 5)
594 {
595 bits <<= (5 - vbit); /* zero-padding */
596 GNUNET_assert (vbit == 2); /* padding by 3: 512+3 mod 5 == 0 */
597 vbit = 5;
598 }
599 GNUNET_assert (wpos <
600 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1);
601 result->encoding[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
602 vbit -= 5;
603 }
604 GNUNET_assert (wpos == sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1);
605 GNUNET_assert (vbit == 0);
606 result->encoding[wpos] = '\0';
607}
608
609/**
610 * Convert ASCII encoding back to GNUNET_CRYPTO_hash
611 *
612 * @param enc the encoding
613 * @param result where to store the GNUNET_CRYPTO_hash code
614 * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding
615 */
616int
617GNUNET_CRYPTO_hash_from_string (const char *enc, GNUNET_HashCode * result)
618{
619 unsigned int rpos;
620 unsigned int wpos;
621 unsigned int bits;
622 unsigned int vbit;
623
624 if (strlen (enc) != sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1)
625 return GNUNET_SYSERR;
626
627 vbit = 2; /* padding! */
628 wpos = sizeof (GNUNET_HashCode);
629 rpos = sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1;
630 bits = getValue__ (enc[--rpos]) >> 3;
631 while (wpos > 0)
632 {
633 GNUNET_assert (rpos > 0);
634 bits = (getValue__ (enc[--rpos]) << vbit) | bits;
635 vbit += 5;
636 if (vbit >= 8)
637 {
638 ((unsigned char *) result)[--wpos] = (unsigned char) bits;
639 bits >>= 8;
640 vbit -= 8;
641 }
642 }
643 GNUNET_assert (rpos == 0);
644 GNUNET_assert (vbit == 0);
645 return GNUNET_OK;
646}
647
648/**
649 * Compute the distance between 2 hashcodes. The computation must be
650 * fast, not involve bits[0] or bits[4] (they're used elsewhere), and be
651 * somewhat consistent. And of course, the result should be a positive
652 * number.
653 *
654 * @returns a positive number which is a measure for
655 * hashcode proximity.
656 */
657unsigned int
658GNUNET_CRYPTO_hash_distance_u32 (const GNUNET_HashCode * a,
659 const GNUNET_HashCode * b)
660{
661 unsigned int x1 = (a->bits[1] - b->bits[1]) >> 16;
662 unsigned int x2 = (b->bits[1] - a->bits[1]) >> 16;
663 return (x1 * x2);
664}
665
666void
667GNUNET_CRYPTO_hash_create_random (GNUNET_HashCode * result)
668{
669 int i;
670 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
671 i--)
672 result->bits[i] = rand ();
673}
674
675void
676GNUNET_CRYPTO_hash_difference (const GNUNET_HashCode * a,
677 const GNUNET_HashCode * b,
678 GNUNET_HashCode * result)
679{
680 int i;
681 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
682 i--)
683 result->bits[i] = b->bits[i] - a->bits[i];
684}
685
686void
687GNUNET_CRYPTO_hash_sum (const GNUNET_HashCode * a,
688 const GNUNET_HashCode * delta,
689 GNUNET_HashCode * result)
690{
691 int i;
692 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
693 i--)
694 result->bits[i] = delta->bits[i] + a->bits[i];
695}
696
697void
698GNUNET_CRYPTO_hash_xor (const GNUNET_HashCode * a,
699 const GNUNET_HashCode * b, GNUNET_HashCode * result)
700{
701 int i;
702 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
703 i--)
704 result->bits[i] = a->bits[i] ^ b->bits[i];
705}
706
707/**
708 * Convert a hashcode into a key.
709 */
710void
711GNUNET_CRYPTO_hash_to_AES_key (const GNUNET_HashCode * hc,
712 struct GNUNET_CRYPTO_AesSessionKey *skey,
713 struct GNUNET_CRYPTO_AesInitializationVector
714 *iv)
715{
716 GNUNET_assert (sizeof (GNUNET_HashCode) >=
717 GNUNET_CRYPTO_AES_KEY_LENGTH +
718 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
719 memcpy (skey, hc, GNUNET_CRYPTO_AES_KEY_LENGTH);
720 skey->crc32 =
721 htonl (GNUNET_CRYPTO_crc32_n (skey, GNUNET_CRYPTO_AES_KEY_LENGTH));
722 memcpy (iv, &((char *) hc)[GNUNET_CRYPTO_AES_KEY_LENGTH],
723 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
724}
725
726/**
727 * Obtain a bit from a hashcode.
728 * @param code the GNUNET_CRYPTO_hash to index bit-wise
729 * @param bit index into the hashcode, [0...511]
730 * @return Bit \a bit from hashcode \a code, -1 for invalid index
731 */
732int
733GNUNET_CRYPTO_hash_get_bit (const GNUNET_HashCode * code, unsigned int bit)
734{
735 GNUNET_assert (bit < 8 * sizeof (GNUNET_HashCode));
736 return (((unsigned char *) code)[bit >> 3] & (1 << (bit & 7))) > 0;
737}
738
739/**
740 * Compare function for HashCodes, producing a total ordering
741 * of all hashcodes.
742 * @return 1 if h1 > h2, -1 if h1 < h2 and 0 if h1 == h2.
743 */
744int
745GNUNET_CRYPTO_hash_cmp (const GNUNET_HashCode * h1,
746 const GNUNET_HashCode * h2)
747{
748 unsigned int *i1;
749 unsigned int *i2;
750 int i;
751
752 i1 = (unsigned int *) h1;
753 i2 = (unsigned int *) h2;
754 for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0;
755 i--)
756 {
757 if (i1[i] > i2[i])
758 return 1;
759 if (i1[i] < i2[i])
760 return -1;
761 }
762 return 0;
763}
764
765/**
766 * Find out which of the two GNUNET_CRYPTO_hash codes is closer to target
767 * in the XOR metric (Kademlia).
768 * @return -1 if h1 is closer, 1 if h2 is closer and 0 if h1==h2.
769 */
770int
771GNUNET_CRYPTO_hash_xorcmp (const GNUNET_HashCode * h1,
772 const GNUNET_HashCode * h2,
773 const GNUNET_HashCode * target)
774{
775 int i;
776 unsigned int d1;
777 unsigned int d2;
778
779 for (i = sizeof (GNUNET_HashCode) / sizeof (unsigned int) - 1; i >= 0; i--)
780 {
781 d1 = ((unsigned int *) h1)[i] ^ ((unsigned int *) target)[i];
782 d2 = ((unsigned int *) h2)[i] ^ ((unsigned int *) target)[i];
783 if (d1 > d2)
784 return 1;
785 else if (d1 < d2)
786 return -1;
787 }
788 return 0;
789}
790
791/* end of hashing.c */
diff --git a/src/util/crypto_ksk.c b/src/util/crypto_ksk.c
new file mode 100644
index 000000000..c3461ae61
--- /dev/null
+++ b/src/util/crypto_ksk.c
@@ -0,0 +1,791 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 1994, 1996, 1998, 2001, 2002, 2003 Free Software Foundation, Inc.
4 Copyright (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
5
6 GNUnet is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2, or (at your
9 option) any later version.
10
11 GNUnet is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNUnet; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20
21 Note: This code is based on code from libgcrypt
22 The code was adapted for GNUnet to support RSA-key generation
23 based on weak, pseudo-random keys. Do NOT use to generate
24 ordinary RSA keys!
25*/
26
27
28/**
29 * @file util/crypto_ksk.c
30 * @brief implementation of RSA-Key generation for KBlocks
31 * (do NOT use for pseudonyms or hostkeys!)
32 * @author Christian Grothoff
33 */
34
35#include "platform.h"
36#include "gnunet_common.h"
37#include "gnunet_crypto_lib.h"
38#include <gmp.h>
39#include <gcrypt.h>
40
41/**
42 * Log an error message at log-level 'level' that indicates
43 * a failure of the command 'cmd' with the message given
44 * by gcry_strerror(rc).
45 */
46#define LOG_GCRY(level, cmd, rc) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0);
47
48
49typedef struct
50{
51 mpz_t n; /* public modulus */
52 mpz_t e; /* public exponent */
53 mpz_t d; /* exponent */
54 mpz_t p; /* prime p. */
55 mpz_t q; /* prime q. */
56 mpz_t u; /* inverse of p mod q. */
57} KBlock_secret_key;
58
59/**
60 * The private information of an RSA key pair.
61 * NOTE: this must match the definition in crypto_rsa.c
62 */
63struct GNUNET_CRYPTO_RsaPrivateKey
64{
65 gcry_sexp_t sexp;
66};
67
68
69/* Note: 2 is not included because it can be tested more easily by
70 looking at bit 0. The last entry in this list is marked by a zero */
71static uint16_t small_prime_numbers[] = {
72 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
73 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
74 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
75 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
76 211, 223, 227, 229, 233, 239, 241, 251, 257, 263,
77 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
78 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
79 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
80 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
81 509, 521, 523, 541, 547, 557, 563, 569, 571, 577,
82 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
83 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
84 709, 719, 727, 733, 739, 743, 751, 757, 761, 769,
85 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
86 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
87 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
88 991, 997, 1009, 1013, 1019, 1021, 1031, 1033,
89 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091,
90 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
91 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
92 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277,
93 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307,
94 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399,
95 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
96 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493,
97 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559,
98 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609,
99 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
100 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
101 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789,
102 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871,
103 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931,
104 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997,
105 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
106 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111,
107 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161,
108 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243,
109 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
110 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
111 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411,
112 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473,
113 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551,
114 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633,
115 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
116 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729,
117 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791,
118 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851,
119 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917,
120 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
121 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061,
122 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137,
123 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209,
124 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271,
125 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
126 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391,
127 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467,
128 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533,
129 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583,
130 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
131 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709,
132 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779,
133 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851,
134 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917,
135 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
136 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049,
137 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111,
138 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177,
139 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243,
140 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
141 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391,
142 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457,
143 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519,
144 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597,
145 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
146 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729,
147 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799,
148 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889,
149 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951,
150 4957, 4967, 4969, 4973, 4987, 4993, 4999,
151 0
152};
153
154#define DIM(v) (sizeof(v)/sizeof((v)[0]))
155static int no_of_small_prime_numbers = DIM (small_prime_numbers) - 1;
156
157
158 static unsigned int
159 get_nbits (mpz_t a)
160{
161 return mpz_sizeinbase (a, 2);
162}
163
164/**
165 * Count the number of zerobits at the low end of A
166 */
167static unsigned int
168get_trailing_zeros (mpz_t a)
169{
170 unsigned int count = 0;
171 unsigned int nbits = get_nbits (a);
172
173 while ((mpz_tstbit (a, count)) && (count < nbits))
174 count++;
175 return count;
176}
177
178/**
179 * Set bit N of A. and clear all bits above
180 */
181static void
182set_highbit (mpz_t a, unsigned int n)
183{
184 unsigned int nbits;
185
186 nbits = get_nbits (a);
187 while (nbits > n)
188 mpz_clrbit (a, nbits--);
189 mpz_setbit (a, n);
190}
191
192static void
193mpz_randomize (mpz_t n, unsigned int nbits, GNUNET_HashCode * rnd)
194{
195 GNUNET_HashCode *tmp;
196 int cnt;
197 int i;
198
199 cnt = (nbits / sizeof (GNUNET_HashCode) / 8) + 1;
200 tmp = GNUNET_malloc (sizeof (GNUNET_HashCode) * cnt);
201
202 tmp[0] = *rnd;
203 for (i = 0; i < cnt - 1; i++)
204 {
205 GNUNET_CRYPTO_hash (&tmp[i], sizeof (GNUNET_HashCode), &tmp[i + 1]);
206 }
207 *rnd = tmp[cnt - 1];
208 mpz_import (n, cnt * sizeof (GNUNET_HashCode) / sizeof (unsigned int),
209 1, sizeof (unsigned int), 1, 0, tmp);
210 GNUNET_free (tmp);
211 i = get_nbits (n);
212 while (i > nbits)
213 mpz_clrbit (n, i--);
214}
215
216/**
217 * Return true if n is probably a prime
218 */
219static int
220is_prime (mpz_t n, int steps, GNUNET_HashCode * hc)
221{
222 mpz_t x;
223 mpz_t y;
224 mpz_t z;
225 mpz_t nminus1;
226 mpz_t a2;
227 mpz_t q;
228 unsigned int i, j, k;
229 int rc = 0;
230 unsigned int nbits;
231
232 mpz_init (x);
233 mpz_init (y);
234 mpz_init (z);
235 mpz_init (nminus1);
236 mpz_init_set_ui (a2, 2);
237 nbits = get_nbits (n);
238 mpz_sub_ui (nminus1, n, 1);
239
240 /* Find q and k, so that n = 1 + 2^k * q . */
241 mpz_init_set (q, nminus1);
242 k = get_trailing_zeros (q);
243 mpz_tdiv_q_2exp (q, q, k);
244
245 for (i = 0; i < steps; i++)
246 {
247 if (!i)
248 {
249 mpz_set_ui (x, 2);
250 }
251 else
252 {
253 mpz_randomize (x, nbits, hc);
254
255 /* Make sure that the number is smaller than the prime and
256 keep the randomness of the high bit. */
257 if (mpz_tstbit (x, nbits - 2))
258 {
259 set_highbit (x, nbits - 2); /* Clear all higher bits. */
260 }
261 else
262 {
263 set_highbit (x, nbits - 2);
264 mpz_clrbit (x, nbits - 2);
265 }
266 GNUNET_assert (mpz_cmp (x, nminus1) < 0 && mpz_cmp_ui (x, 1) > 0);
267 }
268 mpz_powm (y, x, q, n);
269 if (mpz_cmp_ui (y, 1) && mpz_cmp (y, nminus1))
270 {
271 for (j = 1; j < k && mpz_cmp (y, nminus1); j++)
272 {
273 mpz_powm (y, y, a2, n);
274 if (!mpz_cmp_ui (y, 1))
275 goto leave; /* Not a prime. */
276 }
277 if (mpz_cmp (y, nminus1))
278 goto leave; /* Not a prime. */
279 }
280 }
281 rc = 1; /* May be a prime. */
282
283leave:
284 mpz_clear (x);
285 mpz_clear (y);
286 mpz_clear (z);
287 mpz_clear (nminus1);
288 mpz_clear (q);
289 mpz_clear (a2);
290
291 return rc;
292}
293
294static void
295gen_prime (mpz_t ptest, unsigned int nbits, GNUNET_HashCode * hc)
296{
297 mpz_t prime, pminus1, val_2, val_3, result;
298 int i;
299 unsigned x, step;
300 int *mods;
301 mpz_t tmp;
302
303 GNUNET_assert (nbits >= 16);
304
305 mods = GNUNET_malloc (no_of_small_prime_numbers * sizeof (*mods));
306 /* Make nbits fit into mpz_t implementation. */
307 mpz_init_set_ui (val_2, 2);
308 mpz_init_set_ui (val_3, 3);
309 mpz_init (prime);
310 mpz_init (result);
311 mpz_init (pminus1);
312 mpz_init (ptest);
313 while (1)
314 {
315 /* generate a random number */
316 mpz_randomize (prime, nbits, hc);
317 /* Set high order bit to 1, set low order bit to 1. If we are
318 generating a secret prime we are most probably doing that
319 for RSA, to make sure that the modulus does have the
320 requested key size we set the 2 high order bits. */
321 set_highbit (prime, nbits - 1);
322 mpz_setbit (prime, nbits - 2);
323 mpz_setbit (prime, 0);
324
325 /* Calculate all remainders. */
326 mpz_init (tmp);
327 for (i = 0; (x = small_prime_numbers[i]); i++)
328 mods[i] = mpz_fdiv_r_ui (tmp, prime, x);
329 mpz_clear (tmp);
330 /* Now try some primes starting with prime. */
331 for (step = 0; step < 20000; step += 2)
332 {
333 /* Check against all the small primes we have in mods. */
334 for (i = 0; (x = small_prime_numbers[i]); i++)
335 {
336 while (mods[i] + step >= x)
337 mods[i] -= x;
338 if (!(mods[i] + step))
339 break;
340 }
341 if (x)
342 continue; /* Found a multiple of an already known prime. */
343
344 mpz_add_ui (ptest, prime, step);
345 if (!mpz_tstbit (ptest, nbits - 2))
346 break;
347
348 /* Do a fast Fermat test now. */
349 mpz_sub_ui (pminus1, ptest, 1);
350 mpz_powm (result, val_2, pminus1, ptest);
351 if ((!mpz_cmp_ui (result, 1)) && (is_prime (ptest, 5, hc)))
352 {
353 /* Got it. */
354 mpz_clear (val_2);
355 mpz_clear (val_3);
356 mpz_clear (result);
357 mpz_clear (pminus1);
358 mpz_clear (prime);
359 GNUNET_free (mods);
360 return;
361 }
362 }
363 }
364}
365
366/**
367 * Find the greatest common divisor G of A and B.
368 * Return: 1 if this 1, 0 in all other cases
369 */
370static int
371test_gcd (mpz_t g, mpz_t xa, mpz_t xb)
372{
373 mpz_t a, b;
374
375 mpz_init_set (a, xa);
376 mpz_init_set (b, xb);
377
378 /* TAOCP Vol II, 4.5.2, Algorithm A */
379 while (mpz_cmp_ui (b, 0))
380 {
381 mpz_fdiv_r (g, a, b); /* g used as temorary variable */
382 mpz_set (a, b);
383 mpz_set (b, g);
384 }
385 mpz_set (g, a);
386
387 mpz_clear (a);
388 mpz_clear (b);
389 return (0 == mpz_cmp_ui (g, 1));
390}
391
392/**
393 * Generate a key pair with a key of size NBITS.
394 * @param sk where to store the key
395 * @param nbits the number of bits to use
396 * @param hc the HC to use for PRNG (modified!)
397 */
398static void
399generate_kblock_key (KBlock_secret_key * sk,
400 unsigned int nbits, GNUNET_HashCode * hc)
401{
402 mpz_t t1, t2;
403 mpz_t phi; /* helper: (p-1)(q-1) */
404 mpz_t g;
405 mpz_t f;
406
407 /* make sure that nbits is even so that we generate p, q of equal size */
408 if ((nbits & 1))
409 nbits++;
410
411 mpz_init_set_ui (sk->e, 257);
412 mpz_init (sk->n);
413 mpz_init (sk->p);
414 mpz_init (sk->q);
415 mpz_init (sk->d);
416 mpz_init (sk->u);
417
418 mpz_init (t1);
419 mpz_init (t2);
420 mpz_init (phi);
421 mpz_init (g);
422 mpz_init (f);
423
424 do
425 {
426 do
427 {
428 mpz_clear (sk->p);
429 mpz_clear (sk->q);
430 gen_prime (sk->p, nbits / 2, hc);
431 gen_prime (sk->q, nbits / 2, hc);
432
433 if (mpz_cmp (sk->p, sk->q) > 0) /* p shall be smaller than q (for calc of u) */
434 mpz_swap (sk->p, sk->q);
435 /* calculate the modulus */
436 mpz_mul (sk->n, sk->p, sk->q);
437 }
438 while (get_nbits (sk->n) != nbits);
439
440 /* calculate Euler totient: phi = (p-1)(q-1) */
441 mpz_sub_ui (t1, sk->p, 1);
442 mpz_sub_ui (t2, sk->q, 1);
443 mpz_mul (phi, t1, t2);
444 mpz_gcd (g, t1, t2);
445 mpz_fdiv_q (f, phi, g);
446
447 while (0 == test_gcd (t1, sk->e, phi))
448 { /* (while gcd is not 1) */
449 mpz_add_ui (sk->e, sk->e, 2);
450 }
451
452 /* calculate the secret key d = e^1 mod phi */
453 }
454 while ((0 == mpz_invert (sk->d, sk->e, f)) ||
455 (0 == mpz_invert (sk->u, sk->p, sk->q)));
456
457 mpz_clear (t1);
458 mpz_clear (t2);
459 mpz_clear (phi);
460 mpz_clear (f);
461 mpz_clear (g);
462}
463
464
465/**
466 * Internal representation of the private key.
467 */
468struct KskRsaPrivateKeyBinaryEncoded
469{
470 /**
471 * Total size of the structure, in bytes, in big-endian!
472 */
473 uint16_t len GNUNET_PACKED;
474 uint16_t sizen GNUNET_PACKED; /* in big-endian! */
475 uint16_t sizee GNUNET_PACKED; /* in big-endian! */
476 uint16_t sized GNUNET_PACKED; /* in big-endian! */
477 uint16_t sizep GNUNET_PACKED; /* in big-endian! */
478 uint16_t sizeq GNUNET_PACKED; /* in big-endian! */
479 uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */
480 uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */
481 /* followed by the actual values */
482};
483
484
485/**
486 * Deterministically (!) create a hostkey using only the
487 * given HashCode as input to the PRNG.
488 */
489static struct KskRsaPrivateKeyBinaryEncoded *
490makeKblockKeyInternal (const GNUNET_HashCode * hc)
491{
492 KBlock_secret_key sk;
493 GNUNET_HashCode hx;
494 void *pbu[6];
495 mpz_t *pkv[6];
496 size_t sizes[6];
497 struct KskRsaPrivateKeyBinaryEncoded *retval;
498 int i;
499 size_t size;
500
501 hx = *hc;
502 generate_kblock_key (&sk, 1024, /* at least 10x as fast than 2048 bits
503 -- we simply cannot afford 2048 bits
504 even on modern hardware, and especially
505 not since clearly a dictionary attack
506 will still be much cheaper
507 than breaking a 1024 bit RSA key.
508 If an adversary can spend the time to
509 break a 1024 bit RSA key just to forge
510 a signature -- SO BE IT. [ CG, 6/2005 ] */
511 &hx);
512 pkv[0] = &sk.n;
513 pkv[1] = &sk.e;
514 pkv[2] = &sk.d;
515 pkv[3] = &sk.p;
516 pkv[4] = &sk.q;
517 pkv[5] = &sk.u;
518 size = sizeof (struct KskRsaPrivateKeyBinaryEncoded);
519 for (i = 0; i < 6; i++)
520 {
521 pbu[i] = mpz_export (NULL, &sizes[i], 1, /* most significant word first */
522 1, /* unit is bytes */
523 1, /* big endian */
524 0, /* nails */
525 *pkv[i]);
526 size += sizes[i];
527 }
528 GNUNET_assert (size < 65536);
529 retval = GNUNET_malloc (size);
530 retval->len = htons (size);
531 i = 0;
532 retval->sizen = htons (sizes[0]);
533 memcpy (&((char *) &retval[1])[i], pbu[0], sizes[0]);
534 i += sizes[0];
535 retval->sizee = htons (sizes[1]);
536 memcpy (&((char *) &retval[1])[i], pbu[1], sizes[1]);
537 i += sizes[1];
538 retval->sized = htons (sizes[2]);
539 memcpy (&((char *) &retval[1])[i], pbu[2], sizes[2]);
540 i += sizes[2];
541 /* swap p and q! */
542 retval->sizep = htons (sizes[4]);
543 memcpy (&((char *) &retval[1])[i], pbu[4], sizes[4]);
544 i += sizes[4];
545 retval->sizeq = htons (sizes[3]);
546 memcpy (&((char *) &retval[1])[i], pbu[3], sizes[3]);
547 i += sizes[3];
548 retval->sizedmp1 = htons (0);
549 retval->sizedmq1 = htons (0);
550 memcpy (&((char *) &retval[1])[i], pbu[5], sizes[5]);
551 for (i = 0; i < 6; i++)
552 {
553 mpz_clear (*pkv[i]);
554 free (pbu[i]);
555 }
556 return retval;
557}
558
559
560/**
561 * Decode the internal format into the format used
562 * by libgcrypt.
563 */
564static struct GNUNET_CRYPTO_RsaPrivateKey *
565ksk_decode_key (const struct KskRsaPrivateKeyBinaryEncoded *encoding)
566{
567 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
568 gcry_sexp_t res;
569 gcry_mpi_t n, e, d, p, q, u;
570 int rc;
571 size_t size;
572 int pos;
573
574 pos = 0;
575 size = ntohs (encoding->sizen);
576 rc = gcry_mpi_scan (&n,
577 GCRYMPI_FMT_USG,
578 &((const unsigned char *) (&encoding[1]))[pos],
579 size, &size);
580 pos += ntohs (encoding->sizen);
581 if (rc)
582 {
583 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
584 return NULL;
585 }
586 size = ntohs (encoding->sizee);
587 rc = gcry_mpi_scan (&e,
588 GCRYMPI_FMT_USG,
589 &((const unsigned char *) (&encoding[1]))[pos],
590 size, &size);
591 pos += ntohs (encoding->sizee);
592 if (rc)
593 {
594 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
595 gcry_mpi_release (n);
596 return NULL;
597 }
598 size = ntohs (encoding->sized);
599 rc = gcry_mpi_scan (&d,
600 GCRYMPI_FMT_USG,
601 &((const unsigned char *) (&encoding[1]))[pos],
602 size, &size);
603 pos += ntohs (encoding->sized);
604 if (rc)
605 {
606 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
607 gcry_mpi_release (n);
608 gcry_mpi_release (e);
609 return NULL;
610 }
611 /* swap p and q! */
612 size = ntohs (encoding->sizep);
613 if (size > 0)
614 {
615 rc = gcry_mpi_scan (&q,
616 GCRYMPI_FMT_USG,
617 &((const unsigned char *) (&encoding[1]))[pos],
618 size, &size);
619 pos += ntohs (encoding->sizep);
620 if (rc)
621 {
622 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
623 gcry_mpi_release (n);
624 gcry_mpi_release (e);
625 gcry_mpi_release (d);
626 return NULL;
627 }
628 }
629 else
630 q = NULL;
631 size = ntohs (encoding->sizeq);
632 if (size > 0)
633 {
634 rc = gcry_mpi_scan (&p,
635 GCRYMPI_FMT_USG,
636 &((const unsigned char *) (&encoding[1]))[pos],
637 size, &size);
638 pos += ntohs (encoding->sizeq);
639 if (rc)
640 {
641 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
642 gcry_mpi_release (n);
643 gcry_mpi_release (e);
644 gcry_mpi_release (d);
645 if (q != NULL)
646 gcry_mpi_release (q);
647 return NULL;
648 }
649 }
650 else
651 p = NULL;
652 pos += ntohs (encoding->sizedmp1);
653 pos += ntohs (encoding->sizedmq1);
654 size =
655 ntohs (encoding->len) - sizeof (struct KskRsaPrivateKeyBinaryEncoded) -
656 pos;
657 if (size > 0)
658 {
659 rc = gcry_mpi_scan (&u,
660 GCRYMPI_FMT_USG,
661 &((const unsigned char *) (&encoding[1]))[pos],
662 size, &size);
663 if (rc)
664 {
665 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
666 gcry_mpi_release (n);
667 gcry_mpi_release (e);
668 gcry_mpi_release (d);
669 if (p != NULL)
670 gcry_mpi_release (p);
671 if (q != NULL)
672 gcry_mpi_release (q);
673 return NULL;
674 }
675 }
676 else
677 u = NULL;
678
679 if ((p != NULL) && (q != NULL) && (u != NULL))
680 {
681 rc = gcry_sexp_build (&res, &size, /* erroff */
682 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))",
683 n, e, d, p, q, u);
684 }
685 else
686 {
687 if ((p != NULL) && (q != NULL))
688 {
689 rc = gcry_sexp_build (&res, &size, /* erroff */
690 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))",
691 n, e, d, p, q);
692 }
693 else
694 {
695 rc = gcry_sexp_build (&res, &size, /* erroff */
696 "(private-key(rsa(n %m)(e %m)(d %m)))",
697 n, e, d);
698 }
699 }
700 gcry_mpi_release (n);
701 gcry_mpi_release (e);
702 gcry_mpi_release (d);
703 if (p != NULL)
704 gcry_mpi_release (p);
705 if (q != NULL)
706 gcry_mpi_release (q);
707 if (u != NULL)
708 gcry_mpi_release (u);
709
710 if (rc)
711 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
712#if EXTRA_CHECKS
713 if (gcry_pk_testkey (res))
714 {
715 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
716 return NULL;
717 }
718#endif
719 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
720 ret->sexp = res;
721 return ret;
722}
723
724
725
726
727typedef struct
728{
729 GNUNET_HashCode hc;
730 struct KskRsaPrivateKeyBinaryEncoded *pke;
731} KBlockKeyCacheLine;
732
733static KBlockKeyCacheLine **cache;
734static unsigned int cacheSize;
735
736/**
737 * Deterministically (!) create a hostkey using only the
738 * given HashCode as input to the PRNG.
739 */
740struct GNUNET_CRYPTO_RsaPrivateKey *
741GNUNET_CRYPTO_rsa_key_create_from_hash (const GNUNET_HashCode * hc)
742{
743 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
744 KBlockKeyCacheLine *line;
745 int i;
746
747 for (i = 0; i < cacheSize; i++)
748 {
749 if (0 == memcmp (hc, &cache[i]->hc, sizeof (GNUNET_HashCode)))
750 {
751 ret = ksk_decode_key (cache[i]->pke);
752 return ret;
753 }
754 }
755
756 line = GNUNET_malloc (sizeof (KBlockKeyCacheLine));
757 line->hc = *hc;
758 line->pke = makeKblockKeyInternal (hc);
759 GNUNET_array_grow (cache, cacheSize, cacheSize + 1);
760 cache[cacheSize - 1] = line;
761 return ksk_decode_key (line->pke);
762}
763
764void __attribute__ ((constructor)) GNUNET_CRYPTO_ksk_init ()
765{
766 gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
767 if (!gcry_check_version (GCRYPT_VERSION))
768 {
769 fprintf (stderr,
770 _
771 ("libgcrypt has not the expected version (version %s is required).\n"),
772 GCRYPT_VERSION);
773 abort ();
774 }
775#ifdef gcry_fast_random_poll
776 gcry_fast_random_poll ();
777#endif
778}
779
780void __attribute__ ((destructor)) GNUNET_CRYPTO_ksk_fini ()
781{
782 int i;
783 for (i = 0; i < cacheSize; i++)
784 {
785 GNUNET_free (cache[i]->pke);
786 GNUNET_free (cache[i]);
787 }
788 GNUNET_array_grow (cache, cacheSize, 0);
789}
790
791/* end of kblockkey.c */
diff --git a/src/util/crypto_random.c b/src/util/crypto_random.c
new file mode 100644
index 000000000..3f7ac4dd3
--- /dev/null
+++ b/src/util/crypto_random.c
@@ -0,0 +1,136 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @file util/crypto_random.c
24 * @brief functions to gather random numbers
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_crypto_lib.h"
30#include <gcrypt.h>
31
32/**
33 * @return a random value in the interval [0,i[.
34 */
35unsigned int
36GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality mode, unsigned int i)
37{
38#ifdef gcry_fast_random_poll
39 static unsigned int invokeCount;
40#endif
41 unsigned int ret;
42
43 GNUNET_assert (i > 0);
44
45 if (mode == GNUNET_CRYPTO_QUALITY_STRONG)
46 {
47 /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
48#ifdef gcry_fast_random_poll
49 if ((invokeCount++ % 256) == 0)
50 gcry_fast_random_poll ();
51#endif
52 ret = rand (); /* in case gcry_randomize fails,
53 we at least get a pseudo-
54 random number this way */
55 gcry_randomize ((unsigned char *) &ret,
56 sizeof (unsigned int), GCRY_STRONG_RANDOM);
57 return ret % i;
58 }
59 else
60 {
61 ret = i * ((double) RANDOM () / RAND_MAX);
62 if (ret >= i)
63 ret = i - 1;
64 return ret;
65 }
66}
67
68
69/**
70 * Get an array with a random permutation of the
71 * numbers 0...n-1.
72 * @param mode GNUNET_RANDOM_QUALITY_STRONG if the strong (but expensive)
73 * PRNG should be used, GNUNET_RANDOM_QUALITY_WEAK otherwise
74 * @param n the size of the array
75 * @return the permutation array (allocated from heap)
76 */
77unsigned int *
78GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode, unsigned int n)
79{
80 unsigned int *ret;
81 unsigned int i;
82 unsigned int tmp;
83 unsigned int x;
84
85 GNUNET_assert (n > 0);
86 ret = GNUNET_malloc (n * sizeof (int));
87 for (i = 0; i < n; i++)
88 ret[i] = i;
89 for (i = 0; i < n; i++)
90 {
91 x = GNUNET_CRYPTO_random_u32 (mode, n);
92 tmp = ret[x];
93 ret[x] = ret[i];
94 ret[i] = tmp;
95 }
96 return ret;
97}
98
99/**
100 * Random on unsigned 64-bit values.
101 */
102unsigned long long
103GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode,
104 unsigned long long u)
105{
106 unsigned long long ret;
107
108 GNUNET_assert (u > 0);
109 if (mode == GNUNET_CRYPTO_QUALITY_STRONG)
110 {
111 gcry_randomize ((unsigned char *) &ret,
112 sizeof (unsigned long long), GCRY_STRONG_RANDOM);
113 return ret % u;
114 }
115 else
116 {
117 ret = u * ((double) RANDOM () / RAND_MAX);
118 if (ret >= u)
119 ret = u - 1;
120 return ret;
121 }
122}
123
124/**
125 * This function should only be called in testcases
126 * where strong entropy gathering is not desired
127 * (for example, for hostkey generation).
128 */
129void
130GNUNET_CRYPTO_random_disable_entropy_gathering ()
131{
132 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
133}
134
135
136/* end of crypto_random.c */
diff --git a/src/util/crypto_rsa.c b/src/util/crypto_rsa.c
new file mode 100644
index 000000000..b61729e06
--- /dev/null
+++ b/src/util/crypto_rsa.c
@@ -0,0 +1,948 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/crypto_rsa.c
23 * @brief public key cryptography (RSA) with libgcrypt
24 * @author Christian Grothoff
25 *
26 * Note that the code locks often needlessly on the gcrypt-locking api.
27 * One would think that simple MPI operations should not require locking
28 * (since only global operations on the random pool must be locked,
29 * strictly speaking). But libgcrypt does sometimes require locking in
30 * unexpected places, so the safe solution is to always lock even if it
31 * is not required. The performance impact is minimal anyway.
32 */
33
34#include "platform.h"
35#include <gcrypt.h>
36#include "gnunet_common.h"
37#include "gnunet_crypto_lib.h"
38#include "gnunet_disk_lib.h"
39
40/**
41 * The private information of an RSA key pair.
42 * NOTE: this must match the definition in crypto_ksk.c
43 */
44struct GNUNET_CRYPTO_RsaPrivateKey
45{
46 gcry_sexp_t sexp;
47};
48
49
50/**
51 * GNUnet mandates a certain format for the encoding
52 * of private RSA key information that is provided
53 * by the RSA implementations. This format is used
54 * to serialize a private RSA key (typically when
55 * writing it to disk).
56 */
57struct RsaPrivateKeyBinaryEncoded
58{
59 /**
60 * Total size of the structure, in bytes, in big-endian!
61 */
62 uint16_t len GNUNET_PACKED;
63 uint16_t sizen GNUNET_PACKED; /* in big-endian! */
64 uint16_t sizee GNUNET_PACKED; /* in big-endian! */
65 uint16_t sized GNUNET_PACKED; /* in big-endian! */
66 uint16_t sizep GNUNET_PACKED; /* in big-endian! */
67 uint16_t sizeq GNUNET_PACKED; /* in big-endian! */
68 uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */
69 uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */
70 /* followed by the actual values */
71};
72
73
74#define HOSTKEY_LEN 2048
75
76#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS
77
78
79/**
80 * Log an error message at log-level 'level' that indicates
81 * a failure of the command 'cmd' with the message given
82 * by gcry_strerror(rc).
83 */
84#define LOG_GCRY(level, cmd, rc) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0);
85
86/**
87 * If target != size, move target bytes to the
88 * end of the size-sized buffer and zero out the
89 * first target-size bytes.
90 */
91static void
92adjust (unsigned char *buf, size_t size, size_t target)
93{
94 if (size < target)
95 {
96 memmove (&buf[target - size], buf, size);
97 memset (buf, 0, target - size);
98 }
99}
100
101/**
102 * This HostKey implementation uses RSA.
103 */
104struct GNUNET_CRYPTO_RsaPrivateKey *
105GNUNET_CRYPTO_rsa_key_create ()
106{
107 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
108 gcry_sexp_t s_key;
109 gcry_sexp_t s_keyparam;
110
111 GNUNET_assert (0 == gcry_sexp_build (&s_keyparam,
112 NULL,
113 "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))",
114 HOSTKEY_LEN));
115 GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam));
116 gcry_sexp_release (s_keyparam);
117#if EXTRA_CHECKS
118 GNUNET_assert (0 == gcry_pk_testkey (s_key));
119#endif
120 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
121 ret->sexp = s_key;
122 return ret;
123}
124
125/**
126 * Free memory occupied by hostkey
127 */
128void
129GNUNET_CRYPTO_rsa_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
130{
131 gcry_sexp_release (hostkey->sexp);
132 GNUNET_free (hostkey);
133}
134
135static int
136key_from_sexp (gcry_mpi_t * array,
137 gcry_sexp_t sexp, const char *topname, const char *elems)
138{
139 gcry_sexp_t list, l2;
140 const char *s;
141 int i, idx;
142
143 list = gcry_sexp_find_token (sexp, topname, 0);
144 if (!list)
145 {
146 return 1;
147 }
148 l2 = gcry_sexp_cadr (list);
149 gcry_sexp_release (list);
150 list = l2;
151 if (!list)
152 {
153 return 2;
154 }
155
156 idx = 0;
157 for (s = elems; *s; s++, idx++)
158 {
159 l2 = gcry_sexp_find_token (list, s, 1);
160 if (!l2)
161 {
162 for (i = 0; i < idx; i++)
163 {
164 gcry_free (array[i]);
165 array[i] = NULL;
166 }
167 gcry_sexp_release (list);
168 return 3; /* required parameter not found */
169 }
170 array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
171 gcry_sexp_release (l2);
172 if (!array[idx])
173 {
174 for (i = 0; i < idx; i++)
175 {
176 gcry_free (array[i]);
177 array[i] = NULL;
178 }
179 gcry_sexp_release (list);
180 return 4; /* required parameter is invalid */
181 }
182 }
183 gcry_sexp_release (list);
184 return 0;
185}
186
187/**
188 * Extract the public key of the host.
189 * @param hostkey the hostkey to extract into the result.
190 * @param result where to write the result.
191 */
192void
193GNUNET_CRYPTO_rsa_key_get_public (const struct GNUNET_CRYPTO_RsaPrivateKey
194 *hostkey,
195 struct
196 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
197 *result)
198{
199 gcry_mpi_t skey[2];
200 size_t size;
201 int rc;
202
203 rc = key_from_sexp (skey, hostkey->sexp, "public-key", "ne");
204 if (rc)
205 rc = key_from_sexp (skey, hostkey->sexp, "private-key", "ne");
206 if (rc)
207 rc = key_from_sexp (skey, hostkey->sexp, "rsa", "ne");
208 GNUNET_assert (0 == rc);
209 result->len =
210 htons (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) -
211 sizeof (result->padding));
212 result->sizen = htons (GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH);
213 result->padding = 0;
214 size = GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
215 GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
216 &result->key[0], size, &size, skey[0]));
217 adjust (&result->key[0], size, GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH);
218 size =
219 GNUNET_CRYPTO_RSA_KEY_LENGTH - GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
220 GNUNET_assert (0 ==
221 gcry_mpi_print (GCRYMPI_FMT_USG,
222 &result->
223 key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH],
224 size, &size, skey[1]));
225 adjust (&result->key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH], size,
226 GNUNET_CRYPTO_RSA_KEY_LENGTH -
227 GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH);
228 gcry_mpi_release (skey[0]);
229 gcry_mpi_release (skey[1]);
230}
231
232
233/**
234 * Internal: publicKey => RSA-Key.
235 *
236 * Note that the return type is not actually a private
237 * key but rather an sexpression for the public key!
238 */
239static struct GNUNET_CRYPTO_RsaPrivateKey *
240public2PrivateKey (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
241 *publicKey)
242{
243 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
244 gcry_sexp_t result;
245 gcry_mpi_t n;
246 gcry_mpi_t e;
247 size_t size;
248 size_t erroff;
249 int rc;
250
251 if ((ntohs (publicKey->sizen) != GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH) ||
252 (ntohs (publicKey->len) !=
253 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) -
254 sizeof (publicKey->padding)))
255 {
256 GNUNET_break (0);
257 return NULL;
258 }
259 size = GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
260 rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &publicKey->key[0], size, &size);
261 if (rc)
262 {
263 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
264 return NULL;
265 }
266 size =
267 GNUNET_CRYPTO_RSA_KEY_LENGTH - GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
268 rc =
269 gcry_mpi_scan (&e, GCRYMPI_FMT_USG,
270 &publicKey->key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH],
271 size, &size);
272 if (rc)
273 {
274 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
275 gcry_mpi_release (n);
276 return NULL;
277 }
278 rc = gcry_sexp_build (&result,
279 &erroff, "(public-key(rsa(n %m)(e %m)))", n, e);
280 gcry_mpi_release (n);
281 gcry_mpi_release (e);
282 if (rc)
283 {
284 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */
285 return NULL;
286 }
287 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
288 ret->sexp = result;
289 return ret;
290}
291
292
293/**
294 * Encode the private key in a format suitable for
295 * storing it into a file.
296 * @returns encoding of the private key.
297 * The first 4 bytes give the size of the array, as usual.
298 */
299static struct RsaPrivateKeyBinaryEncoded *
300rsa_encode_key (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
301{
302 struct RsaPrivateKeyBinaryEncoded *retval;
303 gcry_mpi_t pkv[6];
304 void *pbu[6];
305 size_t sizes[6];
306 int rc;
307 int i;
308 int size;
309
310#if EXTRA_CHECKS
311 if (gcry_pk_testkey (hostkey->sexp))
312 {
313 GNUNET_break (0);
314 return NULL;
315 }
316#endif
317
318 memset (pkv, 0, sizeof (gcry_mpi_t) * 6);
319 rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpqu");
320 if (rc)
321 rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpqu");
322 if (rc)
323 rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpq");
324 if (rc)
325 rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpq");
326 if (rc)
327 rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "ned");
328 if (rc)
329 rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "ned");
330 GNUNET_assert (0 == rc);
331 size = sizeof (struct RsaPrivateKeyBinaryEncoded);
332 for (i = 0; i < 6; i++)
333 {
334 if (pkv[i] != NULL)
335 {
336 GNUNET_assert (0 == gcry_mpi_aprint (GCRYMPI_FMT_USG,
337 (unsigned char **) &pbu[i],
338 &sizes[i], pkv[i]));
339 size += sizes[i];
340 }
341 else
342 {
343 pbu[i] = NULL;
344 sizes[i] = 0;
345 }
346 }
347 GNUNET_assert (size < 65536);
348 retval = GNUNET_malloc (size);
349 retval->len = htons (size);
350 i = 0;
351 retval->sizen = htons (sizes[0]);
352 memcpy (&((char *) (&retval[1]))[i], pbu[0], sizes[0]);
353 i += sizes[0];
354 retval->sizee = htons (sizes[1]);
355 memcpy (&((char *) (&retval[1]))[i], pbu[1], sizes[1]);
356 i += sizes[1];
357 retval->sized = htons (sizes[2]);
358 memcpy (&((char *) (&retval[1]))[i], pbu[2], sizes[2]);
359 i += sizes[2];
360 /* swap p and q! */
361 retval->sizep = htons (sizes[4]);
362 memcpy (&((char *) (&retval[1]))[i], pbu[4], sizes[4]);
363 i += sizes[4];
364 retval->sizeq = htons (sizes[3]);
365 memcpy (&((char *) (&retval[1]))[i], pbu[3], sizes[3]);
366 i += sizes[3];
367 retval->sizedmp1 = htons (0);
368 retval->sizedmq1 = htons (0);
369 memcpy (&((char *) (&retval[1]))[i], pbu[5], sizes[5]);
370 for (i = 0; i < 6; i++)
371 {
372 if (pkv[i] != NULL)
373 gcry_mpi_release (pkv[i]);
374 if (pbu[i] != NULL)
375 free (pbu[i]);
376 }
377 return retval;
378}
379
380/**
381 * Decode the private key from the file-format back
382 * to the "normal", internal format.
383 */
384static struct GNUNET_CRYPTO_RsaPrivateKey *
385rsa_decode_key (const struct RsaPrivateKeyBinaryEncoded *encoding)
386{
387 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
388 gcry_sexp_t res;
389 gcry_mpi_t n, e, d, p, q, u;
390 int rc;
391 size_t size;
392 int pos;
393
394 pos = 0;
395 size = ntohs (encoding->sizen);
396 rc = gcry_mpi_scan (&n,
397 GCRYMPI_FMT_USG,
398 &((const unsigned char *) (&encoding[1]))[pos],
399 size, &size);
400 pos += ntohs (encoding->sizen);
401 if (rc)
402 {
403 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
404 return NULL;
405 }
406 size = ntohs (encoding->sizee);
407 rc = gcry_mpi_scan (&e,
408 GCRYMPI_FMT_USG,
409 &((const unsigned char *) (&encoding[1]))[pos],
410 size, &size);
411 pos += ntohs (encoding->sizee);
412 if (rc)
413 {
414 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
415 gcry_mpi_release (n);
416 return NULL;
417 }
418 size = ntohs (encoding->sized);
419 rc = gcry_mpi_scan (&d,
420 GCRYMPI_FMT_USG,
421 &((const unsigned char *) (&encoding[1]))[pos],
422 size, &size);
423 pos += ntohs (encoding->sized);
424 if (rc)
425 {
426 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
427 gcry_mpi_release (n);
428 gcry_mpi_release (e);
429 return NULL;
430 }
431 /* swap p and q! */
432 size = ntohs (encoding->sizep);
433 if (size > 0)
434 {
435 rc = gcry_mpi_scan (&q,
436 GCRYMPI_FMT_USG,
437 &((const unsigned char *) (&encoding[1]))[pos],
438 size, &size);
439 pos += ntohs (encoding->sizep);
440 if (rc)
441 {
442 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
443 gcry_mpi_release (n);
444 gcry_mpi_release (e);
445 gcry_mpi_release (d);
446 return NULL;
447 }
448 }
449 else
450 q = NULL;
451 size = ntohs (encoding->sizeq);
452 if (size > 0)
453 {
454 rc = gcry_mpi_scan (&p,
455 GCRYMPI_FMT_USG,
456 &((const unsigned char *) (&encoding[1]))[pos],
457 size, &size);
458 pos += ntohs (encoding->sizeq);
459 if (rc)
460 {
461 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
462 gcry_mpi_release (n);
463 gcry_mpi_release (e);
464 gcry_mpi_release (d);
465 if (q != NULL)
466 gcry_mpi_release (q);
467 return NULL;
468 }
469 }
470 else
471 p = NULL;
472 pos += ntohs (encoding->sizedmp1);
473 pos += ntohs (encoding->sizedmq1);
474 size =
475 ntohs (encoding->len) - sizeof (struct RsaPrivateKeyBinaryEncoded) - pos;
476 if (size > 0)
477 {
478 rc = gcry_mpi_scan (&u,
479 GCRYMPI_FMT_USG,
480 &((const unsigned char *) (&encoding[1]))[pos],
481 size, &size);
482 if (rc)
483 {
484 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
485 gcry_mpi_release (n);
486 gcry_mpi_release (e);
487 gcry_mpi_release (d);
488 if (p != NULL)
489 gcry_mpi_release (p);
490 if (q != NULL)
491 gcry_mpi_release (q);
492 return NULL;
493 }
494 }
495 else
496 u = NULL;
497
498 if ((p != NULL) && (q != NULL) && (u != NULL))
499 {
500 rc = gcry_sexp_build (&res, &size, /* erroff */
501 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))",
502 n, e, d, p, q, u);
503 }
504 else
505 {
506 if ((p != NULL) && (q != NULL))
507 {
508 rc = gcry_sexp_build (&res, &size, /* erroff */
509 "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))",
510 n, e, d, p, q);
511 }
512 else
513 {
514 rc = gcry_sexp_build (&res, &size, /* erroff */
515 "(private-key(rsa(n %m)(e %m)(d %m)))",
516 n, e, d);
517 }
518 }
519 gcry_mpi_release (n);
520 gcry_mpi_release (e);
521 gcry_mpi_release (d);
522 if (p != NULL)
523 gcry_mpi_release (p);
524 if (q != NULL)
525 gcry_mpi_release (q);
526 if (u != NULL)
527 gcry_mpi_release (u);
528
529 if (rc)
530 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
531#if EXTRA_CHECKS
532 if (gcry_pk_testkey (res))
533 {
534 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
535 return NULL;
536 }
537#endif
538 ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
539 ret->sexp = res;
540 return ret;
541}
542
543
544/**
545 * Create a new private key by reading it from a file. If the
546 * files does not exist, create a new key and write it to the
547 * file. Caller must free return value. Note that this function
548 * can not guarantee that another process might not be trying
549 * the same operation on the same file at the same time. The
550 * caller must somehow know that the file either already exists
551 * with a valid key OR be sure that no other process is calling
552 * this function at the same time. If the contents of the file
553 * are invalid the old file is deleted and a fresh key is
554 * created.
555 *
556 * @return new private key, NULL on error (for example,
557 * permission denied)
558 */
559struct GNUNET_CRYPTO_RsaPrivateKey *
560GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename)
561{
562 struct flock fl;
563 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
564 struct RsaPrivateKeyBinaryEncoded *enc;
565 struct stat sbuf;
566 uint16_t len;
567 int fd;
568 unsigned int cnt;
569 int ec;
570
571 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
572 return NULL;
573 while (0 != STAT (filename, &sbuf))
574 {
575 fd = open (filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
576 if (-1 == fd)
577 {
578 if (errno == EEXIST)
579 continue;
580 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
581 "open", filename);
582 return NULL;
583 }
584 memset (&fl, 0, sizeof (struct flock));
585 fl.l_type = F_WRLCK;
586 fl.l_whence = SEEK_SET;
587 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
588 cnt = 0;
589 while (0 != fcntl (fd, F_SETLK, &fl))
590 {
591 sleep (1);
592 if (0 == ++cnt % 10)
593 {
594 ec = errno;
595 fl.l_type = F_GETLK;
596 fcntl (fd, F_GETLK, &fl);
597 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598 _
599 ("Could not aquire lock on file `%s' due to process %u: %s...\n"),
600 filename, fl.l_pid, STRERROR (errno));
601 }
602 memset (&fl, 0, sizeof (struct flock));
603 fl.l_type = F_WRLCK;
604 fl.l_whence = SEEK_SET;
605 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
606 }
607 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
608 _("Creating a new private key. This may take a while.\n"));
609 ret = GNUNET_CRYPTO_rsa_key_create ();
610 GNUNET_assert (ret != NULL);
611 enc = rsa_encode_key (ret);
612 GNUNET_assert (enc != NULL);
613 GNUNET_assert (ntohs (enc->len) == WRITE (fd, enc, ntohs (enc->len)));
614 GNUNET_free (enc);
615 fdatasync (fd);
616 memset (&fl, 0, sizeof (struct flock));
617 fl.l_type = F_UNLCK;
618 fl.l_whence = SEEK_SET;
619 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
620 cnt = 0;
621 if (0 != fcntl (fd, F_SETLK, &fl))
622 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
623 "fcntl", filename);
624 GNUNET_assert (0 == CLOSE (fd));
625 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
626 _("Stored new private key in `%s'.\n"), filename);
627 return ret;
628 }
629 /* hostkey file exists already, read it! */
630 fd = open (filename, O_RDONLY);
631 if (-1 == fd)
632 {
633 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
634 return NULL;
635 }
636 cnt = 0;
637 while (1)
638 {
639 memset (&fl, 0, sizeof (struct flock));
640 fl.l_type = F_RDLCK;
641 fl.l_whence = SEEK_SET;
642 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
643 if (0 != fcntl (fd, F_SETLK, &fl))
644 {
645 if (0 == ++cnt % 10)
646 {
647 ec = errno;
648 fl.l_type = F_GETLK;
649 fcntl (fd, F_GETLK, &fl);
650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651 _
652 ("Could not aquire lock on file `%s' due to process %u: %s...\n"),
653 filename, fl.l_pid, STRERROR (errno));
654 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
655 _
656 ("This may be ok if someone is currently generating a hostkey.\n"));
657 }
658 sleep (1);
659 continue;
660 }
661 if (0 != STAT (filename, &sbuf))
662 {
663 /* eh, what!? File we opened is now gone!? */
664 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
665 "stat", filename);
666 memset (&fl, 0, sizeof (struct flock));
667 fl.l_type = F_UNLCK;
668 fl.l_whence = SEEK_SET;
669 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
670 if (0 != fcntl (fd, F_SETLK, &fl))
671 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
672 "fcntl", filename);
673 GNUNET_assert (0 == CLOSE (fd));
674 return NULL;
675 }
676 if (sbuf.st_size < sizeof (struct RsaPrivateKeyBinaryEncoded))
677 {
678 /* maybe we got the read lock before the hostkey generating
679 process had a chance to get the write lock; give it up! */
680 memset (&fl, 0, sizeof (struct flock));
681 fl.l_type = F_UNLCK;
682 fl.l_whence = SEEK_SET;
683 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
684 if (0 != fcntl (fd, F_SETLK, &fl))
685 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
686 "fcntl", filename);
687 if (0 == ++cnt % 10)
688 {
689 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
690 _
691 ("When trying to read hostkey file `%s' I found %u bytes but I need at least %u.\n"),
692 filename, (unsigned int) sbuf.st_size,
693 (unsigned int) sizeof (struct
694 RsaPrivateKeyBinaryEncoded));
695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
696 _
697 ("This may be ok if someone is currently generating a hostkey.\n"));
698 }
699 sleep (2); /* wait a bit longer! */
700 continue;
701 }
702 break;
703 }
704 enc = GNUNET_malloc (sbuf.st_size);
705 GNUNET_assert (sbuf.st_size == READ (fd, enc, sbuf.st_size));
706 len = ntohs (enc->len);
707 if ((len != sbuf.st_size) || (NULL == (ret = rsa_decode_key (enc))))
708 {
709 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
710 _
711 ("File `%s' does not contain a valid private key. You should delete it.\n"),
712 filename);
713 GNUNET_free (enc);
714 }
715 memset (&fl, 0, sizeof (struct flock));
716 fl.l_type = F_UNLCK;
717 fl.l_whence = SEEK_SET;
718 fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded);
719 if (0 != fcntl (fd, F_SETLK, &fl))
720 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
721 GNUNET_assert (0 == CLOSE (fd));
722 return ret;
723}
724
725
726/**
727 * Encrypt a block with the public key of another host that uses the
728 * same cyper.
729 *
730 * @param block the block to encrypt
731 * @param size the size of block
732 * @param publicKey the encoded public key used to encrypt
733 * @param target where to store the encrypted block
734 * @returns GNUNET_SYSERR on error, GNUNET_OK if ok
735 */
736int
737GNUNET_CRYPTO_rsa_encrypt (const void *block,
738 uint16_t size,
739 const struct
740 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey,
741 struct GNUNET_CRYPTO_RsaEncryptedData *target)
742{
743 gcry_sexp_t result;
744 gcry_sexp_t data;
745 struct GNUNET_CRYPTO_RsaPrivateKey *pubkey;
746 gcry_mpi_t val;
747 gcry_mpi_t rval;
748 size_t isize;
749 size_t erroff;
750
751 GNUNET_assert (size <= sizeof (GNUNET_HashCode));
752 pubkey = public2PrivateKey (publicKey);
753 isize = size;
754 GNUNET_assert (0 ==
755 gcry_mpi_scan (&val, GCRYMPI_FMT_USG, block, isize, &isize));
756 GNUNET_assert (0 ==
757 gcry_sexp_build (&data, &erroff,
758 "(data (flags pkcs1)(value %m))", val));
759 gcry_mpi_release (val);
760 GNUNET_assert (0 == gcry_pk_encrypt (&result, data, pubkey->sexp));
761 gcry_sexp_release (data);
762 GNUNET_CRYPTO_rsa_key_free (pubkey);
763
764 GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "a"));
765 gcry_sexp_release (result);
766 isize = sizeof (struct GNUNET_CRYPTO_RsaEncryptedData);
767 GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
768 (unsigned char *) target, isize, &isize,
769 rval));
770 gcry_mpi_release (rval);
771 adjust (&target->encoding[0], isize,
772 sizeof (struct GNUNET_CRYPTO_RsaEncryptedData));
773 return GNUNET_OK;
774}
775
776/**
777 * Decrypt a given block with the hostkey.
778 *
779 * @param hostkey the hostkey with which to decrypt this block
780 * @param block the data to decrypt, encoded as returned by encrypt
781 * @param result pointer to a location where the result can be stored
782 * @param max the maximum number of bits to store for the result, if
783 * the decrypted block is bigger, an error is returned
784 * @returns the size of the decrypted block, -1 on error
785 */
786int
787GNUNET_CRYPTO_rsa_decrypt (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey,
788 const struct GNUNET_CRYPTO_RsaEncryptedData *block,
789 void *result, uint16_t max)
790{
791 gcry_sexp_t resultsexp;
792 gcry_sexp_t data;
793 size_t erroff;
794 size_t size;
795 gcry_mpi_t val;
796 unsigned char *endp;
797 unsigned char *tmp;
798
799#if EXTRA_CHECKS
800 GNUNET_assert (0 == gcry_pk_testkey (hostkey->sexp));
801#endif
802 size = sizeof (struct GNUNET_CRYPTO_RsaEncryptedData);
803 GNUNET_assert (0 == gcry_mpi_scan (&val,
804 GCRYMPI_FMT_USG, &block->encoding[0],
805 size, &size));
806 GNUNET_assert (0 ==
807 gcry_sexp_build (&data, &erroff,
808 "(enc-val(flags)(rsa(a %m)))", val));
809 gcry_mpi_release (val);
810 GNUNET_assert (0 == gcry_pk_decrypt (&resultsexp, data, hostkey->sexp));
811 gcry_sexp_release (data);
812 /* resultsexp has format "(value %m)" */
813 GNUNET_assert (NULL !=
814 (val = gcry_sexp_nth_mpi (resultsexp, 1, GCRYMPI_FMT_USG)));
815 gcry_sexp_release (resultsexp);
816 tmp = GNUNET_malloc (max + HOSTKEY_LEN / 8);
817 size = max + HOSTKEY_LEN / 8;
818 GNUNET_assert (0 ==
819 gcry_mpi_print (GCRYMPI_FMT_USG, tmp, size, &size, val));
820 gcry_mpi_release (val);
821 endp = tmp;
822 endp += (size - max);
823 size = max;
824 memcpy (result, endp, size);
825 GNUNET_free (tmp);
826 return size;
827}
828
829
830/**
831 * Sign a given block.
832 *
833 * @param hostkey private key to use for the signing
834 * @param purpose what to sign (size, purpose)
835 * @param result where to write the signature
836 * @return GNUNET_SYSERR on error, GNUNET_OK on success
837 */
838int
839GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey,
840 const struct GNUNET_CRYPTO_RsaSignaturePurpose
841 *purpose, struct GNUNET_CRYPTO_RsaSignature *sig)
842{
843 gcry_sexp_t result;
844 gcry_sexp_t data;
845 size_t ssize;
846 gcry_mpi_t rval;
847 GNUNET_HashCode hc;
848 char *buff;
849 int bufSize;
850
851 GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc);
852#define FORMATSTRING "(4:data(5:flags5:pkcs1)(4:hash6:sha51264:0123456789012345678901234567890123456789012345678901234567890123))"
853 bufSize = strlen (FORMATSTRING) + 1;
854 buff = GNUNET_malloc (bufSize);
855 memcpy (buff, FORMATSTRING, bufSize);
856 memcpy (&buff
857 [bufSize -
858 strlen
859 ("0123456789012345678901234567890123456789012345678901234567890123))")
860 - 1], &hc, sizeof (GNUNET_HashCode));
861 GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0));
862 GNUNET_free (buff);
863 GNUNET_assert (0 == gcry_pk_sign (&result, data, hostkey->sexp));
864 gcry_sexp_release (data);
865 GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "s"));
866 gcry_sexp_release (result);
867 ssize = sizeof (struct GNUNET_CRYPTO_RsaSignature);
868 GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG,
869 (unsigned char *) sig, ssize, &ssize,
870 rval));
871 gcry_mpi_release (rval);
872 adjust (sig->sig, ssize, sizeof (struct GNUNET_CRYPTO_RsaSignature));
873 return GNUNET_OK;
874}
875
876
877/**
878 * Verify signature.
879 *
880 * @param purpose what is the purpose that the signature should have?
881 * @param validate block to validate (size, purpose, data)
882 * @param sig signature that is being validated
883 * @param publicKey public key of the signer
884 * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid
885 */
886int
887GNUNET_CRYPTO_rsa_verify (uint32_t purpose,
888 const struct GNUNET_CRYPTO_RsaSignaturePurpose
889 *validate,
890 const struct GNUNET_CRYPTO_RsaSignature *sig,
891 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
892 *publicKey)
893{
894 gcry_sexp_t data;
895 gcry_sexp_t sigdata;
896 size_t size;
897 gcry_mpi_t val;
898 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
899 GNUNET_HashCode hc;
900 char *buff;
901 int bufSize;
902 size_t erroff;
903 int rc;
904
905 if (purpose != ntohl (validate->purpose))
906 return GNUNET_SYSERR; /* purpose mismatch */
907 GNUNET_CRYPTO_hash (validate, ntohl (validate->size), &hc);
908 size = sizeof (struct GNUNET_CRYPTO_RsaSignature);
909 GNUNET_assert (0 == gcry_mpi_scan (&val,
910 GCRYMPI_FMT_USG,
911 (const unsigned char *) sig, size,
912 &size));
913 GNUNET_assert (0 ==
914 gcry_sexp_build (&sigdata, &erroff, "(sig-val(rsa(s %m)))",
915 val));
916 gcry_mpi_release (val);
917 bufSize = strlen (FORMATSTRING) + 1;
918 buff = GNUNET_malloc (bufSize);
919 memcpy (buff, FORMATSTRING, bufSize);
920 memcpy (&buff[strlen (FORMATSTRING) -
921 strlen
922 ("0123456789012345678901234567890123456789012345678901234567890123))")],
923 &hc, sizeof (GNUNET_HashCode));
924 GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0));
925 GNUNET_free (buff);
926 hostkey = public2PrivateKey (publicKey);
927 if (hostkey == NULL)
928 {
929 gcry_sexp_release (data);
930 gcry_sexp_release (sigdata);
931 return GNUNET_SYSERR;
932 }
933 rc = gcry_pk_verify (sigdata, data, hostkey->sexp);
934 GNUNET_CRYPTO_rsa_key_free (hostkey);
935 gcry_sexp_release (data);
936 gcry_sexp_release (sigdata);
937 if (rc)
938 {
939 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
940 _("RSA signature verification failed at %s:%d: %s\n"),
941 __FILE__, __LINE__, gcry_strerror (rc));
942 return GNUNET_SYSERR;
943 }
944 return GNUNET_OK;
945}
946
947
948/* end of crypto_rsa.c */
diff --git a/src/util/disk.c b/src/util/disk.c
new file mode 100644
index 000000000..f0fe9341b
--- /dev/null
+++ b/src/util/disk.c
@@ -0,0 +1,954 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/disk.c
23 * @brief disk IO convenience methods
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_directories.h"
30#include "gnunet_disk_lib.h"
31#include "gnunet_scheduler_lib.h"
32#include "gnunet_strings_lib.h"
33
34
35#if LINUX || CYGWIN
36#include <sys/vfs.h>
37#else
38#ifdef SOMEBSD
39#include <sys/param.h>
40#include <sys/mount.h>
41#else
42#ifdef OSX
43#include <sys/param.h>
44#include <sys/mount.h>
45#else
46#ifdef SOLARIS
47#include <sys/types.h>
48#include <sys/statvfs.h>
49#else
50#ifdef MINGW
51#define _IFMT 0170000 /* type of file */
52#define _IFLNK 0120000 /* symbolic link */
53#define S_ISLNK(m) (((m)&_IFMT) == _IFLNK)
54#else
55#error PORT-ME: need to port statfs (how much space is left on the drive?)
56#endif
57#endif
58#endif
59#endif
60#endif
61
62#ifndef SOMEBSD
63#ifndef WINDOWS
64#ifndef OSX
65#include <wordexp.h>
66#endif
67#endif
68#endif
69
70typedef struct
71{
72 unsigned long long total;
73 int include_sym_links;
74} GetFileSizeData;
75
76static int
77getSizeRec (void *ptr, const char *fn)
78{
79 GetFileSizeData *gfsd = ptr;
80#ifdef HAVE_STAT64
81 struct stat64 buf;
82#else
83 struct stat buf;
84#endif
85
86#ifdef HAVE_STAT64
87 if (0 != STAT64 (fn, &buf))
88 {
89 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
90 return GNUNET_SYSERR;
91 }
92#else
93 if (0 != STAT (fn, &buf))
94 {
95 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn);
96 return GNUNET_SYSERR;
97 }
98#endif
99 if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
100 gfsd->total += buf.st_size;
101 if ((S_ISDIR (buf.st_mode)) &&
102 (0 == ACCESS (fn, X_OK)) &&
103 ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
104 {
105 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd))
106 return GNUNET_SYSERR;
107 }
108 return GNUNET_OK;
109}
110
111/**
112 * Get the size of the file (or directory)
113 * of the given file (in bytes).
114 *
115 * @return GNUNET_SYSERR on error, GNUNET_OK on success
116 */
117int
118GNUNET_DISK_file_size (const char *filename,
119 unsigned long long *size, int includeSymLinks)
120{
121 GetFileSizeData gfsd;
122 int ret;
123
124 GNUNET_assert (size != NULL);
125 gfsd.total = 0;
126 gfsd.include_sym_links = includeSymLinks;
127 ret = getSizeRec (&gfsd, filename);
128 *size = gfsd.total;
129 return ret;
130}
131
132/**
133 * Get the number of blocks that are left on the partition that
134 * contains the given file (for normal users).
135 *
136 * @param part a file on the partition to check
137 * @return -1 on errors, otherwise the number of free blocks
138 */
139long
140GNUNET_DISK_get_blocks_available (const char *part)
141{
142#ifdef SOLARIS
143 struct statvfs buf;
144
145 if (0 != statvfs (part, &buf))
146 {
147 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
148 return -1;
149 }
150 return buf.f_bavail;
151#elif MINGW
152 DWORD dwDummy;
153 DWORD dwBlocks;
154 char szDrive[4];
155
156 memcpy (szDrive, part, 3);
157 szDrive[3] = 0;
158 if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
159 {
160 GNUNET_GE_LOG (GNUNET_ERROR_TYPE_WARNING,
161 _("`%s' failed for drive `%s': %u\n"),
162 "GetDiskFreeSpace", szDrive, GetLastError ());
163
164 return -1;
165 }
166 return dwBlocks;
167#else
168 struct statfs s;
169 if (0 != statfs (part, &s))
170 {
171 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
172 return -1;
173 }
174 return s.f_bavail;
175#endif
176}
177
178/**
179 * Test if fil is a directory.
180 *
181 * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it
182 * does not exist
183 */
184int
185GNUNET_DISK_directory_test (const char *fil)
186{
187 struct stat filestat;
188 int ret;
189
190 ret = STAT (fil, &filestat);
191 if (ret != 0)
192 {
193 if (errno != ENOENT)
194 {
195 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
196 return GNUNET_SYSERR;
197 }
198 return GNUNET_NO;
199 }
200 if (!S_ISDIR (filestat.st_mode))
201 return GNUNET_NO;
202 if (ACCESS (fil, R_OK | X_OK) < 0)
203 {
204 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil);
205 return GNUNET_SYSERR;
206 }
207 return GNUNET_YES;
208}
209
210/**
211 * Check that fil corresponds to a filename
212 * (of a file that exists and that is not a directory).
213 * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something
214 * else (will print an error message in that case, too).
215 */
216int
217GNUNET_DISK_file_test (const char *fil)
218{
219 struct stat filestat;
220 int ret;
221 char *rdir;
222
223 rdir = GNUNET_STRINGS_filename_expand (fil);
224 if (rdir == NULL)
225 return GNUNET_SYSERR;
226
227 ret = STAT (rdir, &filestat);
228 if (ret != 0)
229 {
230 if (errno != ENOENT)
231 {
232 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir);
233 GNUNET_free (rdir);
234 return GNUNET_SYSERR;
235 }
236 GNUNET_free (rdir);
237 return GNUNET_NO;
238 }
239 if (!S_ISREG (filestat.st_mode))
240 {
241 GNUNET_free (rdir);
242 return GNUNET_NO;
243 }
244 if (ACCESS (rdir, R_OK) < 0)
245 {
246 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir);
247 GNUNET_free (rdir);
248 return GNUNET_SYSERR;
249 }
250 GNUNET_free (rdir);
251 return GNUNET_YES;
252}
253
254/**
255 * Implementation of "mkdir -p"
256 * @param dir the directory to create
257 * @returns GNUNET_OK on success, GNUNET_SYSERR on failure
258 */
259int
260GNUNET_DISK_directory_create (const char *dir)
261{
262 char *rdir;
263 int len;
264 int pos;
265 int ret = GNUNET_OK;
266
267 rdir = GNUNET_STRINGS_filename_expand (dir);
268 if (rdir == NULL)
269 return GNUNET_SYSERR;
270
271 len = strlen (rdir);
272#ifndef MINGW
273 pos = 1; /* skip heading '/' */
274#else
275 /* Local or Network path? */
276 if (strncmp (rdir, "\\\\", 2) == 0)
277 {
278 pos = 2;
279 while (rdir[pos])
280 {
281 if (rdir[pos] == '\\')
282 {
283 pos++;
284 break;
285 }
286 pos++;
287 }
288 }
289 else
290 {
291 pos = 3; /* strlen("C:\\") */
292 }
293#endif
294 while (pos <= len)
295 {
296 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
297 {
298 rdir[pos] = '\0';
299 ret = GNUNET_DISK_directory_test (rdir);
300 if (ret == GNUNET_SYSERR)
301 {
302 GNUNET_free (rdir);
303 return GNUNET_SYSERR;
304 }
305 if (ret == GNUNET_NO)
306 {
307#ifndef MINGW
308 ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */
309#else
310 ret = mkdir (rdir);
311#endif
312 if ((ret != 0) && (errno != EEXIST))
313 {
314 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir",
315 rdir);
316 GNUNET_free (rdir);
317 return GNUNET_SYSERR;
318 }
319 }
320 rdir[pos] = DIR_SEPARATOR;
321 }
322 pos++;
323 }
324 GNUNET_free (rdir);
325 return GNUNET_OK;
326}
327
328
329/**
330 * Create the directory structure for storing
331 * a file.
332 *
333 * @param filename name of a file in the directory
334 * @returns GNUNET_OK on success,
335 * GNUNET_SYSERR on failure,
336 * GNUNET_NO if the directory
337 * exists but is not writeable for us
338 */
339int
340GNUNET_DISK_directory_create_for_file (const char *dir)
341{
342 char *rdir;
343 int len;
344 int ret;
345
346 rdir = GNUNET_STRINGS_filename_expand (dir);
347 if (rdir == NULL)
348 return GNUNET_SYSERR;
349 len = strlen (rdir);
350 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
351 len--;
352 rdir[len] = '\0';
353 ret = GNUNET_DISK_directory_create (rdir);
354 if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK)))
355 ret = GNUNET_NO;
356 GNUNET_free (rdir);
357 return ret;
358}
359
360/**
361 * Read the contents of a binary file into a buffer.
362 * @param fileName the name of the file, not freed,
363 * must already be expanded!
364 * @param len the maximum number of bytes to read
365 * @param result the buffer to write the result to
366 * @return the number of bytes read on success, -1 on failure
367 */
368int
369GNUNET_DISK_file_read (const char *fileName, int len, void *result)
370{
371 /* open file, must exist, open read only */
372 int handle;
373 int size;
374
375 GNUNET_assert (fileName != NULL);
376 GNUNET_assert (len > 0);
377 if (len == 0)
378 return 0;
379 GNUNET_assert (result != NULL);
380 handle = GNUNET_DISK_file_open (fileName, O_RDONLY, S_IRUSR);
381 if (handle < 0)
382 return -1;
383 size = READ (handle, result, len);
384 GNUNET_DISK_file_close (fileName, handle);
385 return size;
386}
387
388
389/**
390 * Convert string to value ('755' for chmod-call)
391 */
392static int
393atoo (const char *s)
394{
395 int n = 0;
396
397 while (('0' <= *s) && (*s < '8'))
398 {
399 n <<= 3;
400 n += *s++ - '0';
401 }
402 return n;
403}
404
405/**
406 * Write a buffer to a file.
407 * @param fileName the name of the file, NOT freed!
408 * @param buffer the data to write
409 * @param n number of bytes to write
410 * @param mode permissions to set on the file
411 * @return GNUNET_OK on success, GNUNET_SYSERR on error
412 */
413int
414GNUNET_DISK_file_write (const char *fileName,
415 const void *buffer, unsigned int n, const char *mode)
416{
417 int handle;
418 char *fn;
419
420 /* open file, open with 600, create if not
421 present, otherwise overwrite */
422 GNUNET_assert (fileName != NULL);
423 fn = GNUNET_STRINGS_filename_expand (fileName);
424 handle = GNUNET_DISK_file_open (fn, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
425 if (handle == -1)
426 {
427 GNUNET_free (fn);
428 return GNUNET_SYSERR;
429 }
430 GNUNET_assert ((n == 0) || (buffer != NULL));
431 /* write the buffer take length from the beginning */
432 if (n != WRITE (handle, buffer, n))
433 {
434 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
435 GNUNET_DISK_file_close (fn, handle);
436 GNUNET_free (fn);
437 return GNUNET_SYSERR;
438 }
439 GNUNET_DISK_file_close (fn, handle);
440 if (0 != CHMOD (fn, atoo (mode)))
441 {
442 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
443 }
444 GNUNET_free (fn);
445 return GNUNET_OK;
446}
447
448/**
449 * Scan a directory for files. The name of the directory
450 * must be expanded first (!).
451 * @param dirName the name of the directory
452 * @param callback the method to call for each file,
453 * can be NULL, in that case, we only count
454 * @param data argument to pass to callback
455 * @return the number of files found, GNUNET_SYSERR on error or
456 * ieration aborted by callback returning GNUNET_SYSERR
457 */
458int
459GNUNET_DISK_directory_scan (const char *dirName,
460 GNUNET_FileNameCallback callback, void *data)
461{
462 DIR *dinfo;
463 struct dirent *finfo;
464 struct stat istat;
465 int count = 0;
466 char *name;
467 char *dname;
468 unsigned int name_len;
469 unsigned int n_size;
470
471 GNUNET_assert (dirName != NULL);
472 dname = GNUNET_STRINGS_filename_expand (dirName);
473 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
474 dname[strlen (dname) - 1] = '\0';
475 if (0 != STAT (dname, &istat))
476 {
477 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
478 GNUNET_free (dname);
479 return GNUNET_SYSERR;
480 }
481 if (!S_ISDIR (istat.st_mode))
482 {
483 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
484 _("Expected `%s' to be a directory!\n"), dirName);
485 GNUNET_free (dname);
486 return GNUNET_SYSERR;
487 }
488 errno = 0;
489 dinfo = OPENDIR (dname);
490 if ((errno == EACCES) || (dinfo == NULL))
491 {
492 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
493 if (dinfo != NULL)
494 closedir (dinfo);
495 GNUNET_free (dname);
496 return GNUNET_SYSERR;
497 }
498 name_len = 256;
499 n_size = strlen (dname) + name_len + 2;
500 name = GNUNET_malloc (n_size);
501 while ((finfo = readdir (dinfo)) != NULL)
502 {
503 if ((0 == strcmp (finfo->d_name, ".")) ||
504 (0 == strcmp (finfo->d_name, "..")))
505 continue;
506 if (callback != NULL)
507 {
508 if (name_len < strlen (finfo->d_name))
509 {
510 GNUNET_free (name);
511 name_len = strlen (finfo->d_name);
512 n_size = strlen (dname) + name_len + 2;
513 name = GNUNET_malloc (n_size);
514 }
515 /* dname can end in "/" only if dname == "/";
516 if dname does not end in "/", we need to add
517 a "/" (otherwise, we must not!) */
518 GNUNET_snprintf (name,
519 n_size,
520 "%s%s%s",
521 dname,
522 (strcmp (dname, DIR_SEPARATOR_STR) ==
523 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name);
524 if (GNUNET_OK != callback (data, name))
525 {
526 closedir (dinfo);
527 GNUNET_free (name);
528 GNUNET_free (dname);
529 return GNUNET_SYSERR;
530 }
531 }
532 count++;
533 }
534 closedir (dinfo);
535 GNUNET_free (name);
536 GNUNET_free (dname);
537 return count;
538}
539
540
541/**
542 * Opaque handle used for iterating over a directory.
543 */
544struct GNUNET_DISK_DirectoryIterator
545{
546 /**
547 * Our scheduler.
548 */
549 struct GNUNET_SCHEDULER_Handle *sched;
550
551 /**
552 * Function to call on directory entries.
553 */
554 GNUNET_DISK_DirectoryIteratorCallback callback;
555
556 /**
557 * Closure for callback.
558 */
559 void *callback_cls;
560
561 /**
562 * Reference to directory.
563 */
564 DIR *directory;
565
566 /**
567 * Directory name.
568 */
569 char *dirname;
570
571 /**
572 * Next filename to process.
573 */
574 char *next_name;
575
576 /**
577 * Our priority.
578 */
579 enum GNUNET_SCHEDULER_Priority priority;
580
581};
582
583
584/**
585 * Task used by the directory iterator.
586 */
587static void
588directory_iterator_task (void *cls,
589 const struct GNUNET_SCHEDULER_TaskContext *tc)
590{
591 struct GNUNET_DISK_DirectoryIterator *iter = cls;
592 char *name;
593
594 name = iter->next_name;
595 GNUNET_assert (name != NULL);
596 iter->next_name = NULL;
597 iter->callback (iter->callback_cls, iter, name, iter->dirname);
598 GNUNET_free (name);
599}
600
601
602/**
603 * This function must be called during the DiskIteratorCallback
604 * (exactly once) to schedule the task to process the next
605 * filename in the directory (if there is one).
606 *
607 * @param iter opaque handle for the iterator
608 * @param can set to GNUNET_YES to terminate the iteration early
609 * @return GNUNET_YES if iteration will continue,
610 * GNUNET_NO if this was the last entry (and iteration is complete),
611 * GNUNET_SYSERR if abort was YES
612 */
613int
614GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
615 *iter, int can)
616{
617 struct dirent *finfo;
618
619 GNUNET_assert (iter->next_name == NULL);
620 if (can == GNUNET_YES)
621 {
622 closedir (iter->directory);
623 GNUNET_free (iter->dirname);
624 GNUNET_free (iter);
625 return GNUNET_SYSERR;
626 }
627 while (NULL != (finfo = readdir (iter->directory)))
628 {
629 if ((0 == strcmp (finfo->d_name, ".")) ||
630 (0 == strcmp (finfo->d_name, "..")))
631 continue;
632 GNUNET_asprintf (&iter->next_name,
633 "%s%s%s",
634 iter->dirname, DIR_SEPARATOR_STR, finfo->d_name);
635 break;
636 }
637 if (finfo == NULL)
638 {
639 GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
640 return GNUNET_NO;
641 }
642 GNUNET_SCHEDULER_add_after (iter->sched,
643 GNUNET_YES,
644 iter->priority,
645 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
646 &directory_iterator_task, iter);
647 return GNUNET_YES;
648}
649
650
651/**
652 * Scan a directory for files using the scheduler to run a task for
653 * each entry. The name of the directory must be expanded first (!).
654 * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
655 * may provide a simpler API.
656 *
657 * @param sched scheduler to use
658 * @param prio priority to use
659 * @param dirName the name of the directory
660 * @param callback the method to call for each file
661 * @param callback_cls closure for callback
662 */
663void
664GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
665 enum GNUNET_SCHEDULER_Priority prio,
666 const char *dirName,
667 GNUNET_DISK_DirectoryIteratorCallback
668 callback, void *callback_cls)
669{
670 struct GNUNET_DISK_DirectoryIterator *di;
671
672 di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
673 di->sched = sched;
674 di->callback = callback;
675 di->callback_cls = callback_cls;
676 di->directory = OPENDIR (dirName);
677 di->dirname = GNUNET_strdup (dirName);
678 di->priority = prio;
679 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
680}
681
682
683static int
684remove_helper (void *unused, const char *fn)
685{
686 GNUNET_DISK_directory_remove (fn);
687 return GNUNET_OK;
688}
689
690/**
691 * Remove all files in a directory (rm -rf). Call with
692 * caution.
693 *
694 *
695 * @param fileName the file to remove
696 * @return GNUNET_OK on success, GNUNET_SYSERR on error
697 */
698int
699GNUNET_DISK_directory_remove (const char *fileName)
700{
701 struct stat istat;
702
703 if (0 != LSTAT (fileName, &istat))
704 return GNUNET_NO; /* file may not exist... */
705 if (UNLINK (fileName) == 0)
706 return GNUNET_OK;
707 if ((errno != EISDIR) &&
708 /* EISDIR is not sufficient in all cases, e.g.
709 sticky /tmp directory may result in EPERM on BSD.
710 So we also explicitly check "isDirectory" */
711 (GNUNET_YES != GNUNET_DISK_directory_test (fileName)))
712 {
713 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
714 return GNUNET_SYSERR;
715 }
716 if (GNUNET_SYSERR ==
717 GNUNET_DISK_directory_scan (fileName, remove_helper, NULL))
718 return GNUNET_SYSERR;
719 if (0 != RMDIR (fileName))
720 {
721 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
722 return GNUNET_SYSERR;
723 }
724 return GNUNET_OK;
725}
726
727void
728GNUNET_DISK_file_close (const char *filename, int fd)
729{
730 if (0 != CLOSE (fd))
731 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", filename);
732}
733
734int
735GNUNET_DISK_file_open (const char *filename, int oflag, ...)
736{
737 char *fn;
738 int mode;
739 int ret;
740#ifdef MINGW
741 char szFile[_MAX_PATH + 1];
742 long lRet;
743
744 if ((lRet = plibc_conv_to_win_path (filename, szFile)) != ERROR_SUCCESS)
745 {
746 errno = ENOENT;
747 SetLastError (lRet);
748 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
749 "plibc_conv_to_win_path", filename);
750 return -1;
751 }
752 fn = GNUNET_strdup (szFile);
753#else
754 fn = GNUNET_STRINGS_filename_expand (filename);
755#endif
756 if (oflag & O_CREAT)
757 {
758 va_list arg;
759 va_start (arg, oflag);
760 mode = va_arg (arg, int);
761 va_end (arg);
762 }
763 else
764 {
765 mode = 0;
766 }
767#ifdef MINGW
768 /* set binary mode */
769 oflag |= O_BINARY;
770#endif
771 ret = OPEN (fn, oflag, mode);
772 if (ret == -1)
773 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", fn);
774 GNUNET_free (fn);
775 return ret;
776}
777
778#define COPY_BLK_SIZE 65536
779
780/**
781 * Copy a file.
782 * @return GNUNET_OK on success, GNUNET_SYSERR on error
783 */
784int
785GNUNET_DISK_file_copy (const char *src, const char *dst)
786{
787 char *buf;
788 unsigned long long pos;
789 unsigned long long size;
790 unsigned long long len;
791 int in;
792 int out;
793
794 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
795 return GNUNET_SYSERR;
796 pos = 0;
797 in = GNUNET_DISK_file_open (src, O_RDONLY | O_LARGEFILE);
798 if (in == -1)
799 return GNUNET_SYSERR;
800 out = GNUNET_DISK_file_open (dst,
801 O_LARGEFILE | O_WRONLY | O_CREAT | O_EXCL,
802 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
803 if (out == -1)
804 {
805 GNUNET_DISK_file_close (src, in);
806 return GNUNET_SYSERR;
807 }
808 buf = GNUNET_malloc (COPY_BLK_SIZE);
809 while (pos < size)
810 {
811 len = COPY_BLK_SIZE;
812 if (len > size - pos)
813 len = size - pos;
814 if (len != READ (in, buf, len))
815 goto FAIL;
816 if (len != WRITE (out, buf, len))
817 goto FAIL;
818 pos += len;
819 }
820 GNUNET_free (buf);
821 GNUNET_DISK_file_close (src, in);
822 GNUNET_DISK_file_close (dst, out);
823 return GNUNET_OK;
824FAIL:
825 GNUNET_free (buf);
826 GNUNET_DISK_file_close (src, in);
827 GNUNET_DISK_file_close (dst, out);
828 return GNUNET_SYSERR;
829}
830
831
832/**
833 * @brief Removes special characters as ':' from a filename.
834 * @param fn the filename to canonicalize
835 */
836void
837GNUNET_DISK_filename_canonicalize (char *fn)
838{
839 char *idx;
840 char c;
841
842 idx = fn;
843 while (*idx)
844 {
845 c = *idx;
846
847 if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' ||
848 c == '"' || c == '<' || c == '>' || c == '|')
849 {
850 *idx = '_';
851 }
852
853 idx++;
854 }
855}
856
857
858
859/**
860 * @brief Change owner of a file
861 */
862int
863GNUNET_DISK_file_change_owner (const char *filename, const char *user)
864{
865#ifndef MINGW
866 struct passwd *pws;
867
868 pws = getpwnam (user);
869 if (pws == NULL)
870 {
871 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
872 _("Cannot obtain information about user `%s': %s\n"),
873 user, STRERROR (errno));
874 return GNUNET_SYSERR;
875 }
876 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
877 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
878#endif
879 return GNUNET_OK;
880}
881
882
883/**
884 * Construct full path to a file inside of the private
885 * directory used by GNUnet. Also creates the corresponding
886 * directory. If the resulting name is supposed to be
887 * a directory, end the last argument in '/' (or pass
888 * DIR_SEPARATOR_STR as the last argument before NULL).
889 *
890 * @param serviceName name of the service
891 * @param varargs is NULL-terminated list of
892 * path components to append to the
893 * private directory name.
894 * @return the constructed filename
895 */
896char *
897GNUNET_DISK_get_home_filename (struct GNUNET_CONFIGURATION_Handle *cfg,
898 const char *serviceName, ...)
899{
900 const char *c;
901 char *pfx;
902 char *ret;
903 va_list ap;
904 unsigned int needed;
905
906 if (GNUNET_OK !=
907 GNUNET_CONFIGURATION_get_value_filename (cfg,
908 serviceName, "HOME", &pfx))
909 return NULL;
910 if (pfx == NULL)
911 {
912 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
913 _("No `%s' specified for service `%s' in configuration.\n"),
914 "HOME", serviceName);
915 return NULL;
916 }
917 needed = strlen (pfx) + 2;
918 if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\'))
919 needed++;
920 va_start (ap, serviceName);
921 while (1)
922 {
923 c = va_arg (ap, const char *);
924 if (c == NULL)
925 break;
926 needed += strlen (c);
927 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
928 needed++;
929 }
930 va_end (ap);
931 ret = GNUNET_malloc (needed);
932 strcpy (ret, pfx);
933 GNUNET_free (pfx);
934 va_start (ap, serviceName);
935 while (1)
936 {
937 c = va_arg (ap, const char *);
938 if (c == NULL)
939 break;
940 if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\'))
941 strcat (ret, DIR_SEPARATOR_STR);
942 strcat (ret, c);
943 }
944 va_end (ap);
945 if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
946 GNUNET_DISK_directory_create_for_file (ret);
947 else
948 GNUNET_DISK_directory_create (ret);
949 return ret;
950}
951
952
953
954/* end of disk.c */
diff --git a/src/util/getopt.c b/src/util/getopt.c
new file mode 100644
index 000000000..e069e76f7
--- /dev/null
+++ b/src/util/getopt.c
@@ -0,0 +1,1077 @@
1/* Getopt for GNU.
2 NOTE: getopt is now part of the C library, so if you don't know what
3 "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
4 before changing it!
5
6 Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
7 Free Software Foundation, Inc.
8
9NOTE: The canonical source of this file is maintained with the GNU C Library.
10Bugs can be reported to bug-glibc@prep.ai.mit.edu.
11
12This program is free software; you can redistribute it and/or modify it
13under the terms of the GNU General Public License as published by the
14Free Software Foundation; either version 2, or (at your option) any
15later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25USA.
26
27
28This code was heavily modified for GNUnet.
29Copyright (C) 2006 Christian Grothoff
30*/
31
32/**
33 * @file util/getopt.c
34 * @brief GNU style option parsing
35 *
36 * TODO: get rid of statics (make reentrant) and
37 * replace main GNU getopt parser with one that
38 * actually fits our API.
39 */
40
41#include "platform.h"
42#include "gnunet_common.h"
43#include "gnunet_getopt_lib.h"
44
45#ifdef VMS
46# include <unixlib.h>
47# if HAVE_STRING_H - 0
48# include <string.h>
49# endif
50#endif
51
52#if defined (WIN32) && !defined (__CYGWIN32__)
53/* It's not Unix, really. See? Capital letters. */
54# include <windows.h>
55# define getpid() GetCurrentProcessId()
56#endif
57
58#ifndef _
59/* This is for other GNU distributions with internationalized messages.
60 When compiling libc, the _ macro is predefined. */
61# ifdef HAVE_LIBINTL_H
62# include <libintl.h>
63# define _(msgid) gettext (msgid)
64# else
65# define _(msgid) (msgid)
66# endif
67#endif
68
69/* Describe the long-named options requested by the application.
70 The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
71 of `struct GNoption' terminated by an element containing a name which is
72 zero.
73
74 The field `has_arg' is:
75 no_argument (or 0) if the option does not take an argument,
76 required_argument (or 1) if the option requires an argument,
77 optional_argument (or 2) if the option takes an optional argument.
78
79 If the field `flag' is not NULL, it points to a variable that is set
80 to the value given in the field `val' when the option is found, but
81 left unchanged if the option is not found.
82
83 To have a long-named option do something other than set an `int' to
84 a compiled-in constant, such as set a value from `GNoptarg', set the
85 option's `flag' field to zero and its `val' field to a nonzero
86 value (the equivalent single-letter option character, if there is
87 one). For long options that have a zero `flag' field, `getopt'
88 returns the contents of the `val' field. */
89
90struct GNoption
91{
92 const char *name;
93 /* has_arg can't be an enum because some compilers complain about
94 type mismatches in all the code that assumes it is an int. */
95 int has_arg;
96 int *flag;
97 int val;
98};
99
100
101/* This version of `getopt' appears to the caller like standard Unix `getopt'
102 but it behaves differently for the user, since it allows the user
103 to intersperse the options with the other arguments.
104
105 As `getopt' works, it permutes the elements of ARGV so that,
106 when it is done, all the options precede everything else. Thus
107 all application programs are extended to handle flexible argument order.
108
109 Setting the environment variable POSIXLY_CORRECT disables permutation.
110 Then the behavior is completely standard.
111
112 GNU application programs can use a third alternative mode in which
113 they can distinguish the relative order of options and other arguments. */
114
115/* For communication from `getopt' to the caller.
116 When `getopt' finds an option that takes an argument,
117 the argument value is returned here.
118 Also, when `ordering' is RETURN_IN_ORDER,
119 each non-option ARGV-element is returned here. */
120
121static char *GNoptarg = NULL;
122
123/* Index in ARGV of the next element to be scanned.
124 This is used for communication to and from the caller
125 and for communication between successive calls to `getopt'.
126
127 On entry to `getopt', zero means this is the first call; initialize.
128
129 When `getopt' returns -1, this is the index of the first of the
130 non-option elements that the caller should itself scan.
131
132 Otherwise, `GNoptind' communicates from one call to the next
133 how much of ARGV has been scanned so far. */
134
135/* 1003.2 says this must be 1 before any call. */
136static int GNoptind = 1;
137
138/* Formerly, initialization of getopt depended on GNoptind==0, which
139 causes problems with re-calling getopt as programs generally don't
140 know that. */
141
142static int __getopt_initialized = 0;
143
144/* The next char to be scanned in the option-element
145 in which the last option character we returned was found.
146 This allows us to pick up the scan where we left off.
147
148 If this is zero, or a null string, it means resume the scan
149 by advancing to the next ARGV-element. */
150
151static char *nextchar;
152
153/* Callers store zero here to inhibit the error message
154 for unrecognized options. */
155
156static int GNopterr = 1;
157
158/* Set to an option character which was unrecognized.
159 This must be initialized on some systems to avoid linking in the
160 system's own getopt implementation. */
161
162static int GNoptopt = '?';
163
164/* Describe how to deal with options that follow non-option ARGV-elements.
165
166 If the caller did not specify anything,
167 the default is REQUIRE_ORDER if the environment variable
168 POSIXLY_CORRECT is defined, PERMUTE otherwise.
169
170 REQUIRE_ORDER means don't recognize them as options;
171 stop option processing when the first non-option is seen.
172 This is what Unix does.
173 This mode of operation is selected by either setting the environment
174 variable POSIXLY_CORRECT, or using `+' as the first character
175 of the list of option characters.
176
177 PERMUTE is the default. We GNUNET_CRYPTO_random_permute the contents of ARGV as we scan,
178 so that eventually all the non-options are at the end. This allows options
179 to be given in any order, even with programs that were not written to
180 expect this.
181
182 RETURN_IN_ORDER is an option available to programs that were written
183 to expect GNoptions and other ARGV-elements in any order and that care about
184 the ordering of the two. We describe each non-option ARGV-element
185 as if it were the argument of an option with character code 1.
186 Using `-' as the first character of the list of option characters
187 selects this mode of operation.
188
189 The special argument `--' forces an end of option-scanning regardless
190 of the value of `ordering'. In the case of RETURN_IN_ORDER, only
191 `--' can cause `getopt' to return -1 with `GNoptind' != ARGC. */
192
193static enum
194{
195 REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
196} ordering;
197
198/* Value of POSIXLY_CORRECT environment variable. */
199static char *posixly_correct;
200
201#ifdef __GNU_LIBRARY__
202/* We want to avoid inclusion of string.h with non-GNU libraries
203 because there are many ways it can cause trouble.
204 On some systems, it contains special magic macros that don't work
205 in GCC. */
206#include <string.h>
207#define my_index strchr
208#else
209
210/* Avoid depending on library functions or files
211 whose names are inconsistent. */
212
213char *getenv ();
214
215static char *
216my_index (str, chr)
217 const char *str;
218 int chr;
219{
220 while (*str)
221 {
222 if (*str == chr)
223 return (char *) str;
224 str++;
225 }
226 return 0;
227}
228
229/* If using GCC, we can safely declare strlen this way.
230 If not using GCC, it is ok not to declare it. */
231#ifdef __GNUC__
232/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
233 That was relevant to code that was here before. */
234#if !defined (__STDC__) || !__STDC__
235/* gcc with -traditional declares the built-in strlen to return int,
236 and has done so at least since version 2.4.5. -- rms. */
237extern int strlen (const char *);
238#endif /* not __STDC__ */
239#endif /* __GNUC__ */
240
241#endif /* not __GNU_LIBRARY__ */
242
243/* Handle permutation of arguments. */
244
245/* Describe the part of ARGV that contains non-options that have
246 been skipped. `first_nonopt' is the index in ARGV of the first of them;
247 `last_nonopt' is the index after the last of them. */
248
249static int first_nonopt;
250static int last_nonopt;
251
252#ifdef _LIBC
253/* Bash 2.0 gives us an environment variable containing flags
254 indicating ARGV elements that should not be considered arguments. */
255
256/* Defined in getopt_init.c */
257extern char *__getopt_nonoption_flags;
258
259static int nonoption_flags_max_len;
260static int nonoption_flags_len;
261
262static int original_argc;
263static char *const *original_argv;
264
265extern pid_t __libc_pid;
266
267/* Make sure the environment variable bash 2.0 puts in the environment
268 is valid for the getopt call we must make sure that the ARGV passed
269 to getopt is that one passed to the process. */
270static void
271 __attribute__ ((unused)) store_args_and_env (int argc, char *const *argv)
272{
273 /* XXX This is no good solution. We should rather copy the args so
274 that we can compare them later. But we must not use malloc(3). */
275 original_argc = argc;
276 original_argv = argv;
277}
278
279text_set_element (__libc_subinit, store_args_and_env);
280
281# define SWAP_FLAGS(ch1, ch2) \
282 if (nonoption_flags_len > 0) \
283 { \
284 char __tmp = __getopt_nonoption_flags[ch1]; \
285 __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
286 __getopt_nonoption_flags[ch2] = __tmp; \
287 }
288#else /* !_LIBC */
289# define SWAP_FLAGS(ch1, ch2)
290#endif /* _LIBC */
291
292/* Exchange two adjacent subsequences of ARGV.
293 One subsequence is elements [first_nonopt,last_nonopt)
294 which contains all the non-options that have been skipped so far.
295 The other is elements [last_nonopt,GNoptind), which contains all
296 the options processed since those non-options were skipped.
297
298 `first_nonopt' and `last_nonopt' are relocated so that they describe
299 the new indices of the non-options in ARGV after they are moved. */
300
301#if defined (__STDC__) && __STDC__
302static void exchange (char **);
303#endif
304
305static void
306exchange (argv)
307 char **argv;
308{
309 int bottom = first_nonopt;
310 int middle = last_nonopt;
311 int top = GNoptind;
312 char *tem;
313
314 /* Exchange the shorter segment with the far end of the longer segment.
315 That puts the shorter segment into the right place.
316 It leaves the longer segment in the right place overall,
317 but it consists of two parts that need to be swapped next. */
318
319#ifdef _LIBC
320 /* First make sure the handling of the `__getopt_nonoption_flags'
321 string can work normally. Our top argument must be in the range
322 of the string. */
323 if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
324 {
325 /* We must extend the array. The user plays games with us and
326 presents new arguments. */
327 char *new_str = malloc (top + 1);
328 if (new_str == NULL)
329 nonoption_flags_len = nonoption_flags_max_len = 0;
330 else
331 {
332 memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
333 memset (&new_str[nonoption_flags_max_len], '\0',
334 top + 1 - nonoption_flags_max_len);
335 nonoption_flags_max_len = top + 1;
336 __getopt_nonoption_flags = new_str;
337 }
338 }
339#endif
340
341 while (top > middle && middle > bottom)
342 {
343 if (top - middle > middle - bottom)
344 {
345 /* Bottom segment is the short one. */
346 int len = middle - bottom;
347 register int i;
348
349 /* Swap it with the top part of the top segment. */
350 for (i = 0; i < len; i++)
351 {
352 tem = argv[bottom + i];
353 argv[bottom + i] = argv[top - (middle - bottom) + i];
354 argv[top - (middle - bottom) + i] = tem;
355 SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
356 }
357 /* Exclude the moved bottom segment from further swapping. */
358 top -= len;
359 }
360 else
361 {
362 /* Top segment is the short one. */
363 int len = top - middle;
364 register int i;
365
366 /* Swap it with the bottom part of the bottom segment. */
367 for (i = 0; i < len; i++)
368 {
369 tem = argv[bottom + i];
370 argv[bottom + i] = argv[middle + i];
371 argv[middle + i] = tem;
372 SWAP_FLAGS (bottom + i, middle + i);
373 }
374 /* Exclude the moved top segment from further swapping. */
375 bottom += len;
376 }
377 }
378
379 /* Update records for the slots the non-options now occupy. */
380
381 first_nonopt += (GNoptind - last_nonopt);
382 last_nonopt = GNoptind;
383}
384
385/* Initialize the internal data when the first call is made. */
386
387#if defined (__STDC__) && __STDC__
388static const char *_getopt_initialize (int, char *const *, const char *);
389#endif
390static const char *
391_getopt_initialize (argc, argv, optstring)
392 int argc;
393 char *const *argv;
394 const char *optstring;
395{
396 /* Start processing options with ARGV-element 1 (since ARGV-element 0
397 is the program name); the sequence of previously skipped
398 non-option ARGV-elements is empty. */
399
400 first_nonopt = last_nonopt = GNoptind;
401
402 nextchar = NULL;
403
404 posixly_correct = getenv ("POSIXLY_CORRECT");
405
406 /* Determine how to handle the ordering of options and nonoptions. */
407
408 if (optstring[0] == '-')
409 {
410 ordering = RETURN_IN_ORDER;
411 ++optstring;
412 }
413 else if (optstring[0] == '+')
414 {
415 ordering = REQUIRE_ORDER;
416 ++optstring;
417 }
418 else if (posixly_correct != NULL)
419 ordering = REQUIRE_ORDER;
420 else
421 ordering = PERMUTE;
422
423#ifdef _LIBC
424 if (posixly_correct == NULL
425 && argc == original_argc && argv == original_argv)
426 {
427 if (nonoption_flags_max_len == 0)
428 {
429 if (__getopt_nonoption_flags == NULL
430 || __getopt_nonoption_flags[0] == '\0')
431 nonoption_flags_max_len = -1;
432 else
433 {
434 const char *orig_str = __getopt_nonoption_flags;
435 int len = nonoption_flags_max_len = strlen (orig_str);
436 if (nonoption_flags_max_len < argc)
437 nonoption_flags_max_len = argc;
438 __getopt_nonoption_flags =
439 (char *) malloc (nonoption_flags_max_len);
440 if (__getopt_nonoption_flags == NULL)
441 nonoption_flags_max_len = -1;
442 else
443 {
444 memcpy (__getopt_nonoption_flags, orig_str, len);
445 memset (&__getopt_nonoption_flags[len], '\0',
446 nonoption_flags_max_len - len);
447 }
448 }
449 }
450 nonoption_flags_len = nonoption_flags_max_len;
451 }
452 else
453 nonoption_flags_len = 0;
454#endif
455
456 return optstring;
457}
458
459/* Scan elements of ARGV (whose length is ARGC) for option characters
460 given in OPTSTRING.
461
462 If an element of ARGV starts with '-', and is not exactly "-" or "--",
463 then it is an option element. The characters of this element
464 (aside from the initial '-') are option characters. If `getopt'
465 is called repeatedly, it returns successively each of the option characters
466 from each of the option elements.
467
468 If `getopt' finds another option character, it returns that character,
469 updating `GNoptind' and `nextchar' so that the next call to `getopt' can
470 resume the scan with the following option character or ARGV-element.
471
472 If there are no more option characters, `getopt' returns -1.
473 Then `GNoptind' is the index in ARGV of the first ARGV-element
474 that is not an option. (The ARGV-elements have been permuted
475 so that those that are not options now come last.)
476
477 OPTSTRING is a string containing the legitimate option characters.
478 If an option character is seen that is not listed in OPTSTRING,
479 return '?' after printing an error message. If you set `GNopterr' to
480 zero, the error message is suppressed but we still return '?'.
481
482 If a char in OPTSTRING is followed by a colon, that means it wants an arg,
483 so the following text in the same ARGV-element, or the text of the following
484 ARGV-element, is returned in `GNoptarg'. Two colons mean an option that
485 wants an optional arg; if there is text in the current ARGV-element,
486 it is returned in `GNoptarg', otherwise `GNoptarg' is set to zero.
487
488 If OPTSTRING starts with `-' or `+', it requests different methods of
489 handling the non-option ARGV-elements.
490 See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
491
492 Long-named options begin with `--' instead of `-'.
493 Their names may be abbreviated as long as the abbreviation is unique
494 or is an exact match for some defined option. If they have an
495 argument, it follows the option name in the same ARGV-element, separated
496 from the option name by a `=', or else the in next ARGV-element.
497 When `getopt' finds a long-named option, it returns 0 if that option's
498 `flag' field is nonzero, the value of the option's `val' field
499 if the `flag' field is zero.
500
501 The elements of ARGV aren't really const, because we GNUNET_CRYPTO_random_permute them.
502 But we pretend they're const in the prototype to be compatible
503 with other systems.
504
505 LONGOPTS is a vector of `struct GNoption' terminated by an
506 element containing a name which is zero.
507
508 LONGIND returns the index in LONGOPT of the long-named option found.
509 It is only valid when a long-named option has been found by the most
510 recent call.
511
512 If LONG_ONLY is nonzero, '-' as well as '--' can introduce
513 long-named options. */
514
515static int
516GN_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
517 int argc;
518 char *const *argv;
519 const char *optstring;
520 const struct GNoption *longopts;
521 int *longind;
522 int long_only;
523{
524 GNoptarg = NULL;
525
526 if (GNoptind == 0 || !__getopt_initialized)
527 {
528 if (GNoptind == 0)
529 GNoptind = 1; /* Don't scan ARGV[0], the program name. */
530 optstring = _getopt_initialize (argc, argv, optstring);
531 __getopt_initialized = 1;
532 }
533
534 /* Test whether ARGV[GNoptind] points to a non-option argument.
535 Either it does not have option syntax, or there is an environment flag
536 from the shell indicating it is not an option. The later information
537 is only used when the used in the GNU libc. */
538#ifdef _LIBC
539#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0' \
540 || (GNoptind < nonoption_flags_len \
541 && __getopt_nonoption_flags[GNoptind] == '1'))
542#else
543#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0')
544#endif
545
546 if (nextchar == NULL || *nextchar == '\0')
547 {
548 /* Advance to the next ARGV-element. */
549
550 /* Give FIRST_NONOPT & LAST_NONOPT rational values if GNoptind has been
551 moved back by the user (who may also have changed the arguments). */
552 if (last_nonopt > GNoptind)
553 last_nonopt = GNoptind;
554 if (first_nonopt > GNoptind)
555 first_nonopt = GNoptind;
556
557 if (ordering == PERMUTE)
558 {
559 /* If we have just processed some options following some non-options,
560 exchange them so that the options come first. */
561
562 if (first_nonopt != last_nonopt && last_nonopt != GNoptind)
563 exchange ((char **) argv);
564 else if (last_nonopt != GNoptind)
565 first_nonopt = GNoptind;
566
567 /* Skip any additional non-options
568 and extend the range of non-options previously skipped. */
569
570 while (GNoptind < argc && NONOPTION_P)
571 GNoptind++;
572 last_nonopt = GNoptind;
573 }
574
575 /* The special ARGV-element `--' means premature end of options.
576 Skip it like a null option,
577 then exchange with previous non-options as if it were an option,
578 then skip everything else like a non-option. */
579 if (GNoptind != argc && !strcmp (argv[GNoptind], "--"))
580 {
581 GNoptind++;
582
583 if (first_nonopt != last_nonopt && last_nonopt != GNoptind)
584 exchange ((char **) argv);
585 else if (first_nonopt == last_nonopt)
586 first_nonopt = GNoptind;
587 last_nonopt = argc;
588
589 GNoptind = argc;
590 }
591
592 /* If we have done all the ARGV-elements, stop the scan
593 and back over any non-options that we skipped and permuted. */
594
595 if (GNoptind == argc)
596 {
597 /* Set the next-arg-index to point at the non-options
598 that we previously skipped, so the caller will digest them. */
599 if (first_nonopt != last_nonopt)
600 GNoptind = first_nonopt;
601 return -1;
602 }
603
604 /* If we have come to a non-option and did not permute it,
605 either stop the scan or describe it to the caller and pass it by. */
606
607 if (NONOPTION_P)
608 {
609 if (ordering == REQUIRE_ORDER)
610 return -1;
611 GNoptarg = argv[GNoptind++];
612 return 1;
613 }
614
615 /* We have found another option-ARGV-element.
616 Skip the initial punctuation. */
617
618 nextchar = (argv[GNoptind] + 1
619 + (longopts != NULL && argv[GNoptind][1] == '-'));
620 }
621
622 /* Decode the current option-ARGV-element. */
623
624 /* Check whether the ARGV-element is a long option.
625
626 If long_only and the ARGV-element has the form "-f", where f is
627 a valid short option, don't consider it an abbreviated form of
628 a long option that starts with f. Otherwise there would be no
629 way to give the -f short option.
630
631 On the other hand, if there's a long option "fubar" and
632 the ARGV-element is "-fu", do consider that an abbreviation of
633 the long option, just like "--fu", and not "-f" with arg "u".
634
635 This distinction seems to be the most useful approach. */
636
637 if (longopts != NULL
638 && (argv[GNoptind][1] == '-'
639 || (long_only
640 && (argv[GNoptind][2]
641 || !my_index (optstring, argv[GNoptind][1])))))
642 {
643 char *nameend;
644 const struct GNoption *p;
645 const struct GNoption *pfound = NULL;
646 int exact = 0;
647 int ambig = 0;
648 int indfound = -1;
649 int option_index;
650
651 for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
652 /* Do nothing. */ ;
653
654 /* Test all long options for either exact match
655 or abbreviated matches. */
656 for (p = longopts, option_index = 0; p->name; p++, option_index++)
657 if (!strncmp (p->name, nextchar, nameend - nextchar))
658 {
659 if ((unsigned int) (nameend - nextchar)
660 == (unsigned int) strlen (p->name))
661 {
662 /* Exact match found. */
663 pfound = p;
664 indfound = option_index;
665 exact = 1;
666 break;
667 }
668 else if (pfound == NULL)
669 {
670 /* First nonexact match found. */
671 pfound = p;
672 indfound = option_index;
673 }
674 else
675 /* Second or later nonexact match found. */
676 ambig = 1;
677 }
678
679 if (ambig && !exact)
680 {
681 if (GNopterr)
682 fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
683 argv[0], argv[GNoptind]);
684 nextchar += strlen (nextchar);
685 GNoptind++;
686 GNoptopt = 0;
687 return '?';
688 }
689
690 if (pfound != NULL)
691 {
692 option_index = indfound;
693 GNoptind++;
694 if (*nameend)
695 {
696 /* Don't test has_arg with >, because some C compilers don't
697 allow it to be used on enums. */
698 if (pfound->has_arg)
699 GNoptarg = nameend + 1;
700 else
701 {
702 if (GNopterr)
703 {
704 if (argv[GNoptind - 1][1] == '-')
705 /* --option */
706 fprintf (stderr,
707 _
708 ("%s: option `--%s' does not allow an argument\n"),
709 argv[0], pfound->name);
710 else
711 /* +option or -option */
712 fprintf (stderr,
713 _
714 ("%s: option `%c%s' does not allow an argument\n"),
715 argv[0], argv[GNoptind - 1][0],
716 pfound->name);
717 }
718 nextchar += strlen (nextchar);
719
720 GNoptopt = pfound->val;
721 return '?';
722 }
723 }
724 else if (pfound->has_arg == 1)
725 {
726 if (GNoptind < argc)
727 {
728 GNoptarg = argv[GNoptind++];
729 }
730 else
731 {
732 if (GNopterr)
733 {
734 fprintf (stderr,
735 _("%s: option `%s' requires an argument\n"),
736 argv[0], argv[GNoptind - 1]);
737 }
738 nextchar += strlen (nextchar);
739 GNoptopt = pfound->val;
740 return (optstring[0] == ':') ? ':' : '?';
741 }
742 }
743 nextchar += strlen (nextchar);
744 if (longind != NULL)
745 *longind = option_index;
746 if (pfound->flag)
747 {
748 *(pfound->flag) = pfound->val;
749 return 0;
750 }
751 return pfound->val;
752 }
753
754 /* Can't find it as a long option. If this is not getopt_long_only,
755 or the option starts with '--' or is not a valid short
756 option, then it's an error.
757 Otherwise interpret it as a short option. */
758 if (!long_only || argv[GNoptind][1] == '-'
759 || my_index (optstring, *nextchar) == NULL)
760 {
761 if (GNopterr)
762 {
763 if (argv[GNoptind][1] == '-')
764 /* --option */
765 fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
766 argv[0], nextchar);
767 else
768 /* +option or -option */
769 fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
770 argv[0], argv[GNoptind][0], nextchar);
771 }
772 nextchar = (char *) "";
773 GNoptind++;
774 GNoptopt = 0;
775 return '?';
776 }
777 }
778
779 /* Look at and handle the next short option-character. */
780
781 {
782 char c = *nextchar++;
783 char *temp = my_index (optstring, c);
784
785 /* Increment `GNoptind' when we start to process its last character. */
786 if (*nextchar == '\0')
787 ++GNoptind;
788
789 if (temp == NULL || c == ':')
790 {
791 if (GNopterr)
792 {
793 if (posixly_correct)
794 /* 1003.2 specifies the format of this message. */
795 fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
796 else
797 fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
798 }
799 GNoptopt = c;
800 return '?';
801 }
802 /* Convenience. Treat POSIX -W foo same as long option --foo */
803 if (temp[0] == 'W' && temp[1] == ';')
804 {
805 char *nameend;
806 const struct GNoption *p;
807 const struct GNoption *pfound = NULL;
808 int exact = 0;
809 int ambig = 0;
810 int indfound = 0;
811 int option_index;
812
813 /* This is an option that requires an argument. */
814 if (*nextchar != '\0')
815 {
816 GNoptarg = nextchar;
817 /* If we end this ARGV-element by taking the rest as an arg,
818 we must advance to the next element now. */
819 GNoptind++;
820 }
821 else if (GNoptind == argc)
822 {
823 if (GNopterr)
824 {
825 /* 1003.2 specifies the format of this message. */
826 fprintf (stderr, _("%s: option requires an argument -- %c\n"),
827 argv[0], c);
828 }
829 GNoptopt = c;
830 if (optstring[0] == ':')
831 c = ':';
832 else
833 c = '?';
834 return c;
835 }
836 else
837 /* We already incremented `GNoptind' once;
838 increment it again when taking next ARGV-elt as argument. */
839 GNoptarg = argv[GNoptind++];
840
841 /* GNoptarg is now the argument, see if it's in the
842 table of longopts. */
843
844 for (nextchar = nameend = GNoptarg; *nameend && *nameend != '=';
845 nameend++)
846 /* Do nothing. */ ;
847
848 /* Test all long options for either exact match
849 or abbreviated matches. */
850 for (p = longopts, option_index = 0; p->name; p++, option_index++)
851 if (!strncmp (p->name, nextchar, nameend - nextchar))
852 {
853 if ((unsigned int) (nameend - nextchar) == strlen (p->name))
854 {
855 /* Exact match found. */
856 pfound = p;
857 indfound = option_index;
858 exact = 1;
859 break;
860 }
861 else if (pfound == NULL)
862 {
863 /* First nonexact match found. */
864 pfound = p;
865 indfound = option_index;
866 }
867 else
868 /* Second or later nonexact match found. */
869 ambig = 1;
870 }
871 if (ambig && !exact)
872 {
873 if (GNopterr)
874 fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
875 argv[0], argv[GNoptind]);
876 nextchar += strlen (nextchar);
877 GNoptind++;
878 return '?';
879 }
880 if (pfound != NULL)
881 {
882 option_index = indfound;
883 if (*nameend)
884 {
885 /* Don't test has_arg with >, because some C compilers don't
886 allow it to be used on enums. */
887 if (pfound->has_arg)
888 GNoptarg = nameend + 1;
889 else
890 {
891 if (GNopterr)
892 fprintf (stderr, _("\
893%s: option `-W %s' does not allow an argument\n"), argv[0], pfound->name);
894
895 nextchar += strlen (nextchar);
896 return '?';
897 }
898 }
899 else if (pfound->has_arg == 1)
900 {
901 if (GNoptind < argc)
902 GNoptarg = argv[GNoptind++];
903 else
904 {
905 if (GNopterr)
906 fprintf (stderr,
907 _("%s: option `%s' requires an argument\n"),
908 argv[0], argv[GNoptind - 1]);
909 nextchar += strlen (nextchar);
910 return optstring[0] == ':' ? ':' : '?';
911 }
912 }
913 nextchar += strlen (nextchar);
914 if (longind != NULL)
915 *longind = option_index;
916 if (pfound->flag)
917 {
918 *(pfound->flag) = pfound->val;
919 return 0;
920 }
921 return pfound->val;
922 }
923 nextchar = NULL;
924 return 'W'; /* Let the application handle it. */
925 }
926 if (temp[1] == ':')
927 {
928 if (temp[2] == ':')
929 {
930 /* This is an option that accepts an argument optionally. */
931 if (*nextchar != '\0')
932 {
933 GNoptarg = nextchar;
934 GNoptind++;
935 }
936 else
937 GNoptarg = NULL;
938 nextchar = NULL;
939 }
940 else
941 {
942 /* This is an option that requires an argument. */
943 if (*nextchar != '\0')
944 {
945 GNoptarg = nextchar;
946 /* If we end this ARGV-element by taking the rest as an arg,
947 we must advance to the next element now. */
948 GNoptind++;
949 }
950 else if (GNoptind == argc)
951 {
952 if (GNopterr)
953 {
954 /* 1003.2 specifies the format of this message. */
955 fprintf (stderr,
956 _("%s: option requires an argument -- %c\n"),
957 argv[0], c);
958 }
959 GNoptopt = c;
960 if (optstring[0] == ':')
961 c = ':';
962 else
963 c = '?';
964 }
965 else
966 /* We already incremented `GNoptind' once;
967 increment it again when taking next ARGV-elt as argument. */
968 GNoptarg = argv[GNoptind++];
969 nextchar = NULL;
970 }
971 }
972 return c;
973 }
974}
975
976static int
977GNgetopt_long (int argc,
978 char *const *argv,
979 const char *options,
980 const struct GNoption *long_options, int *opt_index)
981{
982 return GN_getopt_internal (argc, argv, options, long_options, opt_index, 0);
983}
984
985/* ******************** now the GNUnet specific modifications... ********************* */
986
987/**
988 * Parse the command line.
989 *
990 * @param binaryName name of the binary / application with options
991 * @param cfg for storing/accessing configuration data
992 * @param allOptions defined options and handlers
993 * @param argc number of arguments
994 * @param argv actual arguments
995 * @return index into argv with first non-option
996 * argument, or -1 on error
997 */
998int
999GNUNET_GETOPT_run (const char *binaryOptions,
1000 struct GNUNET_CONFIGURATION_Handle *cfg,
1001 const struct GNUNET_GETOPT_CommandLineOption *allOptions,
1002 unsigned int argc, char *const *argv)
1003{
1004 struct GNoption *long_options;
1005 struct GNUNET_GETOPT_CommandLineProcessorContext clpc;
1006 int count;
1007 int i;
1008 char *shorts;
1009 int spos;
1010 int cont;
1011 int c;
1012
1013 GNUNET_assert (argc > 0);
1014 GNoptind = 0;
1015 clpc.binaryName = argv[0];
1016 clpc.binaryOptions = binaryOptions;
1017 clpc.allOptions = allOptions;
1018 clpc.argv = argv;
1019 clpc.argc = argc;
1020 clpc.cfg = cfg;
1021 count = 0;
1022 while (allOptions[count].name != NULL)
1023 count++;
1024 long_options = GNUNET_malloc (sizeof (struct GNoption) * (count + 1));
1025 shorts = GNUNET_malloc (count * 2 + 1);
1026 spos = 0;
1027 for (i = 0; i < count; i++)
1028 {
1029 long_options[i].name = allOptions[i].name;
1030 long_options[i].has_arg = allOptions[i].require_argument;
1031 long_options[i].flag = NULL;
1032 long_options[i].val = allOptions[i].shortName;
1033 shorts[spos++] = allOptions[i].shortName;
1034 if (allOptions[i].require_argument != 0)
1035 shorts[spos++] = ':';
1036 }
1037 long_options[count].name = NULL;
1038 long_options[count].has_arg = 0;
1039 long_options[count].flag = NULL;
1040 long_options[count].val = '\0';
1041 shorts[spos++] = '\0';
1042 cont = GNUNET_OK;
1043 /* main getopt loop */
1044 while (cont == GNUNET_OK)
1045 {
1046 int option_index = 0;
1047 c = GNgetopt_long (argc, argv, shorts, long_options, &option_index);
1048
1049 if (c == GNUNET_SYSERR)
1050 break; /* No more flags to process */
1051
1052 for (i = 0; i < count; i++)
1053 {
1054 clpc.currentArgument = GNoptind - 1;
1055 if ((char) c == allOptions[i].shortName)
1056 {
1057 cont = allOptions[i].processor (&clpc,
1058 allOptions[i].scls,
1059 allOptions[i].name, GNoptarg);
1060 break;
1061 }
1062 }
1063 if (i == count)
1064 {
1065 fprintf (stderr, _("Use --help to get a list of options.\n"));
1066 cont = GNUNET_SYSERR;
1067 }
1068 }
1069
1070 GNUNET_free (shorts);
1071 GNUNET_free (long_options);
1072 if (cont == GNUNET_SYSERR)
1073 return GNUNET_SYSERR;
1074 return GNoptind;
1075}
1076
1077/* end of getopt.c */
diff --git a/src/util/getopt_helpers.c b/src/util/getopt_helpers.c
new file mode 100644
index 000000000..3aea5d102
--- /dev/null
+++ b/src/util/getopt_helpers.c
@@ -0,0 +1,200 @@
1/*
2 This file is part of GNUnet
3 (C) 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/util/getopt_helpers.c
23 * @brief implements command line that sets option
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_getopt_lib.h"
30
31
32int
33GNUNET_GETOPT_print_version_ (struct GNUNET_GETOPT_CommandLineProcessorContext
34 *ctx, void *scls, const char *option,
35 const char *value)
36{
37 const char *version = scls;
38
39 printf ("%s v%s\n", ctx->binaryName, version);
40 return GNUNET_SYSERR;
41}
42
43
44
45#define BORDER 29
46
47int
48GNUNET_GETOPT_format_help_ (struct GNUNET_GETOPT_CommandLineProcessorContext
49 *ctx, void *scls, const char *option,
50 const char *value)
51{
52 const char *about = scls;
53 int slen;
54 int i;
55 int j;
56 int ml;
57 int p;
58 char *scp;
59 const char *trans;
60 const struct GNUNET_GETOPT_CommandLineOption *opt;
61
62 printf ("%s\n%s\n", ctx->binaryOptions, gettext (about));
63 printf (_
64 ("Arguments mandatory for long options are also mandatory for short options.\n"));
65 slen = 0;
66 i = 0;
67 opt = ctx->allOptions;
68 while (opt[i].description != NULL)
69 {
70 if (opt[i].shortName == '\0')
71 printf (" ");
72 else
73 printf (" -%c, ", opt[i].shortName);
74 printf ("--%s", opt[i].name);
75 slen = 8 + strlen (opt[i].name);
76 if (opt[i].argumentHelp != NULL)
77 {
78 printf ("=%s", opt[i].argumentHelp);
79 slen += 1 + strlen (opt[i].argumentHelp);
80 }
81 if (slen > BORDER)
82 {
83 printf ("\n%*s", BORDER, "");
84 slen = BORDER;
85 }
86 if (slen < BORDER)
87 {
88 printf ("%*s", BORDER - slen, "");
89 slen = BORDER;
90 }
91 trans = gettext (opt[i].description);
92 ml = strlen (trans);
93 p = 0;
94 OUTER:
95 while (ml - p > 78 - slen)
96 {
97 for (j = p + 78 - slen; j > p; j--)
98 {
99 if (isspace (trans[j]))
100 {
101 scp = GNUNET_malloc (j - p + 1);
102 memcpy (scp, &trans[p], j - p);
103 scp[j - p] = '\0';
104 printf ("%s\n%*s", scp, BORDER + 2, "");
105 GNUNET_free (scp);
106 p = j + 1;
107 slen = BORDER + 2;
108 goto OUTER;
109 }
110 }
111 /* could not find space to break line */
112 scp = GNUNET_malloc (78 - slen + 1);
113 memcpy (scp, &trans[p], 78 - slen);
114 scp[78 - slen] = '\0';
115 printf ("%s\n%*s", scp, BORDER + 2, "");
116 GNUNET_free (scp);
117 slen = BORDER + 2;
118 p = p + 78 - slen;
119 }
120 /* print rest */
121 if (p < ml)
122 printf ("%s\n", &trans[p]);
123 if (strlen (trans) == 0)
124 printf ("\n");
125 i++;
126 }
127 printf ("Report bugs to gnunet-developers@gnu.org.\n"
128 "GNUnet home page: http://www.gnu.org/software/gnunet/\n"
129 "General help using GNU software: http://www.gnu.org/gethelp/\n");
130 return GNUNET_SYSERR;
131}
132
133
134int
135GNUNET_GETOPT_increment_value (struct
136 GNUNET_GETOPT_CommandLineProcessorContext *ctx,
137 void *scls, const char *cmdLineOption,
138 const char *value)
139{
140 int *val = scls;
141 (*val)++;
142 return GNUNET_OK;
143}
144
145int
146GNUNET_GETOPT_set_one (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
147 void *scls, const char *option, const char *value)
148{
149 int *val = scls;
150 *val = 1;
151 return GNUNET_OK;
152}
153
154int
155GNUNET_GETOPT_set_string (struct GNUNET_GETOPT_CommandLineProcessorContext
156 *ctx, void *scls, const char *option,
157 const char *value)
158{
159 char **val = scls;
160
161 GNUNET_assert (value != NULL);
162 if (NULL != *val)
163 GNUNET_free (*val);
164 *val = GNUNET_strdup (value);
165 return GNUNET_OK;
166}
167
168int
169GNUNET_GETOPT_set_ulong (struct GNUNET_GETOPT_CommandLineProcessorContext
170 *ctx, void *scls, const char *option,
171 const char *value)
172{
173 unsigned long long *val = scls;
174 if (1 != SSCANF (value, "%llu", val))
175 {
176 fprintf (stderr,
177 _("You must pass a number to the `%s' option.\n"), "-X");
178 return GNUNET_SYSERR;
179 }
180 return GNUNET_OK;
181}
182
183
184int
185GNUNET_GETOPT_set_uint (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
186 void *scls, const char *option, const char *value)
187{
188 unsigned int *val = scls;
189
190 if (1 != SSCANF (value, "%u", val))
191 {
192 fprintf (stderr,
193 _("You must pass a number to the `%s' option.\n"), "-X");
194 return GNUNET_SYSERR;
195 }
196 return GNUNET_OK;
197}
198
199
200/* end of getopt_helpers.c */
diff --git a/src/util/network.c b/src/util/network.c
new file mode 100644
index 000000000..22c1ca632
--- /dev/null
+++ b/src/util/network.c
@@ -0,0 +1,1239 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/network/network.c
23 * @brief basic, low-level TCP networking interface
24 * @author Christian Grothoff
25 *
26 * This code is rather complex. Only modify it if you
27 * 1) Have a NEW testcase showing that the new code
28 * is needed and correct
29 * 2) All EXISTING testcases pass with the new code
30 * These rules should apply in general, but for this
31 * module they are VERY, VERY important.
32 *
33 * TODO:
34 * - can we merge receive_ready and receive_again?
35 * - can we integrate the nth.timeout_task with the write_task's timeout?
36 */
37
38#include "platform.h"
39#include "gnunet_common.h"
40#include "gnunet_network_lib.h"
41#include "gnunet_scheduler_lib.h"
42
43#define DEBUG_NETWORK GNUNET_NO
44
45struct GNUNET_NETWORK_TransmitHandle
46{
47
48 /**
49 * Function to call if the send buffer has notify_size
50 * bytes available.
51 */
52 GNUNET_NETWORK_TransmitReadyNotify notify_ready;
53
54 /**
55 * Closure for notify_ready.
56 */
57 void *notify_ready_cls;
58
59 /**
60 * Our socket handle.
61 */
62 struct GNUNET_NETWORK_SocketHandle *sh;
63
64 /**
65 * Timeout for receiving (in absolute time).
66 */
67 struct GNUNET_TIME_Absolute transmit_timeout;
68
69 /**
70 * Task called on timeout.
71 */
72 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
73
74 /**
75 * At what number of bytes available in the
76 * write buffer should the notify method be called?
77 */
78 size_t notify_size;
79
80};
81
82/**
83 * @brief handle for a network socket
84 */
85struct GNUNET_NETWORK_SocketHandle
86{
87
88 /**
89 * Scheduler that was used for the connect task.
90 */
91 struct GNUNET_SCHEDULER_Handle *sched;
92
93 /**
94 * Address information for connect (may be NULL).
95 */
96 struct addrinfo *ai;
97
98 /**
99 * Index for the next struct addrinfo for connect attempts (may be NULL)
100 */
101 struct addrinfo *ai_pos;
102
103 /**
104 * Network address of the other end-point, may be NULL.
105 */
106 struct sockaddr *addr;
107
108 /**
109 * Pointer to our write buffer.
110 */
111 char *write_buffer;
112
113 /**
114 * Size of our write buffer.
115 */
116 size_t write_buffer_size;
117
118 /**
119 * Current write-offset in write buffer (where
120 * would we write next).
121 */
122 size_t write_buffer_off;
123
124 /**
125 * Current read-offset in write buffer (how many
126 * bytes have already been send).
127 */
128 size_t write_buffer_pos;
129
130 /**
131 * Length of addr.
132 */
133 socklen_t addrlen;
134
135 /**
136 * Connect task that we may need to wait for.
137 */
138 GNUNET_SCHEDULER_TaskIdentifier connect_task;
139
140 /**
141 * Read task that we may need to wait for.
142 */
143 GNUNET_SCHEDULER_TaskIdentifier read_task;
144
145 /**
146 * Write task that we may need to wait for.
147 */
148 GNUNET_SCHEDULER_TaskIdentifier write_task;
149
150 /**
151 * The handle we return for GNUNET_NETWORK_notify_transmit_ready.
152 */
153 struct GNUNET_NETWORK_TransmitHandle nth;
154
155 /**
156 * Underlying OS's socket, set to -1 after fatal errors.
157 */
158 int sock;
159
160 /**
161 * Port to connect to.
162 */
163 uint16_t port;
164
165 /**
166 * Function to call on data received, NULL
167 * if no receive is pending.
168 */
169 GNUNET_NETWORK_Receiver receiver;
170
171 /**
172 * Closure for receiver.
173 */
174 void *receiver_cls;
175
176 /**
177 * Timeout for receiving (in absolute time).
178 */
179 struct GNUNET_TIME_Absolute receive_timeout;
180
181 /**
182 * Maximum number of bytes to read
183 * (for receiving).
184 */
185 size_t max;
186
187};
188
189
190/**
191 * Create a socket handle by boxing an existing OS socket. The OS
192 * socket should henceforth be no longer used directly.
193 * GNUNET_socket_destroy will close it.
194 *
195 * @param sched scheduler to use
196 * @param osSocket existing socket to box
197 * @param maxbuf maximum write buffer size for the socket (use
198 * 0 for sockets that need no write buffers, such as listen sockets)
199 * @return the boxed socket handle
200 */
201struct GNUNET_NETWORK_SocketHandle *
202GNUNET_NETWORK_socket_create_from_existing (struct GNUNET_SCHEDULER_Handle
203 *sched, int osSocket,
204 size_t maxbuf)
205{
206 struct GNUNET_NETWORK_SocketHandle *ret;
207 ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf);
208 ret->write_buffer = (char *) &ret[1];
209 ret->write_buffer_size = maxbuf;
210 ret->sock = osSocket;
211 ret->sched = sched;
212 return ret;
213}
214
215
216/**
217 * Create a socket handle by accepting on a listen socket. This
218 * function may block if the listen socket has no connection ready.
219 *
220 * @param sched scheduler to use
221 * @param access function to use to check if access is allowed
222 * @param access_cls closure for access
223 * @param lsock listen socket
224 * @param maxbuf maximum write buffer size for the socket (use
225 * 0 for sockets that need no write buffers, such as listen sockets)
226 * @return the socket handle, NULL on error
227 */
228struct GNUNET_NETWORK_SocketHandle *
229GNUNET_NETWORK_socket_create_from_accept (struct GNUNET_SCHEDULER_Handle
230 *sched,
231 GNUNET_NETWORK_AccessCheck access,
232 void *access_cls, int lsock,
233 size_t maxbuf)
234{
235 struct GNUNET_NETWORK_SocketHandle *ret;
236 char addr[32];
237 char msg[INET6_ADDRSTRLEN];
238 socklen_t addrlen;
239 int fam;
240 int fd;
241 int aret;
242 struct sockaddr_in *v4;
243 struct sockaddr_in6 *v6;
244 struct sockaddr *sa;
245 void *uaddr;
246
247 addrlen = sizeof (addr);
248 fd = accept (lsock, (struct sockaddr *) &addr, &addrlen);
249 if (fd == -1)
250 {
251 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "accept");
252 return NULL;
253 }
254 if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC))
255 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
256 "fcntl");
257 if (addrlen > sizeof (addr))
258 {
259 GNUNET_break (0);
260 GNUNET_break (0 == CLOSE (fd));
261 return NULL;
262 }
263
264 sa = (struct sockaddr *) addr;
265 v6 = (struct sockaddr_in6 *) addr;
266 if ((sa->sa_family == AF_INET6) && (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr)))
267 {
268 /* convert to V4 address */
269 v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
270 memset (v4, 0, sizeof (struct sockaddr_in));
271 v4->sin_family = AF_INET;
272 memcpy (&v4->sin_addr,
273 &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) -
274 sizeof (struct in_addr)],
275 sizeof (struct in_addr));
276 v4->sin_port = v6->sin6_port;
277 uaddr = v4;
278 addrlen = sizeof (struct sockaddr_in);
279 }
280 else
281 {
282 uaddr = GNUNET_malloc (addrlen);
283 memcpy (uaddr, addr, addrlen);
284 }
285
286 if ((access != NULL) &&
287 (GNUNET_YES != (aret = access (access_cls, uaddr, addrlen))))
288 {
289 if (aret == GNUNET_NO)
290 {
291 fam = ((struct sockaddr *) addr)->sa_family;
292 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
293 _("Access denied to `%s'\n"),
294 inet_ntop (fam,
295 (fam == AF_INET6)
296 ? (const void *) &((struct sockaddr_in6 *)
297 &addr)->
298 sin6_addr : (const void *)
299 &((struct sockaddr_in *) &addr)->sin_addr,
300 msg, sizeof (msg)));
301 }
302 GNUNET_break (0 == SHUTDOWN (fd, SHUT_RDWR));
303 GNUNET_break (0 == CLOSE (fd));
304 GNUNET_free (uaddr);
305 return NULL;
306 }
307 ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf);
308 ret->write_buffer = (char *) &ret[1];
309 ret->write_buffer_size = maxbuf;
310 ret->addr = uaddr;
311 ret->addrlen = addrlen;
312 ret->sock = fd;
313 ret->sched = sched;
314 return ret;
315}
316
317/**
318 * Obtain the network address of the other party.
319 *
320 * @param sock the client to get the address for
321 * @param addr where to store the address
322 * @param addrlen where to store the length of the address
323 * @return GNUNET_OK on success
324 */
325int
326GNUNET_NETWORK_socket_get_address (struct GNUNET_NETWORK_SocketHandle *sock,
327 void **addr, size_t * addrlen)
328{
329 if ((sock->addr == NULL) || (sock->addrlen == 0))
330 return GNUNET_NO;
331 *addr = GNUNET_malloc (sock->addrlen);
332 memcpy (*addr, sock->addr, sock->addrlen);
333 *addrlen = sock->addrlen;
334 return GNUNET_OK;
335}
336
337
338/**
339 * Set if a socket should use blocking or non-blocking IO.
340 *
341 * @return GNUNET_OK on success, GNUNET_SYSERR on error
342 */
343static int
344socket_set_blocking (int handle, int doBlock)
345{
346#if MINGW
347 u_long mode;
348 mode = !doBlock;
349#if HAVE_PLIBC_FD
350 if (ioctlsocket (plibc_fd_get_handle (handle), FIONBIO, &mode) ==
351 SOCKET_ERROR)
352 {
353 SetErrnoFromWinsockError (WSAGetLastError ());
354 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket");
355 return GNUNET_SYSERR;
356 }
357#else
358 if (ioctlsocket (handle, FIONBIO, &mode) == SOCKET_ERROR)
359 {
360 SetErrnoFromWinsockError (WSAGetLastError ());
361 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket");
362 return GNUNET_SYSERR;
363 }
364#endif
365 /* store the blocking mode */
366#if HAVE_PLIBC_FD
367 plibc_fd_set_blocking (handle, doBlock);
368#else
369 __win_SetHandleBlockingMode (handle, doBlock);
370#endif
371 return GNUNET_OK;
372
373#else
374 /* not MINGW */
375 int flags = fcntl (handle, F_GETFL);
376 if (flags == -1)
377 {
378 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");
379 return GNUNET_SYSERR;
380 }
381 if (doBlock)
382 flags &= ~O_NONBLOCK;
383 else
384 flags |= O_NONBLOCK;
385 if (0 != fcntl (handle, F_SETFL, flags))
386 {
387 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");
388 return GNUNET_SYSERR;
389 }
390 return GNUNET_OK;
391#endif
392}
393
394
395/**
396 * Initiate asynchronous TCP connect request.
397 *
398 * @param sock what socket to connect
399 * @return GNUNET_SYSERR error (no more addresses to try)
400 */
401static int
402try_connect (struct GNUNET_NETWORK_SocketHandle *sock)
403{
404 int s;
405
406 if (sock->addr != NULL)
407 {
408 GNUNET_free (sock->addr);
409 sock->addr = NULL;
410 sock->addrlen = 0;
411 }
412 while (1)
413 {
414 if (sock->ai_pos == NULL)
415 {
416 /* no more addresses to try, fatal! */
417 return GNUNET_SYSERR;
418 }
419 switch (sock->ai_pos->ai_family)
420 {
421 case AF_INET:
422 ((struct sockaddr_in *) sock->ai_pos->ai_addr)->sin_port =
423 htons (sock->port);
424 break;
425 case AF_INET6:
426 ((struct sockaddr_in6 *) sock->ai_pos->ai_addr)->sin6_port =
427 htons (sock->port);
428 break;
429 default:
430 sock->ai_pos = sock->ai_pos->ai_next;
431 continue;
432 }
433 s = SOCKET (sock->ai_pos->ai_family, SOCK_STREAM, 0);
434 if (s == -1)
435 {
436 /* maybe unsupported address family, try next */
437 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "socket");
438 sock->ai_pos = sock->ai_pos->ai_next;
439 continue;
440 }
441 if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC))
442 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
443 "fcntl");
444 if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO))
445 {
446 /* we'll treat this one as fatal */
447 GNUNET_break (0 == CLOSE (s));
448 return GNUNET_SYSERR;
449 }
450 if ((0 != CONNECT (s,
451 sock->ai_pos->ai_addr,
452 sock->ai_pos->ai_addrlen)) && (errno != EINPROGRESS))
453 {
454 /* maybe refused / unsupported address, try next */
455 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect");
456 GNUNET_break (0 == CLOSE (s));
457 continue;
458 }
459 break;
460 }
461 /* got one! copy address information! */
462 sock->addrlen = sock->ai_pos->ai_addrlen;
463 sock->addr = GNUNET_malloc (sock->addrlen);
464 memcpy (sock->addr, sock->ai_pos->ai_addr, sock->addrlen);
465 sock->ai_pos = sock->ai_pos->ai_next;
466 sock->sock = s;
467 return GNUNET_OK;
468}
469
470
471/**
472 * Scheduler let us know that we're either ready to
473 * write on the socket OR connect timed out. Do the
474 * right thing.
475 */
476static void
477connect_continuation (void *cls,
478 const struct GNUNET_SCHEDULER_TaskContext *tc)
479{
480 struct GNUNET_NETWORK_SocketHandle *sock = cls;
481 unsigned int len;
482 int error;
483
484 /* nobody needs to wait for us anymore... */
485 sock->connect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
486 /* Note: write-ready does NOT mean connect succeeded,
487 we need to use getsockopt to be sure */
488 len = sizeof (error);
489 errno = 0;
490 error = 0;
491 if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
492 (0 != getsockopt (sock->sock, SOL_SOCKET, SO_ERROR, &error, &len)) ||
493 (error != 0) || (errno != 0))
494 {
495 /* connect failed / timed out */
496 GNUNET_break (0 == CLOSE (sock->sock));
497 sock->sock = -1;
498 if (GNUNET_SYSERR == try_connect (sock))
499 {
500 /* failed for good */
501 GNUNET_break (sock->ai_pos == NULL);
502 freeaddrinfo (sock->ai);
503 sock->ai = NULL;
504 return;
505 }
506 sock->connect_task = GNUNET_SCHEDULER_add_write (tc->sched, GNUNET_NO, /* abort on shutdown */
507 GNUNET_SCHEDULER_PRIORITY_KEEP,
508 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
509 GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT,
510 sock->sock,
511 &connect_continuation,
512 sock);
513 return;
514 }
515 /* connect succeeded! clean up "ai" */
516#if DEBUG_NETWORK
517 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection succeeded!\n");
518#endif
519 freeaddrinfo (sock->ai);
520 sock->ai_pos = NULL;
521 sock->ai = NULL;
522}
523
524
525/**
526 * Create a socket handle by (asynchronously) connecting to a host.
527 * This function returns immediately, even if the connection has not
528 * yet been established. This function only creates TCP connections.
529 *
530 * @param sched scheduler to use
531 * @param hostname name of the host to connect to
532 * @param port port to connect to
533 * @param maxbuf maximum write buffer size for the socket (use
534 * 0 for sockets that need no write buffers, such as listen sockets)
535 * @return the socket handle
536 */
537struct GNUNET_NETWORK_SocketHandle *
538GNUNET_NETWORK_socket_create_from_connect (struct GNUNET_SCHEDULER_Handle
539 *sched, const char *hostname,
540 uint16_t port, size_t maxbuf)
541{
542 struct GNUNET_NETWORK_SocketHandle *ret;
543 struct addrinfo hints;
544 int ec;
545
546 ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf);
547 ret->sock = -1;
548 ret->sched = sched;
549 ret->write_buffer = (char *) &ret[1];
550 ret->write_buffer_size = maxbuf;
551 ret->port = port;
552 memset (&hints, 0, sizeof (hints));
553 hints.ai_family = AF_UNSPEC;
554 hints.ai_socktype = SOCK_STREAM;
555 if (0 != (ec = getaddrinfo (hostname, NULL, &hints, &ret->ai)))
556 {
557 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
558 "`%s' failed for hostname `%s': %s\n",
559 "getaddrinfo", hostname, gai_strerror (ec));
560 GNUNET_free (ret);
561 return NULL;
562 }
563 ret->ai_pos = ret->ai;
564 if (GNUNET_SYSERR == try_connect (ret))
565 {
566 freeaddrinfo (ret->ai);
567 GNUNET_free (ret);
568 return NULL;
569 }
570 ret->connect_task = GNUNET_SCHEDULER_add_write (sched, GNUNET_NO, /* abort on shutdown */
571 GNUNET_SCHEDULER_PRIORITY_KEEP,
572 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
573 GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT,
574 ret->sock,
575 &connect_continuation, ret);
576 return ret;
577
578}
579
580
581/**
582 * Create a socket handle by (asynchronously) connecting to a host.
583 * This function returns immediately, even if the connection has not
584 * yet been established. This function only creates TCP connections.
585 *
586 * @param sched scheduler to use
587 * @param af_family address family to use
588 * @param serv_addr server address
589 * @param addrlen length of server address
590 * @param maxbuf maximum write buffer size for the socket (use
591 * 0 for sockets that need no write buffers, such as listen sockets)
592 * @return the socket handle
593 */
594struct GNUNET_NETWORK_SocketHandle *
595GNUNET_NETWORK_socket_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle
596 *sched, int af_family,
597 const struct sockaddr *serv_addr,
598 socklen_t addrlen, size_t maxbuf)
599{
600 int s;
601
602 s = SOCKET (af_family, SOCK_STREAM, 0);
603 if (s == -1)
604 {
605 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
606 GNUNET_ERROR_TYPE_BULK, "socket");
607 return NULL;
608 }
609 if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC))
610 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
611 "fcntl");
612 if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO))
613 {
614 /* we'll treat this one as fatal */
615 GNUNET_break (0 == CLOSE (s));
616 return NULL;
617 }
618 if ((0 != CONNECT (s, serv_addr, addrlen)) && (errno != EINPROGRESS))
619 {
620 /* maybe refused / unsupported address, try next */
621 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect");
622 GNUNET_break (0 == CLOSE (s));
623 return NULL;
624 }
625 return GNUNET_NETWORK_socket_create_from_existing (sched, s, maxbuf);
626}
627
628
629/**
630 * Check if socket is valid (no fatal errors have happened so far).
631 * Note that a socket that is still trying to connect is considered
632 * valid.
633 *
634 * @param sock socket to check
635 * @return GNUNET_YES if valid, GNUNET_NO otherwise
636 */
637int
638GNUNET_NETWORK_socket_check (struct GNUNET_NETWORK_SocketHandle *sock)
639{
640 if (sock->ai != NULL)
641 return GNUNET_YES; /* still trying to connect */
642 return (sock->sock == -1) ? GNUNET_NO : GNUNET_YES;
643}
644
645
646/**
647 * Scheduler let us know that the connect task is finished (or was
648 * cancelled due to shutdown). Now really clean up.
649 */
650static void
651destroy_continuation (void *cls,
652 const struct GNUNET_SCHEDULER_TaskContext *tc)
653{
654 struct GNUNET_NETWORK_SocketHandle *sock = cls;
655 GNUNET_NETWORK_TransmitReadyNotify notify;
656
657 if (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
658 {
659#if DEBUG_NETWORK
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "Destroy code waiting for writes to complete.\n");
662#endif
663 GNUNET_SCHEDULER_add_after (sock->sched,
664 GNUNET_YES,
665 GNUNET_SCHEDULER_PRIORITY_KEEP,
666 sock->write_task,
667 &destroy_continuation, sock);
668 return;
669 }
670 if (sock->sock != -1)
671 {
672#if DEBUG_NETWORK
673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket.\n");
674#endif
675 SHUTDOWN (sock->sock, SHUT_RDWR);
676 }
677 if (sock->read_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
678 {
679#if DEBUG_NETWORK
680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681 "Destroy code waiting for receive to complete.\n");
682#endif
683 GNUNET_SCHEDULER_add_after (sock->sched,
684 GNUNET_YES,
685 GNUNET_SCHEDULER_PRIORITY_KEEP,
686 sock->read_task,
687 &destroy_continuation, sock);
688 return;
689 }
690 if (NULL != (notify = sock->nth.notify_ready))
691 {
692 sock->nth.notify_ready = NULL;
693 notify (sock->nth.notify_ready_cls, 0, NULL);
694 if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
695 {
696 GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task);
697 sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
698 }
699 }
700 if (sock->sock != -1)
701 {
702#if DEBUG_NETWORK
703 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Closing socket.\n");
704#endif
705 GNUNET_break (0 == CLOSE (sock->sock));
706 }
707 GNUNET_free_non_null (sock->addr);
708 if (sock->ai != NULL)
709 freeaddrinfo (sock->ai);
710 GNUNET_free (sock);
711}
712
713
714/**
715 * Close the socket and free associated resources. Pending
716 * transmissions are simply dropped. A pending receive call will be
717 * called with an error code of "EPIPE".
718 *
719 * @param sock socket to destroy
720 */
721void
722GNUNET_NETWORK_socket_destroy (struct GNUNET_NETWORK_SocketHandle *sock)
723{
724#if DEBUG_NETWORK
725 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
726 "Network asked to destroy socket %p\n", sock);
727#endif
728 if (sock->write_buffer_off == 0)
729 sock->ai_pos = NULL; /* if we're still trying to connect and have
730 no message pending, stop trying! */
731 GNUNET_assert (sock->sched != NULL);
732 GNUNET_SCHEDULER_add_after (sock->sched,
733 GNUNET_YES,
734 GNUNET_SCHEDULER_PRIORITY_KEEP,
735 sock->connect_task,
736 &destroy_continuation, sock);
737}
738
739/**
740 * Tell the receiver callback that a timeout was reached.
741 */
742static void
743signal_timeout (struct GNUNET_NETWORK_SocketHandle *sh)
744{
745 GNUNET_NETWORK_Receiver receiver;
746
747#if DEBUG_NETWORK
748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
749 "Network signals timeout to receiver!\n");
750#endif
751 GNUNET_assert (NULL != (receiver = sh->receiver));
752 sh->receiver = NULL;
753 receiver (sh->receiver_cls, NULL, 0, NULL, 0, 0);
754}
755
756
757/**
758 * Tell the receiver callback that we had an IO error.
759 */
760static void
761signal_error (struct GNUNET_NETWORK_SocketHandle *sh, int errcode)
762{
763 GNUNET_NETWORK_Receiver receiver;
764 GNUNET_assert (NULL != (receiver = sh->receiver));
765 sh->receiver = NULL;
766 receiver (sh->receiver_cls, NULL, 0, sh->addr, sh->addrlen, errcode);
767}
768
769
770/**
771 * This function is called once we either timeout
772 * or have data ready to read.
773 */
774static void
775receive_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
776{
777 struct GNUNET_NETWORK_SocketHandle *sh = cls;
778 struct GNUNET_TIME_Absolute now;
779 char buffer[sh->max];
780 ssize_t ret;
781 GNUNET_NETWORK_Receiver receiver;
782
783 sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
784 now = GNUNET_TIME_absolute_get ();
785 if ((now.value > sh->receive_timeout.value) ||
786 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) ||
787 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
788 {
789#if DEBUG_NETWORK
790 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
791 "Receive encounters error: timeout...\n");
792#endif
793 signal_timeout (sh);
794 return;
795 }
796 if (sh->sock == -1)
797 {
798 /* connect failed for good */
799#if DEBUG_NETWORK
800 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801 "Receive encounters error, socket closed...\n");
802#endif
803 signal_error (sh, ECONNREFUSED);
804 return;
805 }
806 GNUNET_assert (FD_ISSET (sh->sock, tc->read_ready));
807RETRY:
808 ret = RECV (sh->sock, buffer, sh->max, MSG_DONTWAIT);
809 if (ret == -1)
810 {
811 if (errno == EINTR)
812 goto RETRY;
813#if DEBUG_NETWORK
814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815 "Error receiving: %s\n", STRERROR (errno));
816#endif
817 signal_error (sh, errno);
818 return;
819 }
820#if DEBUG_NETWORK
821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
822 "Receive got %d bytes from OS!\n", ret);
823#endif
824 GNUNET_assert (NULL != (receiver = sh->receiver));
825 sh->receiver = NULL;
826 receiver (sh->receiver_cls, buffer, ret, sh->addr, sh->addrlen, 0);
827}
828
829
830/**
831 * This function is called after establishing a connection either has
832 * succeeded or timed out. Note that it is possible that the attempt
833 * timed out and that we're immediately retrying. If we are retrying,
834 * we need to wait again (or timeout); if we succeeded, we need to
835 * wait for data (or timeout).
836 */
837static void
838receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
839{
840 struct GNUNET_NETWORK_SocketHandle *sh = cls;
841 struct GNUNET_TIME_Absolute now;
842
843 sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
844 if ((sh->sock == -1) &&
845 (sh->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK))
846 {
847 /* not connected and no longer trying */
848#if DEBUG_NETWORK
849 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
850 "Receive encounters error, socket closed...\n");
851#endif
852 signal_error (sh, ECONNREFUSED);
853 return;
854 }
855 now = GNUNET_TIME_absolute_get ();
856 if ((now.value > sh->receive_timeout.value) ||
857 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
858 {
859#if DEBUG_NETWORK
860 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
861 "Receive encounters error: timeout...\n");
862#endif
863 signal_timeout (sh);
864 return;
865 }
866 if (sh->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
867 {
868 /* connect was retried */
869#if DEBUG_NETWORK
870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
871 "Receive still waits on connect...\n");
872#endif
873 sh->read_task = GNUNET_SCHEDULER_add_after (tc->sched,
874 GNUNET_YES,
875 GNUNET_SCHEDULER_PRIORITY_KEEP,
876 sh->connect_task,
877 &receive_again, sh);
878 }
879 else
880 {
881 /* connect succeeded, wait for data! */
882#if DEBUG_NETWORK
883 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884 "Receive now waits for socket...\n");
885#endif
886 sh->read_task = GNUNET_SCHEDULER_add_read (tc->sched,
887 GNUNET_YES,
888 GNUNET_SCHEDULER_PRIORITY_KEEP,
889 sh->connect_task,
890 GNUNET_TIME_absolute_get_remaining
891 (sh->receive_timeout),
892 sh->sock, &receive_ready,
893 sh);
894 }
895}
896
897
898/**
899 * Receive data from the given socket. Note that this function will
900 * call "receiver" asynchronously using the scheduler. It will
901 * "immediately" return. Note that there MUST only be one active
902 * receive call per socket at any given point in time (so do not
903 * call receive again until the receiver callback has been invoked).
904 *
905 * @param sched scheduler to use
906 * @param sock socket handle
907 * @param max maximum number of bytes to read
908 * @param timeout maximum amount of time to wait (use -1 for "forever")
909 * @param receiver function to call with received data
910 * @param receiver_cls closure for receiver
911 * @return scheduler task ID used for receiving, GNUNET_SCHEDULER_NO_PREREQUISITE_TASK on error
912 */
913GNUNET_SCHEDULER_TaskIdentifier
914GNUNET_NETWORK_receive (struct GNUNET_NETWORK_SocketHandle *sock,
915 size_t max,
916 struct GNUNET_TIME_Relative timeout,
917 GNUNET_NETWORK_Receiver receiver, void *receiver_cls)
918{
919 struct GNUNET_SCHEDULER_TaskContext tc;
920#if DEBUG_NETWORK
921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
922 "Network asked to receive from socket...\n");
923#endif
924 GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) &&
925 (sock->receiver == NULL));
926 sock->receiver = receiver;
927 sock->receiver_cls = receiver_cls;
928 sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
929 sock->max = max;
930 memset (&tc, 0, sizeof (tc));
931 tc.sched = sock->sched;
932 tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE;
933 receive_again (sock, &tc);
934 return sock->read_task;
935}
936
937
938/**
939 * Cancel receive job on the given socket. Note that the
940 * receiver callback must not have been called yet in order
941 * for the cancellation to be valid.
942 *
943 * @param sock socket handle
944 * @param task task identifier returned from the receive call
945 * @return closure of the original receiver callback
946 */
947void *
948GNUNET_NETWORK_receive_cancel (struct GNUNET_NETWORK_SocketHandle *sock,
949 GNUNET_SCHEDULER_TaskIdentifier task)
950{
951 GNUNET_assert (sock->read_task == task);
952 GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->sched, task));
953 sock->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
954 sock->receiver = NULL;
955 return sock->receiver_cls;
956}
957
958
959/**
960 * Try to call the transmit notify method (check if we do
961 * have enough space available first)!
962 *
963 * @param sock socket for which we should do this processing
964 * @return GNUNET_YES if we were able to call notify
965 */
966static int
967process_notify (struct GNUNET_NETWORK_SocketHandle *sock)
968{
969 size_t used;
970 size_t avail;
971 size_t size;
972 GNUNET_NETWORK_TransmitReadyNotify notify;
973
974 GNUNET_assert (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
975 if (NULL == (notify = sock->nth.notify_ready))
976 return GNUNET_NO;
977 used = sock->write_buffer_off - sock->write_buffer_pos;
978 avail = sock->write_buffer_size - used;
979 size = sock->nth.notify_size;
980 if (sock->nth.notify_size > avail)
981 return GNUNET_NO;
982 sock->nth.notify_ready = NULL;
983 if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
984 {
985 GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task);
986 sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
987 }
988 if (sock->write_buffer_size - sock->write_buffer_off < size)
989 {
990 /* need to compact */
991 memmove (sock->write_buffer,
992 &sock->write_buffer[sock->write_buffer_pos], used);
993 sock->write_buffer_off -= sock->write_buffer_pos;
994 sock->write_buffer_pos = 0;
995 }
996 GNUNET_assert (sock->write_buffer_size - sock->write_buffer_off >= size);
997 size = notify (sock->nth.notify_ready_cls,
998 sock->write_buffer_size - sock->write_buffer_off,
999 &sock->write_buffer[sock->write_buffer_off]);
1000 sock->write_buffer_off += size;
1001 return GNUNET_YES;
1002}
1003
1004
1005/**
1006 * Task invoked by the scheduler when a call to transmit
1007 * is timing out (we never got enough buffer space to call
1008 * the callback function before the specified timeout
1009 * expired).
1010 *
1011 * This task notifies the client about the timeout.
1012 */
1013static void
1014transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1015{
1016 struct GNUNET_NETWORK_SocketHandle *sock = cls;
1017 GNUNET_NETWORK_TransmitReadyNotify notify;
1018
1019#if DEBUG_NETWORK
1020 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmit fails, time out reached.\n");
1021#endif
1022 notify = sock->nth.notify_ready;
1023 sock->nth.notify_ready = NULL;
1024 notify (sock->nth.notify_ready_cls, 0, NULL);
1025}
1026
1027
1028static void
1029transmit_error (struct GNUNET_NETWORK_SocketHandle *sock)
1030{
1031 if (sock->nth.notify_ready == NULL)
1032 return; /* nobody to tell about it */
1033 if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1034 {
1035 GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task);
1036 sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1037 }
1038 transmit_timeout (sock, NULL);
1039}
1040
1041
1042/**
1043 * See if we are now connected. If not, wait longer for
1044 * connect to succeed. If connected, we should be able
1045 * to write now as well, unless we timed out.
1046 */
1047static void
1048transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1049{
1050 struct GNUNET_NETWORK_SocketHandle *sock = cls;
1051 ssize_t ret;
1052 size_t have;
1053
1054#if DEBUG_NETWORK
1055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1056 "Transmit ready called --- will try to send\n");
1057#endif
1058 GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1059 sock->write_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1060 if (sock->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1061 {
1062 /* still waiting for connect */
1063#if DEBUG_NETWORK
1064 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1065 "Transmission still waiting for connect...\n");
1066#endif
1067 GNUNET_assert (sock->write_task ==
1068 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1069 sock->write_task =
1070 GNUNET_SCHEDULER_add_delayed (tc->sched, GNUNET_NO,
1071 GNUNET_SCHEDULER_PRIORITY_KEEP,
1072 sock->connect_task,
1073 GNUNET_TIME_UNIT_ZERO, &transmit_ready,
1074 sock);
1075 return;
1076 }
1077 if (sock->sock == -1)
1078 {
1079 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1080 _
1081 ("Did not transmit request, socket closed or connect failed.\n"));
1082 transmit_error (sock);
1083 return; /* connect failed for good, we're finished */
1084 }
1085 if ((tc->write_ready == NULL) || (!FD_ISSET (sock->sock, tc->write_ready)))
1086 {
1087#if DEBUG_NETWORK
1088 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1089 "Socket not yet ready for writing, will wait for that.\n");
1090#endif
1091 goto SCHEDULE_WRITE;
1092 }
1093 GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos);
1094 process_notify (sock);
1095 have = sock->write_buffer_off - sock->write_buffer_pos;
1096 if (have == 0)
1097 {
1098#if DEBUG_NETWORK
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No data ready for writing.\n");
1100#endif
1101 return;
1102 }
1103RETRY:
1104 ret = SEND (sock->sock,
1105 &sock->write_buffer[sock->write_buffer_pos],
1106 have, MSG_DONTWAIT | MSG_NOSIGNAL);
1107 if (ret == -1)
1108 {
1109 if (errno == EINTR)
1110 goto RETRY;
1111#if DEBUG_NETWORK
1112 GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send");
1113#endif
1114 SHUTDOWN (sock->sock, SHUT_RDWR);
1115 GNUNET_break (0 == CLOSE (sock->sock));
1116 sock->sock = -1;
1117 transmit_error (sock);
1118 return;
1119 }
1120#if DEBUG_NETWORK
1121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitted %d bytes to OS\n", ret);
1122#endif
1123 sock->write_buffer_pos += ret;
1124 if (sock->write_buffer_pos == sock->write_buffer_off)
1125 {
1126 /* transmitted all pending data */
1127 sock->write_buffer_pos = 0;
1128 sock->write_buffer_off = 0;
1129#if DEBUG_NETWORK
1130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1131 "Transmission buffer now empty.\n", ret);
1132#endif
1133 }
1134 if ((sock->write_buffer_off == 0) && (NULL == sock->nth.notify_ready))
1135 return; /* all data sent! */
1136 /* not done writing, schedule more */
1137#if DEBUG_NETWORK
1138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139 "More data ready for transmission, scheduling task again!\n");
1140#endif
1141SCHEDULE_WRITE:
1142 if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1143 sock->write_task =
1144 GNUNET_SCHEDULER_add_write (tc->sched,
1145 GNUNET_NO,
1146 GNUNET_SCHEDULER_PRIORITY_KEEP,
1147 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1148 GNUNET_TIME_UNIT_FOREVER_REL,
1149 sock->sock, &transmit_ready, sock);
1150}
1151
1152
1153/**
1154 * Ask the socket to call us once the specified number of bytes
1155 * are free in the transmission buffer. May call the notify
1156 * method immediately if enough space is available.
1157 *
1158 * @param sock socket
1159 * @param size number of bytes to send
1160 * @param timeout after how long should we give up (and call
1161 * notify with buf NULL and size 0)?
1162 * @param notify function to call
1163 * @param notify_cls closure for notify
1164 * @return non-NULL if the notify callback was queued,
1165 * NULL if we are already going to notify someone else (busy)
1166 */
1167struct GNUNET_NETWORK_TransmitHandle *
1168GNUNET_NETWORK_notify_transmit_ready (struct GNUNET_NETWORK_SocketHandle
1169 *sock, size_t size,
1170 struct GNUNET_TIME_Relative timeout,
1171 GNUNET_NETWORK_TransmitReadyNotify
1172 notify, void *notify_cls)
1173{
1174 if (sock->nth.notify_ready != NULL)
1175 return NULL;
1176 GNUNET_assert (notify != NULL);
1177 GNUNET_assert (sock->write_buffer_size >= size);
1178
1179 if ((sock->sock == -1) &&
1180 (sock->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK))
1181 {
1182#if DEBUG_NETWORK
1183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1184 "Transmit fails, connection failed.\n");
1185#endif
1186 notify (notify_cls, 0, NULL);
1187 return &sock->nth;
1188 }
1189 GNUNET_assert (sock->write_buffer_off <= sock->write_buffer_size);
1190 GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_size);
1191 GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_off);
1192 sock->nth.notify_ready = notify;
1193 sock->nth.notify_ready_cls = notify_cls;
1194 sock->nth.sh = sock;
1195 sock->nth.notify_size = size;
1196 sock->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout);
1197 sock->nth.timeout_task = GNUNET_SCHEDULER_add_delayed (sock->sched,
1198 GNUNET_NO,
1199 GNUNET_SCHEDULER_PRIORITY_KEEP,
1200 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1201 timeout,
1202 &transmit_timeout,
1203 sock);
1204#if DEBUG_NETWORK
1205 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1206 "Scheduling asynchronous transmission once connect is done...\n");
1207#endif
1208 if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
1209 sock->write_task = GNUNET_SCHEDULER_add_delayed (sock->sched,
1210 GNUNET_NO,
1211 GNUNET_SCHEDULER_PRIORITY_KEEP,
1212 sock->connect_task,
1213 GNUNET_TIME_UNIT_ZERO,
1214 &transmit_ready, sock);
1215 return &sock->nth;
1216}
1217
1218
1219/**
1220 * Cancel the specified transmission-ready
1221 * notification.
1222 */
1223void
1224GNUNET_NETWORK_notify_transmit_ready_cancel (struct
1225 GNUNET_NETWORK_TransmitHandle *h)
1226{
1227 GNUNET_assert (h->notify_ready != NULL);
1228 GNUNET_SCHEDULER_cancel (h->sh->sched, h->timeout_task);
1229 h->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1230 h->notify_ready = NULL;
1231}
1232
1233
1234#if 0 /* keep Emacsens' auto-indent happy */
1235{
1236#endif
1237#ifdef __cplusplus
1238}
1239#endif
diff --git a/src/util/os_installation.c b/src/util/os_installation.c
new file mode 100644
index 000000000..201095544
--- /dev/null
+++ b/src/util/os_installation.c
@@ -0,0 +1,443 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/util/os_installation.c
23 * @brief get paths used by the program
24 * @author Milan
25 */
26
27#ifdef __cplusplus
28extern "C"
29{
30#if 0 /* keep Emacsens' auto-indent happy */
31}
32#endif
33#endif
34
35#include <sys/stat.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39
40#include "platform.h"
41#include "gnunet_common.h"
42#include "gnunet_configuration_lib.h"
43#include "gnunet_disk_lib.h"
44#include "gnunet_os_lib.h"
45#if OSX
46#include <mach-o/ldsyms.h>
47#include <mach-o/dyld.h>
48#endif
49
50#if LINUX
51/**
52 * Try to determine path by reading /proc/PID/exe
53 */
54static char *
55get_path_from_proc_maps ()
56{
57 char fn[64];
58 char *line;
59 char *dir;
60 FILE *f;
61
62 GNUNET_snprintf (fn, 64, "/proc/%u/maps", getpid ());
63 line = GNUNET_malloc (1024);
64 dir = GNUNET_malloc (1024);
65 f = fopen (fn, "r");
66 if (f != NULL)
67 {
68 while (NULL != fgets (line, 1024, f))
69 {
70 if ((1 == sscanf (line,
71 "%*x-%*x %*c%*c%*c%*c %*x %*2u:%*2u %*u%*[ ]%s",
72 dir)) && (NULL != strstr (dir, "libgnunetutil")))
73 {
74 strstr (dir, "libgnunetutil")[0] = '\0';
75 fclose (f);
76 GNUNET_free (line);
77 return dir;
78 }
79 }
80 fclose (f);
81 }
82 GNUNET_free (dir);
83 GNUNET_free (line);
84 return NULL;
85}
86
87/**
88 * Try to determine path by reading /proc/PID/exe
89 */
90static char *
91get_path_from_proc_exe ()
92{
93 char fn[64];
94 char *lnk;
95 size_t size;
96
97 GNUNET_snprintf (fn, 64, "/proc/%u/exe", getpid ());
98 lnk = GNUNET_malloc (1024);
99 size = readlink (fn, lnk, 1023);
100 if ((size == 0) || (size >= 1024))
101 {
102 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
103 GNUNET_free (lnk);
104 return NULL;
105 }
106 lnk[size] = '\0';
107 while ((lnk[size] != '/') && (size > 0))
108 size--;
109 if ((size < 4) || (lnk[size - 4] != '/'))
110 {
111 /* not installed in "/bin/" -- binary path probably useless */
112 GNUNET_free (lnk);
113 return NULL;
114 }
115 lnk[size] = '\0';
116 return lnk;
117}
118#endif
119
120#if WINDOWS
121/**
122 * Try to determine path with win32-specific function
123 */
124static char *
125get_path_from_module_filename ()
126{
127 char *path;
128 char *idx;
129
130 path = GNUNET_malloc (4097);
131 GetModuleFileName (NULL, path, 4096);
132 idx = path + strlen (path);
133 while ((idx > path) && (*idx != '\\') && (*idx != '/'))
134 idx--;
135 *idx = '\0';
136 return path;
137}
138#endif
139
140#if OSX
141typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize);
142
143static char *
144get_path_from_NSGetExecutablePath ()
145{
146 static char zero = '\0';
147 char *path;
148 size_t len;
149 MyNSGetExecutablePathProto func;
150 int ret;
151
152 path = NULL;
153 func =
154 (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath");
155 if (!func)
156 return NULL;
157 path = &zero;
158 len = 0;
159 /* get the path len, including the trailing \0 */
160 func (path, &len);
161 if (len == 0)
162 return NULL;
163 path = GNUNET_malloc (len);
164 ret = func (path, &len);
165 if (ret != 0)
166 {
167 GNUNET_free (path);
168 return NULL;
169 }
170 len = strlen (path);
171 while ((path[len] != '/') && (len > 0))
172 len--;
173 path[len] = '\0';
174 return path;
175}
176
177static char *
178get_path_from_dyld_image ()
179{
180 const char *path;
181 char *p, *s;
182 int i;
183 int c;
184
185 p = NULL;
186 c = _dyld_image_count ();
187 for (i = 0; i < c; i++)
188 {
189 if (_dyld_get_image_header (i) == &_mh_dylib_header)
190 {
191 path = _dyld_get_image_name (i);
192 if (path != NULL && strlen (path) > 0)
193 {
194 p = strdup (path);
195 s = p + strlen (p);
196 while ((s > p) && (*s != '/'))
197 s--;
198 s++;
199 *s = '\0';
200 }
201 break;
202 }
203 }
204 return p;
205}
206#endif
207
208static char *
209get_path_from_PATH ()
210{
211 char *path;
212 char *pos;
213 char *end;
214 char *buf;
215 const char *p;
216 size_t size;
217
218 p = getenv ("PATH");
219 if (p == NULL)
220 return NULL;
221 path = GNUNET_strdup (p); /* because we write on it */
222 buf = GNUNET_malloc (strlen (path) + 20);
223 size = strlen (path);
224 pos = path;
225
226 while (NULL != (end = strchr (pos, ':')))
227 {
228 *end = '\0';
229 sprintf (buf, "%s/%s", pos, "gnunetd");
230 if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
231 {
232 pos = GNUNET_strdup (pos);
233 GNUNET_free (buf);
234 GNUNET_free (path);
235 return pos;
236 }
237 pos = end + 1;
238 }
239 sprintf (buf, "%s/%s", pos, "gnunetd");
240 if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
241 {
242 pos = GNUNET_strdup (pos);
243 GNUNET_free (buf);
244 GNUNET_free (path);
245 return pos;
246 }
247 GNUNET_free (buf);
248 GNUNET_free (path);
249 return NULL;
250}
251
252static char *
253get_path_from_GNUNET_PREFIX ()
254{
255 const char *p;
256
257 p = getenv ("GNUNET_PREFIX");
258 if (p != NULL)
259 return GNUNET_strdup (p);
260 return NULL;
261}
262
263/*
264 * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path
265 * @author Milan
266 *
267 * @return a pointer to the executable path, or NULL on error
268 */
269static char *
270os_get_gnunet_path ()
271{
272 char *ret;
273
274 ret = get_path_from_GNUNET_PREFIX ();
275 if (ret != NULL)
276 return ret;
277#if LINUX
278 ret = get_path_from_proc_maps ();
279 if (ret != NULL)
280 return ret;
281 ret = get_path_from_proc_exe ();
282 if (ret != NULL)
283 return ret;
284#endif
285#if WINDOWS
286 ret = get_path_from_module_filename ();
287 if (ret != NULL)
288 return ret;
289#endif
290#if OSX
291 ret = get_path_from_dyld_image ();
292 if (ret != NULL)
293 return ret;
294 ret = get_path_from_NSGetExecutablePath ();
295 if (ret != NULL)
296 return ret;
297#endif
298 ret = get_path_from_PATH ();
299 if (ret != NULL)
300 return ret;
301 /* other attempts here */
302 return NULL;
303}
304
305/*
306 * @brief get the path to current app's bin/
307 * @author Milan
308 *
309 * @return a pointer to the executable path, or NULL on error
310 */
311static char *
312os_get_exec_path ()
313{
314 char *ret;
315
316#if LINUX
317 ret = get_path_from_proc_exe ();
318 if (ret != NULL)
319 return ret;
320#endif
321#if WINDOWS
322 ret = get_path_from_module_filename ();
323 if (ret != NULL)
324 return ret;
325#endif
326#if OSX
327 ret = get_path_from_NSGetExecutablePath ();
328 if (ret != NULL)
329 return ret;
330#endif
331 /* other attempts here */
332 return NULL;
333}
334
335
336
337/**
338 * @brief get the path to a specific GNUnet installation directory or,
339 * with GNUNET_IPK_SELF_PREFIX, the current running apps installation directory
340 * @author Milan
341 * @return a pointer to the dir path (to be freed by the caller)
342 */
343char *
344GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
345{
346 size_t n;
347 const char *dirname;
348 char *execpath = NULL;
349 char *tmp;
350 int isbasedir;
351
352 /* if wanted, try to get the current app's bin/ */
353 if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
354 execpath = os_get_exec_path ();
355
356 /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some
357 * guess for the current app */
358 if (execpath == NULL)
359 execpath = os_get_gnunet_path ();
360
361 if (execpath == NULL)
362 return NULL;
363
364 n = strlen (execpath);
365 if (n == 0)
366 {
367 /* should never happen, but better safe than sorry */
368 GNUNET_free (execpath);
369 return NULL;
370 }
371 /* remove filename itself */
372 while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
373 execpath[--n] = '\0';
374
375 isbasedir = 1;
376 if ((n > 5) &&
377 ((0 == strcasecmp (&execpath[n - 5], "lib32")) ||
378 (0 == strcasecmp (&execpath[n - 5], "lib64"))))
379 {
380 if (dirkind != GNUNET_OS_IPK_LIBDIR)
381 {
382 /* strip '/lib32' or '/lib64' */
383 execpath[n - 5] = '\0';
384 n -= 5;
385 }
386 else
387 isbasedir = 0;
388 }
389 else if ((n > 3) &&
390 ((0 == strcasecmp (&execpath[n - 3], "bin")) ||
391 (0 == strcasecmp (&execpath[n - 3], "lib"))))
392 {
393 /* strip '/bin' or '/lib' */
394 execpath[n - 3] = '\0';
395 n -= 3;
396 }
397 /* in case this was a directory named foo-bin, remove "foo-" */
398 while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
399 execpath[--n] = '\0';
400 switch (dirkind)
401 {
402 case GNUNET_OS_IPK_PREFIX:
403 case GNUNET_OS_IPK_SELF_PREFIX:
404 dirname = DIR_SEPARATOR_STR;
405 break;
406 case GNUNET_OS_IPK_BINDIR:
407 dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR;
408 break;
409 case GNUNET_OS_IPK_LIBDIR:
410 if (isbasedir)
411 dirname =
412 DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet"
413 DIR_SEPARATOR_STR;
414 else
415 dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
416 break;
417 case GNUNET_OS_IPK_DATADIR:
418 dirname =
419 DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "gnunet"
420 DIR_SEPARATOR_STR;
421 break;
422 case GNUNET_OS_IPK_LOCALEDIR:
423 dirname =
424 DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale"
425 DIR_SEPARATOR_STR;
426 break;
427 default:
428 GNUNET_free (execpath);
429 return NULL;
430 }
431 tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
432 sprintf (tmp, "%s%s", execpath, dirname);
433 GNUNET_free (execpath);
434 return tmp;
435}
436
437#if 0 /* keep Emacsens' auto-indent happy */
438{
439#endif
440#ifdef __cplusplus
441}
442#endif
443/* end of installpath.c */
diff --git a/src/util/os_load.c b/src/util/os_load.c
new file mode 100644
index 000000000..d1115e33f
--- /dev/null
+++ b/src/util/os_load.c
@@ -0,0 +1,653 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/os_load_cpu.c
23 * @brief calls to determine current CPU load
24 * @author Tzvetan Horozov
25 * @author Christian Grothoff
26 * @author Igor Wronsky
27 * @author Alex Harper (OS X portion)
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_os_lib.h"
33#include "gnunet_strings_lib.h"
34
35#if SOLARIS
36#if HAVE_KSTAT_H
37#include <kstat.h>
38#endif
39#if HAVE_SYS_SYSINFO_H
40#include <sys/sysinfo.h>
41#endif
42#if HAVE_KVM_H
43#include <kvm.h>
44#endif
45#endif
46#if SOMEBSD
47#if HAVE_KVM_H
48#include <kvm.h>
49#endif
50#endif
51
52#ifdef OSX
53#include <mach/mach.h>
54
55static processor_cpu_load_info_t prev_cpu_load;
56#endif
57
58#define DEBUG_STATUSCALLS GNUNET_NO
59
60#ifdef LINUX
61static FILE *proc_stat;
62#endif
63
64/**
65 * Current CPU load, as percentage of CPU cycles not idle or
66 * blocked on IO.
67 */
68static int currentCPULoad;
69
70static double agedCPULoad = -1;
71
72/**
73 * Current IO load, as percentage of CPU cycles blocked on IO.
74 */
75static int currentIOLoad;
76
77static double agedIOLoad = -1;
78
79#ifdef OSX
80static int
81initMachCpuStats ()
82{
83 unsigned int cpu_count;
84 processor_cpu_load_info_t cpu_load;
85 mach_msg_type_number_t cpu_msg_count;
86 kern_return_t kret;
87 int i, j;
88
89 kret = host_processor_info (mach_host_self (),
90 PROCESSOR_CPU_LOAD_INFO,
91 &cpu_count,
92 (processor_info_array_t *) & cpu_load,
93 &cpu_msg_count);
94 if (kret != KERN_SUCCESS)
95 {
96 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
97 "host_processor_info failed.");
98 return GNUNET_SYSERR;
99 }
100 prev_cpu_load = GNUNET_malloc (cpu_count * sizeof (*prev_cpu_load));
101 for (i = 0; i < cpu_count; i++)
102 {
103 for (j = 0; j < CPU_STATE_MAX; j++)
104 {
105 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
106 }
107 }
108 vm_deallocate (mach_task_self (),
109 (vm_address_t) cpu_load,
110 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
111 return GNUNET_OK;
112}
113#endif
114
115/**
116 * Update the currentCPU and currentIO load values.
117 *
118 * Before its first invocation the method initStatusCalls() must be called.
119 * If there is an error the method returns -1.
120 */
121static int
122updateUsage ()
123{
124 currentIOLoad = -1;
125 currentCPULoad = -1;
126#ifdef LINUX
127 /* under linux, first try %idle/usage using /proc/stat;
128 if that does not work, disable /proc/stat for the future
129 by closing the file and use the next-best method. */
130 if (proc_stat != NULL)
131 {
132 static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 };
133 static int have_last_cpu = GNUNET_NO;
134 int ret;
135 char line[256];
136 unsigned long long user_read, system_read, nice_read, idle_read,
137 iowait_read;
138 unsigned long long user, system, nice, idle, iowait;
139 unsigned long long usage_time = 0, total_time = 1;
140
141 /* Get the first line with the data */
142 rewind (proc_stat);
143 fflush (proc_stat);
144 if (NULL == fgets (line, 256, proc_stat))
145 {
146 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
147 GNUNET_ERROR_TYPE_BULK,
148 "fgets", "/proc/stat");
149 fclose (proc_stat);
150 proc_stat = NULL; /* don't try again */
151 }
152 else
153 {
154 iowait_read = 0;
155 ret = sscanf (line, "%*s %llu %llu %llu %llu %llu",
156 &user_read,
157 &system_read, &nice_read, &idle_read, &iowait_read);
158 if (ret < 4)
159 {
160 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
161 GNUNET_ERROR_TYPE_BULK,
162 "fgets-sscanf", "/proc/stat");
163 fclose (proc_stat);
164 proc_stat = NULL; /* don't try again */
165 have_last_cpu = GNUNET_NO;
166 }
167 else
168 {
169 /* Store the current usage */
170 user = user_read - last_cpu_results[0];
171 system = system_read - last_cpu_results[1];
172 nice = nice_read - last_cpu_results[2];
173 idle = idle_read - last_cpu_results[3];
174 iowait = iowait_read - last_cpu_results[4];
175 /* Calculate the % usage */
176 usage_time = user + system + nice;
177 total_time = usage_time + idle + iowait;
178 if ((total_time > 0) && (have_last_cpu == GNUNET_YES))
179 {
180 currentCPULoad = (int) (100L * usage_time / total_time);
181 if (ret > 4)
182 currentIOLoad = (int) (100L * iowait / total_time);
183 else
184 currentIOLoad = -1; /* 2.4 kernel */
185 }
186 /* Store the values for the next calculation */
187 last_cpu_results[0] = user_read;
188 last_cpu_results[1] = system_read;
189 last_cpu_results[2] = nice_read;
190 last_cpu_results[3] = idle_read;
191 last_cpu_results[4] = iowait_read;
192 have_last_cpu = GNUNET_YES;
193 return GNUNET_OK;
194 }
195 }
196 }
197#endif
198
199#ifdef OSX
200 {
201 unsigned int cpu_count;
202 processor_cpu_load_info_t cpu_load;
203 mach_msg_type_number_t cpu_msg_count;
204 unsigned long long t_sys, t_user, t_nice, t_idle, t_total;
205 unsigned long long t_idle_all, t_total_all;
206 kern_return_t kret;
207 int i, j;
208
209 t_idle_all = t_total_all = 0;
210 kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO,
211 &cpu_count,
212 (processor_info_array_t *) & cpu_load,
213 &cpu_msg_count);
214 if (kret == KERN_SUCCESS)
215 {
216 for (i = 0; i < cpu_count; i++)
217 {
218 if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >=
219 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM])
220 {
221 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] -
222 prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM];
223 }
224 else
225 {
226 t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
227 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] +
228 1);
229 }
230
231 if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >=
232 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER])
233 {
234 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] -
235 prev_cpu_load[i].cpu_ticks[CPU_STATE_USER];
236 }
237 else
238 {
239 t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] +
240 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] +
241 1);
242 }
243
244 if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >=
245 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE])
246 {
247 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] -
248 prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE];
249 }
250 else
251 {
252 t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
253 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] +
254 1);
255 }
256
257 if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >=
258 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE])
259 {
260 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] -
261 prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE];
262 }
263 else
264 {
265 t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
266 (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] +
267 1);
268 }
269 t_total = t_sys + t_user + t_nice + t_idle;
270 t_idle_all += t_idle;
271 t_total_all += t_total;
272 }
273 for (i = 0; i < cpu_count; i++)
274 {
275 for (j = 0; j < CPU_STATE_MAX; j++)
276 {
277 prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j];
278 }
279 }
280 if (t_total_all > 0)
281 currentCPULoad = 100 - (100 * t_idle_all) / t_total_all;
282 else
283 currentCPULoad = -1;
284 vm_deallocate (mach_task_self (),
285 (vm_address_t) cpu_load,
286 (vm_size_t) (cpu_msg_count * sizeof (*cpu_load)));
287 currentIOLoad = -1; /* FIXME-OSX! */
288 return GNUNET_OK;
289 }
290 else
291 {
292 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
293 "host_processor_info failed.");
294 return GNUNET_SYSERR;
295 }
296 }
297#endif
298 /* try kstat (Solaris only) */
299#if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H
300 {
301 static long long last_idlecount;
302 static long long last_totalcount;
303 static int kstat_once; /* if open fails, don't keep
304 trying */
305 kstat_ctl_t *kc;
306 kstat_t *khelper;
307 long long idlecount;
308 long long totalcount;
309 long long deltaidle;
310 long long deltatotal;
311
312 if (kstat_once == 1)
313 goto ABORT_KSTAT;
314 kc = kstat_open ();
315 if (kc == NULL)
316 {
317 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
318 "kstat_open");
319 goto ABORT_KSTAT;
320 }
321
322 idlecount = 0;
323 totalcount = 0;
324 for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next)
325 {
326 cpu_stat_t stats;
327
328 if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat")))
329 continue;
330 if (khelper->ks_data_size > sizeof (cpu_stat_t))
331 continue; /* better save then sorry! */
332 if (-1 != kstat_read (kc, khelper, &stats))
333 {
334 idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE];
335 totalcount
336 += stats.cpu_sysinfo.cpu[CPU_IDLE] +
337 stats.cpu_sysinfo.cpu[CPU_USER] +
338 stats.cpu_sysinfo.cpu[CPU_KERNEL] +
339 stats.cpu_sysinfo.cpu[CPU_WAIT];
340 }
341 }
342 if (0 != kstat_close (kc))
343 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
344 "kstat_close");
345 if ((idlecount == 0) && (totalcount == 0))
346 goto ABORT_KSTAT; /* no stats found => abort */
347 deltaidle = idlecount - last_idlecount;
348 deltatotal = totalcount - last_totalcount;
349 if ((deltatotal > 0) && (last_totalcount > 0))
350 {
351 currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal);
352 if (currentCPULoad > 100)
353 currentCPULoad = 100; /* odd */
354 if (currentCPULoad < 0)
355 currentCPULoad = 0; /* odd */
356 currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */
357 }
358 else
359 currentCPULoad = -1;
360 currentIOLoad = -1; /* FIXME-SOLARIS! */
361 last_idlecount = idlecount;
362 last_totalcount = totalcount;
363 return GNUNET_OK;
364 ABORT_KSTAT:
365 kstat_once = 1; /* failed, don't try again */
366 return GNUNET_SYSERR;
367 }
368#endif
369
370 /* insert methods better than getloadavg for
371 other platforms HERE! */
372
373 /* ok, maybe we have getloadavg on this platform */
374#if HAVE_GETLOADAVG
375 {
376 static int warnOnce = 0;
377 double loadavg;
378 if (1 != getloadavg (&loadavg, 1))
379 {
380 /* only warn once, if there is a problem with
381 getloadavg, we're going to hit it frequently... */
382 if (warnOnce == 0)
383 {
384 warnOnce = 1;
385 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "getloadavg");
386 }
387 return GNUNET_SYSERR;
388 }
389 else
390 {
391 /* success with getloadavg */
392 currentCPULoad = (int) (100 * loadavg);
393 currentIOLoad = -1; /* FIXME */
394 return GNUNET_OK;
395 }
396 }
397#endif
398
399#if MINGW
400 /* Win NT? */
401 if (GNNtQuerySystemInformation)
402 {
403 static double dLastKernel;
404 static double dLastIdle;
405 static double dLastUser;
406 double dKernel;
407 double dIdle;
408 double dUser;
409 double dDiffKernel;
410 double dDiffIdle;
411 double dDiffUser;
412 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo;
413
414 if (GNNtQuerySystemInformation (SystemProcessorPerformanceInformation,
415 &theInfo,
416 sizeof (theInfo), NULL) == NO_ERROR)
417 {
418 /* PORT-ME MINGW: Multi-processor? */
419 dKernel = Li2Double (theInfo.KernelTime);
420 dIdle = Li2Double (theInfo.IdleTime);
421 dUser = Li2Double (theInfo.UserTime);
422 dDiffKernel = dKernel - dLastKernel;
423 dDiffIdle = dIdle - dLastIdle;
424 dDiffUser = dUser - dLastUser;
425
426 if (((dDiffKernel + dDiffUser) > 0) &&
427 (dLastIdle + dLastKernel + dLastUser > 0))
428 currentCPULoad =
429 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0;
430 else
431 currentCPULoad = -1; /* don't know (yet) */
432
433 dLastKernel = dKernel;
434 dLastIdle = dIdle;
435 dLastUser = dUser;
436
437 currentIOLoad = -1; /* FIXME-MINGW */
438 return GNUNET_OK;
439 }
440 else
441 {
442 /* only warn once, if there is a problem with
443 NtQuery..., we're going to hit it frequently... */
444 static int once;
445 if (once == 0)
446 {
447 once = 1;
448 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
449 _("Cannot query the CPU usage (Windows NT).\n"));
450 }
451 return GNUNET_SYSERR;
452 }
453 }
454 else
455 { /* Win 9x */
456 HKEY hKey;
457 DWORD dwDataSize, dwType, dwDummy;
458
459 /* Start query */
460 if (RegOpenKeyEx (HKEY_DYN_DATA,
461 "PerfStats\\StartSrv",
462 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
463 {
464 /* only warn once */
465 static int once = 0;
466 if (once == 0)
467 {
468 once = 1;
469 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
470 _("Cannot query the CPU usage (Win 9x)\n"));
471 }
472 }
473
474 RegOpenKeyEx (HKEY_DYN_DATA,
475 "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey);
476 dwDataSize = sizeof (dwDummy);
477 RegQueryValueEx (hKey,
478 "KERNEL\\CPUUsage",
479 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
480 RegCloseKey (hKey);
481
482 /* Get CPU usage */
483 RegOpenKeyEx (HKEY_DYN_DATA,
484 "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey);
485 dwDataSize = sizeof (currentCPULoad);
486 RegQueryValueEx (hKey,
487 "KERNEL\\CPUUsage",
488 NULL, &dwType, (LPBYTE) & currentCPULoad, &dwDataSize);
489 RegCloseKey (hKey);
490 currentIOLoad = -1; /* FIXME-MINGW! */
491
492 /* Stop query */
493 RegOpenKeyEx (HKEY_DYN_DATA,
494 "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey);
495 RegOpenKeyEx (HKEY_DYN_DATA,
496 "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey);
497 dwDataSize = sizeof (dwDummy);
498 RegQueryValueEx (hKey,
499 "KERNEL\\CPUUsage",
500 NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize);
501 RegCloseKey (hKey);
502
503 return GNUNET_OK;
504 }
505#endif
506
507 /* loadaverage not defined and no platform
508 specific alternative defined
509 => default: error
510 */
511 return GNUNET_SYSERR;
512}
513
514/**
515 * Update load values (if enough time has expired),
516 * including computation of averages. Code assumes
517 * that lock has already been obtained.
518 */
519static void
520updateAgedLoad (struct GNUNET_CONFIGURATION_Handle *cfg)
521{
522 static struct GNUNET_TIME_Absolute lastCall;
523
524 if ((agedCPULoad == -1)
525 || (GNUNET_TIME_absolute_get_duration (lastCall).value > 500))
526 {
527 /* use smoothing, but do NOT update lastRet at frequencies higher
528 than 500ms; this makes the smoothing (mostly) independent from
529 the frequency at which getCPULoad is called (and we don't spend
530 more time measuring CPU than actually computing something). */
531 lastCall = GNUNET_TIME_absolute_get ();
532 updateUsage ();
533 if (currentCPULoad == -1)
534 {
535 agedCPULoad = -1;
536 }
537 else
538 {
539 if (agedCPULoad == -1)
540 {
541 agedCPULoad = currentCPULoad;
542 }
543 else
544 {
545 /* for CPU, we don't do the 'fast increase' since CPU is much
546 more jitterish to begin with */
547 agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32;
548 }
549 }
550 if (currentIOLoad == -1)
551 {
552 agedIOLoad = -1;
553 }
554 else
555 {
556 if (agedIOLoad == -1)
557 {
558 agedIOLoad = currentIOLoad;
559 }
560 else
561 {
562 /* for IO, we don't do the 'fast increase' since IO is much
563 more jitterish to begin with */
564 agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32;
565 }
566 }
567 }
568}
569
570/**
571 * Get the load of the CPU relative to what is allowed.
572 * @return the CPU load as a percentage of allowed
573 * (100 is equivalent to full load)
574 */
575int
576GNUNET_OS_load_cpu_get (struct GNUNET_CONFIGURATION_Handle *cfg)
577{
578 unsigned long long maxCPULoad;
579 int ret;
580
581 updateAgedLoad (cfg);
582 ret = agedCPULoad;
583 if (ret == -1)
584 return -1;
585 if (GNUNET_OK !=
586 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXCPULOAD",
587 &maxCPULoad))
588 return GNUNET_SYSERR;
589 return (100 * ret) / maxCPULoad;
590}
591
592
593/**
594 * Get the load of the CPU relative to what is allowed.
595 * @return the CPU load as a percentage of allowed
596 * (100 is equivalent to full load)
597 */
598int
599GNUNET_OS_load_disk_get (struct GNUNET_CONFIGURATION_Handle *cfg)
600{
601 unsigned long long maxIOLoad;
602 int ret;
603
604 updateAgedLoad (cfg);
605 ret = agedIOLoad;
606 if (ret == -1)
607 return -1;
608 if (-1 ==
609 GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXIOLOAD",
610 &maxIOLoad))
611 return GNUNET_SYSERR;
612 return (100 * ret) / maxIOLoad;
613}
614
615/**
616 * The following method is called in order to initialize the status calls
617 * routines. After that it is safe to call each of the status calls separately
618 * @return GNUNET_OK on success and GNUNET_SYSERR on error (or calls errexit).
619 */
620void __attribute__ ((constructor)) GNUNET_cpustats_ltdl_init ()
621{
622#ifdef LINUX
623 proc_stat = fopen ("/proc/stat", "r");
624 if (NULL == proc_stat)
625 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", "/proc/stat");
626#elif OSX
627 initMachCpuStats ();
628#elif MINGW
629 InitWinEnv (NULL);
630#endif
631 updateUsage (); /* initialize */
632}
633
634/**
635 * Shutdown the status calls module.
636 */
637void __attribute__ ((destructor)) GNUNET_cpustats_ltdl_fini ()
638{
639#ifdef LINUX
640 if (proc_stat != NULL)
641 {
642 fclose (proc_stat);
643 proc_stat = NULL;
644 }
645#elif OSX
646 GNUNET_free_non_null (prev_cpu_load);
647#elif MINGW
648 ShutdownWinEnv ();
649#endif
650}
651
652
653/* end of os_load_cpu.c */
diff --git a/src/util/os_network.c b/src/util/os_network.c
new file mode 100644
index 000000000..cb5ccb12a
--- /dev/null
+++ b/src/util/os_network.c
@@ -0,0 +1,286 @@
1/*
2 This file is part of GNUnet.
3 (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @file util/os_network.c
24 * @brief function to determine available network interfaces
25 * @author Nils Durner
26 * @author Heikki Lindholm
27 * @author Jake Dust
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_os_lib.h"
33
34/**
35 * @brief Enumerate all network interfaces
36 * @param callback the callback function
37 */
38void
39GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor proc,
40 void *cls)
41{
42#ifdef MINGW
43 PMIB_IFTABLE pTable;
44 PMIB_IPADDRTABLE pAddrTable;
45 DWORD dwIfIdx, dwExternalNIC;
46 IPAddr theIP;
47
48 /* Determine our external NIC */
49 theIP = inet_addr ("192.0.34.166"); /* www.example.com */
50 if ((!GNGetBestInterface) ||
51 (GNGetBestInterface (theIP, &dwExternalNIC) != NO_ERROR))
52 {
53 dwExternalNIC = 0;
54 }
55
56 /* Enumerate NICs */
57 EnumNICs (&pTable, &pAddrTable);
58
59 if (pTable)
60 {
61 for (dwIfIdx = 0; dwIfIdx <= pTable->dwNumEntries; dwIfIdx++)
62 {
63 char szEntry[1001];
64 DWORD dwIP = 0;
65 int iItm;
66 PIP_ADAPTER_INFO pAdapterInfo;
67 PIP_ADAPTER_INFO pAdapter = NULL;
68 DWORD dwRetVal = 0;
69
70 /* Get IP-Address */
71 int i;
72 for (i = 0; i < pAddrTable->dwNumEntries; i++)
73 {
74 if (pAddrTable->table[i].dwIndex ==
75 pTable->table[dwIfIdx].dwIndex)
76 {
77 dwIP = pAddrTable->table[i].dwAddr;
78 break;
79 }
80 }
81
82 if (dwIP)
83 {
84 BYTE bPhysAddr[MAXLEN_PHYSADDR];
85 char *pszIfName = NULL;
86 char dst[INET_ADDRSTRLEN];
87
88 /* Get friendly interface name */
89 pAdapterInfo =
90 (IP_ADAPTER_INFO *) malloc (sizeof (IP_ADAPTER_INFO));
91 ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
92
93 /* Make an initial call to GetAdaptersInfo to get
94 the necessary size into the ulOutBufLen variable */
95 if (GGetAdaptersInfo (pAdapterInfo, &ulOutBufLen) ==
96 ERROR_BUFFER_OVERFLOW)
97 {
98 free (pAdapterInfo);
99 pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
100 }
101
102 if ((dwRetVal =
103 GGetAdaptersInfo (pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
104 {
105 pAdapter = pAdapterInfo;
106 while (pAdapter)
107 {
108 if (pTable->table[dwIfIdx].dwIndex == pAdapter->Index)
109 {
110 char szKey[251];
111 long lLen = 250;
112
113 sprintf (szKey,
114 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
115 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s\\Connection",
116 pAdapter->AdapterName);
117 pszIfName = (char *) malloc (251);
118 if (QueryRegistry
119 (HKEY_LOCAL_MACHINE, szKey, "Name", pszIfName,
120 &lLen) != ERROR_SUCCESS)
121 {
122 free (pszIfName);
123 pszIfName = NULL;
124 }
125 }
126 pAdapter = pAdapter->Next;
127 }
128 }
129 free (pAdapterInfo);
130
131 /* Set entry */
132 memset (bPhysAddr, 0, MAXLEN_PHYSADDR);
133 memcpy (bPhysAddr,
134 pTable->table[dwIfIdx].bPhysAddr,
135 pTable->table[dwIfIdx].dwPhysAddrLen);
136
137 snprintf (szEntry, 1000, "%s (%s - %I64u)",
138 pszIfName ? pszIfName : (char *) pTable->
139 table[dwIfIdx].bDescr, inet_ntop (AF_INET, &dwIP, dst,
140 INET_ADDRSTRLEN),
141 *((unsigned long long *) bPhysAddr));
142 szEntry[1000] = 0;
143
144 if (pszIfName)
145 free (pszIfName);
146
147 if (GNUNET_OK !=
148 proc (cls,
149 szEntry,
150 pAddrTable->table[dwIfIdx].dwIndex == dwExternalNIC,
151 NULL /* FIXME: pass actual IP address! */ ,
152 0))
153 break;
154 }
155 }
156 GlobalFree (pAddrTable);
157 GlobalFree (pTable);
158 }
159
160 return GNUNET_YES;
161
162#elif HAVE_GETIFADDRS && HAVE_FREEIFADDRS
163
164 struct ifaddrs *ifa_first;
165 socklen_t alen;
166
167 if (getifaddrs (&ifa_first) == 0)
168 {
169 struct ifaddrs *ifa_ptr;
170
171 ifa_ptr = ifa_first;
172 for (ifa_ptr = ifa_first; ifa_ptr != NULL; ifa_ptr = ifa_ptr->ifa_next)
173 {
174 if (ifa_ptr->ifa_name != NULL &&
175 ifa_ptr->ifa_addr != NULL && (ifa_ptr->ifa_flags & IFF_UP) != 0)
176 {
177 if ((ifa_ptr->ifa_addr->sa_family != AF_INET) &&
178 (ifa_ptr->ifa_addr->sa_family != AF_INET6))
179 continue;
180 if (ifa_ptr->ifa_addr->sa_family == AF_INET)
181 alen = sizeof (struct sockaddr_in);
182 else
183 alen = sizeof (struct sockaddr_in6);
184 if (GNUNET_OK != proc (cls,
185 ifa_ptr->ifa_name,
186 0 == strcmp (ifa_ptr->ifa_name,
187 GNUNET_DEFAULT_INTERFACE),
188 ifa_ptr->ifa_addr, alen))
189 break;
190 }
191 }
192 freeifaddrs (ifa_first);
193 }
194#else
195 char line[1024];
196 const char *start;
197 char ifc[12];
198 char addrstr[128];
199 FILE *f;
200 int have_ifc;
201 struct sockaddr_in a4;
202 struct sockaddr_in6 a6;
203 struct in_addr v4;
204 struct in6_addr v6;
205
206 if (system ("ifconfig -a > /dev/null 2> /dev/null"))
207 if (system ("/sbin/ifconfig -a > /dev/null 2> /dev/null") == 0)
208 f = popen ("/sbin/ifconfig -a 2> /dev/null", "r");
209 else
210 f = NULL;
211 else
212 f = popen ("ifconfig -a 2> /dev/null", "r");
213 if (!f)
214 {
215 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
216 GNUNET_ERROR_TYPE_BULK, "popen", "ifconfig");
217 return;
218 }
219
220 have_ifc = GNUNET_NO;
221 ifc[11] = '\0';
222 while (NULL != fgets (line, sizeof (line), f))
223 {
224 if (strlen (line) == 0)
225 {
226 have_ifc = GNUNET_NO;
227 continue;
228 }
229 if (!isspace (line[0]))
230 {
231 have_ifc =
232 (1 == SSCANF (line, "%11s", ifc)) ? GNUNET_YES : GNUNET_NO;
233 /* would end with ':' on OSX, fix it! */
234 if (ifc[strlen (ifc) - 1] == ':')
235 ifc[strlen (ifc) - 1] = '\0';
236 continue;
237 }
238 if (!have_ifc)
239 continue; /* strange input, hope for the best */
240 start = line;
241 while (('\0' != *start) && (isspace (*start)))
242 start++;
243 if ( /* Linux */
244 (1 == SSCANF (start, "inet addr:%127s", addrstr)) ||
245 (1 == SSCANF (start, "inet6 addr:%127s", addrstr)) ||
246 /* Solaris, OS X */
247 (1 == SSCANF (start, "inet %127s", addrstr)) ||
248 (1 == SSCANF (start, "inet6 %127s", addrstr)))
249 {
250 /* IPv4 */
251 if (1 == inet_pton (AF_INET, addrstr, &v4))
252 {
253 memset (&a4, 0, sizeof (a4));
254 a4.sin_family = AF_INET;
255 a4.sin_addr = v4;
256 if (GNUNET_OK !=
257 proc (cls,
258 ifc,
259 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE),
260 (const struct sockaddr *) &a4, sizeof (a4)))
261 break;
262 continue;
263 }
264 /* IPv6 */
265 if (1 == inet_pton (AF_INET6, addrstr, &v6))
266 {
267 memset (&a6, 0, sizeof (a6));
268 a6.sin6_family = AF_INET6;
269 a6.sin6_addr = v6;
270 fprintf (stderr, "procing %s\n", addrstr);
271 if (GNUNET_OK !=
272 proc (cls,
273 ifc,
274 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE),
275 (const struct sockaddr *) &a6, sizeof (a6)))
276 break;
277 continue;
278 }
279 }
280 }
281 pclose (f);
282#endif
283}
284
285
286/* end of os_network.c */
diff --git a/src/util/os_priority.c b/src/util/os_priority.c
new file mode 100644
index 000000000..7862d68e9
--- /dev/null
+++ b/src/util/os_priority.c
@@ -0,0 +1,186 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/os/priority.c
23 * @brief Methods to set process priority
24 * @author Nils Durner
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_os_lib.h"
30
31/**
32 * Set our process priority
33 */
34int
35GNUNET_OS_set_process_priority (pid_t proc,
36 enum GNUNET_SCHEDULER_Priority eprio)
37{
38 int prio = 0;
39
40 GNUNET_assert (eprio < GNUNET_SCHEDULER_PRIORITY_COUNT);
41 if (eprio == GNUNET_SCHEDULER_PRIORITY_KEEP)
42 return GNUNET_OK;
43 /* convert to MINGW/Unix values */
44 switch (eprio)
45 {
46 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
47#ifdef MINGW
48 prio = NORMAL_PRIORITY_CLASS;
49#else
50 prio = 0;
51#endif
52 break;
53 case GNUNET_SCHEDULER_PRIORITY_HIGH:
54#ifdef MINGW
55 prio = ABOVE_NORMAL_PRIORITY_CLASS;
56#else
57 prio = -5;
58#endif
59 break;
60 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
61#ifdef MINGW
62 prio = BELOW_NORMAL_PRIORITY_CLASS;
63#else
64 prio = 10;
65#endif
66 break;
67 case GNUNET_SCHEDULER_PRIORITY_UI:
68 case GNUNET_SCHEDULER_PRIORITY_URGENT:
69#ifdef MINGW
70 prio = HIGH_PRIORITY_CLASS;
71#else
72 prio = -10;
73#endif
74 break;
75 case GNUNET_SCHEDULER_PRIORITY_IDLE:
76#ifdef MINGW
77 prio = IDLE_PRIORITY_CLASS;
78#else
79 prio = 19;
80#endif
81 break;
82 default:
83 GNUNET_assert (0);
84 return GNUNET_SYSERR;
85 }
86 /* Set process priority */
87#ifdef MINGW
88 SetPriorityClass (GetCurrentProcess (), prio);
89#else
90 if (proc == getpid ())
91 {
92 errno = 0;
93 if ((-1 == nice (prio)) && (errno != 0))
94 {
95 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
96 GNUNET_ERROR_TYPE_BULK, "nice");
97 return GNUNET_SYSERR;
98 }
99 }
100 else
101 {
102 if (0 != setpriority (PRIO_PROCESS, proc, prio))
103
104 {
105 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
106 GNUNET_ERROR_TYPE_BULK, "setpriority");
107 return GNUNET_SYSERR;
108 }
109 }
110#endif
111 return GNUNET_OK;
112}
113
114
115
116/**
117 * Start a process.
118 *
119 * @param filename name of the binary
120 * @param ... NULL-terminated list of arguments to the process
121 * @return process ID of the new process, -1 on error
122 */
123pid_t
124GNUNET_OS_start_process (const char *filename, ...)
125{
126 pid_t ret;
127 char **argv;
128 va_list ap;
129 int argc;
130
131 ret = fork ();
132 if (ret != 0)
133 {
134 if (ret == -1)
135 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
136 return ret;
137 }
138 argc = 0;
139 va_start (ap, filename);
140 while (NULL != va_arg (ap, char *))
141 argc++;
142 va_end (ap);
143 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
144 argc = 0;
145 va_start (ap, filename);
146 while (NULL != (argv[argc] = va_arg (ap, char *)))
147 argc++;
148 va_end (ap);
149 execvp (filename, argv);
150 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
151 exit (1);
152}
153
154
155
156
157/**
158 * Start a process.
159 *
160 * @param filename name of the binary
161 * @param argv NULL-terminated list of arguments to the process
162 * @return process ID of the new process, -1 on error
163 */
164pid_t
165GNUNET_OS_start_process_v (const char *filename, char *const argv[])
166{
167 pid_t ret;
168
169 ret = fork ();
170 if (ret != 0)
171 {
172 if (ret == -1)
173 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
174 return ret;
175 }
176 execvp (filename, argv);
177 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
178 exit (1);
179}
180
181
182
183
184
185
186/* end of os_priority.c */
diff --git a/src/util/perf_crypto_hash.c b/src/util/perf_crypto_hash.c
new file mode 100644
index 000000000..a6f6ba340
--- /dev/null
+++ b/src/util/perf_crypto_hash.c
@@ -0,0 +1,64 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @author Christian Grothoff
23 * @file util/perf_crypto_hash.c
24 * @brief measure performance of hash function
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_time_lib.h"
30
31static void
32perfHash ()
33{
34 GNUNET_HashCode hc1;
35 GNUNET_HashCode hc2;
36 GNUNET_HashCode hc3;
37 int i;
38 char *buf;
39
40 buf = GNUNET_malloc (1024 * 64);
41 memset (buf, 1, 1024 * 64);
42 GNUNET_CRYPTO_hash ("foo", 3, &hc1);
43 for (i = 0; i < 1024; i++)
44 {
45 GNUNET_CRYPTO_hash (&hc1, sizeof (GNUNET_HashCode), &hc2);
46 GNUNET_CRYPTO_hash (&hc2, sizeof (GNUNET_HashCode), &hc1);
47 GNUNET_CRYPTO_hash (buf, 1024 * 64, &hc3);
48 }
49 GNUNET_free (buf);
50}
51
52int
53main (int argc, char *argv[])
54{
55 struct GNUNET_TIME_Absolute start;
56
57 start = GNUNET_TIME_absolute_get ();
58 perfHash ();
59 printf ("Hash perf took %llu ms\n",
60 GNUNET_TIME_absolute_get_duration (start).value);
61 return 0;
62}
63
64/* end of hashperf.c */
diff --git a/src/util/plugin.c b/src/util/plugin.c
new file mode 100644
index 000000000..d26a343c7
--- /dev/null
+++ b/src/util/plugin.c
@@ -0,0 +1,243 @@
1/*
2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/plugin.c
23 * @brief Methods to access plugins
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include <libltdl/ltdl.h>
29#include "gnunet_common.h"
30#include "gnunet_os_lib.h"
31#include "gnunet_plugin_lib.h"
32
33/**
34 * Linked list of active plugins.
35 */
36struct PluginList
37{
38 /**
39 * This is a linked list.
40 */
41 struct PluginList *next;
42
43 /**
44 * Name of the library.
45 */
46 char *name;
47
48 /**
49 * System handle.
50 */
51 void *handle;
52};
53
54
55/**
56 * Libtool search path before we started.
57 */
58static char *old_dlsearchpath;
59
60
61/**
62 * List of plugins we have loaded.
63 */
64static struct PluginList *plugins;
65
66
67/**
68 * Setup libtool paths.
69 */
70void __attribute__ ((constructor)) GNUNET_PLUGIN_init ()
71{
72 int err;
73 const char *opath;
74 char *path;
75 char *cpath;
76
77#ifdef MINGW
78 InitWinEnv (NULL);
79#endif
80
81 err = lt_dlinit ();
82 if (err > 0)
83 {
84 fprintf (stderr,
85 _("Initialization of plugin mechanism failed: %s!\n"),
86 lt_dlerror ());
87 return;
88 }
89 opath = lt_dlgetsearchpath ();
90 if (opath != NULL)
91 old_dlsearchpath = GNUNET_strdup (opath);
92 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
93 if (path != NULL)
94 {
95 if (opath != NULL)
96 {
97 cpath = GNUNET_malloc (strlen (path) + strlen (opath) + 4);
98 strcpy (cpath, opath);
99 strcat (cpath, ":");
100 strcat (cpath, path);
101 lt_dlsetsearchpath (cpath);
102 GNUNET_free (path);
103 GNUNET_free (cpath);
104 }
105 else
106 {
107 lt_dlsetsearchpath (path);
108 GNUNET_free (path);
109 }
110 }
111}
112
113
114/**
115 * Shutdown libtool.
116 */
117void __attribute__ ((destructor)) GNUNET_PLUGIN_fini ()
118{
119 lt_dlsetsearchpath (old_dlsearchpath);
120 if (old_dlsearchpath != NULL)
121 {
122 GNUNET_free (old_dlsearchpath);
123 old_dlsearchpath = NULL;
124 }
125
126#ifdef MINGW
127 ShutdownWinEnv ();
128#endif
129
130 lt_dlexit ();
131}
132
133
134/**
135 * Lookup a function in the plugin.
136 */
137static GNUNET_PLUGIN_Callback
138resolve_function (struct PluginList *plug, const char *name)
139{
140 char *initName;
141 void *mptr;
142
143 GNUNET_asprintf (&initName, "_%s_%s", plug->name, name);
144 mptr = lt_dlsym (plug->handle, &initName[1]);
145 if (mptr == NULL)
146 mptr = lt_dlsym (plug->handle, initName);
147 if (mptr == NULL)
148 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149 _("`%s' failed to resolve method '%s' with error: %s\n"),
150 "lt_dlsym", &initName[1], lt_dlerror ());
151 GNUNET_free (initName);
152 return mptr;
153}
154
155
156/**
157 * Setup plugin (runs the "init" callback and returns whatever "init"
158 * returned). If "init" returns NULL, the plugin is unloaded.
159 *
160 * Note that the library must export symbols called
161 * "library_name_init" and "library_name_done". These will be called
162 * when the library is loaded and unloaded respectively.
163 *
164 * @param library_name name of the plugin to load
165 * @param arg argument to the plugin initialization function
166 * @return whatever the initialization function returned
167 */
168void *
169GNUNET_PLUGIN_load (const char *library_name, void *arg)
170{
171 void *libhandle;
172 struct PluginList *plug;
173 GNUNET_PLUGIN_Callback init;
174 void *ret;
175
176 libhandle = lt_dlopenext (library_name);
177 if (libhandle == NULL)
178 {
179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180 _("`%s' failed for library `%s' with error: %s\n"),
181 "lt_dlopenext", library_name, lt_dlerror ());
182 return NULL;
183 }
184 plug = GNUNET_malloc (sizeof (struct PluginList));
185 plug->handle = libhandle;
186 plug->name = GNUNET_strdup (library_name);
187 plug->next = plugins;
188 plugins = plug;
189 init = resolve_function (plug, "init");
190 if ((init == NULL) || (NULL == (ret = init (arg))))
191 {
192 GNUNET_free (plug->name);
193 plugins = plug->next;
194 GNUNET_free (plug);
195 return NULL;
196 }
197 return ret;
198}
199
200
201/**
202 * Unload plugin (runs the "done" callback and returns whatever "done"
203 * returned). The plugin is then unloaded.
204 *
205 * @param library_name name of the plugin to unload
206 * @param arg argument to the plugin shutdown function
207 * @return whatever the shutdown function returned
208 */
209void *
210GNUNET_PLUGIN_unload (const char *library_name, void *arg)
211{
212 struct PluginList *pos;
213 struct PluginList *prev;
214 GNUNET_PLUGIN_Callback done;
215 void *ret;
216
217 prev = NULL;
218 pos = plugins;
219 while ((pos != NULL) && (0 != strcmp (pos->name, library_name)))
220 {
221 prev = pos;
222 pos = pos->next;
223 }
224 if (pos == NULL)
225 return NULL;
226
227 done = resolve_function (pos, "done");
228 ret = NULL;
229 if (done != NULL)
230 ret = done (arg);
231 if (prev == NULL)
232 plugins = pos->next;
233 else
234 prev->next = pos->next;
235 // lt_dlclose (pos->handle);
236 GNUNET_free (pos->name);
237 GNUNET_free (pos);
238 return ret;
239}
240
241
242
243/* end of plugin.c */
diff --git a/src/util/program.c b/src/util/program.c
new file mode 100644
index 000000000..c8ebfc4eb
--- /dev/null
+++ b/src/util/program.c
@@ -0,0 +1,202 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/program.c
23 * @brief standard code for GNUnet startup and shutdown
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_configuration_lib.h"
30#include "gnunet_crypto_lib.h"
31#include "gnunet_directories.h"
32#include "gnunet_getopt_lib.h"
33#include "gnunet_os_lib.h"
34#include "gnunet_program_lib.h"
35#include "gnunet_scheduler_lib.h"
36#include <gcrypt.h>
37
38/**
39 * Context for the command.
40 */
41struct CommandContext
42{
43 /**
44 * Argv argument.
45 */
46 char *const *args;
47
48 /**
49 * Name of the configuration file used, can be NULL!
50 */
51 char *cfgfile;
52
53 /**
54 * Main function to run.
55 */
56 GNUNET_PROGRAM_Main task;
57
58 /**
59 * Closure for task.
60 */
61 void *task_cls;
62
63 /**
64 * Configuration to use.
65 */
66 struct GNUNET_CONFIGURATION_Handle *cfg;
67
68};
69
70
71/**
72 * Initial task called by the scheduler for each
73 * program. Runs the program-specific main task.
74 */
75static void
76program_main (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
77{
78 struct CommandContext *cc = cls;
79
80 cc->task (cc->task_cls, tc->sched, cc->args, cc->cfgfile, cc->cfg);
81}
82
83
84/**
85 * Compare function for 'qsort' to sort command-line arguments by the
86 * short option.
87 */
88static int
89cmd_sorter (const void *a1, const void *a2)
90{
91 const struct GNUNET_GETOPT_CommandLineOption *c1 = a1;
92 const struct GNUNET_GETOPT_CommandLineOption *c2 = a2;
93 if (toupper (c1->shortName) > toupper (c2->shortName))
94 return 1;
95 if (toupper (c1->shortName) < toupper (c2->shortName))
96 return -1;
97 if (c1->shortName > c2->shortName)
98 return 1;
99 if (c1->shortName < c2->shortName)
100 return -1;
101 return 0;
102}
103
104
105/**
106 * Run a standard GNUnet command startup sequence (initialize loggers
107 * and configuration, parse options).
108 *
109 * @param argc number of command line arguments
110 * @param argv command line arguments
111 * @param binaryName our expected name
112 * @param options command line options
113 * @param task main function to run
114 * @param task_cls closure for task
115 * @return GNUNET_SYSERR on error, GNUNET_OK on success
116 */
117int
118GNUNET_PROGRAM_run (int argc,
119 char *const *argv,
120 const char *binaryName,
121 const char *binaryHelp,
122 const struct GNUNET_GETOPT_CommandLineOption *options,
123 GNUNET_PROGRAM_Main task, void *task_cls)
124{
125 struct CommandContext cc;
126 char *path;
127 char *loglev;
128 int ret;
129 unsigned int cnt;
130 struct GNUNET_GETOPT_CommandLineOption defoptions[] = {
131 GNUNET_GETOPT_OPTION_CFG_FILE (&cc.cfgfile),
132 GNUNET_GETOPT_OPTION_HELP (binaryHelp),
133 GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev),
134 GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION)
135 };
136 struct GNUNET_GETOPT_CommandLineOption *allopts;
137
138 memset (&cc, 0, sizeof (cc));
139 loglev = NULL;
140 cc.task = task;
141 cc.task_cls = task_cls;
142 cc.cfg = GNUNET_CONFIGURATION_create ();
143
144 /* prepare */
145#if ENABLE_NLS
146 setlocale (LC_ALL, "");
147 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
148 if (path != NULL)
149 {
150 BINDTEXTDOMAIN ("GNUnet", path);
151 GNUNET_free (path);
152 }
153 textdomain ("GNUnet");
154#endif
155 cnt = 0;
156 while (options[cnt].name != NULL)
157 cnt++;
158 allopts =
159 GNUNET_malloc ((cnt +
160 1) * sizeof (struct GNUNET_GETOPT_CommandLineOption) +
161 sizeof (defoptions));
162 memcpy (allopts, defoptions, sizeof (defoptions));
163 memcpy (&allopts
164 [sizeof (defoptions) /
165 sizeof (struct GNUNET_GETOPT_CommandLineOption)], options,
166 (cnt + 1) * sizeof (struct GNUNET_GETOPT_CommandLineOption));
167 cnt +=
168 sizeof (defoptions) / sizeof (struct GNUNET_GETOPT_CommandLineOption);
169 qsort (allopts, cnt, sizeof (struct GNUNET_GETOPT_CommandLineOption),
170 &cmd_sorter);
171 loglev = GNUNET_strdup ("WARNING");
172 if ((-1 == (ret = GNUNET_GETOPT_run (binaryName,
173 cc.cfg,
174 allopts,
175 (unsigned int) argc, argv))) ||
176 ((GNUNET_OK !=
177 GNUNET_log_setup (binaryName,
178 loglev,
179 NULL)) ||
180 (GNUNET_OK != GNUNET_CONFIGURATION_load (cc.cfg, cc.cfgfile))))
181
182 {
183 GNUNET_free_non_null (cc.cfgfile);
184 GNUNET_free (loglev);
185 GNUNET_free (allopts);
186 return GNUNET_SYSERR;
187 }
188 GNUNET_free (allopts);
189
190 /* run */
191 cc.args = &argv[ret];
192 GNUNET_SCHEDULER_run (&program_main, &cc);
193
194 /* clean up */
195 GNUNET_CONFIGURATION_destroy (cc.cfg);
196 GNUNET_free_non_null (cc.cfgfile);
197 GNUNET_free (loglev);
198 return GNUNET_OK;
199}
200
201
202/* end of program.c */
diff --git a/src/util/pseudonym.c b/src/util/pseudonym.c
new file mode 100644
index 000000000..6d9146613
--- /dev/null
+++ b/src/util/pseudonym.c
@@ -0,0 +1,545 @@
1/*
2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/pseudonym.c
23 * @brief helper functions
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30#include "gnunet_disk_lib.h"
31#include "gnunet_pseudonym_lib.h"
32
33#define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/metadata" DIR_SEPARATOR_STR
34#define PS_NAMES_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/names" DIR_SEPARATOR_STR
35
36struct DiscoveryCallback
37{
38 struct DiscoveryCallback *next;
39 GNUNET_PSEUDONYM_Iterator callback;
40 void *closure;
41};
42
43static struct DiscoveryCallback *head;
44
45/**
46 * Internal notification about new tracked URI.
47 */
48static void
49internal_notify (const GNUNET_HashCode * id,
50 const struct GNUNET_CONTAINER_MetaData *md, int rating)
51{
52 struct DiscoveryCallback *pos;
53
54 pos = head;
55 while (pos != NULL)
56 {
57 pos->callback (pos->closure, id, md, rating);
58 pos = pos->next;
59 }
60}
61
62/**
63 * Register callback to be invoked whenever we discover
64 * a new pseudonym.
65 */
66int
67GNUNET_PSEUDONYM_discovery_callback_register (struct
68 GNUNET_CONFIGURATION_Handle
69 *cfg,
70 GNUNET_PSEUDONYM_Iterator
71 iterator, void *closure)
72{
73 struct DiscoveryCallback *list;
74
75 list = GNUNET_malloc (sizeof (struct DiscoveryCallback));
76 list->callback = iterator;
77 list->closure = closure;
78 list->next = head;
79 head = list;
80 GNUNET_PSEUDONYM_list_all (cfg, iterator, closure);
81 return GNUNET_OK;
82}
83
84/**
85 * Unregister pseudonym discovery callback.
86 */
87int
88GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
89 iterator, void *closure)
90{
91 struct DiscoveryCallback *prev;
92 struct DiscoveryCallback *pos;
93
94 prev = NULL;
95 pos = head;
96 while ((pos != NULL) &&
97 ((pos->callback != iterator) || (pos->closure != closure)))
98 {
99 prev = pos;
100 pos = pos->next;
101 }
102 if (pos == NULL)
103 return GNUNET_SYSERR;
104 if (prev == NULL)
105 head = pos->next;
106 else
107 prev->next = pos->next;
108 GNUNET_free (pos);
109 return GNUNET_OK;
110}
111
112
113/**
114 * Get the filename (or directory name) for the given
115 * pseudonym identifier and directory prefix.
116 */
117static char *
118get_data_filename (struct GNUNET_CONFIGURATION_Handle
119 *cfg, const char *prefix, const GNUNET_HashCode * psid)
120{
121 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
122
123 if (psid != NULL)
124 GNUNET_CRYPTO_hash_to_enc (psid, &enc);
125 return GNUNET_DISK_get_home_filename (cfg,
126 GNUNET_CLIENT_SERVICE_NAME,
127 prefix,
128 (psid ==
129 NULL) ? NULL : (const char *) &enc,
130 NULL);
131}
132
133static void
134write_pseudonym_info (struct GNUNET_CONFIGURATION_Handle *cfg,
135 const GNUNET_HashCode * nsid,
136 const struct GNUNET_CONTAINER_MetaData *meta,
137 int32_t ranking, const char *ns_name)
138{
139 unsigned int size;
140 unsigned int tag;
141 unsigned int off;
142 char *buf;
143 char *fn;
144
145 fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
146 GNUNET_assert (fn != NULL);
147 size = GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
148 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
149 tag = size + sizeof (int) + 1;
150 off = 0;
151 if (ns_name != NULL)
152 {
153 off = strlen (ns_name);
154 tag += off;
155 }
156 buf = GNUNET_malloc (tag);
157 ((int *) buf)[0] = htonl (ranking); /* ranking */
158 if (ns_name != NULL)
159 {
160 memcpy (&buf[sizeof (int)], ns_name, off + 1);
161 }
162 else
163 {
164 buf[sizeof (int)] = '\0';
165 }
166 GNUNET_assert
167 (size == GNUNET_CONTAINER_meta_data_serialize (meta,
168 &buf[sizeof
169 (int) +
170 off + 1],
171 size,
172 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL));
173 GNUNET_DISK_file_write (fn, buf, tag, "660");
174 GNUNET_free (fn);
175 GNUNET_free (buf);
176 /* create entry for pseudonym name in names */
177 GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
178}
179
180static int
181read_info (struct GNUNET_CONFIGURATION_Handle *cfg,
182 const GNUNET_HashCode * nsid,
183 struct GNUNET_CONTAINER_MetaData **meta,
184 int32_t * ranking, char **ns_name)
185{
186 unsigned long long len;
187 unsigned int size;
188 unsigned int zend;
189 struct stat sbuf;
190 char *buf;
191 char *fn;
192
193 if (meta != NULL)
194 *meta = NULL;
195 if (ns_name != NULL)
196 *ns_name = NULL;
197 fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
198 GNUNET_assert (fn != NULL);
199
200 if ((0 != STAT (fn, &sbuf))
201 || (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES)))
202 {
203 GNUNET_free (fn);
204 return GNUNET_SYSERR;
205 }
206 if (len <= sizeof (int) + 1)
207 {
208 GNUNET_free (fn);
209 return GNUNET_SYSERR;
210 }
211 if (len > 16 * 1024 * 1024)
212 {
213 /* too big, must be invalid! remove! */
214 GNUNET_break (0);
215 if (0 != UNLINK (fn))
216 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
217 GNUNET_free (fn);
218 return GNUNET_SYSERR;
219 }
220 buf = GNUNET_malloc (len);
221 if (len != GNUNET_DISK_file_read (fn, len, buf))
222 {
223 GNUNET_free (buf);
224 GNUNET_free (fn);
225 return GNUNET_SYSERR;
226 }
227 if (ranking != NULL)
228 *ranking = ntohl (((int *) buf)[0]);
229 zend = sizeof (int);
230 while ((zend < len) && (buf[zend] != '\0'))
231 zend++;
232 if (zend == len)
233 {
234 GNUNET_free (buf);
235 GNUNET_free (fn);
236 return GNUNET_SYSERR;
237 }
238 if (ns_name != NULL)
239 {
240 if (zend != sizeof (int))
241 *ns_name = GNUNET_strdup (&buf[sizeof (int)]);
242 else
243 *ns_name = NULL;
244 }
245 zend++;
246 size = len - zend;
247 if (meta != NULL)
248 {
249 *meta = GNUNET_CONTAINER_meta_data_deserialize (&buf[zend], size);
250 if ((*meta) == NULL)
251 {
252 /* invalid data! remove! */
253 GNUNET_break (0);
254 if (0 != UNLINK (fn))
255 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
256 "unlink", fn);
257 GNUNET_free (buf);
258 GNUNET_free (fn);
259 return GNUNET_SYSERR;
260 }
261 }
262 GNUNET_free (fn);
263 GNUNET_free (buf);
264 return GNUNET_OK;
265}
266
267
268
269/**
270 * Return the unique, human readable name for the given namespace.
271 *
272 * @return NULL on failure (should never happen)
273 */
274char *
275GNUNET_PSEUDONYM_id_to_name (struct GNUNET_CONFIGURATION_Handle *cfg,
276 const GNUNET_HashCode * nsid)
277{
278 struct GNUNET_CONTAINER_MetaData *meta;
279 char *name;
280 GNUNET_HashCode nh;
281 char *fn;
282 unsigned long long len;
283 int fd;
284 unsigned int i;
285 unsigned int idx;
286 char *ret;
287 struct stat sbuf;
288
289 meta = NULL;
290 name = NULL;
291 if (GNUNET_OK == read_info (cfg, nsid, &meta, NULL, &name))
292 {
293 if ((meta != NULL) && (name == NULL))
294 name = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
295 EXTRACTOR_TITLE,
296 EXTRACTOR_FILENAME,
297 EXTRACTOR_DESCRIPTION,
298 EXTRACTOR_SUBJECT,
299 EXTRACTOR_PUBLISHER,
300 EXTRACTOR_AUTHOR,
301 EXTRACTOR_COMMENT,
302 EXTRACTOR_SUMMARY,
303 EXTRACTOR_OWNER,
304 -1);
305 if (meta != NULL)
306 {
307 GNUNET_CONTAINER_meta_data_destroy (meta);
308 meta = NULL;
309 }
310 }
311 if (name == NULL)
312 name = GNUNET_strdup (_("no-name"));
313 GNUNET_CRYPTO_hash (name, strlen (name), &nh);
314 fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
315 GNUNET_assert (fn != NULL);
316
317 len = 0;
318 if (0 == STAT (fn, &sbuf))
319 GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
320 fd = GNUNET_DISK_file_open (fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
321 i = 0;
322 idx = -1;
323 while ((len >= sizeof (GNUNET_HashCode)) &&
324 (sizeof (GNUNET_HashCode)
325 == READ (fd, &nh, sizeof (GNUNET_HashCode))))
326 {
327 if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
328 {
329 idx = i;
330 break;
331 }
332 i++;
333 len -= sizeof (GNUNET_HashCode);
334 }
335 if (idx == -1)
336 {
337 idx = i;
338 if (sizeof (GNUNET_HashCode) !=
339 WRITE (fd, nsid, sizeof (GNUNET_HashCode)))
340 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
341 }
342 CLOSE (fd);
343 ret = GNUNET_malloc (strlen (name) + 32);
344 GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
345 GNUNET_free (name);
346 GNUNET_free (fn);
347 return ret;
348}
349
350/**
351 * Get the namespace ID belonging to the given namespace name.
352 *
353 * @return GNUNET_OK on success
354 */
355int
356GNUNET_PSEUDONYM_name_to_id (struct GNUNET_CONFIGURATION_Handle *cfg,
357 const char *ns_uname, GNUNET_HashCode * nsid)
358{
359 size_t slen;
360 unsigned long long len;
361 unsigned int idx;
362 char *name;
363 GNUNET_HashCode nh;
364 char *fn;
365 int fd;
366
367 idx = -1;
368 slen = strlen (ns_uname);
369 while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
370 slen--;
371 if (slen == 0)
372 return GNUNET_SYSERR;
373 name = GNUNET_strdup (ns_uname);
374 name[slen - 1] = '\0';
375 GNUNET_CRYPTO_hash (name, strlen (name), &nh);
376 GNUNET_free (name);
377 fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
378 GNUNET_assert (fn != NULL);
379
380 if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
381 (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
382 ((idx + 1) * sizeof (GNUNET_HashCode) > len))
383 {
384 GNUNET_free (fn);
385 return GNUNET_SYSERR;
386 }
387 fd = GNUNET_DISK_file_open (fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
388 GNUNET_free (fn);
389 LSEEK (fd, idx * sizeof (GNUNET_HashCode), SEEK_SET);
390 if (sizeof (GNUNET_HashCode) != READ (fd, nsid, sizeof (GNUNET_HashCode)))
391 {
392 CLOSE (fd);
393 return GNUNET_SYSERR;
394 }
395 CLOSE (fd);
396 return GNUNET_OK;
397}
398
399
400
401
402struct ListPseudonymClosure
403{
404 GNUNET_PSEUDONYM_Iterator iterator;
405 void *closure;
406 struct GNUNET_CONFIGURATION_Handle *cfg;
407};
408
409static int
410list_pseudonym_helper (void *cls, const char *fullname)
411{
412 struct ListPseudonymClosure *c = cls;
413 int ret;
414 GNUNET_HashCode id;
415 int rating;
416 struct GNUNET_CONTAINER_MetaData *meta;
417 const char *fn;
418
419 if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
420 return GNUNET_OK;
421 fn =
422 &fullname[strlen (fullname) + 1 -
423 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
424 if (fn[-1] != DIR_SEPARATOR)
425 return GNUNET_OK;
426 ret = GNUNET_OK;
427 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
428 return GNUNET_OK; /* invalid name */
429 if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, NULL))
430 return GNUNET_OK; /* ignore entry */
431 if (c->iterator != NULL)
432 ret = c->iterator (c->closure, &id, meta, rating);
433 GNUNET_CONTAINER_meta_data_destroy (meta);
434 return ret;
435}
436
437/**
438 * List all available pseudonyms.
439 */
440int
441GNUNET_PSEUDONYM_list_all (struct GNUNET_CONFIGURATION_Handle *cfg,
442 GNUNET_PSEUDONYM_Iterator iterator, void *closure)
443{
444 struct ListPseudonymClosure cls;
445 char *fn;
446 int ret;
447
448 cls.iterator = iterator;
449 cls.closure = closure;
450 cls.cfg = cfg;
451 fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
452 GNUNET_assert (fn != NULL);
453 GNUNET_DISK_directory_create (fn);
454 ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
455 GNUNET_free (fn);
456 return ret;
457}
458
459/**
460 * Change the ranking of a pseudonym.
461 *
462 * @param nsid id of the pseudonym
463 * @param delta by how much should the rating be
464 * changed?
465 * @return new rating of the pseudonym
466 */
467int
468GNUNET_PSEUDONYM_rank (struct GNUNET_CONFIGURATION_Handle *cfg,
469 const GNUNET_HashCode * nsid, int delta)
470{
471 struct GNUNET_CONTAINER_MetaData *meta;
472 int ret;
473 int32_t ranking;
474 char *name;
475
476 name = NULL;
477 ret = read_info (cfg, nsid, &meta, &ranking, &name);
478 if (ret == GNUNET_SYSERR)
479 {
480 ranking = 0;
481 meta = GNUNET_CONTAINER_meta_data_create ();
482 }
483 ranking += delta;
484 write_pseudonym_info (cfg, nsid, meta, ranking, name);
485 GNUNET_CONTAINER_meta_data_destroy (meta);
486 GNUNET_free_non_null (name);
487 return ranking;
488}
489
490/**
491 * Insert metadata into existing MD record (passed as cls).
492 */
493static int
494merge_meta_helper (EXTRACTOR_KeywordType type, const char *data, void *cls)
495{
496 struct GNUNET_CONTAINER_MetaData *meta = cls;
497 GNUNET_CONTAINER_meta_data_insert (meta, type, data);
498 return GNUNET_OK;
499}
500
501
502
503/**
504 * Add a pseudonym to the set of known pseudonyms.
505 * For all pseudonym advertisements that we discover
506 * FSUI should automatically call this function.
507 *
508 * @param id the pseudonym identifier
509 */
510void
511GNUNET_PSEUDONYM_add (struct GNUNET_CONFIGURATION_Handle *cfg,
512 const GNUNET_HashCode * id,
513 const struct GNUNET_CONTAINER_MetaData *meta)
514{
515 char *name;
516 int32_t ranking;
517 struct GNUNET_CONTAINER_MetaData *old;
518 char *fn;
519 struct stat sbuf;
520
521 ranking = 0;
522 fn = get_data_filename (cfg, PS_METADATA_DIR, id);
523 GNUNET_assert (fn != NULL);
524
525 if ((0 == STAT (fn, &sbuf)) &&
526 (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
527 {
528 GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old);
529 write_pseudonym_info (cfg, id, old, ranking, name);
530 GNUNET_CONTAINER_meta_data_destroy (old);
531 GNUNET_free_non_null (name);
532 }
533 else
534 {
535 write_pseudonym_info (cfg, id, meta, ranking, NULL);
536 }
537 GNUNET_free (fn);
538 internal_notify (id, meta, ranking);
539}
540
541
542
543
544
545/* end of pseudonym.c */
diff --git a/src/util/scheduler.c b/src/util/scheduler.c
new file mode 100644
index 000000000..0d30910e0
--- /dev/null
+++ b/src/util/scheduler.c
@@ -0,0 +1,886 @@
1/*
2 This file is part of GNUnet
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @file util/scheduler/scheduler.c
23 * @brief schedule computations using continuation passing style
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_signal_lib.h"
30#include "gnunet_time_lib.h"
31
32/**
33 * Linked list of pending tasks.
34 */
35struct Task
36{
37 /**
38 * This is a linked list.
39 */
40 struct Task *next;
41
42 /**
43 * Function to run when ready.
44 */
45 GNUNET_SCHEDULER_Task callback;
46
47 /**
48 * Closure for the callback.
49 */
50 void *callback_cls;
51
52 /**
53 * Set of file descriptors this task is waiting
54 * for for reading. Once ready, this is updated
55 * to reflect the set of file descriptors ready
56 * for operation.
57 */
58 fd_set read_set;
59
60 /**
61 * Set of file descriptors this task is waiting
62 * for for writing. Once ready, this is updated
63 * to reflect the set of file descriptors ready
64 * for operation.
65 */
66 fd_set write_set;
67
68 /**
69 * Unique task identifier.
70 */
71 GNUNET_SCHEDULER_TaskIdentifier id;
72
73 /**
74 * Identifier of a prerequisite task.
75 */
76 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
77
78 /**
79 * Absolute timeout value for the task, or
80 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
81 */
82 struct GNUNET_TIME_Absolute timeout;
83
84 /**
85 * Why is the task ready? Set after task is added to ready queue.
86 * Initially set to zero. All reasons that have already been
87 * satisfied (i.e. read or write ready) will be set over time.
88 */
89 enum GNUNET_SCHEDULER_Reason reason;
90
91 /**
92 * Task priority.
93 */
94 enum GNUNET_SCHEDULER_Priority priority;
95
96 /**
97 * highest-numbered file descriptor in read_set or write_set plus one
98 */
99 int nfds;
100
101 /**
102 * Should this task be run on shutdown?
103 */
104 int run_on_shutdown;
105
106};
107
108
109/**
110 * Handle for the scheduling service.
111 */
112struct GNUNET_SCHEDULER_Handle
113{
114
115 /**
116 * List of tasks waiting for an event.
117 */
118 struct Task *pending;
119
120 /**
121 * List of tasks ready to run right now,
122 * grouped by importance.
123 */
124 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
125
126 /**
127 * Identity of the last task queued. Incremented for each task to
128 * generate a unique task ID (it is virtually impossible to start
129 * more than 2^64 tasks during the lifetime of a process).
130 */
131 GNUNET_SCHEDULER_TaskIdentifier last_id;
132
133 /**
134 * Highest number so that all tasks with smaller identifiers
135 * have already completed. Also the lowest number of a task
136 * still waiting to be executed.
137 */
138 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
139
140 /**
141 * GNUNET_NO if we are running normally,
142 * GNUNET_YES if we are in shutdown mode.
143 */
144 int shutdown;
145
146 /**
147 * Number of tasks on the ready list.
148 */
149 unsigned int ready_count;
150
151 /**
152 * Priority of the task running right now. Only
153 * valid while a task is running.
154 */
155 enum GNUNET_SCHEDULER_Priority current_priority;
156
157};
158
159
160/**
161 * Check that the given priority is legal (and return it).
162 */
163static enum GNUNET_SCHEDULER_Priority
164check_priority (enum GNUNET_SCHEDULER_Priority p)
165{
166 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
167 return p;
168 GNUNET_assert (0);
169 return 0; /* make compiler happy */
170}
171
172
173/**
174 * Update the timeout value so that it is smaller than min.
175 */
176static void
177update_timeout (struct timeval *tv, struct GNUNET_TIME_Relative min)
178{
179 if (((tv->tv_sec * 1000) + (tv->tv_usec / 1000)) > min.value)
180 {
181 tv->tv_sec = min.value / 1000;
182 tv->tv_usec = (min.value - tv->tv_sec * 1000) * 1000;
183 }
184}
185
186
187/**
188 * Set the given file descriptor bit in the given set and update max
189 * to the maximum of the existing max and fd+1.
190 */
191static void
192set_fd (int fd, int *max, fd_set * set)
193{
194 if (*max <= fd)
195 *max = fd + 1;
196 FD_SET (fd, set);
197}
198
199
200/**
201 * Is a task with this identifier still pending? Also updates
202 * "lowest_pending_id" as a side-effect (for faster checks in the
203 * future), but only if the return value is "GNUNET_NO" (and
204 * the "lowest_pending_id" check failed).
205 *
206 * @return GNUNET_YES if so, GNUNET_NO if not
207 */
208static int
209is_pending (struct GNUNET_SCHEDULER_Handle *sched,
210 GNUNET_SCHEDULER_TaskIdentifier id)
211{
212 struct Task *pos;
213 enum GNUNET_SCHEDULER_Priority p;
214 GNUNET_SCHEDULER_TaskIdentifier min;
215
216 if (id < sched->lowest_pending_id)
217 return GNUNET_NO;
218 min = -1; /* maximum value */
219 pos = sched->pending;
220 while (pos != NULL)
221 {
222 if (pos->id == id)
223 return GNUNET_YES;
224 if (pos->id < min)
225 min = pos->id;
226 pos = pos->next;
227 }
228 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
229 {
230 pos = sched->ready[p];
231 while (pos != NULL)
232 {
233 if (pos->id == id)
234 return GNUNET_YES;
235 if (pos->id < min)
236 min = pos->id;
237 pos = pos->next;
238 }
239 }
240 sched->lowest_pending_id = min;
241 return GNUNET_NO;
242}
243
244
245/**
246 * Update all sets and timeout for select.
247 */
248static void
249update_sets (struct GNUNET_SCHEDULER_Handle *sched,
250 int *max, fd_set * rs, fd_set * ws, struct timeval *tv)
251{
252 int i;
253 struct Task *pos;
254
255 pos = sched->pending;
256 while (pos != NULL)
257 {
258 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) &&
259 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
260 {
261 pos = pos->next;
262 continue;
263 }
264
265 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
266 update_timeout (tv,
267 GNUNET_TIME_absolute_get_remaining (pos->timeout));
268 for (i = 0; i < pos->nfds; i++)
269 {
270 if (FD_ISSET (i, &pos->read_set))
271 set_fd (i, max, rs);
272 if (FD_ISSET (i, &pos->write_set))
273 set_fd (i, max, ws);
274 }
275 pos = pos->next;
276 }
277}
278
279
280/**
281 * Check if the ready set overlaps with the set we want to have ready.
282 * If so, update the want set (set all FDs that are ready). If not,
283 * return GNUNET_NO.
284 *
285 * @param maxfd highest FD that needs to be checked.
286 * @return GNUNET_YES if there was some overlap
287 */
288static int
289set_overlaps (const fd_set * ready, fd_set * want, int maxfd)
290{
291 int i;
292
293 for (i = 0; i < maxfd; i++)
294 if (FD_ISSET (i, want) && FD_ISSET (i, ready))
295 {
296 /* copy all over (yes, there maybe unrelated bits,
297 but this should not hurt well-written clients) */
298 memcpy (want, ready, sizeof (fd_set));
299 return GNUNET_YES;
300 }
301 return GNUNET_NO;
302}
303
304
305/**
306 * Check if the given task is eligible to run now.
307 * Also set the reason why it is eligible.
308 *
309 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
310 */
311static int
312is_ready (struct GNUNET_SCHEDULER_Handle *sched,
313 struct Task *task,
314 struct GNUNET_TIME_Absolute now,
315 const fd_set * rs, const fd_set * ws)
316{
317 if ((GNUNET_NO == task->run_on_shutdown) && (GNUNET_YES == sched->shutdown))
318 return GNUNET_NO;
319 if ((GNUNET_YES == task->run_on_shutdown) &&
320 (GNUNET_YES == sched->shutdown))
321 task->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
322 if (now.value >= task->timeout.value)
323 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
324 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
325 (rs != NULL) && (set_overlaps (rs, &task->read_set, task->nfds)))
326 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
327 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
328 (ws != NULL) && (set_overlaps (ws, &task->write_set, task->nfds)))
329 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
330 if (task->reason == 0)
331 return GNUNET_NO; /* not ready */
332 if (task->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)
333 {
334 if (GNUNET_YES == is_pending (sched, task->prereq_id))
335 return GNUNET_NO; /* prereq waiting */
336 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
337 }
338 return GNUNET_YES;
339}
340
341
342/**
343 * Put a task that is ready for execution into the ready queue.
344 */
345static void
346queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
347{
348 task->next = handle->ready[check_priority (task->priority)];
349 handle->ready[check_priority (task->priority)] = task;
350 handle->ready_count++;
351}
352
353
354/**
355 * Check which tasks are ready and move them
356 * to the respective ready queue.
357 */
358static void
359check_ready (struct GNUNET_SCHEDULER_Handle *handle,
360 const fd_set * rs, const fd_set * ws)
361{
362 struct Task *pos;
363 struct Task *prev;
364 struct Task *next;
365 struct GNUNET_TIME_Absolute now;
366
367 now = GNUNET_TIME_absolute_get ();
368 prev = NULL;
369 pos = handle->pending;
370 while (pos != NULL)
371 {
372 next = pos->next;
373 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
374 {
375 if (prev == NULL)
376 handle->pending = next;
377 else
378 prev->next = next;
379 queue_ready_task (handle, pos);
380 pos = next;
381 continue;
382 }
383 prev = pos;
384 pos = next;
385 }
386}
387
388
389/**
390 * Run at least one task in the highest-priority queue that is not
391 * empty. Keep running tasks until we are either no longer running
392 * "URGENT" tasks or until we have at least one "pending" task (which
393 * may become ready, hence we should select on it). Naturally, if
394 * there are no more ready tasks, we also return.
395 */
396static void
397run_ready (struct GNUNET_SCHEDULER_Handle *sched)
398{
399 enum GNUNET_SCHEDULER_Priority p;
400 struct Task *pos;
401 struct GNUNET_SCHEDULER_TaskContext tc;
402
403 do
404 {
405 if (sched->ready_count == 0)
406 return;
407 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
408 /* yes, p>0 is correct, 0 is "KEEP" which should
409 always be an empty queue (see assertion)! */
410 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
411 {
412 pos = sched->ready[p];
413 if (pos != NULL)
414 break;
415 }
416 GNUNET_assert (pos != NULL); /* ready_count wrong? */
417 sched->ready[p] = pos->next;
418 sched->ready_count--;
419 sched->current_priority = p;
420 GNUNET_assert (pos->priority == p);
421 tc.sched = sched;
422 tc.reason = pos->reason;
423 tc.read_ready = &pos->read_set;
424 tc.write_ready = &pos->write_set;
425 pos->callback (pos->callback_cls, &tc);
426 GNUNET_free (pos);
427 }
428 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
429}
430
431
432/**
433 * Have we (ever) received a SIGINT/TERM/QUIT/HUP?
434 */
435static volatile int sig_shutdown;
436
437
438/**
439 * Signal handler called for signals that should cause us to shutdown.
440 */
441static void
442sighandler_shutdown ()
443{
444 sig_shutdown = 1;
445}
446
447
448/**
449 * Initialize a scheduler using this thread. This function will
450 * return when either a shutdown was initiated (via signal) and all
451 * tasks marked to "run_on_shutdown" have been completed or when all
452 * tasks in general have been completed.
453 *
454 * @param task task to run immediately
455 * @param cls closure of task
456 */
457void
458GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls)
459{
460 struct GNUNET_SCHEDULER_Handle sched;
461 fd_set rs;
462 fd_set ws;
463 int max;
464 struct timeval tv;
465 int ret;
466 struct GNUNET_SIGNAL_Context *shc_int;
467 struct GNUNET_SIGNAL_Context *shc_term;
468 struct GNUNET_SIGNAL_Context *shc_quit;
469 struct GNUNET_SIGNAL_Context *shc_hup;
470 struct Task *tpos;
471
472 sig_shutdown = 0;
473 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
474 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
475 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
476 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
477 memset (&sched, 0, sizeof (sched));
478 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
479 GNUNET_SCHEDULER_add_continuation (&sched,
480 GNUNET_YES,
481 task,
482 cls, GNUNET_SCHEDULER_REASON_STARTUP);
483 while ((GNUNET_NO == sched.shutdown) &&
484 (!sig_shutdown) &&
485 ((sched.pending != NULL) || (sched.ready_count > 0)))
486 {
487 FD_ZERO (&rs);
488 FD_ZERO (&ws);
489 max = 0;
490 tv.tv_sec = 0x7FFFFFFF;
491 tv.tv_usec = 0;
492 if (sched.ready_count > 0)
493 {
494 /* no blocking, more work already ready! */
495 tv.tv_sec = 0;
496 tv.tv_usec = 0;
497 }
498 update_sets (&sched, &max, &rs, &ws, &tv);
499 ret = SELECT (max, &rs, &ws, NULL, &tv);
500 if (ret == -1)
501 {
502 if (errno == EINTR)
503 continue;
504 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
505 break;
506 }
507 check_ready (&sched, &rs, &ws);
508 run_ready (&sched);
509 }
510 if (sig_shutdown)
511 sched.shutdown = GNUNET_YES;
512 GNUNET_SIGNAL_handler_uninstall (shc_int);
513 GNUNET_SIGNAL_handler_uninstall (shc_term);
514 GNUNET_SIGNAL_handler_uninstall (shc_quit);
515 GNUNET_SIGNAL_handler_uninstall (shc_hup);
516 do
517 {
518 run_ready (&sched);
519 check_ready (&sched, NULL, NULL);
520 }
521 while (sched.ready_count > 0);
522 while (NULL != (tpos = sched.pending))
523 {
524 sched.pending = tpos->next;
525 GNUNET_free (tpos);
526 }
527}
528
529
530/**
531 * Request the shutdown of a scheduler. This function can be used to
532 * stop a scheduling thread when created with the
533 * "GNUNET_SCHEDULER_init_thread" function or from within the signal
534 * handler for signals causing shutdowns.
535 */
536void
537GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
538{
539 sched->shutdown = GNUNET_YES;
540}
541
542
543/**
544 * Get information about the current load of this scheduler. Use this
545 * function to determine if an elective task should be added or simply
546 * dropped (if the decision should be made based on the number of
547 * tasks ready to run).
548 *
549 * @param sched scheduler to query
550 * @return number of tasks pending right now
551 */
552unsigned int
553GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
554 enum GNUNET_SCHEDULER_Priority p)
555{
556 struct Task *pos;
557 unsigned int ret;
558
559 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
560 return sched->ready_count;
561 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
562 p = sched->current_priority;
563 ret = 0;
564 pos = sched->ready[p];
565 while (pos != NULL)
566 {
567 pos = pos->next;
568 ret++;
569 }
570 return ret;
571}
572
573
574/**
575 * Cancel the task with the specified identifier.
576 * The task must not yet have run.
577 *
578 * @param sched scheduler to use
579 * @param task id of the task to cancel
580 */
581void *
582GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
583 GNUNET_SCHEDULER_TaskIdentifier task)
584{
585 struct Task *t;
586 struct Task *prev;
587 enum GNUNET_SCHEDULER_Priority p;
588 void *ret;
589
590 prev = NULL;
591 t = sched->pending;
592 while (t != NULL)
593 {
594 if (t->id == task)
595 break;
596 prev = t;
597 t = t->next;
598 }
599 p = 0;
600 while (t == NULL)
601 {
602 p++;
603 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
604 prev = NULL;
605 t = sched->ready[p];
606 while (t != NULL)
607 {
608 if (t->id == task)
609 {
610 sched->ready_count--;
611 break;
612 }
613 prev = t;
614 t = t->next;
615 }
616 }
617 if (prev == NULL)
618 {
619 if (p == 0)
620 sched->pending = t->next;
621 else
622 sched->ready[p] = t->next;
623 }
624 else
625 prev->next = t->next;
626 ret = t->callback_cls;
627 GNUNET_free (t);
628 return ret;
629}
630
631
632/**
633 * Continue the current execution with the given function. This is
634 * similar to the other "add" functions except that there is no delay
635 * and the reason code can be specified.
636 *
637 * @param sched scheduler to use
638 * @param main main function of the task
639 * @param cls closure of task
640 * @param reason reason for task invocation
641 */
642void
643GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
644 int run_on_shutdown,
645 GNUNET_SCHEDULER_Task main,
646 void *cls,
647 enum GNUNET_SCHEDULER_Reason reason)
648{
649 struct Task *task;
650
651 task = GNUNET_malloc (sizeof (struct Task));
652 task->callback = main;
653 task->callback_cls = cls;
654 task->id = ++sched->last_id;
655 task->reason = reason;
656 task->priority = sched->current_priority;
657 task->run_on_shutdown = run_on_shutdown;
658 queue_ready_task (sched, task);
659}
660
661
662/**
663 * Schedule a new task to be run after the specified
664 * prerequisite task has completed.
665 *
666 * @param sched scheduler to use
667 * @param run_on_shutdown run on shutdown?
668 * @param prio how important is this task?
669 * @param prerequisite_task run this task after the task with the given
670 * task identifier completes (and any of our other
671 * conditions, such as delay, read or write-readyness
672 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
673 * on completion of other tasks.
674 * @param main main function of the task
675 * @param cls closure of task
676 * @return unique task identifier for the job
677 * only valid until "main" is started!
678 */
679GNUNET_SCHEDULER_TaskIdentifier
680GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
681 int run_on_shutdown,
682 enum GNUNET_SCHEDULER_Priority prio,
683 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
684 GNUNET_SCHEDULER_Task main, void *cls)
685{
686 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
687 prerequisite_task,
688 GNUNET_TIME_UNIT_ZERO,
689 0, NULL, NULL, main, cls);
690}
691
692
693/**
694 * Schedule a new task to be run with a specified delay. The task
695 * will be scheduled for execution once the delay has expired and the
696 * prerequisite task has completed.
697 *
698 * @param sched scheduler to use
699 * @param run_on_shutdown run on shutdown? You can use this
700 * argument to run a function only during shutdown
701 * by setting delay to -1. Set this
702 * argument to GNUNET_NO to skip this task if
703 * the user requested process termination.
704 * @param prio how important is this task?
705 * @param prerequisite_task run this task after the task with the given
706 * task identifier completes (and any of our other
707 * conditions, such as delay, read or write-readyness
708 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
709 * on completion of other tasks.
710 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
711 * @param main main function of the task
712 * @param cls closure of task
713 * @return unique task identifier for the job
714 * only valid until "main" is started!
715 */
716GNUNET_SCHEDULER_TaskIdentifier
717GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
718 int run_on_shutdown,
719 enum GNUNET_SCHEDULER_Priority prio,
720 GNUNET_SCHEDULER_TaskIdentifier
721 prerequisite_task,
722 struct GNUNET_TIME_Relative delay,
723 GNUNET_SCHEDULER_Task main, void *cls)
724{
725 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
726 prerequisite_task, delay,
727 0, NULL, NULL, main, cls);
728}
729
730
731/**
732 * Schedule a new task to be run with a specified delay or when the
733 * specified file descriptor is ready for reading. The delay can be
734 * used as a timeout on the socket being ready. The task will be
735 * scheduled for execution once either the delay has expired or the
736 * socket operation is ready.
737 *
738 * @param sched scheduler to use
739 * @param run_on_shutdown run on shutdown? Set this
740 * argument to GNUNET_NO to skip this task if
741 * the user requested process termination.
742 * @param prio how important is this task?
743 * @param prerequisite_task run this task after the task with the given
744 * task identifier completes (and any of our other
745 * conditions, such as delay, read or write-readyness
746 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
747 * on completion of other tasks.
748 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
749 * @param rfd read file-descriptor
750 * @param main main function of the task
751 * @param cls closure of task
752 * @return unique task identifier for the job
753 * only valid until "main" is started!
754 */
755GNUNET_SCHEDULER_TaskIdentifier
756GNUNET_SCHEDULER_add_read (struct GNUNET_SCHEDULER_Handle * sched,
757 int run_on_shutdown,
758 enum GNUNET_SCHEDULER_Priority prio,
759 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
760 struct GNUNET_TIME_Relative delay,
761 int rfd, GNUNET_SCHEDULER_Task main, void *cls)
762{
763 fd_set rs;
764
765 GNUNET_assert (rfd >= 0);
766 FD_ZERO (&rs);
767 FD_SET (rfd, &rs);
768 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
769 prerequisite_task, delay,
770 rfd + 1, &rs, NULL, main, cls);
771}
772
773
774/**
775 * Schedule a new task to be run with a specified delay or when the
776 * specified file descriptor is ready for writing. The delay can be
777 * used as a timeout on the socket being ready. The task will be
778 * scheduled for execution once either the delay has expired or the
779 * socket operation is ready.
780 *
781 * @param sched scheduler to use
782 * @param run_on_shutdown run on shutdown? Set this
783 * argument to GNUNET_NO to skip this task if
784 * the user requested process termination.
785 * @param prio how important is this task?
786 * @param prerequisite_task run this task after the task with the given
787 * task identifier completes (and any of our other
788 * conditions, such as delay, read or write-readyness
789 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
790 * on completion of other tasks.
791 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
792 * @param wfd write file-descriptor
793 * @param main main function of the task
794 * @param cls closure of task
795 * @return unique task identifier for the job
796 * only valid until "main" is started!
797 */
798GNUNET_SCHEDULER_TaskIdentifier
799GNUNET_SCHEDULER_add_write (struct GNUNET_SCHEDULER_Handle * sched,
800 int run_on_shutdown,
801 enum GNUNET_SCHEDULER_Priority prio,
802 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
803 struct GNUNET_TIME_Relative delay,
804 int wfd, GNUNET_SCHEDULER_Task main, void *cls)
805{
806 fd_set ws;
807
808 GNUNET_assert (wfd >= 0);
809 FD_ZERO (&ws);
810 FD_SET (wfd, &ws);
811 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
812 prerequisite_task, delay,
813 wfd + 1, NULL, &ws, main, cls);
814}
815
816
817/**
818 * Schedule a new task to be run with a specified delay or when any of
819 * the specified file descriptor sets is ready. The delay can be used
820 * as a timeout on the socket(s) being ready. The task will be
821 * scheduled for execution once either the delay has expired or any of
822 * the socket operations is ready. This is the most general
823 * function of the "add" family. Note that the "prerequisite_task"
824 * must be satisfied in addition to any of the other conditions. In
825 * other words, the task will be started when
826 * <code>
827 * (prerequisite-run)
828 * && (delay-ready
829 * || any-rs-ready
830 * || any-ws-ready
831 * || (shutdown-active && run-on-shutdown) )
832 * </code>
833 *
834 * @param sched scheduler to use
835 * @param run_on_shutdown run on shutdown? Set this
836 * argument to GNUNET_NO to skip this task if
837 * the user requested process termination.
838 * @param prio how important is this task?
839 * @param prerequisite_task run this task after the task with the given
840 * task identifier completes (and any of our other
841 * conditions, such as delay, read or write-readyness
842 * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
843 * on completion of other tasks.
844 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
845 * @param nfds highest-numbered file descriptor in any of the two sets plus one
846 * @param rs set of file descriptors we want to read (can be NULL)
847 * @param ws set of file descriptors we want to write (can be NULL)
848 * @param main main function of the task
849 * @param cls closure of task
850 * @return unique task identifier for the job
851 * only valid until "main" is started!
852 */
853GNUNET_SCHEDULER_TaskIdentifier
854GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
855 int run_on_shutdown,
856 enum GNUNET_SCHEDULER_Priority prio,
857 GNUNET_SCHEDULER_TaskIdentifier
858 prerequisite_task,
859 struct GNUNET_TIME_Relative delay,
860 int nfds, const fd_set * rs, const fd_set * ws,
861 GNUNET_SCHEDULER_Task main, void *cls)
862{
863 struct Task *task;
864
865 task = GNUNET_malloc (sizeof (struct Task));
866 task->callback = main;
867 task->callback_cls = cls;
868 if ((rs != NULL) && (nfds > 0))
869 memcpy (&task->read_set, rs, sizeof (fd_set));
870 if ((ws != NULL) && (nfds > 0))
871 memcpy (&task->write_set, ws, sizeof (fd_set));
872 task->id = ++sched->last_id;
873 task->prereq_id = prerequisite_task;
874 task->timeout = GNUNET_TIME_relative_to_absolute (delay);
875 task->priority =
876 check_priority ((prio ==
877 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->
878 current_priority : prio);
879 task->nfds = nfds;
880 task->run_on_shutdown = run_on_shutdown;
881 task->next = sched->pending;
882 sched->pending = task;
883 return task->id;
884}
885
886/* end of scheduler.c */
diff --git a/src/util/server.c b/src/util/server.c
new file mode 100644
index 000000000..91bc8cc7a
--- /dev/null
+++ b/src/util/server.c
@@ -0,0 +1,1091 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/server.c
23 * @brief library for building GNUnet network servers
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - fix inefficient memmove in message processing
28 */
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_network_lib.h"
33#include "gnunet_scheduler_lib.h"
34#include "gnunet_server_lib.h"
35#include "gnunet_time_lib.h"
36
37/**
38 * List of arrays of message handlers.
39 */
40struct HandlerList
41{
42 /**
43 * This is a linked list.
44 */
45 struct HandlerList *next;
46
47 /**
48 * NULL-terminated array of handlers.
49 */
50 const struct GNUNET_SERVER_MessageHandler *handlers;
51};
52
53
54/**
55 * List of arrays of message handlers.
56 */
57struct NotifyList
58{
59 /**
60 * This is a linked list.
61 */
62 struct NotifyList *next;
63
64 /**
65 * Function to call.
66 */
67 GNUNET_SERVER_DisconnectCallback callback;
68
69 /**
70 * Closure for callback.
71 */
72 void *callback_cls;
73};
74
75
76/**
77 * @brief handle for a server
78 */
79struct GNUNET_SERVER_Handle
80{
81 /**
82 * My scheduler.
83 */
84 struct GNUNET_SCHEDULER_Handle *sched;
85
86 /**
87 * List of handlers for incoming messages.
88 */
89 struct HandlerList *handlers;
90
91 /**
92 * List of our current clients.
93 */
94 struct GNUNET_SERVER_Client *clients;
95
96 /**
97 * Linked list of functions to call on disconnects by clients.
98 */
99 struct NotifyList *disconnect_notify_list;
100
101 /**
102 * Function to call for access control.
103 */
104 GNUNET_NETWORK_AccessCheck access;
105
106 /**
107 * Closure for access.
108 */
109 void *access_cls;
110
111 /**
112 * After how long should an idle connection time
113 * out (on write).
114 */
115 struct GNUNET_TIME_Relative idle_timeout;
116
117 /**
118 * maximum write buffer size for accepted sockets
119 */
120 size_t maxbuf;
121
122 /**
123 * Pipe used to signal shutdown of the server.
124 */
125 int shutpipe[2];
126
127 /**
128 * Socket used to listen for new connections. Set to
129 * "-1" by GNUNET_SERVER_destroy to initiate shutdown.
130 */
131 int listen_socket;
132
133 /**
134 * Set to GNUNET_YES if we are shutting down.
135 */
136 int do_shutdown;
137
138 /**
139 * Do we ignore messages of types that we do not
140 * understand or do we require that a handler
141 * is found (and if not kill the connection)?
142 */
143 int require_found;
144
145};
146
147
148/**
149 * @brief handle for a client of the server
150 */
151struct GNUNET_SERVER_Client
152{
153
154 /**
155 * Size of the buffer for incoming data. Should be
156 * first so we get nice alignment.
157 */
158 char incoming_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
159
160 /**
161 * This is a linked list.
162 */
163 struct GNUNET_SERVER_Client *next;
164
165 /**
166 * Server that this client belongs to.
167 */
168 struct GNUNET_SERVER_Handle *server;
169
170 /**
171 * Client closure for callbacks.
172 */
173 void *client_closure;
174
175 /**
176 * Callback to receive from client.
177 */
178 GNUNET_SERVER_ReceiveCallback receive;
179
180 /**
181 * Callback to cancel receive from client.
182 */
183 GNUNET_SERVER_ReceiveCancelCallback receive_cancel;
184
185 /**
186 * Callback to ask about transmit-ready notification.
187 */
188 GNUNET_SERVER_TransmitReadyCallback notify_transmit_ready;
189
190 /**
191 * Callback to ask about transmit-ready notification.
192 */
193 GNUNET_SERVER_TransmitReadyCancelCallback notify_transmit_ready_cancel;
194
195 /**
196 * Callback to check if client is still valid.
197 */
198 GNUNET_SERVER_CheckCallback check;
199
200 /**
201 * Callback to destroy client.
202 */
203 GNUNET_SERVER_DestroyCallback destroy;
204
205 /**
206 * Side-buffer for incoming data used when processing
207 * is suspended.
208 */
209 char *side_buf;
210
211 /**
212 * Number of bytes in the side buffer.
213 */
214 size_t side_buf_size;
215
216 /**
217 * Last activity on this socket (used to time it out
218 * if reference_count == 0).
219 */
220 struct GNUNET_TIME_Absolute last_activity;
221
222 /**
223 * Current task identifier for the receive call
224 * (or GNUNET_SCHEDULER_NO_PREREQUISITE_TASK for none).
225 */
226 GNUNET_SCHEDULER_TaskIdentifier my_receive;
227
228 /**
229 * How many bytes in the "incoming_buffer" are currently
230 * valid? (starting at offset 0).
231 */
232 size_t receive_pos;
233
234 /**
235 * Number of external entities with a reference to
236 * this client object.
237 */
238 unsigned int reference_count;
239
240 /**
241 * Was processing if incoming messages suspended while
242 * we were still processing data already received?
243 * This is a counter saying how often processing was
244 * suspended (once per handler invoked).
245 */
246 unsigned int suspended;
247
248 /**
249 * Are we currently in the "process_client_buffer" function (and
250 * will hence restart the receive job on exit if suspended == 0 once
251 * we are done?). If this is set, then "receive_done" will
252 * essentially only decrement suspended; if this is not set, then
253 * "receive_done" may need to restart the receive process (either
254 * from the side-buffer or via select/recv).
255 */
256 int in_process_client_buffer;
257
258 /**
259 * We're about to close down this client due to some serious
260 * error.
261 */
262 int shutdown_now;
263
264};
265
266
267/**
268 * Server has been asked to shutdown, free resources.
269 */
270static void
271destroy_server (struct GNUNET_SERVER_Handle *server)
272{
273 struct GNUNET_SERVER_Client *pos;
274 struct HandlerList *hpos;
275 struct NotifyList *npos;
276
277 GNUNET_assert (server->listen_socket == -1);
278 GNUNET_break (0 == CLOSE (server->shutpipe[0]));
279 GNUNET_break (0 == CLOSE (server->shutpipe[1]));
280 while (server->clients != NULL)
281 {
282 pos = server->clients;
283 server->clients = pos->next;
284 pos->server = NULL;
285 }
286 while (NULL != (hpos = server->handlers))
287 {
288 server->handlers = hpos->next;
289 GNUNET_free (hpos);
290 }
291 while (NULL != (npos = server->disconnect_notify_list))
292 {
293 server->disconnect_notify_list = npos->next;
294 GNUNET_free (npos);
295 }
296 GNUNET_free (server);
297}
298
299
300/**
301 * Scheduler says our listen socket is ready.
302 * Process it!
303 */
304static void
305process_listen_socket (void *cls,
306 const struct GNUNET_SCHEDULER_TaskContext *tc)
307{
308 struct GNUNET_SERVER_Handle *server = cls;
309 struct GNUNET_NETWORK_SocketHandle *sock;
310 struct GNUNET_SERVER_Client *client;
311 fd_set r;
312
313 if ((server->do_shutdown) ||
314 ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0))
315 {
316 /* shutdown was initiated */
317 GNUNET_assert (server->listen_socket != -1);
318 GNUNET_break (0 == CLOSE (server->listen_socket));
319 server->listen_socket = -1;
320 if (server->do_shutdown)
321 destroy_server (server);
322 return;
323 }
324 GNUNET_assert (FD_ISSET (server->listen_socket, tc->read_ready));
325 GNUNET_assert (!FD_ISSET (server->shutpipe[0], tc->read_ready));
326 sock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
327 server->access,
328 server->access_cls,
329 server->listen_socket,
330 server->maxbuf);
331 if (sock != NULL)
332 {
333 client = GNUNET_SERVER_connect_socket (server, sock);
334 /* decrement reference count, we don't keep "client" alive */
335 GNUNET_SERVER_client_drop (client);
336 }
337 /* listen for more! */
338 FD_ZERO (&r);
339 FD_SET (server->listen_socket, &r);
340 FD_SET (server->shutpipe[0], &r);
341 GNUNET_SCHEDULER_add_select (server->sched,
342 GNUNET_YES,
343 GNUNET_SCHEDULER_PRIORITY_HIGH,
344 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
345 GNUNET_TIME_UNIT_FOREVER_REL,
346 GNUNET_MAX (server->listen_socket,
347 server->shutpipe[0]) + 1, &r, NULL,
348 &process_listen_socket, server);
349}
350
351
352/**
353 * Create and initialize a listen socket for the server.
354 *
355 * @return -1 on error, otherwise the listen socket
356 */
357static int
358open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
359{
360 const static int on = 1;
361 int fd;
362 uint16_t port;
363
364 switch (serverAddr->sa_family)
365 {
366 case AF_INET:
367 port = ntohs (((const struct sockaddr_in *) serverAddr)->sin_port);
368 break;
369 case AF_INET6:
370 port = ntohs (((const struct sockaddr_in6 *) serverAddr)->sin6_port);
371 break;
372 default:
373 GNUNET_break (0);
374 return -1;
375 }
376 fd = SOCKET (serverAddr->sa_family, SOCK_STREAM, 0);
377 if (fd < 0)
378 {
379 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
380 return -1;
381 }
382 if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC))
383 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
384 "fcntl");
385 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
386 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
387 "setsockopt");
388 /* bind the socket */
389 if (BIND (fd, serverAddr, socklen) < 0)
390 {
391 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
392 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393 _
394 ("`%s' failed for port %d. Is the service already running?\n"),
395 "bind", port);
396 GNUNET_break (0 == CLOSE (fd));
397 return -1;
398 }
399 if (0 != LISTEN (fd, 5))
400 {
401 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
402 GNUNET_break (0 == CLOSE (fd));
403 return -1;
404 }
405 return fd;
406}
407
408
409/**
410 * Create a new server.
411 *
412 * @param sched scheduler to use
413 * @param access function for access control
414 * @param access_cls closure for access
415 * @param serverAddr address to listen on (including port), use NULL
416 * for internal server (no listening)
417 * @param socklen length of serverAddr
418 * @param maxbuf maximum write buffer size for accepted sockets
419 * @param idle_timeout after how long should we timeout idle connections?
420 * @param require_found if YES, connections sending messages of unknown type
421 * will be closed
422 * @return handle for the new server, NULL on error
423 * (typically, "port" already in use)
424 */
425struct GNUNET_SERVER_Handle *
426GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched,
427 GNUNET_NETWORK_AccessCheck access,
428 void *access_cls,
429 const struct sockaddr *serverAddr,
430 socklen_t socklen,
431 size_t maxbuf,
432 struct GNUNET_TIME_Relative
433 idle_timeout, int require_found)
434{
435 struct GNUNET_SERVER_Handle *ret;
436 int lsock;
437 fd_set r;
438
439 lsock = -2;
440 if (serverAddr != NULL)
441 {
442 lsock = open_listen_socket (serverAddr, socklen);
443 if (lsock == -1)
444 return NULL;
445 }
446 ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle));
447 if (0 != PIPE (ret->shutpipe))
448 {
449 GNUNET_break (0 == CLOSE (lsock));
450 GNUNET_free (ret);
451 return NULL;
452 }
453 ret->sched = sched;
454 ret->maxbuf = maxbuf;
455 ret->idle_timeout = idle_timeout;
456 ret->listen_socket = lsock;
457 ret->access = access;
458 ret->access_cls = access_cls;
459 ret->require_found = require_found;
460 if (lsock >= 0)
461 {
462 FD_ZERO (&r);
463 FD_SET (ret->listen_socket, &r);
464 FD_SET (ret->shutpipe[0], &r);
465 GNUNET_SCHEDULER_add_select (sched,
466 GNUNET_YES,
467 GNUNET_SCHEDULER_PRIORITY_HIGH,
468 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
469 GNUNET_TIME_UNIT_FOREVER_REL,
470 GNUNET_MAX (ret->listen_socket,
471 ret->shutpipe[0]) + 1, &r,
472 NULL, &process_listen_socket, ret);
473 }
474 return ret;
475}
476
477
478/**
479 * Free resources held by this server.
480 */
481void
482GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s)
483{
484 static char c;
485
486 GNUNET_assert (s->do_shutdown == GNUNET_NO);
487 s->do_shutdown = GNUNET_YES;
488 if (s->listen_socket == -1)
489 destroy_server (s);
490 else
491 GNUNET_break (1 == WRITE (s->shutpipe[1], &c, 1));
492}
493
494
495/**
496 * Add additional handlers to an existing server.
497 *
498 * @param server the server to add handlers to
499 * @param handlers array of message handlers for
500 * incoming messages; the last entry must
501 * have "NULL" for the "callback"; multiple
502 * entries for the same type are allowed,
503 * they will be called in order of occurence.
504 * These handlers can be removed later;
505 * the handlers array must exist until removed
506 * (or server is destroyed).
507 */
508void
509GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server,
510 const struct GNUNET_SERVER_MessageHandler
511 *handlers)
512{
513 struct HandlerList *p;
514
515 p = GNUNET_malloc (sizeof (struct HandlerList));
516 p->handlers = handlers;
517 p->next = server->handlers;
518 server->handlers = p;
519}
520
521
522/**
523 * Inject a message into the server, pretend it came
524 * from the specified client. Delivery of the message
525 * will happen instantly (if a handler is installed;
526 * otherwise the call does nothing).
527 *
528 * @param server the server receiving the message
529 * @param sender the "pretended" sender of the message
530 * can be NULL!
531 * @param message message to transmit
532 * @return GNUNET_OK if the message was OK and the
533 * connection can stay open
534 * GNUNET_SYSERR if the connection to the
535 * client should be shut down
536 */
537int
538GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server,
539 struct GNUNET_SERVER_Client *sender,
540 const struct GNUNET_MessageHeader *message)
541{
542 struct HandlerList *pos;
543 const struct GNUNET_SERVER_MessageHandler *mh;
544 unsigned int i;
545 uint16_t type;
546 uint16_t size;
547 int found;
548
549 type = ntohs (message->type);
550 size = ntohs (message->size);
551 pos = server->handlers;
552 found = GNUNET_NO;
553 while (pos != NULL)
554 {
555 i = 0;
556 while (pos->handlers[i].callback != NULL)
557 {
558 mh = &pos->handlers[i];
559 if (mh->type == type)
560 {
561 if ((mh->expected_size != 0) && (mh->expected_size != size))
562 {
563 GNUNET_break_op (0);
564 return GNUNET_SYSERR;
565 }
566 if (sender != NULL)
567 sender->suspended++;
568 mh->callback (mh->callback_cls, server, sender, message);
569 found = GNUNET_YES;
570 }
571 i++;
572 }
573 pos = pos->next;
574 }
575 if (found == GNUNET_NO)
576 {
577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
578 _("Received message of unknown type %d\n"), type);
579 if (server->require_found == GNUNET_YES)
580 return GNUNET_SYSERR;
581 }
582 return GNUNET_OK;
583}
584
585
586/**
587 * We're finished with this client and especially its input
588 * processing. If the RC is zero, free all resources otherwise wait
589 * until RC hits zero to do so.
590 */
591static void
592shutdown_incoming_processing (struct GNUNET_SERVER_Client *client)
593{
594 struct GNUNET_SERVER_Client *prev;
595 struct GNUNET_SERVER_Client *pos;
596 struct GNUNET_SERVER_Handle *server;
597 struct NotifyList *n;
598 unsigned int rc;
599
600 GNUNET_assert (client->my_receive == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
601 rc = client->reference_count;
602 if (client->server != NULL)
603 {
604 server = client->server;
605 client->server = NULL;
606 prev = NULL;
607 pos = server->clients;
608 while ((pos != NULL) && (pos != client))
609 {
610 prev = pos;
611 pos = pos->next;
612 }
613 GNUNET_assert (pos != NULL);
614 if (prev == NULL)
615 server->clients = pos->next;
616 else
617 prev->next = pos->next;
618 n = server->disconnect_notify_list;
619 while (n != NULL)
620 {
621 n->callback (n->callback_cls, client);
622 n = n->next;
623 }
624 }
625 /* wait for RC to hit zero, then free */
626 if (rc > 0)
627 return;
628 client->destroy (client->client_closure);
629 GNUNET_free (client);
630}
631
632
633static void
634process_client_buffer (struct GNUNET_SERVER_Client *client)
635{
636 struct GNUNET_SERVER_Handle *server;
637 const struct GNUNET_MessageHeader *hdr;
638 size_t msize;
639
640 client->in_process_client_buffer = GNUNET_YES;
641 server = client->server;
642 while ((client->receive_pos >= sizeof (struct GNUNET_MessageHeader)) &&
643 (0 == client->suspended) && (GNUNET_YES != client->shutdown_now))
644 {
645 hdr = (const struct GNUNET_MessageHeader *) &client->incoming_buffer;
646 msize = ntohs (hdr->size);
647 if (msize > client->receive_pos)
648 break;
649 if ((msize < sizeof (struct GNUNET_MessageHeader)) ||
650 (GNUNET_OK != GNUNET_SERVER_inject (server, client, hdr)))
651 {
652 client->in_process_client_buffer = GNUNET_NO;
653 shutdown_incoming_processing (client);
654 return;
655 }
656 /* FIXME: this is highly inefficient; we should
657 try to avoid this if the new base address is
658 already nicely aligned. See old handler code... */
659 memmove (client->incoming_buffer,
660 &client->incoming_buffer[msize], client->receive_pos - msize);
661 client->receive_pos -= msize;
662 }
663 client->in_process_client_buffer = GNUNET_NO;
664 if (GNUNET_YES == client->shutdown_now)
665 shutdown_incoming_processing (client);
666}
667
668
669/**
670 * We are receiving an incoming message. Process it.
671 */
672static void
673process_incoming (void *cls,
674 const void *buf,
675 size_t available,
676 const struct sockaddr *addr, socklen_t addrlen, int errCode)
677{
678 struct GNUNET_SERVER_Client *client = cls;
679 struct GNUNET_SERVER_Handle *server = client->server;
680 const char *cbuf = buf;
681 size_t maxcpy;
682
683 client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
684 if ((buf == NULL) ||
685 (available == 0) ||
686 (errCode != 0) ||
687 (server == NULL) ||
688 (client->shutdown_now == GNUNET_YES) ||
689 (GNUNET_YES != client->check (client->client_closure)))
690 {
691 /* other side closed connection, error connecting, etc. */
692 shutdown_incoming_processing (client);
693 return;
694 }
695 GNUNET_SERVER_client_keep (client);
696 client->last_activity = GNUNET_TIME_absolute_get ();
697 /* process data (if available) */
698 while (available > 0)
699 {
700 maxcpy = available;
701 if (maxcpy > sizeof (client->incoming_buffer) - client->receive_pos)
702 maxcpy = sizeof (client->incoming_buffer) - client->receive_pos;
703 memcpy (&client->incoming_buffer[client->receive_pos], cbuf, maxcpy);
704 client->receive_pos += maxcpy;
705 cbuf += maxcpy;
706 available -= maxcpy;
707 if (0 < client->suspended)
708 {
709 if (available > 0)
710 {
711 client->side_buf_size = available;
712 client->side_buf = GNUNET_malloc (available);
713 memcpy (client->side_buf, cbuf, available);
714 available = 0;
715 }
716 break; /* do not run next client iteration! */
717 }
718 process_client_buffer (client);
719 }
720 GNUNET_assert (available == 0);
721 if ((client->suspended == 0) &&
722 (GNUNET_YES != client->shutdown_now) && (client->server != NULL))
723 {
724 /* Finally, keep receiving! */
725 client->my_receive = client->receive (client->client_closure,
726 GNUNET_SERVER_MAX_MESSAGE_SIZE,
727 server->idle_timeout,
728 &process_incoming, client);
729 }
730 if (GNUNET_YES == client->shutdown_now)
731 shutdown_incoming_processing (client);
732 GNUNET_SERVER_client_drop (client);
733}
734
735
736static void
737restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
738{
739 struct GNUNET_SERVER_Client *client = cls;
740
741 process_client_buffer (client);
742 if (0 == client->suspended)
743 client->my_receive = client->receive (client->client_closure,
744 GNUNET_SERVER_MAX_MESSAGE_SIZE,
745 client->server->idle_timeout,
746 &process_incoming, client);
747}
748
749
750/**
751 * Add a client to the set of our clients and
752 * start receiving.
753 */
754static void
755add_client (struct GNUNET_SERVER_Handle *server,
756 struct GNUNET_SERVER_Client *client)
757{
758 client->server = server;
759 client->last_activity = GNUNET_TIME_absolute_get ();
760 client->next = server->clients;
761 server->clients = client;
762 client->my_receive = client->receive (client->client_closure,
763 GNUNET_SERVER_MAX_MESSAGE_SIZE,
764 server->idle_timeout,
765 &process_incoming, client);
766}
767
768static GNUNET_SCHEDULER_TaskIdentifier
769sock_receive (void *cls,
770 size_t max,
771 struct GNUNET_TIME_Relative timeout,
772 GNUNET_NETWORK_Receiver receiver, void *receiver_cls)
773{
774 return GNUNET_NETWORK_receive (cls, max, timeout, receiver, receiver_cls);
775}
776
777static void
778sock_receive_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti)
779{
780 GNUNET_NETWORK_receive_cancel (cls, ti);
781}
782
783
784static void *
785sock_notify_transmit_ready (void *cls,
786 size_t size,
787 struct GNUNET_TIME_Relative timeout,
788 GNUNET_NETWORK_TransmitReadyNotify notify,
789 void *notify_cls)
790{
791 return GNUNET_NETWORK_notify_transmit_ready (cls, size, timeout, notify,
792 notify_cls);
793}
794
795
796static void
797sock_notify_transmit_ready_cancel (void *cls, void *h)
798{
799 GNUNET_NETWORK_notify_transmit_ready_cancel (h);
800}
801
802
803/**
804 * Check if socket is still valid (no fatal errors have happened so far).
805 *
806 * @param cls the socket
807 * @return GNUNET_YES if valid, GNUNET_NO otherwise
808 */
809static int
810sock_check (void *cls)
811{
812 return GNUNET_NETWORK_socket_check (cls);
813}
814
815
816/**
817 * Destroy this socket (free resources).
818 *
819 * @param cls the socket
820 */
821static void
822sock_destroy (void *cls)
823{
824 GNUNET_NETWORK_socket_destroy (cls);
825}
826
827
828/**
829 * Add a TCP socket-based connection to the set of handles managed by
830 * this server. Use this function for outgoing (P2P) connections that
831 * we initiated (and where this server should process incoming
832 * messages).
833 *
834 * @param server the server to use
835 * @param connection the connection to manage (client must
836 * stop using this connection from now on)
837 * @return the client handle (client should call
838 * "client_drop" on the return value eventually)
839 */
840struct GNUNET_SERVER_Client *
841GNUNET_SERVER_connect_socket (struct
842 GNUNET_SERVER_Handle
843 *server,
844 struct GNUNET_NETWORK_SocketHandle *connection)
845{
846 struct GNUNET_SERVER_Client *client;
847
848 client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client));
849 client->client_closure = connection;
850 client->receive = &sock_receive;
851 client->receive_cancel = &sock_receive_cancel;
852 client->notify_transmit_ready = &sock_notify_transmit_ready;
853 client->notify_transmit_ready_cancel = &sock_notify_transmit_ready_cancel;
854 client->check = &sock_check;
855 client->destroy = &sock_destroy;
856 client->reference_count = 1;
857 add_client (server, client);
858 return client;
859}
860
861
862/**
863 * Add an arbitrary connection to the set of handles managed by this
864 * server. This can be used if a sending and receiving does not
865 * really go over the network (internal transmission) or for servers
866 * using UDP.
867 *
868 * @param server the server to use
869 * @param chandle opaque handle for the connection
870 * @param creceive receive function for the connection
871 * @param ccancel cancel receive function for the connection
872 * @param cnotify transmit notification function for the connection
873 * @param cnotify_cancel transmit notification cancellation function for the connection
874 * @param ccheck function to test if the connection is still up
875 * @param cdestroy function to close and free the connection
876 * @return the client handle (client should call
877 * "client_drop" on the return value eventually)
878 */
879struct GNUNET_SERVER_Client *
880GNUNET_SERVER_connect_callback (struct
881 GNUNET_SERVER_Handle
882 *server,
883 void *chandle,
884 GNUNET_SERVER_ReceiveCallback
885 creceive,
886 GNUNET_SERVER_ReceiveCancelCallback
887 ccancel,
888 GNUNET_SERVER_TransmitReadyCallback
889 cnotify,
890 GNUNET_SERVER_TransmitReadyCancelCallback
891 cnotify_cancel,
892 GNUNET_SERVER_CheckCallback
893 ccheck,
894 GNUNET_SERVER_DestroyCallback cdestroy)
895{
896 struct GNUNET_SERVER_Client *client;
897
898 client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client));
899 client->client_closure = chandle;
900 client->receive = creceive;
901 client->receive_cancel = ccancel;
902 client->notify_transmit_ready = cnotify;
903 client->notify_transmit_ready_cancel = cnotify_cancel;
904 client->check = ccheck;
905 client->destroy = cdestroy;
906 client->reference_count = 1;
907 add_client (server, client);
908 return client;
909}
910
911
912/**
913 * Notify the server that the given client handle should
914 * be kept (keeps the connection up if possible, increments
915 * the internal reference counter).
916 *
917 * @param client the client to keep
918 */
919void
920GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client)
921{
922 client->reference_count++;
923}
924
925
926/**
927 * Notify the server that the given client handle is no
928 * longer required. Decrements the reference counter. If
929 * that counter reaches zero an inactive connection maybe
930 * closed.
931 *
932 * @param client the client to drop
933 */
934void
935GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client)
936{
937 GNUNET_assert (client->reference_count > 0);
938 client->reference_count--;
939 if ((client->server == NULL) && (client->reference_count == 0))
940 shutdown_incoming_processing (client);
941}
942
943
944/**
945 * Obtain the network address of the other party.
946 *
947 * @param client the client to get the address for
948 * @param addr where to store the address
949 * @param addrlen where to store the length of the address
950 * @return GNUNET_OK on success
951 */
952int
953GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client,
954 void **addr, size_t * addrlen)
955{
956 if (client->receive != &sock_receive)
957 return GNUNET_SYSERR; /* not a network client */
958 return GNUNET_NETWORK_socket_get_address (client->client_closure,
959 addr, addrlen);
960}
961
962
963/**
964 * Ask the server to notify us whenever a client disconnects.
965 * This function is called whenever the actual network connection
966 * is closed; the reference count may be zero or larger than zero
967 * at this point.
968 *
969 * @param server the server manageing the clients
970 * @param callback function to call on disconnect
971 * @param callback_cls closure for callback
972 */
973void
974GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server,
975 GNUNET_SERVER_DisconnectCallback callback,
976 void *callback_cls)
977{
978 struct NotifyList *n;
979
980 n = GNUNET_malloc (sizeof (struct NotifyList));
981 n->callback = callback;
982 n->callback_cls = callback_cls;
983 n->next = server->disconnect_notify_list;
984 server->disconnect_notify_list = n;
985}
986
987
988/**
989 * Ask the server to disconnect from the given client.
990 * This is the same as returning GNUNET_SYSERR from a message
991 * handler, except that it allows dropping of a client even
992 * when not handling a message from that client.
993 *
994 * @param client the client to disconnect from
995 */
996void
997GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client)
998{
999 if (client->server == NULL)
1000 return; /* already disconnected */
1001 GNUNET_assert (client->my_receive != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK);
1002 client->receive_cancel (client->client_closure, client->my_receive);
1003 client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
1004 shutdown_incoming_processing (client);
1005}
1006
1007
1008/**
1009 * Notify us when the server has enough space to transmit
1010 * a message of the given size to the given client.
1011 *
1012 * @param server the server to use
1013 * @param client client to transmit message to
1014 * @param size requested amount of buffer space
1015 * @param timeout after how long should we give up (and call
1016 * notify with buf NULL and size 0)?
1017 * @param callback function to call when space is available
1018 * @param callback_cls closure for callback
1019 * @return non-NULL if the notify callback was queued; can be used
1020 * to cancel the request using
1021 * GNUNET_NETWORK_notify_transmit_ready_cancel.
1022 * NULL if we are already going to notify someone else (busy)
1023 */
1024struct GNUNET_NETWORK_TransmitHandle *
1025GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
1026 size_t size,
1027 struct GNUNET_TIME_Relative timeout,
1028 GNUNET_NETWORK_TransmitReadyNotify
1029 callback, void *callback_cls)
1030{
1031 return client->notify_transmit_ready (client->client_closure,
1032 size,
1033 timeout, callback, callback_cls);
1034}
1035
1036
1037/**
1038 * Resume receiving from this client, we are done processing the
1039 * current request. This function must be called from within each
1040 * GNUNET_SERVER_MessageCallback (or its respective continuations).
1041 *
1042 * @param client client we were processing a message of
1043 * @param success GNUNET_OK to keep the connection open and
1044 * continue to receive
1045 * GNUNET_SYSERR to close the connection (signal
1046 * serious error)
1047 */
1048void
1049GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success)
1050{
1051 char *sb;
1052
1053 if (client == NULL)
1054 return;
1055 GNUNET_assert (client->suspended > 0);
1056 client->suspended--;
1057 if (success != GNUNET_OK)
1058 client->shutdown_now = GNUNET_YES;
1059 if (client->suspended > 0)
1060 return;
1061 if (client->in_process_client_buffer == GNUNET_YES)
1062 return;
1063 if (client->side_buf_size > 0)
1064 {
1065 /* resume processing from side-buf */
1066 sb = client->side_buf;
1067 client->side_buf = NULL;
1068 /* this will also resume the receive job */
1069 if (GNUNET_YES != client->shutdown_now)
1070 process_incoming (client, sb, client->side_buf_size, NULL, 0, 0);
1071 else
1072 shutdown_incoming_processing (client);
1073 /* finally, free the side-buf */
1074 GNUNET_free (sb);
1075 return;
1076 }
1077 /* resume receive job */
1078 if (GNUNET_YES != client->shutdown_now)
1079 {
1080 GNUNET_SCHEDULER_add_continuation (client->server->sched,
1081 GNUNET_NO,
1082 &restart_processing,
1083 client,
1084 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1085 return;
1086 }
1087 shutdown_incoming_processing (client);
1088}
1089
1090
1091/* end of server.c */
diff --git a/src/util/server_tc.c b/src/util/server_tc.c
new file mode 100644
index 000000000..dc51e1433
--- /dev/null
+++ b/src/util/server_tc.c
@@ -0,0 +1,198 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/server_tc.c
23 * @brief convenience functions for transmission of
24 * complex responses as a server
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_network_lib.h"
31#include "gnunet_scheduler_lib.h"
32#include "gnunet_server_lib.h"
33#include "gnunet_time_lib.h"
34
35
36
37/**
38 * How much buffer space do we want to have at least
39 * before transmitting another increment?
40 */
41#define MIN_BLOCK_SIZE 128
42
43
44
45struct GNUNET_SERVER_TransmitContext
46{
47 /**
48 * Which client are we transmitting to?
49 */
50 struct GNUNET_SERVER_Client *client;
51
52 /**
53 * Transmission buffer. (current offset for writing).
54 */
55 char *buf;
56
57 /**
58 * Number of bytes in buf.
59 */
60 size_t total;
61
62 /**
63 * Offset for writing in buf.
64 */
65 size_t off;
66
67 /**
68 * Timeout for this request.
69 */
70 struct GNUNET_TIME_Absolute timeout;
71};
72
73
74/**
75 * Helper function for incremental transmission of the response.
76 */
77static size_t
78transmit_response (void *cls, size_t size, void *buf)
79{
80 struct GNUNET_SERVER_TransmitContext *tc = cls;
81 size_t msize;
82 if (buf == NULL)
83 {
84 GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR);
85 GNUNET_free_non_null (tc->buf);
86 GNUNET_free (tc);
87 return 0;
88 }
89 if (tc->total - tc->off > size)
90 msize = size;
91 else
92 msize = tc->total - tc->off;
93 memcpy (buf, &tc->buf[tc->off], msize);
94 tc->off += msize;
95 if (tc->total == tc->off)
96 {
97 GNUNET_SERVER_receive_done (tc->client, GNUNET_OK);
98 GNUNET_free_non_null (tc->buf);
99 GNUNET_free (tc);
100 }
101 else
102 {
103 if (NULL == GNUNET_SERVER_notify_transmit_ready (tc->client,
104 GNUNET_MIN
105 (MIN_BLOCK_SIZE,
106 tc->total - tc->off),
107 GNUNET_TIME_absolute_get_remaining
108 (tc->timeout),
109 &transmit_response,
110 tc))
111 {
112 GNUNET_break (0);
113 GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR);
114 GNUNET_free_non_null (tc->buf);
115 GNUNET_free (tc);
116 }
117 }
118 return msize;
119}
120
121
122/**
123 * Create a new transmission context for the
124 * given client.
125 *
126 * @param client client to create the context for.
127 * @return NULL on error
128 */
129struct GNUNET_SERVER_TransmitContext *
130GNUNET_SERVER_transmit_context_create (struct GNUNET_SERVER_Client *client)
131{
132 struct GNUNET_SERVER_TransmitContext *tc;
133
134 GNUNET_assert (client != NULL);
135 tc = GNUNET_malloc (sizeof (struct GNUNET_SERVER_TransmitContext));
136 tc->client = client;
137 return tc;
138}
139
140
141/**
142 * Append a message to the transmission context.
143 * All messages in the context will be sent by
144 * the transmit_context_run method.
145 *
146 * @param tc context to use
147 * @param data what to append to the result message
148 * @param length length of data
149 * @param type type of the message
150 */
151void
152GNUNET_SERVER_transmit_context_append (struct GNUNET_SERVER_TransmitContext
153 *tc, const void *data, size_t length,
154 uint16_t type)
155{
156 struct GNUNET_MessageHeader *msg;
157 size_t size;
158
159 GNUNET_assert (length < GNUNET_SERVER_MAX_MESSAGE_SIZE);
160 size = length + sizeof (struct GNUNET_MessageHeader);
161 GNUNET_assert (size > length);
162 tc->buf = GNUNET_realloc (tc->buf, tc->total + size);
163 msg = (struct GNUNET_MessageHeader *) &tc->buf[tc->total];
164 tc->total += size;
165 msg->size = htons (size);
166 msg->type = htons (type);
167 memcpy (&msg[1], data, length);
168}
169
170
171/**
172 * Execute a transmission context. If there is
173 * an error in the transmission, the receive_done
174 * method will be called with an error code (GNUNET_SYSERR),
175 * otherwise with GNUNET_OK.
176 *
177 * @param tc transmission context to use
178 * @param timeout when to time out and abort the transmission
179 */
180void
181GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc,
182 struct GNUNET_TIME_Relative timeout)
183{
184 tc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
185 if (NULL ==
186 GNUNET_SERVER_notify_transmit_ready (tc->client,
187 GNUNET_MIN (MIN_BLOCK_SIZE,
188 tc->total), timeout,
189 &transmit_response, tc))
190 {
191 GNUNET_break (0);
192 GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR);
193 GNUNET_free_non_null (tc->buf);
194 GNUNET_free (tc);
195 }
196}
197
198/* end of server_tc.c */
diff --git a/src/util/service.c b/src/util/service.c
new file mode 100644
index 000000000..af71db692
--- /dev/null
+++ b/src/util/service.c
@@ -0,0 +1,1451 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/service.c
23 * @brief functions related to starting services
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_configuration_lib.h"
29#include "gnunet_crypto_lib.h"
30#include "gnunet_directories.h"
31#include "gnunet_disk_lib.h"
32#include "gnunet_getopt_lib.h"
33#include "gnunet_os_lib.h"
34#include "gnunet_protocols.h"
35#include "gnunet_server_lib.h"
36#include "gnunet_service_lib.h"
37
38/* ******************* access control ******************** */
39
40/**
41 * @brief IPV4 network in CIDR notation.
42 */
43struct IPv4NetworkSet
44{
45 struct in_addr network;
46 struct in_addr netmask;
47};
48
49/**
50 * @brief network in CIDR notation for IPV6.
51 */
52struct IPv6NetworkSet
53{
54 struct in6_addr network;
55 struct in6_addr netmask;
56};
57
58
59/**
60 * Parse a network specification. The argument specifies
61 * a list of networks. The format is
62 * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated
63 * with a semicolon). The network must be given in dotted-decimal
64 * notation. The netmask can be given in CIDR notation (/16) or
65 * in dotted-decimal (/255.255.0.0).
66 * <p>
67 * @param routeList a string specifying the forbidden networks
68 * @return the converted list, NULL if the synatx is flawed
69 */
70static struct IPv4NetworkSet *
71parse_ipv4_specification (const char *routeList)
72{
73 unsigned int count;
74 unsigned int i;
75 unsigned int j;
76 unsigned int len;
77 int cnt;
78 unsigned int pos;
79 unsigned int temps[8];
80 int slash;
81 struct IPv4NetworkSet *result;
82
83 if (routeList == NULL)
84 return NULL;
85 len = strlen (routeList);
86 if (len == 0)
87 return NULL;
88 count = 0;
89 for (i = 0; i < len; i++)
90 if (routeList[i] == ';')
91 count++;
92 result = GNUNET_malloc (sizeof (struct IPv4NetworkSet) * (count + 1));
93 /* add termination */
94 memset (result, 0, sizeof (struct IPv4NetworkSet) * (count + 1));
95 i = 0;
96 pos = 0;
97 while (i < count)
98 {
99 cnt = sscanf (&routeList[pos],
100 "%u.%u.%u.%u/%u.%u.%u.%u;",
101 &temps[0],
102 &temps[1],
103 &temps[2],
104 &temps[3], &temps[4], &temps[5], &temps[6], &temps[7]);
105 if (cnt == 8)
106 {
107 for (j = 0; j < 8; j++)
108 if (temps[j] > 0xFF)
109 {
110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
111 _("Invalid format for IP: `%s'\n"),
112 &routeList[pos]);
113 GNUNET_free (result);
114 return NULL;
115 }
116 result[i].network.s_addr
117 =
118 htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
119 temps[3]);
120 result[i].netmask.s_addr =
121 htonl ((temps[4] << 24) + (temps[5] << 16) + (temps[6] << 8) +
122 temps[7]);
123 while (routeList[pos] != ';')
124 pos++;
125 pos++;
126 i++;
127 continue;
128 }
129 /* try second notation */
130 cnt = sscanf (&routeList[pos],
131 "%u.%u.%u.%u/%u;",
132 &temps[0], &temps[1], &temps[2], &temps[3], &slash);
133 if (cnt == 5)
134 {
135 for (j = 0; j < 4; j++)
136 if (temps[j] > 0xFF)
137 {
138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
139 _("Invalid format for IP: `%s'\n"),
140 &routeList[pos]);
141 GNUNET_free (result);
142 return NULL;
143 }
144 result[i].network.s_addr
145 =
146 htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
147 temps[3]);
148 if ((slash <= 32) && (slash >= 0))
149 {
150 result[i].netmask.s_addr = 0;
151 while (slash > 0)
152 {
153 result[i].netmask.s_addr
154 = (result[i].netmask.s_addr >> 1) + 0x80000000;
155 slash--;
156 }
157 result[i].netmask.s_addr = htonl (result[i].netmask.s_addr);
158 while (routeList[pos] != ';')
159 pos++;
160 pos++;
161 i++;
162 continue;
163 }
164 else
165 {
166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
167 _
168 ("Invalid network notation ('/%d' is not legal in IPv4 CIDR)."),
169 slash);
170 GNUNET_free (result);
171 return NULL; /* error */
172 }
173 }
174 /* try third notation */
175 slash = 32;
176 cnt = sscanf (&routeList[pos],
177 "%u.%u.%u.%u;",
178 &temps[0], &temps[1], &temps[2], &temps[3]);
179 if (cnt == 4)
180 {
181 for (j = 0; j < 4; j++)
182 if (temps[j] > 0xFF)
183 {
184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
185 _("Invalid format for IP: `%s'\n"),
186 &routeList[pos]);
187 GNUNET_free (result);
188 return NULL;
189 }
190 result[i].network.s_addr
191 =
192 htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
193 temps[3]);
194 result[i].netmask.s_addr = 0;
195 while (slash > 0)
196 {
197 result[i].netmask.s_addr
198 = (result[i].netmask.s_addr >> 1) + 0x80000000;
199 slash--;
200 }
201 result[i].netmask.s_addr = htonl (result[i].netmask.s_addr);
202 while (routeList[pos] != ';')
203 pos++;
204 pos++;
205 i++;
206 continue;
207 }
208 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
209 _("Invalid format for IP: `%s'\n"), &routeList[pos]);
210 GNUNET_free (result);
211 return NULL; /* error */
212 }
213 if (pos < strlen (routeList))
214 {
215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216 _("Invalid format for IP: `%s'\n"), &routeList[pos]);
217 GNUNET_free (result);
218 return NULL; /* oops */
219 }
220 return result; /* ok */
221}
222
223
224/**
225 * Parse a network specification. The argument specifies
226 * a list of networks. The format is
227 * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated
228 * with a semicolon). The network must be given in colon-hex
229 * notation. The netmask must be given in CIDR notation (/16) or
230 * can be omitted to specify a single host.
231 * <p>
232 * @param routeList a string specifying the forbidden networks
233 * @return the converted list, NULL if the synatx is flawed
234 */
235static struct IPv6NetworkSet *
236parse_ipv6_specification (const char *routeListX)
237{
238 unsigned int count;
239 unsigned int i;
240 unsigned int len;
241 unsigned int pos;
242 int start;
243 int slash;
244 int ret;
245 char *routeList;
246 struct IPv6NetworkSet *result;
247 unsigned int bits;
248 unsigned int off;
249 int save;
250
251 if (routeListX == NULL)
252 return NULL;
253 len = strlen (routeListX);
254 if (len == 0)
255 return NULL;
256 routeList = GNUNET_strdup (routeListX);
257 count = 0;
258 for (i = 0; i < len; i++)
259 if (routeList[i] == ';')
260 count++;
261 if (routeList[len - 1] != ';')
262 {
263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
264 _
265 ("Invalid network notation (does not end with ';': `%s')\n"),
266 routeList);
267 GNUNET_free (routeList);
268 return NULL;
269 }
270
271 result = GNUNET_malloc (sizeof (struct IPv6NetworkSet) * (count + 1));
272 memset (result, 0, sizeof (struct IPv6NetworkSet) * (count + 1));
273 i = 0;
274 pos = 0;
275 while (i < count)
276 {
277 start = pos;
278 while (routeList[pos] != ';')
279 pos++;
280 slash = pos;
281 while ((slash >= start) && (routeList[slash] != '/'))
282 slash--;
283 if (slash < start)
284 {
285 memset (&result[i].netmask, 0xFF, sizeof (struct in6_addr));
286 slash = pos;
287 }
288 else
289 {
290 routeList[pos] = '\0';
291 ret = inet_pton (AF_INET6,
292 &routeList[slash + 1], &result[i].netmask);
293 if (ret <= 0)
294 {
295 save = errno;
296 if ((1 != SSCANF (&routeList[slash + 1],
297 "%u", &bits)) || (bits >= 128))
298 {
299 if (ret == 0)
300 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
301 _("Wrong format `%s' for netmask\n"),
302 &routeList[slash + 1]);
303 else
304 {
305 errno = save;
306 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
307 "inet_pton");
308 }
309 GNUNET_free (result);
310 GNUNET_free (routeList);
311 return NULL;
312 }
313 off = 0;
314 while (bits > 8)
315 {
316 result[i].netmask.s6_addr[off++] = 0xFF;
317 bits -= 8;
318 }
319 while (bits > 0)
320 {
321 result[i].netmask.s6_addr[off]
322 = (result[i].netmask.s6_addr[off] >> 1) + 0x80;
323 bits--;
324 }
325 }
326 }
327 routeList[slash] = '\0';
328 ret = inet_pton (AF_INET6, &routeList[start], &result[i].network);
329 if (ret <= 0)
330 {
331 if (ret == 0)
332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333 _("Wrong format `%s' for network\n"),
334 &routeList[slash + 1]);
335 else
336 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "inet_pton");
337 GNUNET_free (result);
338 GNUNET_free (routeList);
339 return NULL;
340 }
341 pos++;
342 i++;
343 }
344 GNUNET_free (routeList);
345 return result;
346}
347
348
349/**
350 * Check if the given IP address is in the list of IP addresses.
351 *
352 * @param list a list of networks
353 * @param ip the IP to check (in network byte order)
354 * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is
355 */
356static int
357check_ipv4_listed (const struct IPv4NetworkSet *list,
358 const struct in_addr *add)
359{
360 int i;
361
362 i = 0;
363 if (list == NULL)
364 return GNUNET_NO;
365
366 while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0))
367 {
368 if ((add->s_addr & list[i].netmask.s_addr) ==
369 (list[i].network.s_addr & list[i].netmask.s_addr))
370 return GNUNET_YES;
371 i++;
372 }
373 return GNUNET_NO;
374}
375
376/**
377 * Check if the given IP address is in the list of IP addresses.
378 *
379 * @param list a list of networks
380 * @param ip the IP to check (in network byte order)
381 * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is
382 */
383static int
384check_ipv6_listed (const struct IPv6NetworkSet *list,
385 const struct in6_addr *ip)
386{
387 unsigned int i;
388 unsigned int j;
389 struct in6_addr zero;
390
391 if (list == NULL)
392 return GNUNET_NO;
393
394 memset (&zero, 0, sizeof (struct in6_addr));
395 i = 0;
396NEXT:
397 while (memcmp (&zero, &list[i].network, sizeof (struct in6_addr)) != 0)
398 {
399 for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++)
400 if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) !=
401 (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j]))
402 {
403 i++;
404 goto NEXT;
405 }
406 return GNUNET_YES;
407 }
408 return GNUNET_NO;
409}
410
411
412/* ****************** service struct ****************** */
413
414
415/**
416 * Context for "service_task".
417 */
418struct GNUNET_SERVICE_Context
419{
420 /**
421 * Our configuration.
422 */
423 struct GNUNET_CONFIGURATION_Handle *cfg;
424
425 /**
426 * Handle for the server.
427 */
428 struct GNUNET_SERVER_Handle *server;
429
430 /**
431 * Scheduler for the server.
432 */
433 struct GNUNET_SCHEDULER_Handle *sched;
434
435 /**
436 * Address to bind to.
437 */
438 struct sockaddr *addr;
439
440 /**
441 * Name of our service.
442 */
443 const char *serviceName;
444
445 /**
446 * Main service-specific task to run.
447 */
448 GNUNET_SERVICE_Main task;
449
450 /**
451 * Closure for task.
452 */
453 void *task_cls;
454
455 /**
456 * IPv4 addresses that are not allowed to connect.
457 */
458 struct IPv4NetworkSet *v4_denied;
459
460 /**
461 * IPv6 addresses that are not allowed to connect.
462 */
463 struct IPv6NetworkSet *v6_denied;
464
465 /**
466 * IPv4 addresses that are allowed to connect (if not
467 * set, all are allowed).
468 */
469 struct IPv4NetworkSet *v4_allowed;
470
471 /**
472 * IPv6 addresses that are allowed to connect (if not
473 * set, all are allowed).
474 */
475 struct IPv6NetworkSet *v6_allowed;
476
477 /**
478 * My (default) message handlers. Adjusted copy
479 * of "defhandlers".
480 */
481 struct GNUNET_SERVER_MessageHandler *my_handlers;
482
483 /**
484 * Idle timeout for server.
485 */
486 struct GNUNET_TIME_Relative timeout;
487
488 /**
489 * Maximum buffer size for the server.
490 */
491 size_t maxbuf;
492
493 /**
494 * Overall success/failure of the service start.
495 */
496 int ret;
497
498 /**
499 * If we are daemonizing, this FD is set to the
500 * pipe to the parent. Send '.' if we started
501 * ok, '!' if not. -1 if we are not daemonizing.
502 */
503 int ready_confirm_fd;
504
505 /**
506 * Do we close connections if we receive messages
507 * for which we have no handler?
508 */
509 int require_found;
510
511 /**
512 * Can clients ask us to initiate a shutdown?
513 */
514 int allow_shutdown;
515
516 /**
517 * Length of addr.
518 */
519 socklen_t addrlen;
520
521};
522
523
524/* ****************** message handlers ****************** */
525
526static size_t
527write_test (void *cls, size_t size, void *buf)
528{
529 struct GNUNET_SERVER_Client *client = cls;
530 struct GNUNET_MessageHeader *msg;
531
532 if (size < sizeof (struct GNUNET_MessageHeader))
533 {
534 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
535 return 0; /* client disconnected */
536 }
537 msg = (struct GNUNET_MessageHeader *) buf;
538 msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
539 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
540 GNUNET_SERVER_receive_done (client, GNUNET_OK);
541 return sizeof (struct GNUNET_MessageHeader);
542}
543
544/**
545 * Handler for TEST message.
546 *
547 * @param cls closure (refers to service)
548 * @param server the server handling the message
549 * @param client identification of the client
550 * @param message the actual message
551 */
552static void
553handle_test (void *cls,
554 struct GNUNET_SERVER_Handle *server,
555 struct GNUNET_SERVER_Client *client,
556 const struct GNUNET_MessageHeader *message)
557{
558 /* simply bounce message back to acknowledge */
559 if (NULL == GNUNET_SERVER_notify_transmit_ready (client,
560 sizeof (struct
561 GNUNET_MessageHeader),
562 GNUNET_TIME_UNIT_FOREVER_REL,
563 &write_test, client))
564 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
565}
566
567
568/**
569 * Handler for SHUTDOWN message.
570 *
571 * @param cls closure (refers to service)
572 * @param server the server handling the message
573 * @param client identification of the client
574 * @param message the actual message
575 */
576static void
577handle_shutdown (void *cls,
578 struct GNUNET_SERVER_Handle *server,
579 struct GNUNET_SERVER_Client *client,
580 const struct GNUNET_MessageHeader *message)
581{
582 struct GNUNET_SERVICE_Context *service = cls;
583 if (!service->allow_shutdown)
584 {
585 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
586 _
587 ("Received shutdown request, but configured to ignore!\n"));
588 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
589 return;
590 }
591 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
592 _("Initiating shutdown as requested by client.\n"));
593 GNUNET_assert (service->sched != NULL);
594 GNUNET_SCHEDULER_shutdown (service->sched);
595 GNUNET_SERVER_receive_done (client, GNUNET_OK);
596}
597
598
599/**
600 * Default handlers for all services. Will be copied and the
601 * "callback_cls" fields will be replaced with the specific service
602 * struct.
603 */
604static const struct GNUNET_SERVER_MessageHandler defhandlers[] = {
605 {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST,
606 sizeof (struct GNUNET_MessageHeader)},
607 {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_SHUTDOWN,
608 sizeof (struct GNUNET_MessageHeader)},
609 {NULL, NULL, 0, 0}
610};
611
612
613
614/* ****************** service core routines ************** */
615
616
617/**
618 * Check if access to the service is allowed from the given address.
619 */
620static int
621check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen)
622{
623 struct GNUNET_SERVICE_Context *sctx = cls;
624 const struct sockaddr_in *i4;
625 const struct sockaddr_in6 *i6;
626 int ret;
627 char buf[INET6_ADDRSTRLEN];
628 uint16_t port;
629
630 switch (addr->sa_family)
631 {
632 case AF_INET:
633 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
634 i4 = (const struct sockaddr_in *) addr;
635 port = ntohs (i4->sin_port);
636 ret = ((sctx->v4_allowed == NULL) ||
637 (check_ipv4_listed (sctx->v4_allowed,
638 &i4->sin_addr)))
639 && ((sctx->v4_denied == NULL) ||
640 (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr)));
641 if (ret != GNUNET_OK)
642 inet_ntop (AF_INET, &i4->sin_addr, buf, sizeof (buf));
643 break;
644 case AF_INET6:
645 GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
646 i6 = (const struct sockaddr_in6 *) addr;
647 port = ntohs (i6->sin6_port);
648 ret = ((sctx->v6_allowed == NULL) ||
649 (check_ipv6_listed (sctx->v6_allowed,
650 &i6->sin6_addr)))
651 && ((sctx->v6_denied == NULL) ||
652 (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr)));
653 if (ret != GNUNET_OK)
654 inet_ntop (AF_INET6, &i6->sin6_addr, buf, sizeof (buf));
655 break;
656 default:
657 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
658 _("Unknown address family %d\n"), addr->sa_family);
659 return GNUNET_SYSERR;
660 }
661 if (ret != GNUNET_OK)
662 {
663 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
664 _("Access from `%s:%u' denied to service `%s'\n"),
665 buf, port, sctx->serviceName);
666 }
667 return ret;
668}
669
670
671/**
672 * Get the name of the file where we will
673 * write the PID of the service.
674 */
675static char *
676get_pid_file_name (struct GNUNET_SERVICE_Context *sctx)
677{
678
679 char *pif;
680
681 if (GNUNET_OK !=
682 GNUNET_CONFIGURATION_get_value_filename (sctx->cfg,
683 sctx->serviceName,
684 "PIDFILE", &pif))
685 return NULL;
686 return pif;
687}
688
689
690/**
691 * Parse an IPv4 access control list.
692 */
693static int
694process_acl4 (struct IPv4NetworkSet **ret,
695 struct GNUNET_SERVICE_Context *sctx, const char *option)
696{
697 char *opt;
698
699 if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option))
700 return GNUNET_OK;
701 GNUNET_break (GNUNET_OK ==
702 GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
703 sctx->serviceName,
704 option, &opt));
705 if (NULL == (*ret = parse_ipv4_specification (opt)))
706 {
707 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
708 _
709 ("Could not parse IPv4 network specification `%s' for `%s:%s'\n"),
710 opt, sctx->serviceName, option);
711 GNUNET_free (opt);
712 return GNUNET_SYSERR;
713 }
714 GNUNET_free (opt);
715 return GNUNET_OK;
716}
717
718
719/**
720 * Parse an IPv4 access control list.
721 */
722static int
723process_acl6 (struct IPv6NetworkSet **ret,
724 struct GNUNET_SERVICE_Context *sctx, const char *option)
725{
726 char *opt;
727 if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option))
728 return GNUNET_OK;
729 GNUNET_break (GNUNET_OK ==
730 GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
731 sctx->serviceName,
732 option, &opt));
733 if (NULL == (*ret = parse_ipv6_specification (opt)))
734 {
735 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
736 _
737 ("Could not parse IPv6 network specification `%s' for `%s:%s'\n"),
738 opt, sctx->serviceName, option);
739 GNUNET_free (opt);
740 return GNUNET_SYSERR;
741 }
742 GNUNET_free (opt);
743 return GNUNET_OK;
744}
745
746
747/**
748 * Setup addr, addrlen, maxbuf, idle_timeout
749 * based on configuration!
750 *
751 * Configuration must specify a "PORT". It may
752 * specify:
753 * - TIMEOUT (after how many ms does an inactive service timeout);
754 * - MAXBUF (maximum incoming message size supported)
755 * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack)
756 * - ALLOW_SHUTDOWN (allow clients to shutdown this service)
757 * - BINDTO (hostname or IP address to bind to, otherwise we take everything)
758 * - ACCEPT_FROM (only allow connections from specified IPv4 subnets)
759 * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets)
760 * - REJECT_FROM (disallow allow connections from specified IPv4 subnets)
761 * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets)
762 *
763 * @return GNUNET_OK if configuration succeeded
764 */
765static int
766setup_service (struct GNUNET_SERVICE_Context *sctx)
767{
768 unsigned long long maxbuf;
769 unsigned long long idleout;
770 char *hostname;
771 unsigned long long port;
772 int disablev6;
773 struct addrinfo hints;
774 struct addrinfo *res;
775 struct addrinfo *pos;
776 int ret;
777 int tolerant;
778
779 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
780 sctx->serviceName, "TIMEOUT"))
781 {
782 if (GNUNET_OK !=
783 GNUNET_CONFIGURATION_get_value_number (sctx->cfg,
784 sctx->serviceName,
785 "TIMEOUT", &idleout))
786 return GNUNET_SYSERR;
787
788 sctx->timeout.value = idleout;
789 }
790 else
791 sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
792 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
793 sctx->serviceName, "MAXBUF"))
794 {
795 if (GNUNET_OK !=
796 GNUNET_CONFIGURATION_get_value_number (sctx->cfg,
797 sctx->serviceName,
798 "MAXBUF", &maxbuf))
799 return GNUNET_SYSERR;
800 }
801 else
802 maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE;
803 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
804 sctx->serviceName, "DISABLEV6"))
805 {
806 if (GNUNET_SYSERR ==
807 (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg,
808 sctx->
809 serviceName,
810 "DISABLEV6")))
811 return GNUNET_SYSERR;
812 }
813 else
814 disablev6 = GNUNET_NO;
815 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
816 sctx->serviceName, "ALLOW_SHUTDOWN"))
817 {
818 if (GNUNET_SYSERR ==
819 (sctx->allow_shutdown =
820 GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName,
821 "ALLOW_SHUTDOWN")))
822 return GNUNET_SYSERR;
823 }
824 else
825 sctx->allow_shutdown = GNUNET_NO;
826
827 if (!disablev6)
828 {
829 /* probe IPv6 support */
830 ret = SOCKET (PF_INET6, SOCK_STREAM, 0);
831 if (ret == -1)
832 {
833 if ((errno == ENOBUFS) ||
834 (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES))
835 {
836 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
837 return GNUNET_SYSERR;
838 }
839 ret = SOCKET (PF_INET, SOCK_STREAM, 0);
840 if (ret != -1)
841 {
842 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
843 _
844 ("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"),
845 sctx->serviceName, strerror (errno));
846 disablev6 = GNUNET_YES;
847 }
848 }
849 if (ret != -1)
850 GNUNET_break (0 == CLOSE (ret));
851 }
852
853
854
855 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
856 sctx->serviceName, "TOLERANT"))
857 {
858 if (GNUNET_SYSERR ==
859 (tolerant = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg,
860 sctx->serviceName,
861 "TOLERANT")))
862 return GNUNET_SYSERR;
863 }
864 else
865 tolerant = GNUNET_NO;
866 sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES;
867
868
869 if ((GNUNET_OK !=
870 GNUNET_CONFIGURATION_get_value_number (sctx->cfg,
871 sctx->serviceName,
872 "PORT",
873 &port)) || (port > 65535))
874 {
875 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
876 _
877 ("Require valid port number for service `%s' in configuration!\n"),
878 sctx->serviceName);
879 return GNUNET_SYSERR;
880 }
881 if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
882 sctx->serviceName, "BINDTO"))
883 {
884 GNUNET_break (GNUNET_OK ==
885 GNUNET_CONFIGURATION_get_value_string (sctx->cfg,
886 sctx->serviceName,
887 "BINDTO",
888 &hostname));
889 }
890 else
891 hostname = NULL;
892
893 if (hostname != NULL)
894 {
895 memset (&hints, 0, sizeof (struct addrinfo));
896 if (disablev6)
897 hints.ai_family = AF_INET;
898 if ((0 != (ret = getaddrinfo (hostname,
899 NULL, &hints, &res))) || (res == NULL))
900 {
901 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
902 _("Failed to resolve `%s': %s\n"),
903 hostname, gai_strerror (ret));
904 GNUNET_free (hostname);
905 return GNUNET_SYSERR;
906 }
907 pos = res;
908 while ((NULL != pos) &&
909 (((disablev6) &&
910 (pos->ai_family != AF_INET)) ||
911 ((pos->ai_family != AF_INET) && (pos->ai_family != AF_INET6))))
912 pos = pos->ai_next;
913 if (pos == NULL)
914 {
915 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
916 _("Failed to find IPv4 address for `%s'.\n"), hostname);
917 freeaddrinfo (res);
918 GNUNET_free (hostname);
919 return GNUNET_SYSERR;
920 }
921 GNUNET_free (hostname);
922 if (pos->ai_family == AF_INET)
923 {
924 GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in));
925 sctx->addrlen = pos->ai_addrlen;
926 sctx->addr = GNUNET_malloc (sctx->addrlen);
927 memcpy (sctx->addr, res->ai_addr, sctx->addrlen);
928 ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port);
929 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
930 _
931 ("Configured to bind to %s address; %s connections to this service will fail!\n"),
932 "IPv4", "IPv6");
933 }
934 else
935 {
936 GNUNET_assert (pos->ai_family == AF_INET6);
937 GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6));
938 sctx->addrlen = pos->ai_addrlen;
939 sctx->addr = GNUNET_malloc (sctx->addrlen);
940 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
941 _
942 ("Configured to bind to %s address; %s connections to this service will fail!\n"),
943 "IPv6", "IPv4");
944 ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port);
945 }
946 freeaddrinfo (res);
947 }
948 else
949 {
950 /* will bind against everything, just set port */
951 if (disablev6)
952 {
953 /* V4-only */
954 sctx->addrlen = sizeof (struct sockaddr_in6);
955 sctx->addr = GNUNET_malloc (sctx->addrlen);
956 ((struct sockaddr_in *) sctx->addr)->sin_family = AF_INET;
957 ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port);
958 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
959 _
960 ("Configured to bind to %s address; %s connections to this service will fail!\n"),
961 "IPv4", "IPv6");
962 }
963 else
964 {
965 /* dual stack */
966 sctx->addrlen = sizeof (struct sockaddr_in6);
967 sctx->addr = GNUNET_malloc (sctx->addrlen);
968 ((struct sockaddr_in6 *) sctx->addr)->sin6_family = AF_INET6;
969 ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port);
970 }
971 }
972 sctx->maxbuf = (size_t) maxbuf;
973 if (sctx->maxbuf != maxbuf)
974 {
975 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
976 _
977 ("Value in configuration for `%s' and service `%s' too large!\n"),
978 "MAXBUF", sctx->serviceName);
979 return GNUNET_SYSERR;
980 }
981
982
983 if ((GNUNET_OK !=
984 process_acl4 (&sctx->v4_denied,
985 sctx,
986 "REJECT_FROM")) ||
987 (GNUNET_OK !=
988 process_acl4 (&sctx->v4_allowed,
989 sctx,
990 "ACCEPT_FROM")) ||
991 (GNUNET_OK !=
992 process_acl6 (&sctx->v6_denied,
993 sctx,
994 "REJECT_FROM6")) ||
995 (GNUNET_OK != process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6")))
996 return GNUNET_SYSERR;
997 return GNUNET_OK;
998}
999
1000
1001/**
1002 * Get the name of the user that'll be used
1003 * to provide the service.
1004 */
1005static char *
1006get_user_name (struct GNUNET_SERVICE_Context *sctx)
1007{
1008
1009 char *un;
1010
1011 if (GNUNET_OK !=
1012 GNUNET_CONFIGURATION_get_value_filename (sctx->cfg,
1013 sctx->serviceName,
1014 "USERNAME", &un))
1015 return NULL;
1016 return un;
1017}
1018
1019/**
1020 * Write PID file.
1021 */
1022static int
1023write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid)
1024{
1025 FILE *pidfd;
1026 char *pif;
1027 char *user;
1028 char *rdir;
1029 int len;
1030
1031 if (NULL == (pif = get_pid_file_name (sctx)))
1032 return GNUNET_OK; /* no file desired */
1033 user = get_user_name (sctx);
1034 rdir = GNUNET_strdup (pif);
1035 len = strlen (rdir);
1036 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
1037 len--;
1038 rdir[len] = '\0';
1039 if (0 != ACCESS (rdir, F_OK))
1040 {
1041 /* we get to create a directory -- and claim it
1042 as ours! */
1043 GNUNET_DISK_directory_create (rdir);
1044 if ((user != NULL) && (0 < strlen (user)))
1045 GNUNET_DISK_file_change_owner (rdir, user);
1046 }
1047 if (0 != ACCESS (rdir, W_OK | X_OK))
1048 {
1049 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "access", rdir);
1050 GNUNET_free (rdir);
1051 GNUNET_free_non_null (user);
1052 GNUNET_free (pif);
1053 return GNUNET_SYSERR;
1054 }
1055 GNUNET_free (rdir);
1056 pidfd = FOPEN (pif, "w");
1057 if (pidfd == NULL)
1058 {
1059 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", pif);
1060 GNUNET_free (pif);
1061 GNUNET_free_non_null (user);
1062 return GNUNET_SYSERR;
1063 }
1064 if (0 > FPRINTF (pidfd, "%u", pid))
1065 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif);
1066 GNUNET_break (0 == fclose (pidfd));
1067 if ((user != NULL) && (0 < strlen (user)))
1068 GNUNET_DISK_file_change_owner (pif, user);
1069 GNUNET_free_non_null (user);
1070 GNUNET_free (pif);
1071 return GNUNET_OK;
1072}
1073
1074
1075/**
1076 * Initial task for the service.
1077 */
1078static void
1079service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1080{
1081 struct GNUNET_SERVICE_Context *sctx = cls;
1082 unsigned int i;
1083
1084 sctx->sched = tc->sched;
1085 sctx->server = GNUNET_SERVER_create (tc->sched,
1086 &check_access,
1087 sctx,
1088 sctx->addr,
1089 sctx->addrlen,
1090 sctx->maxbuf,
1091 sctx->timeout, sctx->require_found);
1092 if (sctx->server == NULL)
1093 {
1094 sctx->ret = GNUNET_SYSERR;
1095 return;
1096 }
1097 sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers));
1098 memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers));
1099 i = 0;
1100 while ((sctx->my_handlers[i].callback != NULL))
1101 sctx->my_handlers[i++].callback_cls = sctx;
1102 GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers);
1103 if (sctx->ready_confirm_fd != -1)
1104 {
1105 GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1));
1106 GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd));
1107 sctx->ready_confirm_fd = -1;
1108 write_pid_file (sctx, getpid ());
1109 }
1110
1111 sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg);
1112}
1113
1114
1115/**
1116 * Detach from terminal.
1117 */
1118static int
1119detach_terminal (struct GNUNET_SERVICE_Context *sctx)
1120{
1121 pid_t pid;
1122 int nullfd;
1123 int filedes[2];
1124
1125#ifndef MINGW
1126 if (0 != PIPE (filedes))
1127 {
1128 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
1129 return GNUNET_SYSERR;
1130 }
1131 pid = fork ();
1132 if (pid < 0)
1133 {
1134 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
1135 return GNUNET_SYSERR;
1136 }
1137 if (pid != 0)
1138 {
1139 /* Parent */
1140 char c;
1141
1142 GNUNET_break (0 == CLOSE (filedes[1]));
1143 c = 'X';
1144 if (1 != READ (filedes[0], &c, sizeof (char)))
1145 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
1146 fflush (stdout);
1147 switch (c)
1148 {
1149 case '.':
1150 exit (0);
1151 case 'I':
1152 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1153 _("Service process failed to initialize\n"));
1154 break;
1155 case 'S':
1156 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1157 _
1158 ("Service process could not initialize server function\n"));
1159 break;
1160 case 'X':
1161 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1162 _("Service process failed to report status\n"));
1163 break;
1164 }
1165 exit (1); /* child reported error */
1166 }
1167 GNUNET_break (0 == CLOSE (0));
1168 GNUNET_break (0 == CLOSE (1));
1169 GNUNET_break (0 == CLOSE (filedes[0]));
1170 nullfd = GNUNET_DISK_file_open ("/dev/null", O_RDWR | O_APPEND);
1171 if (nullfd < 0)
1172 return GNUNET_SYSERR;
1173 /* set stdin/stdout to /dev/null */
1174 if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0))
1175 {
1176 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
1177 return GNUNET_SYSERR;
1178 }
1179 /* Detach from controlling terminal */
1180 pid = setsid ();
1181 if (pid == -1)
1182 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsid");
1183 sctx->ready_confirm_fd = filedes[1];
1184#else
1185 /* FIXME: we probably need to do something else
1186 elsewhere in order to fork the process itself... */
1187 FreeConsole ();
1188#endif
1189 return GNUNET_OK;
1190}
1191
1192
1193/**
1194 * Set user ID.
1195 */
1196static int
1197set_user_id (struct GNUNET_SERVICE_Context *sctx)
1198{
1199 char *user;
1200
1201 if (NULL == (user = get_user_name (sctx)))
1202 return GNUNET_OK; /* keep */
1203#ifndef MINGW
1204 struct passwd *pws;
1205
1206 errno = 0;
1207 pws = getpwnam (user);
1208 if (pws == NULL)
1209 {
1210 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1211 _("Cannot obtain information about user `%s': %s\n"),
1212 user, errno == 0 ? _("No such user") : STRERROR (errno));
1213 GNUNET_free (user);
1214 return GNUNET_SYSERR;
1215 }
1216 if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) ||
1217#if HAVE_INITGROUPS
1218 (0 != initgroups (user, pws->pw_gid)) ||
1219#endif
1220 (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid)))
1221 {
1222 if ((0 != setregid (pws->pw_gid, pws->pw_gid)) ||
1223 (0 != setreuid (pws->pw_uid, pws->pw_uid)))
1224 {
1225 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1226 _("Cannot change user/group to `%s': %s\n"), user,
1227 STRERROR (errno));
1228 GNUNET_free (user);
1229 return GNUNET_SYSERR;
1230 }
1231 }
1232#endif
1233 GNUNET_free (user);
1234 return GNUNET_OK;
1235}
1236
1237
1238/**
1239 * Delete the PID file that was created by our parent.
1240 */
1241static void
1242pid_file_delete (struct GNUNET_SERVICE_Context *sctx)
1243{
1244 char *pif = get_pid_file_name (sctx);
1245 if (pif == NULL)
1246 return; /* no PID file */
1247 if (0 != UNLINK (pif))
1248 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", pif);
1249 GNUNET_free (pif);
1250}
1251
1252/**
1253 * Run a standard GNUnet service startup sequence (initialize loggers
1254 * and configuration, parse options).
1255 *
1256 * @param argc number of command line arguments
1257 * @param argv command line arguments
1258 * @param serviceName our service name
1259 * @param task main task of the service
1260 * @param task_cls closure for task
1261 * @param term termination task of the service
1262 * @param term_cls closure for term
1263 * @return GNUNET_SYSERR on error, GNUNET_OK
1264 * if we shutdown nicely
1265 */
1266int
1267GNUNET_SERVICE_run (int argc,
1268 char *const *argv,
1269 const char *serviceName,
1270 GNUNET_SERVICE_Main task,
1271 void *task_cls, GNUNET_SERVICE_Term term, void *term_cls)
1272{
1273 char *cfg_fn;
1274 char *loglev;
1275 char *logfile;
1276 int do_daemonize;
1277 struct GNUNET_SERVICE_Context sctx;
1278 struct GNUNET_GETOPT_CommandLineOption service_options[] = {
1279 GNUNET_GETOPT_OPTION_CFG_FILE (&cfg_fn),
1280 {'d', "daemonize", NULL,
1281 gettext_noop ("do daemonize (detach from terminal)"), 0,
1282 GNUNET_GETOPT_set_one, &do_daemonize},
1283 GNUNET_GETOPT_OPTION_HELP (serviceName),
1284 GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev),
1285 GNUNET_GETOPT_OPTION_LOGFILE (&logfile),
1286 GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION),
1287 GNUNET_GETOPT_OPTION_END
1288 };
1289 do_daemonize = 0;
1290 logfile = NULL;
1291 loglev = GNUNET_strdup ("WARNING");
1292 cfg_fn = GNUNET_strdup (GNUNET_DEFAULT_DAEMON_CONFIG_FILE);
1293 memset (&sctx, 0, sizeof (sctx));
1294 sctx.ready_confirm_fd = -1;
1295 sctx.ret = GNUNET_OK;
1296 sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1297 sctx.maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE;
1298 sctx.task = task;
1299 sctx.serviceName = serviceName;
1300 sctx.cfg = GNUNET_CONFIGURATION_create ();
1301 /* setup subsystems */
1302 if ((GNUNET_SYSERR ==
1303 GNUNET_GETOPT_run (serviceName,
1304 sctx.cfg,
1305 service_options,
1306 argc,
1307 argv)) ||
1308 (GNUNET_OK !=
1309 GNUNET_log_setup (serviceName, loglev, logfile)) ||
1310 (GNUNET_OK !=
1311 GNUNET_CONFIGURATION_load (sctx.cfg, cfg_fn)) ||
1312 (GNUNET_OK !=
1313 setup_service (&sctx)) ||
1314 ((do_daemonize == 1) &&
1315 (GNUNET_OK != detach_terminal (&sctx))) ||
1316 (GNUNET_OK != set_user_id (&sctx)))
1317 {
1318 if (sctx.ready_confirm_fd != -1)
1319 {
1320 if (1 != WRITE (sctx.ready_confirm_fd, "I", 1))
1321 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1322 GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd));
1323 }
1324 GNUNET_CONFIGURATION_destroy (sctx.cfg);
1325 GNUNET_free_non_null (sctx.addr);
1326 GNUNET_free_non_null (logfile);
1327 GNUNET_free (loglev);
1328 GNUNET_free (cfg_fn);
1329 GNUNET_free_non_null (sctx.v4_denied);
1330 GNUNET_free_non_null (sctx.v6_denied);
1331 GNUNET_free_non_null (sctx.v4_allowed);
1332 GNUNET_free_non_null (sctx.v6_allowed);
1333 return GNUNET_SYSERR;
1334 }
1335
1336 /* actually run service */
1337 GNUNET_SCHEDULER_run (&service_task, &sctx);
1338 if (sctx.ready_confirm_fd != -1)
1339 {
1340 if (1 != WRITE (sctx.ready_confirm_fd, "S", 1))
1341 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1342 GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd));
1343 }
1344
1345 /* shutdown */
1346 if (term != NULL)
1347 term (term_cls, sctx.cfg);
1348 if ((do_daemonize == 1) && (sctx.server != NULL))
1349 pid_file_delete (&sctx);
1350 if (sctx.server != NULL)
1351 GNUNET_SERVER_destroy (sctx.server);
1352 GNUNET_free_non_null (sctx.my_handlers);
1353 GNUNET_CONFIGURATION_destroy (sctx.cfg);
1354 GNUNET_free_non_null (sctx.addr);
1355 GNUNET_free_non_null (logfile);
1356 GNUNET_free (loglev);
1357 GNUNET_free (cfg_fn);
1358 GNUNET_free_non_null (sctx.v4_denied);
1359 GNUNET_free_non_null (sctx.v6_denied);
1360 GNUNET_free_non_null (sctx.v4_allowed);
1361 GNUNET_free_non_null (sctx.v6_allowed);
1362 return sctx.ret;
1363}
1364
1365
1366/**
1367 * Run a service startup sequence within an existing
1368 * initialized system.
1369 *
1370 * @param serviceName our service name
1371 * @param sched scheduler to use
1372 * @param cfg configuration to use
1373 * @return NULL on error, service handle
1374 */
1375struct GNUNET_SERVICE_Context *
1376GNUNET_SERVICE_start (const char *serviceName,
1377 struct GNUNET_SCHEDULER_Handle *sched,
1378 struct GNUNET_CONFIGURATION_Handle *cfg)
1379{
1380 int i;
1381 struct GNUNET_SERVICE_Context *sctx;
1382
1383 sctx = GNUNET_malloc (sizeof (struct GNUNET_SERVICE_Context));
1384 sctx->ready_confirm_fd = -1; /* no daemonizing */
1385 sctx->ret = GNUNET_OK;
1386 sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1387 sctx->maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE;
1388 sctx->serviceName = serviceName;
1389 sctx->cfg = cfg;
1390 sctx->sched = sched;
1391
1392 /* setup subsystems */
1393 if ((GNUNET_OK != setup_service (sctx)) ||
1394 (NULL == (sctx->server = GNUNET_SERVER_create (sched,
1395 &check_access,
1396 sctx,
1397 sctx->addr,
1398 sctx->addrlen,
1399 sctx->maxbuf,
1400 sctx->timeout,
1401 sctx->require_found))))
1402 {
1403 GNUNET_SERVICE_stop (sctx);
1404 return NULL;
1405 }
1406 sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers));
1407 memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers));
1408 i = 0;
1409 while ((sctx->my_handlers[i].callback != NULL))
1410 sctx->my_handlers[i++].callback_cls = sctx;
1411 GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers);
1412
1413
1414 return sctx;
1415}
1416
1417/**
1418 * Obtain the server used by a service. Note that the server must NOT
1419 * be destroyed by the caller.
1420 *
1421 * @param ctx the service context returned from the start function
1422 * @return handle to the server for this service, NULL if there is none
1423 */
1424struct GNUNET_SERVER_Handle *
1425GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx)
1426{
1427 return ctx->server;
1428}
1429
1430
1431/**
1432 * Stop a service that was started with "GNUNET_SERVICE_start".
1433 *
1434 * @param ctx the service context returned from the start function
1435 */
1436void
1437GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx)
1438{
1439 if (NULL != sctx->server)
1440 GNUNET_SERVER_destroy (sctx->server);
1441 GNUNET_free_non_null (sctx->my_handlers);
1442 GNUNET_free_non_null (sctx->addr);
1443 GNUNET_free_non_null (sctx->v4_denied);
1444 GNUNET_free_non_null (sctx->v6_denied);
1445 GNUNET_free_non_null (sctx->v4_allowed);
1446 GNUNET_free_non_null (sctx->v6_allowed);
1447 GNUNET_free (sctx);
1448}
1449
1450
1451/* end of service.c */
diff --git a/src/util/signal.c b/src/util/signal.c
new file mode 100644
index 000000000..478551ca2
--- /dev/null
+++ b/src/util/signal.c
@@ -0,0 +1,76 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/signal.c
23 * @brief code for installing and uninstalling signal handlers
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_signal_lib.h"
30
31struct GNUNET_SIGNAL_Context
32{
33 int sig;
34
35 GNUNET_SIGNAL_Handler method;
36
37#ifndef MINGW
38 struct sigaction oldsig;
39#endif
40};
41
42struct GNUNET_SIGNAL_Context *
43GNUNET_SIGNAL_handler_install (int signal, GNUNET_SIGNAL_Handler handler)
44{
45 struct GNUNET_SIGNAL_Context *ret;
46#ifndef MINGW
47 struct sigaction sig;
48#endif
49
50 ret = GNUNET_malloc (sizeof (struct GNUNET_SIGNAL_Context));
51 ret->sig = signal;
52 ret->method = handler;
53#ifndef MINGW
54 sig.sa_handler = (void *) handler;
55 sigemptyset (&sig.sa_mask);
56#ifdef SA_INTERRUPT
57 sig.sa_flags = SA_INTERRUPT; /* SunOS */
58#else
59 sig.sa_flags = SA_RESTART;
60#endif
61 sigaction (signal, &sig, &ret->oldsig);
62#endif
63 return ret;
64}
65
66void
67GNUNET_SIGNAL_handler_uninstall (struct GNUNET_SIGNAL_Context *ctx)
68{
69#ifndef MINGW
70 struct sigaction sig;
71
72 sigemptyset (&sig.sa_mask);
73 sigaction (ctx->sig, &ctx->oldsig, &sig);
74#endif
75 GNUNET_free (ctx);
76}
diff --git a/src/util/strings.c b/src/util/strings.c
new file mode 100644
index 000000000..18f6582e8
--- /dev/null
+++ b/src/util/strings.c
@@ -0,0 +1,396 @@
1/*
2 This file is part of GNUnet.
3 (C) 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/strings.c
23 * @brief string functions
24 * @author Nils Durner
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#if HAVE_ICONV_H
30#include <iconv.h>
31#endif
32#include "gnunet_common.h"
33#include "gnunet_strings_lib.h"
34
35
36/**
37 * Fill a buffer of the given size with
38 * count 0-terminated strings (given as varargs).
39 * If "buffer" is NULL, only compute the amount of
40 * space required (sum of "strlen(arg)+1").
41 *
42 * Unlike using "snprintf" with "%s", this function
43 * will add 0-terminators after each string. The
44 * "GNUNET_string_buffer_tokenize" function can be
45 * used to parse the buffer back into individual
46 * strings.
47 *
48 * @return number of bytes written to the buffer
49 * (or number of bytes that would have been written)
50 */
51unsigned int
52GNUNET_STRINGS_buffer_fill (char *buffer,
53 unsigned int size, unsigned int count, ...)
54{
55 unsigned int needed;
56 unsigned int slen;
57 const char *s;
58 va_list ap;
59
60 needed = 0;
61 va_start (ap, count);
62 while (count > 0)
63 {
64 s = va_arg (ap, const char *);
65 slen = strlen (s) + 1;
66 if (buffer != NULL)
67 {
68 GNUNET_assert (needed + slen <= size);
69 memcpy (&buffer[needed], s, slen);
70 }
71 needed += slen;
72 count--;
73 }
74 va_end (ap);
75 return needed;
76}
77
78
79/**
80 * Given a buffer of a given size, find "count"
81 * 0-terminated strings in the buffer and assign
82 * the count (varargs) of type "const char**" to the
83 * locations of the respective strings in the
84 * buffer.
85 *
86 * @param buffer the buffer to parse
87 * @param size size of the buffer
88 * @param count number of strings to locate
89 * @return offset of the character after the last 0-termination
90 * in the buffer, or 0 on error.
91 */
92unsigned int
93GNUNET_STRINGS_buffer_tokenize (const char *buffer,
94 unsigned int size, unsigned int count, ...)
95{
96 unsigned int start;
97 unsigned int needed;
98 const char **r;
99 va_list ap;
100
101 needed = 0;
102 va_start (ap, count);
103 while (count > 0)
104 {
105 r = va_arg (ap, const char **);
106 start = needed;
107 while ((needed < size) && (buffer[needed] != '\0'))
108 needed++;
109 if (needed == size)
110 {
111 va_end (ap);
112 return 0; /* error */
113 }
114 *r = &buffer[start];
115 needed++; /* skip 0-termination */
116 count--;
117 }
118 va_end (ap);
119 return needed;
120}
121
122
123/**
124 * Convert a given filesize into a fancy human-readable format.
125 */
126char *
127GNUNET_STRINGS_byte_size_fancy (unsigned long long size)
128{
129 const char *unit = _( /* size unit */ "b");
130 char *ret;
131
132 if (size > 5 * 1024)
133 {
134 size = size / 1024;
135 unit = _( /* size unit */ "KiB");
136 if (size > 5 * 1024)
137 {
138 size = size / 1024;
139 unit = _( /* size unit */ "MiB");
140 if (size > 5 * 1024)
141 {
142 size = size / 1024;
143 unit = _( /* size unit */ "GiB");
144 if (size > 5 * 1024)
145 {
146 size = size / 1024;
147 unit = _( /* size unit */ "TiB");
148 }
149 }
150 }
151 }
152 ret = GNUNET_malloc (32);
153 GNUNET_snprintf (ret, 32, "%llu%s", size, unit);
154 return ret;
155}
156
157
158/**
159 * Convert the len characters long character sequence
160 * given in input that is in the given charset
161 * to UTF-8.
162 * @return the converted string (0-terminated),
163 * if conversion fails, a copy of the orignal
164 * string is returned.
165 */
166char *
167GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset)
168{
169 char *ret;
170#if ENABLE_NLS && HAVE_ICONV
171 size_t tmpSize;
172 size_t finSize;
173 char *tmp;
174 char *itmp;
175 iconv_t cd;
176
177 cd = iconv_open ("UTF-8", charset);
178 if (cd == (iconv_t) - 1)
179 {
180 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
181 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
182 _("Character set requested was `%s'\n"), charset);
183 ret = GNUNET_malloc (len + 1);
184 memcpy (ret, input, len);
185 ret[len] = '\0';
186 return ret;
187 }
188 tmpSize = 3 * len + 4;
189 tmp = GNUNET_malloc (tmpSize);
190 itmp = tmp;
191 finSize = tmpSize;
192 if (iconv (cd, (char **) &input, &len, &itmp, &finSize) == (size_t) - 1)
193 {
194 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv");
195 iconv_close (cd);
196 GNUNET_free (tmp);
197 ret = GNUNET_malloc (len + 1);
198 memcpy (ret, input, len);
199 ret[len] = '\0';
200 return ret;
201 }
202 ret = GNUNET_malloc (tmpSize - finSize + 1);
203 memcpy (ret, tmp, tmpSize - finSize);
204 ret[tmpSize - finSize] = '\0';
205 GNUNET_free (tmp);
206 if (0 != iconv_close (cd))
207 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_close");
208 return ret;
209#else
210 ret = GNUNET_malloc (len + 1);
211 memcpy (ret, input, len);
212 ret[len] = '\0';
213 return ret;
214#endif
215}
216
217
218/**
219 * Complete filename (a la shell) from abbrevition.
220 * @param fil the name of the file, may contain ~/ or
221 * be relative to the current directory
222 * @returns the full file name,
223 * NULL is returned on error
224 */
225char *
226GNUNET_STRINGS_filename_expand (const char *fil)
227{
228 char *buffer;
229#ifndef MINGW
230 size_t len;
231 size_t n;
232 char *fm;
233 const char *fil_ptr;
234#else
235 char *fn;
236 long lRet;
237#endif
238
239 if (fil == NULL)
240 return NULL;
241
242#ifndef MINGW
243 if (fil[0] == DIR_SEPARATOR)
244 /* absolute path, just copy */
245 return GNUNET_strdup (fil);
246 if (fil[0] == '~')
247 {
248 fm = getenv ("HOME");
249 if (fm == NULL)
250 {
251 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
252 _
253 ("Failed to expand `$HOME': environment variable `HOME' not set"));
254 return NULL;
255 }
256 fm = GNUNET_strdup (fm);
257 /* do not copy '~' */
258 fil_ptr = fil + 1;
259
260 /* skip over dir seperator to be consistent */
261 if (fil_ptr[0] == DIR_SEPARATOR)
262 fil_ptr++;
263 }
264 else
265 {
266 /* relative path */
267 fil_ptr = fil;
268 len = 512;
269 fm = NULL;
270 while (1)
271 {
272 buffer = GNUNET_malloc (len);
273 if (getcwd (buffer, len) != NULL)
274 {
275 fm = buffer;
276 break;
277 }
278 if ((errno == ERANGE) && (len < 1024 * 1024 * 4))
279 {
280 len *= 2;
281 GNUNET_free (buffer);
282 continue;
283 }
284 GNUNET_free (buffer);
285 break;
286 }
287 if (fm == NULL)
288 {
289 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getcwd");
290 buffer = getenv ("PWD"); /* alternative */
291 if (buffer != NULL)
292 fm = GNUNET_strdup (buffer);
293 }
294 if (fm == NULL)
295 fm = GNUNET_strdup ("./"); /* give up */
296 }
297 n = strlen (fm) + 1 + strlen (fil_ptr) + 1;
298 buffer = GNUNET_malloc (n);
299 GNUNET_snprintf (buffer, n, "%s%s%s",
300 fm,
301 (fm[strlen (fm) - 1] ==
302 DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
303 GNUNET_free (fm);
304 return buffer;
305#else
306 fn = GNUNET_malloc (MAX_PATH + 1);
307
308 if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
309 {
310 SetErrnoFromWinError (lRet);
311 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
312 "plibc_conv_to_win_path");
313 return NULL;
314 }
315 /* is the path relative? */
316 if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0))
317 {
318 char szCurDir[MAX_PATH + 1];
319 lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir);
320 if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
321 {
322 SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
323 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
324 "GetCurrentDirectory");
325 return NULL;
326 }
327 buffer = GNUNET_malloc (MAX_PATH + 1);
328 GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn);
329 GNUNET_free (fn);
330 fn = buffer;
331 }
332
333 return fn;
334#endif
335}
336
337
338/**
339 * Give relative time in human-readable fancy format.
340 * @param delta time in milli seconds
341 */
342char *
343GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative del)
344{
345 const char *unit = _( /* time unit */ "ms");
346 char *ret;
347 uint64_t delta = del.value;
348
349 if (delta > 5 * 1000)
350 {
351 delta = delta / 1000;
352 unit = _( /* time unit */ "s");
353 if (delta > 5 * 60)
354 {
355 delta = delta / 60;
356 unit = _( /* time unit */ "m");
357 if (delta > 5 * 60)
358 {
359 delta = delta / 60;
360 unit = _( /* time unit */ "h");
361 if (delta > 5 * 24)
362 {
363 delta = delta / 24;
364 unit = _( /* time unit */ " days");
365 }
366 }
367 }
368 }
369 GNUNET_asprintf (&ret, "%llu%s", delta, unit);
370 return ret;
371}
372
373
374/**
375 * "man ctime_r", except for GNUnet time; also, unlike ctime, the
376 * return value does not include the newline character.
377 */
378char *
379GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
380{
381 time_t tt;
382 char *ret;
383
384 tt = t.value / 1000;
385#ifdef ctime_r
386 ret = ctime_r (&tt, GNUNET_malloc (32));
387#else
388 ret = GNUNET_strdup (ctime (&tt));
389#endif
390 ret[strlen (ret) - 1] = '\0';
391 return ret;
392}
393
394
395
396/* end of strings.c */
diff --git a/src/util/test_client.c b/src/util/test_client.c
new file mode 100644
index 000000000..5c2e552e9
--- /dev/null
+++ b/src/util/test_client.c
@@ -0,0 +1,195 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_client.c
22 * @brief tests for client.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_client_lib.h"
27#include "gnunet_configuration_lib.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_server_lib.h"
30#include "gnunet_time_lib.h"
31
32#define VERBOSE GNUNET_NO
33
34#define PORT 14325
35
36#define MYNAME "test_client"
37
38static struct GNUNET_CLIENT_Connection *client;
39
40static struct GNUNET_SERVER_Handle *server;
41
42static struct GNUNET_CONFIGURATION_Handle *cfg;
43
44#define MY_TYPE 130
45
46struct CopyContext
47{
48 struct GNUNET_SERVER_Client *client;
49 struct GNUNET_MessageHeader *cpy;
50};
51
52static size_t
53copy_msg (void *cls, size_t size, void *buf)
54{
55 struct CopyContext *ctx = cls;
56 struct GNUNET_MessageHeader *cpy = ctx->cpy;
57
58 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (cpy->size));
59 GNUNET_assert (size >= ntohs (cpy->size));
60 memcpy (buf, cpy, ntohs (cpy->size));
61 GNUNET_SERVER_receive_done (ctx->client, GNUNET_OK);
62 GNUNET_free (cpy);
63 GNUNET_free (ctx);
64 return sizeof (struct GNUNET_MessageHeader);
65}
66
67
68/**
69 * Callback that just bounces the message back to the sender.
70 */
71static void
72echo_cb (void *cls,
73 struct GNUNET_SERVER_Handle *server,
74 struct GNUNET_SERVER_Client *client,
75 const struct GNUNET_MessageHeader *message)
76{
77 struct CopyContext *cc;
78 struct GNUNET_MessageHeader *cpy;
79
80 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) ==
81 ntohs (message->size));
82 cc = GNUNET_malloc (sizeof (struct CopyContext));
83 cc->client = client;
84 cpy = GNUNET_malloc (ntohs (message->size));
85 memcpy (cpy, message, ntohs (message->size));
86 cc->cpy = cpy;
87 GNUNET_assert (NULL !=
88 GNUNET_SERVER_notify_transmit_ready (client,
89 ntohs (message->size),
90 GNUNET_TIME_UNIT_SECONDS,
91 &copy_msg, cc));
92}
93
94
95static struct GNUNET_SERVER_MessageHandler handlers[] = {
96 {&echo_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
97 {NULL, NULL, 0, 0}
98};
99
100
101static void
102recv_bounce (void *cls, const struct GNUNET_MessageHeader *got)
103{
104 int *ok = cls;
105 struct GNUNET_MessageHeader msg;
106
107 GNUNET_assert (got != NULL); /* timeout */
108 msg.type = htons (MY_TYPE);
109 msg.size = htons (sizeof (msg));
110 GNUNET_assert (0 == memcmp (got, &msg, sizeof (msg)));
111 GNUNET_CLIENT_disconnect (client);
112 client = NULL;
113 GNUNET_SERVER_destroy (server);
114 server = NULL;
115 *ok = 0;
116}
117
118
119static size_t
120make_msg (void *cls, size_t size, void *buf)
121{
122 struct GNUNET_MessageHeader *msg = buf;
123 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
124 msg->type = htons (MY_TYPE);
125 msg->size = htons (sizeof (msg));
126 return sizeof (struct GNUNET_MessageHeader);
127}
128
129
130static void
131task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
132{
133 struct sockaddr_in sa;
134
135 memset (&sa, 0, sizeof (sa));
136 sa.sin_family = AF_INET;
137 sa.sin_port = htons (PORT);
138 server = GNUNET_SERVER_create (tc->sched,
139 NULL,
140 NULL,
141 (const struct sockaddr *) &sa,
142 sizeof (sa),
143 1024,
144 GNUNET_TIME_relative_multiply
145 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
146 GNUNET_NO);
147 GNUNET_assert (server != NULL);
148 handlers[0].callback_cls = cls;
149 handlers[1].callback_cls = cls;
150 GNUNET_SERVER_add_handlers (server, handlers);
151 client = GNUNET_CLIENT_connect (tc->sched, MYNAME, cfg);
152 GNUNET_assert (client != NULL);
153 GNUNET_assert (NULL !=
154 GNUNET_CLIENT_notify_transmit_ready (client,
155 sizeof (struct
156 GNUNET_MessageHeader),
157 GNUNET_TIME_UNIT_SECONDS,
158 &make_msg, NULL));
159 GNUNET_CLIENT_receive (client, &recv_bounce, cls,
160 GNUNET_TIME_relative_multiply
161 (GNUNET_TIME_UNIT_MILLISECONDS, 250));
162}
163
164
165/**
166 * Main method, starts scheduler with task1,
167 * checks that "ok" is correct at the end.
168 */
169static int
170check ()
171{
172 int ok;
173
174 cfg = GNUNET_CONFIGURATION_create ();
175 GNUNET_CONFIGURATION_set_value_number (cfg, MYNAME, "PORT", PORT);
176 GNUNET_CONFIGURATION_set_value_string (cfg,
177 MYNAME, "HOSTNAME", "localhost");
178 ok = 1;
179 GNUNET_SCHEDULER_run (&task, &ok);
180 GNUNET_CONFIGURATION_destroy (cfg);
181 return ok;
182}
183
184int
185main (int argc, char *argv[])
186{
187 int ret = 0;
188
189 GNUNET_log_setup ("test_client", "WARNING", NULL);
190 ret += check ();
191
192 return ret;
193}
194
195/* end of test_client.c */
diff --git a/src/util/test_common_allocation.c b/src/util/test_common_allocation.c
new file mode 100644
index 000000000..76c7dc8a3
--- /dev/null
+++ b/src/util/test_common_allocation.c
@@ -0,0 +1,112 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_common_allocation.c
23 * @brief testcase for common_allocation.c
24 */
25#include "platform.h"
26#include "gnunet_common.h"
27
28static int
29check ()
30{
31#define MAX_TESTVAL 1024
32 char *ptrs[MAX_TESTVAL];
33 int i;
34 int j;
35 int k;
36 unsigned int ui;
37
38 /* GNUNET_malloc/GNUNET_free test */
39 k = 352; /* random start value */
40 for (i = 1; i < MAX_TESTVAL; i++)
41 {
42 ptrs[i] = GNUNET_malloc (i);
43 for (j = 0; j < i; j++)
44 ptrs[i][j] = k++;
45 }
46
47 for (i = MAX_TESTVAL - 1; i >= 1; i--)
48 {
49 for (j = i - 1; j >= 0; j--)
50 if (ptrs[i][j] != (char) --k)
51 return 1;
52 GNUNET_free (ptrs[i]);
53 }
54
55 /* GNUNET_free_non_null test */
56 GNUNET_free_non_null (NULL);
57 GNUNET_free_non_null (GNUNET_malloc (4));
58
59 /* GNUNET_strdup tests */
60 ptrs[0] = GNUNET_strdup ("bar");
61 if (0 != strcmp (ptrs[0], "bar"))
62 return 3;
63 /* now realloc */
64 ptrs[0] = GNUNET_realloc (ptrs[0], 12);
65 strcpy (ptrs[0], "Hello World");
66
67 GNUNET_free (ptrs[0]);
68 GNUNET_asprintf (&ptrs[0], "%s %s", "Hello", "World");
69 GNUNET_assert (strlen (ptrs[0]) == 11);
70 GNUNET_free (ptrs[0]);
71
72 /* GNUNET_array_grow tests */
73 ptrs[0] = NULL;
74 ui = 0;
75 GNUNET_array_grow (ptrs[0], ui, 42);
76 if (ui != 42)
77 return 4;
78 GNUNET_array_grow (ptrs[0], ui, 22);
79 if (ui != 22)
80 return 5;
81 for (j = 0; j < 22; j++)
82 ptrs[0][j] = j;
83 GNUNET_array_grow (ptrs[0], ui, 32);
84 for (j = 0; j < 22; j++)
85 if (ptrs[0][j] != j)
86 return 6;
87 for (j = 22; j < 32; j++)
88 if (ptrs[0][j] != 0)
89 return 7;
90 GNUNET_array_grow (ptrs[0], ui, 0);
91 if (i != 0)
92 return 8;
93 if (ptrs[0] != NULL)
94 return 9;
95
96
97 return 0;
98}
99
100int
101main (int argc, char *argv[])
102{
103 int ret;
104
105 GNUNET_log_setup ("test-common-allocation", "WARNING", NULL);
106 ret = check ();
107 if (ret != 0)
108 fprintf (stderr, "ERROR %d.\n", ret);
109 return ret;
110}
111
112/* end of test_common_allocation.c */
diff --git a/src/util/test_common_endian.c b/src/util/test_common_endian.c
new file mode 100644
index 000000000..43f163902
--- /dev/null
+++ b/src/util/test_common_endian.c
@@ -0,0 +1,42 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_common_endian.c
22 * @brief testcase for common_endian.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26
27#define CHECK(n) if (n != GNUNET_htonll(GNUNET_ntohll(n))) return 1;
28
29int
30main (int argc, char *argv[])
31{
32 GNUNET_log_setup ("test-common-endian", "WARNING", NULL);
33 CHECK (1);
34 CHECK (0x12345678);
35 CHECK (123456789012345LL);
36 if ((0x1234567890ABCDEFLL !=
37 GNUNET_htonll (0xEFCDAB9078563412LL)) && 42 != htonl (42))
38 return 1;
39 return 0;
40}
41
42/* end of test_common_endian.c */
diff --git a/src/util/test_common_logging.c b/src/util/test_common_logging.c
new file mode 100644
index 000000000..eb337aac4
--- /dev/null
+++ b/src/util/test_common_logging.c
@@ -0,0 +1,100 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_common_logging.c
23 * @brief testcase for the logging module
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28
29static void
30my_log (void *ctx, enum GNUNET_ErrorType kind,
31 const char *component, const char *date, const char *msg)
32{
33 unsigned int *c = ctx;
34 (*c)++;
35}
36
37
38
39int
40main (int argc, char *argv[])
41{
42 unsigned int failureCount = 0;
43 unsigned int logs = 0;
44
45 fclose (stderr);
46 stderr = NULL;
47 GNUNET_logger_add (&my_log, &logs);
48 GNUNET_logger_add (&my_log, &logs);
49 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
50 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
51 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
52 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
53 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
54 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
55 GNUNET_logger_remove (&my_log, &logs);
56 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Flusher...\n");
57 /* the last 6 calls should be merged (repated bulk messages!) */
58 GNUNET_logger_remove (&my_log, &logs);
59 if (logs != 4)
60 {
61 fprintf (stdout, "Expected 4 log calls, got %u\n", logs);
62 failureCount++;
63 }
64 GNUNET_break (0 ==
65 strcmp (_("ERROR"),
66 GNUNET_error_type_to_string
67 (GNUNET_ERROR_TYPE_ERROR)));
68 GNUNET_break (0 ==
69 strcmp (_("WARNING"),
70 GNUNET_error_type_to_string
71 (GNUNET_ERROR_TYPE_WARNING)));
72 GNUNET_break (0 ==
73 strcmp (_("INFO"),
74 GNUNET_error_type_to_string
75 (GNUNET_ERROR_TYPE_INFO)));
76 GNUNET_break (0 ==
77 strcmp (_("DEBUG"),
78 GNUNET_error_type_to_string
79 (GNUNET_ERROR_TYPE_DEBUG)));
80 GNUNET_log_setup ("test_common_logging", "WARNING", "/dev/null");
81 logs = 0;
82 GNUNET_logger_add (&my_log, &logs);
83 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Checker...\n");
84 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Drop me...\n");
85 GNUNET_logger_remove (&my_log, &logs);
86 if (logs != 1)
87 {
88 fprintf (stdout, "Expected 1 log call, got %u\n", logs);
89 failureCount++;
90 }
91
92 if (failureCount != 0)
93 {
94 fprintf (stdout, "%u TESTS FAILED!\n", failureCount);
95 return -1;
96 }
97 return 0;
98} /* end of main */
99
100/* end of test_common_logging.c */
diff --git a/src/util/test_configuration.c b/src/util/test_configuration.c
new file mode 100644
index 000000000..2f37c684c
--- /dev/null
+++ b/src/util/test_configuration.c
@@ -0,0 +1,229 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_configuration.c
22 * @brief Test that the configuration module works.
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_configuration_lib.h"
29
30static struct GNUNET_CONFIGURATION_Handle *cfg;
31
32static int
33testConfig ()
34{
35 char *c;
36 unsigned long long l;
37
38 if (GNUNET_OK !=
39 GNUNET_CONFIGURATION_get_value_string (cfg, "test", "b", &c))
40 return 1;
41 if (0 != strcmp ("b", c))
42 {
43 fprintf (stderr, "Got `%s'\n", c);
44 GNUNET_free (c);
45 return 2;
46 }
47 GNUNET_free (c);
48 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
49 "test", "five", &l))
50 return 3;
51 if (5 != l)
52 return 4;
53 GNUNET_CONFIGURATION_set_value_string (cfg, "more", "c", "YES");
54 if (GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno (cfg, "more", "c"))
55 return 5;
56 GNUNET_CONFIGURATION_set_value_number (cfg, "NUMBERS", "TEN", 10);
57 if (GNUNET_OK !=
58 GNUNET_CONFIGURATION_get_value_string (cfg, "NUMBERS", "TEN", &c))
59 return 6;
60 if (0 != strcmp (c, "10"))
61 {
62 GNUNET_free (c);
63 return 7;
64 }
65 GNUNET_free (c);
66
67 if (GNUNET_OK !=
68 GNUNET_CONFIGURATION_get_value_filename (cfg, "last", "test", &c))
69 return 8;
70 if (0 != strcmp (c, "/hello/world"))
71 {
72 GNUNET_free (c);
73 return 9;
74 }
75 GNUNET_free (c);
76
77 return 0;
78}
79
80static const char *want[] = {
81 "/Hello",
82 "/File Name",
83 "/World",
84 NULL,
85 NULL,
86};
87
88static int
89check (void *data, const char *fn)
90{
91 int *idx = data;
92
93 if (0 == strcmp (want[*idx], fn))
94 {
95 (*idx)++;
96 return GNUNET_OK;
97 }
98 return GNUNET_SYSERR;
99}
100
101static int
102testConfigFilenames ()
103{
104 int idx;
105
106 idx = 0;
107 if (3 != GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
108 "FILENAMES",
109 "test",
110 &check, &idx))
111 return 8;
112 if (idx != 3)
113 return 16;
114 if (GNUNET_OK !=
115 GNUNET_CONFIGURATION_remove_value_filename (cfg,
116 "FILENAMES",
117 "test", "/File Name"))
118 return 24;
119
120 if (GNUNET_NO !=
121 GNUNET_CONFIGURATION_remove_value_filename (cfg,
122 "FILENAMES",
123 "test", "/File Name"))
124 return 32;
125 if (GNUNET_NO !=
126 GNUNET_CONFIGURATION_remove_value_filename (cfg,
127 "FILENAMES",
128 "test", "Stuff"))
129 return 40;
130
131 if (GNUNET_NO !=
132 GNUNET_CONFIGURATION_append_value_filename (cfg,
133 "FILENAMES",
134 "test", "/Hello"))
135 return 48;
136 if (GNUNET_NO !=
137 GNUNET_CONFIGURATION_append_value_filename (cfg,
138 "FILENAMES",
139 "test", "/World"))
140 return 56;
141
142 if (GNUNET_YES !=
143 GNUNET_CONFIGURATION_append_value_filename (cfg,
144 "FILENAMES",
145 "test", "/File 1"))
146 return 64;
147
148 if (GNUNET_YES !=
149 GNUNET_CONFIGURATION_append_value_filename (cfg,
150 "FILENAMES",
151 "test", "/File 2"))
152 return 72;
153
154 idx = 0;
155 want[1] = "/World";
156 want[2] = "/File 1";
157 want[3] = "/File 2";
158 if (4 != GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
159 "FILENAMES",
160 "test",
161 &check, &idx))
162 return 80;
163 if (idx != 4)
164 return 88;
165 return 0;
166}
167
168int
169main (int argc, char *argv[])
170{
171 int failureCount = 0;
172 char *c;
173
174 GNUNET_log_setup ("test_configuration", "WARNING", NULL);
175 cfg = GNUNET_CONFIGURATION_create ();
176 GNUNET_assert (cfg != NULL);
177 if (GNUNET_OK !=
178 GNUNET_CONFIGURATION_parse (cfg, "test_configuration_data.conf"))
179 {
180 fprintf (stderr, "Failed to parse configuration file\n");
181 GNUNET_CONFIGURATION_destroy (cfg);
182 return 1;
183 }
184 failureCount += testConfig ();
185 failureCount += 2 * testConfigFilenames ();
186
187 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, "/tmp/gnunet-test.conf"))
188 {
189 fprintf (stderr, "Failed to write configuration file\n");
190 GNUNET_CONFIGURATION_destroy (cfg);
191 return 1;
192 }
193 GNUNET_CONFIGURATION_destroy (cfg);
194 GNUNET_assert (0 == UNLINK ("/tmp/gnunet-test.conf"));
195
196 cfg = GNUNET_CONFIGURATION_create ();
197 if (GNUNET_OK !=
198 GNUNET_CONFIGURATION_load (cfg, "test_configuration_data.conf"))
199 {
200 GNUNET_break (0);
201 GNUNET_CONFIGURATION_destroy (cfg);
202 return 1;
203 }
204 if ((GNUNET_OK !=
205 GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "WEAKRANDOM",
206 &c))
207 || (0 != strcmp (c, "YES")))
208 {
209 GNUNET_CONFIGURATION_destroy (cfg);
210 return 1;
211 }
212 GNUNET_free (c);
213 if ((GNUNET_OK !=
214 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME",
215 &c))
216 || (0 != strcmp (c, "/var/lib/gnunet/")))
217 {
218 GNUNET_CONFIGURATION_destroy (cfg);
219 return 1;
220 }
221 GNUNET_free (c);
222 GNUNET_CONFIGURATION_destroy (cfg);
223 if (failureCount != 0)
224 {
225 fprintf (stderr, "Test failed: %u\n", failureCount);
226 return 1;
227 }
228 return 0;
229}
diff --git a/src/util/test_configuration_data.conf b/src/util/test_configuration_data.conf
new file mode 100644
index 000000000..52b6b8220
--- /dev/null
+++ b/src/util/test_configuration_data.conf
@@ -0,0 +1,30 @@
1[PATHS]
2SUBST=/hello
3
4[GNUNET]
5SUBST=hello
6GNUNET_HOME=/tmp
7
8[test]
9a=a
10b=b
11five=5
12
13[more]
14c=c
15five=42
16
17
18[last]
19test = $SUBST/world
20boom = "1 2 3 testing"
21trailing = YES
22
23[FILENAMES]
24test = "/Hello /File\ Name /World"
25
26
27[TESTING]
28WEAKRANDOM = YES
29
30
diff --git a/src/util/test_container_bloomfilter.c b/src/util/test_container_bloomfilter.c
new file mode 100644
index 000000000..9beb11298
--- /dev/null
+++ b/src/util/test_container_bloomfilter.c
@@ -0,0 +1,246 @@
1/*
2 This file is part of GNUnet.
3 (C) 2004, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_container_bloomfilter.c
22 * @brief Testcase for the bloomfilter.
23 * @author Christian Grothoff
24 * @author Igor Wronsky
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30
31#define K 4
32#define SIZE 65536
33#define TESTFILE "/tmp/bloomtest.dat"
34
35/**
36 * Generate a random hashcode.
37 */
38static void
39nextHC (GNUNET_HashCode * hc)
40{
41 GNUNET_CRYPTO_hash_create_random (hc);
42}
43
44static int
45add_iterator (GNUNET_HashCode * next, void *arg)
46{
47 int *ret = arg;
48 GNUNET_HashCode pos;
49
50 if (0 == (*ret)--)
51 return GNUNET_NO;
52 nextHC (&pos);
53 *next = pos;
54 return GNUNET_YES;
55}
56
57int
58main (int argc, char *argv[])
59{
60 struct GNUNET_CONTAINER_BloomFilter *bf;
61 struct GNUNET_CONTAINER_BloomFilter *bfi;
62 GNUNET_HashCode tmp;
63 int i;
64 int ok1;
65 int ok2;
66 int falseok;
67 char buf[SIZE];
68 struct stat sbuf;
69
70 GNUNET_log_setup ("test-container-bloomfilter", "WARNING", NULL);
71 srand (1);
72 if (0 == stat (TESTFILE, &sbuf))
73 if (0 != UNLINK (TESTFILE))
74 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink", TESTFILE);
75 bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K);
76
77 for (i = 0; i < 200; i++)
78 {
79 nextHC (&tmp);
80 GNUNET_CONTAINER_bloomfilter_add (bf, &tmp);
81 }
82 srand (1);
83 ok1 = 0;
84 for (i = 0; i < 200; i++)
85 {
86 nextHC (&tmp);
87 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
88 ok1++;
89 }
90 if (ok1 != 200)
91 {
92 printf ("Got %d elements out of"
93 "200 expected after insertion.\n", ok1);
94 GNUNET_CONTAINER_bloomfilter_free (bf);
95 return -1;
96 }
97 if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_get_raw_data (bf, buf, SIZE))
98 {
99 GNUNET_CONTAINER_bloomfilter_free (bf);
100 return -1;
101 }
102
103 GNUNET_CONTAINER_bloomfilter_free (bf);
104
105 bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K);
106 GNUNET_assert (bf != NULL);
107 bfi = GNUNET_CONTAINER_bloomfilter_init (buf, SIZE, K);
108 GNUNET_assert (bfi != NULL);
109
110 srand (1);
111 ok1 = 0;
112 ok2 = 0;
113 for (i = 0; i < 200; i++)
114 {
115 nextHC (&tmp);
116 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
117 ok1++;
118 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
119 ok2++;
120 }
121 if (ok1 != 200)
122 {
123 printf ("Got %d elements out of 200 "
124 "expected after reloading.\n", ok1);
125 GNUNET_CONTAINER_bloomfilter_free (bf);
126 GNUNET_CONTAINER_bloomfilter_free (bfi);
127 return -1;
128 }
129
130 if (ok2 != 200)
131 {
132 printf ("Got %d elements out of 200 "
133 "expected after initialization.\n", ok2);
134 GNUNET_CONTAINER_bloomfilter_free (bf);
135 GNUNET_CONTAINER_bloomfilter_free (bfi);
136 return -1;
137 }
138
139 srand (1);
140 for (i = 0; i < 100; i++)
141 {
142 nextHC (&tmp);
143 GNUNET_CONTAINER_bloomfilter_remove (bf, &tmp);
144 GNUNET_CONTAINER_bloomfilter_remove (bfi, &tmp);
145 }
146
147 srand (1);
148
149 ok1 = 0;
150 ok2 = 0;
151 for (i = 0; i < 200; i++)
152 {
153 nextHC (&tmp);
154 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
155 ok1++;
156 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
157 ok2++;
158 }
159
160 if (ok1 != 100)
161 {
162 printf ("Expected 100 elements in loaded filter"
163 " after adding 200 and deleting 100, got %d\n", ok1);
164 GNUNET_CONTAINER_bloomfilter_free (bf);
165 GNUNET_CONTAINER_bloomfilter_free (bfi);
166 return -1;
167 }
168 if (ok2 != 200)
169 {
170 printf ("Expected 200 elements in initialized filter"
171 " after adding 200 and deleting 100 "
172 "(which should do nothing for a filter not backed by a file), got %d\n",
173 ok2);
174 GNUNET_CONTAINER_bloomfilter_free (bf);
175 GNUNET_CONTAINER_bloomfilter_free (bfi);
176 return -1;
177 }
178
179 srand (3);
180
181 GNUNET_CONTAINER_bloomfilter_clear (bf);
182 falseok = 0;
183 for (i = 0; i < 1000; i++)
184 {
185 nextHC (&tmp);
186 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
187 falseok++;
188 }
189 if (falseok > 0)
190 {
191 GNUNET_CONTAINER_bloomfilter_free (bf);
192 GNUNET_CONTAINER_bloomfilter_free (bfi);
193 return -1;
194 }
195
196 if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_or (bf, buf, SIZE))
197 {
198 GNUNET_CONTAINER_bloomfilter_free (bf);
199 GNUNET_CONTAINER_bloomfilter_free (bfi);
200 return -1;
201 }
202
203 srand (2);
204 i = 20;
205 GNUNET_CONTAINER_bloomfilter_resize (bfi, &add_iterator, &i, SIZE * 2, K);
206
207 srand (2);
208 i = 20;
209 GNUNET_CONTAINER_bloomfilter_resize (bf, &add_iterator, &i, SIZE * 2, K);
210 srand (2);
211
212 ok1 = 0;
213 ok2 = 0;
214 for (i = 0; i < 20; i++)
215 {
216 nextHC (&tmp);
217 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
218 ok1++;
219 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
220 ok2++;
221 }
222
223 if (ok1 != 20)
224 {
225 printf ("Expected 20 elements in resized file-backed filter"
226 " after adding 20, got %d\n", ok1);
227 GNUNET_CONTAINER_bloomfilter_free (bf);
228 GNUNET_CONTAINER_bloomfilter_free (bfi);
229 return -1;
230 }
231 if (ok2 != 20)
232 {
233 printf ("Expected 20 elements in resized filter"
234 " after adding 20, got %d\n", ok2);
235 GNUNET_CONTAINER_bloomfilter_free (bf);
236 GNUNET_CONTAINER_bloomfilter_free (bfi);
237 return -1;
238 }
239
240
241 GNUNET_CONTAINER_bloomfilter_free (bf);
242 GNUNET_CONTAINER_bloomfilter_free (bfi);
243
244 GNUNET_break (0 == UNLINK (TESTFILE));
245 return 0;
246}
diff --git a/src/util/test_container_heap.c b/src/util/test_container_heap.c
new file mode 100644
index 000000000..511589af5
--- /dev/null
+++ b/src/util/test_container_heap.c
@@ -0,0 +1,112 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @author Nathan Evans
23 * @file util/containers/heaptest.c
24 * @brief Test of heap operations
25 */
26
27#include "gnunet_util.h"
28#include "gnunet_util_containers.h"
29#include "dv.h"
30
31static int
32iterator_callback (void *element, GNUNET_CONTAINER_HeapCost cost,
33 struct GNUNET_CONTAINER_Heap *root, void *cls)
34{
35 struct GNUNET_dv_neighbor *node;
36 node = (struct GNUNET_dv_neighbor *) element;
37 fprintf (stdout, "%d\n", node->cost);
38 //fprintf (stdout, "%d\n", ((struct GNUNET_dv_neighbor *)element)->cost);
39
40 return GNUNET_OK;
41}
42
43
44int
45main (int argc, char **argv)
46{
47 struct GNUNET_CONTAINER_Heap *myHeap;
48 struct GNUNET_dv_neighbor *neighbor1;
49 struct GNUNET_dv_neighbor *neighbor2;
50 struct GNUNET_dv_neighbor *neighbor3;
51 struct GNUNET_dv_neighbor *neighbor4;
52 struct GNUNET_dv_neighbor *neighbor5;
53 struct GNUNET_dv_neighbor *neighbor6;
54
55 GNUNET_log_setup ("test-container-heap", "WARNING", NULL);
56
57 myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
58
59 neighbor1 = malloc (sizeof (struct GNUNET_dv_neighbor));
60 neighbor2 = malloc (sizeof (struct GNUNET_dv_neighbor));
61 neighbor3 = malloc (sizeof (struct GNUNET_dv_neighbor));
62 neighbor4 = malloc (sizeof (struct GNUNET_dv_neighbor));
63 neighbor5 = malloc (sizeof (struct GNUNET_dv_neighbor));
64 neighbor6 = malloc (sizeof (struct GNUNET_dv_neighbor));
65
66 neighbor1->cost = 60;
67 neighbor2->cost = 50;
68 neighbor3->cost = 70;
69 neighbor4->cost = 120;
70 neighbor5->cost = 100;
71 neighbor6->cost = 30;
72
73 GNUNET_CONTAINER_heap_insert (myHeap, neighbor1, neighbor1->cost);
74 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
75
76 fprintf (stdout, "\n");
77 GNUNET_CONTAINER_heap_insert (myHeap, neighbor2, neighbor2->cost);
78
79 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
80 fprintf (stdout, "\n");
81 GNUNET_CONTAINER_heap_insert (myHeap, neighbor3, neighbor3->cost);
82
83 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
84 fprintf (stdout, "\n");
85 GNUNET_CONTAINER_heap_insert (myHeap, neighbor4, neighbor4->cost);
86
87 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
88 fprintf (stdout, "\n");
89 GNUNET_CONTAINER_heap_insert (myHeap, neighbor5, neighbor5->cost);
90
91 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
92 fprintf (stdout, "\n");
93 GNUNET_CONTAINER_heap_insert (myHeap, neighbor6, neighbor6->cost);
94
95 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
96 fprintf (stdout, "\n");
97 GNUNET_CONTAINER_heap_remove_node (myHeap, neighbor5);
98
99 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
100 fprintf (stdout, "\n");
101 GNUNET_CONTAINER_heap_remove_root (myHeap);
102
103 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
104 fprintf (stdout, "\n");
105 GNUNET_CONTAINER_heap_update_cost (myHeap, neighbor6, 200);
106
107 GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL);
108 fprintf (stdout, "\n");
109 return 0;
110}
111
112/* end of heaptest.c */
diff --git a/src/util/test_container_meta_data.c b/src/util/test_container_meta_data.c
new file mode 100644
index 000000000..17ef79161
--- /dev/null
+++ b/src/util/test_container_meta_data.c
@@ -0,0 +1,262 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_container_meta_data.c
23 * @brief Test for container_meta_data.c
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30
31#define ABORT(m) { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); if (m != NULL) GNUNET_CONTAINER_meta_data_destroy(m); return 1; }
32
33static int
34testMeta (int i)
35{
36 struct GNUNET_CONTAINER_MetaData *m;
37 char *val;
38 int j;
39 unsigned int size;
40
41 m = GNUNET_CONTAINER_meta_data_create ();
42 if (GNUNET_OK !=
43 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_TITLE, "TestTitle"))
44 ABORT (m);
45 if (GNUNET_OK !=
46 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_AUTHOR, "TestTitle"))
47 ABORT (m);
48 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_TITLE, "TestTitle")) /* dup! */
49 ABORT (m);
50 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_AUTHOR, "TestTitle")) /* dup! */
51 ABORT (m);
52 if (2 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
53 ABORT (m);
54 if (GNUNET_OK !=
55 GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_AUTHOR, "TestTitle"))
56 ABORT (m);
57 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_AUTHOR, "TestTitle")) /* already gone */
58 ABORT (m);
59 if (1 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
60 ABORT (m);
61 if (GNUNET_OK !=
62 GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_TITLE, "TestTitle"))
63 ABORT (m);
64 if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_TITLE, "TestTitle")) /* already gone */
65 ABORT (m);
66 if (0 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
67 ABORT (m);
68 val = GNUNET_malloc (256);
69 for (j = 0; j < i; j++)
70 {
71 GNUNET_snprintf (val, 256, "%s.%d",
72 "A teststring that should compress well.", j);
73 if (GNUNET_OK !=
74 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_UNKNOWN, val))
75 {
76 GNUNET_free (val);
77 ABORT (m);
78 }
79 }
80 GNUNET_free (val);
81 if (i != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
82 ABORT (m);
83
84 size =
85 GNUNET_CONTAINER_meta_data_get_serialized_size (m,
86 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
87 val = GNUNET_malloc (size);
88 if (size != GNUNET_CONTAINER_meta_data_serialize (m, val, size,
89 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
90 {
91 GNUNET_free (val);
92 ABORT (m);
93 }
94 GNUNET_CONTAINER_meta_data_destroy (m);
95 m = GNUNET_CONTAINER_meta_data_deserialize (val, size);
96 GNUNET_free (val);
97 if (m == NULL)
98 ABORT (m);
99 val = GNUNET_malloc (256);
100 for (j = 0; j < i; j++)
101 {
102 GNUNET_snprintf (val, 256, "%s.%d",
103 "A teststring that should compress well.", j);
104 if (GNUNET_OK !=
105 GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_UNKNOWN, val))
106 {
107 GNUNET_free (val);
108 ABORT (m);
109 }
110 }
111 GNUNET_free (val);
112 if (0 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL))
113 {
114 ABORT (m);
115 }
116 GNUNET_CONTAINER_meta_data_destroy (m);
117 return 0;
118}
119
120int
121testMetaMore (int i)
122{
123 struct GNUNET_CONTAINER_MetaData *meta;
124 int q;
125 char txt[128];
126 char *data;
127 unsigned long long size;
128
129 meta = GNUNET_CONTAINER_meta_data_create ();
130 for (q = 0; q <= i; q++)
131 {
132 GNUNET_snprintf (txt, 128, "%u -- %u\n", i, q);
133 GNUNET_CONTAINER_meta_data_insert (meta,
134 q %
135 EXTRACTOR_getHighestKeywordTypeNumber
136 (), txt);
137 }
138 size =
139 GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
140 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
141 data = GNUNET_malloc (size * 4);
142 if (size != GNUNET_CONTAINER_meta_data_serialize (meta,
143 data, size * 4,
144 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
145 {
146 GNUNET_free (data);
147 ABORT (meta);
148 }
149 GNUNET_CONTAINER_meta_data_destroy (meta);
150 GNUNET_free (data);
151 return 0;
152}
153
154static int
155testMetaLink ()
156{
157 struct GNUNET_CONTAINER_MetaData *m;
158 char *val;
159 unsigned int size;
160
161 m = GNUNET_CONTAINER_meta_data_create ();
162 if (GNUNET_OK !=
163 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_UNKNOWN, "link"))
164 ABORT (m);
165 if (GNUNET_OK !=
166 GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_FILENAME,
167 "lib-link.m4"))
168 ABORT (m);
169 size =
170 GNUNET_CONTAINER_meta_data_get_serialized_size (m,
171 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
172 val = GNUNET_malloc (size);
173 if (size != GNUNET_CONTAINER_meta_data_serialize (m, val, size,
174 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
175 {
176 GNUNET_free (val);
177 ABORT (m);
178 }
179 GNUNET_CONTAINER_meta_data_destroy (m);
180 m = GNUNET_CONTAINER_meta_data_deserialize (val, size);
181 GNUNET_free (val);
182 if (m == NULL)
183 ABORT (m);
184 GNUNET_CONTAINER_meta_data_destroy (m);
185 return 0;
186}
187
188
189static int
190testThumbnail ()
191{
192 struct GNUNET_CONTAINER_MetaData *m;
193 struct GNUNET_CONTAINER_MetaData *d;
194 EXTRACTOR_ExtractorList *ex;
195 unsigned char *thumb;
196 size_t size;
197 char *date;
198
199 ex = EXTRACTOR_loadConfigLibraries (NULL, "libextractor_thumbnail");
200 if (ex == NULL)
201 {
202 fprintf (stderr,
203 "Test incomplete, have no thumbnail extractor available.\n");
204 return 0; /* can not test, no thumbnailer */
205 }
206 ex = EXTRACTOR_loadConfigLibraries (ex, "libextractor_mime");
207 m = GNUNET_CONTAINER_meta_data_create ();
208 if (3 != GNUNET_CONTAINER_meta_data_extract_from_file (m,
209 "test_container_meta_data_image.jpg",
210 ex))
211 {
212 GNUNET_break (0);
213 EXTRACTOR_removeAll (ex);
214 GNUNET_CONTAINER_meta_data_destroy (m);
215 return 1;
216 }
217 EXTRACTOR_removeAll (ex);
218 d = GNUNET_CONTAINER_meta_data_duplicate (m);
219 GNUNET_CONTAINER_meta_data_destroy (m);
220 size = GNUNET_CONTAINER_meta_data_get_thumbnail (d, &thumb);
221 if (size == 0)
222 {
223 GNUNET_break (0);
224 GNUNET_CONTAINER_meta_data_destroy (d);
225 return 1;
226 }
227 GNUNET_free (thumb);
228 GNUNET_CONTAINER_meta_data_add_publication_date (d);
229 date = GNUNET_CONTAINER_meta_data_get_by_type (d,
230 EXTRACTOR_PUBLICATION_DATE);
231 if (date == NULL)
232 {
233 GNUNET_break (0);
234 GNUNET_CONTAINER_meta_data_destroy (d);
235 return 1;
236 }
237 GNUNET_free (date);
238 GNUNET_CONTAINER_meta_data_destroy (d);
239 return 0;
240}
241
242
243int
244main (int argc, char *argv[])
245{
246 int failureCount = 0;
247 int i;
248
249 GNUNET_log_setup ("test-container-meta-data", "WARNING", NULL);
250 for (i = 0; i < 255; i++)
251 failureCount += testMeta (i);
252 for (i = 1; i < 255; i++)
253 failureCount += testMetaMore (i);
254 failureCount += testMetaLink ();
255 failureCount += testThumbnail ();
256
257 if (failureCount != 0)
258 return 1;
259 return 0;
260}
261
262/* end of metatest.c */
diff --git a/src/util/test_container_meta_data_image.jpg b/src/util/test_container_meta_data_image.jpg
new file mode 100644
index 000000000..3d1ba3307
--- /dev/null
+++ b/src/util/test_container_meta_data_image.jpg
Binary files differ
diff --git a/src/util/test_container_multihashmap.c b/src/util/test_container_multihashmap.c
new file mode 100644
index 000000000..e92ab49ba
--- /dev/null
+++ b/src/util/test_container_multihashmap.c
@@ -0,0 +1,113 @@
1/*
2 This file is part of GNUnet.
3 (C) 2008 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_container_multihashmap.c
23 * @brief Test for container_multihashmap.c
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30
31#define ABORT() { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); if (m != NULL) GNUNET_CONTAINER_multihashmap_destroy(m); return 1; }
32#define CHECK(c) { if (! (c)) ABORT(); }
33
34static int
35testMap (int i)
36{
37 struct GNUNET_CONTAINER_MultiHashMap *m;
38 GNUNET_HashCode k1;
39 GNUNET_HashCode k2;
40 int j;
41 void *r;
42
43 CHECK (NULL != (m = GNUNET_CONTAINER_multihashmap_create (i)));
44 memset (&k1, 0, sizeof (k1));
45 memset (&k2, 1, sizeof (k2));
46 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k1));
47 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2));
48 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k1, NULL));
49 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k2, NULL));
50 CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k1));
51 CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k2));
52 CHECK (0 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1));
53 CHECK (0 == GNUNET_CONTAINER_multihashmap_size (m));
54 CHECK (0 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL));
55 CHECK (0 ==
56 GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL));
57
58 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
59 &k1,
60 "v1",
61 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
62 CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m));
63 CHECK (0 == strcmp ("v1", GNUNET_CONTAINER_multihashmap_get (m, &k1)));
64 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_put (m,
65 &k1,
66 "v1",
67 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
68 CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m));
69 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
70 &k1,
71 "v2",
72 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
73 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
74 &k1,
75 "v3",
76 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
77 CHECK (3 == GNUNET_CONTAINER_multihashmap_size (m));
78 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (m, &k1, "v3"));
79 CHECK (2 == GNUNET_CONTAINER_multihashmap_size (m));
80 CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (m, &k1));
81 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2));
82 CHECK (2 ==
83 GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL));
84 CHECK (0 ==
85 GNUNET_CONTAINER_multihashmap_get_multiple (m, &k2, NULL, NULL));
86 CHECK (2 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL));
87 r = GNUNET_CONTAINER_multihashmap_get_random (m);
88 CHECK (0 == strcmp (r, "v1") || 0 == strcmp (r, "v2"));
89 CHECK (2 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1));
90 for (j = 0; j < 1024; j++)
91 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m,
92 &k1,
93 "v2",
94 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
95 GNUNET_CONTAINER_multihashmap_destroy (m);
96 return 0;
97}
98
99int
100main (int argc, char *argv[])
101{
102 int failureCount = 0;
103 int i;
104
105 GNUNET_log_setup ("test-container-multihashmap", "WARNING", NULL);
106 for (i = 1; i < 255; i++)
107 failureCount += testMap (i);
108 if (failureCount != 0)
109 return 1;
110 return 0;
111}
112
113/* end of maptest.c */
diff --git a/src/util/test_crypto_aes.c b/src/util/test_crypto_aes.c
new file mode 100644
index 000000000..cdae243e0
--- /dev/null
+++ b/src/util/test_crypto_aes.c
@@ -0,0 +1,180 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21/**
22 * @author Christian Grothoff
23 * @file util/test_crypto_aes.c
24 * @brief test for AES ciphers
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29
30#define TESTSTRING "Hello World!"
31#define INITVALUE "InitializationVectorValue"
32
33static int
34testSymcipher ()
35{
36 struct GNUNET_CRYPTO_AesSessionKey key;
37 char result[100];
38 int size;
39 char res[100];
40
41 GNUNET_CRYPTO_aes_create_session_key (&key);
42 size = GNUNET_CRYPTO_aes_encrypt (TESTSTRING,
43 strlen (TESTSTRING) + 1,
44 &key,
45 (const struct
46 GNUNET_CRYPTO_AesInitializationVector *)
47 INITVALUE, result);
48 if (size == -1)
49 {
50 printf ("symciphertest failed: encryptBlock returned %d\n", size);
51 return 1;
52 }
53 size = GNUNET_CRYPTO_aes_decrypt (&key,
54 result, size,
55 (const struct
56 GNUNET_CRYPTO_AesInitializationVector *)
57 INITVALUE, res);
58 if (strlen (TESTSTRING) + 1 != size)
59 {
60 printf ("symciphertest failed: decryptBlock returned %d\n", size);
61 return 1;
62 }
63 if (0 != strcmp (res, TESTSTRING))
64 {
65 printf ("symciphertest failed: %s != %s\n", res, TESTSTRING);
66 return 1;
67 }
68 else
69 return 0;
70}
71
72int
73verifyCrypto ()
74{
75 struct GNUNET_CRYPTO_AesSessionKey key;
76 char result[GNUNET_CRYPTO_AES_KEY_LENGTH];
77 char *res;
78 int ret;
79
80 unsigned char plain[] =
81 { 29, 128, 192, 253, 74, 171, 38, 187, 84, 219, 76, 76, 209, 118, 33, 249,
82 172, 124, 96, 9, 157, 110, 8, 215, 200, 63, 69, 230, 157, 104, 247, 164
83 };
84 unsigned char raw_key[] =
85 { 106, 74, 209, 88, 145, 55, 189, 135, 125, 180, 225, 108, 183, 54, 25,
86 169, 129, 188, 131, 75, 227, 245, 105, 10, 225, 15, 115, 159, 148, 184,
87 34, 191
88 };
89 unsigned char encrresult[] =
90 { 167, 102, 230, 233, 127, 195, 176, 107, 17, 91, 199, 127, 96, 113, 75,
91 195, 245, 217, 61, 236, 159, 165, 103, 121, 203, 99, 202, 41, 23, 222, 25,
92 102, 1
93 };
94
95 res = NULL;
96 ret = 0;
97
98 memcpy (key.key, raw_key, GNUNET_CRYPTO_AES_KEY_LENGTH);
99 key.crc32 =
100 htonl (GNUNET_CRYPTO_crc32_n (&key, GNUNET_CRYPTO_AES_KEY_LENGTH));
101
102 if (ntohl (key.crc32) != (unsigned int) 38125195LL)
103 {
104 printf ("Static key has different CRC: %u - %u\n",
105 ntohl (key.crc32), key.crc32);
106
107 ret = 1;
108 goto error;
109 }
110
111 if (GNUNET_CRYPTO_AES_KEY_LENGTH !=
112 GNUNET_CRYPTO_aes_encrypt (plain,
113 GNUNET_CRYPTO_AES_KEY_LENGTH,
114 &key,
115 (const struct
116 GNUNET_CRYPTO_AesInitializationVector *)
117 "testtesttesttest", result))
118 {
119 printf ("Wrong return value from encrypt block.\n");
120 ret = 1;
121 goto error;
122 }
123
124 if (memcmp (encrresult, result, GNUNET_CRYPTO_AES_KEY_LENGTH) != 0)
125 {
126 printf ("Encrypted result wrong.\n");
127 ret = 1;
128 goto error;
129 }
130
131 res = GNUNET_malloc (GNUNET_CRYPTO_AES_KEY_LENGTH);
132
133 if (GNUNET_CRYPTO_AES_KEY_LENGTH !=
134 GNUNET_CRYPTO_aes_decrypt (&key,
135 result,
136 GNUNET_CRYPTO_AES_KEY_LENGTH,
137 (const struct
138 GNUNET_CRYPTO_AesInitializationVector *)
139 "testtesttesttest", res))
140 {
141 printf ("Wrong return value from decrypt block.\n");
142 ret = 1;
143 goto error;
144 }
145
146 if (memcmp (res, plain, GNUNET_CRYPTO_AES_KEY_LENGTH) != 0)
147 {
148 printf ("Decrypted result does not match input.\n");
149
150 ret = 1;
151 }
152
153error:
154
155 GNUNET_free_non_null (res);
156
157 return ret;
158}
159
160int
161main (int argc, char *argv[])
162{
163 int failureCount = 0;
164
165 GNUNET_log_setup ("test-crypto-aes", "WARNING", NULL);
166 GNUNET_CRYPTO_random_disable_entropy_gathering ();
167 GNUNET_assert (strlen (INITVALUE) >
168 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
169 failureCount += testSymcipher ();
170 failureCount += verifyCrypto ();
171
172 if (failureCount != 0)
173 {
174 printf ("%d TESTS FAILED!\n", failureCount);
175 return -1;
176 }
177 return 0;
178}
179
180/* end of test_crypto_aes.c */
diff --git a/src/util/test_crypto_aes_weak.c b/src/util/test_crypto_aes_weak.c
new file mode 100644
index 000000000..27ee57968
--- /dev/null
+++ b/src/util/test_crypto_aes_weak.c
@@ -0,0 +1,203 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @author Krista Bennett
24 * @author Christian Grothoff
25 * @file util/test_crypto_aes_weak.c
26 * @brief AES weak key test.
27 */
28#include "platform.h"
29#include "gnunet_common.h"
30#include "gnunet_crypto_lib.h"
31#include <gcrypt.h>
32
33#define MAX_WEAK_KEY_TRIALS 100000
34#define GENERATE_WEAK_KEYS GNUNET_NO
35#define WEAK_KEY_TESTSTRING "I hate weak keys."
36
37static void
38printWeakKey (struct GNUNET_CRYPTO_AesSessionKey *key)
39{
40 int i;
41 for (i = 0; i < GNUNET_CRYPTO_AES_KEY_LENGTH; i++)
42 {
43 printf ("%x ", (int) (key->key[i]));
44 }
45}
46
47static int
48testWeakKey ()
49{
50 char result[100];
51 char res[100];
52 int size;
53 struct GNUNET_CRYPTO_AesSessionKey weak_key;
54 struct GNUNET_CRYPTO_AesInitializationVector INITVALUE;
55
56 memset (&INITVALUE, 42,
57 sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
58 /* sorry, this is not a weak key -- I don't have
59 any at the moment! */
60 weak_key.key[0] = (char) (0x4c);
61 weak_key.key[1] = (char) (0x31);
62 weak_key.key[2] = (char) (0xc6);
63 weak_key.key[3] = (char) (0x2b);
64 weak_key.key[4] = (char) (0xc1);
65 weak_key.key[5] = (char) (0x5f);
66 weak_key.key[6] = (char) (0x4d);
67 weak_key.key[7] = (char) (0x1f);
68 weak_key.key[8] = (char) (0x31);
69 weak_key.key[9] = (char) (0xaa);
70 weak_key.key[10] = (char) (0x12);
71 weak_key.key[11] = (char) (0x2e);
72 weak_key.key[12] = (char) (0xb7);
73 weak_key.key[13] = (char) (0x82);
74 weak_key.key[14] = (char) (0xc0);
75 weak_key.key[15] = (char) (0xb6);
76 weak_key.key[16] = (char) (0x4d);
77 weak_key.key[17] = (char) (0x1f);
78 weak_key.key[18] = (char) (0x31);
79 weak_key.key[19] = (char) (0xaa);
80 weak_key.key[20] = (char) (0x4c);
81 weak_key.key[21] = (char) (0x31);
82 weak_key.key[22] = (char) (0xc6);
83 weak_key.key[23] = (char) (0x2b);
84 weak_key.key[24] = (char) (0xc1);
85 weak_key.key[25] = (char) (0x5f);
86 weak_key.key[26] = (char) (0x4d);
87 weak_key.key[27] = (char) (0x1f);
88 weak_key.key[28] = (char) (0x31);
89 weak_key.key[29] = (char) (0xaa);
90 weak_key.key[30] = (char) (0xaa);
91 weak_key.key[31] = (char) (0xaa);
92 /* memset(&weak_key, 0, 32); */
93 weak_key.crc32 =
94 htonl (GNUNET_CRYPTO_crc32_n (&weak_key, GNUNET_CRYPTO_AES_KEY_LENGTH));
95
96 size = GNUNET_CRYPTO_aes_encrypt (WEAK_KEY_TESTSTRING,
97 strlen (WEAK_KEY_TESTSTRING) + 1,
98 &weak_key, &INITVALUE, result);
99
100 if (size == -1)
101 {
102 GNUNET_break (0);
103 return 1;
104 }
105
106 size = GNUNET_CRYPTO_aes_decrypt (&weak_key, result, size, &INITVALUE, res);
107
108 if ((strlen (WEAK_KEY_TESTSTRING) + 1) != size)
109 {
110 GNUNET_break (0);
111 return 1;
112 }
113 if (0 != strcmp (res, WEAK_KEY_TESTSTRING))
114 {
115 GNUNET_break (0);
116 return 1;
117 }
118 else
119 return 0;
120}
121
122static int
123getWeakKeys ()
124{
125 struct GNUNET_CRYPTO_AesSessionKey sessionkey;
126 int number_of_weak_keys = 0;
127 int number_of_runs;
128
129 gcry_cipher_hd_t handle;
130 int rc;
131
132 for (number_of_runs = 0; number_of_runs < MAX_WEAK_KEY_TRIALS;
133 number_of_runs++)
134 {
135
136 if (number_of_runs % 1000 == 0)
137 fprintf (stderr, ".");
138 /*printf("Got to run number %d.\n", number_of_runs); */
139 GNUNET_CRYPTO_aes_create_session_key (&sessionkey);
140
141 rc = gcry_cipher_open (&handle,
142 GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 0);
143
144 if (rc)
145 {
146 printf ("testweakkey: gcry_cipher_open failed on trial %d. %s\n",
147 number_of_runs, gcry_strerror (rc));
148 rc = 0;
149 continue;
150 }
151
152 rc =
153 gcry_cipher_setkey (handle, &sessionkey,
154 GNUNET_CRYPTO_AES_KEY_LENGTH);
155
156 if ((char) rc == GPG_ERR_WEAK_KEY)
157 {
158 printf ("\nWeak key (in hex): ");
159 printWeakKey (&sessionkey);
160 printf ("\n");
161 number_of_weak_keys++;
162 }
163 else if (rc)
164 {
165 printf ("\nUnexpected error generating keys. Error is %s\n",
166 gcry_strerror (rc));
167 }
168
169 gcry_cipher_close (handle);
170
171 }
172
173 return number_of_weak_keys;
174}
175
176int
177main (int argc, char *argv[])
178{
179 int weak_keys;
180
181 GNUNET_log_setup ("test-crypto-aes-weak", "WARNING", NULL);
182 GNUNET_CRYPTO_random_disable_entropy_gathering ();
183 if (GENERATE_WEAK_KEYS)
184 {
185 weak_keys = getWeakKeys ();
186
187 if (weak_keys == 0)
188 {
189 printf ("\nNo weak keys found in %d runs.\n", MAX_WEAK_KEY_TRIALS);
190 }
191 else
192 {
193 printf ("\n%d weak keys found in %d runs.\n",
194 weak_keys, MAX_WEAK_KEY_TRIALS);
195 }
196 }
197
198 if (testWeakKey () != 0)
199 return -1;
200 return 0;
201}
202
203/* end of weakkeytest.c */
diff --git a/src/util/test_crypto_crc.c b/src/util/test_crypto_crc.c
new file mode 100644
index 000000000..d1505516b
--- /dev/null
+++ b/src/util/test_crypto_crc.c
@@ -0,0 +1,221 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20 For the actual CRC code:
21 Copyright abandoned; this code is in the public domain.
22 Provided to GNUnet by peter@horizon.com
23*/
24
25/**
26 * @file util/test_crypto_crc.c
27 * @brief testcase for crypto_crc.c
28 */
29#include "platform.h"
30#include "gnunet_common.h"
31#include "gnunet_crypto_lib.h"
32
33static int expected[] = {
34 -1223996378, 929797997, -1048047323, 1791081351, -425765913, 2138425902,
35 82584863, 1939615314, 1806463044, -1505003452, 1878277636, -997353517,
36 201238705, 1723258694, -1107452366, -344562561, -1102247383, 1973035265,
37 715213337, -1886586005, 2021214515, -1387332962, 593019378, -571088044,
38 1412577760, 412164558, -1626111170, 1556494863, -289796528, -850404775,
39 2066714587, -911838105, -1426027382, 499684507, -835420055, 1817119454,
40 -1221795958, 1516966784, -1038806877, -2115880691, 532627620, 1984437415,
41 -396341583, -1345366324, -590766745, -1801923449, 1752427988, -386896390,
42 453906317, 1552589433, -858925718, 1160445643, -740188079, -486609040,
43 1102529269, -515846212, -1614217202, 1572162207, 943558923, -467330358,
44 -1870764193, 1477005328, -793029208, -888983175, -696956020, 842706021,
45 1642390067, -805889494, 1284862057, 1562545388, 2091626273, 1852404553,
46 -2076508101, 370903003, 1186422975, 1936085227, 769358463, 180401058,
47 2032612572, -105461719, -1119935472, 617249831, 1169304728, 1771205256,
48 -2042554284, 653270859, -918610713, 336081663, -913685370, 1962213744,
49 -505406126, -838622649, -1141518710, 893143582, -1330296611, 122119483,
50 1111564496, 688811976, 1016241049, -1803438473, 359630107, 1034798954,
51 -581359286, 1590946527, -389997034, 2020318460, 1695967527, -464069727,
52 -862641495, -1405012109, -771244841, 738226150, -1035328134, -933945474,
53 1254965774, 1661863830, -884127998, 1800460481, 814702567, -1214068102,
54 -541120421, 1898656429, -236825530, 1505866267, 1252462132, -981007520,
55 1502096471, -2134644056, 483221797, 1276403836, 541133290, -1234093967,
56 350748780, 257941070, 1030457090, 434988890, -1098135432, -1000556640,
57 -577128022, 644806294, -787536281, -1288346343, 998079404, 1259353935,
58 955771631, -958377466, 1746756252, 451579658, 1913409243, -952026299,
59 -1556035958, -830279881, 834744289, -1878491428, 700000962, -1027245802,
60 1393574384, -1260409147, -841420884, 892132797, 1494730226, -1649181766,
61 1651097838, -1041807403, -1916675721, -1324525963, 157405899, -655788033,
62 -1943555237, -79747022, 339721623, -138341083, 1111902411, -435322914,
63 -533294200, -190220608, -1718346014, -1631301894, 1706265243, 745533899,
64 1351941230, 1803009594, -1218191958, 1467751062, 84368433, -711251880,
65 1699423788, -768792716, 846639904, 2103267723, -2095288070, -440571408,
66 -362144485, 2020468971, 352105963, -849211036, -1272592429, 1743440467,
67 2020667861, -1649992312, 172682343, 816705364, -1990206923, 902689869,
68 -298510060, 164207498, 190378213, 242531543, 113383268, 304810777,
69 -1081099373, 819221134, -1100982926, -855941239, 1091308887, -934548124,
70 520508733, -1381763773, -491593287, -2143492665, 700894653, -2049034808,
71 -160942046, -2009323577, 1464245054, 1584746011, -768646852, -993282698,
72 1265838699, -1873820824, 575704373, -986682955, 1270688416, 88587481,
73 -1723991633, -409928242, 866669946, -483811323, -181759253, -963525431,
74 -1686612238, -1663460076, -1128449775, -1368922329, 122318131, 795862385,
75 528576131, -19927090, 1369299478, 1285665642, -738964611, 1328292127,
76 552041252, -1431494354, -1205275362, 42768297, -1329537238, -449177266,
77 943925221, 987016465, -945138414, -270064876, 1650366626, -369252552,
78 582030210, -1229235374, 147901387, -517510506, -1609742888, -1086838308,
79 1391998445, -313975512, -613392078, 855706229, 1475706341, -1112105406,
80 2032001400, 1565777625, 2030937777, 435522421, 1823527907, -691390605,
81 -827253664, 1057171580, -314146639, -630099999, -1347514552, 478716232,
82 -1533658804, -1425371979, 761987780, 1560243817, -1945893959, 1205759225,
83 -959343783, -576742354, -154125407, -1158108776, 1183788580, 1354198127,
84 -1534207721, -823991517, -170534462, -912524170, 1858513573, 467072185,
85 2091040157, -1765027018, -1659401643, -1173890143, -1912754057, -84568053,
86 2010781784, -921970156, 944508352, -922040609, 1055102010, 1018688871,
87 -1186761311, -2012263648, 1311654161, 277659086, 2029602288, 1127061510,
88 1029452642, 285677123, -188521091, -641039012, 653836416, -805916340,
89 -1644860596, 1352872213, 691634876, -1477113308, -748430369, 1030697363,
90 -2007864449, -1196662616, 1313997192, 177342476, -566676450, -1118618118,
91 1697953104, 344671484, -1489783116, -889507873, 1259591310, -716567168,
92 2116447062, 324368527, 1789366816, 1558930442, 1950250221, -785460151,
93 1174714258, -430047304, -859487565, -580633932, 607732845, -1128150220,
94 1544355315, 1460298016, -1771194297, 1215703690, 277231808, -416020628,
95 -418936577, -1724839216, 404731389, 1058730508, -1508366681, 229883053,
96 -572310243, 1883189553, 931286849, 1659300867, -94236383, -241524462,
97 548020458, -302406981, 579986475, 73468197, -984957614, 1554382245,
98 2084807492, -1456802798, -1105192593, 629440327, -16313961, -2102585261,
99 1873675206, 161035128, 1497033351, 1990150811, -499405222, 304019482,
100 41935663, -805987182, -571699268, 1748462913, 2096239823, -116359807,
101 -1871127553, -1074832534, -1558866192, 231353861, 2122854560, -2102323721,
102 -281462361, -343403210, -673268171, 1776058383, 1581561150, 2059580579,
103 768848632, 1347190372, -1701705879, 245282007, -563267886, -592558289,
104 1662399958, 1390406821, -1522485580, -706446863, 2069516289, -301855859,
105 -778346387, -1454093198, 1249083752, -1760506745, 262193320, 630751125,
106 -1495939124, -29980580, -1989626563, 659039376, -329477132, -1003507166,
107 -1322549020, 358606508, -2052572059, 1848014133, 1826958586, -1004948862,
108 -1775370541, 2134177912, -1739214473, 1892700918, 926629675, -1042761322,
109 2020075900, 606370962, -1256609305, 117577265, -586848924, 191368285,
110 1653535275, -1329269701, -375879127, -1089901406, 1206489978, 534223924,
111 -1042752982, -1178316881, -445594741, -1501682065, -1598136839,
112 -467688289, 750784023, 1781080461, 1729380226, 16906088, 862168532,
113 -2037752683, 1455274138, -1491220107, 1058323960, 1711530558, 1355062750,
114 227640096, 396568027, -173579098, -408975801, -993618329, -1470751562,
115 371076647, 209563718, 2015405719, -723460281, -1423934420, -2089643958,
116 353260489, 2084264341, -792676687, 701391030, -1440658244, 1479321011,
117 1907822880, 1232524257, -256712289, 401077577, 621808069, 868263613,
118 1244930119, 2020996902, 117483907, 1341376744, -1936988014, -445200547,
119 -843751811, -435291191, 1041695743, 476132726, -1226874735, -1436046747,
120 -297047422, 1739645396, 1948680937, -718144374, 1141983978, 1673650568,
121 -197244350, 1604464002, 1424069853, -485626505, 1708710014, -849136541,
122 1573778103, 530360999, 1777767203, 1376958336, -1088364352, 1826167753,
123 742735448, -1386211659, -1991323164, -444115655, -443055378, -1586901006,
124 -1741686587, 1925818034, -2118916824, 803890920, -1481793154, 992278937,
125 1302616410, 444517030, 1393144770, -2025632978, 1902300505, -1683582981,
126 800654133, 873850324, -619580878, -2002070410, -2024936385, 1978986634,
127 2012024264, 675768872, 389435615, -867217540, 231209167, -303917385,
128 1445676969, -1385982721, 1310476490, 580273453, -160600202, -1330895874,
129 487110497, 1124384798, 227637416, -1829783306, 1014818058, -1336870683,
130 -1042199518, -468525587, -1186267363, -472843891, 1215617600, -2056648329,
131 -873216891, 156780951, -1883246047, -842549253, -717684332, 760531638,
132 1074787431, 786267513, 814031289, -561255343, -110302255, -1837376592,
133 989669060, -81350614, 546038730, 222899882, 1298746805, 1791615733,
134 1565630269, 1516024174, 421691479, 1860326051, -1973359550, 1854393443,
135 -1401468528, -158562295, 1509929255, -124024738, -462937489, 259890715,
136 -1515121317, -289511197, -913738664, 698079062, -1631229382, -507275144,
137 1897739663, -1118192766, -1687033399, 61405556, -1913606579, -473308896,
138 -259107170, -576944609, -1689355510, 322156799, 545090192, 127425176,
139 -1815211748, -2070235628, -1172529316, 599259550, -910906653, 1797380363,
140 -938649427, 142991392, 504559631, 1208867355, -807699247, -616021271,
141 -254935281, -57151221, -1095534993, 1998380318, 1772459584, 713271407,
142 -1197898266, 808881935, -308133481, -1314455137, 284321772, -743117625,
143 -1622364240, -1667535152, 118713606, 1053615347, -2072876023, -178189072,
144 -828319551, 2047304928, -1311435786, -1970672907, -747972100, 86806159,
145 -436088421, 1464645587, 735840899, 32600466, -190473426, -735703440,
146 482872155, 475662392, -713681085, 1424078728, -150668609, -1137197868,
147 -1682762563, -48035649, 1143959866, -1542015129, 284920371, -1587695586,
148 -625236551, -753893357, -433976266, -1329796037, -1636712478, 1686783454,
149 27839146, 1748631474, -879528256, 2057796026, 773734654, 112269667,
150 -2011541314, 1517797297, -1943171794, 268166111, -1037010413, -1945824504,
151 -1672323792, 306260758, -692968628, -701704965, -462980996, 939188824,
152 553289792, 1790245000, 2093793129, -658085781, -186055037, -2130433650,
153 -1013235433, 1190870089, -2126586963, -1509655742, -1291895256,
154 -1427857845, 309538950, 388316741, 259659733, -1895092434, 110126220,
155 -170175575, -419430224, -696234084, -832170948, -353431720, -797675726,
156 -1644136054, 715163272, -1305904349, -145786463, -99586244, -695450446,
157 -871327102, -725496060, 952863853, -688441983, -1729929460, -103732092,
158 1059054528, 568873585, -982665223, -128672783, 2099418320, 1508239336,
159 -2089480835, -390935727, 664306522, -1607364342, -163246802, -1121295140,
160 -128375779, -615694409, -2079391797, 760542037, 677761593, -750117849,
161 -1060525080, 2128437080, 525250908, 1987657172, 2032530557, -2011247936,
162 1942775263, 1681562788, 688229491, -803856505, 684707948, 1308988965,
163 1455480037, 790659611, 1557968784, -383203149, -361510986, -742575828,
164 558837193, -1214977424, 1253274105, -119513513, -993964385, -33438767,
165 -177452803, 1186928041, -2073533871, 1188528559, 1896514695, 1200128512,
166 1930588755, -1914141443, 1534656032, -1192989829, -1848274656, -220848455,
167 1001806509, 1298797392, 1533031884, -1912322446, 1705583815, 1568094347,
168 -1397640627, 807828512, -1852996497, -1529733505, -1575634185,
169 -1280270160, -1567624159, -1861904922, 1276738579, 1163432999, 626879833,
170 316942006, -1871138342, 1341039701, 1595907877, 1950911580, 1634717748,
171 1071476055, -809354290, -1161553341, -2081621710, -2085557943, 19360224,
172 322135580, -698485151, 1267663094, -233890834, -126361189, -1426257522,
173 1094007921, 500179855, -283548002, -1678987343, 1946999943, 1489410849,
174 2089571262, 1430799093, 1961848046, -99462663, -552833264, 1168700661,
175 -1783882181, 2089196401, 1092839657, 914488673, 80263859, -2140947098,
176 -726384741, -1022448237, 2113887675, 1485770846, -112922517, 1995461466,
177 774613726, 944068011, 1521975359, 289086919, -386920759, -1960513175,
178 358460021, -238698524, -1913640563, -1000324864, 1731755224, -1271586254,
179 -1917469655, 2134162829, -828097534, -1089292503, -1514835999, 1682931514,
180 -482307169, 2110243841, 115744834, -2038340170, 65889188, -539445712,
181 -1713206408, -1842396726, -1659545588, -909558923, 860164922, 1328713040,
182 1044007120, -2103807103, -1073990344, -1312783785, -884980824, -705318011,
183 -1263408788, -2032228692, -1732844111, -1813827156, 1462566279,
184 1179250845, 1732421772, 604429013, -92284336, -1192166516, 304654351,
185 1998552034, -1802461575, -1802704071, -1704833934, -976264396, 1005840702,
186 2108843914, 1363909309, 843040834, -1039625241, 1285007226, 91610001,
187 418426329, 678422358, -945360697, -440008081, -1053091357, 425719777,
188 -1372778676, 591912153, 1229089037, -56663158, 2140251400, 830257037,
189 763914157, 175610373, -2105655963, -1040826150, 1174443038, 339290593,
190 346618443, -180504100, -1363190515, 210620018, 1028894425, 573529714,
191 698460117, 136999397, 1015621712, -1401813739, -297990684, -1820934845,
192 -1299093313, 1299361369, -366522415, 91527707, 1113466178, -956229484,
193 22204763, -1394374195, -1912666711, -1453789804, 1613408399, -169509567,
194 1350520309, 540761213, -2086682848, 1095131491, -812787911, 1860108594,
195 -1121378737, -1667252487, -486084366, 166519760, 1609891237, 728218405,
196 291075010, 646168382, 108462277, -1616661910, 1016600360, 2099958568,
197 27934736, 183821196, 13660496, -805589719, 936068730, -439037934,
198 1414622584, 215845485, -1352304469, -1817427526, -1318710977, -110207199,
199 228524335, 1704746590, 998293651, -1521016702, -641956531, -2089808167,
200 2094404052, -1446381065, -662186492, 1670154584, 9637833, 493925511,
201 660047318, 1197537103, 1696017374, -204994399, -1104145601, -852330465,
202 -1936369658, -829716674, -1255255217, 1264013799, 1642611772, -652520861,
203 777247164, 2028895987, -1424241853, -54367829, -1940161761, -1802831079,
204 -449405299, 838242661, -323055438, 794295411, -136989378, -446686673,
205 -421252799, -16777216,
206};
207
208int
209main (int argc, char *argv[])
210{
211 char buf[1024];
212 int i;
213
214 GNUNET_log_setup ("test-crypto-crc", "WARNING", NULL);
215 for (i = 0; i < 1024; i++)
216 buf[i] = (char) i;
217 for (i = 0; i < 1024; i++)
218 if (expected[i] != GNUNET_CRYPTO_crc32_n (&buf[i], 1024 - i))
219 return 1;
220 return 0;
221}
diff --git a/src/util/test_crypto_hash.c b/src/util/test_crypto_hash.c
new file mode 100644
index 000000000..a22a0e16a
--- /dev/null
+++ b/src/util/test_crypto_hash.c
@@ -0,0 +1,165 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @author Christian Grothoff
23 * @file util/test_crypto_hash.c
24 * @brief Test for crypto_hash.c
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_scheduler_lib.h"
30
31static char block[65536];
32
33#define FILENAME "testblock.dat"
34
35static int
36test (int number)
37{
38 GNUNET_HashCode h1;
39 GNUNET_HashCode h2;
40 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
41
42 memset (&h1, number, sizeof (GNUNET_HashCode));
43 GNUNET_CRYPTO_hash_to_enc (&h1, &enc);
44 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &h2))
45 {
46 printf ("enc2hash failed!\n");
47 return 1;
48 }
49 if (0 != memcmp (&h1, &h2, sizeof (GNUNET_HashCode)))
50 return 1;
51 return 0;
52}
53
54static int
55testEncoding ()
56{
57 int i;
58 for (i = 0; i < 255; i++)
59 if (0 != test (i))
60 return 1;
61 return 0;
62}
63
64static int
65testArithmetic ()
66{
67 static struct GNUNET_CRYPTO_AesSessionKey zskey;
68 static struct GNUNET_CRYPTO_AesInitializationVector ziv;
69 GNUNET_HashCode h1;
70 GNUNET_HashCode h2;
71 GNUNET_HashCode d;
72 GNUNET_HashCode s;
73 struct GNUNET_CRYPTO_AesSessionKey skey;
74 struct GNUNET_CRYPTO_AesInitializationVector iv;
75
76 GNUNET_CRYPTO_hash_create_random (&h1);
77 GNUNET_CRYPTO_hash_create_random (&h2);
78 if (GNUNET_CRYPTO_hash_distance_u32 (&h1, &h2) !=
79 GNUNET_CRYPTO_hash_distance_u32 (&h2, &h1))
80 return 1;
81 GNUNET_CRYPTO_hash_difference (&h1, &h2, &d);
82 GNUNET_CRYPTO_hash_sum (&h1, &d, &s);
83 if (0 != GNUNET_CRYPTO_hash_cmp (&s, &h2))
84 return 1;
85 GNUNET_CRYPTO_hash_xor (&h1, &h2, &d);
86 GNUNET_CRYPTO_hash_xor (&h1, &d, &s);
87 if (0 != GNUNET_CRYPTO_hash_cmp (&s, &h2))
88 return 1;
89 if (0 != GNUNET_CRYPTO_hash_xorcmp (&s, &h2, &h1))
90 return 1;
91 if (-1 != GNUNET_CRYPTO_hash_xorcmp (&h1, &h2, &h1))
92 return 1;
93 if (1 != GNUNET_CRYPTO_hash_xorcmp (&h1, &h2, &h2))
94 return 1;
95 memset (&d, 0xF0, sizeof (d));
96 if (0 != GNUNET_CRYPTO_hash_get_bit (&d, 3))
97 return 1;
98 if (1 != GNUNET_CRYPTO_hash_get_bit (&d, 6))
99 return 1;
100 memset (&d, 0, sizeof (d));
101 GNUNET_CRYPTO_hash_to_AES_key (&d, &skey, &iv);
102 if ((0 != memcmp (&skey, &zskey, sizeof (skey) - sizeof (unsigned int))) ||
103 (0 != memcmp (&iv, &ziv, sizeof (iv))))
104 return 1;
105 return 0;
106}
107
108static void
109finished_task (void *cls, const GNUNET_HashCode * res)
110{
111 int *ret = cls;
112 GNUNET_HashCode want;
113
114 GNUNET_CRYPTO_hash (block, sizeof (block), &want);
115 if (0 != memcmp (res, &want, sizeof (want)))
116 *ret = 2;
117 else
118 *ret = 0;
119}
120
121
122static void
123file_hasher (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
124{
125 GNUNET_CRYPTO_hash_file (tc->sched,
126 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
127 GNUNET_NO, FILENAME, 1024, &finished_task, cls);
128}
129
130
131static int
132testFileHash ()
133{
134 int ret;
135 FILE *f;
136
137 memset (block, 42, sizeof (block) / 2);
138 memset (&block[sizeof (block) / 2], 43, sizeof (block) / 2);
139 GNUNET_assert (NULL != (f = fopen (FILENAME, "w+")));
140 GNUNET_break (sizeof (block) == fwrite (block, 1, sizeof (block), f));
141 GNUNET_break (0 == fclose (f));
142 ret = 1;
143 GNUNET_SCHEDULER_run (&file_hasher, &ret);
144 GNUNET_break (0 == UNLINK (FILENAME));
145 return ret;
146}
147
148
149int
150main (int argc, char *argv[])
151{
152 int failureCount = 0;
153 int i;
154
155 GNUNET_log_setup ("test-crypto-hash", "WARNING", NULL);
156 for (i = 0; i < 10; i++)
157 failureCount += testEncoding ();
158 failureCount += testArithmetic ();
159 failureCount += testFileHash ();
160 if (failureCount != 0)
161 return 1;
162 return 0;
163}
164
165/* end of hashingtest.c */
diff --git a/src/util/test_crypto_ksk.c b/src/util/test_crypto_ksk.c
new file mode 100644
index 000000000..c8555b5de
--- /dev/null
+++ b/src/util/test_crypto_ksk.c
@@ -0,0 +1,220 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_crypto_ksk.c
23 * @brief testcase for util/crypto_ksk.c
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_time_lib.h"
31
32#define TESTSTRING "Hello World\0"
33#define MAX_TESTVAL 20
34#define UNIQUE_ITER 6
35#define ITER 25
36
37
38static int
39testMultiKey (const char *word)
40{
41 GNUNET_HashCode in;
42 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
43 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
44 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey1;
45 int i;
46
47 fprintf (stderr, "Testing KBlock key uniqueness (%s) ", word);
48 GNUNET_CRYPTO_hash (word, strlen (word), &in);
49 hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
50 if (hostkey == NULL)
51 {
52 GNUNET_break (0);
53 return GNUNET_SYSERR;
54 }
55 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
56 /*
57 for (i=0;i<sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded);i++)
58 printf("%02x", ((unsigned char*) &pkey)[i]);
59 printf("\n"); */
60 GNUNET_CRYPTO_rsa_key_free (hostkey);
61 for (i = 0; i < UNIQUE_ITER; i++)
62 {
63 fprintf (stderr, ".");
64 hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
65 if (hostkey == NULL)
66 {
67 GNUNET_break (0);
68 fprintf (stderr, " ERROR\n");
69 return GNUNET_SYSERR;
70 }
71 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey1);
72 GNUNET_CRYPTO_rsa_key_free (hostkey);
73 if (0 !=
74 memcmp (&pkey, &pkey1,
75 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
76 {
77 GNUNET_break (0);
78 fprintf (stderr, " ERROR\n");
79 return GNUNET_SYSERR;
80 }
81 }
82 fprintf (stderr, " OK\n");
83 return GNUNET_OK;
84}
85
86
87static int
88testEncryptDecrypt (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
89{
90 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
91 struct GNUNET_CRYPTO_RsaEncryptedData target;
92 char result[MAX_TESTVAL];
93 int i;
94 struct GNUNET_TIME_Absolute start;
95 int ok;
96
97 fprintf (stderr, "W");
98 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
99
100 ok = 0;
101 start = GNUNET_TIME_absolute_get ();
102 for (i = 0; i < ITER; i++)
103 {
104 fprintf (stderr, ".");
105 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING,
106 strlen (TESTSTRING) + 1,
107 &pkey, &target))
108 {
109 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
110 ok++;
111 continue;
112 }
113 if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey,
114 &target, result,
115 strlen (TESTSTRING) + 1))
116 {
117 fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n");
118 ok++;
119 continue;
120 }
121 if (strncmp (TESTSTRING, result, strlen (TESTSTRING)) != 0)
122 {
123 printf ("%s != %.*s - testEncryptDecrypt failed!\n",
124 TESTSTRING, MAX_TESTVAL, result);
125 ok++;
126 continue;
127 }
128 }
129 printf ("%d RSA encrypt/decrypt operations %llums (%d failures)\n",
130 ITER,
131 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
132 value, ok);
133 if (ok == 0)
134 return GNUNET_OK;
135 else
136 return GNUNET_SYSERR;
137}
138
139static int
140testSignVerify (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey)
141{
142 struct GNUNET_CRYPTO_RsaSignature sig;
143 struct GNUNET_CRYPTO_RsaSignaturePurpose purp;
144 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
145 int i;
146 struct GNUNET_TIME_Absolute start;
147 int ok = GNUNET_OK;
148
149 fprintf (stderr, "W");
150 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
151 start = GNUNET_TIME_absolute_get ();
152 purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose));
153 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
154 for (i = 0; i < ITER; i++)
155 {
156 fprintf (stderr, ".");
157 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
158 {
159 fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n");
160 ok = GNUNET_SYSERR;
161 continue;
162 }
163 if (GNUNET_SYSERR ==
164 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TEST,
165 &purp, &sig, &pkey))
166 {
167 printf ("GNUNET_CRYPTO_rsa_verify failed!\n");
168 ok = GNUNET_SYSERR;
169 continue;
170 }
171 if (GNUNET_SYSERR !=
172 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO,
173 &purp, &sig, &pkey))
174 {
175 printf ("GNUNET_CRYPTO_rsa_verify failed to fail!\n");
176 ok = GNUNET_SYSERR;
177 continue;
178 }
179 }
180 printf ("%d RSA sign/verify operations %llums\n",
181 ITER,
182 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
183 value);
184 return ok;
185}
186
187
188int
189main (int argc, char *argv[])
190{
191 int failureCount = 0;
192 GNUNET_HashCode in;
193 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
194
195 GNUNET_log_setup ("test-crypto-ksk", "WARNING", NULL);
196 GNUNET_CRYPTO_hash_create_random (&in);
197 hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
198 if (hostkey == NULL)
199 {
200 printf ("\nGNUNET_CRYPTO_rsa_key_create_from_hash failed!\n");
201 return 1;
202 }
203
204 if (GNUNET_OK != testMultiKey ("foo"))
205 failureCount++;
206 if (GNUNET_OK != testMultiKey ("bar"))
207 failureCount++;
208 if (GNUNET_OK != testEncryptDecrypt (hostkey))
209 failureCount++;
210 if (GNUNET_OK != testSignVerify (hostkey))
211 failureCount++;
212 GNUNET_CRYPTO_rsa_key_free (hostkey);
213
214 if (failureCount != 0)
215 {
216 printf ("\n\n%d TESTS FAILED!\n\n", failureCount);
217 return -1;
218 }
219 return 0;
220}
diff --git a/src/util/test_crypto_random.c b/src/util/test_crypto_random.c
new file mode 100644
index 000000000..e237c14a4
--- /dev/null
+++ b/src/util/test_crypto_random.c
@@ -0,0 +1,71 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21
22/**
23 * @file util/test_crypto_random.c
24 * @brief testcase for crypto_random.c
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29
30static int
31test (enum GNUNET_CRYPTO_Quality mode)
32{
33
34 int buf[1024];
35 unsigned int *b2;
36 int i;
37 unsigned long long n;
38
39 for (i = 0; i < 1024; i++)
40 GNUNET_break (1024 > (buf[i] = GNUNET_CRYPTO_random_u32 (mode, 1024)));
41 for (i = 0; i < 10; i++)
42 {
43 b2 = GNUNET_CRYPTO_random_permute (mode, 1024);
44 if (0 == memcmp (b2, buf, sizeof (buf)))
45 {
46 fprintf (stderr, "!");
47 GNUNET_free (b2);
48 continue;
49 }
50 GNUNET_free (b2);
51 break;
52 }
53 if (i == 10)
54 return 1; /* virtually impossible... */
55
56 for (n = 10; n < 1024LL * 1024LL * 1024LL; n *= 10)
57 GNUNET_break (n > GNUNET_CRYPTO_random_u64 (mode, n));
58 return 0;
59}
60
61int
62main (int argc, char *argv[])
63{
64 GNUNET_log_setup ("test-crypto-random", "WARNING", NULL);
65 if (0 != test (GNUNET_CRYPTO_QUALITY_WEAK))
66 return 1;
67 if (0 != test (GNUNET_CRYPTO_QUALITY_STRONG))
68 return 1;
69
70 return 0;
71}
diff --git a/src/util/test_crypto_rsa.c b/src/util/test_crypto_rsa.c
new file mode 100644
index 000000000..e71decca8
--- /dev/null
+++ b/src/util/test_crypto_rsa.c
@@ -0,0 +1,335 @@
1/*
2 This file is part of GNUnet.
3 (C) 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19
20*/
21/**
22 * @file util/test_crypto_rsa.c
23 * @brief testcase for RSA public key crypto
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_crypto_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_time_lib.h"
31
32#define TESTSTRING "Hello World\0"
33#define MAX_TESTVAL sizeof(struct GNUNET_CRYPTO_AesSessionKey)
34#define ITER 25
35#define KEYFILE "/tmp/test-gnunet-crypto-rsa.key"
36
37#define PERF GNUNET_YES
38
39static int
40testEncryptDecrypt ()
41{
42 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
43 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
44 struct GNUNET_CRYPTO_RsaEncryptedData target;
45 char result[MAX_TESTVAL];
46 int i;
47 struct GNUNET_TIME_Absolute start;
48 int ok;
49
50 fprintf (stderr, "W");
51 hostkey = GNUNET_CRYPTO_rsa_key_create ();
52 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
53
54 ok = 0;
55 start = GNUNET_TIME_absolute_get ();
56 for (i = 0; i < ITER; i++)
57 {
58 fprintf (stderr, ".");
59 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING,
60 strlen (TESTSTRING) + 1,
61 &pkey, &target))
62 {
63 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
64 ok++;
65 continue;
66 }
67 if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey,
68 &target, result,
69 strlen (TESTSTRING) + 1))
70 {
71 fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n");
72 ok++;
73 continue;
74
75 }
76 if (strncmp (TESTSTRING, result, strlen (TESTSTRING)) != 0)
77 {
78 printf ("%s != %.*s - testEncryptDecrypt failed!\n",
79 TESTSTRING, MAX_TESTVAL, result);
80 ok++;
81 continue;
82 }
83 }
84 printf ("%d RSA encrypt/decrypt operations %llums (%d failures)\n",
85 ITER,
86 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
87 value, ok);
88 GNUNET_CRYPTO_rsa_key_free (hostkey);
89 if (ok == 0)
90 return GNUNET_OK;
91 else
92 return GNUNET_SYSERR;
93}
94
95#if PERF
96static int
97testEncryptPerformance ()
98{
99 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
100 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
101 struct GNUNET_CRYPTO_RsaEncryptedData target;
102 int i;
103 struct GNUNET_TIME_Absolute start;
104 int ok;
105
106 fprintf (stderr, "W");
107 hostkey = GNUNET_CRYPTO_rsa_key_create ();
108 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
109
110 ok = 0;
111 start = GNUNET_TIME_absolute_get ();
112 for (i = 0; i < ITER; i++)
113 {
114 fprintf (stderr, ".");
115 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING,
116 strlen (TESTSTRING) + 1,
117 &pkey, &target))
118 {
119 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
120 ok++;
121 continue;
122 }
123 }
124 printf ("%d RSA encrypt operations %llu ms (%d failures)\n",
125 ITER,
126 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
127 value, ok);
128 GNUNET_CRYPTO_rsa_key_free (hostkey);
129 if (ok != 0)
130 return GNUNET_SYSERR;
131 return GNUNET_OK;
132}
133#endif
134
135static int
136testEncryptDecryptSK ()
137{
138 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
139 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
140 struct GNUNET_CRYPTO_RsaEncryptedData target;
141 struct GNUNET_CRYPTO_AesSessionKey insk;
142 struct GNUNET_CRYPTO_AesSessionKey outsk;
143 int i;
144 struct GNUNET_TIME_Absolute start;
145 int ok;
146
147 fprintf (stderr, "W");
148 hostkey = GNUNET_CRYPTO_rsa_key_create ();
149 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
150
151 ok = 0;
152 start = GNUNET_TIME_absolute_get ();
153 for (i = 0; i < ITER; i++)
154 {
155 fprintf (stderr, ".");
156 GNUNET_CRYPTO_aes_create_session_key (&insk);
157 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (&insk,
158 sizeof (struct
159 GNUNET_CRYPTO_AesSessionKey),
160 &pkey, &target))
161 {
162 fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
163 ok++;
164 continue;
165 }
166 if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey,
167 &target, &outsk,
168 sizeof (struct
169 GNUNET_CRYPTO_AesSessionKey)))
170 {
171 fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n");
172 ok++;
173 continue;
174 }
175 if (0 !=
176 memcmp (&insk, &outsk, sizeof (struct GNUNET_CRYPTO_AesSessionKey)))
177 {
178 printf ("testEncryptDecryptSK failed!\n");
179 ok++;
180 continue;
181 }
182 }
183 printf ("%d RSA encrypt/decrypt SK operations %llus (%d failures)\n",
184 ITER,
185 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
186 value, ok);
187 GNUNET_CRYPTO_rsa_key_free (hostkey);
188 if (ok != 0)
189 return GNUNET_SYSERR;
190 return GNUNET_OK;
191}
192
193
194static int
195testSignVerify ()
196{
197 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
198 struct GNUNET_CRYPTO_RsaSignature sig;
199 struct GNUNET_CRYPTO_RsaSignaturePurpose purp;
200 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
201 int i;
202 struct GNUNET_TIME_Absolute start;
203 int ok = GNUNET_OK;
204
205 fprintf (stderr, "W");
206 hostkey = GNUNET_CRYPTO_rsa_key_create ();
207 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
208 start = GNUNET_TIME_absolute_get ();
209 purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose));
210 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
211
212 for (i = 0; i < ITER; i++)
213 {
214 fprintf (stderr, ".");
215 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
216 {
217 fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n");
218 ok = GNUNET_SYSERR;
219 continue;
220 }
221 if (GNUNET_SYSERR ==
222 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TEST,
223 &purp, &sig, &pkey))
224 {
225 printf ("GNUNET_CRYPTO_rsa_verify failed!\n");
226 ok = GNUNET_SYSERR;
227 continue;
228 }
229 if (GNUNET_SYSERR !=
230 GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO,
231 &purp, &sig, &pkey))
232 {
233 printf ("GNUNET_CRYPTO_rsa_verify failed to fail!\n");
234 ok = GNUNET_SYSERR;
235 continue;
236 }
237 }
238 printf ("%d RSA sign/verify operations %llums\n",
239 ITER,
240 (unsigned long long) GNUNET_TIME_absolute_get_duration (start).
241 value);
242 GNUNET_CRYPTO_rsa_key_free (hostkey);
243 return ok;
244}
245
246
247#if PERF
248static int
249testSignPerformance ()
250{
251 struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
252 struct GNUNET_CRYPTO_RsaSignaturePurpose purp;
253 struct GNUNET_CRYPTO_RsaSignature sig;
254 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
255 int i;
256 struct GNUNET_TIME_Absolute start;
257 int ok = GNUNET_OK;
258
259 purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose));
260 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
261 fprintf (stderr, "W");
262 hostkey = GNUNET_CRYPTO_rsa_key_create ();
263 GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
264 start = GNUNET_TIME_absolute_get ();
265 for (i = 0; i < ITER; i++)
266 {
267 fprintf (stderr, ".");
268 if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
269 {
270 fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n");
271 ok = GNUNET_SYSERR;
272 continue;
273 }
274 }
275 printf ("%d RSA sign operations %llu ms\n", ITER,
276 GNUNET_TIME_absolute_get_duration (start).value);
277 GNUNET_CRYPTO_rsa_key_free (hostkey);
278 return ok;
279}
280#endif
281
282
283static int
284testCreateFromFile ()
285{
286 struct GNUNET_CRYPTO_RsaPrivateKey *key;
287 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded p1;
288 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded p2;
289
290 key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE);
291 GNUNET_CRYPTO_rsa_key_get_public (key, &p1);
292 GNUNET_CRYPTO_rsa_key_free (key);
293 key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE);
294 GNUNET_CRYPTO_rsa_key_get_public (key, &p2);
295 GNUNET_assert (0 == memcmp (&p1, &p2, sizeof (p1)));
296 GNUNET_CRYPTO_rsa_key_free (key);
297 GNUNET_assert (0 == UNLINK (KEYFILE));
298 key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE);
299 GNUNET_CRYPTO_rsa_key_get_public (key, &p2);
300 GNUNET_assert (0 != memcmp (&p1, &p2, sizeof (p1)));
301 GNUNET_CRYPTO_rsa_key_free (key);
302 GNUNET_assert (0 == UNLINK (KEYFILE));
303 return GNUNET_OK;
304}
305
306
307int
308main (int argc, char *argv[])
309{
310 int failureCount = 0;
311
312 GNUNET_log_setup ("test-crypto-rsa", "WARNING", NULL);
313 GNUNET_CRYPTO_random_disable_entropy_gathering ();
314 if (GNUNET_OK != testCreateFromFile ())
315 failureCount++;
316#if PERF
317 if (GNUNET_OK != testEncryptPerformance ())
318 failureCount++;
319 if (GNUNET_OK != testSignPerformance ())
320 failureCount++;
321#endif
322 if (GNUNET_OK != testEncryptDecryptSK ())
323 failureCount++;
324 if (GNUNET_OK != testEncryptDecrypt ())
325 failureCount++;
326 if (GNUNET_OK != testSignVerify ())
327 failureCount++;
328
329 if (failureCount != 0)
330 {
331 printf ("\n\n%d TESTS FAILED!\n\n", failureCount);
332 return -1;
333 }
334 return 0;
335} /* end of main */
diff --git a/src/util/test_disk.c b/src/util/test_disk.c
new file mode 100644
index 000000000..0d385afa7
--- /dev/null
+++ b/src/util/test_disk.c
@@ -0,0 +1,274 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_disk.c
23 * @brief testcase for the storage module
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_disk_lib.h"
29#include "gnunet_scheduler_lib.h"
30
31#define TESTSTRING "Hello World\0"
32
33static int
34testReadWrite ()
35{
36 char tmp[100 + 1];
37 int ret;
38
39 if (GNUNET_OK !=
40 GNUNET_DISK_file_write (".testfile", TESTSTRING, strlen (TESTSTRING),
41 "644"))
42 return 1;
43 if (GNUNET_OK != GNUNET_DISK_file_test (".testfile"))
44 return 1;
45 ret = GNUNET_DISK_file_read (".testfile", sizeof (tmp) - 1, tmp);
46 if (ret < 0)
47 {
48 fprintf (stderr,
49 "Error reading file `%s' in testReadWrite\n", ".testfile");
50 return 1;
51 }
52 tmp[ret] = '\0';
53 if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1))
54 {
55 fprintf (stderr,
56 "Error in testReadWrite: *%s* != *%s* for file %s\n",
57 tmp, TESTSTRING, ".testfile");
58 return 1;
59 }
60 GNUNET_DISK_file_copy (".testfile", ".testfile2");
61 memset (tmp, 0, sizeof (tmp));
62 ret = GNUNET_DISK_file_read (".testfile2", sizeof (tmp) - 1, tmp);
63 if (ret < 0)
64 {
65 fprintf (stderr,
66 "Error reading file `%s' in testReadWrite\n", ".testfile2");
67 return 1;
68 }
69 tmp[ret] = '\0';
70 if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1))
71 {
72 fprintf (stderr,
73 "Error in testReadWrite: *%s* != *%s* for file %s\n",
74 tmp, TESTSTRING, ".testfile2");
75 return 1;
76 }
77
78 GNUNET_break (0 == UNLINK (".testfile"));
79 GNUNET_break (0 == UNLINK (".testfile2"));
80 if (GNUNET_NO != GNUNET_DISK_file_test (".testfile"))
81 return 1;
82
83 return 0;
84}
85
86static int
87testOpenClose ()
88{
89 int fd;
90 unsigned long long size;
91 long avail;
92
93 fd = GNUNET_DISK_file_open (".testfile",
94 O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
95 GNUNET_assert (-1 != fd);
96 GNUNET_break (5 == WRITE (fd, "Hello", 5));
97 GNUNET_DISK_file_close (".testfile", fd);
98 GNUNET_break (GNUNET_OK ==
99 GNUNET_DISK_file_size (".testfile", &size, GNUNET_NO));
100 if (size != 5)
101 return 1;
102 GNUNET_break (0 == UNLINK (".testfile"));
103
104 /* test that avail goes down as we fill the disk... */
105 GNUNET_log_skip (1);
106 avail = GNUNET_DISK_get_blocks_available (".testfile");
107 GNUNET_log_skip (0);
108 fd = GNUNET_DISK_file_open (".testfile",
109 O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
110 GNUNET_assert (-1 != fd);
111 while ((avail == GNUNET_DISK_get_blocks_available (".testfile")) &&
112 (avail != -1))
113 if (16 != WRITE (fd, "HelloWorld123456", 16))
114 {
115 GNUNET_DISK_file_close (".testfile", fd);
116 GNUNET_break (0 == UNLINK (".testfile"));
117 return 1;
118 }
119 GNUNET_DISK_file_close (".testfile", fd);
120 GNUNET_break (0 == UNLINK (".testfile"));
121
122 return 0;
123}
124
125static int ok;
126
127static int
128scan_callback (void *want, const char *filename)
129{
130 if (NULL != strstr (filename, want))
131 ok++;
132 return GNUNET_OK;
133}
134
135static int
136testDirScan ()
137{
138 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry"))
139 return 1;
140 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_more"))
141 return 1;
142 GNUNET_DISK_directory_scan ("test", &scan_callback, "test/entry");
143 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
144 return 1;
145 if (ok < 2)
146 return 1;
147 return 0;
148}
149
150static void
151iter_callback (void *cls,
152 struct GNUNET_DISK_DirectoryIterator *di,
153 const char *filename, const char *dirname)
154{
155 int *i = cls;
156 (*i)++;
157 GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
158}
159
160static void
161iter_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
162{
163 GNUNET_DISK_directory_iterator_start (tc->sched,
164 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
165 "test", &iter_callback, cls);
166}
167
168static int
169testDirIter ()
170{
171 int i;
172
173 i = 0;
174 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry"))
175 return 1;
176 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_many"))
177 return 1;
178 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_more"))
179 return 1;
180 GNUNET_SCHEDULER_run (&iter_task, &i);
181 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
182 return 1;
183 if (i < 3)
184 return 1;
185 return 0;
186}
187
188
189static int
190testGetHome ()
191{
192 struct GNUNET_CONFIGURATION_Handle *cfg;
193 char *fn;
194 int ret;
195
196 cfg = GNUNET_CONFIGURATION_create ();
197 GNUNET_assert (cfg != NULL);
198 GNUNET_CONFIGURATION_set_value_string (cfg, "service", "HOME",
199 "/tmp/a/b/c");
200 fn = GNUNET_DISK_get_home_filename (cfg, "service", "d", "e", NULL);
201 GNUNET_assert (fn != NULL);
202 GNUNET_CONFIGURATION_destroy (cfg);
203 ret = strcmp ("/tmp/a/b/c/d/e", fn);
204 GNUNET_free (fn);
205 return ret;
206}
207
208static int
209testCanonicalize ()
210{
211 char *fn = GNUNET_strdup ("ab?><|cd*ef:/g\"");
212 GNUNET_DISK_filename_canonicalize (fn);
213 if (0 != strcmp (fn, "ab____cd_ef__g_"))
214 {
215 GNUNET_free (fn);
216 return 1;
217 }
218 GNUNET_free (fn);
219 return 0;
220}
221
222static int
223testChangeOwner ()
224{
225 GNUNET_log_skip (1);
226 if (GNUNET_OK == GNUNET_DISK_file_change_owner ("/dev/null", "unknownuser"))
227 return 1;
228 return 0;
229}
230
231static int
232testDirMani ()
233{
234 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file ("test/ing"))
235 return 1;
236 if (GNUNET_NO != GNUNET_DISK_file_test ("test"))
237 return 1;
238 if (GNUNET_NO != GNUNET_DISK_file_test ("test/ing"))
239 return 1;
240 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
241 return 1;
242 if (GNUNET_OK != GNUNET_DISK_directory_create ("test"))
243 return 1;
244 if (GNUNET_YES != GNUNET_DISK_directory_test ("test"))
245 return 1;
246 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
247 return 1;
248
249
250 return 0;
251}
252
253
254int
255main (int argc, char *argv[])
256{
257 unsigned int failureCount = 0;
258
259 GNUNET_log_setup ("test-disk", "WARNING", NULL);
260 failureCount += testReadWrite ();
261 failureCount += testOpenClose ();
262 failureCount += testDirScan ();
263 failureCount += testDirIter ();
264 failureCount += testGetHome ();
265 failureCount += testCanonicalize ();
266 failureCount += testChangeOwner ();
267 failureCount += testDirMani ();
268 if (failureCount != 0)
269 {
270 fprintf (stderr, "\n%u TESTS FAILED!\n", failureCount);
271 return -1;
272 }
273 return 0;
274} /* end of main */
diff --git a/src/util/test_getopt.c b/src/util/test_getopt.c
new file mode 100644
index 000000000..89e7be863
--- /dev/null
+++ b/src/util/test_getopt.c
@@ -0,0 +1,239 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_getopt.c
22 * @brief testcase for util/getopt.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_configuration_lib.h"
27#include "gnunet_getopt_lib.h"
28
29#define VERBOSE 0
30
31static int
32testMinimal ()
33{
34 struct GNUNET_CONFIGURATION_Handle *cfg;
35 char *const emptyargv[] = {
36 "test",
37 NULL
38 };
39 const struct GNUNET_GETOPT_CommandLineOption emptyoptionlist[] = {
40 GNUNET_GETOPT_OPTION_END
41 };
42
43 cfg = GNUNET_CONFIGURATION_create ();
44 if (1 != GNUNET_GETOPT_run ("test", cfg, emptyoptionlist, 1, emptyargv))
45 {
46 GNUNET_CONFIGURATION_destroy (cfg);
47 return 1;
48 }
49 GNUNET_CONFIGURATION_destroy (cfg);
50
51 return 0;
52}
53
54static int
55testVerbose ()
56{
57 struct GNUNET_CONFIGURATION_Handle *cfg;
58 char *const myargv[] = {
59 "test",
60 "-V",
61 "-V",
62 "more",
63 NULL
64 };
65 unsigned int vflags = 0;
66 const struct GNUNET_GETOPT_CommandLineOption verboseoptionlist[] = {
67 GNUNET_GETOPT_OPTION_VERBOSE (&vflags),
68 GNUNET_GETOPT_OPTION_END
69 };
70
71 cfg = GNUNET_CONFIGURATION_create ();
72 if (3 != GNUNET_GETOPT_run ("test", cfg, verboseoptionlist, 4, myargv))
73 {
74 GNUNET_break (0);
75 GNUNET_CONFIGURATION_destroy (cfg);
76 return 1;
77 }
78 GNUNET_CONFIGURATION_destroy (cfg);
79 if (vflags != 2)
80 {
81 GNUNET_break (0);
82 return 1;
83 }
84 return 0;
85}
86
87static int
88testVersion ()
89{
90 struct GNUNET_CONFIGURATION_Handle *cfg;
91 char *const myargv[] = {
92 "test_getopt",
93 "-v",
94 NULL
95 };
96 const struct GNUNET_GETOPT_CommandLineOption versionoptionlist[] = {
97 GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION),
98 GNUNET_GETOPT_OPTION_END
99 };
100
101 cfg = GNUNET_CONFIGURATION_create ();
102 if (-1 != GNUNET_GETOPT_run ("test_getopt",
103 cfg, versionoptionlist, 2, myargv))
104 {
105 GNUNET_break (0);
106 GNUNET_CONFIGURATION_destroy (cfg);
107 return 1;
108 }
109 GNUNET_CONFIGURATION_destroy (cfg);
110 return 0;
111}
112
113static int
114testAbout ()
115{
116 struct GNUNET_CONFIGURATION_Handle *cfg;
117 char *const myargv[] = {
118 "test_getopt",
119 "-h",
120 NULL
121 };
122 const struct GNUNET_GETOPT_CommandLineOption aboutoptionlist[] = {
123 GNUNET_GETOPT_OPTION_HELP ("Testing"),
124 GNUNET_GETOPT_OPTION_END
125 };
126
127 cfg = GNUNET_CONFIGURATION_create ();
128 if (-1 != GNUNET_GETOPT_run ("test_getopt",
129 cfg, aboutoptionlist, 2, myargv))
130 {
131 GNUNET_break (0);
132 GNUNET_CONFIGURATION_destroy (cfg);
133 return 1;
134 }
135 GNUNET_CONFIGURATION_destroy (cfg);
136 return 0;
137}
138
139static int
140testLogOpts ()
141{
142 struct GNUNET_CONFIGURATION_Handle *cfg;
143 char *const myargv[] = {
144 "test_getopt",
145 "-l", "filename",
146 "-L", "WARNING",
147 NULL
148 };
149 char *level = GNUNET_strdup ("stuff");
150 char *fn = NULL;
151 const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] = {
152 GNUNET_GETOPT_OPTION_LOGFILE (&fn),
153 GNUNET_GETOPT_OPTION_LOGLEVEL (&level),
154 GNUNET_GETOPT_OPTION_END
155 };
156
157 cfg = GNUNET_CONFIGURATION_create ();
158 if (5 != GNUNET_GETOPT_run ("test_getopt", cfg, logoptionlist, 5, myargv))
159 {
160 GNUNET_break (0);
161 GNUNET_CONFIGURATION_destroy (cfg);
162 return 1;
163 }
164 GNUNET_assert (fn != NULL);
165 GNUNET_CONFIGURATION_destroy (cfg);
166 if ((0 != strcmp (level, "WARNING")) || (0 != strcmp (fn, "filename")))
167 {
168 GNUNET_break (0);
169 GNUNET_free (level);
170 GNUNET_free (fn);
171 return 1;
172 }
173 GNUNET_free (level);
174 GNUNET_free (fn);
175 return 0;
176}
177
178static int
179testFlagNum ()
180{
181 struct GNUNET_CONFIGURATION_Handle *cfg;
182 char *const myargv[] = {
183 "test_getopt",
184 "-f",
185 "-n", "42",
186 "-N", "42",
187 NULL
188 };
189 int flag = 0;
190 unsigned int num = 0;
191 unsigned long long lnum = 0;
192 const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] = {
193 {'f', "--flag", NULL, "helptext", 0, &GNUNET_GETOPT_set_one,
194 (void *) &flag},
195 {'n', "--num", "ARG", "helptext", 1, &GNUNET_GETOPT_set_uint,
196 (void *) &num},
197 {'N', "--lnum", "ARG", "helptext", 1, &GNUNET_GETOPT_set_ulong,
198 (void *) &lnum},
199 GNUNET_GETOPT_OPTION_END
200 };
201
202 cfg = GNUNET_CONFIGURATION_create ();
203 if (6 != GNUNET_GETOPT_run ("test_getopt", cfg, logoptionlist, 6, myargv))
204 {
205 GNUNET_break (0);
206 GNUNET_CONFIGURATION_destroy (cfg);
207 return 1;
208 }
209 GNUNET_CONFIGURATION_destroy (cfg);
210 if ((1 != flag) || (42 != num) || (42 != lnum))
211 {
212 GNUNET_break (0);
213 return 1;
214 }
215 return 0;
216}
217
218int
219main (int argc, char *argv[])
220{
221 int errCnt = 0;
222
223 GNUNET_log_setup ("test_getopt", "WARNING", NULL);
224 /* suppress output from -h, -v options */
225 GNUNET_break (0 == CLOSE (1));
226 if (0 != testMinimal ())
227 errCnt++;
228 if (0 != testVerbose ())
229 errCnt++;
230 if (0 != testVersion ())
231 errCnt++;
232 if (0 != testAbout ())
233 errCnt++;
234 if (0 != testLogOpts ())
235 errCnt++;
236 if (0 != testFlagNum ())
237 errCnt++;
238 return errCnt;
239}
diff --git a/src/util/test_network.c b/src/util/test_network.c
new file mode 100644
index 000000000..377ec836f
--- /dev/null
+++ b/src/util/test_network.c
@@ -0,0 +1,209 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34
35static struct GNUNET_NETWORK_SocketHandle *csock;
36
37static struct GNUNET_NETWORK_SocketHandle *asock;
38
39static struct GNUNET_NETWORK_SocketHandle *lsock;
40
41static size_t sofar;
42
43static int ls;
44
45
46
47/**
48 * Create and initialize a listen socket for the server.
49 *
50 * @return -1 on error, otherwise the listen socket
51 */
52static int
53open_listen_socket ()
54{
55 const static int on = 1;
56 struct sockaddr_in sa;
57 int fd;
58
59 memset (&sa, 0, sizeof (sa));
60 sa.sin_port = htons (PORT);
61 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
62 GNUNET_assert (fd >= 0);
63 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
64 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
65 "setsockopt");
66 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
67 LISTEN (fd, 5);
68 return fd;
69}
70
71static void
72receive_check (void *cls,
73 const void *buf,
74 size_t available,
75 const struct sockaddr *addr, socklen_t addrlen, int errCode)
76{
77 int *ok = cls;
78
79#if VERBOSE
80 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive validates incoming data\n");
81#endif
82 GNUNET_assert (buf != NULL); /* no timeout */
83 if (0 == memcmp (&"Hello World"[sofar], buf, available))
84 sofar += available;
85 if (sofar < 12)
86 {
87#if VERBOSE
88 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive needs more data\n");
89#endif
90 GNUNET_NETWORK_receive (asock,
91 1024,
92 GNUNET_TIME_relative_multiply
93 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check,
94 cls);
95 }
96 else
97 {
98#if VERBOSE
99 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
100 "Receive closes accepted socket\n");
101#endif
102 *ok = 0;
103 GNUNET_NETWORK_socket_destroy (asock);
104 }
105}
106
107
108static void
109run_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
110{
111#if VERBOSE
112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test accepts connection\n");
113#endif
114 asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
115 NULL, NULL, ls, 1024);
116 GNUNET_assert (asock != NULL);
117 GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock));
118#if VERBOSE
119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys listen socket\n");
120#endif
121 GNUNET_NETWORK_socket_destroy (lsock);
122#if VERBOSE
123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124 "Test asks to receive on accepted socket\n");
125#endif
126 GNUNET_NETWORK_receive (asock,
127 1024,
128 GNUNET_TIME_relative_multiply
129 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, cls);
130}
131
132static size_t
133make_hello (void *cls, size_t size, void *buf)
134{
135#if VERBOSE
136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
137 "Test prepares to transmit on connect socket\n");
138#endif
139 GNUNET_assert (size >= 12);
140 strcpy ((char *) buf, "Hello World");
141 return 12;
142}
143
144static void
145task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
146{
147 ls = open_listen_socket ();
148 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
149 GNUNET_assert (lsock != NULL);
150 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
151 "localhost", PORT, 1024);
152 GNUNET_assert (csock != NULL);
153#if VERBOSE
154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test asks for write notification\n");
155#endif
156 GNUNET_assert (NULL !=
157 GNUNET_NETWORK_notify_transmit_ready (csock,
158 12,
159 GNUNET_TIME_UNIT_SECONDS,
160 &make_hello, NULL));
161#if VERBOSE
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys client socket\n");
163#endif
164 GNUNET_NETWORK_socket_destroy (csock);
165#if VERBOSE
166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test prepares to accept\n");
167#endif
168 GNUNET_SCHEDULER_add_read (tc->sched,
169 GNUNET_NO,
170 GNUNET_SCHEDULER_PRIORITY_HIGH,
171 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
172 GNUNET_TIME_UNIT_FOREVER_REL,
173 ls, &run_accept, cls);
174}
175
176
177/**
178 * Main method, starts scheduler with task ,
179 * checks that "ok" is correct at the end.
180 */
181static int
182check ()
183{
184 int ok;
185
186 ok = 1;
187 GNUNET_SCHEDULER_run (&task, &ok);
188 return ok;
189}
190
191
192
193int
194main (int argc, char *argv[])
195{
196 int ret = 0;
197
198 GNUNET_log_setup ("test_network",
199#if VERBOSE
200 "DEBUG",
201#else
202 "WARNING",
203#endif
204 NULL);
205 ret += check ();
206 return ret;
207}
208
209/* end of test_network.c */
diff --git a/src/util/test_network_addressing.c b/src/util/test_network_addressing.c
new file mode 100644
index 000000000..8312b721f
--- /dev/null
+++ b/src/util/test_network_addressing.c
@@ -0,0 +1,193 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_addressing.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34
35static struct GNUNET_NETWORK_SocketHandle *csock;
36
37static struct GNUNET_NETWORK_SocketHandle *asock;
38
39static struct GNUNET_NETWORK_SocketHandle *lsock;
40
41static size_t sofar;
42
43static int ls;
44
45
46
47/**
48 * Create and initialize a listen socket for the server.
49 *
50 * @return -1 on error, otherwise the listen socket
51 */
52static int
53open_listen_socket ()
54{
55 const static int on = 1;
56 struct sockaddr_in sa;
57 int fd;
58
59 memset (&sa, 0, sizeof (sa));
60 sa.sin_port = htons (PORT);
61 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
62 GNUNET_assert (fd >= 0);
63 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
64 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
65 "setsockopt");
66 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
67 LISTEN (fd, 5);
68 return fd;
69}
70
71
72static void
73receive_check (void *cls,
74 const void *buf,
75 size_t available,
76 const struct sockaddr *addr, socklen_t addrlen, int errCode)
77{
78 int *ok = cls;
79
80 GNUNET_assert (buf != NULL); /* no timeout */
81 if (0 == memcmp (&"Hello World"[sofar], buf, available))
82 sofar += available;
83 if (sofar < 12)
84 {
85 GNUNET_NETWORK_receive (asock,
86 1024,
87 GNUNET_TIME_relative_multiply
88 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check,
89 cls);
90 }
91 else
92 {
93 *ok = 0;
94 GNUNET_NETWORK_socket_destroy (asock);
95 }
96}
97
98
99static void
100run_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
101{
102 void *addr;
103 size_t alen;
104 struct sockaddr_in *v4;
105 struct sockaddr_in expect;
106
107 asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
108 NULL, NULL, ls, 1024);
109 GNUNET_assert (asock != NULL);
110 GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock));
111 GNUNET_assert (GNUNET_OK ==
112 GNUNET_NETWORK_socket_get_address (asock, &addr, &alen));
113 GNUNET_assert (alen == sizeof (struct sockaddr_in));
114 v4 = addr;
115 memset (&expect, 0, sizeof (expect));
116 expect.sin_family = AF_INET;
117 expect.sin_port = v4->sin_port;
118 expect.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
119 GNUNET_assert (0 == memcmp (&expect, v4, alen));
120 GNUNET_NETWORK_socket_destroy (lsock);
121 GNUNET_NETWORK_receive (asock,
122 1024,
123 GNUNET_TIME_relative_multiply
124 (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, cls);
125}
126
127static size_t
128make_hello (void *cls, size_t size, void *buf)
129{
130 GNUNET_assert (size >= 12);
131 strcpy ((char *) buf, "Hello World");
132 return 12;
133}
134
135static void
136task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
137{
138 struct sockaddr_in v4;
139 ls = open_listen_socket ();
140 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
141 GNUNET_assert (lsock != NULL);
142
143 v4.sin_family = AF_INET;
144 v4.sin_port = htons (PORT);
145 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
146 csock = GNUNET_NETWORK_socket_create_from_sockaddr (tc->sched,
147 AF_INET,
148 (const struct sockaddr
149 *) &v4, sizeof (v4),
150 1024);
151 GNUNET_assert (csock != NULL);
152 GNUNET_assert (NULL !=
153 GNUNET_NETWORK_notify_transmit_ready (csock,
154 12,
155 GNUNET_TIME_UNIT_SECONDS,
156 &make_hello, NULL));
157 GNUNET_NETWORK_socket_destroy (csock);
158 GNUNET_SCHEDULER_add_read (tc->sched,
159 GNUNET_NO,
160 GNUNET_SCHEDULER_PRIORITY_HIGH,
161 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
162 GNUNET_TIME_UNIT_FOREVER_REL,
163 ls, &run_accept, cls);
164}
165
166
167/**
168 * Main method, starts scheduler with task ,
169 * checks that "ok" is correct at the end.
170 */
171static int
172check ()
173{
174 int ok;
175
176 ok = 1;
177 GNUNET_SCHEDULER_run (&task, &ok);
178 return ok;
179}
180
181
182
183int
184main (int argc, char *argv[])
185{
186 int ret = 0;
187
188 GNUNET_log_setup ("test_network_addressing", "WARNING", NULL);
189 ret += check ();
190 return ret;
191}
192
193/* end of test_network_addressing.c */
diff --git a/src/util/test_network_receive_cancel.c b/src/util/test_network_receive_cancel.c
new file mode 100644
index 000000000..e22e24d8c
--- /dev/null
+++ b/src/util/test_network_receive_cancel.c
@@ -0,0 +1,164 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_receive_cancel.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34
35static struct GNUNET_NETWORK_SocketHandle *csock;
36
37static struct GNUNET_NETWORK_SocketHandle *asock;
38
39static struct GNUNET_NETWORK_SocketHandle *lsock;
40
41static int ls;
42
43static GNUNET_SCHEDULER_TaskIdentifier receive_task;
44
45
46
47
48/**
49 * Create and initialize a listen socket for the server.
50 *
51 * @return -1 on error, otherwise the listen socket
52 */
53static int
54open_listen_socket ()
55{
56 const static int on = 1;
57 struct sockaddr_in sa;
58 int fd;
59
60 memset (&sa, 0, sizeof (sa));
61 sa.sin_port = htons (PORT);
62 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
63 GNUNET_assert (fd >= 0);
64 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
65 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
66 "setsockopt");
67 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
68 LISTEN (fd, 5);
69 return fd;
70}
71
72
73
74static void
75dead_receive (void *cls,
76 const void *buf,
77 size_t available,
78 const struct sockaddr *addr, socklen_t addrlen, int errCode)
79{
80 GNUNET_assert (0);
81}
82
83
84static void
85run_accept_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
86{
87
88 asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched,
89 NULL, NULL, ls, 1024);
90 GNUNET_assert (asock != NULL);
91 GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock));
92 GNUNET_NETWORK_socket_destroy (lsock);
93 receive_task
94 = GNUNET_NETWORK_receive (asock,
95 1024,
96 GNUNET_TIME_relative_multiply
97 (GNUNET_TIME_UNIT_SECONDS, 5), &dead_receive,
98 cls);
99}
100
101
102static void
103receive_cancel_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
104{
105 int *ok = cls;
106 GNUNET_NETWORK_receive_cancel (asock, receive_task);
107 GNUNET_NETWORK_socket_destroy (csock);
108 GNUNET_NETWORK_socket_destroy (asock);
109 *ok = 0;
110}
111
112
113
114static void
115task_receive_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
116{
117 ls = open_listen_socket ();
118 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
119 GNUNET_assert (lsock != NULL);
120 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
121 "localhost", PORT, 1024);
122 GNUNET_assert (csock != NULL);
123 GNUNET_SCHEDULER_add_read (tc->sched,
124 GNUNET_NO,
125 GNUNET_SCHEDULER_PRIORITY_HIGH,
126 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
127 GNUNET_TIME_UNIT_FOREVER_REL,
128 ls, &run_accept_cancel, cls);
129 GNUNET_SCHEDULER_add_delayed (tc->sched,
130 GNUNET_NO,
131 GNUNET_SCHEDULER_PRIORITY_KEEP,
132 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
133 GNUNET_TIME_UNIT_SECONDS,
134 &receive_cancel_task, cls);
135}
136
137
138
139/**
140 * Main method, starts scheduler with task_timeout.
141 */
142static int
143check_receive_cancel ()
144{
145 int ok;
146
147 ok = 1;
148 GNUNET_SCHEDULER_run (&task_receive_cancel, &ok);
149 return ok;
150}
151
152
153int
154main (int argc, char *argv[])
155{
156 int ret = 0;
157
158 GNUNET_log_setup ("test_network_receive_cancel", "WARNING", NULL);
159 ret += check_receive_cancel ();
160
161 return ret;
162}
163
164/* end of test_network.c */
diff --git a/src/util/test_network_timeout.c b/src/util/test_network_timeout.c
new file mode 100644
index 000000000..2c2f6f9f2
--- /dev/null
+++ b/src/util/test_network_timeout.c
@@ -0,0 +1,144 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_timeout.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34static struct GNUNET_NETWORK_SocketHandle *csock;
35
36static struct GNUNET_NETWORK_SocketHandle *lsock;
37
38static int ls;
39
40
41/**
42 * Create and initialize a listen socket for the server.
43 *
44 * @return -1 on error, otherwise the listen socket
45 */
46static int
47open_listen_socket ()
48{
49 const static int on = 1;
50 struct sockaddr_in sa;
51 int fd;
52
53 memset (&sa, 0, sizeof (sa));
54 sa.sin_port = htons (PORT);
55 fd = SOCKET (AF_INET, SOCK_STREAM, 0);
56 GNUNET_assert (fd >= 0);
57 if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
58 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
59 "setsockopt");
60 GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0);
61 LISTEN (fd, 5);
62 return fd;
63}
64
65
66static size_t
67send_kilo (void *cls, size_t size, void *buf)
68{
69 int *ok = cls;
70 if (size == 0)
71 {
72#if VERBOSE
73 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got the desired timeout!\n");
74#endif
75 GNUNET_assert (buf == NULL);
76 *ok = 0;
77 GNUNET_NETWORK_socket_destroy (lsock);
78 GNUNET_NETWORK_socket_destroy (csock);
79 return 0;
80 }
81#if VERBOSE
82 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending kilo to fill buffer.\n");
83#endif
84 GNUNET_assert (size >= 1024);
85 memset (buf, 42, 1024);
86
87 GNUNET_assert (NULL !=
88 GNUNET_NETWORK_notify_transmit_ready (csock,
89 1024,
90 GNUNET_TIME_UNIT_SECONDS,
91 &send_kilo, cls));
92 return 1024;
93}
94
95
96static void
97task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
98{
99
100 ls = open_listen_socket ();
101 lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0);
102 GNUNET_assert (lsock != NULL);
103 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
104 "localhost", PORT, 1024);
105 GNUNET_assert (csock != NULL);
106 GNUNET_assert (NULL !=
107 GNUNET_NETWORK_notify_transmit_ready (csock,
108 1024,
109 GNUNET_TIME_UNIT_SECONDS,
110 &send_kilo, cls));
111}
112
113
114
115/**
116 * Main method, starts scheduler with task_timeout.
117 */
118static int
119check_timeout ()
120{
121 int ok;
122
123 ok = 1;
124 GNUNET_SCHEDULER_run (&task_timeout, &ok);
125 return ok;
126}
127
128int
129main (int argc, char *argv[])
130{
131 int ret = 0;
132
133 GNUNET_log_setup ("test_network_timeout",
134#if VERBOSE
135 "DEBUG",
136#else
137 "WARNING",
138#endif
139 NULL);
140 ret += check_timeout ();
141 return ret;
142}
143
144/* end of test_network_timeout.c */
diff --git a/src/util/test_network_timeout_no_connect.c b/src/util/test_network_timeout_no_connect.c
new file mode 100644
index 000000000..fd5e82dcd
--- /dev/null
+++ b/src/util/test_network_timeout_no_connect.c
@@ -0,0 +1,95 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_timeout.c
22 * @brief tests for network.c, doing timeout which connect failure
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 13425
33
34static struct GNUNET_NETWORK_SocketHandle *csock;
35
36static size_t
37handle_timeout (void *cls, size_t size, void *buf)
38{
39 int *ok = cls;
40#if VERBOSE
41 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received timeout signal.\n");
42#endif
43
44 GNUNET_assert (size == 0);
45 GNUNET_assert (buf == NULL);
46 *ok = 0;
47 return 0;
48}
49
50
51static void
52task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
53{
54 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
55 "localhost", PORT, 1024);
56 GNUNET_assert (csock != NULL);
57 GNUNET_assert (NULL !=
58 GNUNET_NETWORK_notify_transmit_ready (csock,
59 1024,
60 GNUNET_TIME_UNIT_SECONDS,
61 &handle_timeout, cls));
62}
63
64
65
66/**
67 * Main method, starts scheduler with task_timeout.
68 */
69static int
70check_timeout ()
71{
72 int ok;
73
74 ok = 1;
75 GNUNET_SCHEDULER_run (&task_timeout, &ok);
76 return ok;
77}
78
79int
80main (int argc, char *argv[])
81{
82 int ret = 0;
83
84 GNUNET_log_setup ("test_network_timeout_no_connect",
85#if VERBOSE
86 "DEBUG",
87#else
88 "WARNING",
89#endif
90 NULL);
91 ret += check_timeout ();
92 return ret;
93}
94
95/* end of test_network_timeout_no_connect.c */
diff --git a/src/util/test_network_transmit_cancel.c b/src/util/test_network_transmit_cancel.c
new file mode 100644
index 000000000..29732d202
--- /dev/null
+++ b/src/util/test_network_transmit_cancel.c
@@ -0,0 +1,97 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_network_transmit_cancel.c
22 * @brief tests for network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_network_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_YES
31
32#define PORT 12435
33
34
35static size_t
36not_run (void *cls, size_t size, void *buf)
37{
38 GNUNET_assert (0);
39 return 0;
40}
41
42
43static void
44task_transmit_cancel (void *cls,
45 const struct GNUNET_SCHEDULER_TaskContext *tc)
46{
47 int *ok = cls;
48 struct GNUNET_NETWORK_TransmitHandle *th;
49 struct GNUNET_NETWORK_SocketHandle *csock;
50
51 csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched,
52 "localhost", PORT, 1024);
53 GNUNET_assert (csock != NULL);
54 th = GNUNET_NETWORK_notify_transmit_ready (csock,
55 12,
56 GNUNET_TIME_UNIT_MINUTES,
57 &not_run, cls);
58 GNUNET_NETWORK_notify_transmit_ready_cancel (th);
59 GNUNET_NETWORK_socket_destroy (csock);
60 *ok = 0;
61}
62
63
64
65
66/**
67 * Main method, starts scheduler with task_timeout.
68 */
69static int
70check_transmit_cancel ()
71{
72 int ok;
73
74 ok = 1;
75 GNUNET_SCHEDULER_run (&task_transmit_cancel, &ok);
76 return ok;
77}
78
79
80int
81main (int argc, char *argv[])
82{
83 int ret = 0;
84
85 GNUNET_log_setup ("test_network_transmit_cancel",
86#if VERBOSE
87 "DEBUG",
88#else
89 "WARNING",
90#endif
91 NULL);
92 ret += check_transmit_cancel ();
93
94 return ret;
95}
96
97/* end of test_network_transmit_cancel.c */
diff --git a/src/util/test_os_load.c b/src/util/test_os_load.c
new file mode 100644
index 000000000..2f82f60c7
--- /dev/null
+++ b/src/util/test_os_load.c
@@ -0,0 +1,182 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_os_load.c
22 * @brief testcase for util/os_load.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_configuration_lib.h"
27#include "gnunet_crypto_lib.h"
28#include "gnunet_disk_lib.h"
29#include "gnunet_os_lib.h"
30#include "gnunet_time_lib.h"
31
32#define VERBOSE 0
33
34static int
35testcpu ()
36{
37 static long k;
38 int ret;
39 struct GNUNET_TIME_Absolute start;
40 struct GNUNET_CONFIGURATION_Handle *cfg;
41
42 fprintf (stderr, "CPU load test, this may take a while.");
43 cfg = GNUNET_CONFIGURATION_create ();
44 GNUNET_assert (cfg != NULL);
45 /* need to run each phase for more than 10s since
46 statuscalls only refreshes that often... */
47 GNUNET_CONFIGURATION_set_value_number (cfg, "LOAD", "MAXCPULOAD", 100);
48 GNUNET_OS_load_cpu_get (cfg);
49 start = GNUNET_TIME_absolute_get ();
50 while ((GNUNET_TIME_absolute_get_duration (start).value < 120 * 1000) &&
51 (0 != GNUNET_OS_load_cpu_get (cfg)))
52 sleep (1);
53 start = GNUNET_TIME_absolute_get ();
54 ret = GNUNET_OS_load_cpu_get (cfg);
55 if (ret > 10)
56 {
57 fprintf (stderr,
58 "\nWARNING: base load too high (%d) to run CPU load test.\n",
59 ret);
60 GNUNET_CONFIGURATION_destroy (cfg);
61 return 0;
62 }
63 if (ret == -1)
64 {
65 fprintf (stderr, "\nWARNING: CPU load determination not supported.\n");
66 GNUNET_CONFIGURATION_destroy (cfg);
67 return 0;
68 }
69 while (GNUNET_TIME_absolute_get_duration (start).value < 60 * 1000)
70 {
71 k++; /* do some processing to drive load up */
72 if (ret < GNUNET_OS_load_cpu_get (cfg))
73 break;
74 }
75 if (ret >= GNUNET_OS_load_cpu_get (cfg))
76 {
77 fprintf (stderr,
78 "\nbusy loop failed to increase CPU load: %d >= %d.",
79 ret, GNUNET_OS_load_cpu_get (cfg));
80 ret = 1;
81 }
82 else
83 {
84#if VERBOSE
85 fprintf (stderr,
86 "\nbusy loop increased CPU load: %d < %d.",
87 ret, GNUNET_OS_load_cpu_get (cfg));
88#endif
89 ret = 0;
90 }
91 fprintf (stderr, "\n");
92
93
94 GNUNET_CONFIGURATION_destroy (cfg);
95 return ret;
96}
97
98static int
99testdisk ()
100{
101 int ret;
102 int fd;
103 char buf[65536];
104 struct GNUNET_TIME_Absolute start;
105 struct GNUNET_CONFIGURATION_Handle *cfg;
106
107 fprintf (stderr, "IO load test, this may take a while.");
108 cfg = GNUNET_CONFIGURATION_create ();
109 GNUNET_assert (cfg != NULL);
110 /* need to run each phase for more than 10s since
111 statuscalls only refreshes that often... */
112 GNUNET_CONFIGURATION_set_value_number (cfg, "LOAD", "MAXIOLOAD", 100);
113 GNUNET_OS_load_disk_get (cfg);
114 start = GNUNET_TIME_absolute_get ();
115 while ((GNUNET_TIME_absolute_get_duration (start).value < 12 * 1000) &&
116 (0 != GNUNET_OS_load_disk_get (cfg)))
117 sleep (1);
118 start = GNUNET_TIME_absolute_get ();
119 ret = GNUNET_OS_load_disk_get (cfg);
120 if (ret > 10)
121 {
122 fprintf (stderr,
123 "WARNING: base load too high (%d) to run IO load test.\n",
124 ret);
125 GNUNET_CONFIGURATION_destroy (cfg);
126 return 0;
127 }
128 if (ret == -1)
129 {
130 fprintf (stderr, "WARNING: IO load determination not supported.\n");
131 GNUNET_CONFIGURATION_destroy (cfg);
132 return 0;
133 }
134 memset (buf, 42, sizeof (buf));
135 fd =
136 GNUNET_DISK_file_open (".loadfile", O_WRONLY | O_CREAT,
137 S_IRUSR | S_IWUSR);
138 GNUNET_assert (fd != -1);
139 while (GNUNET_TIME_absolute_get_duration (start).value < 60 * 1000)
140 {
141 LSEEK (fd, GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
142 1024 * 1024 * 1024), SEEK_SET);
143 GNUNET_assert (sizeof (buf) == WRITE (fd, buf, sizeof (buf)));
144 fsync (fd);
145 if (ret < GNUNET_OS_load_disk_get (cfg))
146 break;
147 }
148 GNUNET_break (0 == CLOSE (fd));
149 GNUNET_break (0 == UNLINK (".loadfile"));
150 if (ret >= GNUNET_OS_load_disk_get (cfg))
151 {
152 fprintf (stderr,
153 "\nbusy loop failed to increase IO load: %d >= %d.",
154 ret, GNUNET_OS_load_disk_get (cfg));
155 ret = 1;
156 }
157 else
158 {
159#if VERBOSE
160 fprintf (stderr,
161 "\nbusy loop increased disk load: %d < %d.",
162 ret, GNUNET_OS_load_disk_get (cfg));
163#endif
164 ret = 0;
165 }
166 fprintf (stderr, "\n");
167 GNUNET_CONFIGURATION_destroy (cfg);
168 return 0;
169}
170
171int
172main (int argc, char *argv[])
173{
174 int errCnt = 0;
175
176 GNUNET_log_setup ("test-os-load", "WARNING", NULL);
177 if (0 != testcpu ())
178 errCnt++;
179 if (0 != testdisk ())
180 errCnt++;
181 return errCnt;
182}
diff --git a/src/util/test_os_network.c b/src/util/test_os_network.c
new file mode 100644
index 000000000..ca57765da
--- /dev/null
+++ b/src/util/test_os_network.c
@@ -0,0 +1,73 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_os_network.c
22 * @brief testcase for util/os_network.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_configuration_lib.h"
27#include "gnunet_os_lib.h"
28
29#define VERBOSE 0
30
31/**
32 * Check if the address we got is IPv4 or IPv6 loopback (which should
33 * be present on all systems at all times); if so, set ok to 0
34 * (success).
35 */
36static int
37proc (void *cls,
38 const char *name,
39 int isDefault, const struct sockaddr *addr, socklen_t addrlen)
40{
41 int *ok = cls;
42 char buf[INET6_ADDRSTRLEN];
43
44 inet_ntop (addr->sa_family,
45 (addr->sa_family == AF_INET) ?
46 (void *) &((struct sockaddr_in *) addr)->sin_addr :
47 (void *) &((struct sockaddr_in6 *) addr)->sin6_addr,
48 buf, sizeof (buf));
49 if ((0 == strcmp ("::1", buf)) || (0 == strcmp ("127.0.0.1", buf)))
50 *ok = 0;
51 return GNUNET_OK;
52}
53
54static int
55testifcs ()
56{
57 int ret;
58
59 ret = 1;
60 GNUNET_OS_network_interfaces_list (&proc, &ret);
61 return ret;
62}
63
64int
65main (int argc, char *argv[])
66{
67 int errCnt = 0;
68
69 GNUNET_log_setup ("test-os-network", "WARNING", NULL);
70 if (0 != testifcs ())
71 errCnt++;
72 return errCnt;
73}
diff --git a/src/util/test_os_priority.c b/src/util/test_os_priority.c
new file mode 100644
index 000000000..8284af5b8
--- /dev/null
+++ b/src/util/test_os_priority.c
@@ -0,0 +1,61 @@
1/*
2 This file is part of GNUnet.
3 (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_os_priority.c
22 * @brief testcase for util/os_priority.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_os_lib.h"
27
28#define VERBOSE 0
29
30static int
31testprio ()
32{
33 pid_t child;
34 if (GNUNET_OK !=
35 GNUNET_OS_set_process_priority (getpid (),
36 GNUNET_SCHEDULER_PRIORITY_DEFAULT))
37 return 1;
38#ifndef MINGW
39 child = fork ();
40 if (child == 0)
41 {
42 sleep (10);
43 exit (0);
44 }
45 if (GNUNET_OK !=
46 GNUNET_OS_set_process_priority (child, GNUNET_SCHEDULER_PRIORITY_IDLE))
47 return 1;
48#endif
49 return 0;
50}
51
52int
53main (int argc, char *argv[])
54{
55 int errCnt = 0;
56
57 GNUNET_log_setup ("test_os_priority", "WARNING", NULL);
58 if (0 != testprio ())
59 errCnt++;
60 return errCnt;
61}
diff --git a/src/util/test_plugin.c b/src/util/test_plugin.c
new file mode 100644
index 000000000..22bf78b92
--- /dev/null
+++ b/src/util/test_plugin.c
@@ -0,0 +1,64 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_plugin.c
22 * @brief testcase for plugin.c
23 */
24#include "platform.h"
25#include "gnunet_plugin_lib.h"
26
27#define VERBOSE GNUNET_NO
28
29static int
30check ()
31{
32 void *ret;
33
34 GNUNET_log_skip (1);
35 ret = GNUNET_PLUGIN_load ("libgnunet_plugin_missing", NULL);
36 GNUNET_log_skip (0);
37 if (ret != NULL)
38 return 1;
39 ret = GNUNET_PLUGIN_load ("libgnunet_plugin_test", "in");
40 if (ret == NULL)
41 return 1;
42 if (0 != strcmp (ret, "Hello"))
43 return 2;
44 ret = GNUNET_PLUGIN_unload ("libgnunet_plugin_test", "out");
45 if (ret == NULL)
46 return 3;
47 if (0 != strcmp (ret, "World"))
48 return 4;
49 free (ret);
50 return 0;
51}
52
53int
54main (int argc, char *argv[])
55{
56 int ret;
57
58 GNUNET_log_setup ("test-plugin", "WARNING", NULL);
59 ret = check ();
60
61 return ret;
62}
63
64/* end of test_plugin.c */
diff --git a/src/util/test_plugin_plug.c b/src/util/test_plugin_plug.c
new file mode 100644
index 000000000..ca4bef277
--- /dev/null
+++ b/src/util/test_plugin_plug.c
@@ -0,0 +1,42 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_plugin_plug.c
22 * @brief plugin for testing
23 */
24#include "platform.h"
25
26void *
27libgnunet_plugin_test_init (void *arg)
28{
29 if (0 == strcmp (arg, "in"))
30 return "Hello";
31 return NULL;
32}
33
34void *
35libgnunet_plugin_test_done (void *arg)
36{
37 if (0 == strcmp (arg, "out"))
38 return strdup ("World");
39 return NULL;
40}
41
42/* end of test_plugin_plug.c */
diff --git a/src/util/test_program.c b/src/util/test_program.c
new file mode 100644
index 000000000..dee602e2a
--- /dev/null
+++ b/src/util/test_program.c
@@ -0,0 +1,94 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_program.c
22 * @brief tests for program.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_program_lib.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_time_lib.h"
29
30static int setme;
31
32static struct GNUNET_GETOPT_CommandLineOption options[] = {
33 {'n', "name", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme},
34 GNUNET_GETOPT_OPTION_END
35};
36
37/**
38 * Main function that will be run.
39 */
40static void
41runner (void *cls,
42 struct GNUNET_SCHEDULER_Handle *sched,
43 char *const *args,
44 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
45{
46 int *ok = cls;
47 GNUNET_assert (setme == 1);
48 GNUNET_assert (sched != NULL);
49 GNUNET_assert (0 == strcmp (args[0], "extra"));
50 GNUNET_assert (args[1] == NULL);
51 GNUNET_assert (0 == strcmp (cfgfile, "test_program_data.conf"));
52
53 *ok = 0;
54}
55
56
57/**
58 * Main method, starts scheduler with task1,
59 * checks that "ok" is correct at the end.
60 */
61static int
62check ()
63{
64 int ok = 1;
65 char *const argv[] = {
66 "test_program",
67 "-c",
68 "test_program_data.conf",
69 "-L",
70 "WARNING",
71 "-n",
72 "extra",
73 NULL
74 };
75 GNUNET_assert (GNUNET_OK ==
76 GNUNET_PROGRAM_run (7,
77 argv,
78 "test_program",
79 "A test", options, &runner, &ok));
80 return ok;
81}
82
83int
84main (int argc, char *argv[])
85{
86 int ret = 0;
87
88 GNUNET_log_setup ("test_program", "WARNING", NULL);
89 ret += check ();
90
91 return ret;
92}
93
94/* end of test_program.c */
diff --git a/src/util/test_program_data.conf b/src/util/test_program_data.conf
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/util/test_program_data.conf
diff --git a/src/util/test_pseudonym.c b/src/util/test_pseudonym.c
new file mode 100644
index 000000000..6d31c8a07
--- /dev/null
+++ b/src/util/test_pseudonym.c
@@ -0,0 +1,138 @@
1/*
2 This file is part of GNUnet.
3 (C) 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/test_pseudonym.c
23 * @brief testcase for pseudonym.c
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_common.h"
29#include "gnunet_container_lib.h"
30#include "gnunet_crypto_lib.h"
31#include "gnunet_disk_lib.h"
32#include "gnunet_pseudonym_lib.h"
33
34#define CHECK(a) if (!(a)) { ok = GNUNET_NO; GNUNET_break(0); goto FAILURE; }
35
36static struct GNUNET_CONTAINER_MetaData *meta;
37
38static GNUNET_HashCode id1;
39
40static int
41iter (void *cls,
42 const GNUNET_HashCode *
43 pseudonym, const struct GNUNET_CONTAINER_MetaData *md, int rating)
44{
45 int *ok = cls;
46
47 if ((0 == memcmp (pseudonym,
48 &id1,
49 sizeof (GNUNET_HashCode))) &&
50 (!GNUNET_CONTAINER_meta_data_test_equal (md, meta)))
51 {
52 *ok = GNUNET_NO;
53 GNUNET_break (0);
54 }
55 return GNUNET_OK;
56}
57
58static int
59noti_callback (void *cls,
60 const GNUNET_HashCode *
61 pseudonym,
62 const struct GNUNET_CONTAINER_MetaData *md, int rating)
63{
64 int *ret = cls;
65 (*ret)++;
66 return GNUNET_OK;
67}
68
69
70int
71main (int argc, char *argv[])
72{
73 int ok;
74 GNUNET_HashCode rid1;
75 GNUNET_HashCode id2;
76 GNUNET_HashCode rid2;
77 int old;
78 int newVal;
79 struct GNUNET_CONFIGURATION_Handle *cfg;
80 char *name1;
81 char *name2;
82 int notiCount;
83
84 GNUNET_log_setup ("test-psuedonym", "WARNING", NULL);
85 ok = GNUNET_YES;
86 GNUNET_CRYPTO_random_disable_entropy_gathering ();
87 GNUNET_DISK_directory_remove ("/tmp/gnunet-pseudonym-test");
88 cfg = GNUNET_CONFIGURATION_create ();
89 if (-1 == GNUNET_CONFIGURATION_parse (cfg, "test_pseudonym_data.conf"))
90 {
91 GNUNET_CONFIGURATION_destroy (cfg);
92 GNUNET_break (0);
93 return -1;
94 }
95 notiCount = 0;
96 GNUNET_PSEUDONYM_discovery_callback_register (cfg,
97 &noti_callback, &notiCount);
98 /* ACTUAL TEST CODE */
99 old = GNUNET_PSEUDONYM_list_all (cfg, NULL, NULL);
100 meta = GNUNET_CONTAINER_meta_data_create ();
101 GNUNET_CONTAINER_meta_data_insert (meta, EXTRACTOR_TITLE, "test");
102 GNUNET_CRYPTO_hash_create_random (&id1);
103 GNUNET_PSEUDONYM_add (cfg, &id1, meta);
104 CHECK (notiCount == 1);
105 GNUNET_PSEUDONYM_add (cfg, &id1, meta);
106 CHECK (notiCount == 2);
107 newVal = GNUNET_PSEUDONYM_list_all (cfg, &iter, &ok);
108 CHECK (old < newVal);
109 old = newVal;
110 GNUNET_CRYPTO_hash_create_random (&id2);
111 GNUNET_PSEUDONYM_add (cfg, &id2, meta);
112 CHECK (notiCount == 3);
113 newVal = GNUNET_PSEUDONYM_list_all (cfg, &iter, &ok);
114 CHECK (old < newVal);
115 name2 = GNUNET_PSEUDONYM_id_to_name (cfg, &id2);
116 CHECK (name2 != NULL);
117 name1 = GNUNET_PSEUDONYM_id_to_name (cfg, &id1);
118 CHECK (name1 != NULL);
119 CHECK (0 != strcmp (name1, name2));
120 CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name2, &rid2));
121 CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name1, &rid1));
122 CHECK (0 == memcmp (&id1, &rid1, sizeof (GNUNET_HashCode)));
123 CHECK (0 == memcmp (&id2, &rid2, sizeof (GNUNET_HashCode)));
124 CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 0));
125 CHECK (5 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5));
126 CHECK (-5 == GNUNET_PSEUDONYM_rank (cfg, &id1, -10));
127 CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5));
128 GNUNET_free (name1);
129 GNUNET_free (name2);
130 /* END OF TEST CODE */
131FAILURE:
132 GNUNET_PSEUDONYM_discovery_callback_unregister (&noti_callback, &notiCount);
133 GNUNET_CONTAINER_meta_data_destroy (meta);
134 GNUNET_CONFIGURATION_destroy (cfg);
135 return (ok == GNUNET_YES) ? 0 : 1;
136}
137
138/* end of test_pseudoynm.c */
diff --git a/src/util/test_pseudonym_data.conf b/src/util/test_pseudonym_data.conf
new file mode 100644
index 000000000..fd2048586
--- /dev/null
+++ b/src/util/test_pseudonym_data.conf
@@ -0,0 +1,8 @@
1# General settings
2[client]
3HOME = "/tmp/gnunet-pseudonym-test"
4
5[TESTING]
6WEAKRANDOM = YES
7
8
diff --git a/src/util/test_scheduler.c b/src/util/test_scheduler.c
new file mode 100644
index 000000000..fe15e987b
--- /dev/null
+++ b/src/util/test_scheduler.c
@@ -0,0 +1,263 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_scheduler.c
22 * @brief tests for the scheduler
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_scheduler_lib.h"
27#include "gnunet_time_lib.h"
28
29#define VERBOSE GNUNET_NO
30
31static void
32task2 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
33{
34 int *ok = cls;
35 GNUNET_assert (2 == *ok);
36 (*ok) = 3;
37}
38
39static void
40task3 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
41{
42 int *ok = cls;
43 /* t4 should be ready (albeit with lower priority) */
44 GNUNET_assert (1 == GNUNET_SCHEDULER_get_load (tc->sched,
45 GNUNET_SCHEDULER_PRIORITY_COUNT));
46 GNUNET_assert (3 == *ok);
47 (*ok) = 4;
48}
49
50static void
51task4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
52{
53 int *ok = cls;
54 GNUNET_assert (4 == *ok);
55 (*ok) = 5;
56}
57
58static int fds[2];
59
60
61static void
62taskWrt (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
63{
64 static char c;
65 int *ok = cls;
66 GNUNET_assert (6 == *ok);
67 GNUNET_assert (FD_ISSET (fds[1], tc->write_ready));
68 (*ok) = 7;
69 GNUNET_assert (1 == WRITE (fds[1], &c, 1));
70 GNUNET_break (0 == CLOSE (fds[1]));
71}
72
73
74static void
75taskNeverRun (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
76{
77 GNUNET_assert (0);
78}
79
80static void
81taskLast (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
82{
83 int *ok = cls;
84 /* t4 should be ready (albeit with lower priority) */
85 GNUNET_assert (8 == *ok);
86 (*ok) = 0;
87}
88
89static void
90taskRd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
91{
92 static char c;
93 int *ok = cls;
94 GNUNET_assert (7 == *ok);
95 GNUNET_assert (FD_ISSET (fds[0], tc->read_ready));
96 GNUNET_assert (1 == READ (fds[0], &c, 1));
97 GNUNET_break (0 == CLOSE (fds[0]));
98 (*ok) = 8;
99 GNUNET_SCHEDULER_add_after (tc->sched,
100 GNUNET_NO,
101 GNUNET_SCHEDULER_PRIORITY_UI,
102 0, &taskNeverRun, NULL);
103 GNUNET_SCHEDULER_add_after (tc->sched,
104 GNUNET_YES,
105 GNUNET_SCHEDULER_PRIORITY_IDLE,
106 0, &taskLast, cls);
107 GNUNET_SCHEDULER_shutdown (tc->sched);
108}
109
110
111static void
112task5 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
113{
114 int *ok = cls;
115 GNUNET_assert (5 == *ok);
116 (*ok) = 6;
117 GNUNET_assert (0 == pipe (fds));
118 GNUNET_SCHEDULER_add_read (tc->sched,
119 GNUNET_NO,
120 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
121 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
122 GNUNET_TIME_UNIT_FOREVER_REL,
123 fds[0], &taskRd, cls);
124 GNUNET_SCHEDULER_add_write (tc->sched,
125 GNUNET_NO,
126 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
127 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
128 GNUNET_TIME_UNIT_FOREVER_REL,
129 fds[1], &taskWrt, cls);
130}
131
132
133static void
134task1 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
135{
136 int *ok = cls;
137 GNUNET_SCHEDULER_TaskIdentifier t2;
138 GNUNET_SCHEDULER_TaskIdentifier t3;
139 GNUNET_SCHEDULER_TaskIdentifier t4;
140
141 GNUNET_assert (1 == *ok);
142 (*ok) = 2;
143 /* t2 will go first -- prereq for all */
144 t2 = GNUNET_SCHEDULER_add_after (tc->sched,
145 GNUNET_NO,
146 GNUNET_SCHEDULER_PRIORITY_IDLE,
147 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
148 &task2, cls);
149 /* t3 will go before t4: higher priority */
150 t4 = GNUNET_SCHEDULER_add_after (tc->sched,
151 GNUNET_NO,
152 GNUNET_SCHEDULER_PRIORITY_IDLE,
153 t2, &task4, cls);
154 t3 = GNUNET_SCHEDULER_add_delayed (tc->sched,
155 GNUNET_NO,
156 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
157 t2,
158 GNUNET_TIME_relative_get_zero (),
159 &task3, cls);
160 /* t4 will go first: lower prio, but prereq! */
161 GNUNET_SCHEDULER_add_after (tc->sched,
162 GNUNET_NO,
163 GNUNET_SCHEDULER_PRIORITY_UI, t4, &task5, cls);
164}
165
166
167
168/**
169 * Main method, starts scheduler with task1,
170 * checks that "ok" is correct at the end.
171 */
172static int
173check ()
174{
175 int ok;
176
177 ok = 1;
178 GNUNET_SCHEDULER_run (&task1, &ok);
179 return ok;
180}
181
182
183static void
184taskSig (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
185{
186 int *ok = cls;
187 GNUNET_assert (1 == *ok);
188 *ok = 8;
189 GNUNET_SCHEDULER_add_after (tc->sched,
190 GNUNET_NO,
191 GNUNET_SCHEDULER_PRIORITY_UI,
192 0, &taskNeverRun, NULL);
193 GNUNET_SCHEDULER_add_after (tc->sched,
194 GNUNET_YES,
195 GNUNET_SCHEDULER_PRIORITY_UI,
196 0, &taskLast, cls);
197 GNUNET_break (0 == PLIBC_KILL (getpid (), SIGTERM));
198}
199
200
201/**
202 * Main method, starts scheduler with task1,
203 * checks that "ok" is correct at the end.
204 */
205static int
206checkSignal ()
207{
208 int ok;
209
210 ok = 1;
211 GNUNET_SCHEDULER_run (&taskSig, &ok);
212 return ok;
213}
214
215
216
217
218static void
219taskCancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
220{
221 int *ok = cls;
222
223 GNUNET_assert (1 == *ok);
224 *ok = 0;
225 GNUNET_SCHEDULER_cancel (tc->sched,
226 GNUNET_SCHEDULER_add_after (tc->sched,
227 GNUNET_NO,
228 GNUNET_SCHEDULER_PRIORITY_UI,
229 0,
230 &taskNeverRun, NULL));
231}
232
233
234/**
235 * Main method, starts scheduler with task1,
236 * checks that "ok" is correct at the end.
237 */
238static int
239checkCancel ()
240{
241 int ok;
242
243 ok = 1;
244 GNUNET_SCHEDULER_run (&taskCancel, &ok);
245 return ok;
246}
247
248
249
250int
251main (int argc, char *argv[])
252{
253 int ret = 0;
254
255 GNUNET_log_setup ("test_scheduler", "WARNING", NULL);
256 ret += check ();
257 ret += checkSignal ();
258 ret += checkCancel ();
259
260 return ret;
261}
262
263/* end of test_scheduler.c */
diff --git a/src/util/test_scheduler_delay.c b/src/util/test_scheduler_delay.c
new file mode 100644
index 000000000..76cbf94d8
--- /dev/null
+++ b/src/util/test_scheduler_delay.c
@@ -0,0 +1,107 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_scheduler_delay.c
22 * @brief testcase for delay of scheduler, measures how
23 * precise the timers are. Expect values between 10 and 20 ms on
24 * modern machines.
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_time_lib.h"
30
31#define VERBOSE GNUNET_NO
32
33static struct GNUNET_TIME_Absolute target;
34
35static int i;
36
37static unsigned long long cumDelta;
38
39#define INCR 47
40
41#define MAXV 1500
42
43/**
44 * Signature of the main function of a task.
45 *
46 * @param cls closure
47 * @param tc context
48 */
49static void
50test_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
51{
52 struct GNUNET_TIME_Absolute now;
53
54 now = GNUNET_TIME_absolute_get ();
55 if (now.value > target.value)
56 cumDelta += (now.value - target.value);
57 else
58 cumDelta += (target.value - now.value);
59 target =
60 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply
61 (GNUNET_TIME_UNIT_MILLISECONDS, i));
62 fprintf (stderr, ".");
63 if (i > MAXV)
64 {
65 fprintf (stderr, "\n");
66 return;
67 }
68 GNUNET_SCHEDULER_add_delayed (tc->sched,
69 GNUNET_NO,
70 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
71 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
72 GNUNET_TIME_relative_multiply
73 (GNUNET_TIME_UNIT_MILLISECONDS, i),
74 &test_task, NULL);
75 i += INCR;
76}
77
78static int
79check ()
80{
81 target = GNUNET_TIME_absolute_get ();
82 GNUNET_SCHEDULER_run (&test_task, NULL);
83 FPRINTF (stdout,
84 "Sleep precision: %llu ms. ", cumDelta / 1000 / (MAXV / INCR));
85 if (cumDelta <= 10 * MAXV / INCR)
86 fprintf (stdout, "Timer precision is excellent.\n");
87 else if (cumDelta <= 50 * MAXV / INCR) /* 50 ms average deviation */
88 fprintf (stdout, "Timer precision is good.\n");
89 else if (cumDelta > 250 * MAXV / INCR)
90 fprintf (stdout, "Timer precision is awful.\n");
91 else
92 fprintf (stdout, "Timer precision is acceptable.\n");
93 return 0;
94}
95
96int
97main (int argc, char *argv[])
98{
99 int ret;
100
101 GNUNET_log_setup ("test-scheduler-delay", "WARNING", NULL);
102 ret = check ();
103
104 return ret;
105}
106
107/* end of test_scheduler_delay.c */
diff --git a/src/util/test_server.c b/src/util/test_server.c
new file mode 100644
index 000000000..e3df0b456
--- /dev/null
+++ b/src/util/test_server.c
@@ -0,0 +1,287 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_server.c
22 * @brief tests for server.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_scheduler_lib.h"
27#include "gnunet_server_lib.h"
28#include "gnunet_time_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32#define PORT 12435
33
34#define MY_TYPE 128
35#define MY_TYPE2 129
36
37static struct GNUNET_SERVER_Handle *server;
38
39static struct GNUNET_SCHEDULER_Handle *sched;
40
41static void
42recv_fin_cb (void *cls,
43 struct GNUNET_SERVER_Handle *server,
44 struct GNUNET_SERVER_Client *client,
45 const struct GNUNET_MessageHeader *message)
46{
47 int *ok = cls;
48 GNUNET_assert (2 == *ok);
49 GNUNET_SERVER_receive_done (client, GNUNET_OK);
50 *ok = 3;
51}
52
53struct SignalTimeoutContext
54{
55 GNUNET_NETWORK_Receiver cb;
56 void *cb_cls;
57};
58
59
60static void
61signal_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
62{
63 struct SignalTimeoutContext *stctx = cls;
64
65 stctx->cb (stctx->cb_cls, NULL, 0, NULL, 0, 0);
66 GNUNET_free (stctx);
67}
68
69
70static GNUNET_SCHEDULER_TaskIdentifier
71my_receive (void *cls,
72 size_t max,
73 struct GNUNET_TIME_Relative timeout,
74 GNUNET_NETWORK_Receiver receiver, void *receiver_cls)
75{
76 int *ok = cls;
77 struct GNUNET_MessageHeader msg;
78 struct SignalTimeoutContext *stctx;
79 GNUNET_SCHEDULER_TaskIdentifier ret;
80
81 ret = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK;
82 switch (*ok)
83 {
84 case 1:
85 *ok = 2; /* report success */
86 msg.type = htons (MY_TYPE2);
87 msg.size = htons (sizeof (msg));
88 receiver (receiver_cls, &msg, sizeof (msg), NULL, 0, 0);
89 break;
90 case 3:
91 /* called after first receive instantly
92 produced a reply;
93 schedule receiver call with timeout
94 after timeout expires! */
95 *ok = 4;
96 stctx = GNUNET_malloc (sizeof (struct SignalTimeoutContext));
97 stctx->cb = receiver;
98 stctx->cb_cls = receiver_cls;
99 ret = GNUNET_SCHEDULER_add_delayed (sched,
100 GNUNET_NO,
101 GNUNET_SCHEDULER_PRIORITY_KEEP,
102 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
103 timeout, &signal_timeout, stctx);
104 break;
105 default:
106 GNUNET_assert (0);
107 }
108 return ret;
109}
110
111
112static void
113my_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti)
114{
115 GNUNET_SCHEDULER_cancel (sched, ti);
116}
117
118static void *
119my_transmit_ready_cb (void *cls,
120 size_t size,
121 struct GNUNET_TIME_Relative timeout,
122 GNUNET_NETWORK_TransmitReadyNotify notify,
123 void *notify_cls)
124{
125 static int non_null_addr;
126 int *ok = cls;
127 char buf[size];
128 struct GNUNET_MessageHeader msg;
129
130 GNUNET_assert (4 == *ok);
131 GNUNET_assert (size == sizeof (struct GNUNET_MessageHeader));
132 notify (notify_cls, size, buf);
133 msg.type = htons (MY_TYPE);
134 msg.size = htons (sizeof (msg));
135 GNUNET_assert (0 == memcmp (&msg, buf, size));
136 *ok = 5; /* report success */
137 return &non_null_addr;
138}
139
140
141static void
142my_transmit_ready_cancel_cb (void *cls, void *ctx)
143{
144 GNUNET_assert (0);
145}
146
147
148static int
149my_check (void *cls)
150{
151 return GNUNET_YES;
152}
153
154
155static void my_destroy (void *cls);
156
157
158struct CopyContext
159{
160 struct GNUNET_SERVER_Client *client;
161 struct GNUNET_MessageHeader *cpy;
162};
163
164static size_t
165copy_msg (void *cls, size_t size, void *buf)
166{
167 struct CopyContext *ctx = cls;
168 struct GNUNET_MessageHeader *cpy = ctx->cpy;
169 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (cpy->size));
170 GNUNET_assert (size >= ntohs (cpy->size));
171 memcpy (buf, cpy, ntohs (cpy->size));
172 GNUNET_free (cpy);
173 GNUNET_free (ctx);
174 return sizeof (struct GNUNET_MessageHeader);
175}
176
177
178static void
179recv_cb (void *cls,
180 struct GNUNET_SERVER_Handle *server,
181 struct GNUNET_SERVER_Client *argclient,
182 const struct GNUNET_MessageHeader *message)
183{
184 struct GNUNET_SERVER_Client *client;
185 struct CopyContext *cc;
186 struct GNUNET_MessageHeader *cpy;
187
188 GNUNET_assert (argclient == NULL);
189 GNUNET_assert (sizeof (struct GNUNET_MessageHeader) ==
190 ntohs (message->size));
191 GNUNET_assert (MY_TYPE == ntohs (message->type));
192 client = GNUNET_SERVER_connect_callback (server,
193 cls,
194 &my_receive,
195 &my_cancel,
196 &my_transmit_ready_cb,
197 &my_transmit_ready_cancel_cb,
198 &my_check, &my_destroy);
199 cc = GNUNET_malloc (sizeof (struct CopyContext));
200 cc->client = client;
201 cpy = GNUNET_malloc (ntohs (message->size));
202 memcpy (cpy, message, ntohs (message->size));
203 cc->cpy = cpy;
204 GNUNET_assert (NULL !=
205 GNUNET_SERVER_notify_transmit_ready (client,
206 ntohs (message->size),
207 GNUNET_TIME_UNIT_SECONDS,
208 &copy_msg, cc));
209 GNUNET_SERVER_client_drop (client);
210}
211
212
213static struct GNUNET_SERVER_MessageHandler handlers[] = {
214 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
215 {&recv_fin_cb, NULL, MY_TYPE2, sizeof (struct GNUNET_MessageHeader)},
216 {NULL, NULL, 0, 0}
217};
218
219
220static void
221my_destroy (void *cls)
222{
223 int *ok = cls;
224 GNUNET_assert (5 == *ok);
225 *ok = 0; /* report success */
226 /* this will cause us to terminate */
227 GNUNET_SERVER_destroy (server);
228}
229
230
231static void
232task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
233{
234 struct sockaddr_in sa;
235 struct GNUNET_MessageHeader msg;
236
237 sched = tc->sched;
238 memset (&sa, 0, sizeof (sa));
239 sa.sin_family = AF_INET;
240 sa.sin_port = htons (PORT);
241 server = GNUNET_SERVER_create (tc->sched,
242 NULL,
243 NULL,
244 (const struct sockaddr *) &sa,
245 sizeof (sa),
246 1024,
247 GNUNET_TIME_relative_multiply
248 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
249 GNUNET_NO);
250 GNUNET_assert (server != NULL);
251 handlers[0].callback_cls = cls;
252 handlers[1].callback_cls = cls;
253 GNUNET_SERVER_add_handlers (server, handlers);
254 msg.type = htons (MY_TYPE);
255 msg.size = htons (sizeof (msg));
256 GNUNET_SERVER_inject (server, NULL, &msg);
257 memset (&msg, 0, sizeof (msg));
258}
259
260
261/**
262 * Main method, starts scheduler with task1,
263 * checks that "ok" is correct at the end.
264 */
265static int
266check ()
267{
268 int ok;
269
270 ok = 1;
271 GNUNET_SCHEDULER_run (&task, &ok);
272 return ok;
273}
274
275
276int
277main (int argc, char *argv[])
278{
279 int ret = 0;
280
281 GNUNET_log_setup ("test_server", "WARNING", NULL);
282 ret += check ();
283
284 return ret;
285}
286
287/* end of test_server.c */
diff --git a/src/util/test_server_disconnect.c b/src/util/test_server_disconnect.c
new file mode 100644
index 000000000..60604b35c
--- /dev/null
+++ b/src/util/test_server_disconnect.c
@@ -0,0 +1,241 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_server_disconnect.c
22 * @brief tests for server.c and client.c,
23 * specifically client_disconnect
24 */
25#include "platform.h"
26#include "gnunet_common.h"
27#include "gnunet_scheduler_lib.h"
28#include "gnunet_client_lib.h"
29#include "gnunet_server_lib.h"
30#include "gnunet_time_lib.h"
31
32#define VERBOSE GNUNET_NO
33
34#define PORT 22335
35
36#define MY_TYPE 128
37
38
39static struct GNUNET_SERVER_Handle *server;
40
41static struct GNUNET_CLIENT_Connection *client;
42
43static struct GNUNET_SCHEDULER_Handle *sched;
44
45static struct GNUNET_CONFIGURATION_Handle *cfg;
46
47static int ok;
48
49static void
50send_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
51{
52 struct GNUNET_SERVER_Client *argclient = cls;
53 GNUNET_assert (ok == 3);
54 ok++;
55 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
56}
57
58static void
59server_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
60{
61 struct GNUNET_SERVER_Client *argclient = cls;
62 GNUNET_assert (ok == 5);
63 ok++;
64 GNUNET_SERVER_client_disconnect (argclient);
65}
66
67
68static void
69recv_cb (void *cls,
70 struct GNUNET_SERVER_Handle *server,
71 struct GNUNET_SERVER_Client *argclient,
72 const struct GNUNET_MessageHeader *message)
73{
74 void *addr;
75 size_t addrlen;
76 struct sockaddr_in sa;
77 struct sockaddr_in *have;
78
79 GNUNET_assert (GNUNET_OK ==
80 GNUNET_SERVER_client_get_address (argclient,
81 &addr, &addrlen));
82
83 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
84 have = addr;
85 memset (&sa, 0, sizeof (sa));
86 sa.sin_family = AF_INET;
87 sa.sin_port = have->sin_port;
88 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
89 GNUNET_assert (0 == memcmp (&sa, addr, addrlen));
90 GNUNET_free (addr);
91 switch (ok)
92 {
93 case 2:
94 ok++;
95 GNUNET_SCHEDULER_add_delayed (sched,
96 GNUNET_YES,
97 GNUNET_SCHEDULER_PRIORITY_KEEP,
98 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
99 GNUNET_TIME_relative_multiply
100 (GNUNET_TIME_UNIT_MILLISECONDS, 50),
101 &send_done, argclient);
102 break;
103 case 4:
104 ok++;
105 GNUNET_SCHEDULER_add_delayed (sched,
106 GNUNET_YES,
107 GNUNET_SCHEDULER_PRIORITY_KEEP,
108 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
109 GNUNET_TIME_relative_multiply
110 (GNUNET_TIME_UNIT_MILLISECONDS, 50),
111 &server_disconnect, argclient);
112 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
113 break;
114 default:
115 GNUNET_assert (0);
116 }
117
118}
119
120static void
121disconnect_notify (void *cls, const struct GNUNET_MessageHeader *msg)
122{
123 GNUNET_assert (msg == NULL);
124 GNUNET_assert (ok == 7);
125 ok = 0;
126 GNUNET_CLIENT_disconnect (client);
127 GNUNET_SCHEDULER_shutdown (sched);
128 GNUNET_CONFIGURATION_destroy (cfg);
129}
130
131
132/**
133 * Functions with this signature are called whenever a client
134 * is disconnected on the network level.
135 *
136 * @param cls closure
137 * @param client identification of the client
138 */
139static void
140notify_disconnect (void *cls, struct GNUNET_SERVER_Client *clientarg)
141{
142 GNUNET_assert (ok == 6);
143 ok++;
144 GNUNET_CLIENT_receive (client,
145 &disconnect_notify,
146 NULL, GNUNET_TIME_UNIT_FOREVER_REL);
147}
148
149
150static size_t
151notify_ready (void *cls, size_t size, void *buf)
152{
153 struct GNUNET_MessageHeader *msg;
154
155 GNUNET_assert (size >= 256);
156 GNUNET_assert (1 == ok);
157 ok++;
158 msg = buf;
159 msg->type = htons (MY_TYPE);
160 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
161 msg++;
162 msg->type = htons (MY_TYPE);
163 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
164 return 2 * sizeof (struct GNUNET_MessageHeader);
165}
166
167
168static struct GNUNET_SERVER_MessageHandler handlers[] = {
169 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
170 {NULL, NULL, 0, 0}
171};
172
173
174static void
175task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
176{
177 struct sockaddr_in sa;
178
179 sched = tc->sched;
180 memset (&sa, 0, sizeof (sa));
181 sa.sin_family = AF_INET;
182 sa.sin_port = htons (PORT);
183 server = GNUNET_SERVER_create (tc->sched,
184 NULL,
185 NULL,
186 (const struct sockaddr *) &sa,
187 sizeof (sa),
188 1024,
189 GNUNET_TIME_relative_multiply
190 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
191 GNUNET_NO);
192 GNUNET_assert (server != NULL);
193 handlers[0].callback_cls = cls;
194 GNUNET_SERVER_add_handlers (server, handlers);
195 GNUNET_SERVER_disconnect_notify (server, &notify_disconnect, cls);
196 cfg = GNUNET_CONFIGURATION_create ();
197 GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT);
198 GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME",
199 "localhost");
200 client = GNUNET_CLIENT_connect (tc->sched, "test", cfg);
201 GNUNET_assert (client != NULL);
202 GNUNET_CLIENT_notify_transmit_ready (client,
203 256,
204 GNUNET_TIME_relative_multiply
205 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
206 &notify_ready, NULL);
207}
208
209
210/**
211 * Main method, starts scheduler with task1,
212 * checks that "ok" is correct at the end.
213 */
214static int
215check ()
216{
217
218 ok = 1;
219 GNUNET_SCHEDULER_run (&task, NULL);
220 return ok;
221}
222
223
224int
225main (int argc, char *argv[])
226{
227 int ret = 0;
228
229 GNUNET_log_setup ("test_server_disconnect",
230#if VERBOSE
231 "DEBUG",
232#else
233 "WARNING",
234#endif
235 NULL);
236 ret += check ();
237
238 return ret;
239}
240
241/* end of test_server_disconnect.c */
diff --git a/src/util/test_server_with_client.c b/src/util/test_server_with_client.c
new file mode 100644
index 000000000..4ca2b5bc3
--- /dev/null
+++ b/src/util/test_server_with_client.c
@@ -0,0 +1,215 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_server_with_client.c
22 * @brief tests for server.c and client.c,
23 * specifically disconnect_notify,
24 * client_get_address and receive_done (resume processing)
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_scheduler_lib.h"
29#include "gnunet_client_lib.h"
30#include "gnunet_server_lib.h"
31#include "gnunet_time_lib.h"
32
33#define VERBOSE GNUNET_NO
34
35#define PORT 22335
36
37#define MY_TYPE 128
38
39
40static struct GNUNET_SERVER_Handle *server;
41
42static struct GNUNET_CLIENT_Connection *client;
43
44static struct GNUNET_SCHEDULER_Handle *sched;
45
46static struct GNUNET_CONFIGURATION_Handle *cfg;
47
48static int ok;
49
50static void
51send_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
52{
53 struct GNUNET_SERVER_Client *argclient = cls;
54 GNUNET_assert (ok == 3);
55 ok++;
56 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
57}
58
59
60static void
61recv_cb (void *cls,
62 struct GNUNET_SERVER_Handle *server,
63 struct GNUNET_SERVER_Client *argclient,
64 const struct GNUNET_MessageHeader *message)
65{
66 void *addr;
67 size_t addrlen;
68 struct sockaddr_in sa;
69 struct sockaddr_in *have;
70
71 GNUNET_assert (GNUNET_OK ==
72 GNUNET_SERVER_client_get_address (argclient,
73 &addr, &addrlen));
74
75 GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
76 have = addr;
77 memset (&sa, 0, sizeof (sa));
78 sa.sin_family = AF_INET;
79 sa.sin_port = have->sin_port;
80 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
81 GNUNET_assert (0 == memcmp (&sa, addr, addrlen));
82 GNUNET_free (addr);
83 switch (ok)
84 {
85 case 2:
86 ok++;
87 GNUNET_SCHEDULER_add_delayed (sched,
88 GNUNET_YES,
89 GNUNET_SCHEDULER_PRIORITY_KEEP,
90 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
91 GNUNET_TIME_relative_multiply
92 (GNUNET_TIME_UNIT_MILLISECONDS, 50),
93 &send_done, argclient);
94 break;
95 case 4:
96 ok++;
97 GNUNET_CLIENT_disconnect (client);
98 GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
99 break;
100 default:
101 GNUNET_assert (0);
102 }
103
104}
105
106
107/**
108 * Functions with this signature are called whenever a client
109 * is disconnected on the network level.
110 *
111 * @param cls closure
112 * @param client identification of the client
113 */
114static void
115notify_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
116{
117 GNUNET_assert (ok == 5);
118 ok = 0;
119 GNUNET_SCHEDULER_shutdown (sched);
120 GNUNET_CONFIGURATION_destroy (cfg);
121}
122
123
124static size_t
125notify_ready (void *cls, size_t size, void *buf)
126{
127 struct GNUNET_MessageHeader *msg;
128
129 GNUNET_assert (size >= 256);
130 GNUNET_assert (1 == ok);
131 ok++;
132 msg = buf;
133 msg->type = htons (MY_TYPE);
134 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
135 msg++;
136 msg->type = htons (MY_TYPE);
137 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
138 return 2 * sizeof (struct GNUNET_MessageHeader);
139}
140
141
142static struct GNUNET_SERVER_MessageHandler handlers[] = {
143 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
144 {NULL, NULL, 0, 0}
145};
146
147
148static void
149task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
150{
151 struct sockaddr_in sa;
152
153 sched = tc->sched;
154 memset (&sa, 0, sizeof (sa));
155 sa.sin_family = AF_INET;
156 sa.sin_port = htons (PORT);
157 server = GNUNET_SERVER_create (tc->sched,
158 NULL,
159 NULL,
160 (const struct sockaddr *) &sa,
161 sizeof (sa),
162 1024,
163 GNUNET_TIME_relative_multiply
164 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
165 GNUNET_NO);
166 GNUNET_assert (server != NULL);
167 handlers[0].callback_cls = cls;
168 GNUNET_SERVER_add_handlers (server, handlers);
169 GNUNET_SERVER_disconnect_notify (server, &notify_disconnect, cls);
170 cfg = GNUNET_CONFIGURATION_create ();
171 GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT);
172 GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME",
173 "localhost");
174 client = GNUNET_CLIENT_connect (tc->sched, "test", cfg);
175 GNUNET_assert (client != NULL);
176 GNUNET_CLIENT_notify_transmit_ready (client,
177 256,
178 GNUNET_TIME_relative_multiply
179 (GNUNET_TIME_UNIT_MILLISECONDS, 250),
180 &notify_ready, NULL);
181}
182
183
184/**
185 * Main method, starts scheduler with task1,
186 * checks that "ok" is correct at the end.
187 */
188static int
189check ()
190{
191
192 ok = 1;
193 GNUNET_SCHEDULER_run (&task, NULL);
194 return ok;
195}
196
197
198int
199main (int argc, char *argv[])
200{
201 int ret = 0;
202
203 GNUNET_log_setup ("test_server_with_client",
204#if VERBOSE
205 "DEBUG",
206#else
207 "WARNING",
208#endif
209 NULL);
210 ret += check ();
211
212 return ret;
213}
214
215/* end of test_server_with_client.c */
diff --git a/src/util/test_service.c b/src/util/test_service.c
new file mode 100644
index 000000000..b5cf0805e
--- /dev/null
+++ b/src/util/test_service.c
@@ -0,0 +1,337 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_service.c
22 * @brief tests for service.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_client_lib.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_program_lib.h"
29#include "gnunet_service_lib.h"
30#include "gnunet_scheduler_lib.h"
31#include "gnunet_time_lib.h"
32
33#define VERBOSE GNUNET_NO
34
35#define PORT 12435
36
37#define MY_TYPE 256
38
39static struct GNUNET_SCHEDULER_Handle *sched;
40
41static struct GNUNET_SERVICE_Context *sctx;
42
43static void
44end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
45{
46 struct GNUNET_CLIENT_Connection *client = cls;
47
48 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down service\n");
49 GNUNET_CLIENT_service_shutdown (client);
50 GNUNET_CLIENT_disconnect (client);
51 if (sctx != NULL)
52 GNUNET_SERVICE_stop (sctx);
53}
54
55
56static size_t
57build_msg (void *cls, size_t size, void *buf)
58{
59 struct GNUNET_CLIENT_Connection *client = cls;
60 struct GNUNET_MessageHeader *msg = buf;
61
62 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected, transmitting\n");
63 GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
64 msg->type = htons (MY_TYPE);
65 msg->size = htons (sizeof (msg));
66 GNUNET_SCHEDULER_add_continuation (sched,
67 GNUNET_YES,
68 &end_it,
69 client,
70 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
71 return sizeof (struct GNUNET_MessageHeader);
72}
73
74static void
75ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
76{
77 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
78 struct GNUNET_CLIENT_Connection *client;
79
80 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service confirmed running\n");
81 sched = tc->sched;
82 GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE));
83 client = GNUNET_CLIENT_connect (tc->sched, "test_service", cfg);
84 GNUNET_assert (client != NULL);
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "Client connecting, waiting to transmit\n");
87 GNUNET_CLIENT_notify_transmit_ready (client,
88 sizeof (struct GNUNET_MessageHeader),
89 GNUNET_TIME_UNIT_SECONDS,
90 &build_msg, client);
91}
92
93static void
94recv_cb (void *cls,
95 struct GNUNET_SERVER_Handle *server,
96 struct GNUNET_SERVER_Client *client,
97 const struct GNUNET_MessageHeader *message)
98{
99 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving client message...\n");
100 GNUNET_SERVER_receive_done (client, GNUNET_OK);
101}
102
103static struct GNUNET_SERVER_MessageHandler myhandlers[] = {
104 {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
105 {NULL, NULL, 0, 0}
106};
107
108static void
109runner (void *cls,
110 struct GNUNET_SCHEDULER_Handle *sched,
111 struct GNUNET_SERVER_Handle *server,
112 struct GNUNET_CONFIGURATION_Handle *cfg)
113{
114 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service initializing\n");
115 GNUNET_SERVER_add_handlers (server, myhandlers);
116 GNUNET_CLIENT_service_test (sched,
117 "test_service",
118 cfg, GNUNET_TIME_UNIT_SECONDS, &ready, cfg);
119}
120
121static void
122term (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg)
123{
124 int *ok = cls;
125 *ok = 0;
126}
127
128/**
129 * Main method, starts scheduler with task1,
130 * checks that "ok" is correct at the end.
131 */
132static int
133check ()
134{
135 int ok = 1;
136 char *const argv[] = {
137 "test_service",
138 "-c",
139 "test_service_data.conf",
140 "-L",
141#if VERBOSE
142 "DEBUG",
143#else
144 "WARNING",
145#endif
146 NULL
147 };
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting service\n");
149 GNUNET_assert (GNUNET_OK ==
150 GNUNET_SERVICE_run (5,
151 argv,
152 "test_service",
153 &runner, &ok, &term, &ok));
154 GNUNET_assert (0 == ok);
155 return ok;
156}
157
158static void
159ready6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
160{
161 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
162 struct GNUNET_CLIENT_Connection *client;
163
164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "V6 ready\n");
165 sched = tc->sched;
166 GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE));
167 client = GNUNET_CLIENT_connect (tc->sched, "test_service6", cfg);
168 GNUNET_assert (client != NULL);
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "V6 client connected\n");
170 GNUNET_CLIENT_notify_transmit_ready (client,
171 sizeof (struct GNUNET_MessageHeader),
172 GNUNET_TIME_UNIT_SECONDS,
173 &build_msg, client);
174}
175
176static void
177runner6 (void *cls,
178 struct GNUNET_SCHEDULER_Handle *sched,
179 struct GNUNET_SERVER_Handle *server,
180 struct GNUNET_CONFIGURATION_Handle *cfg)
181{
182 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing v6 service\n");
183 GNUNET_SERVER_add_handlers (server, myhandlers);
184 GNUNET_CLIENT_service_test (sched,
185 "test_service6",
186 cfg, GNUNET_TIME_UNIT_SECONDS, &ready6, cfg);
187}
188
189/**
190 * Main method, starts scheduler with task1,
191 * checks that "ok" is correct at the end.
192 */
193static int
194check6 ()
195{
196 int ok = 1;
197 char *const argv[] = {
198 "test_service6",
199 "-c",
200 "test_service_data.conf",
201 "-L",
202#if VERBOSE
203 "DEBUG",
204#else
205 "WARNING",
206#endif
207 NULL
208 };
209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting v6 service\n");
210 GNUNET_assert (GNUNET_OK ==
211 GNUNET_SERVICE_run (5,
212 argv,
213 "test_service6",
214 &runner6, &ok, &term, &ok));
215 GNUNET_assert (0 == ok);
216 return ok;
217}
218
219
220/**
221 * Main method, starts scheduler with task1,
222 * checks that "ok" is correct at the end.
223 */
224static int
225check6d ()
226{
227 int ok = 1;
228 char *const argv[] = {
229 "test_service6",
230 "-c",
231 "test_service_data.conf",
232 "-L",
233#if VERBOSE
234 "DEBUG",
235#else
236 "WARNING",
237#endif
238 "-d",
239 NULL
240 };
241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting V6 as daemon\n");
242 GNUNET_assert (GNUNET_OK ==
243 GNUNET_SERVICE_run (6,
244 argv,
245 "test_service6",
246 &runner6, &ok, &term, &ok));
247 GNUNET_break (0 == ok);
248 return ok;
249}
250
251
252static void
253start_stop_main (void *cls,
254 struct GNUNET_SCHEDULER_Handle *sched,
255 char *const *args,
256 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
257{
258 int *ret = cls;
259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260 "Starting service using start method\n");
261 sctx = GNUNET_SERVICE_start ("test_service", sched, cfg);
262 runner (cls, sched, GNUNET_SERVICE_get_server (sctx), cfg);
263 *ret = 0;
264}
265
266
267static int
268check_start_stop ()
269{
270 char *const argv[] = {
271 "test-service-program",
272 "-c",
273 "test_service_data.conf",
274 "-L",
275#if VERBOSE
276 "DEBUG",
277#else
278 "WARNING",
279#endif
280 NULL
281 };
282 const struct GNUNET_GETOPT_CommandLineOption options[] = {
283 GNUNET_GETOPT_OPTION_END
284 };
285 int ret = 1;
286 GNUNET_assert (GNUNET_OK ==
287 GNUNET_PROGRAM_run (5,
288 argv,
289 "test-service-program",
290 "no help",
291 options, &start_stop_main, &ret));
292
293 GNUNET_break (0 == ret);
294 return ret;
295}
296
297
298int
299main (int argc, char *argv[])
300{
301 int ret = 0;
302 int s;
303
304 GNUNET_log_setup ("test-service",
305#if VERBOSE
306 "DEBUG",
307#else
308 "WARNING",
309#endif
310 NULL);
311 ret += check ();
312 ret += check ();
313 s = SOCKET (PF_INET6, SOCK_STREAM, 0);
314 if (s == -1)
315 {
316 if ((errno == ENOBUFS) ||
317 (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES))
318 {
319 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
320 return 1;
321 }
322 fprintf (stderr,
323 "IPv6 support seems to not be available (%s), not testing it!\n",
324 strerror (errno));
325 }
326 else
327 {
328 GNUNET_break (0 == CLOSE (s));
329 ret += check6 ();
330 ret += check6d (); /* with daemonization */
331 }
332 ret += check_start_stop ();
333
334 return ret;
335}
336
337/* end of test_service.c */
diff --git a/src/util/test_service_data.conf b/src/util/test_service_data.conf
new file mode 100644
index 000000000..92c723619
--- /dev/null
+++ b/src/util/test_service_data.conf
@@ -0,0 +1,26 @@
1[test_service]
2PORT=12435
3BINDTO=localhost
4PIDFILE=/tmp/test-service.pid
5TIMEOUT=30000
6MAXBUF=1024
7DISABLEV6=NO
8ACCEPT_FROM=127.0.0.1;
9REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0;
10ACCEPT_FROM6=::1;
11REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40;
12HOSTNAME=localhost
13ALLOW_SHUTDOWN=YES
14
15[test_service6]
16PORT=12435
17PIDFILE=/tmp/test-service.pid
18TIMEOUT=30000
19MAXBUF=1024
20DISABLEV6=NO
21ACCEPT_FROM=127.0.0.1;
22REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0;
23ACCEPT_FROM6=::1;
24REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40;
25HOSTNAME=::1
26ALLOW_SHUTDOWN=YES
diff --git a/src/util/test_strings.c b/src/util/test_strings.c
new file mode 100644
index 000000000..be166e629
--- /dev/null
+++ b/src/util/test_strings.c
@@ -0,0 +1,111 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_strings.c
22 * @brief testcase for strings.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_strings_lib.h"
27
28#define VERBOSE GNUNET_NO
29
30#define WANT(a,b) if (0 != strcmp(a,b)) { fprintf(stderr, "Got `%s', wanted `%s'\n", b, a); GNUNET_free(b); GNUNET_break(0); return 1;} else { GNUNET_free (b); }
31#define WANTB(a,b,l) if (0 != memcmp(a,b,l)) { GNUNET_break(0); return 1;} else { }
32
33static int
34check ()
35{
36 char buf[128];
37 char *r;
38 char *b;
39 struct GNUNET_TIME_Absolute at;
40
41 sprintf (buf, "4%s", _( /* size unit */ "b"));
42 b = GNUNET_STRINGS_byte_size_fancy (4);
43 WANT (buf, b);
44 sprintf (buf, "10%s", _( /* size unit */ "KiB"));
45 b = GNUNET_STRINGS_byte_size_fancy (10240);
46 WANT (buf, b);
47 sprintf (buf, "10%s", _( /* size unit */ "TiB"));
48 b = GNUNET_STRINGS_byte_size_fancy (10240LL * 1024LL * 1024LL * 1024LL);
49 WANT (buf, b);
50 sprintf (buf, "4%s", _( /* time unit */ "ms"));
51 b =
52 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
53 (GNUNET_TIME_UNIT_MILLISECONDS,
54 4));
55 WANT (buf, b);
56 sprintf (buf, "7%s", _( /* time unit */ "s"));
57 b =
58 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
59 (GNUNET_TIME_UNIT_MILLISECONDS,
60 7 * 1000));
61 WANT (buf, b);
62 sprintf (buf, "7%s", _( /* time unit */ "h"));
63 b =
64 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
65 (GNUNET_TIME_UNIT_MILLISECONDS,
66 7 * 60 * 60 * 1000));
67 WANT (buf, b);
68 sprintf (buf, "%s%s", getenv ("HOME"), DIR_SEPARATOR_STR);
69 b = GNUNET_STRINGS_filename_expand ("~");
70 WANT (buf, b);
71 GNUNET_STRINGS_buffer_fill (buf, sizeof (buf), 3, "a", "btx", "c");
72 WANTB ("a\0btx\0c", buf, 8);
73 if (6 != GNUNET_STRINGS_buffer_tokenize (buf, sizeof (buf), 2, &r, &b))
74 return 1;
75 r = GNUNET_strdup (r);
76 WANT ("a", r);
77 b = GNUNET_strdup (b);
78 WANT ("btx", b);
79 if (0 != GNUNET_STRINGS_buffer_tokenize (buf, 2, 2, &r, &b))
80 return 1;
81 at.value = 5000;
82 r = GNUNET_STRINGS_absolute_time_to_string (at);
83 /* r should be something like "Wed Dec 31 17:00:05 1969"
84 where the details of the day and hour depend on the timezone;
85 however, the "0:05 19" should always be there; hence: */
86 if (NULL == strstr (r, "0:05 19"))
87 {
88 fprintf (stderr, "Got %s\n", r);
89 GNUNET_break (0);
90 GNUNET_free (r);
91 return 1;
92 }
93 b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "ASCII");
94 WANT ("TEST", b);
95 b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "unknown");
96 WANT ("TEST", b);
97 GNUNET_free (r);
98 return 0;
99}
100
101int
102main (int argc, char *argv[])
103{
104 int ret;
105
106 GNUNET_log_setup ("test_strings", "ERROR", NULL);
107 ret = check ();
108 return ret;
109}
110
111/* end of test_strings.c */
diff --git a/src/util/test_time.c b/src/util/test_time.c
new file mode 100644
index 000000000..9505952c2
--- /dev/null
+++ b/src/util/test_time.c
@@ -0,0 +1,122 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file util/test_time.c
22 * @brief testcase for time.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_time_lib.h"
27
28#define VERBOSE GNUNET_NO
29
30static int
31check ()
32{
33 struct GNUNET_TIME_Absolute now;
34 struct GNUNET_TIME_AbsoluteNBO nown;
35 struct GNUNET_TIME_Absolute future;
36 struct GNUNET_TIME_Absolute past;
37 struct GNUNET_TIME_Absolute last;
38 struct GNUNET_TIME_Relative rel;
39 struct GNUNET_TIME_RelativeNBO reln;
40 unsigned int i;
41
42 last = now = GNUNET_TIME_absolute_get ();
43 while (now.value == last.value)
44 now = GNUNET_TIME_absolute_get ();
45 GNUNET_assert (now.value > last.value);
46
47 /* test overflow checking in multiply */
48 rel = GNUNET_TIME_UNIT_SECONDS;
49 GNUNET_log_skip (1);
50 for (i = 0; i < 55; i++)
51 rel = GNUNET_TIME_relative_multiply (rel, 2);
52 GNUNET_log_skip (0);
53 GNUNET_assert (rel.value == GNUNET_TIME_UNIT_FOREVER_REL.value);
54
55 /* test infinity-check for relative to absolute */
56 last = GNUNET_TIME_relative_to_absolute (rel);
57 GNUNET_assert (last.value == GNUNET_TIME_UNIT_FOREVER_ABS.value);
58
59 /* check overflow for r2a */
60 rel.value = ((uint64_t) - 1LL) - 1024;
61 GNUNET_log_skip (1);
62 last = GNUNET_TIME_relative_to_absolute (rel);
63 GNUNET_log_skip (0);
64 GNUNET_assert (last.value == GNUNET_TIME_UNIT_FOREVER_ABS.value);
65
66 /* check overflow for relative add */
67 GNUNET_log_skip (1);
68 rel = GNUNET_TIME_relative_add (rel, rel);
69 GNUNET_log_skip (0);
70 GNUNET_assert (rel.value == GNUNET_TIME_UNIT_FOREVER_REL.value);
71
72 /* check relation check in get_duration */
73 future.value = now.value + 1000000;
74 GNUNET_assert (GNUNET_TIME_absolute_get_difference (now, future).value ==
75 1000000);
76 GNUNET_assert (GNUNET_TIME_absolute_get_difference (future, now).value ==
77 0);
78
79 past.value = now.value - 1000000;
80 rel = GNUNET_TIME_absolute_get_duration (future);
81 GNUNET_assert (rel.value == 0);
82 rel = GNUNET_TIME_absolute_get_duration (past);
83 GNUNET_assert (rel.value >= 1000000);
84
85 /* check get remaining */
86 rel = GNUNET_TIME_absolute_get_remaining (now);
87 GNUNET_assert (rel.value == 0);
88 rel = GNUNET_TIME_absolute_get_remaining (past);
89 GNUNET_assert (rel.value == 0);
90 rel = GNUNET_TIME_absolute_get_remaining (future);
91 GNUNET_assert (rel.value > 0);
92 GNUNET_assert (rel.value <= 1000000);
93
94 /* check endianess */
95 reln = GNUNET_TIME_relative_hton (rel);
96 GNUNET_assert (rel.value == GNUNET_TIME_relative_ntoh (reln).value);
97 nown = GNUNET_TIME_absolute_hton (now);
98 GNUNET_assert (now.value == GNUNET_TIME_absolute_ntoh (nown).value);
99
100 /* check absolute addition */
101 future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_SECONDS);
102 GNUNET_assert (future.value == now.value + 1000);
103
104 /* check zero */
105 future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_ZERO);
106 GNUNET_assert (future.value == now.value);
107
108 return 0;
109}
110
111int
112main (int argc, char *argv[])
113{
114 int ret;
115
116 GNUNET_log_setup ("test-time", "WARNING", NULL);
117 ret = check ();
118
119 return ret;
120}
121
122/* end of test_time.c */
diff --git a/src/util/time.c b/src/util/time.c
new file mode 100644
index 000000000..3ae472561
--- /dev/null
+++ b/src/util/time.c
@@ -0,0 +1,286 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/time.c
23 * @author Christian Grothoff
24 * @brief functions for handling time and time arithmetic
25 */
26#include "platform.h"
27#include "gnunet_time_lib.h"
28
29
30/**
31 * Get the current time (works just as "time", just that we use the
32 * unit of time that the cron-jobs use (and is 64 bit)).
33 *
34 * @return the current time
35 */
36struct GNUNET_TIME_Absolute
37GNUNET_TIME_absolute_get ()
38{
39 struct GNUNET_TIME_Absolute ret;
40 struct timeval tv;
41
42 gettimeofday (&tv, NULL);
43 ret.value = tv.tv_sec * 1000 + tv.tv_usec / 1000;
44 return ret;
45}
46
47
48/**
49 * Return relative time of 0ms.
50 */
51struct GNUNET_TIME_Relative
52GNUNET_TIME_relative_get_zero ()
53{
54 static struct GNUNET_TIME_Relative zero;
55 return zero;
56}
57
58/**
59 * Return relative time of 1ms.
60 */
61struct GNUNET_TIME_Relative
62GNUNET_TIME_relative_get_unit ()
63{
64 static struct GNUNET_TIME_Relative one = { 1 };
65 return one;
66}
67
68/**
69 * Return "forever".
70 */
71struct GNUNET_TIME_Relative
72GNUNET_TIME_relative_get_forever ()
73{
74 static struct GNUNET_TIME_Relative forever = { (uint64_t) - 1LL };
75 return forever;
76}
77
78/**
79 * Return "forever".
80 */
81struct GNUNET_TIME_Absolute
82GNUNET_TIME_absolute_get_forever ()
83{
84 static struct GNUNET_TIME_Absolute forever = { (uint64_t) - 1LL };
85 return forever;
86}
87
88/**
89 * Convert relative time to an absolute time in the
90 * future.
91 *
92 * @return timestamp that is "rel" in the future, or FOREVER if rel==FOREVER (or if we would overflow)
93 */
94struct GNUNET_TIME_Absolute
95GNUNET_TIME_relative_to_absolute (struct GNUNET_TIME_Relative rel)
96{
97 struct GNUNET_TIME_Absolute ret;
98 if (rel.value == (uint64_t) - 1LL)
99 return GNUNET_TIME_absolute_get_forever ();
100 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
101 if (rel.value + now.value < rel.value)
102 {
103 GNUNET_break (0); /* overflow... */
104 return GNUNET_TIME_absolute_get_forever ();
105 }
106 ret.value = rel.value + now.value;
107 return ret;
108}
109
110/**
111 * Given a timestamp in the future, how much time
112 * remains until then?
113 *
114 * @return future - now, or 0 if now >= future, or FOREVER if future==FOREVER.
115 */
116struct GNUNET_TIME_Relative
117GNUNET_TIME_absolute_get_remaining (struct GNUNET_TIME_Absolute future)
118{
119 struct GNUNET_TIME_Relative ret;
120 if (future.value == (uint64_t) - 1LL)
121 return GNUNET_TIME_relative_get_forever ();
122 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
123 if (now.value > future.value)
124 return GNUNET_TIME_relative_get_zero ();
125 ret.value = future.value - now.value;
126 return ret;
127}
128
129/**
130 * Compute the time difference between the given start and end times.
131 * Use this function instead of actual subtraction to ensure that
132 * "FOREVER" and overflows are handeled correctly.
133 *
134 * @return 0 if start >= end; FOREVER if end==FOREVER; otherwise end - start
135 */
136struct GNUNET_TIME_Relative
137GNUNET_TIME_absolute_get_difference (struct GNUNET_TIME_Absolute start,
138 struct GNUNET_TIME_Absolute end)
139{
140 struct GNUNET_TIME_Relative ret;
141 if (end.value == (uint64_t) - 1LL)
142 return GNUNET_TIME_relative_get_forever ();
143 if (end.value < start.value)
144 return GNUNET_TIME_relative_get_zero ();
145 ret.value = end.value - start.value;
146 return ret;
147}
148
149/**
150 * Get the duration of an operation as the
151 * difference of the current time and the given start time "hence".
152 *
153 * @return aborts if hence==FOREVER, 0 if hence > now, otherwise now-hence.
154 */
155struct GNUNET_TIME_Relative
156GNUNET_TIME_absolute_get_duration (struct GNUNET_TIME_Absolute hence)
157{
158 struct GNUNET_TIME_Absolute now;
159 struct GNUNET_TIME_Relative ret;
160
161 now = GNUNET_TIME_absolute_get ();
162 GNUNET_assert (hence.value != (uint64_t) - 1LL);
163 if (hence.value > now.value)
164 return GNUNET_TIME_relative_get_zero ();
165 ret.value = now.value - hence.value;
166 return ret;
167}
168
169
170/**
171 * Add a given relative duration to the
172 * given start time.
173 *
174 * @return FOREVER if either argument is FOREVER or on overflow; start+duration otherwise
175 */
176struct GNUNET_TIME_Absolute
177GNUNET_TIME_absolute_add (struct GNUNET_TIME_Absolute start,
178 struct GNUNET_TIME_Relative duration)
179{
180 struct GNUNET_TIME_Absolute ret;
181
182 if ((start.value == (uint64_t) - 1LL) ||
183 (duration.value == (uint64_t) - 1LL))
184 return GNUNET_TIME_absolute_get_forever ();
185 if (start.value + duration.value < start.value)
186 {
187 GNUNET_break (0);
188 return GNUNET_TIME_absolute_get_forever ();
189 }
190 ret.value = start.value + duration.value;
191 return ret;
192}
193
194/**
195 * Multiply relative time by a given factor.
196 *
197 * @return FOREVER if rel=FOREVER or on overflow; otherwise rel*factor
198 */
199struct GNUNET_TIME_Relative
200GNUNET_TIME_relative_multiply (struct GNUNET_TIME_Relative rel,
201 unsigned int factor)
202{
203 struct GNUNET_TIME_Relative ret;
204 if (factor == 0)
205 return GNUNET_TIME_relative_get_zero ();
206 ret.value = rel.value * factor;
207 if (ret.value / factor != rel.value)
208 {
209 GNUNET_break (0);
210 return GNUNET_TIME_relative_get_forever ();
211 }
212 return ret;
213}
214
215/**
216 * Add relative times together.
217 *
218 * @return FOREVER if either argument is FOREVER or on overflow; a1+a2 otherwise
219 */
220struct GNUNET_TIME_Relative
221GNUNET_TIME_relative_add (struct GNUNET_TIME_Relative a1,
222 struct GNUNET_TIME_Relative a2)
223{
224 struct GNUNET_TIME_Relative ret;
225
226 if ((a1.value == (uint64_t) - 1LL) || (a2.value == (uint64_t) - 1LL))
227 return GNUNET_TIME_relative_get_forever ();
228 if (a1.value + a2.value < a1.value)
229 {
230 GNUNET_break (0);
231 return GNUNET_TIME_relative_get_forever ();
232 }
233 ret.value = a1.value + a2.value;
234 return ret;
235}
236
237
238/**
239 * Convert relative time to network byte order.
240 */
241struct GNUNET_TIME_RelativeNBO
242GNUNET_TIME_relative_hton (struct GNUNET_TIME_Relative a)
243{
244 struct GNUNET_TIME_RelativeNBO ret;
245 ret.value = GNUNET_htonll (a.value);
246 return ret;
247}
248
249/**
250 * Convert relative time from network byte order.
251 */
252struct GNUNET_TIME_Relative
253GNUNET_TIME_relative_ntoh (struct GNUNET_TIME_RelativeNBO a)
254{
255 struct GNUNET_TIME_Relative ret;
256 ret.value = GNUNET_ntohll (a.value);
257 return ret;
258
259}
260
261/**
262 * Convert absolute time to network byte order.
263 */
264struct GNUNET_TIME_AbsoluteNBO
265GNUNET_TIME_absolute_hton (struct GNUNET_TIME_Absolute a)
266{
267 struct GNUNET_TIME_AbsoluteNBO ret;
268 ret.value = GNUNET_htonll (a.value);
269 return ret;
270}
271
272/**
273 * Convert absolute time from network byte order.
274 */
275struct GNUNET_TIME_Absolute
276GNUNET_TIME_absolute_ntoh (struct GNUNET_TIME_AbsoluteNBO a)
277{
278 struct GNUNET_TIME_Absolute ret;
279 ret.value = GNUNET_ntohll (a.value);
280 return ret;
281
282}
283
284
285
286/* end of time.c */