aboutsummaryrefslogtreecommitdiff
path: root/src/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/service')
-rw-r--r--src/service/Makefile.am6
-rw-r--r--src/service/arm/.gitignore7
-rw-r--r--src/service/arm/Makefile.am77
-rw-r--r--src/service/arm/arm.conf.in60
-rw-r--r--src/service/arm/arm.h164
-rw-r--r--src/service/arm/arm_api.c1102
-rw-r--r--src/service/arm/arm_monitor_api.c274
-rw-r--r--src/service/arm/gnunet-service-arm.c2227
-rw-r--r--src/service/arm/meson.build86
-rw-r--r--src/service/arm/mockup-service.c123
-rwxr-xr-xsrc/service/arm/mockup_service130
-rw-r--r--src/service/arm/test_arm_api.c258
-rw-r--r--src/service/arm/test_arm_api_data.conf38
-rw-r--r--src/service/arm/test_exponential_backoff.c416
-rw-r--r--src/service/arm/test_gnunet_arm.py.in129
-rw-r--r--src/service/arm/test_gnunet_service_arm.c266
-rw-r--r--src/service/core/.gitignore9
-rw-r--r--src/service/core/Makefile.am127
-rw-r--r--src/service/core/core.conf.in23
-rw-r--r--src/service/core/core.h326
-rw-r--r--src/service/core/core_api.c772
-rw-r--r--src/service/core/core_api_cmd_connecting_peers.c275
-rw-r--r--src/service/core/core_api_monitor_peers.c196
-rw-r--r--src/service/core/gnunet-service-core.c988
-rw-r--r--src/service/core/gnunet-service-core.h198
-rw-r--r--src/service/core/gnunet-service-core_kx.c1934
-rw-r--r--src/service/core/gnunet-service-core_kx.h102
-rw-r--r--src/service/core/gnunet-service-core_sessions.c1028
-rw-r--r--src/service/core/gnunet-service-core_sessions.h183
-rw-r--r--src/service/core/gnunet-service-core_typemap.c370
-rw-r--r--src/service/core/gnunet-service-core_typemap.h162
-rw-r--r--src/service/core/meson.build119
-rw-r--r--src/service/core/test_core_api.c348
-rw-r--r--src/service/core/test_core_api_data.conf11
-rw-r--r--src/service/core/test_core_api_peer1.conf42
-rw-r--r--src/service/core/test_core_api_peer2.conf43
-rw-r--r--src/service/core/test_core_api_reliability.c537
-rw-r--r--src/service/core/test_core_api_send_to_self.c195
-rw-r--r--src/service/core/test_core_api_send_to_self.conf19
-rw-r--r--src/service/core/test_core_api_start_only.c258
-rw-r--r--src/service/core/test_core_defaults.conf22
-rw-r--r--src/service/core/test_core_plugin_cmd_just_run.c524
-rw-r--r--src/service/core/test_core_quota_asymmetric_recv_limited_peer1.conf54
-rw-r--r--src/service/core/test_core_quota_asymmetric_recv_limited_peer2.conf57
-rw-r--r--src/service/core/test_core_quota_asymmetric_send_limit_peer1.conf52
-rw-r--r--src/service/core/test_core_quota_asymmetric_send_limit_peer2.conf61
-rw-r--r--src/service/core/test_core_quota_compliance.c788
-rw-r--r--src/service/core/test_core_quota_peer1.conf58
-rw-r--r--src/service/core/test_core_quota_peer2.conf59
-rwxr-xr-xsrc/service/core/test_core_start_testcase.sh15
-rw-r--r--src/service/identity/.gitignore4
-rw-r--r--src/service/identity/Makefile.am67
-rw-r--r--src/service/identity/gnunet-service-identity.c1035
-rw-r--r--src/service/identity/identity.conf.in17
-rw-r--r--src/service/identity/identity.h226
-rw-r--r--src/service/identity/identity_api.c768
-rw-r--r--src/service/identity/identity_api_lookup.c244
-rw-r--r--src/service/identity/identity_api_suffix_lookup.c246
-rw-r--r--src/service/identity/meson.build60
-rw-r--r--src/service/identity/test_identity.c324
-rw-r--r--src/service/identity/test_identity.conf11
-rwxr-xr-xsrc/service/identity/test_identity_messages.sh50
-rwxr-xr-xsrc/service/identity/test_plugin_rest_identity.sh157
-rwxr-xr-xsrc/service/identity/test_plugin_rest_identity_signature.sh81
-rw-r--r--src/service/rest/.gitignore1
-rw-r--r--src/service/rest/Makefile.am42
-rw-r--r--src/service/rest/gnunet-rest-server.c1381
-rw-r--r--src/service/rest/meson.build58
-rw-r--r--src/service/rest/rest.c99
-rw-r--r--src/service/rest/rest.conf14
70 files changed, 20202 insertions, 1 deletions
diff --git a/src/service/Makefile.am b/src/service/Makefile.am
index d94046cde..737a4fd4d 100644
--- a/src/service/Makefile.am
+++ b/src/service/Makefile.am
@@ -1,2 +1,6 @@
1SUBDIRS = \ 1SUBDIRS = \
2 util 2 util \
3 arm \
4 core \
5 identity \
6 rest
diff --git a/src/service/arm/.gitignore b/src/service/arm/.gitignore
new file mode 100644
index 000000000..b1d66852b
--- /dev/null
+++ b/src/service/arm/.gitignore
@@ -0,0 +1,7 @@
1arm.conf
2mockup-service
3gnunet-service-arm
4test_arm_api
5test_exponential_backoff
6test_gnunet_arm.py
7test_gnunet_service_arm
diff --git a/src/service/arm/Makefile.am b/src/service/arm/Makefile.am
new file mode 100644
index 000000000..32addeef1
--- /dev/null
+++ b/src/service/arm/Makefile.am
@@ -0,0 +1,77 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfg_DATA = \
9 arm.conf
10
11if USE_COVERAGE
12 AM_CFLAGS = --coverage -O0
13 XLIB = -lgcov
14endif
15
16lib_LTLIBRARIES = libgnunetarm.la
17
18libgnunetarm_la_SOURCES = \
19 arm_api.c arm_monitor_api.c arm.h
20libgnunetarm_la_LIBADD = \
21 $(top_builddir)/src/lib/util/libgnunetutil.la \
22 $(GN_LIBINTL) $(XLIB)
23libgnunetarm_la_LDFLAGS = \
24 $(GN_LIB_LDFLAGS) \
25 -version-info 2:0:0
26
27
28noinst_PROGRAMS = \
29 mockup-service
30
31libexec_PROGRAMS = \
32 gnunet-service-arm
33
34gnunet_service_arm_SOURCES = \
35 gnunet-service-arm.c
36gnunet_service_arm_LDADD = \
37 $(top_builddir)/src/lib/util/libgnunetutil.la \
38 $(GN_LIBINTL)
39
40mockup_service_SOURCES = \
41 mockup-service.c
42 mockup_service_LDADD = \
43 $(top_builddir)/src/lib/util/libgnunetutil.la
44mockup_service_LDFLAGS = \
45 $(GN_LIBINTL)
46
47
48check_PROGRAMS = \
49 test_arm_api \
50 test_exponential_backoff \
51 test_gnunet_service_arm
52
53if ENABLE_TEST_RUN
54AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
55TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
56endif
57
58test_arm_api_SOURCES = \
59 test_arm_api.c
60test_arm_api_LDADD = \
61 libgnunetarm.la \
62 $(top_builddir)/src/lib/util/libgnunetutil.la
63
64test_exponential_backoff_SOURCES = \
65 test_exponential_backoff.c
66test_exponential_backoff_LDADD = \
67 libgnunetarm.la \
68 $(top_builddir)/src/lib/util/libgnunetutil.la
69
70test_gnunet_service_arm_SOURCES = \
71 test_gnunet_service_arm.c
72 test_gnunet_service_arm_LDADD = \
73 libgnunetarm.la \
74 $(top_builddir)/src/lib/util/libgnunetutil.la
75
76EXTRA_DIST = \
77 test_arm_api_data.conf
diff --git a/src/service/arm/arm.conf.in b/src/service/arm/arm.conf.in
new file mode 100644
index 000000000..8015e0e15
--- /dev/null
+++ b/src/service/arm/arm.conf.in
@@ -0,0 +1,60 @@
1[arm]
2@UNIXONLY@ PORT = 2087
3HOSTNAME = localhost
4BINARY = gnunet-service-arm
5ACCEPT_FROM = 127.0.0.1;
6ACCEPT_FROM6 = ::1;
7
8# Special case, uses user runtime dir even for per-system service.
9UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-arm.sock
10UNIX_MATCH_UID = YES
11UNIX_MATCH_GID = YES
12
13# In the "-l" option, format characters from 'strftime' are allowed;
14# In the GLOBAL_POSTFIX, "{}" stands for the name of the respective
15# service. Thus the following option would introduce per-service
16# logging with a new log file each day. Note that only the last 3
17# log files are preserved.
18# GLOBAL_POSTFIX = -l $GNUNET_CACHE_HOME/{}-%Y-%m-%d.log
19GLOBAL_PREFIX =
20
21# By default, when launching GNUnet via gnunet-arm, log
22# everything to a simple flat log file.
23OPTIONS = -l $GNUNET_CACHE_HOME/gnunet-%Y-%m-%d.log
24
25# If set to YES, ARM will only start services that are marked as
26# system-level services (and we'll expect a second ARM to be
27# run per-user to run user-level services). Note that in this
28# case you must have manually created a different configuration
29# file with the user where at least this and the START_USER_SERVICES
30# options differ.
31# START_SYSTEM_SERVICES = YES
32
33# If set to YES, ARM will only start services that are marked as
34# per-user services (and we'll expect a system user to run ARM to
35# provide system-level services). Per-user services enable
36# better personalization and privilege separation and in particular
37# ensures that personal data is stored under $HOME, which might
38# be important in a multi-user system (or if $HOME is encrypted
39# and /var/ is not).
40#
41# Note that if you have different ARM services for SYSTEM and USER,
42# and you are not on UNIX, you need to change the PORT option for the
43# USER ARM instances to some free port (counting down from 2085 should
44# be sane).
45#
46# START_USER_SERVICES = YES
47
48# File where we should log per-service resource consumption on exit.
49# RESOURCE_DIAGNOSTICS = resource.log
50
51
52# Name of the user that will be used to provide the service
53# USERNAME =
54# MAXBUF =
55# TIMEOUT =
56# DISABLEV6 =
57# BINDTO =
58# REJECT_FROM =
59# REJECT_FROM6 =
60# PREFIX =
diff --git a/src/service/arm/arm.h b/src/service/arm/arm.h
new file mode 100644
index 000000000..8888e0105
--- /dev/null
+++ b/src/service/arm/arm.h
@@ -0,0 +1,164 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
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/**
31 * This option will turn on the DEBUG loglevel for
32 * all processes controlled by this ARM!
33 */
34#define DEBUG_ARM GNUNET_EXTRA_LOGGING
35
36GNUNET_NETWORK_STRUCT_BEGIN
37
38/**
39 * Status update from ARM to client.
40 */
41struct GNUNET_ARM_StatusMessage
42{
43 /**
44 * Reply to client, of type is #GNUNET_MESSAGE_TYPE_ARM_STATUS.
45 */
46 struct GNUNET_MessageHeader header;
47
48 /**
49 * Status from the 'enum GNUNET_ARM_ServiceStatus'
50 */
51 uint32_t status;
52
53 /* followed by a 0-terminated service name */
54};
55
56struct GNUNET_ARM_Message
57{
58 /**
59 * Reply to client, type is #GNUNET_MESSAGE_TYPE_ARM_RESULT or
60 * #GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT.
61 * OR
62 * Request from client, type is #GNUNET_MESSAGE_TYPE_ARM_START or
63 * #GNUNET_MESSAGE_TYPE_ARM_STOP.
64 */
65 struct GNUNET_MessageHeader header;
66
67 /**
68 * For alignment.
69 */
70 uint32_t reserved;
71
72 /**
73 * ID of a request that is being replied to.
74 * OR
75 * ID of a request that is being sent.
76 */
77 uint64_t request_id;
78
79 /* For requests - followed by a 0-terminated service name */
80};
81
82
83/**
84 * Reply from ARM to client.
85 */
86struct GNUNET_ARM_ResultMessage
87{
88 /**
89 * Reply to client, of type is #GNUNET_MESSAGE_TYPE_ARM_RESULT, with an ID.
90 */
91 struct GNUNET_ARM_Message arm_msg;
92
93 /**
94 * Result from the `enum GNUNET_ARM_Result`
95 */
96 uint32_t result;
97};
98
99struct GNUNET_ARM_ServiceInfoMessage
100{
101 /**
102 * String pool index for the service's name.
103 */
104 uint16_t name_index;
105
106 /**
107 * String pool index for the service's binary.
108 */
109 uint16_t binary_index;
110
111 /**
112 * Last process exit status.
113 */
114 int16_t last_exit_status;
115
116 /**
117 * Padding.
118 */
119 uint16_t padding;
120
121 /**
122 * Status from the 'enum GNUNET_ARM_ServiceStatus'
123 */
124 uint32_t status;
125
126 /**
127 * Time when the service will be restarted, if applicable
128 * to the current status.
129 */
130 struct GNUNET_TIME_AbsoluteNBO restart_at;
131
132 /**
133 * Time when the service was first started, if applicable.
134 */
135 struct GNUNET_TIME_AbsoluteNBO last_started_at;
136};
137
138/**
139 * Reply from ARM to client for the
140 * #GNUNET_MESSAGE_TYPE_ARM_LIST request followed by count
141 * '\0' terminated strings. header->size contains the
142 * total size (including all strings).
143 */
144struct GNUNET_ARM_ListResultMessage
145{
146 /**
147 * Reply to client, of type is #GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
148 * with an ID.
149 */
150 struct GNUNET_ARM_Message arm_msg;
151
152 /**
153 * Number of 'struct GNUNET_ARM_ServiceInfoMessage' that
154 * are at the end of this message.
155 */
156 uint16_t count;
157
158 /* struct GNUNET_ARM_ServiceInfoMessage[count]; */
159 /* pool of 0-terminated strings */
160};
161
162GNUNET_NETWORK_STRUCT_END
163
164#endif
diff --git a/src/service/arm/arm_api.c b/src/service/arm/arm_api.c
new file mode 100644
index 000000000..1360ecf14
--- /dev/null
+++ b/src/service/arm/arm_api.c
@@ -0,0 +1,1102 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2013, 2016, 2019 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file arm/arm_api.c
23 * @brief API for accessing the ARM service
24 * @author Christian Grothoff
25 * @author LRN
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_arm_service.h"
30#include "gnunet_protocols.h"
31#include "arm.h"
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "arm-api", __VA_ARGS__)
34
35
36/**
37 * Entry in a doubly-linked list of operations awaiting for replies
38 * (in-order) from the ARM service.
39 */
40struct GNUNET_ARM_Operation
41{
42 /**
43 * This is a doubly-linked list.
44 */
45 struct GNUNET_ARM_Operation *next;
46
47 /**
48 * This is a doubly-linked list.
49 */
50 struct GNUNET_ARM_Operation *prev;
51
52 /**
53 * ARM handle.
54 */
55 struct GNUNET_ARM_Handle *h;
56
57 /**
58 * Callback for service state change requests.
59 */
60 GNUNET_ARM_ResultCallback result_cont;
61
62 /**
63 * Callback for service list requests.
64 */
65 GNUNET_ARM_ServiceListCallback list_cont;
66
67 /**
68 * Closure for @e result_cont or @e list_cont.
69 */
70 void *cont_cls;
71
72 /**
73 * Task for async completion.
74 */
75 struct GNUNET_SCHEDULER_Task *async;
76
77 /**
78 * Unique ID for the request.
79 */
80 uint64_t id;
81
82 /**
83 * Result of this operation for #notify_starting().
84 */
85 enum GNUNET_ARM_Result starting_ret;
86
87 /**
88 * File descriptor to close on operation stop, if not NULL.
89 */
90 struct GNUNET_DISK_FileHandle *rfd;
91
92 /**
93 * Is this an operation to stop the ARM service?
94 */
95 int is_arm_stop;
96};
97
98
99/**
100 * Handle for interacting with ARM.
101 */
102struct GNUNET_ARM_Handle
103{
104 /**
105 * Our connection to the ARM service.
106 */
107 struct GNUNET_MQ_Handle *mq;
108
109 /**
110 * The configuration that we are using.
111 */
112 const struct GNUNET_CONFIGURATION_Handle *cfg;
113
114 /**
115 * Head of doubly-linked list of pending operations.
116 */
117 struct GNUNET_ARM_Operation *operation_pending_head;
118
119 /**
120 * Tail of doubly-linked list of pending operations.
121 */
122 struct GNUNET_ARM_Operation *operation_pending_tail;
123
124 /**
125 * Callback to invoke on connection/disconnection.
126 */
127 GNUNET_ARM_ConnectionStatusCallback conn_status;
128
129 /**
130 * Closure for @e conn_status.
131 */
132 void *conn_status_cls;
133
134 /**
135 * ARM operation where the goal is to wait for ARM shutdown to
136 * complete. This operation is special in that it waits for an
137 * error on the @e mq. So we complete it by calling the
138 * continuation in the #mq_error_handler(). Note that the operation
139 * is no longer in the @e operation_pending_head DLL once it is
140 * referenced from this field.
141 */
142 struct GNUNET_ARM_Operation *thm;
143
144 /**
145 * ID of the reconnect task (if any).
146 */
147 struct GNUNET_SCHEDULER_Task *reconnect_task;
148
149 /**
150 * Current delay we use for re-trying to connect to core.
151 */
152 struct GNUNET_TIME_Relative retry_backoff;
153
154 /**
155 * Counter for request identifiers. They are used to match replies
156 * from ARM to operations in the @e operation_pending_head DLL.
157 */
158 uint64_t request_id_counter;
159
160 /**
161 * Have we detected that ARM is up?
162 */
163 int currently_up;
164};
165
166
167/**
168 * Connect to arm.
169 *
170 * @param h arm handle
171 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
172 */
173static int
174reconnect_arm (struct GNUNET_ARM_Handle *h);
175
176
177/**
178 * Task scheduled to try to re-connect to arm.
179 *
180 * @param cls the `struct GNUNET_ARM_Handle`
181 */
182static void
183reconnect_arm_task (void *cls)
184{
185 struct GNUNET_ARM_Handle *h = cls;
186
187 h->reconnect_task = NULL;
188 reconnect_arm (h);
189}
190
191
192/**
193 * Close down any existing connection to the ARM service and
194 * try re-establishing it later.
195 *
196 * @param h our handle
197 */
198static void
199reconnect_arm_later (struct GNUNET_ARM_Handle *h)
200{
201 struct GNUNET_ARM_Operation *op;
202
203 if (NULL != h->mq)
204 {
205 GNUNET_MQ_destroy (h->mq);
206 h->mq = NULL;
207 }
208 h->currently_up = GNUNET_NO;
209 GNUNET_assert (NULL == h->reconnect_task);
210 h->reconnect_task =
211 GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
212 &reconnect_arm_task,
213 h);
214 while (NULL != (op = h->operation_pending_head))
215 {
216 if (NULL != op->result_cont)
217 op->result_cont (op->cont_cls,
218 GNUNET_ARM_REQUEST_DISCONNECTED,
219 0);
220 if (NULL != op->list_cont)
221 op->list_cont (op->cont_cls,
222 GNUNET_ARM_REQUEST_DISCONNECTED,
223 0,
224 NULL);
225 GNUNET_ARM_operation_cancel (op);
226 }
227 GNUNET_assert (NULL == h->operation_pending_head);
228 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
229 if (NULL != h->conn_status)
230 h->conn_status (h->conn_status_cls,
231 GNUNET_NO);
232}
233
234
235/**
236 * Find a control message by its unique ID.
237 *
238 * @param h ARM handle
239 * @param id unique message ID to use for the lookup
240 * @return NULL if not found
241 */
242static struct GNUNET_ARM_Operation *
243find_op_by_id (struct GNUNET_ARM_Handle *h,
244 uint64_t id)
245{
246 for (struct GNUNET_ARM_Operation *result = h->operation_pending_head;
247 NULL != result;
248 result = result->next)
249 if (id == result->id)
250 return result;
251 return NULL;
252}
253
254
255/**
256 * Handler for ARM replies.
257 *
258 * @param cls our `struct GNUNET_ARM_Handle`
259 * @param res the message received from the arm service
260 */
261static void
262handle_arm_result (void *cls,
263 const struct GNUNET_ARM_ResultMessage *res)
264{
265 struct GNUNET_ARM_Handle *h = cls;
266 struct GNUNET_ARM_Operation *op;
267 uint64_t id;
268 enum GNUNET_ARM_Result result;
269 GNUNET_ARM_ResultCallback result_cont;
270 void *result_cont_cls;
271
272 id = GNUNET_ntohll (res->arm_msg.request_id);
273 op = find_op_by_id (h,
274 id);
275 if (NULL == op)
276 {
277 LOG (GNUNET_ERROR_TYPE_DEBUG,
278 "Message with unknown id %llu\n",
279 (unsigned long long) id);
280 return;
281 }
282
283 result = (enum GNUNET_ARM_Result) ntohl (res->result);
284 if ( (GNUNET_YES == op->is_arm_stop) &&
285 (GNUNET_ARM_RESULT_STOPPING == result) )
286 {
287 /* special case: if we are stopping 'gnunet-service-arm', we do not just
288 wait for the result message, but also wait for the service to close
289 the connection (and then we have to close our client handle as well);
290 this is done by installing a different receive handler, waiting for
291 the connection to go down */if (NULL != h->thm)
292 {
293 GNUNET_break (0);
294 op->result_cont (h->thm->cont_cls,
295 GNUNET_ARM_REQUEST_SENT_OK,
296 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
297 GNUNET_free (h->thm);
298 }
299 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
300 h->operation_pending_tail,
301 op);
302 h->thm = op;
303 return;
304 }
305 result_cont = op->result_cont;
306 result_cont_cls = op->cont_cls;
307 GNUNET_ARM_operation_cancel (op);
308 if (NULL != result_cont)
309 result_cont (result_cont_cls,
310 GNUNET_ARM_REQUEST_SENT_OK,
311 result);
312}
313
314
315/**
316 * Read from a string pool.
317 *
318 * @param pool_start start of the string pool
319 * @param pool_size size of the string pool
320 * @param str_index index into the string pool
321 * @returns an index into the string pool, or
322 * NULL if the index is out of bounds
323 */
324static const char *
325pool_get (const char *pool_start,
326 size_t pool_size,
327 size_t str_index)
328{
329 const char *str_start;
330 const char *end;
331
332 if (str_index >= pool_size)
333 return NULL;
334 str_start = pool_start + str_index;
335 end = memchr (str_start, 0, pool_size - str_index);
336 if (NULL == end)
337 return NULL;
338 return str_start;
339}
340
341
342/**
343 * Check that list result message is well-formed.
344 *
345 * @param cls our `struct GNUNET_ARM_Handle`
346 * @param lres the message received from the arm service
347 * @return #GNUNET_OK if message is well-formed
348 */
349static int
350check_arm_list_result (void *cls,
351 const struct GNUNET_ARM_ListResultMessage *lres)
352{
353 uint16_t rcount = ntohs (lres->count);
354 uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres);
355 struct GNUNET_ARM_ServiceInfoMessage *ssm;
356 size_t pool_size;
357 char *pool_start;
358
359 (void) cls;
360 if ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) > msize))
361 {
362 GNUNET_break_op (0);
363 return GNUNET_NO;
364 }
365 ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1];
366 pool_start = (char *) (ssm + rcount);
367 pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage));
368 for (unsigned int i = 0; i < rcount; i++)
369 {
370 uint16_t name_index = ntohs (ssm->name_index);
371 uint16_t binary_index = ntohs (ssm->binary_index);
372 if (NULL == pool_get (pool_start,
373 pool_size,
374 name_index))
375 {
376 GNUNET_break_op (0);
377 return GNUNET_NO;
378 }
379 if (NULL == pool_get (pool_start,
380 pool_size,
381 binary_index))
382 {
383 GNUNET_break_op (0);
384 return GNUNET_NO;
385 }
386 ssm++;
387 }
388 return GNUNET_OK;
389}
390
391
392/**
393 * Handler for ARM list replies.
394 *
395 * @param cls our `struct GNUNET_ARM_Handle`
396 * @param lres the message received from the arm service
397 */
398static void
399handle_arm_list_result (void *cls,
400 const struct GNUNET_ARM_ListResultMessage *lres)
401{
402 struct GNUNET_ARM_Handle *h = cls;
403 uint16_t rcount = ntohs (lres->count);
404 uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres);
405 struct GNUNET_ARM_ServiceInfo list[rcount];
406 struct GNUNET_ARM_ServiceInfoMessage *ssm;
407 struct GNUNET_ARM_Operation *op;
408 uint64_t id;
409 size_t pool_size;
410 char *pool_start;
411
412 id = GNUNET_ntohll (lres->arm_msg.request_id);
413 op = find_op_by_id (h, id);
414 if (NULL == op)
415 {
416 LOG (GNUNET_ERROR_TYPE_DEBUG,
417 "Message with unknown id %llu\n",
418 (unsigned long long) id);
419 return;
420 }
421
422 GNUNET_assert ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) <=
423 msize));
424
425 ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1];
426 pool_start = (char *) (ssm + rcount);
427 pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage));
428
429 for (unsigned int i = 0; i < rcount; i++)
430 {
431 uint16_t name_index = ntohs (ssm->name_index);
432 uint16_t binary_index = ntohs (ssm->binary_index);
433 const char *name;
434 const char *binary;
435
436 name = pool_get (pool_start, pool_size, name_index);
437 binary = pool_get (pool_start, pool_size, binary_index);
438 GNUNET_assert (NULL != name);
439 GNUNET_assert (NULL != binary);
440 list[i] = (struct GNUNET_ARM_ServiceInfo) {
441 .name = name,
442 .binary = binary,
443 .status = ntohl (ssm->status),
444 .last_started_at = GNUNET_TIME_absolute_ntoh (ssm->last_started_at),
445 .restart_at = GNUNET_TIME_absolute_ntoh (ssm->restart_at),
446 .last_exit_status = ntohs (ssm->last_exit_status),
447 };
448 ssm++;
449 }
450 if (NULL != op->list_cont)
451 op->list_cont (op->cont_cls,
452 GNUNET_ARM_REQUEST_SENT_OK,
453 rcount,
454 list);
455 GNUNET_ARM_operation_cancel (op);
456}
457
458
459/**
460 * Receive confirmation from test, ARM service is up.
461 *
462 * @param cls closure with the `struct GNUNET_ARM_Handle`
463 * @param msg message received
464 */
465static void
466handle_confirm (void *cls,
467 const struct GNUNET_MessageHeader *msg)
468{
469 struct GNUNET_ARM_Handle *h = cls;
470
471 (void) msg;
472 LOG (GNUNET_ERROR_TYPE_DEBUG,
473 "Got confirmation from ARM that we are up!\n");
474 if (GNUNET_NO == h->currently_up)
475 {
476 h->currently_up = GNUNET_YES;
477 if (NULL != h->conn_status)
478 h->conn_status (h->conn_status_cls, GNUNET_YES);
479 }
480}
481
482
483/**
484 * Generic error handler, called with the appropriate error code and
485 * the same closure specified at the creation of the message queue.
486 * Not every message queue implementation supports an error handler.
487 *
488 * @param cls closure with the `struct GNUNET_ARM_Handle *`
489 * @param error error code
490 */
491static void
492mq_error_handler (void *cls,
493 enum GNUNET_MQ_Error error)
494{
495 struct GNUNET_ARM_Handle *h = cls;
496 struct GNUNET_ARM_Operation *op;
497
498 (void) error;
499 h->currently_up = GNUNET_NO;
500 if (NULL != (op = h->thm))
501 {
502 h->thm = NULL;
503 op->result_cont (op->cont_cls,
504 GNUNET_ARM_REQUEST_SENT_OK,
505 GNUNET_ARM_RESULT_STOPPED);
506 GNUNET_free (op);
507 }
508 reconnect_arm_later (h);
509}
510
511
512/**
513 * Connect to arm.
514 *
515 * @param h arm handle
516 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
517 */
518static int
519reconnect_arm (struct GNUNET_ARM_Handle *h)
520{
521 struct GNUNET_MQ_MessageHandler handlers[] = {
522 GNUNET_MQ_hd_fixed_size (arm_result,
523 GNUNET_MESSAGE_TYPE_ARM_RESULT,
524 struct GNUNET_ARM_ResultMessage,
525 h),
526 GNUNET_MQ_hd_var_size (arm_list_result,
527 GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
528 struct GNUNET_ARM_ListResultMessage,
529 h),
530 GNUNET_MQ_hd_fixed_size (confirm,
531 GNUNET_MESSAGE_TYPE_ARM_TEST,
532 struct GNUNET_MessageHeader,
533 h),
534 GNUNET_MQ_handler_end ()
535 };
536 struct GNUNET_MessageHeader *test;
537 struct GNUNET_MQ_Envelope *env;
538
539 if (NULL != h->mq)
540 return GNUNET_OK;
541 GNUNET_assert (GNUNET_NO == h->currently_up);
542 h->mq = GNUNET_CLIENT_connect (h->cfg,
543 "arm",
544 handlers,
545 &mq_error_handler,
546 h);
547 if (NULL == h->mq)
548 {
549 LOG (GNUNET_ERROR_TYPE_DEBUG,
550 "GNUNET_CLIENT_connect returned NULL\n");
551 if (NULL != h->conn_status)
552 h->conn_status (h->conn_status_cls,
553 GNUNET_SYSERR);
554 return GNUNET_SYSERR;
555 }
556 LOG (GNUNET_ERROR_TYPE_DEBUG,
557 "Sending TEST message to ARM\n");
558 env = GNUNET_MQ_msg (test,
559 GNUNET_MESSAGE_TYPE_ARM_TEST);
560 GNUNET_MQ_send (h->mq, env);
561 return GNUNET_OK;
562}
563
564
565/**
566 * Set up a context for communicating with ARM, then
567 * start connecting to the ARM service using that context.
568 *
569 * @param cfg configuration to use (needed to contact ARM;
570 * the ARM service may internally use a different
571 * configuration to determine how to start the service).
572 * @param conn_status will be called when connecting/disconnecting
573 * @param conn_status_cls closure for @a conn_status
574 * @return context to use for further ARM operations, NULL on error.
575 */
576struct GNUNET_ARM_Handle *
577GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
578 GNUNET_ARM_ConnectionStatusCallback conn_status,
579 void *conn_status_cls)
580{
581 struct GNUNET_ARM_Handle *h;
582
583 h = GNUNET_new (struct GNUNET_ARM_Handle);
584 h->cfg = cfg;
585 h->conn_status = conn_status;
586 h->conn_status_cls = conn_status_cls;
587 if (GNUNET_OK != reconnect_arm (h))
588 {
589 GNUNET_free (h);
590 return NULL;
591 }
592 return h;
593}
594
595
596/**
597 * Disconnect from the ARM service (if connected) and destroy the context.
598 *
599 * @param h the handle that was being used
600 */
601void
602GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
603{
604 struct GNUNET_ARM_Operation *op;
605
606 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
607 while (NULL != (op = h->operation_pending_head))
608 {
609 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
610 h->operation_pending_tail,
611 op);
612 if (NULL != op->result_cont)
613 op->result_cont (op->cont_cls,
614 GNUNET_ARM_REQUEST_DISCONNECTED,
615 0);
616 if (NULL != op->list_cont)
617 op->list_cont (op->cont_cls,
618 GNUNET_ARM_REQUEST_DISCONNECTED,
619 0,
620 NULL);
621 if (NULL != op->async)
622 {
623 GNUNET_SCHEDULER_cancel (op->async);
624 op->async = NULL;
625 }
626 GNUNET_free (op);
627 }
628 if (NULL != h->mq)
629 {
630 GNUNET_MQ_destroy (h->mq);
631 h->mq = NULL;
632 }
633 if (NULL != h->reconnect_task)
634 {
635 GNUNET_SCHEDULER_cancel (h->reconnect_task);
636 h->reconnect_task = NULL;
637 }
638 GNUNET_free (h);
639}
640
641
642/**
643 * A client specifically requested starting of ARM itself.
644 * Starts the ARM service.
645 *
646 * @param h the handle with configuration details
647 * @param std_inheritance inheritance of std streams
648 * @param sigfd socket to pass to ARM for signalling
649 * @return operation status code
650 */
651static enum GNUNET_ARM_Result
652start_arm_service (struct GNUNET_ARM_Handle *h,
653 enum GNUNET_OS_InheritStdioFlags std_inheritance,
654 struct GNUNET_DISK_FileHandle *sigfd)
655{
656 struct GNUNET_OS_Process *proc;
657 char *cbinary;
658 char *binary;
659 char *quotedbinary;
660 char *config;
661 char *loprefix;
662 char *lopostfix;
663 int ld[2];
664 int *lsocks;
665
666 if (NULL == sigfd)
667 {
668 lsocks = NULL;
669 }
670 else
671 {
672 ld[0] = sigfd->fd;
673 ld[1] = -1;
674 lsocks = ld;
675 }
676 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
677 "arm",
678 "PREFIX",
679 &loprefix))
680 loprefix = GNUNET_strdup ("");
681 else
682 loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, loprefix);
683 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
684 "arm",
685 "OPTIONS",
686 &lopostfix))
687 lopostfix = GNUNET_strdup ("");
688 else
689 lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg,
690 lopostfix);
691 if (GNUNET_OK !=
692 GNUNET_CONFIGURATION_get_value_string (h->cfg,
693 "arm",
694 "BINARY",
695 &cbinary))
696 {
697 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
698 "arm",
699 "BINARY");
700 GNUNET_free (loprefix);
701 GNUNET_free (lopostfix);
702 return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
703 }
704 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg,
705 "arm",
706 "CONFIG",
707 &config))
708 config = NULL;
709 binary = GNUNET_OS_get_libexec_binary_path (cbinary);
710 GNUNET_asprintf (&quotedbinary,
711 "\"%s\"",
712 binary);
713 GNUNET_free (cbinary);
714 if ( (GNUNET_YES ==
715 GNUNET_CONFIGURATION_have_value (h->cfg,
716 "TESTING",
717 "WEAKRANDOM")) &&
718 (GNUNET_YES ==
719 GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
720 "TESTING",
721 "WEAKRANDOM")) &&
722 (GNUNET_NO ==
723 GNUNET_CONFIGURATION_have_value (h->cfg,
724 "TESTING",
725 "HOSTFILE")) )
726 {
727 /* Means we are ONLY running locally */
728 /* we're clearly running a test, don't daemonize */
729 if (NULL == config)
730 proc = GNUNET_OS_start_process_s (std_inheritance,
731 lsocks,
732 loprefix,
733 quotedbinary,
734 /* no daemonization! */
735 lopostfix,
736 NULL);
737 else
738 proc = GNUNET_OS_start_process_s (std_inheritance,
739 lsocks,
740 loprefix,
741 quotedbinary,
742 "-c",
743 config,
744 /* no daemonization! */
745 lopostfix,
746 NULL);
747 }
748 else
749 {
750 if (NULL == config)
751 proc = GNUNET_OS_start_process_s (std_inheritance,
752 lsocks,
753 loprefix,
754 quotedbinary,
755 "-d", /* do daemonize */
756 lopostfix,
757 NULL);
758 else
759 proc = GNUNET_OS_start_process_s (std_inheritance,
760 lsocks,
761 loprefix,
762 quotedbinary,
763 "-c",
764 config,
765 "-d", /* do daemonize */
766 lopostfix,
767 NULL);
768 }
769 GNUNET_free (binary);
770 GNUNET_free (quotedbinary);
771 if (NULL != config)
772 GNUNET_free (config);
773 GNUNET_free (loprefix);
774 GNUNET_free (lopostfix);
775 if (NULL == proc)
776 return GNUNET_ARM_RESULT_START_FAILED;
777 GNUNET_OS_process_destroy (proc);
778 return GNUNET_ARM_RESULT_STARTING;
779}
780
781
782/**
783 * Abort an operation. Only prevents the callback from being
784 * called, the operation may still complete.
785 *
786 * @param op operation to cancel
787 */
788void
789GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
790{
791 struct GNUNET_ARM_Handle *h = op->h;
792
793 if (NULL != op->async)
794 {
795 GNUNET_SCHEDULER_cancel (op->async);
796 op->async = NULL;
797 }
798 if (NULL != op->rfd)
799 {
800 GNUNET_DISK_file_close (op->rfd);
801 op->rfd = NULL;
802 }
803 if (h->thm == op)
804 {
805 op->result_cont = NULL;
806 return;
807 }
808 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
809 h->operation_pending_tail,
810 op);
811 GNUNET_free (op);
812}
813
814
815/**
816 * Start or stop a service.
817 *
818 * @param h handle to ARM
819 * @param service_name name of the service
820 * @param cb callback to invoke when service is ready
821 * @param cb_cls closure for @a cb
822 * @param type type of the request
823 * @return handle to queue, NULL on error
824 */
825static struct GNUNET_ARM_Operation *
826change_service (struct GNUNET_ARM_Handle *h,
827 const char *service_name,
828 GNUNET_ARM_ResultCallback cb,
829 void *cb_cls,
830 uint16_t type)
831{
832 struct GNUNET_ARM_Operation *op;
833 size_t slen;
834 struct GNUNET_MQ_Envelope *env;
835 struct GNUNET_ARM_Message *msg;
836
837 slen = strlen (service_name) + 1;
838 if (slen + sizeof(struct GNUNET_ARM_Message) >= GNUNET_MAX_MESSAGE_SIZE)
839 {
840 GNUNET_break (0);
841 return NULL;
842 }
843 if (0 == h->request_id_counter)
844 h->request_id_counter++;
845 op = GNUNET_new (struct GNUNET_ARM_Operation);
846 op->h = h;
847 op->result_cont = cb;
848 op->cont_cls = cb_cls;
849 op->id = h->request_id_counter++;
850 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
851 h->operation_pending_tail,
852 op);
853 env = GNUNET_MQ_msg_extra (msg, slen, type);
854 msg->reserved = htonl (0);
855 msg->request_id = GNUNET_htonll (op->id);
856 GNUNET_memcpy (&msg[1], service_name, slen);
857 GNUNET_MQ_send (h->mq, env);
858 return op;
859}
860
861
862/**
863 * Task run to notify application that ARM is already up.
864 *
865 * @param cls the operation that asked ARM to be started
866 */
867static void
868notify_running (void *cls)
869{
870 struct GNUNET_ARM_Operation *op = cls;
871 struct GNUNET_ARM_Handle *h = op->h;
872
873 op->async = NULL;
874 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
875 h->operation_pending_tail,
876 op);
877 if (NULL != op->result_cont)
878 op->result_cont (op->cont_cls,
879 GNUNET_ARM_REQUEST_SENT_OK,
880 GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
881 if ( (GNUNET_YES == h->currently_up) &&
882 (NULL != h->conn_status) )
883 h->conn_status (h->conn_status_cls,
884 GNUNET_YES);
885 GNUNET_free (op);
886}
887
888
889/**
890 * Task run to notify application that ARM is being started.
891 *
892 * @param cls the operation that asked ARM to be started
893 */
894static void
895notify_starting (void *cls)
896{
897 struct GNUNET_ARM_Operation *op = cls;
898 struct GNUNET_ARM_Handle *h = op->h;
899
900 op->async = NULL;
901 LOG (GNUNET_ERROR_TYPE_DEBUG,
902 "Notifying client that we started the ARM service\n");
903 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
904 h->operation_pending_tail,
905 op);
906 if (NULL != op->result_cont)
907 op->result_cont (op->cont_cls,
908 GNUNET_ARM_REQUEST_SENT_OK,
909 op->starting_ret);
910 GNUNET_DISK_file_close(op->rfd);
911 GNUNET_free (op);
912}
913
914
915/**
916 * Request for a service to be started.
917 *
918 * @param h handle to ARM
919 * @param service_name name of the service
920 * @param std_inheritance inheritance of std streams
921 * @param cont callback to invoke after request is sent or not sent
922 * @param cont_cls closure for @a cont
923 * @return handle for the operation, NULL on error
924 */
925struct GNUNET_ARM_Operation *
926GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
927 const char *service_name,
928 enum GNUNET_OS_InheritStdioFlags
929 std_inheritance,
930 GNUNET_ARM_ResultCallback cont,
931 void *cont_cls)
932{
933 struct GNUNET_ARM_Operation *op;
934 enum GNUNET_ARM_Result ret;
935 struct GNUNET_DISK_PipeHandle *sig;
936 struct GNUNET_DISK_FileHandle *wsig;
937
938 LOG (GNUNET_ERROR_TYPE_DEBUG,
939 "Starting service `%s'\n",
940 service_name);
941 if (0 != strcasecmp ("arm",
942 service_name))
943 return change_service (h,
944 service_name,
945 cont,
946 cont_cls,
947 GNUNET_MESSAGE_TYPE_ARM_START);
948
949 /* Possible cases:
950 * 1) We're connected to ARM already. Invoke the callback immediately.
951 * 2) We're not connected to ARM.
952 * Cancel any reconnection attempts temporarily, then perform
953 * a service test.
954 */
955 if (GNUNET_YES == h->currently_up)
956 {
957 LOG (GNUNET_ERROR_TYPE_DEBUG,
958 "ARM is already running\n");
959 op = GNUNET_new (struct GNUNET_ARM_Operation);
960 op->h = h;
961 op->result_cont = cont;
962 op->cont_cls = cont_cls;
963 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
964 h->operation_pending_tail,
965 op);
966 op->async = GNUNET_SCHEDULER_add_now (&notify_running, op);
967 return op;
968 }
969 /* This is an inherently uncertain choice, as it is of course
970 theoretically possible that ARM is up and we just did not
971 yet complete the MQ handshake. However, given that users
972 are unlikely to hammer 'gnunet-arm -s' on a busy system,
973 the above check should catch 99.99% of the cases where ARM
974 is already running. */
975 LOG (GNUNET_ERROR_TYPE_DEBUG,
976 "Starting ARM service\n");
977 if (NULL == (sig = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE)))
978 {
979 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
980 "pipe");
981 ret = GNUNET_ARM_RESULT_START_FAILED;
982 }
983 else
984 {
985 wsig = GNUNET_DISK_pipe_detach_end (sig,
986 GNUNET_DISK_PIPE_END_WRITE);
987 ret = start_arm_service (h,
988 std_inheritance,
989 wsig);
990 GNUNET_DISK_file_close (wsig);
991 if (GNUNET_ARM_RESULT_STARTING == ret)
992 reconnect_arm (h);
993 }
994 op = GNUNET_new (struct GNUNET_ARM_Operation);
995 op->h = h;
996 op->result_cont = cont;
997 op->cont_cls = cont_cls;
998 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
999 h->operation_pending_tail,
1000 op);
1001 op->starting_ret = ret;
1002 if (NULL != sig)
1003 {
1004 op->rfd = GNUNET_DISK_pipe_detach_end (sig,
1005 GNUNET_DISK_PIPE_END_READ);
1006 /* Wait at most a minute for gnunet-service-arm to be up, as beyond
1007 that something clearly just went wrong */
1008 op->async = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_MINUTES,
1009 op->rfd,
1010 &notify_starting,
1011 op);
1012 GNUNET_DISK_pipe_close (sig);
1013 }
1014 else
1015 {
1016 op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
1017 op);
1018 }
1019 return op;
1020}
1021
1022
1023/**
1024 * Request a service to be stopped. Stopping arm itself will not
1025 * invalidate its handle, and ARM API will try to restore connection
1026 * to the ARM service, even if ARM connection was lost because you
1027 * asked for ARM to be stopped. Call
1028 * #GNUNET_ARM_disconnect() to free the handle and prevent
1029 * further connection attempts.
1030 *
1031 * @param h handle to ARM
1032 * @param service_name name of the service
1033 * @param cont callback to invoke after request is sent or is not sent
1034 * @param cont_cls closure for @a cont
1035 * @return handle for the operation, NULL on error
1036 */
1037struct GNUNET_ARM_Operation *
1038GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
1039 const char *service_name,
1040 GNUNET_ARM_ResultCallback cont,
1041 void *cont_cls)
1042{
1043 struct GNUNET_ARM_Operation *op;
1044
1045 LOG (GNUNET_ERROR_TYPE_DEBUG,
1046 "Stopping service `%s'\n",
1047 service_name);
1048 op = change_service (h,
1049 service_name,
1050 cont,
1051 cont_cls,
1052 GNUNET_MESSAGE_TYPE_ARM_STOP);
1053 if (NULL == op)
1054 return NULL;
1055 /* If the service is ARM, set a flag as we will use MQ errors
1056 to detect that the process is really gone. */
1057 if (0 == strcasecmp (service_name,
1058 "arm"))
1059 op->is_arm_stop = GNUNET_YES;
1060 return op;
1061}
1062
1063
1064/**
1065 * Request a list of running services.
1066 *
1067 * @param h handle to ARM
1068 * @param cont callback to invoke after request is sent or is not sent
1069 * @param cont_cls closure for @a cont
1070 * @return handle for the operation, NULL on error
1071 */
1072struct GNUNET_ARM_Operation *
1073GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
1074 GNUNET_ARM_ServiceListCallback cont,
1075 void *cont_cls)
1076{
1077 struct GNUNET_ARM_Operation *op;
1078 struct GNUNET_MQ_Envelope *env;
1079 struct GNUNET_ARM_Message *msg;
1080
1081 LOG (GNUNET_ERROR_TYPE_DEBUG,
1082 "Requesting LIST from ARM service\n");
1083 if (0 == h->request_id_counter)
1084 h->request_id_counter++;
1085 op = GNUNET_new (struct GNUNET_ARM_Operation);
1086 op->h = h;
1087 op->list_cont = cont;
1088 op->cont_cls = cont_cls;
1089 op->id = h->request_id_counter++;
1090 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1091 h->operation_pending_tail,
1092 op);
1093 env = GNUNET_MQ_msg (msg,
1094 GNUNET_MESSAGE_TYPE_ARM_LIST);
1095 msg->reserved = htonl (0);
1096 msg->request_id = GNUNET_htonll (op->id);
1097 GNUNET_MQ_send (h->mq, env);
1098 return op;
1099}
1100
1101
1102/* end of arm_api.c */
diff --git a/src/service/arm/arm_monitor_api.c b/src/service/arm/arm_monitor_api.c
new file mode 100644
index 000000000..2c418d0ce
--- /dev/null
+++ b/src/service/arm/arm_monitor_api.c
@@ -0,0 +1,274 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file arm/arm_monitor_api.c
23 * @brief API for monitoring the ARM service
24 * @author Christian Grothoff
25 * @author LRN
26 */
27#include "platform.h"
28#include "gnunet_arm_service.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_protocols.h"
31#include "arm.h"
32
33#define INIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "arm-monitor-api", __VA_ARGS__)
36
37/**
38 * Handle for interacting with ARM.
39 */
40struct GNUNET_ARM_MonitorHandle
41{
42 /**
43 * Our control connection to the ARM service.
44 */
45 struct GNUNET_MQ_Handle *mq;
46
47 /**
48 * The configuration that we are using.
49 */
50 const struct GNUNET_CONFIGURATION_Handle *cfg;
51
52 /**
53 * ID of the reconnect task (if any).
54 */
55 struct GNUNET_SCHEDULER_Task *reconnect_task;
56
57 /**
58 * Current delay we use for re-trying to connect to core.
59 */
60 struct GNUNET_TIME_Relative retry_backoff;
61
62 /**
63 * Callback to invoke on status updates.
64 */
65 GNUNET_ARM_ServiceMonitorCallback service_status;
66
67 /**
68 * Closure for @e service_status.
69 */
70 void *service_status_cls;
71};
72
73
74/**
75 * Connect to the ARM service for monitoring.
76 *
77 * @param h handle to connect
78 * @return #GNUNET_OK on success
79 */
80static int
81reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h);
82
83
84/**
85 * Task scheduled to try to re-connect to arm.
86 *
87 * @param cls the `struct GNUNET_ARM_MonitorHandle`
88 */
89static void
90reconnect_arm_monitor_task (void *cls)
91{
92 struct GNUNET_ARM_MonitorHandle *h = cls;
93
94 h->reconnect_task = NULL;
95 LOG (GNUNET_ERROR_TYPE_DEBUG,
96 "Connecting to ARM service for monitoring after delay\n");
97 GNUNET_break (GNUNET_OK == reconnect_arm_monitor (h));
98}
99
100
101/**
102 * Close down any existing connection to the ARM service and
103 * try re-establishing it later.
104 *
105 * @param h our handle
106 */
107static void
108reconnect_arm_monitor_later (struct GNUNET_ARM_MonitorHandle *h)
109{
110 if (NULL != h->mq)
111 {
112 GNUNET_MQ_destroy (h->mq);
113 h->mq = NULL;
114 }
115 GNUNET_assert (NULL == h->reconnect_task);
116 h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
117 &reconnect_arm_monitor_task,
118 h);
119 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
120}
121
122
123/**
124 * Check notification messages received from ARM is well-formed.
125 *
126 * @param cls our `struct GNUNET_ARM_MonitorHandle`
127 * @param msg the message received from the arm service
128 * @return #GNUNET_OK if the message is well-formed
129 */
130static int
131check_monitor_notify (void *cls, const struct GNUNET_ARM_StatusMessage *msg)
132{
133 size_t sl =
134 ntohs (msg->header.size) - sizeof(struct GNUNET_ARM_StatusMessage);
135 const char *name = (const char *) &msg[1];
136
137 (void) cls;
138 if ((0 == sl) || ('\0' != name[sl - 1]))
139 {
140 GNUNET_break (0);
141 return GNUNET_SYSERR;
142 }
143 return GNUNET_OK;
144}
145
146
147/**
148 * Handler for notification messages received from ARM.
149 *
150 * @param cls our `struct GNUNET_ARM_MonitorHandle`
151 * @param res the message received from the arm service
152 */
153static void
154handle_monitor_notify (void *cls, const struct GNUNET_ARM_StatusMessage *res)
155{
156 struct GNUNET_ARM_MonitorHandle *h = cls;
157 enum GNUNET_ARM_ServiceMonitorStatus status;
158
159 status = (enum GNUNET_ARM_ServiceMonitorStatus) ntohl (res->status);
160 LOG (GNUNET_ERROR_TYPE_DEBUG,
161 "Received notification from ARM for service `%s' with status %d\n",
162 (const char *) &res[1],
163 (int) status);
164 if (NULL != h->service_status)
165 h->service_status (h->service_status_cls, (const char *) &res[1], status);
166}
167
168
169/**
170 * Generic error handler, called with the appropriate error code and
171 * the same closure specified at the creation of the message queue.
172 * Not every message queue implementation supports an error handler.
173 *
174 * @param cls closure with the `struct GNUNET_ARM_MonitorHandle *`
175 * @param error error code
176 */
177static void
178mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
179{
180 struct GNUNET_ARM_MonitorHandle *h = cls;
181
182 (void) error;
183 reconnect_arm_monitor_later (h);
184}
185
186
187/**
188 * Connect to the ARM service for monitoring.
189 *
190 * @param h handle to connect
191 * @return #GNUNET_OK on success
192 */
193static int
194reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h)
195{
196 struct GNUNET_MQ_MessageHandler handlers[] =
197 { GNUNET_MQ_hd_var_size (monitor_notify,
198 GNUNET_MESSAGE_TYPE_ARM_STATUS,
199 struct GNUNET_ARM_StatusMessage,
200 h),
201 GNUNET_MQ_handler_end () };
202 struct GNUNET_MessageHeader *msg;
203 struct GNUNET_MQ_Envelope *env;
204
205 GNUNET_assert (NULL == h->mq);
206 h->mq = GNUNET_CLIENT_connect (h->cfg, "arm", handlers, &mq_error_handler, h);
207 if (NULL == h->mq)
208 {
209 if (NULL != h->service_status)
210 h->service_status (h->service_status_cls,
211 NULL,
212 GNUNET_ARM_SERVICE_STOPPED);
213 return GNUNET_SYSERR;
214 }
215 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_MONITOR);
216 GNUNET_MQ_send (h->mq, env);
217 return GNUNET_OK;
218}
219
220
221/**
222 * Setup a context for monitoring ARM, then
223 * start connecting to the ARM service for monitoring using that context.
224 *
225 * @param cfg configuration to use (needed to contact ARM;
226 * the ARM service may internally use a different
227 * configuration to determine how to start the service).
228 * @param cont callback to invoke on status updates
229 * @param cont_cls closure for @a cont
230 * @return context to use for further ARM monitor operations, NULL on error.
231 */
232struct GNUNET_ARM_MonitorHandle *
233GNUNET_ARM_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
234 GNUNET_ARM_ServiceMonitorCallback cont,
235 void *cont_cls)
236{
237 struct GNUNET_ARM_MonitorHandle *h;
238
239 h = GNUNET_new (struct GNUNET_ARM_MonitorHandle);
240 h->cfg = cfg;
241 h->service_status = cont;
242 h->service_status_cls = cont_cls;
243 if (GNUNET_OK != reconnect_arm_monitor (h))
244 {
245 GNUNET_free (h);
246 return NULL;
247 }
248 return h;
249}
250
251
252/**
253 * Disconnect from the ARM service (if connected) and destroy the context.
254 *
255 * @param h the handle that was being used
256 */
257void
258GNUNET_ARM_monitor_stop (struct GNUNET_ARM_MonitorHandle *h)
259{
260 if (NULL != h->mq)
261 {
262 GNUNET_MQ_destroy (h->mq);
263 h->mq = NULL;
264 }
265 if (NULL != h->reconnect_task)
266 {
267 GNUNET_SCHEDULER_cancel (h->reconnect_task);
268 h->reconnect_task = NULL;
269 }
270 GNUNET_free (h);
271}
272
273
274/* end of arm_api.c */
diff --git a/src/service/arm/gnunet-service-arm.c b/src/service/arm/gnunet-service-arm.c
new file mode 100644
index 000000000..da94efdd8
--- /dev/null
+++ b/src/service/arm/gnunet-service-arm.c
@@ -0,0 +1,2227 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2011, 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file arm/gnunet-service-arm.c
23 * @brief the automated restart manager service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_arm_service.h"
29#include "gnunet_protocols.h"
30#include "arm.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util", __VA_ARGS__)
33
34#define LOG_STRERROR(kind, syscall) \
35 GNUNET_log_from_strerror (kind, "util", syscall)
36
37
38#if HAVE_WAIT4
39/**
40 * Name of the file for writing resource utilization summaries to.
41 */
42static char *wait_filename;
43
44/**
45 * Handle for the file for writing resource summaries.
46 */
47static FILE *wait_file;
48#endif
49
50
51/**
52 * How many messages do we queue up at most for optional
53 * notifications to a client? (this can cause notifications
54 * about outgoing messages to be dropped).
55 */
56#define MAX_NOTIFY_QUEUE 1024
57
58
59/**
60 * List of our services.
61 */
62struct ServiceList;
63
64
65/**
66 * Record with information about a listen socket we have open.
67 */
68struct ServiceListeningInfo
69{
70 /**
71 * This is a linked list.
72 */
73 struct ServiceListeningInfo *next;
74
75 /**
76 * This is a linked list.
77 */
78 struct ServiceListeningInfo *prev;
79
80 /**
81 * Address this socket is listening on.
82 */
83 struct sockaddr *service_addr;
84
85 /**
86 * Service this listen socket is for.
87 */
88 struct ServiceList *sl;
89
90 /**
91 * Number of bytes in @e service_addr
92 */
93 socklen_t service_addr_len;
94
95 /**
96 * Our listening socket.
97 */
98 struct GNUNET_NETWORK_Handle *listen_socket;
99
100 /**
101 * Task doing the accepting.
102 */
103 struct GNUNET_SCHEDULER_Task *accept_task;
104};
105
106
107/**
108 * List of our services.
109 */
110struct ServiceList
111{
112 /**
113 * This is a doubly-linked list.
114 */
115 struct ServiceList *next;
116
117 /**
118 * This is a doubly-linked list.
119 */
120 struct ServiceList *prev;
121
122 /**
123 * Linked list of listen sockets associated with this service.
124 */
125 struct ServiceListeningInfo *listen_head;
126
127 /**
128 * Linked list of listen sockets associated with this service.
129 */
130 struct ServiceListeningInfo *listen_tail;
131
132 /**
133 * Name of the service.
134 */
135 char *name;
136
137 /**
138 * Name of the binary used.
139 */
140 char *binary;
141
142 /**
143 * Name of the configuration file used.
144 */
145 char *config;
146
147 /**
148 * Client to notify upon kill completion (waitpid), NULL
149 * if we should simply restart the process.
150 */
151 struct GNUNET_SERVICE_Client *killing_client;
152
153 /**
154 * ID of the request that killed the service (for reporting back).
155 */
156 uint64_t killing_client_request_id;
157
158 /**
159 * Process structure pointer of the child.
160 */
161 struct GNUNET_OS_Process *proc;
162
163 /**
164 * Process exponential backoff time
165 */
166 struct GNUNET_TIME_Relative backoff;
167
168 /**
169 * Absolute time at which the process was (re-)started last.
170 */
171 struct GNUNET_TIME_Absolute last_started_at;
172
173 /**
174 * Absolute time at which the process is scheduled to restart in case of death
175 */
176 struct GNUNET_TIME_Absolute restart_at;
177
178 /**
179 * Time we asked the service to shut down (used to calculate time it took
180 * the service to terminate).
181 */
182 struct GNUNET_TIME_Absolute killed_at;
183
184 /**
185 * Is this service to be started by default (or did a client tell us explicitly
186 * to start it)? #GNUNET_NO if the service is started only upon 'accept' on a
187 * listen socket or possibly explicitly by a client changing the value.
188 */
189 int force_start;
190
191 /**
192 * Should we use pipes to signal this process? (YES for Java binaries and if we
193 * are on Windoze).
194 */
195 int pipe_control;
196
197 /**
198 * Last exit status of the process.
199 */
200 int last_exit_status;
201};
202
203/**
204 * List of running services.
205 */
206static struct ServiceList *running_head;
207
208/**
209 * List of running services.
210 */
211static struct ServiceList *running_tail;
212
213/**
214 * Our configuration
215 */
216static const struct GNUNET_CONFIGURATION_Handle *cfg;
217
218/**
219 * Command to prepend to each actual command.
220 */
221static char *prefix_command;
222
223/**
224 * Option to append to each actual command.
225 */
226static char *final_option;
227
228/**
229 * ID of task called whenever we get a SIGCHILD.
230 */
231static struct GNUNET_SCHEDULER_Task *child_death_task;
232
233/**
234 * ID of task called whenever the timeout for restarting a child
235 * expires.
236 */
237static struct GNUNET_SCHEDULER_Task *child_restart_task;
238
239/**
240 * Pipe used to communicate shutdown via signal.
241 */
242static struct GNUNET_DISK_PipeHandle *sigpipe;
243
244/**
245 * Are we in shutdown mode?
246 */
247static int in_shutdown;
248
249/**
250 * Return value from main
251 */
252static int global_ret;
253
254/**
255 * Are we starting user services?
256 */
257static int start_user = GNUNET_YES;
258
259/**
260 * Are we starting system services?
261 */
262static int start_system = GNUNET_YES;
263
264/**
265 * Handle to our service instance. Our service is a bit special in that
266 * its service is not immediately stopped once we get a shutdown
267 * request (since we need to continue service until all of our child
268 * processes are dead). This handle is used to shut down the service
269 * (and thus trigger process termination) once all child processes are
270 * also dead. A special option in the ARM configuration modifies the
271 * behaviour of the service implementation to not do the shutdown
272 * immediately.
273 */
274static struct GNUNET_SERVICE_Handle *service;
275
276/**
277 * Context for notifications we need to send to our clients.
278 */
279static struct GNUNET_NotificationContext *notifier;
280
281
282/**
283 * Add the given UNIX domain path as an address to the
284 * list (as the first entry).
285 *
286 * @param saddrs array to update
287 * @param saddrlens where to store the address length
288 * @param unixpath path to add
289 * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This
290 * parameter is ignore on systems other than LINUX
291 */
292static void
293add_unixpath (struct sockaddr **saddrs,
294 socklen_t *saddrlens,
295 const char *unixpath,
296 int abstract)
297{
298#ifdef AF_UNIX
299 struct sockaddr_un *un;
300
301 un = GNUNET_new (struct sockaddr_un);
302 un->sun_family = AF_UNIX;
303 GNUNET_strlcpy (un->sun_path, unixpath, sizeof(un->sun_path));
304#ifdef __linux__
305 if (GNUNET_YES == abstract)
306 un->sun_path[0] = '\0';
307#endif
308#if HAVE_SOCKADDR_UN_SUN_LEN
309 un->sun_len = (u_char) sizeof(struct sockaddr_un);
310#endif
311 *saddrs = (struct sockaddr *) un;
312 *saddrlens = sizeof(struct sockaddr_un);
313#else
314 /* this function should never be called
315 * unless AF_UNIX is defined! */
316 GNUNET_assert (0);
317#endif
318}
319
320
321/**
322 * Get the list of addresses that a server for the given service
323 * should bind to.
324 *
325 * @param service_name name of the service
326 * @param cfg configuration (which specifies the addresses)
327 * @param addrs set (call by reference) to an array of pointers to the
328 * addresses the server should bind to and listen on; the
329 * array will be NULL-terminated (on success)
330 * @param addr_lens set (call by reference) to an array of the lengths
331 * of the respective `struct sockaddr` struct in the @a addrs
332 * array (on success)
333 * @return number of addresses found on success,
334 * #GNUNET_SYSERR if the configuration
335 * did not specify reasonable finding information or
336 * if it specified a hostname that could not be resolved;
337 * #GNUNET_NO if the number of addresses configured is
338 * zero (in this case, `*addrs` and `*addr_lens` will be
339 * set to NULL).
340 */
341static int
342get_server_addresses (const char *service_name,
343 const struct GNUNET_CONFIGURATION_Handle *cfg,
344 struct sockaddr ***addrs,
345 socklen_t **addr_lens)
346{
347 int disablev6;
348 struct GNUNET_NETWORK_Handle *desc;
349 unsigned long long port;
350 char *unixpath;
351 struct addrinfo hints;
352 struct addrinfo *res;
353 struct addrinfo *pos;
354 struct addrinfo *next;
355 unsigned int i;
356 int resi;
357 int ret;
358 int abstract;
359 struct sockaddr **saddrs;
360 socklen_t *saddrlens;
361 char *hostname;
362
363 *addrs = NULL;
364 *addr_lens = NULL;
365 desc = NULL;
366 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6"))
367 {
368 if (GNUNET_SYSERR ==
369 (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
370 service_name,
371 "DISABLEV6")))
372 return GNUNET_SYSERR;
373 }
374 else
375 disablev6 = GNUNET_NO;
376
377 if (! disablev6)
378 {
379 /* probe IPv6 support */
380 desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
381 if (NULL == desc)
382 {
383 if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) ||
384 (EACCES == errno))
385 {
386 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
387 return GNUNET_SYSERR;
388 }
389 LOG (GNUNET_ERROR_TYPE_INFO,
390 _ (
391 "Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"),
392 service_name,
393 strerror (errno));
394 disablev6 = GNUNET_YES;
395 }
396 else
397 {
398 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
399 desc = NULL;
400 }
401 }
402
403 port = 0;
404 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT"))
405 {
406 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
407 service_name,
408 "PORT",
409 &port))
410 {
411 LOG (GNUNET_ERROR_TYPE_ERROR,
412 _ ("Require valid port number for service `%s' in configuration!\n"),
413 service_name);
414 }
415 if (port > 65535)
416 {
417 LOG (GNUNET_ERROR_TYPE_ERROR,
418 _ ("Require valid port number for service `%s' in configuration!\n"),
419 service_name);
420 return GNUNET_SYSERR;
421 }
422 }
423
424 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO"))
425 {
426 GNUNET_break (GNUNET_OK ==
427 GNUNET_CONFIGURATION_get_value_string (cfg,
428 service_name,
429 "BINDTO",
430 &hostname));
431 }
432 else
433 hostname = NULL;
434
435 unixpath = NULL;
436 abstract = GNUNET_NO;
437#ifdef AF_UNIX
438 if ((GNUNET_YES ==
439 GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) &&
440 (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
441 service_name,
442 "UNIXPATH",
443 &unixpath)) &&
444 (0 < strlen (unixpath)))
445 {
446 /* probe UNIX support */
447 struct sockaddr_un s_un;
448
449 if (strlen (unixpath) >= sizeof(s_un.sun_path))
450 {
451 LOG (GNUNET_ERROR_TYPE_WARNING,
452 _ ("UNIXPATH `%s' too long, maximum length is %llu\n"),
453 unixpath,
454 (unsigned long long) sizeof(s_un.sun_path));
455 unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
456 LOG (GNUNET_ERROR_TYPE_INFO, _ ("Using `%s' instead\n"), unixpath);
457 }
458#ifdef __linux__
459 abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
460 "TESTING",
461 "USE_ABSTRACT_SOCKETS");
462 if (GNUNET_SYSERR == abstract)
463 abstract = GNUNET_NO;
464#endif
465 if ((GNUNET_YES != abstract) &&
466 (GNUNET_OK != GNUNET_DISK_directory_create_for_file (unixpath)))
467 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", unixpath);
468 }
469 if (NULL != unixpath)
470 {
471 desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0);
472 if (NULL == desc)
473 {
474 if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) ||
475 (EACCES == errno))
476 {
477 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
478 GNUNET_free (hostname);
479 GNUNET_free (unixpath);
480 return GNUNET_SYSERR;
481 }
482 LOG (GNUNET_ERROR_TYPE_INFO,
483 _ (
484 "Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"),
485 service_name,
486 strerror (errno));
487 GNUNET_free (unixpath);
488 unixpath = NULL;
489 }
490 else
491 {
492 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
493 desc = NULL;
494 }
495 }
496#endif
497
498 if ((0 == port) && (NULL == unixpath))
499 {
500 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg,
501 service_name,
502 "START_ON_DEMAND"))
503 LOG (GNUNET_ERROR_TYPE_ERROR,
504 _ (
505 "Have neither PORT nor UNIXPATH for service `%s', but one is required\n"),
506 service_name);
507 GNUNET_free (hostname);
508 return GNUNET_SYSERR;
509 }
510 if (0 == port)
511 {
512 saddrs = GNUNET_new_array (2, struct sockaddr *);
513 saddrlens = GNUNET_new_array (2, socklen_t);
514 add_unixpath (saddrs, saddrlens, unixpath, abstract);
515 GNUNET_free (unixpath);
516 GNUNET_free (hostname);
517 *addrs = saddrs;
518 *addr_lens = saddrlens;
519 return 1;
520 }
521
522 if (NULL != hostname)
523 {
524 LOG (GNUNET_ERROR_TYPE_DEBUG,
525 "Resolving `%s' since that is where `%s' will bind to.\n",
526 hostname,
527 service_name);
528 memset (&hints, 0, sizeof(struct addrinfo));
529 if (disablev6)
530 hints.ai_family = AF_INET;
531 hints.ai_protocol = IPPROTO_TCP;
532 if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
533 (NULL == res))
534 {
535 LOG (GNUNET_ERROR_TYPE_ERROR,
536 _ ("Failed to resolve `%s': %s\n"),
537 hostname,
538 gai_strerror (ret));
539 GNUNET_free (hostname);
540 GNUNET_free (unixpath);
541 return GNUNET_SYSERR;
542 }
543 next = res;
544 i = 0;
545 while (NULL != (pos = next))
546 {
547 next = pos->ai_next;
548 if ((disablev6) && (pos->ai_family == AF_INET6))
549 continue;
550 i++;
551 }
552 if (0 == i)
553 {
554 LOG (GNUNET_ERROR_TYPE_ERROR,
555 _ ("Failed to find %saddress for `%s'.\n"),
556 disablev6 ? "IPv4 " : "",
557 hostname);
558 freeaddrinfo (res);
559 GNUNET_free (hostname);
560 GNUNET_free (unixpath);
561 return GNUNET_SYSERR;
562 }
563 resi = i;
564 if (NULL != unixpath)
565 resi++;
566 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
567 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
568 i = 0;
569 if (NULL != unixpath)
570 {
571 add_unixpath (saddrs, saddrlens, unixpath, abstract);
572 i++;
573 }
574 next = res;
575 while (NULL != (pos = next))
576 {
577 next = pos->ai_next;
578 if ((disablev6) && (AF_INET6 == pos->ai_family))
579 continue;
580 if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol))
581 continue; /* not TCP */
582 if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype))
583 continue; /* huh? */
584 LOG (GNUNET_ERROR_TYPE_DEBUG,
585 "Service `%s' will bind to `%s'\n",
586 service_name,
587 GNUNET_a2s (pos->ai_addr, pos->ai_addrlen));
588 if (AF_INET == pos->ai_family)
589 {
590 GNUNET_assert (sizeof(struct sockaddr_in) == pos->ai_addrlen);
591 saddrlens[i] = pos->ai_addrlen;
592 saddrs[i] = GNUNET_malloc (saddrlens[i]);
593 GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
594 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
595 }
596 else
597 {
598 GNUNET_assert (AF_INET6 == pos->ai_family);
599 GNUNET_assert (sizeof(struct sockaddr_in6) == pos->ai_addrlen);
600 saddrlens[i] = pos->ai_addrlen;
601 saddrs[i] = GNUNET_malloc (saddrlens[i]);
602 GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
603 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
604 }
605 i++;
606 }
607 GNUNET_free (hostname);
608 freeaddrinfo (res);
609 resi = i;
610 }
611 else
612 {
613 /* will bind against everything, just set port */
614 if (disablev6)
615 {
616 /* V4-only */
617 resi = 1;
618 if (NULL != unixpath)
619 resi++;
620 i = 0;
621 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
622 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
623 if (NULL != unixpath)
624 {
625 add_unixpath (saddrs, saddrlens, unixpath, abstract);
626 i++;
627 }
628 saddrlens[i] = sizeof(struct sockaddr_in);
629 saddrs[i] = GNUNET_malloc (saddrlens[i]);
630#if HAVE_SOCKADDR_IN_SIN_LEN
631 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
632#endif
633 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
634 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
635 }
636 else
637 {
638 /* dual stack */
639 resi = 2;
640 if (NULL != unixpath)
641 resi++;
642 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
643 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
644 i = 0;
645 if (NULL != unixpath)
646 {
647 add_unixpath (saddrs, saddrlens, unixpath, abstract);
648 i++;
649 }
650 saddrlens[i] = sizeof(struct sockaddr_in6);
651 saddrs[i] = GNUNET_malloc (saddrlens[i]);
652#if HAVE_SOCKADDR_IN_SIN_LEN
653 ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
654#endif
655 ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
656 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
657 i++;
658 saddrlens[i] = sizeof(struct sockaddr_in);
659 saddrs[i] = GNUNET_malloc (saddrlens[i]);
660#if HAVE_SOCKADDR_IN_SIN_LEN
661 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
662#endif
663 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
664 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
665 }
666 }
667 GNUNET_free (unixpath);
668 *addrs = saddrs;
669 *addr_lens = saddrlens;
670 return resi;
671}
672
673
674/**
675 * Signal our client that we will start or stop the
676 * service.
677 *
678 * @param client who is being signalled
679 * @param name name of the service
680 * @param request_id id of the request that is being responded to.
681 * @param result message type to send
682 * @return NULL if it was not found
683 */
684static void
685signal_result (struct GNUNET_SERVICE_Client *client,
686 const char *name,
687 uint64_t request_id,
688 enum GNUNET_ARM_Result result)
689{
690 struct GNUNET_MQ_Envelope *env;
691 struct GNUNET_ARM_ResultMessage *msg;
692
693 (void) name;
694 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_RESULT);
695 msg->result = htonl (result);
696 msg->arm_msg.request_id = GNUNET_htonll (request_id);
697 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
698}
699
700
701/**
702 * Tell all clients about status change of a service.
703 *
704 * @param name name of the service
705 * @param status message type to send
706 * @param unicast if not NULL, send to this client only.
707 * otherwise, send to all clients in the notifier
708 */
709static void
710broadcast_status (const char *name,
711 enum GNUNET_ARM_ServiceMonitorStatus status,
712 struct GNUNET_SERVICE_Client *unicast)
713{
714 struct GNUNET_MQ_Envelope *env;
715 struct GNUNET_ARM_StatusMessage *msg;
716 size_t namelen;
717
718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719 "Sending status %u of service `%s' to client\n",
720 (unsigned int) status,
721 name);
722 namelen = strlen (name) + 1;
723 env = GNUNET_MQ_msg_extra (msg, namelen, GNUNET_MESSAGE_TYPE_ARM_STATUS);
724 msg->status = htonl ((uint32_t) (status));
725 GNUNET_memcpy ((char *) &msg[1], name, namelen);
726 if (NULL == unicast)
727 {
728 if (NULL != notifier)
729 GNUNET_notification_context_broadcast (notifier,
730 &msg->header,
731 GNUNET_YES);
732 GNUNET_MQ_discard (env);
733 }
734 else
735 {
736 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (unicast), env);
737 }
738}
739
740
741/**
742 * Actually start the process for the given service.
743 *
744 * @param sl identifies service to start
745 * @param client that asked to start the service (may be NULL)
746 * @param request_id id of the request in response to which the process is
747 * being started. 0 if starting was not requested.
748 */
749static void
750start_process (struct ServiceList *sl,
751 struct GNUNET_SERVICE_Client *client,
752 uint64_t request_id)
753{
754 char *loprefix;
755 char *options;
756 int use_debug;
757 int is_simple_service;
758 struct ServiceListeningInfo *sli;
759 int *lsocks;
760 unsigned int ls;
761 char *binary;
762 char *quotedbinary;
763
764 /* calculate listen socket list */
765 lsocks = NULL;
766 ls = 0;
767 for (sli = sl->listen_head; NULL != sli; sli = sli->next)
768 {
769 GNUNET_array_append (lsocks,
770 ls,
771 GNUNET_NETWORK_get_fd (sli->listen_socket));
772 if (NULL != sli->accept_task)
773 {
774 GNUNET_SCHEDULER_cancel (sli->accept_task);
775 sli->accept_task = NULL;
776 }
777 }
778
779 GNUNET_array_append (lsocks, ls, -1);
780
781 /* obtain configuration */
782 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
783 sl->name,
784 "PREFIX",
785 &loprefix))
786 loprefix = GNUNET_strdup (prefix_command);
787 else
788 loprefix = GNUNET_CONFIGURATION_expand_dollar (cfg, loprefix);
789 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
790 sl->name,
791 "OPTIONS",
792 &options))
793 options = NULL;
794 else
795 options = GNUNET_CONFIGURATION_expand_dollar (cfg, options);
796 {
797 char *new_options;
798 char *optpos;
799 char *fin_options;
800
801 fin_options = GNUNET_strdup (final_option);
802 /* replace '{}' with service name */
803 while (NULL != (optpos = strstr (fin_options, "{}")))
804 {
805 /* terminate string at opening parenthesis */
806 *optpos = 0;
807 GNUNET_asprintf (&new_options,
808 "%s%s%s",
809 fin_options,
810 sl->name,
811 optpos + 2);
812 GNUNET_free (fin_options);
813 fin_options = new_options;
814 }
815 if (NULL != options)
816 {
817 /* combine "fin_options" with "options" */
818 optpos = options;
819 GNUNET_asprintf (&options, "%s %s", fin_options, optpos);
820 GNUNET_free (fin_options);
821 GNUNET_free (optpos);
822 }
823 else
824 {
825 /* only have "fin_options", use that */
826 options = fin_options;
827 }
828 }
829 options = GNUNET_CONFIGURATION_expand_dollar (cfg, options);
830 use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
831 {
832 const char *service_type = NULL;
833 const char *choices[] = { "GNUNET", "SIMPLE", NULL };
834
835 is_simple_service = GNUNET_NO;
836 if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_choice (cfg,
837 sl->name,
838 "TYPE",
839 choices,
840 &service_type)) &&
841 (0 == strcasecmp (service_type, "SIMPLE")))
842 is_simple_service = GNUNET_YES;
843 }
844
845 GNUNET_assert (NULL == sl->proc);
846 if (GNUNET_YES == is_simple_service)
847 {
848 /* A simple service will receive no GNUnet specific
849 command line options. */
850 binary = GNUNET_strdup (sl->binary);
851 binary = GNUNET_CONFIGURATION_expand_dollar (cfg, binary);
852 GNUNET_asprintf (&quotedbinary, "\"%s\"", sl->binary);
853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854 "Starting simple service `%s' using binary `%s'\n",
855 sl->name,
856 sl->binary);
857 /* FIXME: dollar expansion should only be done outside
858 * of ''-quoted strings, escaping should be considered. */
859 if (NULL != options)
860 options = GNUNET_CONFIGURATION_expand_dollar (cfg, options);
861 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
862 ? GNUNET_OS_INHERIT_STD_OUT_AND_ERR
863 | GNUNET_OS_USE_PIPE_CONTROL
864 : GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
865 lsocks,
866 loprefix,
867 quotedbinary,
868 options,
869 NULL);
870 }
871 else
872 {
873 /* actually start process */
874 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
875 "Starting service `%s' using binary `%s' and configuration `%s'\n",
876 sl->name,
877 sl->binary,
878 sl->config);
879 binary = GNUNET_OS_get_libexec_binary_path (sl->binary);
880 GNUNET_asprintf (&quotedbinary, "\"%s\"", binary);
881
882 if (GNUNET_YES == use_debug)
883 {
884 if (NULL == sl->config)
885 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
886 ?
887 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
888 | GNUNET_OS_USE_PIPE_CONTROL
889 :
890 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
891 lsocks,
892 loprefix,
893 quotedbinary,
894 "-L",
895 "DEBUG",
896 options,
897 NULL);
898 else
899 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
900 ?
901 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
902 | GNUNET_OS_USE_PIPE_CONTROL
903 :
904 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
905 lsocks,
906 loprefix,
907 quotedbinary,
908 "-c",
909 sl->config,
910 "-L",
911 "DEBUG",
912 options,
913 NULL);
914 }
915 else
916 {
917 if (NULL == sl->config)
918 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
919 ?
920 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
921 | GNUNET_OS_USE_PIPE_CONTROL
922 :
923 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
924 lsocks,
925 loprefix,
926 quotedbinary,
927 options,
928 NULL);
929 else
930 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
931 ?
932 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
933 | GNUNET_OS_USE_PIPE_CONTROL
934 :
935 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
936 lsocks,
937 loprefix,
938 quotedbinary,
939 "-c",
940 sl->config,
941 options,
942 NULL);
943 }
944 }
945 GNUNET_free (binary);
946 GNUNET_free (quotedbinary);
947 sl->last_started_at = GNUNET_TIME_absolute_get ();
948 if (NULL == sl->proc)
949 {
950 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
951 _ ("Failed to start service `%s'\n"),
952 sl->name);
953 if (client)
954 signal_result (client,
955 sl->name,
956 request_id,
957 GNUNET_ARM_RESULT_START_FAILED);
958 }
959 else
960 {
961 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
962 _ ("Starting service `%s'\n"),
963 sl->name);
964 broadcast_status (sl->name, GNUNET_ARM_SERVICE_STARTING, NULL);
965 if (client)
966 signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_STARTING);
967 }
968 /* clean up */
969 GNUNET_free (loprefix);
970 GNUNET_free (options);
971 GNUNET_array_grow (lsocks, ls, 0);
972}
973
974
975/**
976 * Find the process with the given service
977 * name in the given list and return it.
978 *
979 * @param name which service entry to look up
980 * @return NULL if it was not found
981 */
982static struct ServiceList *
983find_service (const char *name)
984{
985 struct ServiceList *sl;
986
987 sl = running_head;
988 while (sl != NULL)
989 {
990 if (0 == strcasecmp (sl->name, name))
991 return sl;
992 sl = sl->next;
993 }
994 return NULL;
995}
996
997
998/**
999 * First connection has come to the listening socket associated with the service,
1000 * create the service in order to relay the incoming connection to it
1001 *
1002 * @param cls callback data, `struct ServiceListeningInfo` describing a listen socket
1003 */
1004static void
1005accept_connection (void *cls)
1006{
1007 struct ServiceListeningInfo *sli = cls;
1008 struct ServiceList *sl = sli->sl;
1009
1010 sli->accept_task = NULL;
1011 GNUNET_assert (GNUNET_NO == in_shutdown);
1012 start_process (sl, NULL, 0);
1013}
1014
1015
1016/**
1017 * Creating a listening socket for each of the service's addresses and
1018 * wait for the first incoming connection to it
1019 *
1020 * @param sa address associated with the service
1021 * @param addr_len length of @a sa
1022 * @param sl service entry for the service in question
1023 */
1024static void
1025create_listen_socket (struct sockaddr *sa,
1026 socklen_t addr_len,
1027 struct ServiceList *sl)
1028{
1029 static int on = 1;
1030 struct GNUNET_NETWORK_Handle *sock;
1031 struct ServiceListeningInfo *sli;
1032
1033 int match_uid;
1034 int match_gid;
1035
1036 switch (sa->sa_family)
1037 {
1038 case AF_INET:
1039 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1040 break;
1041
1042 case AF_INET6:
1043 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1044 break;
1045
1046 case AF_UNIX:
1047 if (0 == strcmp (GNUNET_a2s (sa, addr_len),
1048 "@")) /* Do not bind to blank UNIX path! */
1049 return;
1050 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1051 break;
1052
1053 default:
1054 GNUNET_break (0);
1055 sock = NULL;
1056 errno = EAFNOSUPPORT;
1057 break;
1058 }
1059 if (NULL == sock)
1060 {
1061 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1062 _ ("Unable to create socket for service `%s': %s\n"),
1063 sl->name,
1064 strerror (errno));
1065 GNUNET_free (sa);
1066 return;
1067 }
1068 if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (sock,
1069 SOL_SOCKET,
1070 SO_REUSEADDR,
1071 &on,
1072 sizeof(on)))
1073 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1074 "setsockopt");
1075#ifdef IPV6_V6ONLY
1076 if ((sa->sa_family == AF_INET6) &&
1077 (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (sock,
1078 IPPROTO_IPV6,
1079 IPV6_V6ONLY,
1080 &on,
1081 sizeof(on))))
1082 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1083 "setsockopt");
1084#endif
1085 if (AF_UNIX == sa->sa_family)
1086 GNUNET_NETWORK_unix_precheck ((struct sockaddr_un *) sa);
1087 if (GNUNET_OK !=
1088 GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len))
1089 {
1090 GNUNET_log (
1091 GNUNET_ERROR_TYPE_WARNING,
1092 _ (
1093 "Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1094 sl->name,
1095 GNUNET_a2s (sa, addr_len),
1096 strerror (errno));
1097 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1098 GNUNET_free (sa);
1099 return;
1100 }
1101 if ((AF_UNIX == sa->sa_family)
1102#ifdef __linux__
1103 /* Permission settings are not required when abstract sockets are used */
1104 && ('\0' != ((const struct sockaddr_un *) sa)->sun_path[0])
1105#endif
1106 )
1107 {
1108 match_uid =
1109 GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "UNIX_MATCH_UID");
1110 match_gid =
1111 GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "UNIX_MATCH_GID");
1112 GNUNET_DISK_fix_permissions (((const struct sockaddr_un *) sa)->sun_path,
1113 match_uid,
1114 match_gid);
1115 }
1116 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5))
1117 {
1118 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1119 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1120 GNUNET_free (sa);
1121 return;
1122 }
1123 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1124 _ ("ARM now monitors connections to service `%s' at `%s'\n"),
1125 sl->name,
1126 GNUNET_a2s (sa, addr_len));
1127 sli = GNUNET_new (struct ServiceListeningInfo);
1128 sli->service_addr = sa;
1129 sli->service_addr_len = addr_len;
1130 sli->listen_socket = sock;
1131 sli->sl = sl;
1132 sli->accept_task =
1133 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1134 sock,
1135 &accept_connection,
1136 sli);
1137 GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli);
1138}
1139
1140
1141/**
1142 * Remove and free an entry in the service list. Listen sockets
1143 * must have already been cleaned up. Only to be called during shutdown.
1144 *
1145 * @param sl entry to free
1146 */
1147static void
1148free_service (struct ServiceList *sl)
1149{
1150 GNUNET_assert (GNUNET_YES == in_shutdown);
1151 GNUNET_CONTAINER_DLL_remove (running_head, running_tail, sl);
1152 GNUNET_assert (NULL == sl->listen_head);
1153 GNUNET_free (sl->config);
1154 GNUNET_free (sl->binary);
1155 GNUNET_free (sl->name);
1156 GNUNET_free (sl);
1157}
1158
1159
1160/**
1161 * Check START-message.
1162 *
1163 * @param cls identification of the client
1164 * @param amsg the actual message
1165 * @return #GNUNET_OK to keep the connection open,
1166 * #GNUNET_SYSERR to close it (signal serious error)
1167 */
1168static int
1169check_start (void *cls, const struct GNUNET_ARM_Message *amsg)
1170{
1171 (void) cls;
1172 GNUNET_MQ_check_zero_termination (amsg);
1173 return GNUNET_OK;
1174}
1175
1176
1177/**
1178 * Handle START-message.
1179 *
1180 * @param cls identification of the client
1181 * @param amsg the actual message
1182 */
1183static void
1184handle_start (void *cls, const struct GNUNET_ARM_Message *amsg)
1185{
1186 struct GNUNET_SERVICE_Client *client = cls;
1187 const char *servicename;
1188 struct ServiceList *sl;
1189 uint64_t request_id;
1190
1191 request_id = GNUNET_ntohll (amsg->request_id);
1192 servicename = (const char *) &amsg[1];
1193 GNUNET_SERVICE_client_continue (client);
1194 if (GNUNET_YES == in_shutdown)
1195 {
1196 signal_result (client,
1197 servicename,
1198 request_id,
1199 GNUNET_ARM_RESULT_IN_SHUTDOWN);
1200 return;
1201 }
1202 sl = find_service (servicename);
1203 if (NULL == sl)
1204 {
1205 signal_result (client,
1206 servicename,
1207 request_id,
1208 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
1209 return;
1210 }
1211 sl->force_start = GNUNET_YES;
1212 if (NULL != sl->proc)
1213 {
1214 signal_result (client,
1215 servicename,
1216 request_id,
1217 GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
1218 return;
1219 }
1220 start_process (sl, client, request_id);
1221}
1222
1223
1224/**
1225 * Start a shutdown sequence.
1226 *
1227 * @param cls closure (refers to service)
1228 */
1229static void
1230trigger_shutdown (void *cls)
1231{
1232 (void) cls;
1233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Triggering shutdown\n");
1234 GNUNET_SCHEDULER_shutdown ();
1235}
1236
1237
1238/**
1239 * Check STOP-message.
1240 *
1241 * @param cls identification of the client
1242 * @param amsg the actual message
1243 * @return #GNUNET_OK to keep the connection open,
1244 * #GNUNET_SYSERR to close it (signal serious error)
1245 */
1246static int
1247check_stop (void *cls, const struct GNUNET_ARM_Message *amsg)
1248{
1249 (void) cls;
1250 GNUNET_MQ_check_zero_termination (amsg);
1251 return GNUNET_OK;
1252}
1253
1254
1255/**
1256 * Handle STOP-message.
1257 *
1258 * @param cls identification of the client
1259 * @param amsg the actual message
1260 */
1261static void
1262handle_stop (void *cls, const struct GNUNET_ARM_Message *amsg)
1263{
1264 struct GNUNET_SERVICE_Client *client = cls;
1265 struct ServiceList *sl;
1266 const char *servicename;
1267 uint64_t request_id;
1268
1269 request_id = GNUNET_ntohll (amsg->request_id);
1270 servicename = (const char *) &amsg[1];
1271 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1272 _ ("Preparing to stop `%s'\n"),
1273 servicename);
1274 GNUNET_SERVICE_client_continue (client);
1275 if (0 == strcasecmp (servicename, "arm"))
1276 {
1277 broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
1278 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_STOPPING);
1279 GNUNET_SERVICE_client_persist (client);
1280 GNUNET_SCHEDULER_add_now (&trigger_shutdown, NULL);
1281 return;
1282 }
1283 sl = find_service (servicename);
1284 if (NULL == sl)
1285 {
1286 signal_result (client,
1287 servicename,
1288 request_id,
1289 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
1290 return;
1291 }
1292 sl->force_start = GNUNET_NO;
1293 if (GNUNET_YES == in_shutdown)
1294 {
1295 /* shutdown in progress */
1296 signal_result (client,
1297 servicename,
1298 request_id,
1299 GNUNET_ARM_RESULT_IN_SHUTDOWN);
1300 return;
1301 }
1302 if (NULL != sl->killing_client)
1303 {
1304 /* killing already in progress */
1305 signal_result (client,
1306 servicename,
1307 request_id,
1308 GNUNET_ARM_RESULT_IS_STOPPING_ALREADY);
1309 return;
1310 }
1311 if (NULL == sl->proc)
1312 {
1313 /* process is down */
1314 signal_result (client,
1315 servicename,
1316 request_id,
1317 GNUNET_ARM_RESULT_IS_STOPPED_ALREADY);
1318 return;
1319 }
1320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1321 "Sending kill signal to service `%s', waiting for process to die.\n",
1322 servicename);
1323 broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
1324 /* no signal_start - only when it's STOPPED */
1325 sl->killed_at = GNUNET_TIME_absolute_get ();
1326 if (0 != GNUNET_OS_process_kill (sl->proc, GNUNET_TERM_SIG))
1327 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
1328 sl->killing_client = client;
1329 sl->killing_client_request_id = request_id;
1330}
1331
1332
1333/**
1334 * Write a string to a string pool.
1335 *
1336 * @param pool_start pointer to the start of the string pool
1337 * @param pool_size size of the string pool
1338 * @param[in,out] pool_pos current position index in the string pool,
1339 * will be updated
1340 * @param str string to write to the string pool
1341 * @returns GNUNET_OK if the string fits into the pool,
1342 * GNUNET_SYSERR otherwise
1343 */
1344static int
1345pool_write (char *pool_start, size_t pool_size, size_t *pool_pos, char *str)
1346{
1347 size_t next_pos = (*pool_pos) + strlen (str) + 1;
1348
1349 if (next_pos > pool_size)
1350 return GNUNET_SYSERR;
1351 memcpy (pool_start + *pool_pos, str, strlen (str) + 1);
1352 *pool_pos = next_pos;
1353 return GNUNET_OK;
1354}
1355
1356
1357/**
1358 * Handle LIST-message.
1359 *
1360 * @param cls identification of the client
1361 * @param request the actual message
1362 */
1363static void
1364handle_list (void *cls, const struct GNUNET_ARM_Message *request)
1365{
1366 struct GNUNET_SERVICE_Client *client = cls;
1367 struct GNUNET_MQ_Envelope *env;
1368 struct GNUNET_ARM_ListResultMessage *msg;
1369 size_t extra_size;
1370 struct ServiceList *sl;
1371 uint16_t count;
1372 size_t pool_size;
1373 size_t pool_pos;
1374 char *pool_start;
1375 struct GNUNET_ARM_ServiceInfoMessage *ssm;
1376
1377 GNUNET_break_op (0 == ntohl (request->reserved));
1378 count = 0;
1379 pool_size = 0;
1380
1381 /* Do one pass over the list to compute the number of services
1382 * and the string pool size */
1383 for (sl = running_head; NULL != sl; sl = sl->next)
1384 {
1385 pool_size += strlen (sl->name) + 1;
1386 pool_size += strlen (sl->binary) + 1;
1387 count++;
1388 }
1389
1390 extra_size = pool_size + (count * sizeof (struct
1391 GNUNET_ARM_ServiceInfoMessage));
1392 env = GNUNET_MQ_msg_extra (msg,
1393 extra_size,
1394 GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT);
1395 msg->arm_msg.request_id = request->request_id;
1396 msg->count = htons (count);
1397
1398 ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &msg[1];
1399 pool_start = (char *) (ssm + count);
1400 pool_pos = 0;
1401
1402 for (sl = running_head; NULL != sl; sl = sl->next)
1403 {
1404 ssm->name_index = htons ((uint16_t) pool_pos);
1405 GNUNET_assert (GNUNET_OK == pool_write (pool_start, pool_size, &pool_pos,
1406 sl->name));
1407 ssm->binary_index = htons ((uint16_t) pool_pos);
1408 GNUNET_assert (GNUNET_OK == pool_write (pool_start, pool_size, &pool_pos,
1409 sl->binary));
1410 if (NULL == sl->proc)
1411 {
1412 if (0 == sl->last_started_at.abs_value_us)
1413 {
1414 /* Process never started */
1415 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_STOPPED);
1416 }
1417 else if (0 == sl->last_exit_status)
1418 {
1419 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_FINISHED);
1420 }
1421 else
1422 {
1423 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_FAILED);
1424 ssm->last_exit_status = htons (sl->last_exit_status);
1425 }
1426 }
1427 else if ((NULL != sl->killing_client) || (GNUNET_YES == in_shutdown))
1428 {
1429 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_STOPPING);
1430 }
1431 else
1432 {
1433 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_STARTED);
1434 }
1435 ssm->last_started_at = GNUNET_TIME_absolute_hton (sl->last_started_at);
1436 ssm->restart_at = GNUNET_TIME_absolute_hton (sl->restart_at);
1437 ssm++;
1438 }
1439 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1440 GNUNET_SERVICE_client_continue (client);
1441}
1442
1443
1444/**
1445 * Handle TEST-message by sending back TEST.
1446 *
1447 * @param cls identification of the client
1448 * @param message the actual message
1449 */
1450static void
1451handle_test (void *cls, const struct GNUNET_MessageHeader *message)
1452{
1453 struct GNUNET_SERVICE_Client *client = cls;
1454 struct GNUNET_MQ_Envelope *env;
1455 struct GNUNET_MessageHeader *msg;
1456
1457 (void) message;
1458 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_TEST);
1459 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1460 GNUNET_SERVICE_client_continue (client);
1461}
1462
1463
1464/**
1465 * We are done with everything. Stop remaining
1466 * tasks, signal handler and the server.
1467 */
1468static void
1469do_shutdown ()
1470{
1471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last shutdown phase\n");
1472 if (NULL != notifier)
1473 {
1474 GNUNET_notification_context_destroy (notifier);
1475 notifier = NULL;
1476 }
1477 if (NULL != service)
1478 {
1479 GNUNET_SERVICE_shutdown (service);
1480 service = NULL;
1481 }
1482 if (NULL != child_death_task)
1483 {
1484 GNUNET_SCHEDULER_cancel (child_death_task);
1485 child_death_task = NULL;
1486 }
1487}
1488
1489
1490/**
1491 * Count how many services are still active.
1492 *
1493 * @param running_head list of services
1494 * @return number of active services found
1495 */
1496static unsigned int
1497list_count (struct ServiceList *running_head)
1498{
1499 struct ServiceList *i;
1500 unsigned int res;
1501
1502 for (res = 0, i = running_head; NULL != i; i = i->next, res++)
1503 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", i->name);
1504 return res;
1505}
1506
1507
1508/**
1509 * Task run for shutdown.
1510 *
1511 * @param cls closure, NULL if we need to self-restart
1512 */
1513static void
1514shutdown_task (void *cls)
1515{
1516 struct ServiceList *pos;
1517 struct ServiceList *nxt;
1518 struct ServiceListeningInfo *sli;
1519
1520 (void) cls;
1521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "First shutdown phase\n");
1522 if (NULL != child_restart_task)
1523 {
1524 GNUNET_SCHEDULER_cancel (child_restart_task);
1525 child_restart_task = NULL;
1526 }
1527 in_shutdown = GNUNET_YES;
1528 /* first, stop listening */
1529 for (pos = running_head; NULL != pos; pos = pos->next)
1530 {
1531 while (NULL != (sli = pos->listen_head))
1532 {
1533 GNUNET_CONTAINER_DLL_remove (pos->listen_head, pos->listen_tail, sli);
1534 if (NULL != sli->accept_task)
1535 {
1536 GNUNET_SCHEDULER_cancel (sli->accept_task);
1537 sli->accept_task = NULL;
1538 }
1539 GNUNET_break (GNUNET_OK ==
1540 GNUNET_NETWORK_socket_close (sli->listen_socket));
1541 GNUNET_free (sli->service_addr);
1542 GNUNET_free (sli);
1543 }
1544 }
1545 /* then, shutdown all existing service processes */
1546 nxt = running_head;
1547 while (NULL != (pos = nxt))
1548 {
1549 nxt = pos->next;
1550 if (NULL != pos->proc)
1551 {
1552 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", pos->name);
1553 pos->killed_at = GNUNET_TIME_absolute_get ();
1554 if (0 != GNUNET_OS_process_kill (pos->proc, GNUNET_TERM_SIG))
1555 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
1556 }
1557 else
1558 {
1559 free_service (pos);
1560 }
1561 }
1562 /* finally, should all service processes be already gone, terminate for real */
1563 if (NULL == running_head)
1564 do_shutdown ();
1565 else
1566 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1567 "Delaying shutdown, have %u children still running\n",
1568 list_count (running_head));
1569}
1570
1571
1572/**
1573 * Task run whenever it is time to restart a child that died.
1574 *
1575 * @param cls closure, always NULL
1576 */
1577static void
1578delayed_restart_task (void *cls)
1579
1580{
1581 struct ServiceList *sl;
1582 struct GNUNET_TIME_Relative lowestRestartDelay;
1583 struct ServiceListeningInfo *sli;
1584
1585 (void) cls;
1586 child_restart_task = NULL;
1587 GNUNET_assert (GNUNET_NO == in_shutdown);
1588 lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
1589
1590 /* check for services that need to be restarted due to
1591 * configuration changes or because the last restart failed */
1592 for (sl = running_head; NULL != sl; sl = sl->next)
1593 {
1594 if (NULL != sl->proc)
1595 continue;
1596 /* service is currently not running */
1597 if (0 == GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value_us)
1598 {
1599 /* restart is now allowed */
1600 if (sl->force_start)
1601 {
1602 /* process should run by default, start immediately */
1603 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1604 _ ("Restarting service `%s'.\n"),
1605 sl->name);
1606 start_process (sl, NULL, 0);
1607 }
1608 else
1609 {
1610 /* process is run on-demand, ensure it is re-started if there is demand */
1611 for (sli = sl->listen_head; NULL != sli; sli = sli->next)
1612 if (NULL == sli->accept_task)
1613 {
1614 /* accept was actually paused, so start it again */
1615 sli->accept_task =
1616 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1617 sli->listen_socket,
1618 &accept_connection,
1619 sli);
1620 }
1621 }
1622 }
1623 else
1624 {
1625 /* update calculation for earliest time to reactivate a service */
1626 lowestRestartDelay =
1627 GNUNET_TIME_relative_min (lowestRestartDelay,
1628 GNUNET_TIME_absolute_get_remaining (
1629 sl->restart_at));
1630 }
1631 }
1632 if (lowestRestartDelay.rel_value_us !=
1633 GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
1634 {
1635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1636 "Will restart process in %s\n",
1637 GNUNET_STRINGS_relative_time_to_string (lowestRestartDelay,
1638 GNUNET_YES));
1639 child_restart_task =
1640 GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay,
1641 GNUNET_SCHEDULER_PRIORITY_IDLE,
1642 &delayed_restart_task,
1643 NULL);
1644 }
1645}
1646
1647
1648/**
1649 * Task triggered whenever we receive a SIGCHLD (child
1650 * process died).
1651 *
1652 * @param cls closure, NULL
1653 */
1654static void
1655maint_child_death (void *cls)
1656{
1657 struct ServiceList *pos;
1658 struct ServiceList *next;
1659 struct ServiceListeningInfo *sli;
1660 const char *statstr;
1661 int statcode;
1662 int ret;
1663 char c[16];
1664 enum GNUNET_OS_ProcessStatusType statusType;
1665 unsigned long statusCode;
1666 const struct GNUNET_DISK_FileHandle *pr;
1667
1668 (void) cls;
1669 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
1670 child_death_task = NULL;
1671 /* consume the signal */
1672 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c)));
1673
1674 /* check for services that died (WAITPID) */
1675 next = running_head;
1676 while (NULL != (pos = next))
1677 {
1678 next = pos->next;
1679
1680 if (NULL == pos->proc)
1681 {
1682 if (GNUNET_YES == in_shutdown)
1683 free_service (pos);
1684 continue;
1685 }
1686#if HAVE_WAIT4
1687 if (NULL != wait_file)
1688 {
1689 /* need to use 'wait4()' to obtain and log performance data */
1690 struct rusage ru;
1691 int status;
1692 pid_t pid;
1693
1694 pid = GNUNET_OS_process_get_pid (pos->proc);
1695 ret = wait4 (pid, &status, WNOHANG, &ru);
1696 if (ret <= 0)
1697 continue; /* no process done */
1698 if (WIFEXITED (status))
1699 {
1700 statusType = GNUNET_OS_PROCESS_EXITED;
1701 statusCode = WEXITSTATUS (status);
1702 }
1703 else if (WIFSIGNALED (status))
1704 {
1705 statusType = GNUNET_OS_PROCESS_SIGNALED;
1706 statusCode = WTERMSIG (status);
1707 }
1708 else if (WIFSTOPPED (status))
1709 {
1710 statusType = GNUNET_OS_PROCESS_SIGNALED;
1711 statusCode = WSTOPSIG (status);
1712 }
1713#ifdef WIFCONTINUED
1714 else if (WIFCONTINUED (status))
1715 {
1716 statusType = GNUNET_OS_PROCESS_RUNNING;
1717 statusCode = 0;
1718 }
1719#endif
1720 else
1721 {
1722 statusType = GNUNET_OS_PROCESS_UNKNOWN;
1723 statusCode = 0;
1724 }
1725 if ((GNUNET_OS_PROCESS_EXITED == statusType) ||
1726 (GNUNET_OS_PROCESS_SIGNALED == statusType))
1727 {
1728 double utime = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 10e6);
1729 double stime = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 10e6);
1730 fprintf (wait_file,
1731 "%s(%u) %.3f %.3f %llu %llu %llu %llu %llu\n",
1732 pos->binary,
1733 (unsigned int) pid,
1734 utime,
1735 stime,
1736 (unsigned long long) ru.ru_maxrss,
1737 (unsigned long long) ru.ru_inblock,
1738 (unsigned long long) ru.ru_oublock,
1739 (unsigned long long) ru.ru_nvcsw,
1740 (unsigned long long) ru.ru_nivcsw);
1741 }
1742 }
1743 else /* continue with JUST this "if" as "else" (intentionally no brackets!) */
1744#endif
1745 if ((GNUNET_SYSERR == (ret = GNUNET_OS_process_status (pos->proc,
1746 &statusType,
1747 &statusCode))) ||
1748 (ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) ||
1749 (statusType == GNUNET_OS_PROCESS_UNKNOWN) ||
1750 (statusType == GNUNET_OS_PROCESS_RUNNING))
1751 continue;
1752
1753 if (statusType == GNUNET_OS_PROCESS_EXITED)
1754 {
1755 statstr = _ (/* process termination method */ "exit");
1756 statcode = statusCode;
1757 }
1758 else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
1759 {
1760 statstr = _ (/* process termination method */ "signal");
1761 statcode = statusCode;
1762 }
1763 else
1764 {
1765 statstr = _ (/* process termination method */ "unknown");
1766 statcode = 0;
1767 }
1768 if (0 != pos->killed_at.abs_value_us)
1769 {
1770 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1771 _ ("Service `%s' took %s to terminate\n"),
1772 pos->name,
1773 GNUNET_STRINGS_relative_time_to_string (
1774 GNUNET_TIME_absolute_get_duration (pos->killed_at),
1775 GNUNET_YES));
1776 }
1777 GNUNET_OS_process_destroy (pos->proc);
1778 pos->proc = NULL;
1779 broadcast_status (pos->name, GNUNET_ARM_SERVICE_STOPPED, NULL);
1780 if (NULL != pos->killing_client)
1781 {
1782 signal_result (pos->killing_client,
1783 pos->name,
1784 pos->killing_client_request_id,
1785 GNUNET_ARM_RESULT_STOPPED);
1786 pos->killing_client = NULL;
1787 pos->killing_client_request_id = 0;
1788 }
1789 if (GNUNET_YES != in_shutdown)
1790 {
1791 pos->last_exit_status = statcode;
1792 if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0))
1793 {
1794 /* process terminated normally, allow restart at any time */
1795 pos->restart_at.abs_value_us = 0;
1796 GNUNET_log (
1797 GNUNET_ERROR_TYPE_INFO,
1798 _ ("Service `%s' terminated normally, will restart at any time\n"),
1799 pos->name);
1800 /* process can still be re-started on-demand, ensure it is re-started if there is demand */
1801 for (sli = pos->listen_head; NULL != sli; sli = sli->next)
1802 {
1803 GNUNET_break (NULL == sli->accept_task);
1804 sli->accept_task =
1805 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1806 sli->listen_socket,
1807 &accept_connection,
1808 sli);
1809 }
1810 }
1811 else
1812 {
1813 GNUNET_log (
1814 GNUNET_ERROR_TYPE_WARNING,
1815 _ ("Service `%s' terminated with status %s/%d, will restart in %s\n"),
1816 pos->name,
1817 statstr,
1818 statcode,
1819 GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES));
1820 {
1821 /* Reduce backoff based on runtime of the process,
1822 so that there is a cool-down if a process actually
1823 runs for a while. */
1824 struct GNUNET_TIME_Relative runtime;
1825 unsigned int minutes;
1826
1827 runtime = GNUNET_TIME_absolute_get_duration (pos->restart_at);
1828 minutes =
1829 runtime.rel_value_us / GNUNET_TIME_UNIT_MINUTES.rel_value_us;
1830 if (minutes > 31)
1831 pos->backoff = GNUNET_TIME_UNIT_ZERO;
1832 else
1833 pos->backoff.rel_value_us <<= minutes;
1834 }
1835 /* schedule restart */
1836 pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
1837 pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
1838 if (NULL != child_restart_task)
1839 GNUNET_SCHEDULER_cancel (child_restart_task);
1840 child_restart_task =
1841 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1842 &delayed_restart_task,
1843 NULL);
1844 }
1845 }
1846 else
1847 {
1848 free_service (pos);
1849 }
1850 }
1851 child_death_task =
1852 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1853 pr,
1854 &maint_child_death,
1855 NULL);
1856 if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
1857 do_shutdown ();
1858 else if (GNUNET_YES == in_shutdown)
1859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1860 "Delaying shutdown after child's death, still have %u children\n",
1861 list_count (running_head));
1862}
1863
1864
1865/**
1866 * Signal handler called for SIGCHLD. Triggers the
1867 * respective handler by writing to the trigger pipe.
1868 */
1869static void
1870sighandler_child_death ()
1871{
1872 static char c;
1873 int old_errno = errno; /* back-up errno */
1874
1875 GNUNET_break (
1876 1 ==
1877 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
1878 GNUNET_DISK_PIPE_END_WRITE),
1879 &c,
1880 sizeof(c)));
1881 errno = old_errno; /* restore errno */
1882}
1883
1884
1885/**
1886 * Setup our service record for the given section in the configuration file
1887 * (assuming the section is for a service).
1888 *
1889 * @param cls unused
1890 * @param section a section in the configuration file
1891 * @return #GNUNET_OK (continue)
1892 */
1893static void
1894setup_service (void *cls, const char *section)
1895{
1896 struct ServiceList *sl;
1897 char *binary;
1898 char *config;
1899 struct stat sbuf;
1900 struct sockaddr **addrs;
1901 socklen_t *addr_lens;
1902 int ret;
1903
1904 (void) cls;
1905 if (0 == strcasecmp (section, "arm"))
1906 return;
1907 if (GNUNET_OK !=
1908 GNUNET_CONFIGURATION_get_value_string (cfg, section, "BINARY", &binary))
1909 {
1910 /* not a service section */
1911 return;
1912 }
1913 if ((GNUNET_YES ==
1914 GNUNET_CONFIGURATION_have_value (cfg, section, "RUN_PER_USER")) &&
1915 (GNUNET_YES ==
1916 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "RUN_PER_USER")))
1917 {
1918 if (GNUNET_NO == start_user)
1919 {
1920 GNUNET_free (binary);
1921 return; /* user service, and we don't deal with those */
1922 }
1923 }
1924 else
1925 {
1926 if (GNUNET_NO == start_system)
1927 {
1928 GNUNET_free (binary);
1929 return; /* system service, and we don't deal with those */
1930 }
1931 }
1932 sl = find_service (section);
1933 if (NULL != sl)
1934 {
1935 /* got the same section twice!? */
1936 GNUNET_break (0);
1937 GNUNET_free (binary);
1938 return;
1939 }
1940 config = NULL;
1941 if (((GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1942 section,
1943 "CONFIG",
1944 &config)) &&
1945 (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1946 "PATHS",
1947 "DEFAULTCONFIG",
1948 &config))) ||
1949 (0 != stat (config, &sbuf)))
1950 {
1951 if (NULL != config)
1952 {
1953 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
1954 section,
1955 "CONFIG",
1956 strerror (errno));
1957 GNUNET_free (config);
1958 config = NULL;
1959 }
1960 }
1961 sl = GNUNET_new (struct ServiceList);
1962 sl->name = GNUNET_strdup (section);
1963 sl->binary = binary;
1964 sl->config = config;
1965 sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
1966 sl->restart_at = GNUNET_TIME_UNIT_FOREVER_ABS;
1967 if (GNUNET_CONFIGURATION_have_value (cfg, section, "PIPECONTROL"))
1968 sl->pipe_control =
1969 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "PIPECONTROL");
1970 GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl);
1971 if (GNUNET_YES ==
1972 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "IMMEDIATE_START"))
1973 {
1974 sl->force_start = GNUNET_YES;
1975 if (GNUNET_YES ==
1976 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "NOARMBIND"))
1977 return;
1978 }
1979 else
1980 {
1981 if (GNUNET_YES !=
1982 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "START_ON_DEMAND"))
1983 return;
1984 }
1985 if (0 >= (ret = get_server_addresses (section, cfg, &addrs, &addr_lens)))
1986 return;
1987 /* this will free (or capture) addrs[i] */
1988 for (unsigned int i = 0; i < (unsigned int) ret; i++)
1989 create_listen_socket (addrs[i], addr_lens[i], sl);
1990 GNUNET_free (addrs);
1991 GNUNET_free (addr_lens);
1992}
1993
1994
1995/**
1996 * A client connected, mark as a monitoring client.
1997 *
1998 * @param cls closure
1999 * @param client identification of the client
2000 * @param mq queue to talk to @a client
2001 * @return @a client
2002 */
2003static void *
2004client_connect_cb (void *cls,
2005 struct GNUNET_SERVICE_Client *client,
2006 struct GNUNET_MQ_Handle *mq)
2007{
2008 /* All clients are considered to be of the "monitor" kind
2009 * (that is, they don't affect ARM shutdown).
2010 */
2011 (void) cls;
2012 (void) mq;
2013 GNUNET_SERVICE_client_mark_monitor (client);
2014 return client;
2015}
2016
2017
2018/**
2019 * A client disconnected, clean up associated state.
2020 *
2021 * @param cls closure
2022 * @param client identification of the client
2023 * @param app_ctx must match @a client
2024 */
2025static void
2026client_disconnect_cb (void *cls,
2027 struct GNUNET_SERVICE_Client *client,
2028 void *app_ctx)
2029{
2030 (void) cls;
2031 GNUNET_assert (client == app_ctx);
2032 for (struct ServiceList *sl = running_head; NULL != sl; sl = sl->next)
2033 if (sl->killing_client == client)
2034 sl->killing_client = NULL;
2035}
2036
2037
2038/**
2039 * Handle MONITOR-message.
2040 *
2041 * @param cls identification of the client
2042 * @param message the actual message
2043 * @return #GNUNET_OK to keep the connection open,
2044 * #GNUNET_SYSERR to close it (signal serious error)
2045 */
2046static void
2047handle_monitor (void *cls, const struct GNUNET_MessageHeader *message)
2048{
2049 struct GNUNET_SERVICE_Client *client = cls;
2050
2051 (void) message;
2052 /* FIXME: might want to start by letting monitor know about
2053 services that are already running */
2054 /* Removal is handled by the server implementation, internally. */
2055 GNUNET_notification_context_add (notifier,
2056 GNUNET_SERVICE_client_get_mq (client));
2057 broadcast_status ("arm", GNUNET_ARM_SERVICE_MONITORING_STARTED, client);
2058 GNUNET_SERVICE_client_continue (client);
2059}
2060
2061
2062/**
2063 * Process arm requests.
2064 *
2065 * @param cls closure, NULL
2066 * @param serv the initialized service
2067 * @param c configuration to use
2068 */
2069static void
2070run (void *cls,
2071 const struct GNUNET_CONFIGURATION_Handle *c,
2072 struct GNUNET_SERVICE_Handle *serv)
2073{
2074 struct ServiceList *sl;
2075
2076 (void) cls;
2077 cfg = c;
2078 service = serv;
2079 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
2080 child_death_task = GNUNET_SCHEDULER_add_read_file (
2081 GNUNET_TIME_UNIT_FOREVER_REL,
2082 GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ),
2083 &maint_child_death,
2084 NULL);
2085#if HAVE_WAIT4
2086 if (GNUNET_OK ==
2087 GNUNET_CONFIGURATION_get_value_filename (cfg,
2088 "ARM",
2089 "RESOURCE_DIAGNOSTICS",
2090 &wait_filename))
2091 {
2092 wait_file = fopen (wait_filename, "w");
2093 if (NULL == wait_file)
2094 {
2095 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2096 "fopen",
2097 wait_filename);
2098 }
2099 }
2100#endif
2101 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2102 "ARM",
2103 "GLOBAL_PREFIX",
2104 &prefix_command))
2105 prefix_command = GNUNET_strdup ("");
2106 else
2107 prefix_command = GNUNET_CONFIGURATION_expand_dollar (cfg, prefix_command);
2108 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2109 "ARM",
2110 "GLOBAL_POSTFIX",
2111 &final_option))
2112 final_option = GNUNET_strdup ("");
2113 else
2114 final_option = GNUNET_CONFIGURATION_expand_dollar (cfg, final_option);
2115 start_user =
2116 GNUNET_CONFIGURATION_get_value_yesno (cfg, "ARM", "START_USER_SERVICES");
2117 start_system =
2118 GNUNET_CONFIGURATION_get_value_yesno (cfg, "ARM", "START_SYSTEM_SERVICES");
2119 if ((GNUNET_NO == start_user) && (GNUNET_NO == start_system))
2120 {
2121 GNUNET_log (
2122 GNUNET_ERROR_TYPE_ERROR,
2123 "Please configure either START_USER_SERVICES or START_SYSTEM_SERVICES or both.\n");
2124 GNUNET_SCHEDULER_shutdown ();
2125 global_ret = 1;
2126 return;
2127 }
2128 GNUNET_CONFIGURATION_iterate_sections (cfg, &setup_service, NULL);
2129
2130 /* start default services... */
2131 for (sl = running_head; NULL != sl; sl = sl->next)
2132 if (GNUNET_YES == sl->force_start)
2133 start_process (sl, NULL, 0);
2134 notifier = GNUNET_notification_context_create (MAX_NOTIFY_QUEUE);
2135}
2136
2137
2138/**
2139 * The main function for the arm service.
2140 *
2141 * @param argc number of arguments from the command line
2142 * @param argv command line arguments
2143 * @return 0 ok, 1 on error
2144 */
2145int
2146main (int argc, char *const *argv)
2147{
2148 struct GNUNET_SIGNAL_Context *shc_chld;
2149 struct GNUNET_MQ_MessageHandler handlers[] = {
2150 GNUNET_MQ_hd_var_size (start,
2151 GNUNET_MESSAGE_TYPE_ARM_START,
2152 struct GNUNET_ARM_Message,
2153 NULL),
2154 GNUNET_MQ_hd_var_size (stop,
2155 GNUNET_MESSAGE_TYPE_ARM_STOP,
2156 struct GNUNET_ARM_Message,
2157 NULL),
2158 GNUNET_MQ_hd_fixed_size (monitor,
2159 GNUNET_MESSAGE_TYPE_ARM_MONITOR,
2160 struct GNUNET_MessageHeader,
2161 NULL),
2162 GNUNET_MQ_hd_fixed_size (list,
2163 GNUNET_MESSAGE_TYPE_ARM_LIST,
2164 struct GNUNET_ARM_Message,
2165 NULL),
2166 GNUNET_MQ_hd_fixed_size (test,
2167 GNUNET_MESSAGE_TYPE_ARM_TEST,
2168 struct GNUNET_MessageHeader,
2169 NULL),
2170 GNUNET_MQ_handler_end ()
2171 };
2172
2173 sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
2174 GNUNET_assert (NULL != sigpipe);
2175 shc_chld =
2176 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
2177 &sighandler_child_death);
2178 if (0 != GNUNET_SERVICE_run_ (argc,
2179 argv,
2180 "arm",
2181 GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN
2182 | GNUNET_SERVICE_OPTION_CLOSE_LSOCKS,
2183 &run,
2184 &client_connect_cb,
2185 &client_disconnect_cb,
2186 NULL,
2187 handlers))
2188 global_ret = 2;
2189#if HAVE_WAIT4
2190 if (NULL != wait_file)
2191 {
2192 fclose (wait_file);
2193 wait_file = NULL;
2194 }
2195 if (NULL != wait_filename)
2196 {
2197 GNUNET_free (wait_filename);
2198 wait_filename = NULL;
2199 }
2200#endif
2201 GNUNET_SIGNAL_handler_uninstall (shc_chld);
2202 shc_chld = NULL;
2203 GNUNET_DISK_pipe_close (sigpipe);
2204 sigpipe = NULL;
2205 return global_ret;
2206}
2207
2208
2209#if defined(__linux__) && defined(__GLIBC__)
2210#include <malloc.h>
2211
2212/**
2213 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
2214 */
2215void __attribute__ ((constructor))
2216GNUNET_ARM_memory_init ()
2217{
2218 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
2219 mallopt (M_TOP_PAD, 1 * 1024);
2220 malloc_trim (0);
2221}
2222
2223
2224#endif
2225
2226
2227/* end of gnunet-service-arm.c */
diff --git a/src/service/arm/meson.build b/src/service/arm/meson.build
new file mode 100644
index 000000000..bfeb0b31f
--- /dev/null
+++ b/src/service/arm/meson.build
@@ -0,0 +1,86 @@
1libgnunetarm_src = ['arm_api.c',
2 'arm_monitor_api.c']
3
4gnunetservicearm_src = ['gnunet-service-arm.c']
5
6gnunetarm_src = ['gnunet-arm.c']
7
8testarmapi_src = ['test_arm_api.c']
9testexpbo_src = ['test_exponential_backoff.c']
10testgnunetservice_src = ['test_gnunet_service_arm.c']
11
12configure_file(input : 'arm.conf.in',
13 output : 'arm.conf',
14 configuration : cdata,
15 install: true,
16 install_dir: pkgcfgdir)
17
18if get_option('monolith')
19 foreach p : libgnunetarm_src + gnunetservicearm_src
20 gnunet_src += 'arm/' + p
21 endforeach
22 subdir_done()
23endif
24
25libgnunetarm = library('gnunetarm',
26 libgnunetarm_src,
27 dependencies: libgnunetutil_dep,
28 version: '2.0.0',
29 soversion: '2',
30 include_directories: [incdir, configuration_inc],
31 install: true,
32 install_dir: get_option('libdir'))
33pkg.generate(libgnunetarm, url: 'https://www.gnunet.org',
34 description : 'Provides API for accessing the Automated Restart Manager service')
35libgnunetarm_dep = declare_dependency(link_with : libgnunetarm)
36executable ('gnunet-arm',
37 gnunetarm_src,
38 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
39 include_directories: [incdir, configuration_inc],
40 install: true,
41 install_dir: get_option('bindir'))
42
43executable ('gnunet-service-arm',
44 gnunetservicearm_src,
45 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
46 include_directories: [incdir, configuration_inc],
47 install: true,
48 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
49
50mockservice = executable ('mockup-service',
51 ['mockup-service.c'],
52 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
53 include_directories: [incdir, configuration_inc],
54 install: false)
55
56testarmapi = executable ('test_arm_api',
57 testarmapi_src,
58 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
59 include_directories: [incdir, configuration_inc],
60 install: false)
61testexpbo = executable ('test_exponential_backoff',
62 testexpbo_src,
63 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
64 include_directories: [incdir, configuration_inc],
65 install: false)
66testgnunetservice = executable ('test_gnunet_service',
67 testgnunetservice_src,
68 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
69 include_directories: [incdir, configuration_inc],
70 install: false)
71configure_file(copy: true,
72 input: 'test_arm_api_data.conf',
73 output: 'test_arm_api_data.conf')
74
75# FIXME: We need to convert the convoluted awk script from Makefile.am here
76#configure_file(copy: true,
77# input: 'test_gnunet_arm.py.in',
78# output: 'test_gnunet_arm.py')
79
80test('test_arm_api', testarmapi, workdir: meson.current_build_dir(),
81 suite: 'arm')
82test('test_exponential_backoff', testexpbo, workdir: meson.current_build_dir(),
83 depends: mockservice,
84 timeout: 60, suite: 'arm')
85test('test_gnunet_service_arm', testgnunetservice, workdir: meson.current_build_dir(),
86 suite: 'arm')
diff --git a/src/service/arm/mockup-service.c b/src/service/arm/mockup-service.c
new file mode 100644
index 000000000..3d3f79d35
--- /dev/null
+++ b/src/service/arm/mockup-service.c
@@ -0,0 +1,123 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2007, 2008, 2009, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21#include "platform.h"
22#include <stdlib.h>
23#include "gnunet_util_lib.h"
24#include "gnunet_protocols.h"
25
26
27static int special_ret = 0;
28
29/**
30 * Handler for STOP message.
31 *
32 * @param cls client identification of the client
33 * @param message the actual message
34 */
35static void
36handle_stop (void *cls, const struct GNUNET_MessageHeader *message)
37{
38 struct GNUNET_SERVICE_Client *client = cls;
39
40 (void) message;
41 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
42 _ ("Initiating shutdown as requested by client.\n"));
43 GNUNET_SERVICE_client_persist (client);
44 GNUNET_SCHEDULER_shutdown ();
45 /* ARM won't exponentially increase restart delay if we
46 * terminate normally. This changes the return code.
47 */
48 special_ret = 1;
49}
50
51
52/**
53 * Callback called when a client connects to the service.
54 *
55 * @param cls closure for the service
56 * @param c the new client that connected to the service
57 * @param mq the message queue used to send messages to the client
58 * @return @a c
59 */
60static void *
61client_connect_cb (void *cls,
62 struct GNUNET_SERVICE_Client *c,
63 struct GNUNET_MQ_Handle *mq)
64{
65 (void) cls;
66 (void) mq;
67 return c;
68}
69
70
71/**
72 * Callback called when a client disconnected from the service
73 *
74 * @param cls closure for the service
75 * @param c the client that disconnected
76 * @param internal_cls should be equal to @a c
77 */
78static void
79client_disconnect_cb (void *cls,
80 struct GNUNET_SERVICE_Client *c,
81 void *internal_cls)
82{
83 (void) cls;
84 GNUNET_assert (c == internal_cls);
85}
86
87
88static void
89run (void *cls,
90 const struct GNUNET_CONFIGURATION_Handle *cfg,
91 struct GNUNET_SERVICE_Handle *service)
92{
93 (void) cls;
94 (void) cfg;
95 (void) service;
96 /* nothing to do */
97}
98
99
100/**
101 * Define "main" method using service macro.
102 */
103GNUNET_SERVICE_MAIN ("do-nothing",
104 GNUNET_SERVICE_OPTION_NONE,
105 &run,
106 &client_connect_cb,
107 &client_disconnect_cb,
108 NULL,
109 GNUNET_MQ_hd_fixed_size (stop,
110 GNUNET_MESSAGE_TYPE_ARM_STOP,
111 struct GNUNET_MessageHeader,
112 NULL),
113 GNUNET_MQ_handler_end ());
114
115
116/**
117 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
118 */
119void __attribute__ ((destructor))
120GNUNET_mockup_done ()
121{
122 _exit (special_ret);
123}
diff --git a/src/service/arm/mockup_service b/src/service/arm/mockup_service
new file mode 100755
index 000000000..32f4ccc14
--- /dev/null
+++ b/src/service/arm/mockup_service
@@ -0,0 +1,130 @@
1#! /bin/sh
2
3# mockup_service - temporary wrapper script for .libs/mockup_service
4# Generated by ltmain.sh (GNU libtool) 2.2.6
5#
6# The mockup_service program cannot be directly executed until all the libtool
7# libraries that it depends on are installed.
8#
9# This wrapper script should never be moved out of the build directory.
10# If it is, it will not operate correctly.
11
12# Sed substitution that helps us do robust quoting. It backslashifies
13# metacharacters that are still active within double-quoted strings.
14Xsed='/usr/bin/sed -e 1s/^X//'
15sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
16
17# Be Bourne compatible
18if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
19 emulate sh
20 NULLCMD=:
21 # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
22 # is contrary to our usage. Disable this feature.
23 alias -g '${1+"$@"}'='"$@"'
24 setopt NO_GLOB_SUBST
25else
26 case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
27fi
28BIN_SH=xpg4; export BIN_SH # for Tru64
29DUALCASE=1; export DUALCASE # for MKS sh
30
31# The HP-UX ksh and POSIX shell print the target directory to stdout
32# if CDPATH is set.
33(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
34
35relink_command=""
36
37# This environment variable determines our operation mode.
38if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
39 # install mode needs the following variables:
40 generated_by_libtool_version='2.2.6'
41 notinst_deplibs=' ../../src/util/libgnunetutil.la'
42else
43 # When we are sourced in execute mode, $file and $ECHO are already set.
44 if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
45 ECHO="echo"
46 file="$0"
47 # Make sure echo works.
48 if test "X$1" = X--no-reexec; then
49 # Discard the --no-reexec flag, and continue.
50 shift
51 elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then
52 # Yippee, $ECHO works!
53 :
54 else
55 # Restart under the correct shell, and then maybe $ECHO will work.
56 exec /bin/sh "$0" --no-reexec ${1+"$@"}
57 fi
58 fi
59
60 # Find the directory that this script lives in.
61 thisdir=`$ECHO "X$file" | $Xsed -e 's%/[^/]*$%%'`
62 test "x$thisdir" = "x$file" && thisdir=.
63
64 # Follow symbolic links until we get to the real thisdir.
65 file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
66 while test -n "$file"; do
67 destdir=`$ECHO "X$file" | $Xsed -e 's%/[^/]*$%%'`
68
69 # If there was a directory component, then change thisdir.
70 if test "x$destdir" != "x$file"; then
71 case "$destdir" in
72 [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
73 *) thisdir="$thisdir/$destdir" ;;
74 esac
75 fi
76
77 file=`$ECHO "X$file" | $Xsed -e 's%^.*/%%'`
78 file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
79 done
80
81
82 # Usually 'no', except on cygwin/mingw when embedded into
83 # the cwrapper.
84 WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
85 if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
86 # special case for '.'
87 if test "$thisdir" = "."; then
88 thisdir=`pwd`
89 fi
90 # remove .libs from thisdir
91 case "$thisdir" in
92 *[\\/].libs ) thisdir=`$ECHO "X$thisdir" | $Xsed -e 's%[\\/][^\\/]*$%%'` ;;
93 .libs ) thisdir=. ;;
94 esac
95 fi
96
97 # Try to get the absolute directory name.
98 absdir=`cd "$thisdir" && pwd`
99 test -n "$absdir" && thisdir="$absdir"
100
101 program='mockup_service'
102 progdir="$thisdir/.libs"
103
104
105 if test -f "$progdir/$program"; then
106 # Add our own library path to DYLD_LIBRARY_PATH
107 DYLD_LIBRARY_PATH="/Users/soufi/Career/Munich/TUM/MasterOfScience/Research/GNUnet/EclipseWorkspaceGNUnet/gnunet/src/util/.libs:$DYLD_LIBRARY_PATH"
108
109 # Some systems cannot cope with colon-terminated DYLD_LIBRARY_PATH
110 # The second colon is a workaround for a bug in BeOS R4 sed
111 DYLD_LIBRARY_PATH=`$ECHO "X$DYLD_LIBRARY_PATH" | $Xsed -e 's/::*$//'`
112
113 export DYLD_LIBRARY_PATH
114
115 if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
116 # Run the actual program with our arguments.
117
118 exec "$progdir/$program" ${1+"$@"}
119
120 $ECHO "$0: cannot exec $program $*" 1>&2
121 exit 1
122 fi
123 else
124 # The program doesn't exist.
125 $ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2
126 $ECHO "This script is just a wrapper for $program." 1>&2
127 echo "See the libtool documentation for more information." 1>&2
128 exit 1
129 fi
130fi
diff --git a/src/service/arm/test_arm_api.c b/src/service/arm/test_arm_api.c
new file mode 100644
index 000000000..56a0abbd2
--- /dev/null
+++ b/src/service/arm/test_arm_api.c
@@ -0,0 +1,258 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2011, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file arm/test_arm_api.c
22 * @brief testcase for arm_api.c
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_arm_service.h"
27#include "gnunet_resolver_service.h"
28
29#define LOG(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
30
31#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
32
33static const struct GNUNET_CONFIGURATION_Handle *cfg;
34
35static struct GNUNET_ARM_Handle *arm;
36
37static struct GNUNET_ARM_Operation *op;
38
39static int ok = 1;
40
41static int phase = 0;
42
43
44static void
45arm_stop_cb (void *cls,
46 enum GNUNET_ARM_RequestStatus status,
47 enum GNUNET_ARM_Result result)
48{
49 op = NULL;
50 /* (6), a stop request should be sent to ARM successfully */
51 /* ARM should report that it is stopping */
52 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
53 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
54 GNUNET_break (phase == 6);
55 phase++;
56 LOG ("Sent 'STOP' request for arm to ARM %s\n",
57 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
58 "unsuccessfully");
59 GNUNET_SCHEDULER_shutdown ();
60}
61
62
63static void
64resolver_stop_cb (void *cls,
65 enum GNUNET_ARM_RequestStatus status,
66 enum GNUNET_ARM_Result result)
67{
68 op = NULL;
69 /* (5), a stop request should be sent to ARM successfully.
70 * ARM should report that resolver is stopped.
71 */
72 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
73 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
74 GNUNET_break (phase == 5);
75 LOG ("Sent 'STOP' request for resolver to ARM %s\n",
76 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
77 "unsuccessfully");
78 phase++;
79 GNUNET_assert (NULL == op);
80 op = GNUNET_ARM_request_service_stop (arm,
81 "arm",
82 &arm_stop_cb,
83 NULL);
84}
85
86
87static void
88dns_notify (void *cls,
89 const struct sockaddr *addr,
90 socklen_t addrlen)
91{
92 if (addr == NULL)
93 {
94 /* (4), resolver should finish resolving localhost */
95 GNUNET_break (phase == 4);
96 phase++;
97 LOG ("Finished resolving localhost\n");
98 if (ok != 0)
99 ok = 2;
100 GNUNET_assert (NULL == op);
101 op = GNUNET_ARM_request_service_stop (arm,
102 "resolver",
103 &resolver_stop_cb,
104 NULL);
105 return;
106 }
107 /* (3), resolver should resolve localhost */
108 GNUNET_break (phase == 3);
109 LOG ("Resolved localhost\n");
110 phase++;
111 GNUNET_break (addr != NULL);
112 ok = 0;
113}
114
115
116static void
117resolver_start_cb (void *cls,
118 enum GNUNET_ARM_RequestStatus status,
119 enum GNUNET_ARM_Result result)
120{
121 op = NULL;
122 /* (2), the start request for resolver should be sent successfully
123 * ARM should report that resolver service is starting.
124 */
125 GNUNET_assert (status == GNUNET_ARM_REQUEST_SENT_OK);
126 GNUNET_break (phase == 2);
127 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
128 LOG ("Sent 'START' request for resolver to ARM %s\n",
129 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
130 "unsuccessfully");
131 phase++;
132 GNUNET_RESOLVER_ip_get ("localhost",
133 AF_INET,
134 TIMEOUT,
135 &dns_notify, NULL);
136}
137
138
139static void
140arm_conn (void *cls,
141 int connected)
142{
143 if (GNUNET_SYSERR == connected)
144 {
145 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
146 _ ("Fatal error initializing ARM API.\n"));
147 GNUNET_SCHEDULER_shutdown ();
148 GNUNET_assert (0);
149 return;
150 }
151 if (GNUNET_YES == connected)
152 {
153 /* (1), arm connection should be established */
154 LOG ("Connected to ARM\n");
155 GNUNET_break (phase == 1);
156 phase++;
157 GNUNET_assert (NULL == op);
158 op = GNUNET_ARM_request_service_start (arm,
159 "resolver",
160 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
161 &resolver_start_cb,
162 NULL);
163 }
164 else
165 {
166 /* (7), ARM should stop (we disconnect from it) */
167 LOG ("Disconnected from ARM\n");
168 GNUNET_break (phase == 7);
169 if (phase != 7)
170 ok = 3;
171 else if (ok == 1)
172 ok = 0;
173 }
174}
175
176
177static void
178arm_start_cb (void *cls,
179 enum GNUNET_ARM_RequestStatus status,
180 enum GNUNET_ARM_Result result)
181{
182 op = NULL;
183 /* (0) The request should be "sent" successfully
184 * ("sent", because it isn't going anywhere, ARM API starts ARM service
185 * by itself).
186 * ARM API should report that ARM service is starting.
187 */GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
188 GNUNET_break (phase == 0);
189 LOG ("Sent 'START' request for arm to ARM %s\n",
190 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
191 "unsuccessfully");
192 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
193 phase++;
194}
195
196
197static void
198do_shutdown (void *cls)
199{
200 if (NULL != op)
201 {
202 GNUNET_ARM_operation_cancel (op);
203 op = NULL;
204 }
205 if (NULL != arm)
206 {
207 GNUNET_ARM_disconnect (arm);
208 arm = NULL;
209 }
210}
211
212
213static void
214task (void *cls,
215 char *const *args,
216 const char *cfgfile,
217 const struct GNUNET_CONFIGURATION_Handle *c)
218{
219 cfg = c;
220 arm = GNUNET_ARM_connect (cfg,
221 &arm_conn,
222 NULL);
223 if (NULL == arm)
224 return;
225 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
226 NULL);
227 op = GNUNET_ARM_request_service_start (arm,
228 "arm",
229 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
230 &arm_start_cb,
231 NULL);
232}
233
234
235int
236main (int argc, char *argvx[])
237{
238 char *const argv[] = {
239 "test-arm-api",
240 "-c", "test_arm_api_data.conf",
241 NULL
242 };
243 struct GNUNET_GETOPT_CommandLineOption options[] = {
244 GNUNET_GETOPT_OPTION_END
245 };
246
247 GNUNET_log_setup ("test-arm-api",
248 "WARNING",
249 NULL);
250 GNUNET_assert (GNUNET_OK ==
251 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
252 argv, "test-arm-api", "nohelp", options,
253 &task, NULL));
254 return ok;
255}
256
257
258/* end of test_arm_api.c */
diff --git a/src/service/arm/test_arm_api_data.conf b/src/service/arm/test_arm_api_data.conf
new file mode 100644
index 000000000..fef6cfb40
--- /dev/null
+++ b/src/service/arm/test_arm_api_data.conf
@@ -0,0 +1,38 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4[PATHS]
5GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-arm/
6
7[arm]
8BINARY = gnunet-service-arm
9OPTIONS = -L ERROR
10#PREFIX = valgrind --tool=memcheck --leak-check=yes
11
12[resolver]
13START_ON_DEMAND = YES
14PORT = 23355
15# PREFIX = valgrind
16
17[do-nothing]
18START_ON_DEMAND = NO
19PORT = 48223
20HOSTNAME = localhost
21BINARY = /will/be/overwritten/by/test_exponential_backoff
22ACCEPT_FROM = 127.0.0.1;
23ACCEPT_FROM6 = ::1;
24
25[statistics]
26START_ON_DEMAND = YES
27
28[core]
29START_ON_DEMAND = NO
30
31[transport]
32START_ON_DEMAND = NO
33
34[ats]
35START_ON_DEMAND = NO
36
37[peerinfo]
38START_ON_DEMAND = NO
diff --git a/src/service/arm/test_exponential_backoff.c b/src/service/arm/test_exponential_backoff.c
new file mode 100644
index 000000000..e3eed8568
--- /dev/null
+++ b/src/service/arm/test_exponential_backoff.c
@@ -0,0 +1,416 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file arm/test_exponential_backoff.c
22 * @brief testcase for gnunet-service-arm.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_arm_service.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_protocols.h"
29
30#define LOG(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
31
32#define LOG_BACKOFF GNUNET_NO
33
34#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
35
36#define SERVICE_TEST_TIMEOUT GNUNET_TIME_UNIT_FOREVER_REL
37
38#define FIVE_MILLISECONDS GNUNET_TIME_relative_multiply ( \
39 GNUNET_TIME_UNIT_MILLISECONDS, 5)
40
41#define SERVICE "do-nothing"
42
43#define BINARY "mockup-service"
44
45#define CFGFILENAME "test_arm_api_data2.conf"
46
47
48static const struct GNUNET_CONFIGURATION_Handle *cfg;
49
50static struct GNUNET_ARM_Handle *arm;
51
52static struct GNUNET_ARM_MonitorHandle *mon;
53
54static struct GNUNET_SCHEDULER_Task *kt;
55
56static int ok = 1;
57
58static int phase = 0;
59
60static int trialCount;
61
62static struct GNUNET_TIME_Absolute startedWaitingAt;
63
64struct GNUNET_TIME_Relative waitedFor;
65
66struct GNUNET_TIME_Relative waitedFor_prev;
67
68#if LOG_BACKOFF
69static FILE *killLogFilePtr;
70
71static char *killLogFileName;
72#endif
73
74
75/**
76 * Context for handling the shutdown of a service.
77 */
78struct ShutdownContext
79{
80 /**
81 * Connection to the service that is being shutdown.
82 */
83 struct GNUNET_MQ_Handle *mq;
84
85 /**
86 * Task set up to cancel the shutdown request on timeout.
87 */
88 struct GNUNET_SCHEDULER_Task *cancel_task;
89};
90
91
92static void
93kill_task (void *cbData);
94
95
96/**
97 * Shutting down took too long, cancel receive and return error.
98 *
99 * @param cls closure
100 */
101static void
102service_shutdown_timeout (void *cls)
103{
104 GNUNET_assert (0);
105}
106
107
108/**
109 * Generic error handler, called with the appropriate error code and
110 * the same closure specified at the creation of the message queue.
111 * Not every message queue implementation supports an error handler.
112 *
113 * @param cls closure with the `struct ShutdownContext *`
114 * @param error error code
115 */
116static void
117mq_error_handler (void *cls,
118 enum GNUNET_MQ_Error error)
119{
120 struct ShutdownContext *shutdown_ctx = cls;
121
122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
123 "Service shutdown complete (MQ error).\n");
124 GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
125 GNUNET_MQ_destroy (shutdown_ctx->mq);
126 GNUNET_free (shutdown_ctx);
127}
128
129
130static void
131kill_task (void *cbData)
132{
133 struct ShutdownContext *shutdown_ctx
134 = GNUNET_new (struct ShutdownContext);
135 struct GNUNET_MQ_Envelope *env;
136 struct GNUNET_MessageHeader *msg;
137 struct GNUNET_MQ_MessageHandler handlers[] = {
138 GNUNET_MQ_handler_end ()
139 };
140
141 kt = NULL;
142 if (trialCount == 13)
143 {
144 LOG ("Saw enough kills, asking ARM to stop mock service for good\n");
145 GNUNET_ARM_request_service_stop (arm,
146 SERVICE,
147 NULL,
148 NULL);
149 ok = 0;
150 trialCount++;
151 GNUNET_free (shutdown_ctx);
152 return;
153 }
154 shutdown_ctx->mq = GNUNET_CLIENT_connect (cfg,
155 SERVICE,
156 handlers,
157 &mq_error_handler,
158 shutdown_ctx);
159 GNUNET_assert (NULL != shutdown_ctx->mq);
160 trialCount++;
161 LOG ("Sending a shutdown request to the mock service\n");
162 env = GNUNET_MQ_msg (msg,
163 GNUNET_MESSAGE_TYPE_ARM_STOP); /* FIXME: abuse of message type */
164 GNUNET_MQ_send (shutdown_ctx->mq,
165 env);
166 shutdown_ctx->cancel_task
167 = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
168 &service_shutdown_timeout,
169 shutdown_ctx);
170}
171
172
173static void
174trigger_disconnect (void *cls)
175{
176 GNUNET_ARM_disconnect (arm);
177 GNUNET_ARM_monitor_stop (mon);
178 if (NULL != kt)
179 {
180 GNUNET_SCHEDULER_cancel (kt);
181 kt = NULL;
182 }
183}
184
185
186static void
187arm_stop_cb (void *cls,
188 enum GNUNET_ARM_RequestStatus status,
189 enum GNUNET_ARM_Result result)
190{
191 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
192 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
193 LOG ("ARM service stopped\n");
194 GNUNET_SCHEDULER_shutdown ();
195}
196
197
198static void
199srv_status (void *cls,
200 const char *service,
201 enum GNUNET_ARM_ServiceMonitorStatus status)
202{
203 if (status == GNUNET_ARM_SERVICE_MONITORING_STARTED)
204 {
205 LOG ("ARM monitor started, starting mock service\n");
206 phase++;
207 GNUNET_ARM_request_service_start (arm,
208 SERVICE,
209 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
210 NULL,
211 NULL);
212 return;
213 }
214 if (0 != strcasecmp (service, SERVICE))
215 return; /* not what we care about */
216 if (phase == 1)
217 {
218 GNUNET_break (status == GNUNET_ARM_SERVICE_STARTING);
219 GNUNET_break (phase == 1);
220 LOG ("do-nothing is starting\n");
221 phase++;
222 ok = 1;
223 GNUNET_assert (NULL == kt);
224 startedWaitingAt = GNUNET_TIME_absolute_get ();
225 kt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
226 &kill_task,
227 NULL);
228 }
229 else if (phase == 2)
230 {
231 /* We passively monitor ARM for status updates. ARM should tell us
232 * when do-nothing dies (no need to run a service upness test ourselves).
233 */
234 if (status == GNUNET_ARM_SERVICE_STARTING)
235 {
236 waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt);
237 LOG ("Waited for: %s\n",
238 GNUNET_STRINGS_relative_time_to_string (waitedFor,
239 GNUNET_YES));
240
241 LOG ("do-nothing is starting, killing it...\n");
242 GNUNET_assert (NULL == kt);
243 kt = GNUNET_SCHEDULER_add_now (&kill_task, &ok);
244 }
245 else if ((status == GNUNET_ARM_SERVICE_STOPPED) && (trialCount == 14))
246 {
247 phase++;
248 LOG ("do-nothing stopped working %u times, we are done here\n",
249 (unsigned int) trialCount);
250 GNUNET_ARM_request_service_stop (arm,
251 "arm",
252 &arm_stop_cb,
253 NULL);
254 }
255 }
256}
257
258
259static void
260arm_start_cb (void *cls,
261 enum GNUNET_ARM_RequestStatus status,
262 enum GNUNET_ARM_Result result)
263{
264 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
265 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
266 GNUNET_break (phase == 0);
267 LOG ("Sent 'START' request for arm to ARM %s\n",
268 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
269 "unsuccessfully");
270}
271
272
273static void
274task (void *cls,
275 char *const *args,
276 const char *cfgfile,
277 const struct GNUNET_CONFIGURATION_Handle *c)
278{
279 cfg = c;
280 arm = GNUNET_ARM_connect (cfg, NULL, NULL);
281 if (NULL == arm)
282 {
283 GNUNET_break (0);
284 return;
285 }
286 mon = GNUNET_ARM_monitor_start (cfg,
287 &srv_status,
288 NULL);
289 if (NULL == mon)
290 {
291 GNUNET_break (0);
292 GNUNET_ARM_disconnect (arm);
293 arm = NULL;
294 return;
295 }
296 GNUNET_ARM_request_service_start (arm,
297 "arm",
298 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
299 &arm_start_cb,
300 NULL);
301 GNUNET_SCHEDULER_add_shutdown (&trigger_disconnect,
302 NULL);
303}
304
305
306static int
307check ()
308{
309 char *const argv[] = {
310 "test-exponential-backoff",
311 "-c", CFGFILENAME,
312 NULL
313 };
314 struct GNUNET_GETOPT_CommandLineOption options[] = {
315 GNUNET_GETOPT_OPTION_END
316 };
317
318 /* Running ARM and running the do_nothing task */
319 GNUNET_assert (GNUNET_OK ==
320 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
321 argv,
322 "test-exponential-backoff",
323 "nohelp",
324 options,
325 &task,
326 NULL));
327 return ok;
328}
329
330
331#ifndef PATH_MAX
332/**
333 * Assumed maximum path length (for the log file name).
334 */
335#define PATH_MAX 4096
336#endif
337
338
339static int
340init ()
341{
342 struct GNUNET_CONFIGURATION_Handle *cfg;
343 char pwd[PATH_MAX];
344 char *binary;
345
346 cfg = GNUNET_CONFIGURATION_create ();
347 if (GNUNET_OK != GNUNET_CONFIGURATION_parse (cfg,
348 "test_arm_api_data.conf"))
349 {
350 GNUNET_CONFIGURATION_destroy (cfg);
351 return GNUNET_SYSERR;
352 }
353 if (NULL == getcwd (pwd, PATH_MAX))
354 return GNUNET_SYSERR;
355 GNUNET_assert (0 < GNUNET_asprintf (&binary,
356 "%s/%s",
357 pwd,
358 BINARY));
359 GNUNET_CONFIGURATION_set_value_string (cfg,
360 SERVICE,
361 "BINARY",
362 binary);
363 GNUNET_free (binary);
364 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg,
365 CFGFILENAME))
366 {
367 GNUNET_CONFIGURATION_destroy (cfg);
368 return GNUNET_SYSERR;
369 }
370 GNUNET_CONFIGURATION_destroy (cfg);
371
372#if LOG_BACKOFF
373 killLogFileName = GNUNET_DISK_mktemp ("exponential-backoff-waiting.log");
374 if (NULL == (killLogFilePtr = fopen (killLogFileName,
375 "w")))
376 {
377 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
378 "fopen",
379 killLogFileName);
380 GNUNET_free (killLogFileName);
381 return GNUNET_SYSERR;
382 }
383#endif
384 return GNUNET_OK;
385}
386
387
388static void
389houseKeep ()
390{
391#if LOG_BACKOFF
392 GNUNET_assert (0 == fclose (killLogFilePtr));
393 GNUNET_free (killLogFileName);
394#endif
395 (void) unlink (CFGFILENAME);
396}
397
398
399int
400main (int argc, char *argv[])
401{
402 int ret;
403
404 GNUNET_log_setup ("test-exponential-backoff",
405 "WARNING",
406 NULL);
407
408 if (GNUNET_OK != init ())
409 return 1;
410 ret = check ();
411 houseKeep ();
412 return ret;
413}
414
415
416/* end of test_exponential_backoff.c */
diff --git a/src/service/arm/test_gnunet_arm.py.in b/src/service/arm/test_gnunet_arm.py.in
new file mode 100644
index 000000000..2b30b6b97
--- /dev/null
+++ b/src/service/arm/test_gnunet_arm.py.in
@@ -0,0 +1,129 @@
1#!@PYTHONEXE@
2
3import os
4import sys
5import shutil
6import re
7import subprocess
8import time
9
10# FIXME: There's too much repetition, move generally used parts into reusable modules.
11if os.name == "nt":
12 tmp = os.getenv("TEMP")
13else:
14 tmp = "/tmp"
15
16if os.name == 'nt':
17 st = 'gnunet-statistics.exe'
18 arm = './gnunet-arm.exe'
19else:
20 st = 'gnunet-statistics'
21 arm = './gnunet-arm'
22
23run_arm = [arm, '-c', 'test_arm_api_data.conf', '--no-stdout', '--no-stderr']
24debug = os.getenv('DEBUG')
25if debug:
26 run_arm += [debug.split(' ')]
27
28
29def cleanup():
30 shutil.rmtree(os.path.join(tmp, "test-gnunetd-arm"), True)
31
32
33def sub_run(args, want_stdo=True, want_stde=False, nofail=False):
34 if want_stdo:
35 stdo = subprocess.PIPE
36 else:
37 stdo = None
38 if want_stde:
39 stde = subprocess.PIPE
40 else:
41 stde = None
42 p = subprocess.Popen(args, stdout=stdo, stderr=stde)
43 stdo, stde = p.communicate()
44 if not nofail:
45 if p.returncode != 0:
46 sys.exit(p.returncode)
47 return (p.returncode, stdo, stde)
48
49
50def fail(result):
51 print(result)
52 r_arm(['-e'], want_stdo=False)
53 sys.exit(1)
54
55
56def end_arm_failure(command, rc, stdo, stde, normal):
57 if normal:
58 if rc != 0:
59 fail(
60 "FAIL: error running {}\nCommand output was:\n{}\n{}".format(
61 command, stdo, stde
62 )
63 )
64 else:
65 if rc == 0:
66 fail(
67 "FAIL: expected error while running {}\nCommand output was:\n{}\n{}"
68 .format(command, stdo, stde)
69 )
70
71
72def print_only_failure(command, rc, stdo, stde, normal):
73 if normal:
74 if rc != 0:
75 print(
76 "FAIL: error running {}\nCommand output was:\n{}\n{}".format(
77 command, stdo, stde
78 )
79 )
80 sys.exit(1)
81 else:
82 if rc == 0:
83 print(
84 "FAIL: expected error while running {}\nCommand output was:\n{}\n{}"
85 .format(command, stdo, stde)
86 )
87 sys.exit(1)
88
89
90def r_something(to_run, extra_args, failure=None, normal=True, **kw):
91 rc, stdo, stde = sub_run(
92 to_run + extra_args, nofail=True, want_stde=True, **kw
93 )
94 if failure is not None:
95 failure(to_run + extra_args, rc, stdo, stde, normal)
96 return (rc, stdo, stde)
97
98
99def r_arm(extra_args, **kw):
100 return r_something(run_arm, extra_args, **kw)
101
102
103cleanup()
104
105print("TEST: Bad argument checking...", end='')
106r_arm(['-x'], normal=False, failure=print_only_failure)
107print("PASS")
108
109print("TEST: Start ARM...", end='')
110r_arm(['-s'], failure=print_only_failure)
111time.sleep(1)
112print("PASS")
113
114print("TEST: Start another service...", end='')
115r_arm(['-i', 'resolver'], failure=end_arm_failure)
116time.sleep(1)
117print("PASS")
118
119print("TEST: Stop a service...", end='')
120r_arm(['-k', 'resolver'], failure=end_arm_failure)
121time.sleep(1)
122print("PASS")
123
124print("TEST: Stop ARM...", end='')
125r_arm(['-e'], failure=print_only_failure)
126time.sleep(1)
127print("PASS")
128
129cleanup()
diff --git a/src/service/arm/test_gnunet_service_arm.c b/src/service/arm/test_gnunet_service_arm.c
new file mode 100644
index 000000000..df4ad95c2
--- /dev/null
+++ b/src/service/arm/test_gnunet_service_arm.c
@@ -0,0 +1,266 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file arm/test_gnunet_service_arm.c
22 * @brief testcase for gnunet-service-arm.c; tests ARM by making it start the resolver
23 * @author Safey
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_resolver_service.h"
29#include "gnunet_util_lib.h"
30
31/**
32 * Timeout for starting services, very short because of the strange way start works
33 * (by checking if running before starting, so really this time is always waited on
34 * startup (annoying)).
35 */
36#define START_TIMEOUT GNUNET_TIME_relative_multiply ( \
37 GNUNET_TIME_UNIT_MILLISECONDS, 50)
38
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
40
41
42static int ret = 1;
43
44static int resolved_ok;
45
46static int asked_for_a_list;
47
48static struct GNUNET_ARM_Handle *arm;
49
50static const char hostname[] = "www.gnu.org"; /* any domain should do */
51
52
53static void
54trigger_disconnect (void *cls)
55{
56 GNUNET_ARM_disconnect (arm);
57 arm = NULL;
58}
59
60
61static void
62arm_stop_cb (void *cls,
63 enum GNUNET_ARM_RequestStatus status,
64 enum GNUNET_ARM_Result result)
65{
66 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
67 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
68 if (result != GNUNET_ARM_RESULT_STOPPED)
69 {
70 GNUNET_break (0);
71 ret = 4;
72 }
73 GNUNET_SCHEDULER_add_now (&trigger_disconnect, NULL);
74}
75
76
77static void
78service_list (void *cls,
79 enum GNUNET_ARM_RequestStatus rs,
80 unsigned int count,
81 const struct GNUNET_ARM_ServiceInfo *list)
82{
83 unsigned int i;
84
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "%u services are are currently running\n",
87 count);
88 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
89 goto stop_arm;
90 for (i = 0; i < count; i++)
91 {
92 if ((0 == strcasecmp (list[i].name, "resolver")) &&
93 (0 == strcasecmp (list[i].binary, "gnunet-service-resolver")))
94 {
95 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
96 "Got service list, now stopping arm\n");
97 ret = 0;
98 }
99 }
100
101stop_arm:
102 GNUNET_ARM_request_service_stop (arm,
103 "arm",
104 &arm_stop_cb,
105 NULL);
106}
107
108
109static void
110hostname_resolve_cb (void *cls,
111 const struct sockaddr *addr,
112 socklen_t addrlen)
113{
114 if ((0 == ret) || (4 == ret) || (1 == resolved_ok))
115 return;
116 if (NULL == addr)
117 {
118 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
119 "Failed to resolve hostname!\n");
120 GNUNET_break (0);
121 ret = 3;
122 GNUNET_ARM_request_service_stop (arm,
123 "arm",
124 &arm_stop_cb,
125 NULL);
126 return;
127 }
128 if (0 == asked_for_a_list)
129 {
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131 "Resolved hostname, now checking the service list\n");
132 GNUNET_ARM_request_service_list (arm,
133 &service_list,
134 NULL);
135 asked_for_a_list = 1;
136 resolved_ok = 1;
137 }
138}
139
140
141static void
142arm_start_cb (void *cls,
143 enum GNUNET_ARM_RequestStatus status,
144 enum GNUNET_ARM_Result result)
145{
146 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
147 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Trying to resolve a hostname via the resolver service!\n");
150 /* connect to the resolver service */
151 if (NULL ==
152 GNUNET_RESOLVER_ip_get (hostname,
153 AF_UNSPEC,
154 TIMEOUT,
155 &hostname_resolve_cb,
156 NULL))
157 {
158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
159 "Unable initiate connection to resolver service\n");
160 GNUNET_break (0);
161 ret = 2;
162 GNUNET_ARM_request_service_stop (arm,
163 "arm",
164 &arm_stop_cb,
165 NULL);
166 }
167}
168
169
170static void
171run (void *cls,
172 char *const *args,
173 const char *cfgfile,
174 const struct GNUNET_CONFIGURATION_Handle *c)
175{
176 arm = GNUNET_ARM_connect (c,
177 NULL,
178 NULL);
179 GNUNET_ARM_request_service_start (arm,
180 "arm",
181 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
182 &arm_start_cb,
183 NULL);
184}
185
186
187int
188main (int argc, char *av[])
189{
190 static char *const argv[] = {
191 "test-gnunet-service-arm",
192 "-c",
193 "test_arm_api_data.conf",
194 NULL
195 };
196 static struct GNUNET_GETOPT_CommandLineOption options[] = {
197 GNUNET_GETOPT_OPTION_END
198 };
199
200 /* trigger DNS lookup */
201#if HAVE_GETADDRINFO
202 {
203 struct addrinfo *ai;
204 int ret;
205
206 if (0 != (ret = getaddrinfo (hostname, NULL, NULL, &ai)))
207 {
208 fprintf (stderr,
209 "Failed to resolve `%s', testcase not run.\n",
210 hostname);
211 return 77;
212 }
213 freeaddrinfo (ai);
214 }
215#elif HAVE_GETHOSTBYNAME2
216 {
217 struct hostent *host;
218
219 host = gethostbyname2 (hostname, AF_INET);
220 if (NULL == host)
221 host = gethostbyname2 (hostname, AF_INET6);
222 if (NULL == host)
223 {
224 fprintf (stderr,
225 "Failed to resolve `%s', testcase not run.\n",
226 hostname);
227 return 77;
228 }
229 }
230#elif HAVE_GETHOSTBYNAME
231 {
232 struct hostent *host;
233
234 host = gethostbyname (hostname);
235 if (NULL == host)
236 {
237 fprintf (stderr,
238 "Failed to resolve `%s', testcase not run.\n",
239 hostname);
240 return 77;
241 }
242 }
243#else
244 fprintf (stderr,
245 "libc fails to have resolver function, testcase not run.\n");
246 return 77;
247#endif
248 GNUNET_log_setup ("test-gnunet-service-arm",
249 "WARNING",
250 NULL);
251 GNUNET_break (GNUNET_OK ==
252 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
253 argv, "test-gnunet-service-arm",
254 "nohelp", options,
255 &run, NULL));
256 if (0 != ret)
257 {
258 fprintf (stderr,
259 "Test failed with error code %d\n",
260 ret);
261 }
262 return ret;
263}
264
265
266/* end of test_gnunet_service_arm.c */
diff --git a/src/service/core/.gitignore b/src/service/core/.gitignore
new file mode 100644
index 000000000..e985f5ff4
--- /dev/null
+++ b/src/service/core/.gitignore
@@ -0,0 +1,9 @@
1gnunet-service-core
2core.conf
3test_core_api
4test_core_api_reliability
5test_core_api_send_to_self
6test_core_api_start_only
7test_core_quota_compliance_asymmetric_recv_limited
8test_core_quota_compliance_asymmetric_send_limited
9test_core_quota_compliance_symmetric
diff --git a/src/service/core/Makefile.am b/src/service/core/Makefile.am
new file mode 100644
index 000000000..9ce8cf8b5
--- /dev/null
+++ b/src/service/core/Makefile.am
@@ -0,0 +1,127 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6plugindir = $(libdir)/gnunet
7
8libexecdir= $(pkglibdir)/libexec/
9
10pkgcfg_DATA = \
11 core.conf
12
13if USE_COVERAGE
14 AM_CFLAGS = --coverage -O0
15 XLIB = -lgcov
16endif
17
18plugin_LTLIBRARIES = \
19 libgnunet_test_core_plugin_cmd_just_run.la
20
21TESTING_LIBS = \
22 libgnunetcoretesting.la
23
24lib_LTLIBRARIES = \
25 libgnunetcore.la \
26 $(TESTING_LIBS)
27
28libgnunetcore_la_SOURCES = \
29 core_api.c core.h \
30 core_api_monitor_peers.c
31libgnunetcore_la_LIBADD = \
32 $(top_builddir)/src/lib/util/libgnunetutil.la \
33 $(GN_LIBINTL) $(XLIB)
34libgnunetcore_la_LDFLAGS = \
35 $(GN_LIB_LDFLAGS) \
36 -version-info 0:1:0
37
38libgnunet_test_core_plugin_cmd_just_run_la_SOURCES = \
39 test_core_plugin_cmd_just_run.c
40libgnunet_test_core_plugin_cmd_just_run_la_LIBADD = \
41 libgnunetcoretesting.la \
42 $(top_builddir)/src/transport/libgnunettransportapplication.la \
43 $(top_builddir)/src/transport/libgnunettransportcore.la \
44 $(top_builddir)/src/lib/testing/libgnunettesting.la \
45 $(top_builddir)/src/peerstore/libgnunetpeerstore.la \
46 $(top_builddir)/src/statistics/libgnunetstatistics.la \
47 $(top_builddir)/src/lib/hello/libgnunethello.la \
48 $(top_builddir)/src/service/arm/libgnunetarm.la \
49 $(top_builddir)/src/lib/util/libgnunetutil.la \
50 $(LTLIBINTL)
51libgnunet_test_core_plugin_cmd_just_run_la_LDFLAGS = \
52 $(GN_PLUGIN_LDFLAGS)
53
54libgnunetcoretesting_la_SOURCES = \
55 core_api_cmd_connecting_peers.c
56libgnunetcoretesting_la_LIBADD = \
57 $(top_builddir)/src/lib/testing/libgnunettesting.la \
58 $(top_builddir)/src/service/arm/libgnunetarm.la \
59 $(top_builddir)/src/transport/libgnunettransportapplication.la \
60 $(top_builddir)/src/transport/libgnunettransportcore.la \
61 $(top_builddir)/src/lib/util/libgnunetutil.la
62libgnunetcoretesting_la_LDFLAGS = \
63 $(GN_LIBINTL) \
64 $(GN_LIB_LDFLAGS) \
65 -version-info 0:0:0
66
67
68libexec_PROGRAMS = \
69 gnunet-service-core
70
71gnunet_service_core_SOURCES = \
72 gnunet-service-core.c gnunet-service-core.h \
73 gnunet-service-core_kx.c gnunet-service-core_kx.h \
74 gnunet-service-core_sessions.c gnunet-service-core_sessions.h \
75 gnunet-service-core_typemap.c gnunet-service-core_typemap.h
76gnunet_service_core_LDADD = \
77 $(top_builddir)/src/statistics/libgnunetstatistics.la \
78 $(top_builddir)/src/transport/libgnunettransportapplication.la \
79 $(top_builddir)/src/transport/libgnunettransportcore.la \
80 $(top_builddir)/src/lib/util/libgnunetutil.la \
81 $(GN_LIBINTL) $(Z_LIBS)
82
83
84TESTING_TESTS = \
85 test_core_api_send_to_self
86
87check_PROGRAMS = \
88 test_core_api_start_only \
89 $(TESTING_TESTS)
90
91# Only test TNG if we run experimental
92check_SCRIPTS= \
93 test_core_start_testcase.sh
94
95if ENABLE_TEST_RUN
96AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
97TESTS = $(check_PROGRAMS) \
98 $(check_SCRIPTS)
99endif
100
101test_core_api_send_to_self_SOURCES = \
102 test_core_api_send_to_self.c
103test_core_api_send_to_self_LDADD = \
104 libgnunetcore.la \
105 $(top_builddir)/src/lib/testing/libgnunettesting.la \
106 $(top_builddir)/src/lib/util/libgnunetutil.la
107
108test_core_api_start_only_SOURCES = \
109 test_core_api_start_only.c
110test_core_api_start_only_LDADD = \
111 $(top_builddir)/src/lib/testing/libgnunettesting.la \
112 libgnunetcore.la \
113 $(top_builddir)/src/lib/util/libgnunetutil.la
114
115EXTRA_DIST = \
116 test_core_start_testcase.sh \
117 test_core_defaults.conf \
118 test_core_api_data.conf \
119 test_core_api_peer1.conf \
120 test_core_api_peer2.conf \
121 test_core_api_send_to_self.conf \
122 test_core_quota_asymmetric_recv_limited_peer1.conf \
123 test_core_quota_asymmetric_recv_limited_peer2.conf \
124 test_core_quota_asymmetric_send_limit_peer1.conf \
125 test_core_quota_asymmetric_send_limit_peer2.conf \
126 test_core_quota_peer1.conf \
127 test_core_quota_peer2.conf
diff --git a/src/service/core/core.conf.in b/src/service/core/core.conf.in
new file mode 100644
index 000000000..00faf6079
--- /dev/null
+++ b/src/service/core/core.conf.in
@@ -0,0 +1,23 @@
1[core]
2START_ON_DEMAND = @START_ON_DEMAND@
3@JAVAPORT@PORT = 2092
4HOSTNAME = localhost
5BINARY = gnunet-service-core
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-core.sock
9UNIX_MATCH_UID = NO
10UNIX_MATCH_GID = YES
11# DISABLE_SOCKET_FORWARDING = NO
12# USERNAME =
13# MAXBUF =
14# TIMEOUT =
15# DISABLEV6 =
16# BINDTO =
17# REJECT_FROM =
18# REJECT_FROM6 =
19# PREFIX =
20
21# Note: this MUST be set to YES in production, only set to NO for testing
22# for performance (testbed/cluster-scale use!).
23USE_EPHEMERAL_KEYS = YES
diff --git a/src/service/core/core.h b/src/service/core/core.h
new file mode 100644
index 000000000..d4596f038
--- /dev/null
+++ b/src/service/core/core.h
@@ -0,0 +1,326 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/core.h
23 * @brief common internal definitions for core service
24 * @author Christian Grothoff
25 */
26#ifndef CORE_H
27#define CORE_H
28
29#include "gnunet_util_lib.h"
30#include "gnunet_time_lib.h"
31
32/**
33 * General core debugging.
34 */
35#define DEBUG_CORE GNUNET_EXTRA_LOGGING
36
37/**
38 * Definition of bits in the InitMessage's options field that specify
39 * which events this client cares about. Note that inbound messages
40 * for handlers that were specifically registered are always
41 * transmitted to the client.
42 */
43#define GNUNET_CORE_OPTION_NOTHING 0
44
45/**
46 * Client cares about connectivity changes.
47 */
48#define GNUNET_CORE_OPTION_SEND_STATUS_CHANGE 4
49
50/**
51 * Client wants all inbound messages in full.
52 */
53#define GNUNET_CORE_OPTION_SEND_FULL_INBOUND 8
54
55/**
56 * Client just wants the 4-byte message headers of
57 * all inbound messages.
58 */
59#define GNUNET_CORE_OPTION_SEND_HDR_INBOUND 16
60
61/**
62 * Client wants all outbound messages in full.
63 */
64#define GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND 32
65
66/**
67 * Client just wants the 4-byte message headers of
68 * all outbound messages.
69 */
70#define GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND 64
71
72
73GNUNET_NETWORK_STRUCT_BEGIN
74
75/**
76 * Message transmitted core clients to gnunet-service-core
77 * to start the interaction. This header is followed by
78 * uint16_t type values specifying which messages this
79 * client is interested in.
80 */
81struct InitMessage
82{
83 /**
84 * Header with type #GNUNET_MESSAGE_TYPE_CORE_INIT.
85 */
86 struct GNUNET_MessageHeader header;
87
88 /**
89 * Options, see GNUNET_CORE_OPTION_ values.
90 */
91 uint32_t options GNUNET_PACKED;
92};
93
94
95/**
96 * Message transmitted by the gnunet-service-core process
97 * to its clients in response to an INIT message.
98 */
99struct InitReplyMessage
100{
101 /**
102 * Header with type #GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY
103 */
104 struct GNUNET_MessageHeader header;
105
106 /**
107 * Always zero.
108 */
109 uint32_t reserved GNUNET_PACKED;
110
111 /**
112 * Public key of the local peer.
113 */
114 struct GNUNET_PeerIdentity my_identity;
115};
116
117
118/**
119 * Message sent by the service to clients to notify them
120 * about a peer connecting.
121 */
122struct ConnectNotifyMessage
123{
124 /**
125 * Header with type #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT
126 */
127 struct GNUNET_MessageHeader header;
128
129 /**
130 * Always zero.
131 */
132 uint32_t reserved GNUNET_PACKED;
133
134 /**
135 * Identity of the connecting peer.
136 */
137 struct GNUNET_PeerIdentity peer;
138};
139
140
141/**
142 * Message sent by the service to clients to notify them
143 * about a peer disconnecting.
144 */
145struct DisconnectNotifyMessage
146{
147 /**
148 * Header with type #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT.
149 */
150 struct GNUNET_MessageHeader header;
151
152 /**
153 * Always zero.
154 */
155 uint32_t reserved GNUNET_PACKED;
156
157 /**
158 * Identity of the connecting peer.
159 */
160 struct GNUNET_PeerIdentity peer;
161};
162
163
164/**
165 * Message sent by the service to clients to notify them about
166 * messages being received or transmitted. This overall message is
167 * followed by the real message, or just the header of the real
168 * message (depending on the client's preferences). The receiver can
169 * tell if it got the full message or only a partial message by
170 * looking at the size field in the header of NotifyTrafficMessage and
171 * checking it with the size field in the message that follows.
172 */
173struct NotifyTrafficMessage
174{
175 /**
176 * Header with type #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND
177 * or #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND.
178 */
179 struct GNUNET_MessageHeader header;
180
181 /**
182 * Identity of the receiver or sender.
183 */
184 struct GNUNET_PeerIdentity peer;
185
186 /* Followed by payload (message or just header), variable size */
187};
188
189
190/**
191 * Client notifying core about the maximum-priority
192 * message it has in the queue for a particular target.
193 */
194struct SendMessageRequest
195{
196 /**
197 * Header with type #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST
198 */
199 struct GNUNET_MessageHeader header;
200
201 /**
202 * How important is this message?
203 */
204 uint32_t priority GNUNET_PACKED;
205
206 /**
207 * By what time would the sender really like to see this
208 * message transmitted?
209 */
210 struct GNUNET_TIME_AbsoluteNBO deadline;
211
212 /**
213 * Identity of the intended target.
214 */
215 struct GNUNET_PeerIdentity peer;
216
217 /**
218 * Always zero.
219 */
220 uint32_t reserved GNUNET_PACKED;
221
222 /**
223 * How large is the message?
224 */
225 uint16_t size GNUNET_PACKED;
226
227 /**
228 * Counter for this peer to match SMRs to replies.
229 */
230 uint16_t smr_id GNUNET_PACKED;
231};
232
233
234/**
235 * Core notifying client that it is allowed to now
236 * transmit a message to the given target
237 * (response to #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST).
238 */
239struct SendMessageReady
240{
241 /**
242 * Header with type #GNUNET_MESSAGE_TYPE_CORE_SEND_READY
243 */
244 struct GNUNET_MessageHeader header;
245
246 /**
247 * How many bytes are allowed for transmission?
248 * Guaranteed to be at least as big as the requested size,
249 * or ZERO if the request is rejected (will timeout,
250 * peer disconnected, queue full, etc.).
251 */
252 uint16_t size GNUNET_PACKED;
253
254 /**
255 * smr_id from the request.
256 */
257 uint16_t smr_id GNUNET_PACKED;
258
259 /**
260 * Identity of the intended target.
261 */
262 struct GNUNET_PeerIdentity peer;
263};
264
265
266/**
267 * Client asking core to transmit a particular message to a particular
268 * target (response to #GNUNET_MESSAGE_TYPE_CORE_SEND_READY).
269 */
270struct SendMessage
271{
272 /**
273 * Header with type #GNUNET_MESSAGE_TYPE_CORE_SEND
274 */
275 struct GNUNET_MessageHeader header;
276
277 /**
278 * How important is this message? Contains a
279 * `enum GNUNET_MQ_PriorityPreferences` in NBO.
280 */
281 uint32_t priority GNUNET_PACKED;
282
283 /**
284 * By what time would the sender really like to see this
285 * message transmitted?
286 */
287 struct GNUNET_TIME_AbsoluteNBO deadline;
288
289 /**
290 * Identity of the intended receiver.
291 */
292 struct GNUNET_PeerIdentity peer;
293};
294
295
296/**
297 * Message sent by the service to monitor clients to notify them
298 * about a peer changing status.
299 */
300struct MonitorNotifyMessage
301{
302 /**
303 * Header with type #GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY
304 */
305 struct GNUNET_MessageHeader header;
306
307 /**
308 * New peer state, an `enum GNUNET_CORE_KxState` in NBO.
309 */
310 uint32_t state GNUNET_PACKED;
311
312 /**
313 * Identity of the peer.
314 */
315 struct GNUNET_PeerIdentity peer;
316
317 /**
318 * How long will we stay in this state (if nothing else happens)?
319 */
320 struct GNUNET_TIME_AbsoluteNBO timeout;
321};
322
323
324GNUNET_NETWORK_STRUCT_END
325#endif
326/* end of core.h */
diff --git a/src/service/core/core_api.c b/src/service/core/core_api.c
new file mode 100644
index 000000000..2e0bb1785
--- /dev/null
+++ b/src/service/core/core_api.c
@@ -0,0 +1,772 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file core/core_api.c
22 * @brief core service; this is the main API for encrypted P2P
23 * communications
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_core_service.h"
30#include "core.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "core-api", __VA_ARGS__)
33
34
35/**
36 * Information we track for each peer.
37 */
38struct PeerRecord
39{
40 /**
41 * Corresponding CORE handle.
42 */
43 struct GNUNET_CORE_Handle *h;
44
45 /**
46 * Message queue for the peer.
47 */
48 struct GNUNET_MQ_Handle *mq;
49
50 /**
51 * Message we are currently trying to pass to the CORE service
52 * for this peer (from @e mq).
53 */
54 struct GNUNET_MQ_Envelope *env;
55
56 /**
57 * Value the client returned when we connected, used
58 * as the closure in various places.
59 */
60 void *client_cls;
61
62 /**
63 * Peer the record is about.
64 */
65 struct GNUNET_PeerIdentity peer;
66
67 /**
68 * SendMessageRequest ID generator for this peer.
69 */
70 uint16_t smr_id_gen;
71};
72
73
74/**
75 * Context for the core service connection.
76 */
77struct GNUNET_CORE_Handle
78{
79 /**
80 * Configuration we're using.
81 */
82 const struct GNUNET_CONFIGURATION_Handle *cfg;
83
84 /**
85 * Closure for the various callbacks.
86 */
87 void *cls;
88
89 /**
90 * Function to call once we've handshaked with the core service.
91 */
92 GNUNET_CORE_StartupCallback init;
93
94 /**
95 * Function to call whenever we're notified about a peer connecting.
96 */
97 GNUNET_CORE_ConnectEventHandler connects;
98
99 /**
100 * Function to call whenever we're notified about a peer disconnecting.
101 */
102 GNUNET_CORE_DisconnectEventHandler disconnects;
103
104 /**
105 * Function handlers for messages of particular type.
106 */
107 struct GNUNET_MQ_MessageHandler *handlers;
108
109 /**
110 * Our message queue for transmissions to the service.
111 */
112 struct GNUNET_MQ_Handle *mq;
113
114 /**
115 * Hash map listing all of the peers that we are currently
116 * connected to.
117 */
118 struct GNUNET_CONTAINER_MultiPeerMap *peers;
119
120 /**
121 * Identity of this peer.
122 */
123 struct GNUNET_PeerIdentity me;
124
125 /**
126 * ID of reconnect task (if any).
127 */
128 struct GNUNET_SCHEDULER_Task *reconnect_task;
129
130 /**
131 * Current delay we use for re-trying to connect to core.
132 */
133 struct GNUNET_TIME_Relative retry_backoff;
134
135 /**
136 * Number of entries in the handlers array.
137 */
138 unsigned int hcnt;
139
140 /**
141 * Did we ever get INIT?
142 */
143 int have_init;
144};
145
146
147/**
148 * Our current client connection went down. Clean it up
149 * and try to reconnect!
150 *
151 * @param h our handle to the core service
152 */
153static void
154reconnect (struct GNUNET_CORE_Handle *h);
155
156
157/**
158 * Task schedule to try to re-connect to core.
159 *
160 * @param cls the `struct GNUNET_CORE_Handle`
161 */
162static void
163reconnect_task (void *cls)
164{
165 struct GNUNET_CORE_Handle *h = cls;
166
167 h->reconnect_task = NULL;
168 LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service after delay\n");
169 reconnect (h);
170}
171
172
173/**
174 * Notify clients about disconnect and free the entry for connected
175 * peer.
176 *
177 * @param cls the `struct GNUNET_CORE_Handle *`
178 * @param key the peer identity (not used)
179 * @param value the `struct PeerRecord` to free.
180 * @return #GNUNET_YES (continue)
181 */
182static int
183disconnect_and_free_peer_entry (void *cls,
184 const struct GNUNET_PeerIdentity *key,
185 void *value)
186{
187 struct GNUNET_CORE_Handle *h = cls;
188 struct PeerRecord *pr = value;
189
190 GNUNET_assert (pr->h == h);
191 if (NULL != h->disconnects)
192 h->disconnects (h->cls, &pr->peer, pr->client_cls);
193 GNUNET_assert (GNUNET_YES ==
194 GNUNET_CONTAINER_multipeermap_remove (h->peers, key, pr));
195 GNUNET_MQ_destroy (pr->mq);
196 GNUNET_assert (NULL == pr->mq);
197 if (NULL != pr->env)
198 {
199 GNUNET_MQ_discard (pr->env);
200 pr->env = NULL;
201 }
202 GNUNET_free (pr);
203 return GNUNET_YES;
204}
205
206
207/**
208 * Close down any existing connection to the CORE service and
209 * try re-establishing it later.
210 *
211 * @param h our handle
212 */
213static void
214reconnect_later (struct GNUNET_CORE_Handle *h)
215{
216 GNUNET_assert (NULL == h->reconnect_task);
217 if (NULL != h->mq)
218 {
219 GNUNET_MQ_destroy (h->mq);
220 h->mq = NULL;
221 }
222 GNUNET_assert (NULL == h->reconnect_task);
223 h->reconnect_task =
224 GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_task, h);
225 GNUNET_CONTAINER_multipeermap_iterate (h->peers,
226 &disconnect_and_free_peer_entry,
227 h);
228 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
229}
230
231
232/**
233 * Error handler for the message queue to the CORE service.
234 * On errors, we reconnect.
235 *
236 * @param cls closure, a `struct GNUNET_CORE_Handle *`
237 * @param error error code
238 */
239static void
240handle_mq_error (void *cls, enum GNUNET_MQ_Error error)
241{
242 struct GNUNET_CORE_Handle *h = cls;
243
244 LOG (GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %d\n", error);
245 reconnect_later (h);
246}
247
248
249/**
250 * Implement sending functionality of a message queue for
251 * us sending messages to a peer.
252 *
253 * @param mq the message queue
254 * @param msg the message to send
255 * @param impl_state state of the implementation
256 */
257static void
258core_mq_send_impl (struct GNUNET_MQ_Handle *mq,
259 const struct GNUNET_MessageHeader *msg,
260 void *impl_state)
261{
262 struct PeerRecord *pr = impl_state;
263 struct GNUNET_CORE_Handle *h = pr->h;
264 struct SendMessageRequest *smr;
265 struct SendMessage *sm;
266 struct GNUNET_MQ_Envelope *env;
267 uint16_t msize;
268 enum GNUNET_MQ_PriorityPreferences flags;
269
270 if (NULL == h->mq)
271 {
272 /* We're currently reconnecting, pretend this worked */
273 GNUNET_MQ_impl_send_continue (mq);
274 return;
275 }
276 GNUNET_assert (NULL == pr->env);
277 /* extract options from envelope */
278 env = GNUNET_MQ_get_current_envelope (mq);
279 flags = GNUNET_MQ_env_get_options (env);
280
281 /* check message size for sanity */
282 msize = ntohs (msg->size);
283 if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct SendMessage))
284 {
285 GNUNET_break (0);
286 GNUNET_MQ_impl_send_continue (mq);
287 return;
288 }
289
290 /* ask core for transmission */
291 LOG (GNUNET_ERROR_TYPE_DEBUG,
292 "Asking core for transmission of %u bytes to `%s'\n",
293 (unsigned int) msize,
294 GNUNET_i2s (&pr->peer));
295 env = GNUNET_MQ_msg (smr, GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST);
296 smr->priority = htonl ((uint32_t) flags);
297 smr->peer = pr->peer;
298 smr->size = htons (msize);
299 smr->smr_id = htons (++pr->smr_id_gen);
300 GNUNET_MQ_send (h->mq, env);
301
302 /* prepare message with actual transmission data */
303 pr->env = GNUNET_MQ_msg_nested_mh (sm, GNUNET_MESSAGE_TYPE_CORE_SEND, msg);
304 sm->priority = htonl ((uint32_t) flags);
305 sm->peer = pr->peer;
306 LOG (GNUNET_ERROR_TYPE_DEBUG,
307 "Calling get_message with buffer of %u bytes\n",
308 (unsigned int) msize);
309}
310
311
312/**
313 * Handle destruction of a message queue. Implementations must not
314 * free @a mq, but should take care of @a impl_state.
315 *
316 * @param mq the message queue to destroy
317 * @param impl_state state of the implementation
318 */
319static void
320core_mq_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state)
321{
322 struct PeerRecord *pr = impl_state;
323
324 GNUNET_assert (mq == pr->mq);
325 pr->mq = NULL;
326}
327
328
329/**
330 * Implementation function that cancels the currently sent message.
331 * Should basically undo whatever #mq_send_impl() did.
332 *
333 * @param mq message queue
334 * @param impl_state state specific to the implementation
335 */
336static void
337core_mq_cancel_impl (struct GNUNET_MQ_Handle *mq, void *impl_state)
338{
339 struct PeerRecord *pr = impl_state;
340
341 (void) mq;
342 GNUNET_assert (NULL != pr->env);
343 GNUNET_MQ_discard (pr->env);
344 pr->env = NULL;
345}
346
347
348/**
349 * We had an error processing a message we forwarded from a peer to
350 * the CORE service. We should just complain about it but otherwise
351 * continue processing.
352 *
353 * @param cls closure
354 * @param error error code
355 */
356static void
357core_mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
358{
359 /* struct PeerRecord *pr = cls; */
360 (void) cls;
361 (void) error;
362 GNUNET_break_op (0);
363}
364
365
366/**
367 * Add the given peer to the list of our connected peers
368 * and create the respective data structures and notify
369 * the application.
370 *
371 * @param h the core handle
372 * @param peer the peer that is connecting to us
373 */
374static void
375connect_peer (struct GNUNET_CORE_Handle *h,
376 const struct GNUNET_PeerIdentity *peer)
377{
378 struct PeerRecord *pr;
379
380 pr = GNUNET_new (struct PeerRecord);
381 pr->peer = *peer;
382 pr->h = h;
383 GNUNET_assert (GNUNET_YES ==
384 GNUNET_CONTAINER_multipeermap_put (
385 h->peers,
386 &pr->peer,
387 pr,
388 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
389 pr->mq = GNUNET_MQ_queue_for_callbacks (&core_mq_send_impl,
390 &core_mq_destroy_impl,
391 &core_mq_cancel_impl,
392 pr,
393 h->handlers,
394 &core_mq_error_handler,
395 pr);
396 if (NULL != h->connects)
397 {
398 pr->client_cls = h->connects (h->cls, &pr->peer, pr->mq);
399 GNUNET_MQ_set_handlers_closure (pr->mq, pr->client_cls);
400 }
401}
402
403
404/**
405 * Handle init reply message received from CORE service. Notify
406 * application that we are now connected to the CORE. Also fake
407 * loopback connection.
408 *
409 * @param cls the `struct GNUNET_CORE_Handle`
410 * @param m the init reply
411 */
412static void
413handle_init_reply (void *cls, const struct InitReplyMessage *m)
414{
415 struct GNUNET_CORE_Handle *h = cls;
416 GNUNET_CORE_StartupCallback init;
417
418 GNUNET_break (0 == ntohl (m->reserved));
419 h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
420 if (NULL != (init = h->init))
421 {
422 /* mark so we don't call init on reconnect */
423 h->init = NULL;
424 h->me = m->my_identity;
425 LOG (GNUNET_ERROR_TYPE_DEBUG,
426 "Connected to core service of peer `%s'.\n",
427 GNUNET_i2s (&h->me));
428 h->have_init = GNUNET_YES;
429 init (h->cls, &h->me);
430 }
431 else
432 {
433 LOG (GNUNET_ERROR_TYPE_DEBUG,
434 "Successfully reconnected to core service.\n");
435 if (GNUNET_NO == h->have_init)
436 {
437 h->me = m->my_identity;
438 h->have_init = GNUNET_YES;
439 }
440 else
441 {
442 GNUNET_break (0 == memcmp (&h->me,
443 &m->my_identity,
444 sizeof(struct GNUNET_PeerIdentity)));
445 }
446 }
447 /* fake 'connect to self' */
448 connect_peer (h, &h->me);
449}
450
451
452/**
453 * Handle connect message received from CORE service.
454 * Notify the application about the new connection.
455 *
456 * @param cls the `struct GNUNET_CORE_Handle`
457 * @param cnm the connect message
458 */
459static void
460handle_connect_notify (void *cls, const struct ConnectNotifyMessage *cnm)
461{
462 struct GNUNET_CORE_Handle *h = cls;
463 struct PeerRecord *pr;
464
465 LOG (GNUNET_ERROR_TYPE_DEBUG,
466 "Received notification about connection from `%s'.\n",
467 GNUNET_i2s (&cnm->peer));
468 if (0 == memcmp (&h->me, &cnm->peer, sizeof(struct GNUNET_PeerIdentity)))
469 {
470 /* connect to self!? */
471 GNUNET_break (0);
472 return;
473 }
474 pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &cnm->peer);
475 if (NULL != pr)
476 {
477 GNUNET_break (0);
478 reconnect_later (h);
479 return;
480 }
481 connect_peer (h, &cnm->peer);
482}
483
484
485/**
486 * Handle disconnect message received from CORE service.
487 * Notify the application about the lost connection.
488 *
489 * @param cls the `struct GNUNET_CORE_Handle`
490 * @param dnm message about the disconnect event
491 */
492static void
493handle_disconnect_notify (void *cls, const struct DisconnectNotifyMessage *dnm)
494{
495 struct GNUNET_CORE_Handle *h = cls;
496 struct PeerRecord *pr;
497
498 if (0 == memcmp (&h->me, &dnm->peer, sizeof(struct GNUNET_PeerIdentity)))
499 {
500 /* disconnect from self!? */
501 GNUNET_break (0);
502 return;
503 }
504 GNUNET_break (0 == ntohl (dnm->reserved));
505 LOG (GNUNET_ERROR_TYPE_DEBUG,
506 "Received notification about disconnect from `%s'.\n",
507 GNUNET_i2s (&dnm->peer));
508 pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &dnm->peer);
509 if (NULL == pr)
510 {
511 GNUNET_break (0);
512 reconnect_later (h);
513 return;
514 }
515 disconnect_and_free_peer_entry (h, &pr->peer, pr);
516}
517
518
519/**
520 * Check that message received from CORE service is well-formed.
521 *
522 * @param cls the `struct GNUNET_CORE_Handle`
523 * @param ntm the message we got
524 * @return #GNUNET_OK if the message is well-formed
525 */
526static int
527check_notify_inbound (void *cls, const struct NotifyTrafficMessage *ntm)
528{
529 uint16_t msize;
530 const struct GNUNET_MessageHeader *em;
531
532 (void) cls;
533 msize = ntohs (ntm->header.size) - sizeof(struct NotifyTrafficMessage);
534 if (msize < sizeof(struct GNUNET_MessageHeader))
535 {
536 GNUNET_break (0);
537 return GNUNET_SYSERR;
538 }
539 em = (const struct GNUNET_MessageHeader *) &ntm[1];
540 if (msize != ntohs (em->size))
541 {
542 GNUNET_break (0);
543 return GNUNET_SYSERR;
544 }
545 return GNUNET_OK;
546}
547
548
549/**
550 * Handle inbound message received from CORE service. If applicable,
551 * notify the application.
552 *
553 * @param cls the `struct GNUNET_CORE_Handle`
554 * @param ntm the message we got from CORE.
555 */
556static void
557handle_notify_inbound (void *cls, const struct NotifyTrafficMessage *ntm)
558{
559 struct GNUNET_CORE_Handle *h = cls;
560 const struct GNUNET_MessageHeader *em;
561 struct PeerRecord *pr;
562
563 LOG (GNUNET_ERROR_TYPE_DEBUG,
564 "Received inbound message from `%s'.\n",
565 GNUNET_i2s (&ntm->peer));
566 em = (const struct GNUNET_MessageHeader *) &ntm[1];
567 pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &ntm->peer);
568 if (NULL == pr)
569 {
570 GNUNET_break (0);
571 reconnect_later (h);
572 return;
573 }
574 GNUNET_MQ_inject_message (pr->mq, em);
575}
576
577
578/**
579 * Handle message received from CORE service notifying us that we are
580 * now allowed to send a message to a peer. If that message is still
581 * pending, put it into the queue to be transmitted.
582 *
583 * @param cls the `struct GNUNET_CORE_Handle`
584 * @param smr the message we got
585 */
586static void
587handle_send_ready (void *cls, const struct SendMessageReady *smr)
588{
589 struct GNUNET_CORE_Handle *h = cls;
590 struct PeerRecord *pr;
591
592 pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &smr->peer);
593 if (NULL == pr)
594 {
595 GNUNET_break (0);
596 reconnect_later (h);
597 return;
598 }
599 LOG (GNUNET_ERROR_TYPE_DEBUG,
600 "Received notification about transmission readiness to `%s'.\n",
601 GNUNET_i2s (&smr->peer));
602 if (NULL == pr->env)
603 {
604 /* request must have been cancelled between the original request
605 * and the response from CORE, ignore CORE's readiness */
606 return;
607 }
608 if (ntohs (smr->smr_id) != pr->smr_id_gen)
609 {
610 /* READY message is for expired or cancelled message,
611 * ignore! (we should have already sent another request) */
612 return;
613 }
614
615 /* ok, all good, send message out! */
616 GNUNET_MQ_send (h->mq, pr->env);
617 pr->env = NULL;
618 GNUNET_MQ_impl_send_continue (pr->mq);
619}
620
621
622/**
623 * Our current client connection went down. Clean it up and try to
624 * reconnect!
625 *
626 * @param h our handle to the core service
627 */
628static void
629reconnect (struct GNUNET_CORE_Handle *h)
630{
631 struct GNUNET_MQ_MessageHandler handlers[] =
632 { GNUNET_MQ_hd_fixed_size (init_reply,
633 GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY,
634 struct InitReplyMessage,
635 h),
636 GNUNET_MQ_hd_fixed_size (connect_notify,
637 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT,
638 struct ConnectNotifyMessage,
639 h),
640 GNUNET_MQ_hd_fixed_size (disconnect_notify,
641 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT,
642 struct DisconnectNotifyMessage,
643 h),
644 GNUNET_MQ_hd_var_size (notify_inbound,
645 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND,
646 struct NotifyTrafficMessage,
647 h),
648 GNUNET_MQ_hd_fixed_size (send_ready,
649 GNUNET_MESSAGE_TYPE_CORE_SEND_READY,
650 struct SendMessageReady,
651 h),
652 GNUNET_MQ_handler_end () };
653 struct InitMessage *init;
654 struct GNUNET_MQ_Envelope *env;
655 uint16_t *ts;
656
657 GNUNET_assert (NULL == h->mq);
658 h->mq = GNUNET_CLIENT_connect (h->cfg, "core", handlers, &handle_mq_error, h);
659 if (NULL == h->mq)
660 {
661 reconnect_later (h);
662 return;
663 }
664 env = GNUNET_MQ_msg_extra (init,
665 sizeof(uint16_t) * h->hcnt,
666 GNUNET_MESSAGE_TYPE_CORE_INIT);
667 LOG (GNUNET_ERROR_TYPE_INFO, "(Re)connecting to CORE service\n");
668 init->options = htonl (0);
669 ts = (uint16_t *) &init[1];
670 for (unsigned int hpos = 0; hpos < h->hcnt; hpos++)
671 ts[hpos] = htons (h->handlers[hpos].type);
672 GNUNET_MQ_send (h->mq, env);
673}
674
675
676/**
677 * Connect to the core service. Note that the connection may complete
678 * (or fail) asynchronously.
679 *
680 * @param cfg configuration to use
681 * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
682 * @param init callback to call once we have successfully
683 * connected to the core service
684 * @param connects function to call on peer connect, can be NULL
685 * @param disconnects function to call on peer disconnect / timeout, can be NULL
686 * @param handlers callbacks for messages we care about, NULL-terminated
687 * @return handle to the core service (only useful for disconnect until @a init is called);
688 * NULL on error (in this case, init is never called)
689 */
690struct GNUNET_CORE_Handle *
691GNUNET_CORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
692 void *cls,
693 GNUNET_CORE_StartupCallback init,
694 GNUNET_CORE_ConnectEventHandler connects,
695 GNUNET_CORE_DisconnectEventHandler disconnects,
696 const struct GNUNET_MQ_MessageHandler *handlers)
697{
698 struct GNUNET_CORE_Handle *h;
699
700 h = GNUNET_new (struct GNUNET_CORE_Handle);
701 h->cfg = cfg;
702 h->cls = cls;
703 h->init = init;
704 h->connects = connects;
705 h->disconnects = disconnects;
706 h->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
707 h->handlers = GNUNET_MQ_copy_handlers (handlers);
708 h->hcnt = GNUNET_MQ_count_handlers (handlers);
709 GNUNET_assert (h->hcnt <
710 (GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InitMessage))
711 / sizeof(uint16_t));
712 LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service\n");
713 reconnect (h);
714 if (NULL == h->mq)
715 {
716 GNUNET_CORE_disconnect (h);
717 return NULL;
718 }
719 return h;
720}
721
722
723/**
724 * Disconnect from the core service.
725 *
726 * @param handle connection to core to disconnect
727 */
728void
729GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
730{
731 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from CORE service\n");
732 GNUNET_CONTAINER_multipeermap_iterate (handle->peers,
733 &disconnect_and_free_peer_entry,
734 handle);
735 GNUNET_CONTAINER_multipeermap_destroy (handle->peers);
736 handle->peers = NULL;
737 if (NULL != handle->reconnect_task)
738 {
739 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
740 handle->reconnect_task = NULL;
741 }
742 if (NULL != handle->mq)
743 {
744 GNUNET_MQ_destroy (handle->mq);
745 handle->mq = NULL;
746 }
747 GNUNET_free (handle->handlers);
748 GNUNET_free (handle);
749}
750
751
752/**
753 * Obtain the message queue for a connected peer.
754 *
755 * @param h the core handle
756 * @param pid the identity of the peer to check if it has been connected to us
757 * @return NULL if peer is not connected
758 */
759struct GNUNET_MQ_Handle *
760GNUNET_CORE_get_mq (const struct GNUNET_CORE_Handle *h,
761 const struct GNUNET_PeerIdentity *pid)
762{
763 struct PeerRecord *pr;
764
765 pr = GNUNET_CONTAINER_multipeermap_get (h->peers, pid);
766 if (NULL == pr)
767 return NULL;
768 return pr->mq;
769}
770
771
772/* end of core_api.c */
diff --git a/src/service/core/core_api_cmd_connecting_peers.c b/src/service/core/core_api_cmd_connecting_peers.c
new file mode 100644
index 000000000..ce2184a3c
--- /dev/null
+++ b/src/service/core/core_api_cmd_connecting_peers.c
@@ -0,0 +1,275 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file testing_api_cmd_start_peer.c
23 * @brief cmd to start a peer.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_ng_lib.h"
29#include "gnunet_testing_netjail_lib.h"
30#include "gnunet_transport_application_service.h"
31#include "gnunet_hello_lib.h"
32#include "gnunet_transport_core_service.h"
33
34/**
35 * Generic logging shortcut
36 */
37#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
38
39
40/**
41 * The run method of this cmd will connect to peers.
42 *
43 */
44static void
45connect_peers_run (void *cls,
46 struct GNUNET_TESTING_Interpreter *is)
47{
48 struct GNUNET_TESTING_ConnectPeersState *cps = cls;
49 const struct GNUNET_TESTING_StartPeerState *sps;
50 const struct GNUNET_TESTING_Command *system_cmd;
51 const struct GNUNET_TESTING_System *tl_system;
52 const struct GNUNET_TESTING_Command *peer1_cmd;
53 struct GNUNET_PeerIdentity *peer;
54 enum GNUNET_NetworkType nt = 0;
55 struct GNUNET_TESTING_NodeConnection *pos_connection;
56 struct GNUNET_TESTING_AddressPrefix *pos_prefix;
57 const enum GNUNET_GenericReturnValue *broadcast;
58 unsigned int con_num = 0;
59 uint32_t num;
60 char *addr;
61 char *addr_and_port;
62 char *emsg = NULL;
63
64 cps->is = is;
65 peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is,
66 cps->start_peer_label);
67 GNUNET_TESTING_get_trait_broadcast (peer1_cmd,
68 &broadcast);
69 GNUNET_TESTING_get_trait_state (peer1_cmd,
70 &sps);
71
72 system_cmd = GNUNET_TESTING_interpreter_lookup_command (is,
73 cps->create_label);
74 GNUNET_TESTING_get_trait_test_system (system_cmd,
75 &tl_system);
76
77 cps->tl_system = tl_system;
78
79 LOG (GNUNET_ERROR_TYPE_DEBUG,
80 "cps->num: %u \n",
81 cps->num);
82
83
84 cps->ah = GNUNET_TRANSPORT_application_init (sps->cfg);
85 if (NULL == cps->ah)
86 {
87 LOG (GNUNET_ERROR_TYPE_ERROR,
88 "Failed to initialize the TRANSPORT application suggestion client handle for peer `%s': `%s'\n",
89 sps->cfgname,
90 emsg);
91 GNUNET_free (emsg);
92 GNUNET_TESTING_interpreter_fail (is);
93 return;
94 }
95
96 cps->node_connections_head = GNUNET_TESTING_get_connections (cps->num,
97 cps->topology);
98
99 for (pos_connection = cps->node_connections_head; NULL != pos_connection;
100 pos_connection = pos_connection->next)
101 {
102 con_num++;
103 num = GNUNET_TESTING_calculate_num (pos_connection, cps->topology);
104 for (pos_prefix = pos_connection->address_prefixes_head; NULL != pos_prefix;
105 pos_prefix =
106 pos_prefix->next)
107 {
108 addr = GNUNET_TESTING_get_address (pos_connection,
109 pos_prefix->address_prefix);
110 if (NULL != addr)
111 {
112 char *natted_p = strstr (pos_prefix->address_prefix, "_");
113
114 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
115 "0 validating peer number %s %s %s\n",
116 natted_p,
117 pos_prefix->address_prefix,
118 addr);
119 if (0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp"))
120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
121 "validating memcmp\n");
122 if (GNUNET_YES == *broadcast)
123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124 "validating broadcast\n");
125 if ((0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp")) &&
126 (GNUNET_YES == *broadcast) )
127 GNUNET_asprintf (&addr_and_port,
128 "%s:2086",
129 addr);
130 else if (NULL == natted_p)
131 GNUNET_asprintf (&addr_and_port,
132 "%s:60002",
133 addr);
134 else if (NULL != natted_p)
135 {
136 char *prefix;
137 char *rest;
138 char *rest2;
139 char *address;
140
141 prefix = strtok (addr, "_");
142 rest = strtok (NULL, "_");
143 rest2 = strtok (rest, "-");
144 address = strtok (NULL, "-");
145
146 GNUNET_asprintf (&addr_and_port,
147 "%s-%s:0",
148 prefix,
149 address);
150
151 }
152 peer = GNUNET_TESTING_get_peer (num, tl_system);
153 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
154 "validating peer number %u with identity %s and address %s %u %s and handle %p\n",
155 num,
156 GNUNET_i2s (peer),
157 addr_and_port,
158 *broadcast,
159 pos_prefix->address_prefix,
160 cps->ah);
161 GNUNET_TRANSPORT_application_validate ((struct
162 GNUNET_TRANSPORT_ApplicationHandle
163 *) cps->ah,
164 peer,
165 nt,
166 addr_and_port);
167 GNUNET_free (peer);
168 GNUNET_free (addr);
169 GNUNET_free (addr_and_port);
170 }
171 }
172 }
173 cps->con_num = con_num;
174}
175
176
177/**
178 * The cleanup function of this cmd frees resources the cmd allocated.
179 *
180 */
181static void
182connect_peers_cleanup (void *cls)
183{
184 struct GNUNET_TESTING_ConnectPeersState *cps = cls;
185
186 GNUNET_free (cps->connected_peers_map);
187 GNUNET_free (cps);
188}
189
190
191/**
192 * This function prepares an array with traits.
193 *
194 */
195enum GNUNET_GenericReturnValue
196connect_peers_traits (void *cls,
197 const void **ret,
198 const char *trait,
199 unsigned int index)
200{
201 struct GNUNET_TESTING_ConnectPeersState *cps = cls;
202 struct GNUNET_TESTING_Trait traits[] = {
203 GNUNET_TESTING_make_trait_connect_peer_state ((const void *) cps),
204 GNUNET_TESTING_trait_end ()
205 };
206 return GNUNET_TESTING_get_trait (traits,
207 ret,
208 trait,
209 index);
210}
211
212
213struct GNUNET_TESTING_Command
214GNUNET_CORE_cmd_connect_peers (const char *label,
215 const char *start_peer_label,
216 const char *create_label,
217 uint32_t num,
218 struct GNUNET_TESTING_NetjailTopology *
219 topology,
220 unsigned int additional_connects,
221 unsigned int wait_for_connect,
222 struct GNUNET_MQ_MessageHandler *handlers)
223{
224 struct GNUNET_TESTING_ConnectPeersState *cps;
225 unsigned int node_additional_connects;
226 struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map =
227 GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO);
228 unsigned int i;
229
230 node_additional_connects = GNUNET_TESTING_get_additional_connects (num,
231 topology);
232
233 LOG (GNUNET_ERROR_TYPE_DEBUG,
234 "global: %u and local: %u additional_connects\n",
235 additional_connects,
236 node_additional_connects);
237
238 if (0 != node_additional_connects)
239 additional_connects = node_additional_connects;
240
241 cps = GNUNET_new (struct GNUNET_TESTING_ConnectPeersState);
242 cps->start_peer_label = start_peer_label;
243 cps->num = num;
244 cps->create_label = create_label;
245 cps->topology = topology;
246 cps->additional_connects = additional_connects;
247 cps->wait_for_connect = wait_for_connect;
248 cps->connected_peers_map = connected_peers_map;
249
250 if (NULL != handlers)
251 {
252 for (i = 0; NULL != handlers[i].cb; i++)
253 ;
254 cps->handlers = GNUNET_new_array (i + 1,
255 struct GNUNET_MQ_MessageHandler);
256 GNUNET_memcpy (cps->handlers,
257 handlers,
258 i * sizeof(struct GNUNET_MQ_MessageHandler));
259 }
260
261 if (GNUNET_YES == wait_for_connect)
262 return GNUNET_TESTING_command_new (cps,
263 label,
264 &connect_peers_run,
265 &connect_peers_cleanup,
266 &connect_peers_traits,
267 &cps->ac);
268 else
269 return GNUNET_TESTING_command_new (cps,
270 label,
271 &connect_peers_run,
272 &connect_peers_cleanup,
273 &connect_peers_traits,
274 NULL);
275}
diff --git a/src/service/core/core_api_monitor_peers.c b/src/service/core/core_api_monitor_peers.c
new file mode 100644
index 000000000..3be8e3859
--- /dev/null
+++ b/src/service/core/core_api_monitor_peers.c
@@ -0,0 +1,196 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/core_api_monitor_peers.c
23 * @brief implementation of the peer_iterate function
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */
27#include "platform.h"
28#include "gnunet_core_service.h"
29#include "core.h"
30
31
32/**
33 * Handle to a CORE monitoring operation.
34 */
35struct GNUNET_CORE_MonitorHandle
36{
37 /**
38 * Our configuration.
39 */
40 const struct GNUNET_CONFIGURATION_Handle *cfg;
41
42 /**
43 * Our connection to the service.
44 */
45 struct GNUNET_MQ_Handle *mq;
46
47 /**
48 * Function called with the peer.
49 */
50 GNUNET_CORE_MonitorCallback peer_cb;
51
52 /**
53 * Closure for @e peer_cb.
54 */
55 void *peer_cb_cls;
56};
57
58
59/**
60 * Protocol error, reconnect to CORE service and notify
61 * client.
62 *
63 * @param mh monitoring session to reconnect to CORE
64 */
65static void
66reconnect (struct GNUNET_CORE_MonitorHandle *mh);
67
68
69/**
70 * Generic error handler, called with the appropriate error code and
71 * the same closure specified at the creation of the message queue.
72 * Not every message queue implementation supports an error handler.
73 *
74 * @param cls closure, a `struct GNUNET_CORE_MonitorHandle *`
75 * @param error error code
76 */
77static void
78handle_mq_error (void *cls, enum GNUNET_MQ_Error error)
79{
80 struct GNUNET_CORE_MonitorHandle *mh = cls;
81
82 (void) error;
83 reconnect (mh);
84}
85
86
87/**
88 * Receive reply from CORE service with information about a peer.
89 *
90 * @param cls our `struct GNUNET_CORE_MonitorHandle *`
91 * @param mon_message monitor message
92 */
93static void
94handle_receive_info (void *cls, const struct MonitorNotifyMessage *mon_message)
95{
96 struct GNUNET_CORE_MonitorHandle *mh = cls;
97
98 mh->peer_cb (mh->peer_cb_cls,
99 &mon_message->peer,
100 (enum GNUNET_CORE_KxState) ntohl (mon_message->state),
101 GNUNET_TIME_absolute_ntoh (mon_message->timeout));
102}
103
104
105/**
106 * Protocol error, reconnect to CORE service and notify
107 * client.
108 *
109 * @param mh monitoring session to reconnect to CORE
110 */
111static void
112reconnect (struct GNUNET_CORE_MonitorHandle *mh)
113{
114 struct GNUNET_MQ_MessageHandler handlers[] =
115 { GNUNET_MQ_hd_fixed_size (receive_info,
116 GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY,
117 struct MonitorNotifyMessage,
118 mh),
119 GNUNET_MQ_handler_end () };
120 struct GNUNET_MQ_Envelope *env;
121 struct GNUNET_MessageHeader *msg;
122
123 if (NULL != mh->mq)
124 GNUNET_MQ_destroy (mh->mq);
125 /* FIXME: use backoff? */
126 mh->mq =
127 GNUNET_CLIENT_connect (mh->cfg, "core", handlers, &handle_mq_error, mh);
128 if (NULL == mh->mq)
129 return;
130 /* notify callback about reconnect */
131 if (NULL != mh->peer_cb)
132 mh->peer_cb (mh->peer_cb_cls,
133 NULL,
134 GNUNET_CORE_KX_CORE_DISCONNECT,
135 GNUNET_TIME_UNIT_FOREVER_ABS);
136 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS);
137 GNUNET_MQ_send (mh->mq, env);
138}
139
140
141/**
142 * Monitor connectivity and KX status of all peers known to CORE.
143 * Calls @a peer_cb with the current status for each connected peer,
144 * and then once with NULL to indicate that all peers that are
145 * currently active have been handled. After that, the iteration
146 * continues until it is cancelled. Normal users of the CORE API are
147 * not expected to use this function. It is different in that it
148 * truly lists all connections (including those where the KX is in
149 * progress), not just those relevant to the application. This
150 * function is used by special applications for diagnostics.
151 *
152 * @param cfg configuration handle
153 * @param peer_cb function to call with the peer information
154 * @param peer_cb_cls closure for @a peer_cb
155 * @return NULL on error
156 */
157struct GNUNET_CORE_MonitorHandle *
158GNUNET_CORE_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
159 GNUNET_CORE_MonitorCallback peer_cb,
160 void *peer_cb_cls)
161{
162 struct GNUNET_CORE_MonitorHandle *mh;
163
164 GNUNET_assert (NULL != peer_cb);
165 mh = GNUNET_new (struct GNUNET_CORE_MonitorHandle);
166 mh->cfg = cfg;
167 reconnect (mh);
168 mh->peer_cb = peer_cb;
169 mh->peer_cb_cls = peer_cb_cls;
170 if (NULL == mh->mq)
171 {
172 GNUNET_free (mh);
173 return NULL;
174 }
175 return mh;
176}
177
178
179/**
180 * Stop monitoring CORE activity.
181 *
182 * @param mh monitor to stop
183 */
184void
185GNUNET_CORE_monitor_stop (struct GNUNET_CORE_MonitorHandle *mh)
186{
187 if (NULL != mh->mq)
188 {
189 GNUNET_MQ_destroy (mh->mq);
190 mh->mq = NULL;
191 }
192 GNUNET_free (mh);
193}
194
195
196/* end of core_api_monitor_peers.c */
diff --git a/src/service/core/gnunet-service-core.c b/src/service/core/gnunet-service-core.c
new file mode 100644
index 000000000..e387fecc9
--- /dev/null
+++ b/src/service/core/gnunet-service-core.c
@@ -0,0 +1,988 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core.c
23 * @brief high-level P2P messaging
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <gcrypt.h>
28#include "gnunet_util_lib.h"
29#include "gnunet-service-core.h"
30#include "gnunet-service-core_kx.h"
31#include "gnunet-service-core_sessions.h"
32#include "gnunet-service-core_typemap.h"
33#include "gnunet_constants.h"
34
35/**
36 * How many messages do we queue up at most for any client? This can
37 * cause messages to be dropped if clients do not process them fast
38 * enough! Note that this is a soft limit; we try
39 * to keep a few larger messages above the limit.
40 */
41#define SOFT_MAX_QUEUE 128
42
43/**
44 * How many messages do we queue up at most for any client? This can
45 * cause messages to be dropped if clients do not process them fast
46 * enough! Note that this is the hard limit.
47 */
48#define HARD_MAX_QUEUE 256
49
50
51/**
52 * Data structure for each client connected to the CORE service.
53 */
54struct GSC_Client
55{
56 /**
57 * Clients are kept in a linked list.
58 */
59 struct GSC_Client *next;
60
61 /**
62 * Clients are kept in a linked list.
63 */
64 struct GSC_Client *prev;
65
66 /**
67 * Handle for the client with the server API.
68 */
69 struct GNUNET_SERVICE_Client *client;
70
71 /**
72 * Message queue to talk to @e client.
73 */
74 struct GNUNET_MQ_Handle *mq;
75
76 /**
77 * Array of the types of messages this peer cares
78 * about (with @e tcnt entries). Allocated as part
79 * of this client struct, do not free!
80 */
81 uint16_t *types;
82
83 /**
84 * Map of peer identities to active transmission requests of this
85 * client to the peer (of type `struct GSC_ClientActiveRequest`).
86 */
87 struct GNUNET_CONTAINER_MultiPeerMap *requests;
88
89 /**
90 * Map containing all peers that this client knows we're connected to.
91 */
92 struct GNUNET_CONTAINER_MultiPeerMap *connectmap;
93
94 /**
95 * Options for messages this client cares about,
96 * see GNUNET_CORE_OPTION_ values.
97 */
98 uint32_t options;
99
100 /**
101 * Have we gotten the #GNUNET_MESSAGE_TYPE_CORE_INIT message
102 * from this client already?
103 */
104 int got_init;
105
106 /**
107 * Number of types of incoming messages this client
108 * specifically cares about. Size of the @e types array.
109 */
110 unsigned int tcnt;
111};
112
113
114/**
115 * Our identity.
116 */
117struct GNUNET_PeerIdentity GSC_my_identity;
118
119/**
120 * Our configuration.
121 */
122const struct GNUNET_CONFIGURATION_Handle *GSC_cfg;
123
124/**
125 * For creating statistics.
126 */
127struct GNUNET_STATISTICS_Handle *GSC_stats;
128
129/**
130 * Big "or" of all client options.
131 */
132static uint32_t all_client_options;
133
134/**
135 * Head of linked list of our clients.
136 */
137static struct GSC_Client *client_head;
138
139/**
140 * Tail of linked list of our clients.
141 */
142static struct GSC_Client *client_tail;
143
144
145/**
146 * Test if the client is interested in messages of the given type.
147 *
148 * @param type message type
149 * @param c client to test
150 * @return #GNUNET_YES if @a c is interested, #GNUNET_NO if not.
151 */
152static int
153type_match (uint16_t type, struct GSC_Client *c)
154{
155 if ((0 == c->tcnt) && (0 != c->options))
156 return GNUNET_YES; /* peer without handlers and inbound/outbond
157 callbacks matches ALL */
158 if (NULL == c->types)
159 return GNUNET_NO;
160 for (unsigned int i = 0; i < c->tcnt; i++)
161 if (type == c->types[i])
162 return GNUNET_YES;
163 return GNUNET_NO;
164}
165
166
167/**
168 * Check #GNUNET_MESSAGE_TYPE_CORE_INIT request.
169 *
170 * @param cls client that sent #GNUNET_MESSAGE_TYPE_CORE_INIT
171 * @param im the `struct InitMessage`
172 * @return #GNUNET_OK if @a im is well-formed
173 */
174static int
175check_client_init (void *cls, const struct InitMessage *im)
176{
177 return GNUNET_OK;
178}
179
180
181/**
182 * Handle #GNUNET_MESSAGE_TYPE_CORE_INIT request.
183 *
184 * @param cls client that sent #GNUNET_MESSAGE_TYPE_CORE_INIT
185 * @param im the `struct InitMessage`
186 */
187static void
188handle_client_init (void *cls, const struct InitMessage *im)
189{
190 struct GSC_Client *c = cls;
191 struct GNUNET_MQ_Envelope *env;
192 struct InitReplyMessage *irm;
193 uint16_t msize;
194 const uint16_t *types;
195
196 /* check that we don't have an entry already */
197 msize = ntohs (im->header.size) - sizeof(struct InitMessage);
198 types = (const uint16_t *) &im[1];
199 c->tcnt = msize / sizeof(uint16_t);
200 c->options = ntohl (im->options);
201 c->got_init = GNUNET_YES;
202 all_client_options |= c->options;
203 c->types = GNUNET_malloc (msize);
204 GNUNET_assert (GNUNET_YES ==
205 GNUNET_CONTAINER_multipeermap_put (
206 c->connectmap,
207 &GSC_my_identity,
208 NULL,
209 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
210 for (unsigned int i = 0; i < c->tcnt; i++)
211 c->types[i] = ntohs (types[i]);
212 GSC_TYPEMAP_add (c->types, c->tcnt);
213 GNUNET_log (
214 GNUNET_ERROR_TYPE_DEBUG,
215 "Client connecting to core service is interested in %u message types\n",
216 (unsigned int) c->tcnt);
217 /* send init reply message */
218 env = GNUNET_MQ_msg (irm, GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
219 irm->reserved = htonl (0);
220 irm->my_identity = GSC_my_identity;
221 GNUNET_MQ_send (c->mq, env);
222 GSC_SESSIONS_notify_client_about_sessions (c);
223 GNUNET_SERVICE_client_continue (c->client);
224}
225
226
227/**
228 * We will never be ready to transmit the given message in (disconnect
229 * or invalid request). Frees resources associated with @a car. We
230 * don't explicitly tell the client, it'll learn with the disconnect
231 * (or violated the protocol).
232 *
233 * @param car request that now permanently failed; the
234 * responsibility for the handle is now returned
235 * to CLIENTS (SESSIONS is done with it).
236 * @param drop_client #GNUNET_YES if the client violated the protocol
237 * and we should thus drop the connection
238 */
239void
240GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car,
241 int drop_client)
242{
243 GNUNET_assert (
244 GNUNET_YES ==
245 GNUNET_CONTAINER_multipeermap_remove (car->client_handle->requests,
246 &car->target,
247 car));
248 if (GNUNET_YES == drop_client)
249 GNUNET_SERVICE_client_drop (car->client_handle->client);
250 GNUNET_free (car);
251}
252
253
254/**
255 * Tell a client that we are ready to receive the message.
256 *
257 * @param car request that is now ready; the responsibility
258 * for the handle remains shared between CLIENTS
259 * and SESSIONS after this call.
260 */
261void
262GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
263{
264 struct GSC_Client *c;
265 struct GNUNET_MQ_Envelope *env;
266 struct SendMessageReady *smr;
267 struct GNUNET_TIME_Relative delay;
268 struct GNUNET_TIME_Relative left;
269
270 c = car->client_handle;
271 if (GNUNET_YES !=
272 GNUNET_CONTAINER_multipeermap_contains (c->connectmap, &car->target))
273 {
274 /* connection has gone down since, drop request */
275 GNUNET_assert (0 !=
276 GNUNET_memcmp (&car->target,
277 &GSC_my_identity));
278 GSC_SESSIONS_dequeue_request (car);
279 GSC_CLIENTS_reject_request (car, GNUNET_NO);
280 return;
281 }
282 delay = GNUNET_TIME_absolute_get_duration (car->received_time);
283 left = GNUNET_TIME_absolute_get_duration (car->deadline);
284 if (delay.rel_value_us > GNUNET_CONSTANTS_LATENCY_WARN.rel_value_us)
285 GNUNET_log (
286 GNUNET_ERROR_TYPE_WARNING,
287 "Client waited %s for permission to transmit to `%s'%s (priority %u)\n",
288 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES),
289 GNUNET_i2s (&car->target),
290 (0 == left.rel_value_us) ? " (past deadline)" : "",
291 car->priority);
292 env = GNUNET_MQ_msg (smr, GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
293 smr->size = htons (car->msize);
294 smr->smr_id = car->smr_id;
295 smr->peer = car->target;
296 GNUNET_MQ_send (c->mq, env);
297}
298
299
300/**
301 * Handle #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST message.
302 *
303 * @param cls client that sent a #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST
304 * @param req the `struct SendMessageRequest`
305 */
306static void
307handle_client_send_request (void *cls, const struct SendMessageRequest *req)
308{
309 struct GSC_Client *c = cls;
310 struct GSC_ClientActiveRequest *car;
311 int is_loopback;
312
313 if (NULL == c->requests)
314 c->requests = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_NO);
315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316 "Client asked for transmission to `%s'\n",
317 GNUNET_i2s (&req->peer));
318 is_loopback = (0 == GNUNET_memcmp (&req->peer,
319 &GSC_my_identity));
320 if ((! is_loopback) &&
321 (GNUNET_YES !=
322 GNUNET_CONTAINER_multipeermap_contains (c->connectmap, &req->peer)))
323 {
324 /* neighbour must have disconnected since request was issued,
325 * ignore (client will realize it once it processes the
326 * disconnect notification) */
327 GNUNET_STATISTICS_update (GSC_stats,
328 gettext_noop (
329 "# send requests dropped (disconnected)"),
330 1,
331 GNUNET_NO);
332 GNUNET_SERVICE_client_continue (c->client);
333 return;
334 }
335
336 car = GNUNET_CONTAINER_multipeermap_get (c->requests, &req->peer);
337 if (NULL == car)
338 {
339 /* create new entry */
340 car = GNUNET_new (struct GSC_ClientActiveRequest);
341 GNUNET_assert (GNUNET_OK ==
342 GNUNET_CONTAINER_multipeermap_put (
343 c->requests,
344 &req->peer,
345 car,
346 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
347 car->client_handle = c;
348 }
349 else
350 {
351 /* dequeue and recycle memory from pending request, there can only
352 be at most one per client and peer */
353 GNUNET_STATISTICS_update (GSC_stats,
354 gettext_noop (
355 "# dequeuing CAR (duplicate request)"),
356 1,
357 GNUNET_NO);
358 GSC_SESSIONS_dequeue_request (car);
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Transmission request to `%s' was a duplicate!\n",
361 GNUNET_i2s (&req->peer));
362 }
363 car->target = req->peer;
364 car->received_time = GNUNET_TIME_absolute_get ();
365 car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
366 car->priority = (enum GNUNET_MQ_PriorityPreferences) ntohl (req->priority);
367 car->msize = ntohs (req->size);
368 car->smr_id = req->smr_id;
369 car->was_solicited = GNUNET_NO;
370 GNUNET_SERVICE_client_continue (c->client);
371 if (is_loopback)
372 {
373 /* loopback, satisfy immediately */
374 GSC_CLIENTS_solicit_request (car);
375 return;
376 }
377 GSC_SESSIONS_queue_request (car);
378}
379
380
381/**
382 * Closure for the #client_tokenizer_callback().
383 */
384struct TokenizerContext
385{
386 /**
387 * Active request handle for the message.
388 */
389 struct GSC_ClientActiveRequest *car;
390
391 /**
392 * How important is this message.
393 */
394 enum GNUNET_MQ_PriorityPreferences priority;
395};
396
397
398/**
399 * Functions with this signature are called whenever a complete
400 * message is received by the tokenizer. Used by
401 * #handle_client_send() for dispatching messages from clients to
402 * either the SESSION subsystem or other CLIENT (for loopback).
403 *
404 * @param cls reservation request (`struct TokenizerContext`)
405 * @param message the actual message
406 * @return #GNUNET_OK on success,
407 * #GNUNET_NO to stop further processing (no error)
408 * #GNUNET_SYSERR to stop further processing with error
409 */
410static int
411tokenized_cb (void *cls, const struct GNUNET_MessageHeader *message)
412{
413 struct TokenizerContext *tc = cls;
414 struct GSC_ClientActiveRequest *car = tc->car;
415 char buf[92];
416
417 GNUNET_snprintf (buf,
418 sizeof(buf),
419 gettext_noop ("# bytes of messages of type %u received"),
420 (unsigned int) ntohs (message->type));
421 GNUNET_STATISTICS_update (GSC_stats, buf, ntohs (message->size), GNUNET_NO);
422 if (0 == GNUNET_memcmp (&car->target,
423 &GSC_my_identity))
424 {
425 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
426 "Delivering message of type %u to myself\n",
427 ntohs (message->type));
428 GSC_CLIENTS_deliver_message (&GSC_my_identity,
429 message,
430 ntohs (message->size),
431 GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
432 GSC_CLIENTS_deliver_message (&GSC_my_identity,
433 message,
434 sizeof(struct GNUNET_MessageHeader),
435 GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
436 GSC_CLIENTS_deliver_message (&GSC_my_identity,
437 message,
438 ntohs (message->size),
439 GNUNET_CORE_OPTION_SEND_FULL_INBOUND);
440 GSC_CLIENTS_deliver_message (&GSC_my_identity,
441 message,
442 sizeof(struct GNUNET_MessageHeader),
443 GNUNET_CORE_OPTION_SEND_HDR_INBOUND);
444 }
445 else
446 {
447 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448 "Delivering message of type %u and size %u to %s\n",
449 ntohs (message->type),
450 ntohs (message->size),
451 GNUNET_i2s (&car->target));
452 GSC_CLIENTS_deliver_message (&car->target,
453 message,
454 ntohs (message->size),
455 GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
456 GSC_CLIENTS_deliver_message (&car->target,
457 message,
458 sizeof(struct GNUNET_MessageHeader),
459 GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
460 GSC_SESSIONS_transmit (car, message, tc->priority);
461 }
462 return GNUNET_OK;
463}
464
465
466/**
467 * Check #GNUNET_MESSAGE_TYPE_CORE_SEND request.
468 *
469 * @param cls the `struct GSC_Client`
470 * @param sm the `struct SendMessage`
471 * @return #GNUNET_OK if @a sm is well-formed
472 */
473static int
474check_client_send (void *cls, const struct SendMessage *sm)
475{
476 return GNUNET_OK;
477}
478
479
480/**
481 * Handle #GNUNET_MESSAGE_TYPE_CORE_SEND request.
482 *
483 * @param cls the `struct GSC_Client`
484 * @param sm the `struct SendMessage`
485 */
486static void
487handle_client_send (void *cls, const struct SendMessage *sm)
488{
489 struct GSC_Client *c = cls;
490 struct TokenizerContext tc;
491 uint16_t msize;
492 struct GNUNET_TIME_Relative delay;
493 struct GNUNET_MessageStreamTokenizer *mst;
494
495 msize = ntohs (sm->header.size) - sizeof(struct SendMessage);
496 tc.car = GNUNET_CONTAINER_multipeermap_get (c->requests, &sm->peer);
497 if (NULL == tc.car)
498 {
499 /* Must have been that we first approved the request, then got disconnected
500 * (which triggered removal of the 'car') and now the client gives us a message
501 * just *before* the client learns about the disconnect. Theoretically, we
502 * might also now be *again* connected. So this can happen (but should be
503 * rare). If it does happen, the message is discarded. */GNUNET_STATISTICS_update (GSC_stats,
504 gettext_noop (
505 "# messages discarded (session disconnected)"),
506 1,
507 GNUNET_NO);
508 GNUNET_SERVICE_client_continue (c->client);
509 return;
510 }
511 delay = GNUNET_TIME_absolute_get_duration (tc.car->received_time);
512 tc.priority = (enum GNUNET_MQ_PriorityPreferences) ntohl (sm->priority);
513 if (delay.rel_value_us > GNUNET_CONSTANTS_LATENCY_WARN.rel_value_us)
514 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
515 "Client waited %s for transmission of %u bytes to `%s'\n",
516 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES),
517 msize,
518 GNUNET_i2s (&sm->peer));
519 else
520 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
521 "Client waited %s for transmission of %u bytes to `%s'\n",
522 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES),
523 msize,
524 GNUNET_i2s (&sm->peer));
525
526 GNUNET_assert (
527 GNUNET_YES ==
528 GNUNET_CONTAINER_multipeermap_remove (c->requests, &sm->peer, tc.car));
529 mst = GNUNET_MST_create (&tokenized_cb, &tc);
530 GNUNET_MST_from_buffer (mst,
531 (const char *) &sm[1],
532 msize,
533 GNUNET_YES,
534 GNUNET_NO);
535 GNUNET_MST_destroy (mst);
536 GSC_SESSIONS_dequeue_request (tc.car);
537 GNUNET_free (tc.car);
538 GNUNET_SERVICE_client_continue (c->client);
539}
540
541
542/**
543 * Free client request records.
544 *
545 * @param cls NULL
546 * @param key identity of peer for which this is an active request
547 * @param value the `struct GSC_ClientActiveRequest` to free
548 * @return #GNUNET_YES (continue iteration)
549 */
550static int
551destroy_active_client_request (void *cls,
552 const struct GNUNET_PeerIdentity *key,
553 void *value)
554{
555 struct GSC_ClientActiveRequest *car = value;
556
557 GNUNET_assert (
558 GNUNET_YES ==
559 GNUNET_CONTAINER_multipeermap_remove (car->client_handle->requests,
560 &car->target,
561 car));
562 GSC_SESSIONS_dequeue_request (car);
563 GNUNET_free (car);
564 return GNUNET_YES;
565}
566
567
568/**
569 * A client connected, set up.
570 *
571 * @param cls closure
572 * @param client identification of the client
573 * @param mq message queue to talk to @a client
574 * @return our client handle
575 */
576static void *
577client_connect_cb (void *cls,
578 struct GNUNET_SERVICE_Client *client,
579 struct GNUNET_MQ_Handle *mq)
580{
581 struct GSC_Client *c;
582
583 c = GNUNET_new (struct GSC_Client);
584 c->client = client;
585 c->mq = mq;
586 c->connectmap = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_NO);
587 GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c);
588 return c;
589}
590
591
592/**
593 * A client disconnected, clean up.
594 *
595 * @param cls closure
596 * @param client identification of the client
597 * @param app_ctx our `struct GST_Client` for @a client
598 */
599static void
600client_disconnect_cb (void *cls,
601 struct GNUNET_SERVICE_Client *client,
602 void *app_ctx)
603{
604 struct GSC_Client *c = app_ctx;
605
606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607 "Client %p has disconnected from core service.\n",
608 client);
609 GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c);
610 if (NULL != c->requests)
611 {
612 GNUNET_CONTAINER_multipeermap_iterate (c->requests,
613 &destroy_active_client_request,
614 NULL);
615 GNUNET_CONTAINER_multipeermap_destroy (c->requests);
616 }
617 GNUNET_CONTAINER_multipeermap_destroy (c->connectmap);
618 c->connectmap = NULL;
619 if (NULL != c->types)
620 {
621 GSC_TYPEMAP_remove (c->types, c->tcnt);
622 GNUNET_free (c->types);
623 }
624 GNUNET_free (c);
625
626 /* recalculate 'all_client_options' */
627 all_client_options = 0;
628 for (c = client_head; NULL != c; c = c->next)
629 all_client_options |= c->options;
630}
631
632
633/**
634 * Notify a particular client about a change to existing connection to
635 * one of our neighbours (check if the client is interested). Called
636 * from #GSC_SESSIONS_notify_client_about_sessions().
637 *
638 * @param client client to notify
639 * @param neighbour identity of the neighbour that changed status
640 * @param tmap_old previous type map for the neighbour, NULL for connect
641 * @param tmap_new updated type map for the neighbour, NULL for disconnect
642 */
643void
644GSC_CLIENTS_notify_client_about_neighbour (
645 struct GSC_Client *client,
646 const struct GNUNET_PeerIdentity *neighbour,
647 const struct GSC_TypeMap *tmap_old,
648 const struct GSC_TypeMap *tmap_new)
649{
650 struct GNUNET_MQ_Envelope *env;
651 int old_match;
652 int new_match;
653
654 if (GNUNET_YES != client->got_init)
655 return;
656 old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
657 new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Notifying client about neighbour %s (%d/%d)\n",
660 GNUNET_i2s (neighbour),
661 old_match,
662 new_match);
663 if (old_match == new_match)
664 {
665 GNUNET_assert (
666 old_match ==
667 GNUNET_CONTAINER_multipeermap_contains (client->connectmap, neighbour));
668 return; /* no change */
669 }
670 if (GNUNET_NO == old_match)
671 {
672 struct ConnectNotifyMessage *cnm;
673
674 /* send connect */
675 GNUNET_assert (
676 GNUNET_NO ==
677 GNUNET_CONTAINER_multipeermap_contains (client->connectmap, neighbour));
678 GNUNET_assert (GNUNET_YES ==
679 GNUNET_CONTAINER_multipeermap_put (
680 client->connectmap,
681 neighbour,
682 NULL,
683 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
684 env = GNUNET_MQ_msg (cnm, GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
685 cnm->reserved = htonl (0);
686 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
687 "Sending NOTIFY_CONNECT message about peer %s to client.\n",
688 GNUNET_i2s (neighbour));
689 cnm->peer = *neighbour;
690 GNUNET_MQ_send (client->mq, env);
691 }
692 else
693 {
694 struct DisconnectNotifyMessage *dcm;
695
696 /* send disconnect */
697 GNUNET_assert (
698 GNUNET_YES ==
699 GNUNET_CONTAINER_multipeermap_contains (client->connectmap, neighbour));
700 GNUNET_assert (GNUNET_YES ==
701 GNUNET_CONTAINER_multipeermap_remove (client->connectmap,
702 neighbour,
703 NULL));
704 env = GNUNET_MQ_msg (dcm, GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
705 dcm->reserved = htonl (0);
706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
707 "Sending NOTIFY_DISCONNECT message about peer %s to client.\n",
708 GNUNET_i2s (neighbour));
709 dcm->peer = *neighbour;
710 GNUNET_MQ_send (client->mq, env);
711 }
712}
713
714
715/**
716 * Notify all clients about a change to existing session.
717 * Called from SESSIONS whenever there is a change in sessions
718 * or types processed by the respective peer.
719 *
720 * @param neighbour identity of the neighbour that changed status
721 * @param tmap_old previous type map for the neighbour, NULL for connect
722 * @param tmap_new updated type map for the neighbour, NULL for disconnect
723 */
724void
725GSC_CLIENTS_notify_clients_about_neighbour (
726 const struct GNUNET_PeerIdentity *neighbour,
727 const struct GSC_TypeMap *tmap_old,
728 const struct GSC_TypeMap *tmap_new)
729{
730 struct GSC_Client *c;
731
732 for (c = client_head; NULL != c; c = c->next)
733 GSC_CLIENTS_notify_client_about_neighbour (c,
734 neighbour,
735 tmap_old,
736 tmap_new);
737}
738
739
740/**
741 * Deliver P2P message to interested clients. Caller must have checked
742 * that the sending peer actually lists the given message type as one
743 * of its types.
744 *
745 * @param sender peer who sent us the message
746 * @param msg the message
747 * @param msize number of bytes to transmit
748 * @param options options for checking which clients should
749 * receive the message
750 */
751void
752GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
753 const struct GNUNET_MessageHeader *msg,
754 uint16_t msize,
755 uint32_t options)
756{
757 size_t size = msize + sizeof(struct NotifyTrafficMessage);
758
759 if (size >= GNUNET_MAX_MESSAGE_SIZE)
760 {
761 GNUNET_break (0);
762 return;
763 }
764 if (! ((0 != (all_client_options & options)) ||
765 (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND))))
766 return; /* no client cares about this message notification */
767 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768 "Core service passes message from `%s' of type %u to client.\n",
769 GNUNET_i2s (sender),
770 (unsigned int) ntohs (msg->type));
771 GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type));
772
773 for (struct GSC_Client *c = client_head; NULL != c; c = c->next)
774 {
775 struct GNUNET_MQ_Envelope *env;
776 struct NotifyTrafficMessage *ntm;
777 uint16_t mtype;
778 unsigned int qlen;
779 int tm;
780
781 tm = type_match (ntohs (msg->type), c);
782 if (! ((0 != (c->options & options)) ||
783 ((0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
784 (GNUNET_YES == tm))))
785 continue; /* neither options nor type match permit the message */
786 if ((0 != (options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)) &&
787 ((0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
788 (GNUNET_YES == tm)))
789 continue;
790 if ((0 != (options & GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND)) &&
791 (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND)))
792 continue;
793
794 /* Drop messages if:
795 1) We are above the hard limit, or
796 2) We are above the soft limit, and a coin toss limited
797 to the message size (giving larger messages a
798 proportionally higher chance of being queued) falls
799 below the threshold. The threshold is based on where
800 we are between the soft and the hard limit, scaled
801 to match the range of message sizes we usually encounter
802 (i.e. up to 32k); so a 64k message has a 50% chance of
803 being kept if we are just barely below the hard max,
804 and a 99% chance of being kept if we are at the soft max.
805 The reason is to make it more likely to drop control traffic
806 (ACK, queries) which may be cumulative or highly redundant,
807 and cheap to drop than data traffic. */qlen = GNUNET_MQ_get_length (c->mq);
808 if ((qlen >= HARD_MAX_QUEUE) ||
809 ((qlen > SOFT_MAX_QUEUE) &&
810 ((GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
811 ntohs (msg->size))) <
812 (qlen - SOFT_MAX_QUEUE) * 0x8000
813 / (HARD_MAX_QUEUE - SOFT_MAX_QUEUE))))
814 {
815 char buf[1024];
816
817 GNUNET_log (
818 GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
819 "Dropping decrypted message of type %u as client is too busy (queue full)\n",
820 (unsigned int) ntohs (msg->type));
821 GNUNET_snprintf (buf,
822 sizeof(buf),
823 gettext_noop (
824 "# messages of type %u discarded (client busy)"),
825 (unsigned int) ntohs (msg->type));
826 GNUNET_STATISTICS_update (GSC_stats, buf, 1, GNUNET_NO);
827 continue;
828 }
829
830 GNUNET_log (
831 GNUNET_ERROR_TYPE_DEBUG,
832 "Sending %u message with %u bytes to client interested in messages of type %u.\n",
833 options,
834 ntohs (msg->size),
835 (unsigned int) ntohs (msg->type));
836
837 if (0 != (options & (GNUNET_CORE_OPTION_SEND_FULL_INBOUND
838 | GNUNET_CORE_OPTION_SEND_HDR_INBOUND)))
839 mtype = GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND;
840 else
841 mtype = GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND;
842 env = GNUNET_MQ_msg_extra (ntm, msize, mtype);
843 ntm->peer = *sender;
844 GNUNET_memcpy (&ntm[1], msg, msize);
845
846 GNUNET_assert (
847 (0 == (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
848 (GNUNET_YES != tm) ||
849 (GNUNET_YES ==
850 GNUNET_CONTAINER_multipeermap_contains (c->connectmap, sender)));
851 GNUNET_MQ_send (c->mq, env);
852 }
853}
854
855
856/**
857 * Last task run during shutdown. Disconnects us from
858 * the transport.
859 *
860 * @param cls NULL, unused
861 */
862static void
863shutdown_task (void *cls)
864{
865 struct GSC_Client *c;
866
867 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service shutting down.\n");
868 while (NULL != (c = client_head))
869 GNUNET_SERVICE_client_drop (c->client);
870 GSC_SESSIONS_done ();
871 GSC_KX_done ();
872 GSC_TYPEMAP_done ();
873 if (NULL != GSC_stats)
874 {
875 GNUNET_STATISTICS_destroy (GSC_stats, GNUNET_NO);
876 GSC_stats = NULL;
877 }
878 GSC_cfg = NULL;
879}
880
881
882/**
883 * Handle #GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request. For this
884 * request type, the client does not have to have transmitted an INIT
885 * request. All current peers are returned, regardless of which
886 * message types they accept.
887 *
888 * @param cls client sending the iteration request
889 * @param message iteration request message
890 */
891static void
892handle_client_monitor_peers (void *cls,
893 const struct GNUNET_MessageHeader *message)
894{
895 struct GSC_Client *c = cls;
896
897 GNUNET_SERVICE_client_continue (c->client);
898 GSC_KX_handle_client_monitor_peers (c->mq);
899}
900
901
902/**
903 * Initiate core service.
904 *
905 * @param cls closure
906 * @param c configuration to use
907 * @param service the initialized service
908 */
909static void
910run (void *cls,
911 const struct GNUNET_CONFIGURATION_Handle *c,
912 struct GNUNET_SERVICE_Handle *service)
913{
914 struct GNUNET_CRYPTO_EddsaPrivateKey pk;
915 char *keyfile;
916
917 GSC_cfg = c;
918 if (GNUNET_OK !=
919 GNUNET_CONFIGURATION_get_value_filename (GSC_cfg,
920 "PEER",
921 "PRIVATE_KEY",
922 &keyfile))
923 {
924 GNUNET_log (
925 GNUNET_ERROR_TYPE_ERROR,
926 _ ("Core service is lacking HOSTKEY configuration setting. Exiting.\n"));
927 GNUNET_SCHEDULER_shutdown ();
928 return;
929 }
930 GSC_stats = GNUNET_STATISTICS_create ("core", GSC_cfg);
931 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
932 GNUNET_SERVICE_suspend (service);
933 GSC_TYPEMAP_init ();
934 if (GNUNET_SYSERR ==
935 GNUNET_CRYPTO_eddsa_key_from_file (keyfile,
936 GNUNET_YES,
937 &pk))
938 {
939 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
940 "Failed to setup peer's private key\n");
941 GNUNET_SCHEDULER_shutdown ();
942 GNUNET_free (keyfile);
943 return;
944 }
945 GNUNET_free (keyfile);
946 if (GNUNET_OK != GSC_KX_init (&pk))
947 {
948 GNUNET_SCHEDULER_shutdown ();
949 return;
950 }
951 GSC_SESSIONS_init ();
952 GNUNET_SERVICE_resume (service);
953 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
954 _ ("Core service of `%s' ready.\n"),
955 GNUNET_i2s (&GSC_my_identity));
956}
957
958
959/**
960 * Define "main" method using service macro.
961 */
962GNUNET_SERVICE_MAIN (
963 "core",
964 GNUNET_SERVICE_OPTION_NONE,
965 &run,
966 &client_connect_cb,
967 &client_disconnect_cb,
968 NULL,
969 GNUNET_MQ_hd_var_size (client_init,
970 GNUNET_MESSAGE_TYPE_CORE_INIT,
971 struct InitMessage,
972 NULL),
973 GNUNET_MQ_hd_fixed_size (client_monitor_peers,
974 GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS,
975 struct GNUNET_MessageHeader,
976 NULL),
977 GNUNET_MQ_hd_fixed_size (client_send_request,
978 GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
979 struct SendMessageRequest,
980 NULL),
981 GNUNET_MQ_hd_var_size (client_send,
982 GNUNET_MESSAGE_TYPE_CORE_SEND,
983 struct SendMessage,
984 NULL),
985 GNUNET_MQ_handler_end ());
986
987
988/* end of gnunet-service-core.c */
diff --git a/src/service/core/gnunet-service-core.h b/src/service/core/gnunet-service-core.h
new file mode 100644
index 000000000..0f71f221a
--- /dev/null
+++ b/src/service/core/gnunet-service-core.h
@@ -0,0 +1,198 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core.h
23 * @brief Globals for gnunet-service-core
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_CORE_H
27#define GNUNET_SERVICE_CORE_H
28
29#include "gnunet_statistics_service.h"
30#include "gnunet_core_service.h"
31#include "core.h"
32#include "gnunet-service-core_typemap.h"
33
34
35/**
36 * Opaque handle to a client.
37 */
38struct GSC_Client;
39
40
41/**
42 * Record kept for each request for transmission issued by a
43 * client that is still pending. (This struct is used by
44 * both the 'CLIENTS' and 'SESSIONS' subsystems.)
45 */
46struct GSC_ClientActiveRequest
47{
48 /**
49 * Active requests are kept in a doubly-linked list of
50 * the respective target peer.
51 */
52 struct GSC_ClientActiveRequest *next;
53
54 /**
55 * Active requests are kept in a doubly-linked list of
56 * the respective target peer.
57 */
58 struct GSC_ClientActiveRequest *prev;
59
60 /**
61 * Handle to the client.
62 */
63 struct GSC_Client *client_handle;
64
65 /**
66 * Which peer is the message going to be for?
67 */
68 struct GNUNET_PeerIdentity target;
69
70 /**
71 * At what time did we first see this request?
72 */
73 struct GNUNET_TIME_Absolute received_time;
74
75 /**
76 * By what time would the client want to see this message out?
77 */
78 struct GNUNET_TIME_Absolute deadline;
79
80 /**
81 * How important is this request.
82 */
83 enum GNUNET_MQ_PriorityPreferences priority;
84
85 /**
86 * Has this request been solicited yet?
87 */
88 int was_solicited;
89
90 /**
91 * How many bytes does the client intend to send?
92 */
93 uint16_t msize;
94
95 /**
96 * Unique request ID (in big endian).
97 */
98 uint16_t smr_id;
99};
100
101
102/**
103 * Tell a client that we are ready to receive the message.
104 *
105 * @param car request that is now ready; the responsibility
106 * for the handle remains shared between CLIENTS
107 * and SESSIONS after this call.
108 */
109void
110GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car);
111
112
113/**
114 * We will never be ready to transmit the given message in (disconnect
115 * or invalid request). Frees resources associated with @a car. We
116 * don't explicitly tell the client, it'll learn with the disconnect
117 * (or violated the protocol).
118 *
119 * @param car request that now permanently failed; the
120 * responsibility for the handle is now returned
121 * to CLIENTS (SESSIONS is done with it).
122 * @param drop_client #GNUNET_YES if the client violated the protocol
123 * and we should thus drop the connection
124 */
125void
126GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car,
127 int drop_client);
128
129
130/**
131 * Notify a particular client about a change to existing connection to
132 * one of our neighbours (check if the client is interested). Called
133 * from #GSC_SESSIONS_notify_client_about_sessions().
134 *
135 * @param client client to notify
136 * @param neighbour identity of the neighbour that changed status
137 * @param tmap_old previous type map for the neighbour, NULL for connect
138 * @param tmap_new updated type map for the neighbour, NULL for disconnect
139 */
140void
141GSC_CLIENTS_notify_client_about_neighbour (
142 struct GSC_Client *client,
143 const struct GNUNET_PeerIdentity *neighbour,
144 const struct GSC_TypeMap *tmap_old,
145 const struct GSC_TypeMap *tmap_new);
146
147
148/**
149 * Deliver P2P message to interested clients. Caller must have checked
150 * that the sending peer actually lists the given message type as one
151 * of its types.
152 *
153 * @param sender peer who sent us the message
154 * @param msg the message
155 * @param msize number of bytes to transmit
156 * @param options options for checking which clients should
157 * receive the message
158 */
159void
160GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
161 const struct GNUNET_MessageHeader *msg,
162 uint16_t msize,
163 uint32_t options);
164
165
166/**
167 * Notify all clients about a change to existing session.
168 * Called from SESSIONS whenever there is a change in sessions
169 * or types processed by the respective peer.
170 *
171 * @param neighbour identity of the neighbour that changed status
172 * @param tmap_old previous type map for the neighbour, NULL for connect
173 * @param tmap_new updated type map for the neighbour, NULL for disconnect
174 */
175void
176GSC_CLIENTS_notify_clients_about_neighbour (
177 const struct GNUNET_PeerIdentity *neighbour,
178 const struct GSC_TypeMap *tmap_old,
179 const struct GSC_TypeMap *tmap_new);
180
181
182/**
183 * Our configuration.
184 */
185extern const struct GNUNET_CONFIGURATION_Handle *GSC_cfg;
186
187/**
188 * For creating statistics.
189 */
190extern struct GNUNET_STATISTICS_Handle *GSC_stats;
191
192/**
193 * Our identity.
194 */
195extern struct GNUNET_PeerIdentity GSC_my_identity;
196
197
198#endif
diff --git a/src/service/core/gnunet-service-core_kx.c b/src/service/core/gnunet-service-core_kx.c
new file mode 100644
index 000000000..ca7bccbd9
--- /dev/null
+++ b/src/service/core/gnunet-service-core_kx.c
@@ -0,0 +1,1934 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core_kx.c
23 * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other
24 * peers
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet-service-core_kx.h"
29#include "gnunet_transport_core_service.h"
30#include "gnunet-service-core_sessions.h"
31#include "gnunet-service-core.h"
32#include "gnunet_constants.h"
33#include "gnunet_signatures.h"
34#include "gnunet_protocols.h"
35
36/**
37 * Enable expensive (and possibly problematic for privacy!) logging of KX.
38 */
39#define DEBUG_KX 0
40
41/**
42 * How long do we wait for SET_KEY confirmation initially?
43 */
44#define INITIAL_SET_KEY_RETRY_FREQUENCY \
45 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
46
47/**
48 * What is the minimum frequency for a PING message?
49 */
50#define MIN_PING_FREQUENCY \
51 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
52
53/**
54 * How often do we rekey?
55 */
56#define REKEY_FREQUENCY \
57 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
58
59/**
60 * What time difference do we tolerate?
61 */
62#define REKEY_TOLERANCE \
63 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
64
65/**
66 * What is the maximum age of a message for us to consider processing
67 * it? Note that this looks at the timestamp used by the other peer,
68 * so clock skew between machines does come into play here. So this
69 * should be picked high enough so that a little bit of clock skew
70 * does not prevent peers from connecting to us.
71 */
72#define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS
73
74
75GNUNET_NETWORK_STRUCT_BEGIN
76
77/**
78 * Encapsulation for encrypted messages exchanged between
79 * peers. Followed by the actual encrypted data.
80 */
81struct EncryptedMessage
82{
83 /**
84 * Message type is #GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE.
85 */
86 struct GNUNET_MessageHeader header;
87
88 /**
89 * Random value used for IV generation.
90 */
91 uint32_t iv_seed GNUNET_PACKED;
92
93 /**
94 * MAC of the encrypted message (starting at @e sequence_number),
95 * used to verify message integrity. Everything after this value
96 * (excluding this value itself) will be encrypted and
97 * authenticated. #ENCRYPTED_HEADER_SIZE must be set to the offset
98 * of the *next* field.
99 */
100 struct GNUNET_HashCode hmac;
101
102 /**
103 * Sequence number, in network byte order. This field
104 * must be the first encrypted/decrypted field
105 */
106 uint32_t sequence_number GNUNET_PACKED;
107
108 /**
109 * Reserved, always zero.
110 */
111 uint32_t reserved GNUNET_PACKED;
112
113 /**
114 * Timestamp. Used to prevent replay of ancient messages
115 * (recent messages are caught with the sequence number).
116 */
117 struct GNUNET_TIME_AbsoluteNBO timestamp;
118};
119GNUNET_NETWORK_STRUCT_END
120
121
122/**
123 * Number of bytes (at the beginning) of `struct EncryptedMessage`
124 * that are NOT encrypted.
125 */
126#define ENCRYPTED_HEADER_SIZE \
127 (offsetof (struct EncryptedMessage, sequence_number))
128
129
130/**
131 * Information about the status of a key exchange with another peer.
132 */
133struct GSC_KeyExchangeInfo
134{
135 /**
136 * DLL.
137 */
138 struct GSC_KeyExchangeInfo *next;
139
140 /**
141 * DLL.
142 */
143 struct GSC_KeyExchangeInfo *prev;
144
145 /**
146 * Identity of the peer.
147 */
148 const struct GNUNET_PeerIdentity *peer;
149
150 /**
151 * Message queue for sending messages to @a peer.
152 */
153 struct GNUNET_MQ_Handle *mq;
154
155 /**
156 * Our message stream tokenizer (for encrypted payload).
157 */
158 struct GNUNET_MessageStreamTokenizer *mst;
159
160 /**
161 * PING message we transmit to the other peer.
162 */
163 struct PingMessage ping;
164
165 /**
166 * Ephemeral public ECC key of the other peer.
167 */
168 struct GNUNET_CRYPTO_EcdhePublicKey other_ephemeral_key;
169
170 /**
171 * Key we use to encrypt our messages for the other peer
172 * (initialized by us when we do the handshake).
173 */
174 struct GNUNET_CRYPTO_SymmetricSessionKey encrypt_key;
175
176 /**
177 * Key we use to decrypt messages from the other peer
178 * (given to us by the other peer during the handshake).
179 */
180 struct GNUNET_CRYPTO_SymmetricSessionKey decrypt_key;
181
182 /**
183 * At what time did the other peer generate the decryption key?
184 */
185 struct GNUNET_TIME_Absolute foreign_key_expires;
186
187 /**
188 * When should the session time out (if there are no PONGs)?
189 */
190 struct GNUNET_TIME_Absolute timeout;
191
192 /**
193 * What was the last timeout we informed our monitors about?
194 */
195 struct GNUNET_TIME_Absolute last_notify_timeout;
196
197 /**
198 * At what frequency are we currently re-trying SET_KEY messages?
199 */
200 struct GNUNET_TIME_Relative set_key_retry_frequency;
201
202 /**
203 * ID of task used for re-trying SET_KEY and PING message.
204 */
205 struct GNUNET_SCHEDULER_Task *retry_set_key_task;
206
207 /**
208 * ID of task used for sending keep-alive pings.
209 */
210 struct GNUNET_SCHEDULER_Task *keep_alive_task;
211
212 /**
213 * Bit map indicating which of the 32 sequence numbers before the
214 * last were received (good for accepting out-of-order packets and
215 * estimating reliability of the connection)
216 */
217 uint32_t last_packets_bitmap;
218
219 /**
220 * last sequence number received on this connection (highest)
221 */
222 uint32_t last_sequence_number_received;
223
224 /**
225 * last sequence number transmitted
226 */
227 uint32_t last_sequence_number_sent;
228
229 /**
230 * What was our PING challenge number (for this peer)?
231 */
232 uint32_t ping_challenge;
233
234 /**
235 * #GNUNET_YES if this peer currently has excess bandwidth.
236 */
237 int has_excess_bandwidth;
238
239 /**
240 * What is our connection status?
241 */
242 enum GNUNET_CORE_KxState status;
243};
244
245
246/**
247 * Transport service.
248 */
249static struct GNUNET_TRANSPORT_CoreHandle *transport;
250
251/**
252 * Our private key.
253 */
254static struct GNUNET_CRYPTO_EddsaPrivateKey my_private_key;
255
256/**
257 * Our ephemeral private key.
258 */
259static struct GNUNET_CRYPTO_EcdhePrivateKey my_ephemeral_key;
260
261/**
262 * Current message we send for a key exchange.
263 */
264static struct EphemeralKeyMessage current_ekm;
265
266/**
267 * DLL head.
268 */
269static struct GSC_KeyExchangeInfo *kx_head;
270
271/**
272 * DLL tail.
273 */
274static struct GSC_KeyExchangeInfo *kx_tail;
275
276/**
277 * Task scheduled for periodic re-generation (and thus rekeying) of our
278 * ephemeral key.
279 */
280static struct GNUNET_SCHEDULER_Task *rekey_task;
281
282/**
283 * Notification context for broadcasting to monitors.
284 */
285static struct GNUNET_NotificationContext *nc;
286
287
288/**
289 * Calculate seed value we should use for a message.
290 *
291 * @param kx key exchange context
292 */
293static uint32_t
294calculate_seed (struct GSC_KeyExchangeInfo *kx)
295{
296 /* Note: may want to make this non-random and instead
297 derive from key material to avoid having an undetectable
298 side-channel */
299 return htonl (
300 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
301}
302
303
304/**
305 * Inform all monitors about the KX state of the given peer.
306 *
307 * @param kx key exchange state to inform about
308 */
309static void
310monitor_notify_all (struct GSC_KeyExchangeInfo *kx)
311{
312 struct MonitorNotifyMessage msg;
313
314 msg.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY);
315 msg.header.size = htons (sizeof(msg));
316 msg.state = htonl ((uint32_t) kx->status);
317 msg.peer = *kx->peer;
318 msg.timeout = GNUNET_TIME_absolute_hton (kx->timeout);
319 GNUNET_notification_context_broadcast (nc, &msg.header, GNUNET_NO);
320 kx->last_notify_timeout = kx->timeout;
321}
322
323
324/**
325 * Derive an authentication key from "set key" information
326 *
327 * @param akey authentication key to derive
328 * @param skey session key to use
329 * @param seed seed to use
330 */
331static void
332derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey,
333 const struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
334 uint32_t seed)
335{
336 static const char ctx[] = "authentication key";
337
338#if DEBUG_KX
339 struct GNUNET_HashCode sh;
340
341 GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh);
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "Deriving Auth key from SKEY %s and seed %u\n",
344 GNUNET_h2s (&sh),
345 (unsigned int) seed);
346#endif
347 GNUNET_CRYPTO_hmac_derive_key (akey,
348 skey,
349 &seed,
350 sizeof(seed),
351 skey,
352 sizeof(
353 struct GNUNET_CRYPTO_SymmetricSessionKey),
354 ctx,
355 sizeof(ctx),
356 NULL);
357}
358
359
360/**
361 * Derive an IV from packet information
362 *
363 * @param iv initialization vector to initialize
364 * @param skey session key to use
365 * @param seed seed to use
366 * @param identity identity of the other peer to use
367 */
368static void
369derive_iv (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
370 const struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
371 uint32_t seed,
372 const struct GNUNET_PeerIdentity *identity)
373{
374 static const char ctx[] = "initialization vector";
375
376#if DEBUG_KX
377 struct GNUNET_HashCode sh;
378
379 GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh);
380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381 "Deriving IV from SKEY %s and seed %u for peer %s\n",
382 GNUNET_h2s (&sh),
383 (unsigned int) seed,
384 GNUNET_i2s (identity));
385#endif
386 GNUNET_CRYPTO_symmetric_derive_iv (iv,
387 skey,
388 &seed,
389 sizeof(seed),
390 identity,
391 sizeof(struct GNUNET_PeerIdentity),
392 ctx,
393 sizeof(ctx),
394 NULL);
395}
396
397
398/**
399 * Derive an IV from pong packet information
400 *
401 * @param iv initialization vector to initialize
402 * @param skey session key to use
403 * @param seed seed to use
404 * @param challenge nonce to use
405 * @param identity identity of the other peer to use
406 */
407static void
408derive_pong_iv (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
409 const struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
410 uint32_t seed,
411 uint32_t challenge,
412 const struct GNUNET_PeerIdentity *identity)
413{
414 static const char ctx[] = "pong initialization vector";
415
416#if DEBUG_KX
417 struct GNUNET_HashCode sh;
418
419 GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh);
420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421 "Deriving PONG IV from SKEY %s and seed %u/%u for %s\n",
422 GNUNET_h2s (&sh),
423 (unsigned int) seed,
424 (unsigned int) challenge,
425 GNUNET_i2s (identity));
426#endif
427 GNUNET_CRYPTO_symmetric_derive_iv (iv,
428 skey,
429 &seed,
430 sizeof(seed),
431 identity,
432 sizeof(struct GNUNET_PeerIdentity),
433 &challenge,
434 sizeof(challenge),
435 ctx,
436 sizeof(ctx),
437 NULL);
438}
439
440
441/**
442 * Derive an AES key from key material
443 *
444 * @param sender peer identity of the sender
445 * @param receiver peer identity of the sender
446 * @param key_material high entropy key material to use
447 * @param skey set to derived session key
448 */
449static void
450derive_aes_key (const struct GNUNET_PeerIdentity *sender,
451 const struct GNUNET_PeerIdentity *receiver,
452 const struct GNUNET_HashCode *key_material,
453 struct GNUNET_CRYPTO_SymmetricSessionKey *skey)
454{
455 static const char ctx[] = "aes key generation vector";
456
457#if DEBUG_KX
458 struct GNUNET_HashCode sh;
459
460 GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh);
461 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
462 "Deriving AES Keys for %s to %s from %s\n",
463 GNUNET_i2s (sender),
464 GNUNET_i2s2 (receiver),
465 GNUNET_h2s (key_material));
466#endif
467 GNUNET_CRYPTO_kdf (skey,
468 sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey),
469 ctx,
470 sizeof(ctx),
471 key_material,
472 sizeof(struct GNUNET_HashCode),
473 sender,
474 sizeof(struct GNUNET_PeerIdentity),
475 receiver,
476 sizeof(struct GNUNET_PeerIdentity),
477 NULL);
478}
479
480
481/**
482 * Encrypt size bytes from @a in and write the result to @a out. Use the
483 * @a kx key for outbound traffic of the given neighbour.
484 *
485 * @param kx key information context
486 * @param iv initialization vector to use
487 * @param in ciphertext
488 * @param out plaintext
489 * @param size size of @a in / @a out
490 *
491 * @return #GNUNET_OK on success
492 */
493static int
494do_encrypt (struct GSC_KeyExchangeInfo *kx,
495 const struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
496 const void *in,
497 void *out,
498 size_t size)
499{
500 if (size != (uint16_t) size)
501 {
502 GNUNET_break (0);
503 return GNUNET_NO;
504 }
505 GNUNET_assert (size == GNUNET_CRYPTO_symmetric_encrypt (in,
506 (uint16_t) size,
507 &kx->encrypt_key,
508 iv,
509 out));
510 GNUNET_STATISTICS_update (GSC_stats,
511 gettext_noop ("# bytes encrypted"),
512 size,
513 GNUNET_NO);
514 /* the following is too sensitive to write to log files by accident,
515 so we require manual intervention to get this one... */
516#if DEBUG_KX
517 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518 "Encrypted %u bytes for `%s' using key %s, IV %u\n",
519 (unsigned int) size,
520 GNUNET_i2s (kx->peer),
521 kx->encrypt_key.aes_key,
522 GNUNET_CRYPTO_crc32_n (iv, sizeof(iv)));
523#endif
524 return GNUNET_OK;
525}
526
527
528/**
529 * Decrypt size bytes from @a in and write the result to @a out. Use
530 * the @a kx key for inbound traffic of the given neighbour. This
531 * function does NOT do any integrity-checks on the result.
532 *
533 * @param kx key information context
534 * @param iv initialization vector to use
535 * @param in ciphertext
536 * @param out plaintext
537 * @param size size of @a in / @a out
538 * @return #GNUNET_OK on success
539 */
540static int
541do_decrypt (struct GSC_KeyExchangeInfo *kx,
542 const struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
543 const void *in,
544 void *out,
545 size_t size)
546{
547 if (size != (uint16_t) size)
548 {
549 GNUNET_break (0);
550 return GNUNET_NO;
551 }
552 if ((kx->status != GNUNET_CORE_KX_STATE_KEY_RECEIVED) &&
553 (kx->status != GNUNET_CORE_KX_STATE_UP) &&
554 (kx->status != GNUNET_CORE_KX_STATE_REKEY_SENT))
555 {
556 GNUNET_break_op (0);
557 return GNUNET_SYSERR;
558 }
559 if (size != GNUNET_CRYPTO_symmetric_decrypt (in,
560 (uint16_t) size,
561 &kx->decrypt_key,
562 iv,
563 out))
564 {
565 GNUNET_break (0);
566 return GNUNET_SYSERR;
567 }
568 GNUNET_STATISTICS_update (GSC_stats,
569 gettext_noop ("# bytes decrypted"),
570 size,
571 GNUNET_NO);
572 /* the following is too sensitive to write to log files by accident,
573 so we require manual intervention to get this one... */
574#if DEBUG_KX
575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576 "Decrypted %u bytes from `%s' using key %s, IV %u\n",
577 (unsigned int) size,
578 GNUNET_i2s (kx->peer),
579 kx->decrypt_key.aes_key,
580 GNUNET_CRYPTO_crc32_n (iv, sizeof(*iv)));
581#endif
582 return GNUNET_OK;
583}
584
585
586/**
587 * Send our key (and encrypted PING) to the other peer.
588 *
589 * @param kx key exchange context
590 */
591static void
592send_key (struct GSC_KeyExchangeInfo *kx);
593
594
595/**
596 * Task that will retry #send_key() if our previous attempt failed.
597 *
598 * @param cls our `struct GSC_KeyExchangeInfo`
599 */
600static void
601set_key_retry_task (void *cls)
602{
603 struct GSC_KeyExchangeInfo *kx = cls;
604
605 kx->retry_set_key_task = NULL;
606 kx->set_key_retry_frequency =
607 GNUNET_TIME_STD_BACKOFF (kx->set_key_retry_frequency);
608 GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status);
609 send_key (kx);
610}
611
612
613/**
614 * Create a fresh PING message for transmission to the other peer.
615 *
616 * @param kx key exchange context to create PING for
617 */
618static void
619setup_fresh_ping (struct GSC_KeyExchangeInfo *kx)
620{
621 struct PingMessage pp;
622 struct PingMessage *pm;
623 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
624
625 pm = &kx->ping;
626 kx->ping_challenge =
627 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX);
628 pm->header.size = htons (sizeof(struct PingMessage));
629 pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
630 pm->iv_seed = calculate_seed (kx);
631 derive_iv (&iv, &kx->encrypt_key, pm->iv_seed, kx->peer);
632 pp.challenge = kx->ping_challenge;
633 pp.target = *kx->peer;
634 do_encrypt (kx,
635 &iv,
636 &pp.target,
637 &pm->target,
638 sizeof(struct PingMessage)
639 - ((void *) &pm->target - (void *) pm));
640}
641
642
643/**
644 * Deliver P2P message to interested clients. Invokes send twice,
645 * once for clients that want the full message, and once for clients
646 * that only want the header
647 *
648 * @param cls the `struct GSC_KeyExchangeInfo`
649 * @param m the message
650 * @return #GNUNET_OK on success,
651 * #GNUNET_NO to stop further processing (no error)
652 * #GNUNET_SYSERR to stop further processing with error
653 */
654static int
655deliver_message (void *cls, const struct GNUNET_MessageHeader *m)
656{
657 struct GSC_KeyExchangeInfo *kx = cls;
658
659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660 "Decrypted message of type %d from %s\n",
661 ntohs (m->type),
662 GNUNET_i2s (kx->peer));
663 if (GNUNET_CORE_KX_STATE_UP != kx->status)
664 {
665 GNUNET_STATISTICS_update (GSC_stats,
666 gettext_noop ("# PAYLOAD dropped (out of order)"),
667 1,
668 GNUNET_NO);
669 return GNUNET_OK;
670 }
671 switch (ntohs (m->type))
672 {
673 case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP:
674 case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP:
675 GSC_SESSIONS_set_typemap (kx->peer, m);
676 return GNUNET_OK;
677
678 case GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP:
679 GSC_SESSIONS_confirm_typemap (kx->peer, m);
680 return GNUNET_OK;
681
682 default:
683 GSC_CLIENTS_deliver_message (kx->peer,
684 m,
685 ntohs (m->size),
686 GNUNET_CORE_OPTION_SEND_FULL_INBOUND);
687 GSC_CLIENTS_deliver_message (kx->peer,
688 m,
689 sizeof(struct GNUNET_MessageHeader),
690 GNUNET_CORE_OPTION_SEND_HDR_INBOUND);
691 }
692 return GNUNET_OK;
693}
694
695
696/**
697 * Function called by transport to notify us that
698 * a peer connected to us (on the network level).
699 * Starts the key exchange with the given peer.
700 *
701 * @param cls closure (NULL)
702 * @param pid identity of the peer to do a key exchange with
703 * @return key exchange information context
704 */
705static void *
706handle_transport_notify_connect (void *cls,
707 const struct GNUNET_PeerIdentity *pid,
708 struct GNUNET_MQ_Handle *mq)
709{
710 struct GSC_KeyExchangeInfo *kx;
711 struct GNUNET_HashCode h1;
712 struct GNUNET_HashCode h2;
713
714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
715 "Initiating key exchange with `%s'\n",
716 GNUNET_i2s (pid));
717 GNUNET_STATISTICS_update (GSC_stats,
718 gettext_noop ("# key exchanges initiated"),
719 1,
720 GNUNET_NO);
721 kx = GNUNET_new (struct GSC_KeyExchangeInfo);
722 kx->mst = GNUNET_MST_create (&deliver_message, kx);
723 kx->mq = mq;
724 kx->peer = pid;
725 kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY;
726 GNUNET_CONTAINER_DLL_insert (kx_head, kx_tail, kx);
727 kx->status = GNUNET_CORE_KX_STATE_KEY_SENT;
728 monitor_notify_all (kx);
729 GNUNET_CRYPTO_hash (pid, sizeof(struct GNUNET_PeerIdentity), &h1);
730 GNUNET_CRYPTO_hash (&GSC_my_identity,
731 sizeof(struct GNUNET_PeerIdentity),
732 &h2);
733 if (0 < GNUNET_CRYPTO_hash_cmp (&h1, &h2))
734 {
735 /* peer with "lower" identity starts KX, otherwise we typically end up
736 with both peers starting the exchange and transmit the 'set key'
737 message twice */
738 send_key (kx);
739 }
740 else
741 {
742 /* peer with "higher" identity starts a delayed KX, if the "lower" peer
743 * does not start a KX since it sees no reasons to do so */
744 kx->retry_set_key_task =
745 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
746 &set_key_retry_task,
747 kx);
748 }
749 return kx;
750}
751
752
753/**
754 * Function called by transport telling us that a peer
755 * disconnected.
756 * Stop key exchange with the given peer. Clean up key material.
757 *
758 * @param cls closure
759 * @param peer the peer that disconnected
760 * @param handler_cls the `struct GSC_KeyExchangeInfo` of the peer
761 */
762static void
763handle_transport_notify_disconnect (void *cls,
764 const struct GNUNET_PeerIdentity *peer,
765 void *handler_cls)
766{
767 struct GSC_KeyExchangeInfo *kx = handler_cls;
768
769 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
770 "Peer `%s' disconnected from us.\n",
771 GNUNET_i2s (peer));
772 GSC_SESSIONS_end (kx->peer);
773 GNUNET_STATISTICS_update (GSC_stats,
774 gettext_noop ("# key exchanges stopped"),
775 1,
776 GNUNET_NO);
777 if (NULL != kx->retry_set_key_task)
778 {
779 GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
780 kx->retry_set_key_task = NULL;
781 }
782 if (NULL != kx->keep_alive_task)
783 {
784 GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
785 kx->keep_alive_task = NULL;
786 }
787 kx->status = GNUNET_CORE_KX_PEER_DISCONNECT;
788 monitor_notify_all (kx);
789 GNUNET_CONTAINER_DLL_remove (kx_head, kx_tail, kx);
790 GNUNET_MST_destroy (kx->mst);
791 GNUNET_free (kx);
792}
793
794
795/**
796 * Send our PING to the other peer.
797 *
798 * @param kx key exchange context
799 */
800static void
801send_ping (struct GSC_KeyExchangeInfo *kx)
802{
803 struct GNUNET_MQ_Envelope *env;
804
805 GNUNET_STATISTICS_update (GSC_stats,
806 gettext_noop ("# PING messages transmitted"),
807 1,
808 GNUNET_NO);
809 env = GNUNET_MQ_msg_copy (&kx->ping.header);
810 GNUNET_MQ_send (kx->mq, env);
811}
812
813
814/**
815 * Derive fresh session keys from the current ephemeral keys.
816 *
817 * @param kx session to derive keys for
818 */
819static void
820derive_session_keys (struct GSC_KeyExchangeInfo *kx)
821{
822 struct GNUNET_HashCode key_material;
823
824 if (GNUNET_OK !=
825 GNUNET_CRYPTO_ecc_ecdh (&my_ephemeral_key,
826 &kx->other_ephemeral_key,
827 &key_material))
828 {
829 GNUNET_break (0);
830 return;
831 }
832 derive_aes_key (&GSC_my_identity, kx->peer, &key_material, &kx->encrypt_key);
833 derive_aes_key (kx->peer, &GSC_my_identity, &key_material, &kx->decrypt_key);
834 memset (&key_material, 0, sizeof(key_material));
835 /* fresh key, reset sequence numbers */
836 kx->last_sequence_number_received = 0;
837 kx->last_packets_bitmap = 0;
838 setup_fresh_ping (kx);
839}
840
841
842/**
843 * We received a #GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY message.
844 * Validate and update our key material and status.
845 *
846 * @param cls key exchange status for the corresponding peer
847 * @param m the set key message we received
848 */
849static void
850handle_ephemeral_key (void *cls, const struct EphemeralKeyMessage *m)
851{
852 struct GSC_KeyExchangeInfo *kx = cls;
853 struct GNUNET_TIME_Absolute start_t;
854 struct GNUNET_TIME_Absolute end_t;
855 struct GNUNET_TIME_Absolute now;
856 enum GNUNET_CORE_KxState sender_status;
857
858 end_t = GNUNET_TIME_absolute_ntoh (m->expiration_time);
859 if (((GNUNET_CORE_KX_STATE_KEY_RECEIVED == kx->status) ||
860 (GNUNET_CORE_KX_STATE_UP == kx->status) ||
861 (GNUNET_CORE_KX_STATE_REKEY_SENT == kx->status)) &&
862 (end_t.abs_value_us < kx->foreign_key_expires.abs_value_us))
863 {
864 GNUNET_STATISTICS_update (GSC_stats,
865 gettext_noop ("# old ephemeral keys ignored"),
866 1,
867 GNUNET_NO);
868 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
869 "Received expired EPHEMERAL_KEY from %s\n",
870 GNUNET_i2s (&m->origin_identity));
871 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
872 return;
873 }
874 if (0 == memcmp (&m->ephemeral_key,
875 &kx->other_ephemeral_key,
876 sizeof(m->ephemeral_key)))
877 {
878 GNUNET_STATISTICS_update (GSC_stats,
879 gettext_noop (
880 "# duplicate ephemeral keys ignored"),
881 1,
882 GNUNET_NO);
883 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
884 "Ignoring duplicate EPHEMERAL_KEY from %s\n",
885 GNUNET_i2s (&m->origin_identity));
886 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
887 return;
888 }
889 if (0 != memcmp (&m->origin_identity,
890 kx->peer,
891 sizeof(struct GNUNET_PeerIdentity)))
892 {
893 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
894 "Received EPHEMERAL_KEY from %s, but expected %s\n",
895 GNUNET_i2s (&m->origin_identity),
896 GNUNET_i2s_full (kx->peer));
897 GNUNET_break_op (0);
898 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
899 return;
900 }
901 if ((ntohl (m->purpose.size) !=
902 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
903 + sizeof(struct GNUNET_TIME_AbsoluteNBO)
904 + sizeof(struct GNUNET_TIME_AbsoluteNBO)
905 + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
906 + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)) ||
907 (GNUNET_OK !=
908 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY,
909 &m->purpose,
910 &m->signature,
911 &m->origin_identity.public_key)))
912 {
913 /* invalid signature */
914 GNUNET_break_op (0);
915 GNUNET_STATISTICS_update (GSC_stats,
916 gettext_noop (
917 "# EPHEMERAL_KEYs rejected (bad signature)"),
918 1,
919 GNUNET_NO);
920 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
921 "Received EPHEMERAL_KEY from %s with bad signature\n",
922 GNUNET_i2s (&m->origin_identity));
923 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
924 return;
925 }
926 now = GNUNET_TIME_absolute_get ();
927 start_t = GNUNET_TIME_absolute_ntoh (m->creation_time);
928 if ((end_t.abs_value_us <
929 GNUNET_TIME_absolute_subtract (now, REKEY_TOLERANCE).abs_value_us) ||
930 (start_t.abs_value_us >
931 GNUNET_TIME_absolute_add (now, REKEY_TOLERANCE).abs_value_us))
932 {
933 GNUNET_log (
934 GNUNET_ERROR_TYPE_WARNING,
935 _ (
936 "EPHEMERAL_KEY from peer `%s' rejected as its validity range does not match our system time (%llu not in [%llu,%llu]).\n"),
937 GNUNET_i2s (kx->peer),
938 (unsigned long long) now.abs_value_us,
939 (unsigned long long) start_t.abs_value_us,
940 (unsigned long long) end_t.abs_value_us);
941 GNUNET_STATISTICS_update (GSC_stats,
942 gettext_noop (
943 "# EPHEMERAL_KEY messages rejected due to time"),
944 1,
945 GNUNET_NO);
946 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
947 return;
948 }
949#if DEBUG_KX
950 {
951 struct GNUNET_HashCode eh;
952
953 GNUNET_CRYPTO_hash (&m->ephemeral_key, sizeof(m->ephemeral_key), &eh);
954 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
955 "Received valid EPHEMERAL_KEY `%s' from `%s' in state %d.\n",
956 GNUNET_h2s (&eh),
957 GNUNET_i2s (kx->peer),
958 kx->status);
959 }
960#endif
961 GNUNET_STATISTICS_update (GSC_stats,
962 gettext_noop ("# valid ephemeral keys received"),
963 1,
964 GNUNET_NO);
965 kx->other_ephemeral_key = m->ephemeral_key;
966 kx->foreign_key_expires = end_t;
967 derive_session_keys (kx);
968
969 /* check if we still need to send the sender our key */
970 sender_status = (enum GNUNET_CORE_KxState) ntohl (m->sender_status);
971 switch (sender_status)
972 {
973 case GNUNET_CORE_KX_STATE_DOWN:
974 GNUNET_break_op (0);
975 break;
976
977 case GNUNET_CORE_KX_STATE_KEY_SENT:
978 /* fine, need to send our key after updating our status, see below */
979 GSC_SESSIONS_reinit (kx->peer);
980 break;
981
982 case GNUNET_CORE_KX_STATE_KEY_RECEIVED:
983 /* other peer already got our key, but typemap did go down */
984 GSC_SESSIONS_reinit (kx->peer);
985 break;
986
987 case GNUNET_CORE_KX_STATE_UP:
988 /* other peer already got our key, typemap NOT down */
989 break;
990
991 case GNUNET_CORE_KX_STATE_REKEY_SENT:
992 /* other peer already got our key, typemap NOT down */
993 break;
994
995 default:
996 GNUNET_break (0);
997 break;
998 }
999 /* check if we need to confirm everything is fine via PING + PONG */
1000 switch (kx->status)
1001 {
1002 case GNUNET_CORE_KX_STATE_DOWN:
1003 GNUNET_assert (NULL == kx->keep_alive_task);
1004 kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED;
1005 monitor_notify_all (kx);
1006 if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
1007 send_key (kx);
1008 else
1009 send_ping (kx);
1010 break;
1011
1012 case GNUNET_CORE_KX_STATE_KEY_SENT:
1013 GNUNET_assert (NULL == kx->keep_alive_task);
1014 kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED;
1015 monitor_notify_all (kx);
1016 if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
1017 send_key (kx);
1018 else
1019 send_ping (kx);
1020 break;
1021
1022 case GNUNET_CORE_KX_STATE_KEY_RECEIVED:
1023 GNUNET_assert (NULL == kx->keep_alive_task);
1024 if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
1025 send_key (kx);
1026 else
1027 send_ping (kx);
1028 break;
1029
1030 case GNUNET_CORE_KX_STATE_UP:
1031 kx->status = GNUNET_CORE_KX_STATE_REKEY_SENT;
1032 monitor_notify_all (kx);
1033 if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
1034 send_key (kx);
1035 else
1036 send_ping (kx);
1037 break;
1038
1039 case GNUNET_CORE_KX_STATE_REKEY_SENT:
1040 if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
1041 send_key (kx);
1042 else
1043 send_ping (kx);
1044 break;
1045
1046 default:
1047 GNUNET_break (0);
1048 break;
1049 }
1050 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1051}
1052
1053
1054/**
1055 * We received a PING message. Validate and transmit
1056 * a PONG message.
1057 *
1058 * @param cls key exchange status for the corresponding peer
1059 * @param m the encrypted PING message itself
1060 */
1061static void
1062handle_ping (void *cls, const struct PingMessage *m)
1063{
1064 struct GSC_KeyExchangeInfo *kx = cls;
1065 struct PingMessage t;
1066 struct PongMessage tx;
1067 struct PongMessage *tp;
1068 struct GNUNET_MQ_Envelope *env;
1069 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
1070
1071 GNUNET_STATISTICS_update (GSC_stats,
1072 gettext_noop ("# PING messages received"),
1073 1,
1074 GNUNET_NO);
1075 if ((kx->status != GNUNET_CORE_KX_STATE_KEY_RECEIVED) &&
1076 (kx->status != GNUNET_CORE_KX_STATE_UP) &&
1077 (kx->status != GNUNET_CORE_KX_STATE_REKEY_SENT))
1078 {
1079 /* ignore */
1080 GNUNET_STATISTICS_update (GSC_stats,
1081 gettext_noop (
1082 "# PING messages dropped (out of order)"),
1083 1,
1084 GNUNET_NO);
1085 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1086 return;
1087 }
1088 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1089 "Core service receives PING request from `%s'.\n",
1090 GNUNET_i2s (kx->peer));
1091 derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity);
1092 if (GNUNET_OK != do_decrypt (kx,
1093 &iv,
1094 &m->target,
1095 &t.target,
1096 sizeof(struct PingMessage)
1097 - ((void *) &m->target - (void *) m)))
1098 {
1099 GNUNET_break_op (0);
1100 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1101 return;
1102 }
1103 if (0 !=
1104 memcmp (&t.target, &GSC_my_identity, sizeof(struct GNUNET_PeerIdentity)))
1105 {
1106 if (GNUNET_CORE_KX_STATE_REKEY_SENT != kx->status)
1107 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1108 "Decryption of PING from peer `%s' failed, PING for `%s'?\n",
1109 GNUNET_i2s (kx->peer),
1110 GNUNET_i2s2 (&t.target));
1111 else
1112 GNUNET_log (
1113 GNUNET_ERROR_TYPE_DEBUG,
1114 "Decryption of PING from peer `%s' failed after rekey (harmless)\n",
1115 GNUNET_i2s (kx->peer));
1116 GNUNET_break_op (0);
1117 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1118 return;
1119 }
1120 /* construct PONG */
1121 tx.reserved = 0;
1122 tx.challenge = t.challenge;
1123 tx.target = t.target;
1124 env = GNUNET_MQ_msg (tp, GNUNET_MESSAGE_TYPE_CORE_PONG);
1125 tp->iv_seed = calculate_seed (kx);
1126 derive_pong_iv (&iv, &kx->encrypt_key, tp->iv_seed, t.challenge, kx->peer);
1127 do_encrypt (kx,
1128 &iv,
1129 &tx.challenge,
1130 &tp->challenge,
1131 sizeof(struct PongMessage)
1132 - ((void *) &tp->challenge - (void *) tp));
1133 GNUNET_STATISTICS_update (GSC_stats,
1134 gettext_noop ("# PONG messages created"),
1135 1,
1136 GNUNET_NO);
1137 GNUNET_MQ_send (kx->mq, env);
1138}
1139
1140
1141/**
1142 * Task triggered when a neighbour entry is about to time out
1143 * (and we should prevent this by sending a PING).
1144 *
1145 * @param cls the `struct GSC_KeyExchangeInfo`
1146 */
1147static void
1148send_keep_alive (void *cls)
1149{
1150 struct GSC_KeyExchangeInfo *kx = cls;
1151 struct GNUNET_TIME_Relative retry;
1152 struct GNUNET_TIME_Relative left;
1153
1154 kx->keep_alive_task = NULL;
1155 left = GNUNET_TIME_absolute_get_remaining (kx->timeout);
1156 if (0 == left.rel_value_us)
1157 {
1158 GNUNET_STATISTICS_update (GSC_stats,
1159 gettext_noop ("# sessions terminated by timeout"),
1160 1,
1161 GNUNET_NO);
1162 GSC_SESSIONS_end (kx->peer);
1163 kx->status = GNUNET_CORE_KX_STATE_KEY_SENT;
1164 monitor_notify_all (kx);
1165 send_key (kx);
1166 return;
1167 }
1168 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1169 "Sending KEEPALIVE to `%s'\n",
1170 GNUNET_i2s (kx->peer));
1171 GNUNET_STATISTICS_update (GSC_stats,
1172 gettext_noop ("# keepalive messages sent"),
1173 1,
1174 GNUNET_NO);
1175 setup_fresh_ping (kx);
1176 send_ping (kx);
1177 retry = GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2),
1178 MIN_PING_FREQUENCY);
1179 kx->keep_alive_task =
1180 GNUNET_SCHEDULER_add_delayed (retry, &send_keep_alive, kx);
1181}
1182
1183
1184/**
1185 * We've seen a valid message from the other peer.
1186 * Update the time when the session would time out
1187 * and delay sending our keep alive message further.
1188 *
1189 * @param kx key exchange where we saw activity
1190 */
1191static void
1192update_timeout (struct GSC_KeyExchangeInfo *kx)
1193{
1194 struct GNUNET_TIME_Relative delta;
1195
1196 kx->timeout =
1197 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1198 delta =
1199 GNUNET_TIME_absolute_get_difference (kx->last_notify_timeout, kx->timeout);
1200 if (delta.rel_value_us > 5LL * 1000LL * 1000LL)
1201 {
1202 /* we only notify monitors about timeout changes if those
1203 are bigger than the threshold (5s) */
1204 monitor_notify_all (kx);
1205 }
1206 if (NULL != kx->keep_alive_task)
1207 GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
1208 kx->keep_alive_task = GNUNET_SCHEDULER_add_delayed (
1209 GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
1210 &send_keep_alive,
1211 kx);
1212}
1213
1214
1215/**
1216 * We received a PONG message. Validate and update our status.
1217 *
1218 * @param kx key exchange context for the the PONG
1219 * @param m the encrypted PONG message itself
1220 */
1221static void
1222handle_pong (void *cls, const struct PongMessage *m)
1223{
1224 struct GSC_KeyExchangeInfo *kx = cls;
1225 struct PongMessage t;
1226 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
1227
1228 GNUNET_STATISTICS_update (GSC_stats,
1229 gettext_noop ("# PONG messages received"),
1230 1,
1231 GNUNET_NO);
1232 switch (kx->status)
1233 {
1234 case GNUNET_CORE_KX_STATE_DOWN:
1235 GNUNET_STATISTICS_update (GSC_stats,
1236 gettext_noop (
1237 "# PONG messages dropped (connection down)"),
1238 1,
1239 GNUNET_NO);
1240 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1241 return;
1242
1243 case GNUNET_CORE_KX_STATE_KEY_SENT:
1244 GNUNET_STATISTICS_update (GSC_stats,
1245 gettext_noop (
1246 "# PONG messages dropped (out of order)"),
1247 1,
1248 GNUNET_NO);
1249 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1250 return;
1251
1252 case GNUNET_CORE_KX_STATE_KEY_RECEIVED:
1253 break;
1254
1255 case GNUNET_CORE_KX_STATE_UP:
1256 break;
1257
1258 case GNUNET_CORE_KX_STATE_REKEY_SENT:
1259 break;
1260
1261 default:
1262 GNUNET_break (0);
1263 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1264 return;
1265 }
1266 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1267 "Core service receives PONG response from `%s'.\n",
1268 GNUNET_i2s (kx->peer));
1269 /* mark as garbage, just to be sure */
1270 memset (&t, 255, sizeof(t));
1271 derive_pong_iv (&iv,
1272 &kx->decrypt_key,
1273 m->iv_seed,
1274 kx->ping_challenge,
1275 &GSC_my_identity);
1276 if (GNUNET_OK != do_decrypt (kx,
1277 &iv,
1278 &m->challenge,
1279 &t.challenge,
1280 sizeof(struct PongMessage)
1281 - ((void *) &m->challenge - (void *) m)))
1282 {
1283 GNUNET_break_op (0);
1284 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1285 return;
1286 }
1287 GNUNET_STATISTICS_update (GSC_stats,
1288 gettext_noop ("# PONG messages decrypted"),
1289 1,
1290 GNUNET_NO);
1291 if ((0 !=
1292 memcmp (&t.target, kx->peer, sizeof(struct GNUNET_PeerIdentity))) ||
1293 (kx->ping_challenge != t.challenge))
1294 {
1295 /* PONG malformed */
1296 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1297 "Received malformed PONG wanted sender `%s' with challenge %u\n",
1298 GNUNET_i2s (kx->peer),
1299 (unsigned int) kx->ping_challenge);
1300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1301 "Received malformed PONG received from `%s' with challenge %u\n",
1302 GNUNET_i2s (&t.target),
1303 (unsigned int) t.challenge);
1304 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1305 return;
1306 }
1307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1308 "Received valid PONG from `%s'\n",
1309 GNUNET_i2s (kx->peer));
1310 /* no need to resend key any longer */
1311 if (NULL != kx->retry_set_key_task)
1312 {
1313 GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
1314 kx->retry_set_key_task = NULL;
1315 }
1316 switch (kx->status)
1317 {
1318 case GNUNET_CORE_KX_STATE_DOWN:
1319 GNUNET_assert (0); /* should be impossible */
1320 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1321 return;
1322
1323 case GNUNET_CORE_KX_STATE_KEY_SENT:
1324 GNUNET_assert (0); /* should be impossible */
1325 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1326 return;
1327
1328 case GNUNET_CORE_KX_STATE_KEY_RECEIVED:
1329 GNUNET_STATISTICS_update (GSC_stats,
1330 gettext_noop (
1331 "# session keys confirmed via PONG"),
1332 1,
1333 GNUNET_NO);
1334 kx->status = GNUNET_CORE_KX_STATE_UP;
1335 monitor_notify_all (kx);
1336 GSC_SESSIONS_create (kx->peer, kx);
1337 GNUNET_assert (NULL == kx->keep_alive_task);
1338 update_timeout (kx);
1339 break;
1340
1341 case GNUNET_CORE_KX_STATE_UP:
1342 GNUNET_STATISTICS_update (GSC_stats,
1343 gettext_noop ("# timeouts prevented via PONG"),
1344 1,
1345 GNUNET_NO);
1346 update_timeout (kx);
1347 break;
1348
1349 case GNUNET_CORE_KX_STATE_REKEY_SENT:
1350 GNUNET_STATISTICS_update (GSC_stats,
1351 gettext_noop (
1352 "# rekey operations confirmed via PONG"),
1353 1,
1354 GNUNET_NO);
1355 kx->status = GNUNET_CORE_KX_STATE_UP;
1356 monitor_notify_all (kx);
1357 update_timeout (kx);
1358 break;
1359
1360 default:
1361 GNUNET_break (0);
1362 break;
1363 }
1364}
1365
1366
1367/**
1368 * Send our key to the other peer.
1369 *
1370 * @param kx key exchange context
1371 */
1372static void
1373send_key (struct GSC_KeyExchangeInfo *kx)
1374{
1375 struct GNUNET_MQ_Envelope *env;
1376
1377 GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status);
1378 if (NULL != kx->retry_set_key_task)
1379 {
1380 GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
1381 kx->retry_set_key_task = NULL;
1382 }
1383 /* always update sender status in SET KEY message */
1384#if DEBUG_KX
1385 {
1386 struct GNUNET_HashCode hc;
1387
1388 GNUNET_CRYPTO_hash (&current_ekm.ephemeral_key,
1389 sizeof(current_ekm.ephemeral_key),
1390 &hc);
1391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1392 "Sending EPHEMERAL_KEY %s to `%s' (my status: %d)\n",
1393 GNUNET_h2s (&hc),
1394 GNUNET_i2s (kx->peer),
1395 kx->status);
1396 }
1397#endif
1398 current_ekm.sender_status = htonl ((int32_t) (kx->status));
1399 env = GNUNET_MQ_msg_copy (&current_ekm.header);
1400 GNUNET_MQ_send (kx->mq, env);
1401 if (GNUNET_CORE_KX_STATE_KEY_SENT != kx->status)
1402 send_ping (kx);
1403 kx->retry_set_key_task =
1404 GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency,
1405 &set_key_retry_task,
1406 kx);
1407}
1408
1409
1410void
1411GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx,
1412 const void *payload,
1413 size_t payload_size)
1414{
1415 size_t used = payload_size + sizeof(struct EncryptedMessage);
1416 char pbuf[used]; /* plaintext */
1417 struct EncryptedMessage *em; /* encrypted message */
1418 struct EncryptedMessage *ph; /* plaintext header */
1419 struct GNUNET_MQ_Envelope *env;
1420 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
1421 struct GNUNET_CRYPTO_AuthKey auth_key;
1422
1423 ph = (struct EncryptedMessage *) pbuf;
1424 ph->sequence_number = htonl (++kx->last_sequence_number_sent);
1425 ph->iv_seed = calculate_seed (kx);
1426 ph->reserved = 0;
1427 ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1428 GNUNET_memcpy (&ph[1], payload, payload_size);
1429 env = GNUNET_MQ_msg_extra (em,
1430 payload_size,
1431 GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE);
1432 em->iv_seed = ph->iv_seed;
1433 derive_iv (&iv, &kx->encrypt_key, ph->iv_seed, kx->peer);
1434 GNUNET_assert (GNUNET_OK == do_encrypt (kx,
1435 &iv,
1436 &ph->sequence_number,
1437 &em->sequence_number,
1438 used - ENCRYPTED_HEADER_SIZE));
1439#if DEBUG_KX
1440 {
1441 struct GNUNET_HashCode hc;
1442
1443 GNUNET_CRYPTO_hash (&ph->sequence_number,
1444 used - ENCRYPTED_HEADER_SIZE,
1445 &hc);
1446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1447 "Encrypted payload `%s' of %u bytes for %s\n",
1448 GNUNET_h2s (&hc),
1449 (unsigned int) (used - ENCRYPTED_HEADER_SIZE),
1450 GNUNET_i2s (kx->peer));
1451 }
1452#endif
1453 derive_auth_key (&auth_key, &kx->encrypt_key, ph->iv_seed);
1454 GNUNET_CRYPTO_hmac (&auth_key,
1455 &em->sequence_number,
1456 used - ENCRYPTED_HEADER_SIZE,
1457 &em->hmac);
1458#if DEBUG_KX
1459 {
1460 struct GNUNET_HashCode hc;
1461
1462 GNUNET_CRYPTO_hash (&auth_key, sizeof(auth_key), &hc);
1463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1464 "For peer %s, used AC %s to create hmac %s\n",
1465 GNUNET_i2s (kx->peer),
1466 GNUNET_h2s (&hc),
1467 GNUNET_h2s2 (&em->hmac));
1468 }
1469#endif
1470 kx->has_excess_bandwidth = GNUNET_NO;
1471 GNUNET_MQ_send (kx->mq, env);
1472}
1473
1474
1475/**
1476 * We received an encrypted message. Check that it is
1477 * well-formed (size-wise).
1478 *
1479 * @param cls key exchange context for encrypting the message
1480 * @param m encrypted message
1481 * @return #GNUNET_OK if @a msg is well-formed (size-wise)
1482 */
1483static int
1484check_encrypted (void *cls, const struct EncryptedMessage *m)
1485{
1486 uint16_t size = ntohs (m->header.size) - sizeof(*m);
1487
1488 if (size < sizeof(struct GNUNET_MessageHeader))
1489 {
1490 GNUNET_break_op (0);
1491 return GNUNET_SYSERR;
1492 }
1493 return GNUNET_OK;
1494}
1495
1496
1497/**
1498 * We received an encrypted message. Decrypt, validate and
1499 * pass on to the appropriate clients.
1500 *
1501 * @param cls key exchange context for encrypting the message
1502 * @param m encrypted message
1503 */
1504static void
1505handle_encrypted (void *cls, const struct EncryptedMessage *m)
1506{
1507 struct GSC_KeyExchangeInfo *kx = cls;
1508 struct EncryptedMessage *pt; /* plaintext */
1509 struct GNUNET_HashCode ph;
1510 uint32_t snum;
1511 struct GNUNET_TIME_Absolute t;
1512 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
1513 struct GNUNET_CRYPTO_AuthKey auth_key;
1514 uint16_t size = ntohs (m->header.size);
1515 char buf[size] GNUNET_ALIGN;
1516
1517 if (GNUNET_CORE_KX_STATE_UP != kx->status)
1518 {
1519 GNUNET_STATISTICS_update (GSC_stats,
1520 gettext_noop (
1521 "# DATA message dropped (out of order)"),
1522 1,
1523 GNUNET_NO);
1524 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1525 return;
1526 }
1527 if (0 ==
1528 GNUNET_TIME_absolute_get_remaining (kx->foreign_key_expires).rel_value_us)
1529 {
1530 GNUNET_log (
1531 GNUNET_ERROR_TYPE_WARNING,
1532 _ (
1533 "Session to peer `%s' went down due to key expiration (should not happen)\n"),
1534 GNUNET_i2s (kx->peer));
1535 GNUNET_STATISTICS_update (GSC_stats,
1536 gettext_noop (
1537 "# sessions terminated by key expiration"),
1538 1,
1539 GNUNET_NO);
1540 GSC_SESSIONS_end (kx->peer);
1541 if (NULL != kx->keep_alive_task)
1542 {
1543 GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
1544 kx->keep_alive_task = NULL;
1545 }
1546 kx->status = GNUNET_CORE_KX_STATE_KEY_SENT;
1547 monitor_notify_all (kx);
1548 send_key (kx);
1549 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1550 return;
1551 }
1552
1553 /* validate hash */
1554#if DEBUG_KX
1555 {
1556 struct GNUNET_HashCode hc;
1557
1558 GNUNET_CRYPTO_hash (&m->sequence_number, size - ENCRYPTED_HEADER_SIZE, &hc);
1559 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1560 "Received encrypted payload `%s' of %u bytes from %s\n",
1561 GNUNET_h2s (&hc),
1562 (unsigned int) (size - ENCRYPTED_HEADER_SIZE),
1563 GNUNET_i2s (kx->peer));
1564 }
1565#endif
1566 derive_auth_key (&auth_key, &kx->decrypt_key, m->iv_seed);
1567 GNUNET_CRYPTO_hmac (&auth_key,
1568 &m->sequence_number,
1569 size - ENCRYPTED_HEADER_SIZE,
1570 &ph);
1571#if DEBUG_KX
1572 {
1573 struct GNUNET_HashCode hc;
1574
1575 GNUNET_CRYPTO_hash (&auth_key, sizeof(auth_key), &hc);
1576 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1577 "For peer %s, used AC %s to verify hmac %s\n",
1578 GNUNET_i2s (kx->peer),
1579 GNUNET_h2s (&hc),
1580 GNUNET_h2s2 (&m->hmac));
1581 }
1582#endif
1583 if (0 != memcmp (&ph, &m->hmac, sizeof(struct GNUNET_HashCode)))
1584 {
1585 /* checksum failed */
1586 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1587 "Failed checksum validation for a message from `%s'\n",
1588 GNUNET_i2s (kx->peer));
1589 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1590 return;
1591 }
1592 derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity);
1593 /* decrypt */
1594 if (GNUNET_OK != do_decrypt (kx,
1595 &iv,
1596 &m->sequence_number,
1597 &buf[ENCRYPTED_HEADER_SIZE],
1598 size - ENCRYPTED_HEADER_SIZE))
1599 {
1600 GNUNET_break_op (0);
1601 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1602 return;
1603 }
1604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1605 "Decrypted %u bytes from %s\n",
1606 (unsigned int) (size - ENCRYPTED_HEADER_SIZE),
1607 GNUNET_i2s (kx->peer));
1608 pt = (struct EncryptedMessage *) buf;
1609
1610 /* validate sequence number */
1611 snum = ntohl (pt->sequence_number);
1612 if (kx->last_sequence_number_received == snum)
1613 {
1614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1615 "Received duplicate message, ignoring.\n");
1616 /* duplicate, ignore */
1617 GNUNET_STATISTICS_update (GSC_stats,
1618 gettext_noop ("# bytes dropped (duplicates)"),
1619 size,
1620 GNUNET_NO);
1621 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1622 return;
1623 }
1624 if ((kx->last_sequence_number_received > snum) &&
1625 (kx->last_sequence_number_received - snum > 32))
1626 {
1627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1628 "Received ancient out of sequence message, ignoring.\n");
1629 /* ancient out of sequence, ignore */
1630 GNUNET_STATISTICS_update (GSC_stats,
1631 gettext_noop (
1632 "# bytes dropped (out of sequence)"),
1633 size,
1634 GNUNET_NO);
1635 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1636 return;
1637 }
1638 if (kx->last_sequence_number_received > snum)
1639 {
1640 uint32_t rotbit = 1U << (kx->last_sequence_number_received - snum - 1);
1641
1642 if ((kx->last_packets_bitmap & rotbit) != 0)
1643 {
1644 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1645 "Received duplicate message, ignoring.\n");
1646 GNUNET_STATISTICS_update (GSC_stats,
1647 gettext_noop ("# bytes dropped (duplicates)"),
1648 size,
1649 GNUNET_NO);
1650 /* duplicate, ignore */
1651 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1652 return;
1653 }
1654 kx->last_packets_bitmap |= rotbit;
1655 }
1656 if (kx->last_sequence_number_received < snum)
1657 {
1658 unsigned int shift = (snum - kx->last_sequence_number_received);
1659
1660 if (shift >= 8 * sizeof(kx->last_packets_bitmap))
1661 kx->last_packets_bitmap = 0;
1662 else
1663 kx->last_packets_bitmap <<= shift;
1664 kx->last_sequence_number_received = snum;
1665 }
1666
1667 /* check timestamp */
1668 t = GNUNET_TIME_absolute_ntoh (pt->timestamp);
1669 if (GNUNET_TIME_absolute_get_duration (t).rel_value_us >
1670 MAX_MESSAGE_AGE.rel_value_us)
1671 {
1672 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1673 "Message received far too old (%s). Content ignored.\n",
1674 GNUNET_STRINGS_relative_time_to_string (
1675 GNUNET_TIME_absolute_get_duration (t),
1676 GNUNET_YES));
1677 GNUNET_STATISTICS_update (GSC_stats,
1678 gettext_noop (
1679 "# bytes dropped (ancient message)"),
1680 size,
1681 GNUNET_NO);
1682 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1683 return;
1684 }
1685
1686 /* process decrypted message(s) */
1687 update_timeout (kx);
1688 GNUNET_STATISTICS_update (GSC_stats,
1689 gettext_noop ("# bytes of payload decrypted"),
1690 size - sizeof(struct EncryptedMessage),
1691 GNUNET_NO);
1692 if (GNUNET_OK !=
1693 GNUNET_MST_from_buffer (kx->mst,
1694 &buf[sizeof(struct EncryptedMessage)],
1695 size - sizeof(struct EncryptedMessage),
1696 GNUNET_YES,
1697 GNUNET_NO))
1698 GNUNET_break_op (0);
1699
1700 GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer);
1701}
1702
1703
1704/**
1705 * Setup the message that links the ephemeral key to our persistent
1706 * public key and generate the appropriate signature.
1707 */
1708static void
1709sign_ephemeral_key ()
1710{
1711 current_ekm.header.size = htons (sizeof(struct EphemeralKeyMessage));
1712 current_ekm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY);
1713 current_ekm.sender_status = 0; /* to be set later */
1714 current_ekm.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY);
1715 current_ekm.purpose.size =
1716 htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
1717 + sizeof(struct GNUNET_TIME_AbsoluteNBO)
1718 + sizeof(struct GNUNET_TIME_AbsoluteNBO)
1719 + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)
1720 + sizeof(struct GNUNET_PeerIdentity));
1721 current_ekm.creation_time =
1722 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1723 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (GSC_cfg,
1724 "core",
1725 "USE_EPHEMERAL_KEYS"))
1726 {
1727 current_ekm.expiration_time =
1728 GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (
1729 GNUNET_TIME_relative_add (REKEY_FREQUENCY,
1730 REKEY_TOLERANCE)));
1731 }
1732 else
1733 {
1734 current_ekm.expiration_time =
1735 GNUNET_TIME_absolute_hton (GNUNET_TIME_UNIT_FOREVER_ABS);
1736 }
1737 GNUNET_CRYPTO_ecdhe_key_get_public (&my_ephemeral_key,
1738 &current_ekm.ephemeral_key);
1739 current_ekm.origin_identity = GSC_my_identity;
1740 GNUNET_assert (GNUNET_OK ==
1741 GNUNET_CRYPTO_eddsa_sign_ (&my_private_key,
1742 &current_ekm.purpose,
1743 &current_ekm.signature));
1744}
1745
1746
1747/**
1748 * Task run to trigger rekeying.
1749 *
1750 * @param cls closure, NULL
1751 */
1752static void
1753do_rekey (void *cls)
1754{
1755 struct GSC_KeyExchangeInfo *pos;
1756
1757 rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, &do_rekey, NULL);
1758 GNUNET_CRYPTO_ecdhe_key_create (&my_ephemeral_key);
1759 sign_ephemeral_key ();
1760 {
1761 struct GNUNET_HashCode eh;
1762
1763 GNUNET_CRYPTO_hash (&current_ekm.ephemeral_key,
1764 sizeof(current_ekm.ephemeral_key),
1765 &eh);
1766 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Rekeying to %s\n", GNUNET_h2s (&eh));
1767 }
1768 for (pos = kx_head; NULL != pos; pos = pos->next)
1769 {
1770 if (GNUNET_CORE_KX_STATE_UP == pos->status)
1771 {
1772 pos->status = GNUNET_CORE_KX_STATE_REKEY_SENT;
1773 monitor_notify_all (pos);
1774 derive_session_keys (pos);
1775 }
1776 if (GNUNET_CORE_KX_STATE_DOWN == pos->status)
1777 {
1778 pos->status = GNUNET_CORE_KX_STATE_KEY_SENT;
1779 monitor_notify_all (pos);
1780 }
1781 monitor_notify_all (pos);
1782 send_key (pos);
1783 }
1784}
1785
1786
1787/**
1788 * Initialize KX subsystem.
1789 *
1790 * @param pk private key to use for the peer
1791 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1792 */
1793int
1794GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk)
1795{
1796 struct GNUNET_MQ_MessageHandler handlers[] = {
1797 GNUNET_MQ_hd_fixed_size (ephemeral_key,
1798 GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY,
1799 struct EphemeralKeyMessage,
1800 NULL),
1801 GNUNET_MQ_hd_fixed_size (ping,
1802 GNUNET_MESSAGE_TYPE_CORE_PING,
1803 struct PingMessage,
1804 NULL),
1805 GNUNET_MQ_hd_fixed_size (pong,
1806 GNUNET_MESSAGE_TYPE_CORE_PONG,
1807 struct PongMessage,
1808 NULL),
1809 GNUNET_MQ_hd_var_size (encrypted,
1810 GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE,
1811 struct EncryptedMessage,
1812 NULL),
1813 GNUNET_MQ_handler_end ()
1814 };
1815
1816 my_private_key = *pk;
1817 GNUNET_CRYPTO_eddsa_key_get_public (&my_private_key,
1818 &GSC_my_identity.public_key);
1819 GNUNET_CRYPTO_ecdhe_key_create (&my_ephemeral_key);
1820 sign_ephemeral_key ();
1821 {
1822 struct GNUNET_HashCode eh;
1823
1824 GNUNET_CRYPTO_hash (&current_ekm.ephemeral_key,
1825 sizeof(current_ekm.ephemeral_key),
1826 &eh);
1827 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1828 "Starting with ephemeral key %s\n",
1829 GNUNET_h2s (&eh));
1830 }
1831
1832 nc = GNUNET_notification_context_create (1);
1833 rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, &do_rekey, NULL);
1834 transport =
1835 GNUNET_TRANSPORT_core_connect (GSC_cfg,
1836 &GSC_my_identity,
1837 handlers,
1838 NULL,
1839 &handle_transport_notify_connect,
1840 &handle_transport_notify_disconnect);
1841 if (NULL == transport)
1842 {
1843 GSC_KX_done ();
1844 return GNUNET_SYSERR;
1845 }
1846 return GNUNET_OK;
1847}
1848
1849
1850/**
1851 * Shutdown KX subsystem.
1852 */
1853void
1854GSC_KX_done ()
1855{
1856 if (NULL != transport)
1857 {
1858 GNUNET_TRANSPORT_core_disconnect (transport);
1859 transport = NULL;
1860 }
1861 if (NULL != rekey_task)
1862 {
1863 GNUNET_SCHEDULER_cancel (rekey_task);
1864 rekey_task = NULL;
1865 }
1866 memset (&my_ephemeral_key,
1867 0,
1868 sizeof (my_ephemeral_key));
1869 memset (&my_private_key,
1870 0,
1871 sizeof (my_private_key));
1872 if (NULL != nc)
1873 {
1874 GNUNET_notification_context_destroy (nc);
1875 nc = NULL;
1876 }
1877}
1878
1879
1880/**
1881 * Check how many messages are queued for the given neighbour.
1882 *
1883 * @param kxinfo data about neighbour to check
1884 * @return number of items in the message queue
1885 */
1886unsigned int
1887GSC_NEIGHBOURS_get_queue_length (const struct GSC_KeyExchangeInfo *kxinfo)
1888{
1889 return GNUNET_MQ_get_length (kxinfo->mq);
1890}
1891
1892
1893int
1894GSC_NEIGHBOURS_check_excess_bandwidth (const struct GSC_KeyExchangeInfo *kxinfo)
1895{
1896 return kxinfo->has_excess_bandwidth;
1897}
1898
1899
1900/**
1901 * Handle #GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request. For this
1902 * request type, the client does not have to have transmitted an INIT
1903 * request. All current peers are returned, regardless of which
1904 * message types they accept.
1905 *
1906 * @param mq message queue to add for monitoring
1907 */
1908void
1909GSC_KX_handle_client_monitor_peers (struct GNUNET_MQ_Handle *mq)
1910{
1911 struct GNUNET_MQ_Envelope *env;
1912 struct MonitorNotifyMessage *done_msg;
1913 struct GSC_KeyExchangeInfo *kx;
1914
1915 GNUNET_notification_context_add (nc, mq);
1916 for (kx = kx_head; NULL != kx; kx = kx->next)
1917 {
1918 struct GNUNET_MQ_Envelope *env;
1919 struct MonitorNotifyMessage *msg;
1920
1921 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY);
1922 msg->state = htonl ((uint32_t) kx->status);
1923 msg->peer = *kx->peer;
1924 msg->timeout = GNUNET_TIME_absolute_hton (kx->timeout);
1925 GNUNET_MQ_send (mq, env);
1926 }
1927 env = GNUNET_MQ_msg (done_msg, GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY);
1928 done_msg->state = htonl ((uint32_t) GNUNET_CORE_KX_ITERATION_FINISHED);
1929 done_msg->timeout = GNUNET_TIME_absolute_hton (GNUNET_TIME_UNIT_FOREVER_ABS);
1930 GNUNET_MQ_send (mq, env);
1931}
1932
1933
1934/* end of gnunet-service-core_kx.c */
diff --git a/src/service/core/gnunet-service-core_kx.h b/src/service/core/gnunet-service-core_kx.h
new file mode 100644
index 000000000..8bcac3f68
--- /dev/null
+++ b/src/service/core/gnunet-service-core_kx.h
@@ -0,0 +1,102 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core_kx.h
23 * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other peers
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_CORE_KX_H
27#define GNUNET_SERVICE_CORE_KX_H
28
29#include "gnunet_util_lib.h"
30
31
32/**
33 * Information about the status of a key exchange with another peer.
34 */
35struct GSC_KeyExchangeInfo;
36
37
38/**
39 * Encrypt and transmit a message with the given payload.
40 *
41 * @param kx key exchange context
42 * @param payload payload of the message
43 * @param payload_size number of bytes in 'payload'
44 */
45void
46GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx,
47 const void *payload,
48 size_t payload_size);
49
50
51/**
52 * Initialize KX subsystem.
53 *
54 * @param pk private key to use for the peer
55 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
56 */
57int
58GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk);
59
60
61/**
62 * Shutdown KX subsystem.
63 */
64void
65GSC_KX_done (void);
66
67
68/**
69 * Check if the given neighbour has excess bandwidth available.
70 *
71 * @param target neighbour to check
72 * @return #GNUNET_YES if excess bandwidth is available, #GNUNET_NO if not
73 */
74int
75GSC_NEIGHBOURS_check_excess_bandwidth (const struct
76 GSC_KeyExchangeInfo *target);
77
78
79/**
80 * Check how many messages are queued for the given neighbour.
81 *
82 * @param target neighbour to check
83 * @return number of items in the message queue
84 */
85unsigned int
86GSC_NEIGHBOURS_get_queue_length (const struct GSC_KeyExchangeInfo *target);
87
88
89/**
90 * Handle #GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request. For this
91 * request type, the client does not have to have transmitted an INIT
92 * request. All current peers are returned, regardless of which
93 * message types they accept.
94 *
95 * @param mq message queue to add for monitoring
96 */
97void
98GSC_KX_handle_client_monitor_peers (struct GNUNET_MQ_Handle *mq);
99
100
101#endif
102/* end of gnunet-service-core_kx.h */
diff --git a/src/service/core/gnunet-service-core_sessions.c b/src/service/core/gnunet-service-core_sessions.c
new file mode 100644
index 000000000..e103c89f5
--- /dev/null
+++ b/src/service/core/gnunet-service-core_sessions.c
@@ -0,0 +1,1028 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core_sessions.c
23 * @brief code for managing of 'encrypted' sessions (key exchange done)
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet-service-core.h"
28#include "gnunet-service-core_kx.h"
29#include "gnunet-service-core_typemap.h"
30#include "gnunet-service-core_sessions.h"
31#include "gnunet_constants.h"
32#include "core.h"
33
34
35/**
36 * How many encrypted messages do we queue at most?
37 * Needed to bound memory consumption.
38 */
39#define MAX_ENCRYPTED_MESSAGE_QUEUE_SIZE 4
40
41
42/**
43 * Message ready for encryption. This struct is followed by the
44 * actual content of the message.
45 */
46struct SessionMessageEntry
47{
48 /**
49 * We keep messages in a doubly linked list.
50 */
51 struct SessionMessageEntry *next;
52
53 /**
54 * We keep messages in a doubly linked list.
55 */
56 struct SessionMessageEntry *prev;
57
58 /**
59 * How important is this message.
60 */
61 enum GNUNET_MQ_PriorityPreferences priority;
62
63 /**
64 * Flag set to #GNUNET_YES if this is a typemap message.
65 */
66 int is_typemap;
67
68 /**
69 * Flag set to #GNUNET_YES if this is a typemap confirmation message.
70 */
71 int is_typemap_confirm;
72
73 /**
74 * Deadline for transmission, 1s after we received it (if we
75 * are not corking), otherwise "now". Note that this message
76 * does NOT expire past its deadline.
77 */
78 struct GNUNET_TIME_Absolute deadline;
79
80 /**
81 * How long is the message? (number of bytes following the `struct
82 * MessageEntry`, but not including the size of `struct
83 * MessageEntry` itself!)
84 */
85 size_t size;
86};
87
88
89/**
90 * Data kept per session.
91 */
92struct Session
93{
94 /**
95 * Identity of the other peer.
96 */
97 const struct GNUNET_PeerIdentity *peer;
98
99 /**
100 * Key exchange state for this peer.
101 */
102 struct GSC_KeyExchangeInfo *kx;
103
104 /**
105 * Head of list of requests from clients for transmission to
106 * this peer.
107 */
108 struct GSC_ClientActiveRequest *active_client_request_head;
109
110 /**
111 * Tail of list of requests from clients for transmission to
112 * this peer.
113 */
114 struct GSC_ClientActiveRequest *active_client_request_tail;
115
116 /**
117 * Head of list of messages ready for encryption.
118 */
119 struct SessionMessageEntry *sme_head;
120
121 /**
122 * Tail of list of messages ready for encryption.
123 */
124 struct SessionMessageEntry *sme_tail;
125
126 /**
127 * Current type map for this peer.
128 */
129 struct GSC_TypeMap *tmap;
130
131 /**
132 * Task to transmit corked messages with a delay.
133 */
134 struct GNUNET_SCHEDULER_Task *cork_task;
135
136 /**
137 * Task to transmit our type map.
138 */
139 struct GNUNET_SCHEDULER_Task *typemap_task;
140
141 /**
142 * Retransmission delay we currently use for the typemap
143 * transmissions (if not confirmed).
144 */
145 struct GNUNET_TIME_Relative typemap_delay;
146
147 /**
148 * Is this the first time we're sending the typemap? If so,
149 * we want to send it a bit faster the second time. 0 if
150 * we are sending for the first time, 1 if not.
151 */
152 int first_typemap;
153};
154
155
156GNUNET_NETWORK_STRUCT_BEGIN
157
158/**
159 * Message sent to confirm that a typemap was received.
160 */
161struct TypeMapConfirmationMessage
162{
163 /**
164 * Header with type #GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP.
165 */
166 struct GNUNET_MessageHeader header;
167
168 /**
169 * Reserved, always zero.
170 */
171 uint32_t reserved GNUNET_PACKED;
172
173 /**
174 * Hash of the (decompressed) type map that was received.
175 */
176 struct GNUNET_HashCode tm_hash;
177};
178
179GNUNET_NETWORK_STRUCT_END
180
181
182/**
183 * Map of peer identities to `struct Session`.
184 */
185static struct GNUNET_CONTAINER_MultiPeerMap *sessions;
186
187
188/**
189 * Find the session for the given peer.
190 *
191 * @param peer identity of the peer
192 * @return NULL if we are not connected, otherwise the
193 * session handle
194 */
195static struct Session *
196find_session (const struct GNUNET_PeerIdentity *peer)
197{
198 if (NULL == sessions)
199 return NULL;
200 return GNUNET_CONTAINER_multipeermap_get (sessions, peer);
201}
202
203
204/**
205 * End the session with the given peer (we are no longer
206 * connected).
207 *
208 * @param pid identity of peer to kill session with
209 */
210void
211GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid)
212{
213 struct Session *session;
214 struct GSC_ClientActiveRequest *car;
215 struct SessionMessageEntry *sme;
216
217 session = find_session (pid);
218 if (NULL == session)
219 return;
220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221 "Destroying session for peer `%s'\n",
222 GNUNET_i2s (session->peer));
223 if (NULL != session->cork_task)
224 {
225 GNUNET_SCHEDULER_cancel (session->cork_task);
226 session->cork_task = NULL;
227 }
228 while (NULL != (car = session->active_client_request_head))
229 {
230 GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
231 session->active_client_request_tail,
232 car);
233 GSC_CLIENTS_reject_request (car, GNUNET_NO);
234 }
235 while (NULL != (sme = session->sme_head))
236 {
237 GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme);
238 GNUNET_free (sme);
239 }
240 if (NULL != session->typemap_task)
241 {
242 GNUNET_SCHEDULER_cancel (session->typemap_task);
243 session->typemap_task = NULL;
244 }
245 GSC_CLIENTS_notify_clients_about_neighbour (session->peer,
246 session->tmap,
247 NULL);
248 GNUNET_assert (
249 GNUNET_YES ==
250 GNUNET_CONTAINER_multipeermap_remove (sessions, session->peer, session));
251 GNUNET_STATISTICS_set (GSC_stats,
252 gettext_noop ("# peers connected"),
253 GNUNET_CONTAINER_multipeermap_size (sessions),
254 GNUNET_NO);
255 GSC_TYPEMAP_destroy (session->tmap);
256 session->tmap = NULL;
257 GNUNET_free (session);
258}
259
260
261/**
262 * Transmit our current typemap message to the other peer.
263 * (Done periodically until the typemap is confirmed).
264 *
265 * @param cls the `struct Session *`
266 */
267static void
268transmit_typemap_task (void *cls)
269{
270 struct Session *session = cls;
271 struct GNUNET_MessageHeader *hdr;
272 struct GNUNET_TIME_Relative delay;
273
274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
275 "Sending TYPEMAP to %s\n",
276 GNUNET_i2s (session->peer));
277 session->typemap_delay = GNUNET_TIME_STD_BACKOFF (session->typemap_delay);
278 delay = session->typemap_delay;
279 /* randomize a bit to avoid spont. sync */
280 delay.rel_value_us +=
281 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000 * 1000);
282 session->typemap_task =
283 GNUNET_SCHEDULER_add_delayed (delay, &transmit_typemap_task, session);
284 GNUNET_STATISTICS_update (GSC_stats,
285 gettext_noop ("# type map refreshes sent"),
286 1,
287 GNUNET_NO);
288 hdr = GSC_TYPEMAP_compute_type_map_message ();
289 GSC_KX_encrypt_and_transmit (session->kx, hdr, ntohs (hdr->size));
290 GNUNET_free (hdr);
291}
292
293
294/**
295 * Restart the typemap task for the given session.
296 *
297 * @param session session to restart typemap transmission for
298 */
299static void
300start_typemap_task (struct Session *session)
301{
302 if (NULL != session->typemap_task)
303 GNUNET_SCHEDULER_cancel (session->typemap_task);
304 session->typemap_delay = GNUNET_TIME_UNIT_SECONDS;
305 session->typemap_task = GNUNET_SCHEDULER_add_delayed (session->typemap_delay,
306 &transmit_typemap_task,
307 session);
308}
309
310
311/**
312 * Create a session, a key exchange was just completed.
313 *
314 * @param peer peer that is now connected
315 * @param kx key exchange that completed
316 */
317void
318GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer,
319 struct GSC_KeyExchangeInfo *kx)
320{
321 struct Session *session;
322
323 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
324 "Creating session for peer `%s'\n",
325 GNUNET_i2s (peer));
326 session = GNUNET_new (struct Session);
327 session->tmap = GSC_TYPEMAP_create ();
328 session->peer = peer;
329 session->kx = kx;
330 GNUNET_assert (GNUNET_OK ==
331 GNUNET_CONTAINER_multipeermap_put (
332 sessions,
333 session->peer,
334 session,
335 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
336 GNUNET_STATISTICS_set (GSC_stats,
337 gettext_noop ("# peers connected"),
338 GNUNET_CONTAINER_multipeermap_size (sessions),
339 GNUNET_NO);
340 GSC_CLIENTS_notify_clients_about_neighbour (peer, NULL, session->tmap);
341 start_typemap_task (session);
342}
343
344
345/**
346 * The other peer has indicated that it 'lost' the session
347 * (KX down), reinitialize the session on our end, in particular
348 * this means to restart the typemap transmission.
349 *
350 * @param peer peer that is now connected
351 */
352void
353GSC_SESSIONS_reinit (const struct GNUNET_PeerIdentity *peer)
354{
355 struct Session *session;
356
357 session = find_session (peer);
358 if (NULL == session)
359 {
360 /* KX/session is new for both sides; thus no need to restart what
361 has not yet begun */
362 return;
363 }
364 start_typemap_task (session);
365}
366
367
368/**
369 * The other peer has confirmed receiving our type map,
370 * check if it is current and if so, stop retransmitting it.
371 *
372 * @param peer peer that confirmed the type map
373 * @param msg confirmation message we received
374 */
375void
376GSC_SESSIONS_confirm_typemap (const struct GNUNET_PeerIdentity *peer,
377 const struct GNUNET_MessageHeader *msg)
378{
379 const struct TypeMapConfirmationMessage *cmsg;
380 struct Session *session;
381
382 session = find_session (peer);
383 if (NULL == session)
384 {
385 GNUNET_break (0);
386 return;
387 }
388 if (ntohs (msg->size) != sizeof(struct TypeMapConfirmationMessage))
389 {
390 GNUNET_break_op (0);
391 return;
392 }
393 cmsg = (const struct TypeMapConfirmationMessage *) msg;
394 if (GNUNET_YES != GSC_TYPEMAP_check_hash (&cmsg->tm_hash))
395 {
396 /* our typemap has changed in the meantime, do not
397 accept confirmation */
398 GNUNET_STATISTICS_update (GSC_stats,
399 gettext_noop (
400 "# outdated typemap confirmations received"),
401 1,
402 GNUNET_NO);
403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404 "Got outdated typemap confirmated from peer `%s'\n",
405 GNUNET_i2s (session->peer));
406 return;
407 }
408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409 "Got typemap confirmation from peer `%s'\n",
410 GNUNET_i2s (session->peer));
411 if (NULL != session->typemap_task)
412 {
413 GNUNET_SCHEDULER_cancel (session->typemap_task);
414 session->typemap_task = NULL;
415 }
416 GNUNET_STATISTICS_update (GSC_stats,
417 gettext_noop (
418 "# valid typemap confirmations received"),
419 1,
420 GNUNET_NO);
421}
422
423
424/**
425 * Notify the given client about the session (client is new).
426 *
427 * @param cls the `struct GSC_Client`
428 * @param key peer identity
429 * @param value the `struct Session`
430 * @return #GNUNET_OK (continue to iterate)
431 */
432static int
433notify_client_about_session (void *cls,
434 const struct GNUNET_PeerIdentity *key,
435 void *value)
436{
437 struct GSC_Client *client = cls;
438 struct Session *session = value;
439
440 GSC_CLIENTS_notify_client_about_neighbour (client,
441 session->peer,
442 NULL, /* old TMAP: none */
443 session->tmap);
444 return GNUNET_OK;
445}
446
447
448/**
449 * We have a new client, notify it about all current sessions.
450 *
451 * @param client the new client
452 */
453void
454GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client)
455{
456 /* notify new client about existing sessions */
457 GNUNET_CONTAINER_multipeermap_iterate (sessions,
458 &notify_client_about_session,
459 client);
460}
461
462
463/**
464 * Try to perform a transmission on the given session. Will solicit
465 * additional messages if the 'sme' queue is not full enough.
466 *
467 * @param session session to transmit messages from
468 */
469static void
470try_transmission (struct Session *session);
471
472
473/**
474 * Queue a request from a client for transmission to a particular peer.
475 *
476 * @param car request to queue; this handle is then shared between
477 * the caller (CLIENTS subsystem) and SESSIONS and must not
478 * be released by either until either #GSC_SESSIONS_dequeue(),
479 * #GSC_SESSIONS_transmit() or #GSC_CLIENTS_failed()
480 * have been invoked on it
481 */
482void
483GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car)
484{
485 struct Session *session;
486
487 session = find_session (&car->target);
488 if (NULL == session)
489 {
490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491 "Dropped client request for transmission (am disconnected)\n");
492 GNUNET_break (0); /* should have been rejected earlier */
493 GSC_CLIENTS_reject_request (car, GNUNET_NO);
494 return;
495 }
496 if (car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
497 {
498 GNUNET_break (0);
499 GSC_CLIENTS_reject_request (car, GNUNET_YES);
500 return;
501 }
502 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503 "Received client transmission request. queueing\n");
504 GNUNET_CONTAINER_DLL_insert_tail (session->active_client_request_head,
505 session->active_client_request_tail,
506 car);
507 try_transmission (session);
508}
509
510
511/**
512 * Dequeue a request from a client from transmission to a particular peer.
513 *
514 * @param car request to dequeue; this handle will then be 'owned' by
515 * the caller (CLIENTS sysbsystem)
516 */
517void
518GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car)
519{
520 struct Session *session;
521
522 if (0 == memcmp (&car->target,
523 &GSC_my_identity,
524 sizeof(struct GNUNET_PeerIdentity)))
525 return;
526 session = find_session (&car->target);
527 GNUNET_assert (NULL != session);
528 GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
529 session->active_client_request_tail,
530 car);
531 /* dequeueing of 'high' priority messages may unblock
532 transmission for lower-priority messages, so we also
533 need to try in this case. */
534 try_transmission (session);
535}
536
537
538/**
539 * Solicit messages for transmission, starting with those of the highest
540 * priority.
541 *
542 * @param session session to solict messages for
543 * @param msize how many bytes do we have already
544 */
545static void
546solicit_messages (struct Session *session, size_t msize)
547{
548 struct GSC_ClientActiveRequest *car;
549 struct GSC_ClientActiveRequest *nxt;
550 size_t so_size;
551 enum GNUNET_MQ_PriorityPreferences pmax;
552
553 so_size = msize;
554 pmax = GNUNET_MQ_PRIO_BACKGROUND;
555 for (car = session->active_client_request_head; NULL != car; car = car->next)
556 {
557 if (GNUNET_YES == car->was_solicited)
558 continue;
559 pmax = GNUNET_MAX (pmax, car->priority & GNUNET_MQ_PRIORITY_MASK);
560 }
561 nxt = session->active_client_request_head;
562 while (NULL != (car = nxt))
563 {
564 nxt = car->next;
565 if (car->priority < pmax)
566 continue;
567 if (so_size + car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
568 break;
569 so_size += car->msize;
570 if (GNUNET_YES == car->was_solicited)
571 continue;
572 car->was_solicited = GNUNET_YES;
573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574 "Soliciting message with priority %u\n",
575 car->priority);
576 GSC_CLIENTS_solicit_request (car);
577 /* The above call may *dequeue* requests and thereby
578 clobber 'nxt'. Hence we need to restart from the
579 head of the list. */
580 nxt = session->active_client_request_head;
581 so_size = msize;
582 }
583}
584
585
586/**
587 * Some messages were delayed (corked), but the timeout has now expired.
588 * Send them now.
589 *
590 * @param cls `struct Session` with the messages to transmit now
591 */
592static void
593pop_cork_task (void *cls)
594{
595 struct Session *session = cls;
596
597 session->cork_task = NULL;
598 try_transmission (session);
599}
600
601
602/**
603 * Try to perform a transmission on the given session. Will solicit
604 * additional messages if the 'sme' queue is not full enough or has
605 * only low-priority messages.
606 *
607 * @param session session to transmit messages from
608 */
609static void
610try_transmission (struct Session *session)
611{
612 struct SessionMessageEntry *pos;
613 size_t msize;
614 struct GNUNET_TIME_Absolute now;
615 struct GNUNET_TIME_Absolute min_deadline;
616 enum GNUNET_MQ_PriorityPreferences maxp;
617 enum GNUNET_MQ_PriorityPreferences maxpc;
618 struct GSC_ClientActiveRequest *car;
619 int excess;
620
621 msize = 0;
622 min_deadline = GNUNET_TIME_UNIT_FOREVER_ABS;
623 /* if the peer has excess bandwidth, background traffic is allowed,
624 otherwise not */
625 if (MAX_ENCRYPTED_MESSAGE_QUEUE_SIZE <=
626 GSC_NEIGHBOURS_get_queue_length (session->kx))
627 {
628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629 "Transmission queue already very long, waiting...\n");
630 return; /* queue already too long */
631 }
632 excess = GSC_NEIGHBOURS_check_excess_bandwidth (session->kx);
633 if (GNUNET_YES == excess)
634 maxp = GNUNET_MQ_PRIO_BACKGROUND;
635 else
636 maxp = GNUNET_MQ_PRIO_BEST_EFFORT;
637 /* determine highest priority of 'ready' messages we already solicited from clients */
638 pos = session->sme_head;
639 while ((NULL != pos) &&
640 (msize + pos->size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE))
641 {
642 GNUNET_assert (pos->size < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE);
643 msize += pos->size;
644 maxp = GNUNET_MAX (maxp, pos->priority & GNUNET_MQ_PRIORITY_MASK);
645 min_deadline = GNUNET_TIME_absolute_min (min_deadline, pos->deadline);
646 pos = pos->next;
647 }
648 GNUNET_log (
649 GNUNET_ERROR_TYPE_DEBUG,
650 "Calculating transmission set with %u priority (%s) and %s earliest deadline\n",
651 maxp,
652 (GNUNET_YES == excess) ? "excess bandwidth" : "limited bandwidth",
653 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (
654 min_deadline),
655 GNUNET_YES));
656
657 if (maxp < GNUNET_MQ_PRIO_CRITICAL_CONTROL)
658 {
659 /* if highest already solicited priority from clients is not critical,
660 check if there are higher-priority messages to be solicited from clients */
661 if (GNUNET_YES == excess)
662 maxpc = GNUNET_MQ_PRIO_BACKGROUND;
663 else
664 maxpc = GNUNET_MQ_PRIO_BEST_EFFORT;
665 for (car = session->active_client_request_head; NULL != car;
666 car = car->next)
667 {
668 if (GNUNET_YES == car->was_solicited)
669 continue;
670 maxpc = GNUNET_MAX (maxpc, car->priority & GNUNET_MQ_PRIORITY_MASK);
671 }
672 if (maxpc > maxp)
673 {
674 /* we have messages waiting for solicitation that have a higher
675 priority than those that we already accepted; solicit the
676 high-priority messages first */
677 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
678 "Soliciting messages based on priority (%u > %u)\n",
679 maxpc,
680 maxp);
681 solicit_messages (session, 0);
682 return;
683 }
684 }
685 else
686 {
687 /* never solicit more, we have critical messages to process */
688 excess = GNUNET_NO;
689 maxpc = GNUNET_MQ_PRIO_BACKGROUND;
690 }
691 now = GNUNET_TIME_absolute_get ();
692 if (((GNUNET_YES == excess) || (maxpc >= GNUNET_MQ_PRIO_BEST_EFFORT)) &&
693 ((0 == msize) ||
694 ((msize < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE / 2) &&
695 (min_deadline.abs_value_us > now.abs_value_us))))
696 {
697 /* not enough ready yet (tiny message & cork possible), or no messages at all,
698 and either excess bandwidth or best-effort or higher message waiting at
699 client; in this case, we try to solicit more */
700 GNUNET_log (
701 GNUNET_ERROR_TYPE_DEBUG,
702 "Soliciting messages (excess %d, maxpc %d, message size %u, deadline %s)\n",
703 excess,
704 maxpc,
705 (unsigned int) msize,
706 GNUNET_STRINGS_relative_time_to_string (
707 GNUNET_TIME_absolute_get_remaining (
708 min_deadline),
709 GNUNET_YES));
710 solicit_messages (session, msize);
711 if (msize > 0)
712 {
713 /* if there is data to send, just not yet, make sure we do transmit
714 * it once the deadline is reached */
715 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
716 "Corking until %s\n",
717 GNUNET_STRINGS_relative_time_to_string (
718 GNUNET_TIME_absolute_get_remaining (min_deadline),
719 GNUNET_YES));
720 if (NULL != session->cork_task)
721 GNUNET_SCHEDULER_cancel (session->cork_task);
722 session->cork_task =
723 GNUNET_SCHEDULER_add_at (min_deadline, &pop_cork_task, session);
724 }
725 else
726 {
727 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
728 "Queue empty, waiting for solicitations\n");
729 }
730 return;
731 }
732 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733 "Building combined plaintext buffer to transmit message!\n");
734 /* create plaintext buffer of all messages (that fit), encrypt and
735 transmit */
736 {
737 static unsigned long long total_bytes;
738 static unsigned int total_msgs;
739 char pbuf[msize]; /* plaintext */
740 size_t used;
741
742 used = 0;
743 while ((NULL != (pos = session->sme_head)) && (used + pos->size <= msize))
744 {
745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
746 "Adding message of type %d (%d/%d) to payload for %s\n",
747 ntohs (((const struct GNUNET_MessageHeader *) &pos[1])->type),
748 pos->is_typemap,
749 pos->is_typemap_confirm,
750 GNUNET_i2s (session->peer));
751 GNUNET_memcpy (&pbuf[used], &pos[1], pos->size);
752 used += pos->size;
753 GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, pos);
754 GNUNET_free (pos);
755 }
756 /* compute average payload size */
757 total_bytes += used;
758 total_msgs++;
759 if (0 == total_msgs)
760 {
761 /* 2^32 messages, wrap around... */
762 total_msgs = 1;
763 total_bytes = used;
764 }
765 GNUNET_STATISTICS_set (GSC_stats,
766 "# avg payload per encrypted message",
767 total_bytes / total_msgs,
768 GNUNET_NO);
769 /* now actually transmit... */
770 GSC_KX_encrypt_and_transmit (session->kx, pbuf, used);
771 }
772}
773
774
775/**
776 * Send an updated typemap message to the neighbour now,
777 * and restart typemap transmissions.
778 *
779 * @param cls the message
780 * @param key neighbour's identity
781 * @param value `struct Neighbour` of the target
782 * @return always #GNUNET_OK
783 */
784static int
785do_restart_typemap_message (void *cls,
786 const struct GNUNET_PeerIdentity *key,
787 void *value)
788{
789 const struct GNUNET_MessageHeader *hdr = cls;
790 struct Session *session = value;
791 struct SessionMessageEntry *sme;
792 uint16_t size;
793
794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795 "Restarting sending TYPEMAP to %s\n",
796 GNUNET_i2s (session->peer));
797 size = ntohs (hdr->size);
798 for (sme = session->sme_head; NULL != sme; sme = sme->next)
799 {
800 if (GNUNET_YES == sme->is_typemap)
801 {
802 GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme);
803 GNUNET_free (sme);
804 break;
805 }
806 }
807 sme = GNUNET_malloc (sizeof(struct SessionMessageEntry) + size);
808 sme->is_typemap = GNUNET_YES;
809 GNUNET_memcpy (&sme[1], hdr, size);
810 sme->size = size;
811 sme->priority = GNUNET_MQ_PRIO_CRITICAL_CONTROL;
812 GNUNET_CONTAINER_DLL_insert (session->sme_head, session->sme_tail, sme);
813 try_transmission (session);
814 start_typemap_task (session);
815 return GNUNET_OK;
816}
817
818
819/**
820 * Broadcast an updated typemap message to all neighbours.
821 * Restarts the retransmissions until the typemaps are confirmed.
822 *
823 * @param msg message to transmit
824 */
825void
826GSC_SESSIONS_broadcast_typemap (const struct GNUNET_MessageHeader *msg)
827{
828 if (NULL == sessions)
829 return;
830 GNUNET_CONTAINER_multipeermap_iterate (sessions,
831 &do_restart_typemap_message,
832 (void *) msg);
833}
834
835
836/**
837 * Traffic is being solicited for the given peer. This means that the
838 * message queue on the transport-level (NEIGHBOURS subsystem) is now
839 * empty and it is now OK to transmit another (non-control) message.
840 *
841 * @param pid identity of peer ready to receive data
842 */
843void
844GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid)
845{
846 struct Session *session;
847
848 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
849 "Transport solicits for %s\n",
850 GNUNET_i2s (pid));
851 session = find_session (pid);
852 if (NULL == session)
853 return;
854 try_transmission (session);
855}
856
857
858void
859GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car,
860 const struct GNUNET_MessageHeader *msg,
861 enum GNUNET_MQ_PriorityPreferences priority)
862{
863 struct Session *session;
864 struct SessionMessageEntry *sme;
865 struct SessionMessageEntry *pos;
866 size_t msize;
867
868 session = find_session (&car->target);
869 if (NULL == session)
870 return;
871 msize = ntohs (msg->size);
872 sme = GNUNET_malloc (sizeof(struct SessionMessageEntry) + msize);
873 GNUNET_memcpy (&sme[1], msg, msize);
874 sme->size = msize;
875 sme->priority = priority;
876 if (0 != (GNUNET_MQ_PREF_CORK_ALLOWED & priority))
877 {
878 sme->deadline =
879 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_MAX_CORK_DELAY);
880 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
881 "Message corked, delaying transmission\n");
882 }
883 pos = session->sme_head;
884 while ((NULL != pos) && (pos->priority >= sme->priority))
885 pos = pos->next;
886 if (NULL == pos)
887 GNUNET_CONTAINER_DLL_insert_tail (session->sme_head,
888 session->sme_tail,
889 sme);
890 else
891 GNUNET_CONTAINER_DLL_insert_after (session->sme_head,
892 session->sme_tail,
893 pos->prev,
894 sme);
895 try_transmission (session);
896}
897
898
899void
900GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer,
901 const struct GNUNET_MessageHeader *msg)
902{
903 struct Session *session;
904 struct GSC_TypeMap *nmap;
905 struct SessionMessageEntry *sme;
906 struct TypeMapConfirmationMessage *tmc;
907
908 nmap = GSC_TYPEMAP_get_from_message (msg);
909 if (NULL == nmap)
910 {
911 GNUNET_break_op (0);
912 return; /* malformed */
913 }
914 session = find_session (peer);
915 if (NULL == session)
916 {
917 GSC_TYPEMAP_destroy (nmap);
918 GNUNET_break (0);
919 return;
920 }
921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
922 "Received TYPEMAP from %s\n",
923 GNUNET_i2s (session->peer));
924 for (sme = session->sme_head; NULL != sme; sme = sme->next)
925 {
926 if (GNUNET_YES == sme->is_typemap_confirm)
927 {
928 GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme);
929 GNUNET_free (sme);
930 break;
931 }
932 }
933 sme = GNUNET_malloc (sizeof(struct SessionMessageEntry)
934 + sizeof(struct TypeMapConfirmationMessage));
935 sme->deadline = GNUNET_TIME_absolute_get ();
936 sme->size = sizeof(struct TypeMapConfirmationMessage);
937 sme->priority = GNUNET_MQ_PRIO_CRITICAL_CONTROL;
938 sme->is_typemap_confirm = GNUNET_YES;
939 tmc = (struct TypeMapConfirmationMessage *) &sme[1];
940 tmc->header.size = htons (sizeof(struct TypeMapConfirmationMessage));
941 tmc->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP);
942 tmc->reserved = htonl (0);
943 GSC_TYPEMAP_hash (nmap, &tmc->tm_hash);
944 GNUNET_CONTAINER_DLL_insert (session->sme_head, session->sme_tail, sme);
945 try_transmission (session);
946 GSC_CLIENTS_notify_clients_about_neighbour (peer, session->tmap, nmap);
947 GSC_TYPEMAP_destroy (session->tmap);
948 session->tmap = nmap;
949}
950
951
952/**
953 * The given peer send a message of the specified type. Make sure the
954 * respective bit is set in its type-map and that clients are notified
955 * about the session.
956 *
957 * @param peer peer this is about
958 * @param type type of the message
959 */
960void
961GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer,
962 uint16_t type)
963{
964 struct Session *session;
965 struct GSC_TypeMap *nmap;
966
967 if (0 == memcmp (peer, &GSC_my_identity, sizeof(struct GNUNET_PeerIdentity)))
968 return;
969 session = find_session (peer);
970 GNUNET_assert (NULL != session);
971 if (GNUNET_YES == GSC_TYPEMAP_test_match (session->tmap, &type, 1))
972 return; /* already in it */
973 nmap = GSC_TYPEMAP_extend (session->tmap, &type, 1);
974 GSC_CLIENTS_notify_clients_about_neighbour (peer, session->tmap, nmap);
975 GSC_TYPEMAP_destroy (session->tmap);
976 session->tmap = nmap;
977}
978
979
980/**
981 * Initialize sessions subsystem.
982 */
983void
984GSC_SESSIONS_init ()
985{
986 sessions = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
987}
988
989
990/**
991 * Helper function for #GSC_SESSIONS_done() to free all
992 * active sessions.
993 *
994 * @param cls NULL
995 * @param key identity of the connected peer
996 * @param value the `struct Session` for the peer
997 * @return #GNUNET_OK (continue to iterate)
998 */
999static int
1000free_session_helper (void *cls,
1001 const struct GNUNET_PeerIdentity *key,
1002 void *value)
1003{
1004 /* struct Session *session = value; */
1005
1006 GSC_SESSIONS_end (key);
1007 return GNUNET_OK;
1008}
1009
1010
1011/**
1012 * Shutdown sessions subsystem.
1013 */
1014void
1015GSC_SESSIONS_done ()
1016{
1017 if (NULL != sessions)
1018 {
1019 GNUNET_CONTAINER_multipeermap_iterate (sessions,
1020 &free_session_helper,
1021 NULL);
1022 GNUNET_CONTAINER_multipeermap_destroy (sessions);
1023 sessions = NULL;
1024 }
1025}
1026
1027
1028/* end of gnunet-service-core_sessions.c */
diff --git a/src/service/core/gnunet-service-core_sessions.h b/src/service/core/gnunet-service-core_sessions.h
new file mode 100644
index 000000000..fda2cc32c
--- /dev/null
+++ b/src/service/core/gnunet-service-core_sessions.h
@@ -0,0 +1,183 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core_neighbours.h
23 * @brief code for managing of 'encrypted' sessions (key exchange done)
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_CORE_SESSIONS_H
27#define GNUNET_SERVICE_CORE_SESSIONS_H
28
29#include "gnunet-service-core.h"
30#include "gnunet-service-core_kx.h"
31
32
33/**
34 * Create a session, a key exchange was just completed.
35 *
36 * @param peer peer that is now connected
37 * @param kx key exchange that completed
38 */
39void
40GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer,
41 struct GSC_KeyExchangeInfo *kx);
42
43
44/**
45 * The other peer has indicated that it 'lost' the session
46 * (KX down), reinitialize the session on our end, in particular
47 * this means to restart the typemap transmission.
48 *
49 * @param peer peer that is now connected
50 */
51void
52GSC_SESSIONS_reinit (const struct GNUNET_PeerIdentity *peer);
53
54
55/**
56 * The other peer has confirmed receiving our type map,
57 * check if it is current and if so, stop retransmitting it.
58 *
59 * @param peer peer that confirmed the type map
60 * @param msg confirmation message we received
61 */
62void
63GSC_SESSIONS_confirm_typemap (const struct GNUNET_PeerIdentity *peer,
64 const struct GNUNET_MessageHeader *msg);
65
66
67/**
68 * End the session with the given peer (we are no longer
69 * connected).
70 *
71 * @param pid identity of peer to kill session with
72 */
73void
74GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid);
75
76
77/**
78 * Traffic is being solicited for the given peer. This means that the
79 * message queue on the transport-level (NEIGHBOURS subsystem) is now
80 * empty and it is now OK to transmit another (non-control) message.
81 *
82 * @param pid identity of peer ready to receive data
83 */
84void
85GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid);
86
87
88/**
89 * Queue a request from a client for transmission to a particular peer.
90 *
91 * @param car request to queue; this handle is then shared between
92 * the caller (CLIENTS subsystem) and SESSIONS and must not
93 * be released by either until either 'GNUNET_SESSIONS_dequeue',
94 * or 'GNUNET_CLIENTS_failed'
95 * have been invoked on it
96 */
97void
98GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car);
99
100
101/**
102 * Dequeue a request from a client from transmission to a particular peer.
103 *
104 * @param car request to dequeue; this handle will then be 'owned' by
105 * the caller (CLIENTS sysbsystem)
106 */
107void
108GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car);
109
110
111/**
112 * Transmit a message to a particular peer.
113 *
114 * @param car original request that was queued and then solicited,
115 * ownership does not change (dequeue will be called soon).
116 * @param msg message to transmit
117 * @param priority how important is this message
118 */
119void
120GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car,
121 const struct GNUNET_MessageHeader *msg,
122 enum GNUNET_MQ_PriorityPreferences priority);
123
124
125/**
126 * Broadcast an updated typemap message to all neighbours.
127 * Restarts the retransmissions until the typemaps are confirmed.
128 *
129 * @param msg message to transmit
130 */
131void
132GSC_SESSIONS_broadcast_typemap (const struct GNUNET_MessageHeader *msg);
133
134
135/**
136 * We have a new client, notify it about all current sessions.
137 *
138 * @param client the new client
139 */
140void
141GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client);
142
143
144/**
145 * We've received a typemap message from a peer, update ours.
146 * Notifies clients about the session.
147 *
148 * @param peer peer this is about
149 * @param msg typemap update message
150 */
151void
152GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer,
153 const struct GNUNET_MessageHeader *msg);
154
155
156/**
157 * The given peer send a message of the specified type. Make sure the
158 * respective bit is set in its type-map and that clients are notified
159 * about the session.
160 *
161 * @param peer peer this is about
162 * @param type type of the message
163 */
164void
165GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer,
166 uint16_t type);
167
168
169/**
170 * Initialize sessions subsystem.
171 */
172void
173GSC_SESSIONS_init (void);
174
175
176/**
177 * Shutdown sessions subsystem.
178 */
179void
180GSC_SESSIONS_done (void);
181
182
183#endif
diff --git a/src/service/core/gnunet-service-core_typemap.c b/src/service/core/gnunet-service-core_typemap.c
new file mode 100644
index 000000000..200a84b23
--- /dev/null
+++ b/src/service/core/gnunet-service-core_typemap.c
@@ -0,0 +1,370 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core_typemap.c
23 * @brief management of map that specifies which message types this peer supports
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet-service-core.h"
29#include "gnunet-service-core_sessions.h"
30#include "gnunet-service-core_typemap.h"
31#include <zlib.h>
32
33
34/**
35 * A type map describing which messages a given neighbour is able
36 * to process.
37 */
38struct GSC_TypeMap
39{
40 uint32_t bits[(UINT16_MAX + 1) / 32];
41};
42
43/**
44 * Bitmap of message types this peer is able to handle.
45 */
46static struct GSC_TypeMap my_type_map;
47
48/**
49 * Counters for message types this peer is able to handle.
50 */
51static uint8_t map_counters[UINT16_MAX + 1];
52
53/**
54 * Current hash of our (uncompressed) type map.
55 * Lazily computed when needed.
56 */
57static struct GNUNET_HashCode my_tm_hash;
58
59/**
60 * Is #my_tm_hash() current with respect to our type map?
61 */
62static int hash_current;
63
64
65/**
66 * Our type map changed, recompute its hash.
67 */
68static void
69rehash_typemap ()
70{
71 hash_current = GNUNET_NO;
72}
73
74
75/**
76 * Hash the contents of a type map.
77 *
78 * @param tm map to hash
79 * @param hc where to store the hash code
80 */
81void
82GSC_TYPEMAP_hash (const struct GSC_TypeMap *tm, struct GNUNET_HashCode *hc)
83{
84 GNUNET_CRYPTO_hash (tm, sizeof(struct GSC_TypeMap), hc);
85}
86
87
88/**
89 * Check if the given hash matches our current type map.
90 *
91 * @param hc hash code to check if it matches our type map
92 * @return #GNUNET_YES if the hash matches, #GNUNET_NO if not
93 */
94int
95GSC_TYPEMAP_check_hash (const struct GNUNET_HashCode *hc)
96{
97 if (GNUNET_NO == hash_current)
98 {
99 GSC_TYPEMAP_hash (&my_type_map, &my_tm_hash);
100 hash_current = GNUNET_YES;
101 }
102 return (0 == memcmp (hc, &my_tm_hash, sizeof(struct GNUNET_HashCode)))
103 ? GNUNET_YES
104 : GNUNET_NO;
105}
106
107
108/**
109 * Compute a type map message for this peer.
110 *
111 * @return this peers current type map message.
112 */
113struct GNUNET_MessageHeader *
114GSC_TYPEMAP_compute_type_map_message ()
115{
116 char *tmp;
117 uLongf dlen;
118 struct GNUNET_MessageHeader *hdr;
119
120#ifdef compressBound
121 dlen = compressBound (sizeof(my_type_map));
122#else
123 dlen = sizeof(my_type_map) + (sizeof(my_type_map) / 100) + 20;
124 /* documentation says 100.1% oldSize + 12 bytes, but we
125 * should be able to overshoot by more to be safe */
126#endif
127 hdr = GNUNET_malloc (dlen + sizeof(struct GNUNET_MessageHeader));
128 tmp = (char *) &hdr[1];
129 if ((Z_OK != compress2 ((Bytef *) tmp,
130 &dlen,
131 (const Bytef *) &my_type_map,
132 sizeof(my_type_map),
133 9)) ||
134 (dlen >= sizeof(my_type_map)))
135 {
136 /* compression failed, use uncompressed map */
137 dlen = sizeof(my_type_map);
138 GNUNET_memcpy (tmp, &my_type_map, sizeof(my_type_map));
139 hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP);
140 }
141 else
142 {
143 /* compression worked, use compressed map */
144 hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP);
145 }
146 hdr->size = htons ((uint16_t) dlen + sizeof(struct GNUNET_MessageHeader));
147 return hdr;
148}
149
150
151/**
152 * Extract a type map from a TYPE_MAP message.
153 *
154 * @param msg a type map message
155 * @return NULL on error
156 */
157struct GSC_TypeMap *
158GSC_TYPEMAP_get_from_message (const struct GNUNET_MessageHeader *msg)
159{
160 struct GSC_TypeMap *ret;
161 uint16_t size;
162 uLongf dlen;
163
164 size = ntohs (msg->size);
165 switch (ntohs (msg->type))
166 {
167 case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP:
168 GNUNET_STATISTICS_update (GSC_stats,
169 gettext_noop ("# type maps received"),
170 1,
171 GNUNET_NO);
172 if (size != sizeof(struct GSC_TypeMap))
173 {
174 GNUNET_break_op (0);
175 return NULL;
176 }
177 ret = GNUNET_new (struct GSC_TypeMap);
178 GNUNET_memcpy (ret, &msg[1], sizeof(struct GSC_TypeMap));
179 return ret;
180
181 case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP:
182 GNUNET_STATISTICS_update (GSC_stats,
183 gettext_noop ("# type maps received"),
184 1,
185 GNUNET_NO);
186 ret = GNUNET_new (struct GSC_TypeMap);
187 dlen = sizeof(struct GSC_TypeMap);
188 if ((Z_OK != uncompress ((Bytef *) ret,
189 &dlen,
190 (const Bytef *) &msg[1],
191 (uLong) size)) ||
192 (dlen != sizeof(struct GSC_TypeMap)))
193 {
194 GNUNET_break_op (0);
195 GNUNET_free (ret);
196 return NULL;
197 }
198 return ret;
199
200 default:
201 GNUNET_break (0);
202 return NULL;
203 }
204}
205
206
207/**
208 * Send my type map to all connected peers (it got changed).
209 */
210static void
211broadcast_my_type_map ()
212{
213 struct GNUNET_MessageHeader *hdr;
214
215 hdr = GSC_TYPEMAP_compute_type_map_message ();
216 GNUNET_STATISTICS_update (GSC_stats,
217 gettext_noop ("# updates to my type map"),
218 1,
219 GNUNET_NO);
220 GSC_SESSIONS_broadcast_typemap (hdr);
221 GNUNET_free (hdr);
222}
223
224
225/**
226 * Add a set of types to our type map.
227 *
228 * @param types array of message types supported by this peer
229 * @param tlen number of entries in @a types
230 */
231void
232GSC_TYPEMAP_add (const uint16_t *types, unsigned int tlen)
233{
234 unsigned int i;
235 int changed;
236
237 changed = GNUNET_NO;
238 for (i = 0; i < tlen; i++)
239 {
240 if (0 == map_counters[types[i]]++)
241 {
242 my_type_map.bits[types[i] / 32] |= (1 << (types[i] % 32));
243 changed = GNUNET_YES;
244 }
245 }
246 if (GNUNET_YES == changed)
247 {
248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Typemap changed, broadcasting!\n");
249 rehash_typemap ();
250 broadcast_my_type_map ();
251 }
252}
253
254
255void
256GSC_TYPEMAP_remove (const uint16_t *types, unsigned int tlen)
257{
258 int changed;
259
260 changed = GNUNET_NO;
261 for (unsigned int i = 0; i < tlen; i++)
262 {
263 if (0 == --map_counters[types[i]])
264 {
265 my_type_map.bits[types[i] / 32] &= ~(1 << (types[i] % 32));
266 changed = GNUNET_YES;
267 }
268 }
269 if (GNUNET_YES == changed)
270 {
271 rehash_typemap ();
272 broadcast_my_type_map ();
273 }
274}
275
276
277/**
278 * Test if any of the types from the types array is in the
279 * given type map.
280 *
281 * @param tmap map to test
282 * @param types array of types
283 * @param tcnt number of entries in @a types
284 * @return #GNUNET_YES if a type is in the map, #GNUNET_NO if not
285 */
286int
287GSC_TYPEMAP_test_match (const struct GSC_TypeMap *tmap,
288 const uint16_t *types,
289 unsigned int tcnt)
290{
291 if (NULL == tmap)
292 return GNUNET_NO;
293 if (0 == tcnt)
294 return GNUNET_YES; /* matches all */
295 for (unsigned int i = 0; i < tcnt; i++)
296 if (0 != (tmap->bits[types[i] / 32] & (1 << (types[i] % 32))))
297 return GNUNET_YES;
298 return GNUNET_NO;
299}
300
301
302/**
303 * Add additional types to a given typemap.
304 *
305 * @param tmap map to extend (not changed)
306 * @param types array of types to add
307 * @param tcnt number of entries in @a types
308 * @return updated type map (fresh copy)
309 */
310struct GSC_TypeMap *
311GSC_TYPEMAP_extend (const struct GSC_TypeMap *tmap,
312 const uint16_t *types,
313 unsigned int tcnt)
314{
315 struct GSC_TypeMap *ret;
316
317 ret = GNUNET_new (struct GSC_TypeMap);
318 if (NULL != tmap)
319 GNUNET_memcpy (ret, tmap, sizeof(struct GSC_TypeMap));
320 for (unsigned int i = 0; i < tcnt; i++)
321 ret->bits[types[i] / 32] |= (1 << (types[i] % 32));
322 return ret;
323}
324
325
326/**
327 * Create an empty type map.
328 *
329 * @return an empty type map
330 */
331struct GSC_TypeMap *
332GSC_TYPEMAP_create ()
333{
334 return GNUNET_new (struct GSC_TypeMap);
335}
336
337
338/**
339 * Free the given type map.
340 *
341 * @param tmap a type map
342 */
343void
344GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap)
345{
346 GNUNET_free (tmap);
347}
348
349
350/**
351 * Initialize typemap subsystem.
352 */
353void
354GSC_TYPEMAP_init ()
355{
356 /* nothing to do */
357}
358
359
360/**
361 * Shutdown typemap subsystem.
362 */
363void
364GSC_TYPEMAP_done ()
365{
366 /* nothing to do */
367}
368
369
370/* end of gnunet-service-core_typemap.c */
diff --git a/src/service/core/gnunet-service-core_typemap.h b/src/service/core/gnunet-service-core_typemap.h
new file mode 100644
index 000000000..de41f4220
--- /dev/null
+++ b/src/service/core/gnunet-service-core_typemap.h
@@ -0,0 +1,162 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/gnunet-service-core_typemap.h
23 * @brief management of map that specifies which message types this peer supports
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_CORE_TYPEMAP_H
27#define GNUNET_SERVICE_CORE_TYPEMAP_H
28
29#include "gnunet_util_lib.h"
30
31/**
32 * Map specifying which message types a peer supports.
33 */
34struct GSC_TypeMap;
35
36
37/**
38 * Add a set of types to our type map.
39 *
40 * @param types array of message types supported by this peer
41 * @param tlen number of entries in @a types
42 */
43void
44GSC_TYPEMAP_add (const uint16_t *types,
45 unsigned int tlen);
46
47
48/**
49 * Remove a set of message types from our type map.
50 *
51 * @param types array of message types no longer supported by this peer
52 * @param tlen number of entries in @a types
53 */
54void
55GSC_TYPEMAP_remove (const uint16_t *types,
56 unsigned int tlen);
57
58
59/**
60 * Compute a type map message for this peer.
61 *
62 * @return this peers current type map message.
63 */
64struct GNUNET_MessageHeader *
65GSC_TYPEMAP_compute_type_map_message (void);
66
67
68/**
69 * Check if the given hash matches our current type map.
70 *
71 * @param hc hash code to check if it matches our type map
72 * @return #GNUNET_YES if the hash matches, #GNUNET_NO if not
73 */
74int
75GSC_TYPEMAP_check_hash (const struct GNUNET_HashCode *hc);
76
77
78/**
79 * Hash the contents of a type map.
80 *
81 * @param tm map to hash
82 * @param hc where to store the hash code
83 */
84void
85GSC_TYPEMAP_hash (const struct GSC_TypeMap *tm,
86 struct GNUNET_HashCode *hc);
87
88
89/**
90 * Extract a type map from a
91 * #GNUNET_MESSAGE_TYPE_CORE_COMRESSED_TYPE_MAP or
92 * #GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP message.
93 *
94 * @param msg a type map message
95 * @return NULL on error
96 */
97struct GSC_TypeMap *
98GSC_TYPEMAP_get_from_message (const struct GNUNET_MessageHeader *msg);
99
100
101/**
102 * Test if any of the types from the types array is in the
103 * given type map.
104 *
105 * @param tmap map to test
106 * @param types array of types
107 * @param tcnt number of entries in @a types
108 * @return #GNUNET_YES if a type is in the map, #GNUNET_NO if not
109 */
110int
111GSC_TYPEMAP_test_match (const struct GSC_TypeMap *tmap,
112 const uint16_t *types,
113 unsigned int tcnt);
114
115
116/**
117 * Add additional types to a given typemap.
118 *
119 * @param tmap map to extend (not changed)
120 * @param types array of types to add
121 * @param tcnt number of entries in @a types
122 * @return updated type map (fresh copy)
123 */
124struct GSC_TypeMap *
125GSC_TYPEMAP_extend (const struct GSC_TypeMap *tmap,
126 const uint16_t *types,
127 unsigned int tcnt);
128
129
130/**
131 * Create an empty type map.
132 *
133 * @return an empty type map
134 */
135struct GSC_TypeMap *
136GSC_TYPEMAP_create (void);
137
138
139/**
140 * Free the given type map.
141 *
142 * @param tmap a type map
143 */
144void
145GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap);
146
147
148/**
149 * Initialize typemap subsystem.
150 */
151void
152GSC_TYPEMAP_init (void);
153
154
155/**
156 * Shutdown typemap subsystem.
157 */
158void
159GSC_TYPEMAP_done (void);
160
161#endif
162/* end of gnunet-service-core_typemap.h */
diff --git a/src/service/core/meson.build b/src/service/core/meson.build
new file mode 100644
index 000000000..2e3e958af
--- /dev/null
+++ b/src/service/core/meson.build
@@ -0,0 +1,119 @@
1libgnunetcore_src = ['core_api.c',
2 'core_api_monitor_peers.c']
3
4gnunetservicecore_src = ['gnunet-service-core.c',
5 'gnunet-service-core_kx.c',
6 'gnunet-service-core_sessions.c',
7 'gnunet-service-core_typemap.c']
8
9configure_file(input : 'core.conf.in',
10 output : 'core.conf',
11 configuration : cdata,
12 install: true,
13 install_dir: pkgcfgdir)
14
15
16if get_option('monolith')
17 foreach p : libgnunetcore_src + gnunetservicecore_src
18 gnunet_src += 'core/' + p
19 endforeach
20 subdir_done()
21endif
22
23libgnunetcore = library('gnunetcore',
24 libgnunetcore_src,
25 dependencies: libgnunetutil_dep,
26 include_directories: [incdir, configuration_inc],
27 install: true,
28 soversion: '0',
29 version: '0.0.1',
30 install_dir: get_option('libdir'))
31libgnunetcore_dep = declare_dependency(link_with : libgnunetcore)
32pkg.generate(libgnunetcore, url: 'https://www.gnunet.org',
33 description : 'Provides API for (encrypted) P2P communication')
34
35libgnunetcoretesting = library('gnunetcoretesting',
36 ['core_api_cmd_connecting_peers.c'],
37 dependencies: [
38 libgnunetutil_dep,
39 libgnunettesting_dep,
40 libgnunetarm_dep,
41 libgnunettransportapplication_dep,
42 libgnunettransportcore_dep,
43 ],
44 include_directories: [incdir, configuration_inc],
45 install: true,
46 soversion: '0',
47 version: '0.0.0',
48 install_dir: get_option('libdir'))
49libgnunetcoretesting_dep = declare_dependency(link_with : libgnunetcoretesting)
50
51shared_module('gnunet_test_core_plugin_cmd_just_run',
52 ['test_core_plugin_cmd_just_run.c'],
53 dependencies: [libgnunetutil_dep,
54 libgnunetcoretesting_dep,
55 libgnunettransportcore_dep,
56 libgnunettransportapplication_dep,
57 libgnunettesting_dep,
58 libgnunetpeerstore_dep,
59 libgnunetstatistics_dep,
60 libgnunethello_dep,
61 libgnunetarm_dep
62 ],
63 include_directories: [incdir, configuration_inc],
64 install: true,
65 install_dir: get_option('libdir')/'gnunet')
66
67executable ('gnunet-core',
68 ['gnunet-core.c'],
69 dependencies: [libgnunetcore_dep, libgnunetutil_dep],
70 include_directories: [incdir, configuration_inc],
71 install: true,
72 install_dir: get_option('bindir'))
73
74executable ('gnunet-service-core',
75 gnunetservicecore_src,
76 dependencies: [libgnunetcore_dep, libgnunetutil_dep,
77 libgnunetstatistics_dep,
78 libgnunettransportcore_dep,
79 zlib_dep],
80 include_directories: [incdir, configuration_inc],
81 install: true,
82 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
83
84configure_file(input : 'test_core_defaults.conf',
85 output : 'test_core_defaults.conf',
86 copy: true)
87
88configure_file(input : 'test_core_api_send_to_self.conf',
89 output : 'test_core_api_send_to_self.conf',
90 copy: true)
91
92configure_file(input : 'test_core_api_peer1.conf',
93 output : 'test_core_api_peer1.conf',
94 copy: true)
95
96testcore_api_send_self = executable ('test_core_api_send_to_self',
97 ['test_core_api_send_to_self.c'],
98 dependencies: [
99 libgnunetcore_dep,
100 libgnunetutil_dep,
101 libgnunettesting_dep
102 ],
103 include_directories: [incdir, configuration_inc],
104 install: false)
105
106testcore_api_start = executable ('test_core_api_start_only',
107 ['test_core_api_send_to_self.c'],
108 dependencies: [
109 libgnunetcore_dep,
110 libgnunetutil_dep,
111 libgnunettesting_dep
112 ],
113 include_directories: [incdir, configuration_inc],
114 install: false)
115
116test('test_core_api_send_to_self', testcore_api_send_self,
117 suite: 'core', workdir: meson.current_build_dir())
118test('test_core_api_start_only', testcore_api_start,
119 suite: 'core', workdir: meson.current_build_dir())
diff --git a/src/service/core/test_core_api.c b/src/service/core/test_core_api.c
new file mode 100644
index 000000000..653ce1aa0
--- /dev/null
+++ b/src/service/core/test_core_api.c
@@ -0,0 +1,348 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file core/test_core_api.c
22 * @brief testcase for core_api.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_arm_service.h"
27#include "gnunet_core_service.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_transport_service.h"
30#include "gnunet_transport_hello_service.h"
31#include "gnunet_ats_service.h"
32
33#define MTYPE 12345
34
35struct PeerContext
36{
37 struct GNUNET_CONFIGURATION_Handle *cfg;
38 struct GNUNET_CORE_Handle *ch;
39 struct GNUNET_PeerIdentity id;
40 struct GNUNET_TRANSPORT_OfferHelloHandle *oh;
41 struct GNUNET_TRANSPORT_HelloGetHandle *ghh;
42 struct GNUNET_ATS_ConnectivityHandle *ats;
43 struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh;
44 struct GNUNET_MessageHeader *hello;
45 int connect_status;
46 struct GNUNET_OS_Process *arm_proc;
47};
48
49static struct PeerContext p1;
50
51static struct PeerContext p2;
52
53static struct GNUNET_SCHEDULER_Task *err_task;
54
55static int ok;
56
57#define OKPP \
58 do \
59 { \
60 ok++; \
61 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, \
62 "Now at stage %u at %s:%u\n", \
63 ok, \
64 __FILE__, \
65 __LINE__); \
66 } while (0)
67
68
69static void
70offer_hello_done (void *cls)
71{
72 struct PeerContext *p = cls;
73
74 p->oh = NULL;
75}
76
77
78static void
79process_hello (void *cls, const struct GNUNET_MessageHeader *message)
80{
81 struct PeerContext *p = cls;
82
83 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
84 "Received (my) HELLO from transport service\n");
85 GNUNET_assert (message != NULL);
86 if ((p == &p1) && (NULL == p2.oh))
87 p2.oh =
88 GNUNET_TRANSPORT_offer_hello (p2.cfg, message, &offer_hello_done, &p2);
89 if ((p == &p2) && (NULL == p1.oh))
90 p1.oh =
91 GNUNET_TRANSPORT_offer_hello (p1.cfg, message, &offer_hello_done, &p1);
92}
93
94
95static void
96terminate_peer (struct PeerContext *p)
97{
98 if (NULL != p->ch)
99 {
100 GNUNET_CORE_disconnect (p->ch);
101 p->ch = NULL;
102 }
103 if (NULL != p->ghh)
104 {
105 GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
106 p->ghh = NULL;
107 }
108 if (NULL != p->oh)
109 {
110 GNUNET_TRANSPORT_offer_hello_cancel (p->oh);
111 p->oh = NULL;
112 }
113 if (NULL != p->ats_sh)
114 {
115 GNUNET_ATS_connectivity_suggest_cancel (p->ats_sh);
116 p->ats_sh = NULL;
117 }
118 if (NULL != p->ats)
119 {
120 GNUNET_ATS_connectivity_done (p->ats);
121 p->ats = NULL;
122 }
123}
124
125
126static void
127terminate_task (void *cls)
128{
129 GNUNET_assert (ok == 6);
130 terminate_peer (&p1);
131 terminate_peer (&p2);
132 ok = 0;
133}
134
135
136static void
137terminate_task_error (void *cls)
138{
139 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ENDING ANGRILY %u\n", ok);
140 GNUNET_break (0);
141 terminate_peer (&p1);
142 terminate_peer (&p2);
143 ok = 42;
144}
145
146
147static void *
148connect_notify (void *cls,
149 const struct GNUNET_PeerIdentity *peer,
150 struct GNUNET_MQ_Handle *mq)
151{
152 struct PeerContext *pc = cls;
153 struct GNUNET_MQ_Envelope *env;
154 struct GNUNET_MessageHeader *msg;
155
156 if (0 == memcmp (&pc->id, peer, sizeof(struct GNUNET_PeerIdentity)))
157 return (void *) peer;
158 GNUNET_assert (pc->connect_status == 0);
159 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
160 "Encrypted connection established to peer `%s'\n",
161 GNUNET_i2s (peer));
162 pc->connect_status = 1;
163 if (pc == &p1)
164 {
165 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
166 "Asking core (1) for transmission to peer `%s'\n",
167 GNUNET_i2s (&p2.id));
168 env = GNUNET_MQ_msg (msg, MTYPE);
169 /* enable corking for this test */
170 GNUNET_MQ_env_set_options (env,
171 GNUNET_MQ_PRIO_BEST_EFFORT
172 | GNUNET_MQ_PREF_CORK_ALLOWED);
173 /* now actually transmit message */
174 GNUNET_assert (ok == 4);
175 OKPP;
176 GNUNET_MQ_send (mq, env);
177 }
178 return (void *) peer;
179}
180
181
182static void
183disconnect_notify (void *cls,
184 const struct GNUNET_PeerIdentity *peer,
185 void *internal_cls)
186{
187 struct PeerContext *pc = cls;
188
189 if (0 == memcmp (&pc->id, peer, sizeof(struct GNUNET_PeerIdentity)))
190 return;
191 pc->connect_status = 0;
192 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
193 "Encrypted connection to `%s' cut\n",
194 GNUNET_i2s (peer));
195}
196
197
198static void
199handle_test (void *cls, const struct GNUNET_MessageHeader *message)
200{
201 const struct GNUNET_PeerIdentity *peer = cls;
202
203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204 "Receiving message from `%s'.\n",
205 GNUNET_i2s (peer));
206 GNUNET_assert (ok == 5);
207 OKPP;
208 GNUNET_SCHEDULER_cancel (err_task);
209 err_task = GNUNET_SCHEDULER_add_now (&terminate_task, NULL);
210}
211
212
213static void
214init_notify (void *cls, const struct GNUNET_PeerIdentity *my_identity)
215{
216 struct PeerContext *p = cls;
217 struct GNUNET_MQ_MessageHandler handlers[] =
218 { GNUNET_MQ_hd_fixed_size (test, MTYPE, struct GNUNET_MessageHeader, NULL),
219 GNUNET_MQ_handler_end () };
220
221 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
222 "Core connection to `%s' established\n",
223 GNUNET_i2s (my_identity));
224 p->id = *my_identity;
225 if (cls == &p1)
226 {
227 GNUNET_assert (ok == 2);
228 OKPP;
229 /* connect p2 */
230 p2.ch = GNUNET_CORE_connect (p2.cfg,
231 &p2,
232 &init_notify,
233 &connect_notify,
234 &disconnect_notify,
235 handlers);
236 }
237 else
238 {
239 GNUNET_assert (ok == 3);
240 OKPP;
241 GNUNET_assert (cls == &p2);
242 p1.ats_sh = GNUNET_ATS_connectivity_suggest (p1.ats, &p2.id, 1);
243 }
244}
245
246
247static void
248setup_peer (struct PeerContext *p, const char *cfgname)
249{
250 char *binary;
251
252 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
253 p->cfg = GNUNET_CONFIGURATION_create ();
254 p->arm_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
255 | GNUNET_OS_USE_PIPE_CONTROL,
256 NULL,
257 NULL,
258 NULL,
259 binary,
260 "gnunet-service-arm",
261 "-c",
262 cfgname,
263 NULL);
264 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
265 p->ats = GNUNET_ATS_connectivity_init (p->cfg);
266 GNUNET_assert (NULL != p->ats);
267 p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
268 GNUNET_TRANSPORT_AC_ANY,
269 &process_hello,
270 p);
271 GNUNET_free (binary);
272}
273
274
275static void
276run (void *cls,
277 char *const *args,
278 const char *cfgfile,
279 const struct GNUNET_CONFIGURATION_Handle *cfg)
280{
281 struct GNUNET_MQ_MessageHandler handlers[] =
282 { GNUNET_MQ_hd_fixed_size (test, MTYPE, struct GNUNET_MessageHeader, NULL),
283 GNUNET_MQ_handler_end () };
284
285 GNUNET_assert (ok == 1);
286 OKPP;
287 setup_peer (&p1, "test_core_api_peer1.conf");
288 setup_peer (&p2, "test_core_api_peer2.conf");
289 err_task = GNUNET_SCHEDULER_add_delayed (
290 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300),
291 &terminate_task_error,
292 NULL);
293 p1.ch = GNUNET_CORE_connect (p1.cfg,
294 &p1,
295 &init_notify,
296 &connect_notify,
297 &disconnect_notify,
298 handlers);
299}
300
301
302static void
303stop_arm (struct PeerContext *p)
304{
305 if (0 != GNUNET_OS_process_kill (p->arm_proc, GNUNET_TERM_SIG))
306 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
307 if (GNUNET_OK != GNUNET_OS_process_wait (p->arm_proc))
308 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310 "ARM process %u stopped\n",
311 GNUNET_OS_process_get_pid (p->arm_proc));
312 GNUNET_OS_process_destroy (p->arm_proc);
313 p->arm_proc = NULL;
314 GNUNET_CONFIGURATION_destroy (p->cfg);
315}
316
317
318int
319main (int argc, char *argv1[])
320{
321 char *const argv[] = { "test-core-api", "-c", "test_core_api_data.conf",
322 NULL };
323 struct GNUNET_GETOPT_CommandLineOption options[] =
324 { GNUNET_GETOPT_OPTION_END };
325
326 ok = 1;
327 GNUNET_log_setup ("test-core-api", "WARNING", NULL);
328 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
329 argv,
330 "test-core-api",
331 "nohelp",
332 options,
333 &run,
334 &ok);
335 stop_arm (&p1);
336 stop_arm (&p2);
337 GNUNET_DISK_purge_cfg_dir
338 ("test_core_api_peer1.conf",
339 "GNUNET_TEST_HOME");
340 GNUNET_DISK_purge_cfg_dir
341 ("test_core_api_peer2.conf",
342 "GNUNET_TEST_HOME");
343
344 return ok;
345}
346
347
348/* end of test_core_api.c */
diff --git a/src/service/core/test_core_api_data.conf b/src/service/core/test_core_api_data.conf
new file mode 100644
index 000000000..420849ba9
--- /dev/null
+++ b/src/service/core/test_core_api_data.conf
@@ -0,0 +1,11 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3
4[ats]
5WAN_QUOTA_IN = 64 kiB
6WAN_QUOTA_OUT = 64 kiB
7
8[core]
9PORT = 52092
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-core.sock
11
diff --git a/src/service/core/test_core_api_peer1.conf b/src/service/core/test_core_api_peer1.conf
new file mode 100644
index 000000000..28463effe
--- /dev/null
+++ b/src/service/core/test_core_api_peer1.conf
@@ -0,0 +1,42 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-1/
4
5[arm]
6PORT = 12460
7UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock
8
9[statistics]
10PORT = 12461
11
12[resolver]
13PORT = 12462
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock
15
16[peerinfo]
17PORT = 12463
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock
19
20[transport]
21PORT = 12464
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock
23
24[core]
25PORT = 12475
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-core.sock
27
28[ats]
29PORT = 12476
30UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock
31
32[transport-tcp]
33PORT = 12467
34
35[transport-udp]
36PORT = 12468
37
38[transport-unix]
39PORT = 12469
40
41[transport-http]
42PORT = 12470
diff --git a/src/service/core/test_core_api_peer2.conf b/src/service/core/test_core_api_peer2.conf
new file mode 100644
index 000000000..819d58d17
--- /dev/null
+++ b/src/service/core/test_core_api_peer2.conf
@@ -0,0 +1,43 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-2/
4
5[arm]
6PORT = 22460
7UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock
8
9[statistics]
10PORT = 22461
11UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock
12
13[resolver]
14PORT = 22462
15UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock
16
17[peerinfo]
18PORT = 22463
19UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock
20
21[transport]
22PORT = 22464
23UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock
24
25[core]
26PORT = 22475
27UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-core.sock
28
29[ats]
30PORT = 22476
31UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock
32
33[transport-tcp]
34PORT = 22467
35
36[transport-udp]
37PORT = 22468
38
39[transport-unix]
40PORT = 22469
41
42[transport-http]
43PORT = 22470
diff --git a/src/service/core/test_core_api_reliability.c b/src/service/core/test_core_api_reliability.c
new file mode 100644
index 000000000..d4b55a6b8
--- /dev/null
+++ b/src/service/core/test_core_api_reliability.c
@@ -0,0 +1,537 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file core/test_core_api_reliability.c
22 * @brief testcase for core_api.c focusing on reliable transmission (with TCP)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_arm_service.h"
27#include "gnunet_core_service.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_ats_service.h"
30#include "gnunet_transport_service.h"
31#include "gnunet_transport_hello_service.h"
32#include <gauger.h>
33
34/**
35 * Note that this value must not significantly exceed
36 * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
37 * messages may be dropped even for a reliable transport.
38 */
39#define TOTAL_MSGS (600 * 10)
40
41/**
42 * How long until we give up on transmitting the message?
43 */
44#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
45
46#define MTYPE 12345
47
48
49static unsigned long long total_bytes;
50
51static struct GNUNET_TIME_Absolute start_time;
52
53static struct GNUNET_SCHEDULER_Task *err_task;
54
55
56struct PeerContext
57{
58 struct GNUNET_CONFIGURATION_Handle *cfg;
59 struct GNUNET_CORE_Handle *ch;
60 struct GNUNET_MQ_Handle *mq;
61 struct GNUNET_PeerIdentity id;
62 struct GNUNET_TRANSPORT_OfferHelloHandle *oh;
63 struct GNUNET_MessageHeader *hello;
64 struct GNUNET_TRANSPORT_HelloGetHandle *ghh;
65 struct GNUNET_ATS_ConnectivityHandle *ats;
66 struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh;
67 int connect_status;
68 struct GNUNET_OS_Process *arm_proc;
69};
70
71static struct PeerContext p1;
72
73static struct PeerContext p2;
74
75static int ok;
76
77static int32_t tr_n;
78
79
80#define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, \
81 "Now at stage %u at %s:%u\n", ok, __FILE__, \
82 __LINE__); } while (0)
83
84struct TestMessage
85{
86 struct GNUNET_MessageHeader header;
87 uint32_t num GNUNET_PACKED;
88};
89
90
91static unsigned int
92get_size (unsigned int iter)
93{
94 unsigned int ret;
95
96 if (iter < 60000)
97 return iter + sizeof(struct TestMessage);
98 ret = (iter * iter * iter);
99 return sizeof(struct TestMessage) + (ret % 60000);
100}
101
102
103static void
104terminate_peer (struct PeerContext *p)
105{
106 if (NULL != p->ch)
107 {
108 GNUNET_CORE_disconnect (p->ch);
109 p->ch = NULL;
110 }
111 if (NULL != p->ghh)
112 {
113 GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
114 p->ghh = NULL;
115 }
116 if (NULL != p->oh)
117 {
118 GNUNET_TRANSPORT_offer_hello_cancel (p->oh);
119 p->oh = NULL;
120 }
121 if (NULL != p->ats_sh)
122 {
123 GNUNET_ATS_connectivity_suggest_cancel (p->ats_sh);
124 p->ats_sh = NULL;
125 }
126 if (NULL != p->ats)
127 {
128 GNUNET_ATS_connectivity_done (p->ats);
129 p->ats = NULL;
130 }
131}
132
133
134static void
135terminate_task_error (void *cls)
136{
137 err_task = NULL;
138 GNUNET_break (0);
139 GNUNET_SCHEDULER_shutdown ();
140 ok = 42;
141}
142
143
144static void
145do_shutdown (void *cls)
146{
147 unsigned long long delta;
148
149 delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us;
150 if (0 == delta)
151 delta = 1;
152 fprintf (stderr,
153 "\nThroughput was %llu kb/s\n",
154 total_bytes * 1000000LL / 1024 / delta);
155 GAUGER ("CORE",
156 "Core throughput/s",
157 total_bytes * 1000000LL / 1024 / delta,
158 "kb/s");
159 if (NULL != err_task)
160 {
161 GNUNET_SCHEDULER_cancel (err_task);
162 err_task = NULL;
163 }
164 terminate_peer (&p1);
165 terminate_peer (&p2);
166}
167
168
169static void
170send_message (struct GNUNET_MQ_Handle *mq,
171 int32_t num)
172{
173 struct GNUNET_MQ_Envelope *env;
174 struct TestMessage *hdr;
175 unsigned int s;
176
177 GNUNET_assert (NULL != mq);
178 GNUNET_assert (tr_n < TOTAL_MSGS);
179 s = get_size (tr_n);
180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
181 "Sending message %u of size %u\n",
182 tr_n,
183 s);
184 env = GNUNET_MQ_msg_extra (hdr,
185 s - sizeof(struct TestMessage),
186 MTYPE);
187 hdr->num = htonl (tr_n);
188 memset (&hdr[1],
189 tr_n,
190 s - sizeof(struct TestMessage));
191 tr_n++;
192 GNUNET_SCHEDULER_cancel (err_task);
193 err_task =
194 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
195 &terminate_task_error,
196 NULL);
197 total_bytes += s;
198 GNUNET_MQ_send (mq,
199 env);
200}
201
202
203static void *
204connect_notify (void *cls,
205 const struct GNUNET_PeerIdentity *peer,
206 struct GNUNET_MQ_Handle *mq)
207{
208 struct PeerContext *pc = cls;
209
210 if (0 == memcmp (&pc->id,
211 peer,
212 sizeof(struct GNUNET_PeerIdentity)))
213 return (void *) peer;
214 pc->mq = mq;
215 GNUNET_assert (0 == pc->connect_status);
216 pc->connect_status = 1;
217 if (pc == &p1)
218 {
219 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
220 "Encrypted connection established to peer `%s'\n",
221 GNUNET_i2s (peer));
222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
223 "Asking core (1) for transmission to peer `%s'\n",
224 GNUNET_i2s (&p2.id));
225 GNUNET_SCHEDULER_cancel (err_task);
226 err_task =
227 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
228 &terminate_task_error,
229 NULL);
230 start_time = GNUNET_TIME_absolute_get ();
231 send_message (mq,
232 0);
233 }
234 return (void *) peer;
235}
236
237
238static void
239disconnect_notify (void *cls,
240 const struct GNUNET_PeerIdentity *peer,
241 void *internal_cls)
242{
243 struct PeerContext *pc = cls;
244
245 if (0 == memcmp (&pc->id,
246 peer,
247 sizeof(struct GNUNET_PeerIdentity)))
248 return;
249 pc->mq = NULL;
250 pc->connect_status = 0;
251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
252 "Encrypted connection to `%s' cut\n",
253 GNUNET_i2s (peer));
254}
255
256
257static int
258check_test (void *cls,
259 const struct TestMessage *hdr)
260{
261 return GNUNET_OK; /* accept all */
262}
263
264
265static void
266handle_test (void *cls,
267 const struct TestMessage *hdr)
268{
269 static int n;
270 unsigned int s;
271
272 s = get_size (n);
273 if (ntohs (hdr->header.size) != s)
274 {
275 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
276 "Expected message %u of size %u, got %u bytes of message %u\n",
277 n,
278 s,
279 ntohs (hdr->header.size),
280 ntohl (hdr->num));
281 GNUNET_SCHEDULER_cancel (err_task);
282 err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error,
283 NULL);
284 return;
285 }
286 if (ntohl (hdr->num) != n)
287 {
288 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
289 "Expected message %u of size %u, got %u bytes of message %u\n",
290 n,
291 s,
292 (unsigned int) ntohs (hdr->header.size),
293 (unsigned int) ntohl (hdr->num));
294 GNUNET_SCHEDULER_cancel (err_task);
295 err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error,
296 NULL);
297 return;
298 }
299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300 "Got message %u of size %u\n",
301 (unsigned int) ntohl (hdr->num),
302 (unsigned int) ntohs (hdr->header.size));
303 n++;
304 if (0 == (n % (TOTAL_MSGS / 100)))
305 fprintf (stderr,
306 "%s",
307 ".");
308 if (n == TOTAL_MSGS)
309 {
310 ok = 0;
311 GNUNET_SCHEDULER_shutdown ();
312 }
313 else
314 {
315 if (n == tr_n)
316 {
317 send_message (p1.mq,
318 tr_n);
319 }
320 }
321}
322
323
324static void
325init_notify (void *cls,
326 const struct GNUNET_PeerIdentity *my_identity)
327{
328 struct PeerContext *p = cls;
329 struct GNUNET_MQ_MessageHandler handlers[] = {
330 GNUNET_MQ_hd_var_size (test,
331 MTYPE,
332 struct TestMessage,
333 NULL),
334 GNUNET_MQ_handler_end ()
335 };
336
337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338 "Connection to CORE service of `%s' established\n",
339 GNUNET_i2s (my_identity));
340 p->id = *my_identity;
341 if (cls == &p1)
342 {
343 GNUNET_assert (ok == 2);
344 OKPP;
345 /* connect p2 */
346 GNUNET_assert (NULL !=
347 (p2.ch = GNUNET_CORE_connect (p2.cfg,
348 &p2,
349 &init_notify,
350 &connect_notify,
351 &disconnect_notify,
352 handlers)));
353 }
354 else
355 {
356 GNUNET_assert (ok == 3);
357 OKPP;
358 GNUNET_assert (cls == &p2);
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Asking transport (1) to connect to peer `%s'\n",
361 GNUNET_i2s (&p2.id));
362 p1.ats_sh = GNUNET_ATS_connectivity_suggest (p1.ats,
363 &p2.id,
364 1);
365 }
366}
367
368
369static void
370offer_hello_done (void *cls)
371{
372 struct PeerContext *p = cls;
373
374 p->oh = NULL;
375}
376
377
378static void
379process_hello (void *cls,
380 const struct GNUNET_MessageHeader *message)
381{
382 struct PeerContext *p = cls;
383
384 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385 "Received (my) `%s' from transport service\n", "HELLO");
386 GNUNET_assert (message != NULL);
387 GNUNET_free (p->hello);
388 p->hello = GNUNET_copy_message (message);
389 if ((p == &p1) && (NULL == p2.oh))
390 p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg,
391 message,
392 &offer_hello_done,
393 &p2);
394 if ((p == &p2) && (NULL == p1.oh))
395 p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg,
396 message,
397 &offer_hello_done,
398 &p1);
399
400 if ((p == &p1) && (p2.hello != NULL) && (NULL == p1.oh))
401 p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg,
402 p2.hello,
403 &offer_hello_done,
404 &p1);
405 if ((p == &p2) && (p1.hello != NULL) && (NULL == p2.oh))
406 p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg,
407 p1.hello,
408 &offer_hello_done,
409 &p2);
410}
411
412
413static void
414setup_peer (struct PeerContext *p,
415 const char *cfgname)
416{
417 char *binary;
418
419 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
420 p->cfg = GNUNET_CONFIGURATION_create ();
421 p->arm_proc
422 = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
423 | GNUNET_OS_USE_PIPE_CONTROL,
424 NULL, NULL, NULL,
425 binary,
426 "gnunet-service-arm",
427 "-c",
428 cfgname,
429 NULL);
430 GNUNET_assert (GNUNET_OK ==
431 GNUNET_CONFIGURATION_load (p->cfg,
432 cfgname));
433 p->ats = GNUNET_ATS_connectivity_init (p->cfg);
434 GNUNET_assert (NULL != p->ats);
435 p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
436 GNUNET_TRANSPORT_AC_ANY,
437 &process_hello,
438 p);
439 GNUNET_free (binary);
440}
441
442
443static void
444run (void *cls,
445 char *const *args,
446 const char *cfgfile,
447 const struct GNUNET_CONFIGURATION_Handle *cfg)
448{
449 struct GNUNET_MQ_MessageHandler handlers[] = {
450 GNUNET_MQ_hd_fixed_size (test,
451 MTYPE,
452 struct TestMessage,
453 NULL),
454 GNUNET_MQ_handler_end ()
455 };
456
457 GNUNET_assert (ok == 1);
458 OKPP;
459 setup_peer (&p1,
460 "test_core_api_peer1.conf");
461 setup_peer (&p2,
462 "test_core_api_peer2.conf");
463 err_task =
464 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
465 &terminate_task_error,
466 NULL);
467 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
468 NULL);
469
470 GNUNET_assert (NULL !=
471 (p1.ch = GNUNET_CORE_connect (p1.cfg,
472 &p1,
473 &init_notify,
474 &connect_notify,
475 &disconnect_notify,
476 handlers)));
477}
478
479
480static void
481stop_arm (struct PeerContext *p)
482{
483 if (0 != GNUNET_OS_process_kill (p->arm_proc,
484 GNUNET_TERM_SIG))
485 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
486 "kill");
487 if (GNUNET_OK != GNUNET_OS_process_wait (p->arm_proc))
488 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
489 "waitpid");
490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491 "ARM process %u stopped\n",
492 GNUNET_OS_process_get_pid (p->arm_proc));
493 GNUNET_OS_process_destroy (p->arm_proc);
494 p->arm_proc = NULL;
495 GNUNET_CONFIGURATION_destroy (p->cfg);
496}
497
498
499int
500main (int argc,
501 char *argv1[])
502{
503 char *const argv[] = {
504 "test-core-api-reliability",
505 "-c",
506 "test_core_api_data.conf",
507 NULL
508 };
509 struct GNUNET_GETOPT_CommandLineOption options[] = {
510 GNUNET_GETOPT_OPTION_END
511 };
512
513 ok = 1;
514 GNUNET_log_setup ("test-core-api-reliability",
515 "WARNING",
516 NULL);
517 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
518 argv,
519 "test-core-api-reliability",
520 "nohelp",
521 options,
522 &run,
523 &ok);
524 stop_arm (&p1);
525 stop_arm (&p2);
526 GNUNET_free (p1.hello);
527 GNUNET_free (p2.hello);
528 GNUNET_DISK_purge_cfg_dir ("test_core_api_peer1.conf",
529 "GNUNET_TEST_HOME");
530 GNUNET_DISK_purge_cfg_dir ("test_core_api_peer2.conf",
531 "GNUNET_TEST_HOME");
532
533 return ok;
534}
535
536
537/* end of test_core_api_reliability.c */
diff --git a/src/service/core/test_core_api_send_to_self.c b/src/service/core/test_core_api_send_to_self.c
new file mode 100644
index 000000000..c2e39cd26
--- /dev/null
+++ b/src/service/core/test_core_api_send_to_self.c
@@ -0,0 +1,195 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file core/test_core_api_send_to_self.c
23 * @brief test that sending a message to ourselves via CORE works
24 * @author Philipp Toelke
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testing_lib.h"
30#include "gnunet_protocols.h"
31#include "gnunet_core_service.h"
32#include "gnunet_constants.h"
33
34/**
35 * Final status code.
36 */
37static int ret;
38
39/**
40 * Handle to the cleanup task.
41 */
42static struct GNUNET_SCHEDULER_Task *die_task;
43
44/**
45 * Identity of this peer.
46 */
47static struct GNUNET_PeerIdentity myself;
48
49/**
50 * The handle to core
51 */
52static struct GNUNET_CORE_Handle *core;
53
54
55/**
56 * Function scheduled as very last function, cleans up after us
57 */
58static void
59cleanup (void *cls)
60{
61 if (NULL != die_task)
62 {
63 GNUNET_SCHEDULER_cancel (die_task);
64 die_task = NULL;
65 }
66 if (NULL != core)
67 {
68 GNUNET_CORE_disconnect (core);
69 core = NULL;
70 }
71 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
72 "Ending test.\n");
73}
74
75
76/**
77 * Function scheduled as very last function, cleans up after us
78 */
79static void
80do_timeout (void *cls)
81{
82 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
83 "Test timeout.\n");
84 die_task = NULL;
85 GNUNET_SCHEDULER_shutdown ();
86}
87
88
89static void
90handle_test (void *cls,
91 const struct GNUNET_MessageHeader *message)
92{
93 GNUNET_SCHEDULER_shutdown ();
94 ret = 0;
95}
96
97
98static void
99init (void *cls,
100 const struct GNUNET_PeerIdentity *my_identity)
101{
102 if (NULL == my_identity)
103 {
104 GNUNET_break (0);
105 return;
106 }
107 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
108 "Correctly connected to CORE; we are the peer %s.\n",
109 GNUNET_i2s (my_identity));
110 GNUNET_memcpy (&myself,
111 my_identity,
112 sizeof(struct GNUNET_PeerIdentity));
113}
114
115
116static void *
117connect_cb (void *cls,
118 const struct GNUNET_PeerIdentity *peer,
119 struct GNUNET_MQ_Handle *mq)
120{
121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
122 "Connected to peer %s.\n",
123 GNUNET_i2s (peer));
124 if (0 == memcmp (peer,
125 &myself,
126 sizeof(struct GNUNET_PeerIdentity)))
127 {
128 struct GNUNET_MQ_Envelope *env;
129 struct GNUNET_MessageHeader *msg;
130
131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
132 "Connected to myself; sending message!\n");
133 env = GNUNET_MQ_msg (msg,
134 GNUNET_MESSAGE_TYPE_DUMMY);
135 GNUNET_MQ_send (mq,
136 env);
137 }
138 return NULL;
139}
140
141
142/**
143 * Main function that will be run by the scheduler.
144 *
145 * @param cls closure
146 * @param cfg configuration
147 */
148static void
149run (void *cls,
150 const struct GNUNET_CONFIGURATION_Handle *cfg,
151 struct GNUNET_TESTING_Peer *peer)
152{
153 struct GNUNET_MQ_MessageHandler handlers[] = {
154 GNUNET_MQ_hd_fixed_size (test,
155 GNUNET_MESSAGE_TYPE_DUMMY,
156 struct GNUNET_MessageHeader,
157 NULL),
158 GNUNET_MQ_handler_end ()
159 };
160
161 core =
162 GNUNET_CORE_connect (cfg,
163 NULL,
164 &init,
165 &connect_cb,
166 NULL,
167 handlers);
168 GNUNET_SCHEDULER_add_shutdown (&cleanup,
169 NULL);
170 die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
171 &do_timeout,
172 NULL);
173}
174
175
176/**
177 * The main function to test sending a message to the local peer via core
178 *
179 * @param argc number of arguments from the command line
180 * @param argv command line arguments
181 * @return 0 ok, 1 on error
182 */
183int
184main (int argc, char *argv[])
185{
186 ret = 1;
187 if (0 != GNUNET_TESTING_peer_run ("test-core-api-send-to-self",
188 "test_core_api_peer1.conf",
189 &run, NULL))
190 return 1;
191 return ret;
192}
193
194
195/* end of test_core_api_send_to_self.c */
diff --git a/src/service/core/test_core_api_send_to_self.conf b/src/service/core/test_core_api_send_to_self.conf
new file mode 100644
index 000000000..fe8e69999
--- /dev/null
+++ b/src/service/core/test_core_api_send_to_self.conf
@@ -0,0 +1,19 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-core-api-send-to-self/
4
5[ats]
6WAN_QUOTA_IN = 104857600
7WAN_QUOTA_OUT = 104757600
8
9[test-sts]
10IMMEDIATE_START = YES
11PORT = 59252
12HOSTNAME = localhost
13BINARY = test_core_api_send_to_self
14ACCEPT_FROM = 127.0.0.1;
15ACCEPT_FROM6 = ::1;
16TOTAL_QUOTA_IN = 65536
17TOTAL_QUOTA_OUT = 65536
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-sts.sock
19
diff --git a/src/service/core/test_core_api_start_only.c b/src/service/core/test_core_api_start_only.c
new file mode 100644
index 000000000..e50d3b2ec
--- /dev/null
+++ b/src/service/core/test_core_api_start_only.c
@@ -0,0 +1,258 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
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 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_core_service.h"
29#include "gnunet_util_lib.h"
30
31#define TIMEOUT 5
32
33#define MTYPE 12345
34
35struct PeerContext
36{
37 struct GNUNET_CONFIGURATION_Handle *cfg;
38 struct GNUNET_CORE_Handle *ch;
39 struct GNUNET_PeerIdentity id;
40 struct GNUNET_OS_Process *arm_proc;
41};
42
43static struct PeerContext p1;
44
45static struct PeerContext p2;
46
47static struct GNUNET_SCHEDULER_Task *timeout_task_id;
48
49static int ok;
50
51
52static void *
53connect_notify (void *cls,
54 const struct GNUNET_PeerIdentity *peer,
55 struct GNUNET_MQ_Handle *mq)
56{
57 return NULL;
58}
59
60
61static void
62disconnect_notify (void *cls,
63 const struct GNUNET_PeerIdentity *peer,
64 void *internal_cls)
65{
66}
67
68
69static struct GNUNET_MQ_MessageHandler handlers[] = {
70 GNUNET_MQ_handler_end ()
71};
72
73
74static void
75shutdown_task (void *cls)
76{
77 GNUNET_CORE_disconnect (p1.ch);
78 p1.ch = NULL;
79 GNUNET_CORE_disconnect (p2.ch);
80 p2.ch = NULL;
81 ok = 0;
82}
83
84
85static void
86init_notify (void *cls,
87 const struct GNUNET_PeerIdentity *my_identity)
88{
89 struct PeerContext *p = cls;
90
91 if (p == &p1)
92 {
93 /* connect p2 */
94 p2.ch = GNUNET_CORE_connect (p2.cfg,
95 &p2,
96 &init_notify,
97 &connect_notify,
98 &disconnect_notify,
99 handlers);
100 }
101 else
102 {
103 GNUNET_assert (p == &p2);
104 GNUNET_SCHEDULER_cancel (timeout_task_id);
105 timeout_task_id = NULL;
106 GNUNET_SCHEDULER_add_now (&shutdown_task,
107 NULL);
108 }
109}
110
111
112static void
113setup_peer (struct PeerContext *p,
114 const char *cfgname)
115{
116 char *binary;
117
118 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
119 p->cfg = GNUNET_CONFIGURATION_create ();
120 p->arm_proc =
121 GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
122 | GNUNET_OS_USE_PIPE_CONTROL,
123 NULL, NULL, NULL,
124 binary,
125 "gnunet-service-arm",
126 "-c", cfgname,
127 NULL);
128 GNUNET_assert (GNUNET_OK ==
129 GNUNET_CONFIGURATION_load (p->cfg,
130 cfgname));
131 GNUNET_free (binary);
132}
133
134
135static void
136timeout_task (void *cls)
137{
138 fprintf (stderr,
139 "%s",
140 "Timeout.\n");
141 if (NULL != p1.ch)
142 {
143 GNUNET_CORE_disconnect (p1.ch);
144 p1.ch = NULL;
145 }
146 if (NULL != p2.ch)
147 {
148 GNUNET_CORE_disconnect (p2.ch);
149 p2.ch = NULL;
150 }
151 ok = 42;
152}
153
154
155static void
156run (void *cls,
157 char *const *args,
158 const char *cfgfile,
159 const struct GNUNET_CONFIGURATION_Handle *cfg)
160{
161 GNUNET_assert (ok == 1);
162 ok++;
163 setup_peer (&p1, "test_core_api_peer1.conf");
164 setup_peer (&p2, "test_core_api_peer2.conf");
165 timeout_task_id =
166 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
167 (GNUNET_TIME_UNIT_MINUTES,
168 TIMEOUT),
169 &timeout_task,
170 NULL);
171 p1.ch = GNUNET_CORE_connect (p1.cfg,
172 &p1,
173 &init_notify,
174 &connect_notify,
175 &disconnect_notify,
176 handlers);
177}
178
179
180static void
181stop_arm (struct PeerContext *p)
182{
183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
184 "Stopping peer\n");
185 if (0 != GNUNET_OS_process_kill (p->arm_proc,
186 GNUNET_TERM_SIG))
187 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
188 "kill");
189 if (GNUNET_OK !=
190 GNUNET_OS_process_wait (p->arm_proc))
191 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
192 "waitpid");
193 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
194 "ARM process %u stopped\n",
195 (unsigned int) GNUNET_OS_process_get_pid (p->arm_proc));
196 GNUNET_OS_process_destroy (p->arm_proc);
197 p->arm_proc = NULL;
198 GNUNET_CONFIGURATION_destroy (p->cfg);
199}
200
201
202static int
203check ()
204{
205 char *const argv[] = {
206 "test-core-api-start-only",
207 "-c",
208 "test_core_api_data.conf",
209 NULL
210 };
211 struct GNUNET_GETOPT_CommandLineOption options[] = {
212 GNUNET_GETOPT_OPTION_END
213 };
214
215 GNUNET_DISK_purge_cfg_dir
216 ("test_core_api_peer1.conf",
217 "GNUNET_TEST_HOME");
218 GNUNET_DISK_purge_cfg_dir
219 ("test_core_api_peer2.conf",
220 "GNUNET_TEST_HOME");
221
222 ok = 1;
223 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
224 argv,
225 "test-core-api-start-only",
226 "nohelp",
227 options,
228 &run,
229 &ok);
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231 "Test finished\n");
232 stop_arm (&p1);
233 stop_arm (&p2);
234 return ok;
235}
236
237
238int
239main (int argc,
240 char *argv[])
241{
242 int ret;
243
244 GNUNET_log_setup ("test-core-api-start-only",
245 "WARNING",
246 NULL);
247 ret = check ();
248 GNUNET_DISK_purge_cfg_dir
249 ("test_core_api_peer1.conf",
250 "GNUNET_TEST_HOME");
251 GNUNET_DISK_purge_cfg_dir
252 ("test_core_api_peer2.conf",
253 "GNUNET_TEST_HOME");
254 return ret;
255}
256
257
258/* end of test_core_api_start_only.c */
diff --git a/src/service/core/test_core_defaults.conf b/src/service/core/test_core_defaults.conf
new file mode 100644
index 000000000..7c7dad99e
--- /dev/null
+++ b/src/service/core/test_core_defaults.conf
@@ -0,0 +1,22 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4[PATHS]
5GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core/
6
7[nat]
8ENABLE_UPNP = NO
9
10[ats]
11WAN_QUOTA_IN = 1 GB
12WAN_QUOTA_OUT = 1 GB
13
14[transport-tcp]
15BINDTO = 127.0.0.1
16
17[transport-udp]
18BROADCAST = NO
19
20[peerinfo]
21NO_IO = YES
22
diff --git a/src/service/core/test_core_plugin_cmd_just_run.c b/src/service/core/test_core_plugin_cmd_just_run.c
new file mode 100644
index 000000000..04b65607d
--- /dev/null
+++ b/src/service/core/test_core_plugin_cmd_just_run.c
@@ -0,0 +1,524 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file testbed/plugin_cmd_simple_send.c
23 * @brief a plugin to provide the API for running test cases.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_testing_barrier.h"
28#include "gnunet_testing_netjail_lib.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_transport_application_service.h"
31#include "gnunet_transport_core_service.h"
32#include "gnunet_testing_barrier.h"
33#include "gnunet_core_service.h"
34#include "gnunet_transport_testing_ng_lib.h"
35
36/**
37 * Generic logging shortcut
38 */
39#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
40
41#define BASE_DIR "testdir"
42
43#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
44
45#define MAX_RECEIVED 1000
46
47#define MESSAGE_SIZE 65000
48
49static struct GNUNET_TESTING_Command block_script;
50
51static struct GNUNET_TESTING_Command connect_peers;
52
53static struct GNUNET_TESTING_Command local_prepared;
54
55static struct GNUNET_TESTING_Command start_peer;
56
57static struct GNUNET_TESTING_Interpreter *is;
58
59static struct GNUNET_CONTAINER_MultiPeerMap *senders;
60
61struct TestState
62{
63 /**
64 * Callback to write messages to the master loop.
65 *
66 */
67 GNUNET_TESTING_cmd_helper_write_cb write_message;
68
69 /**
70 * Callback to notify the helper test case has finished.
71 */
72 GNUNET_TESTING_cmd_helper_finish_cb finished_cb;
73
74 /**
75 * The name for a specific test environment directory.
76 *
77 */
78 char *testdir;
79
80 /**
81 * The name for the configuration file of the specific node.
82 *
83 */
84 char *cfgname;
85
86 /**
87 * The complete topology information.
88 */
89 struct GNUNET_TESTING_NetjailTopology *topology;
90};
91
92struct Sender
93{
94 /**
95 * Number of received messages from sender.
96 */
97 unsigned long long num_received;
98
99 /**
100 * Sample mean time the message traveled.
101 */
102 struct GNUNET_TIME_Relative mean_time;
103
104 /**
105 * Time the first message was send.
106 */
107 struct GNUNET_TIME_Absolute time_first;
108};
109
110
111struct GNUNET_TESTING_BarrierList*
112get_waiting_for_barriers ()
113{
114 struct GNUNET_TESTING_BarrierList*barriers;
115 struct GNUNET_TESTING_BarrierListEntry *ble;
116
117 barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList);
118 ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry);
119 ble->barrier_name = "ready-to-connect";
120 ble->expected_reaches = 1;
121 GNUNET_CONTAINER_DLL_insert (barriers->head,
122 barriers->tail,
123 ble);
124
125 ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry);
126 ble->barrier_name = "test-case-finished";
127 ble->expected_reaches = 1;
128 GNUNET_CONTAINER_DLL_insert (barriers->head,
129 barriers->tail,
130 ble);
131 return barriers;
132}
133
134
135/**
136 * Callback to set the flag indicating all peers started. Will be called via the plugin api.
137 *
138 */
139static void
140all_peers_started ()
141{
142}
143
144
145/**
146 * Function called with the final result of the test.
147 *
148 * @param cls the `struct MainParams`
149 * @param rv #GNUNET_OK if the test passed
150 */
151static void
152handle_result (void *cls,
153 enum GNUNET_GenericReturnValue rv)
154{
155 struct TestState *ts = cls;
156
157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
158 "Local test exits with status %d\n",
159 rv);
160
161 ts->finished_cb (rv);
162 GNUNET_free (ts->testdir);
163 GNUNET_free (ts->cfgname);
164 GNUNET_TESTING_free_topology (ts->topology);
165 GNUNET_free (ts);
166}
167
168
169/**
170 * Callback from start peer cmd for signaling a peer got connected.
171 *
172 *
173static void *
174notify_connect (struct GNUNET_TESTING_Interpreter *is,
175 const struct GNUNET_PeerIdentity *peer)
176{
177 const struct ConnectPeersState *cps;
178 const struct GNUNET_TESTING_Command *cmd;
179
180 cmd = GNUNET_TESTING_interpreter_lookup_command (is,
181 "connect-peers");
182 GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd,
183 &cps);
184 void *ret = NULL;
185
186 cps->notify_connect (is,
187 peer);
188 return ret;
189 }*/
190
191
192/**
193 * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api.
194 */
195static void
196all_local_tests_prepared ()
197{
198 const struct GNUNET_TESTING_LocalPreparedState *lfs;
199
200 GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared,
201 &lfs);
202 GNUNET_assert (NULL != &lfs->ac);
203 if (NULL == lfs->ac.cont)
204 GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac);
205 else
206 GNUNET_TESTING_async_finish ((struct
207 GNUNET_TESTING_AsyncContext *) &lfs->ac);
208}
209
210
211static void
212child_completed_callback (void *cls,
213 enum GNUNET_OS_ProcessStatusType type,
214 long unsigned int exit_code)
215{
216
217}
218
219
220/**
221 * Function called to check a message being
222 * received.
223 *
224 */
225static int
226check_encrypted (void *cls, struct GNUNET_MessageHeader *header)
227{
228 return GNUNET_OK;
229}
230
231
232static void
233core_receive_continue (struct GNUNET_PeerIdentity *peer)
234{
235 const struct GNUNET_TESTING_StartPeerState *sps;
236
237 GNUNET_TESTING_get_trait_state (&start_peer,
238 &sps);
239
240 LOG (GNUNET_ERROR_TYPE_DEBUG,
241 "Executing core receive continue\n");
242
243 GNUNET_TRANSPORT_core_receive_continue (sps->th, peer);
244}
245
246
247/*static void
248handle_core (void *cls, struct GNUNET_MessageHeader *header)
249{
250 struct GNUNET_PeerIdentity *peer = cls;
251
252 core_receive_continue (peer);
253 }*/
254
255
256/**
257 * Function called to handle a message being received.
258 *
259 */
260static void
261handle_encrypted (void *cls, struct GNUNET_MessageHeader *header)
262{
263 struct GNUNET_PeerIdentity *peer = cls;
264
265 core_receive_continue (peer);
266}
267
268
269static void
270handle_ephemeral_key (void *cls, struct GNUNET_MessageHeader *header)
271{
272 struct GNUNET_PeerIdentity *peer = cls;
273
274 core_receive_continue (peer);
275}
276
277
278static void
279handle_ping (void *cls, struct GNUNET_MessageHeader *header)
280{
281 struct GNUNET_PeerIdentity *peer = cls;
282
283 core_receive_continue (peer);
284}
285
286
287static void
288handle_pong (void *cls, struct GNUNET_MessageHeader *header)
289{
290 struct GNUNET_PeerIdentity *peer = cls;
291
292 core_receive_continue (peer);
293}
294
295
296/**
297 * Function to start a local test case.
298 *
299 * @param write_message Callback to send a message to the master loop.
300 * @param router_ip Global address of the network namespace.
301 * @param node_ip The IP address of the node.
302 * @param m The number of the node in a network namespace.
303 * @param n The number of the network namespace.
304 * @param local_m The number of nodes in a network namespace.
305 * @param topology_data A file name for the file containing the topology configuration, or a string containing
306 * the topology configuration.
307 * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration,
308 * if read_file is GNUNET_NO the string contains the topology configuration.
309 * @param finish_cb Callback function which writes a message from the helper process running on a netjail
310 * node to the master process * signaling that the test case running on the netjail node finished.
311 * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node.
312 */
313static struct GNUNET_TESTING_Interpreter *
314start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message,
315 const char *router_ip,
316 const char *node_ip,
317 const char *m,
318 const char *n,
319 const char *local_m,
320 const char *topology_data,
321 unsigned int *read_file,
322 GNUNET_TESTING_cmd_helper_finish_cb finished_cb)
323{
324
325 unsigned int n_int;
326 unsigned int m_int;
327 unsigned int local_m_int;
328 unsigned int num;
329 struct TestState *ts = GNUNET_new (struct TestState);
330 struct GNUNET_TESTING_NetjailTopology *topology;
331 unsigned int sscanf_ret = 0;
332 char **argv = NULL;
333 int argc = 0;
334
335 ts->finished_cb = finished_cb;
336 LOG (GNUNET_ERROR_TYPE_ERROR,
337 "n %s m %s\n",
338 n,
339 m);
340
341 if (GNUNET_YES == *read_file)
342 {
343 LOG (GNUNET_ERROR_TYPE_DEBUG,
344 "read from file\n");
345 topology = GNUNET_TESTING_get_topo_from_file (topology_data);
346 }
347 else
348 topology = GNUNET_TESTING_get_topo_from_string (topology_data);
349
350 ts->topology = topology;
351
352 errno = 0;
353 sscanf_ret = sscanf (m, "%u", &m_int);
354 if (errno != 0)
355 {
356 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf");
357 }
358 GNUNET_assert (0 < sscanf_ret);
359 errno = 0;
360 sscanf_ret = sscanf (n, "%u", &n_int);
361 if (errno != 0)
362 {
363 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf");
364 }
365 GNUNET_assert (0 < sscanf_ret);
366 errno = 0;
367 sscanf_ret = sscanf (local_m, "%u", &local_m_int);
368 if (errno != 0)
369 {
370 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf");
371 }
372 GNUNET_assert (0 < sscanf_ret);
373
374 if (0 == n_int)
375 num = m_int;
376 else
377 num = (n_int - 1) * local_m_int + m_int + topology->nodes_x;
378
379 block_script = GNUNET_TESTING_cmd_block_until_external_trigger (
380 "block-script");
381 connect_peers = GNUNET_CORE_cmd_connect_peers ("connect-peers",
382 "start-peer",
383 "system-create",
384 num,
385 topology,
386 0,
387 GNUNET_NO,
388 NULL);
389 local_prepared = GNUNET_TESTING_cmd_local_test_prepared (
390 "local-test-prepared",
391 write_message);
392
393
394 if (1 == m_int)
395 {
396 LOG (GNUNET_ERROR_TYPE_DEBUG,
397 "m_int %u should be 1\n");
398 GNUNET_asprintf (&ts->cfgname,
399 "test_core_just_run_host.conf");
400 }
401 else
402 {
403 LOG (GNUNET_ERROR_TYPE_DEBUG,
404 "m_int %u should not be 1\n");
405 GNUNET_asprintf (&ts->cfgname,
406 "test_core_just_run.conf");
407 }
408
409 LOG (GNUNET_ERROR_TYPE_DEBUG,
410 "plugin cfgname: %s\n",
411 ts->cfgname);
412
413 LOG (GNUNET_ERROR_TYPE_DEBUG,
414 "node ip: %s\n",
415 node_ip);
416
417 GNUNET_asprintf (&ts->testdir,
418 "%s%s%s",
419 BASE_DIR,
420 m,
421 n);
422
423 /*struct GNUNET_MQ_MessageHandler handlers[] = {
424 GNUNET_MQ_hd_fixed_size (ephemeral_key,
425 GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY,
426 struct EphemeralKeyMessage,
427 NULL),
428 GNUNET_MQ_hd_fixed_size (ping,
429 GNUNET_MESSAGE_TYPE_CORE_PING,
430 struct PingMessage,
431 NULL),
432 GNUNET_MQ_hd_fixed_size (pong,
433 GNUNET_MESSAGE_TYPE_CORE_PONG,
434 struct PongMessage,
435 NULL),
436 GNUNET_MQ_handler_end ()
437 };*/
438
439 start_peer = GNUNET_TESTING_cmd_start_peer ("start-peer",
440 "system-create",
441 num,
442 node_ip,
443 ts->cfgname,
444 GNUNET_NO);
445
446 struct GNUNET_TESTING_Command commands[] = {
447 GNUNET_TESTING_cmd_system_create ("system-create",
448 ts->testdir),
449 start_peer,
450 GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached",
451 "ready-to-connect",
452 GNUNET_NO,
453 num,
454 GNUNET_NO,
455 write_message),
456 connect_peers,
457 GNUNET_TESTING_cmd_exec_bash_script ("script",
458 "block.sh",
459 argv,
460 argc,
461 &child_completed_callback),
462 block_script,
463 GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached",
464 "test-case-finished",
465 GNUNET_NO,
466 num,
467 GNUNET_NO,
468 write_message),
469 GNUNET_TESTING_cmd_stop_peer ("stop-peer",
470 "start-peer"),
471 GNUNET_TESTING_cmd_system_destroy ("system-destroy",
472 "system-create"),
473 GNUNET_TESTING_cmd_end ()
474 };
475
476 ts->write_message = write_message;
477
478 is = GNUNET_TESTING_run (commands,
479 TIMEOUT,
480 &handle_result,
481 ts);
482 return is;
483}
484
485
486/**
487 * Entry point for the plugin.
488 *
489 * @param cls NULL
490 * @return the exported block API
491 */
492void *
493libgnunet_test_core_plugin_cmd_just_run_init (void *cls)
494{
495 struct GNUNET_TESTING_PluginFunctions *api;
496
497 GNUNET_log_setup ("simple-send",
498 "DEBUG",
499 NULL);
500
501 api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions);
502 api->start_testcase = &start_testcase;
503 api->get_waiting_for_barriers = get_waiting_for_barriers;
504 return api;
505}
506
507
508/**
509 * Exit point from the plugin.
510 *
511 * @param cls the return value from #libgnunet_test_transport_plugin_just_run_init
512 * @return NULL
513 */
514void *
515libgnunet_test_core_plugin_cmd_just_run_done (void *cls)
516{
517 struct GNUNET_TESTING_PluginFunctions *api = cls;
518
519 GNUNET_free (api);
520 return NULL;
521}
522
523
524/* end of plugin_cmd_simple_send.c */
diff --git a/src/service/core/test_core_quota_asymmetric_recv_limited_peer1.conf b/src/service/core/test_core_quota_asymmetric_recv_limited_peer1.conf
new file mode 100644
index 000000000..766a2e73b
--- /dev/null
+++ b/src/service/core/test_core_quota_asymmetric_recv_limited_peer1.conf
@@ -0,0 +1,54 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-recv-lim-peer-1/
4
5[transport-tcp]
6PORT = 12488
7
8[arm]
9PORT = 12486
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-arm.sock
11
12[statistics]
13PORT = 12487
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-statistics.sock
15
16[resolver]
17PORT = 12484
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-1-service-resolver.sock
19
20[peerinfo]
21PORT = 12489
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-peerinfo.sock
23
24[transport]
25PORT = 12485
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-transport.sock
27
28[transport-udp]
29PORT = 12489
30
31[ats]
32PORT = 12491
33UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-ats.sock
34# UNSPECIFIED
35UNSPECIFIED_QUOTA_IN = 100 MiB
36UNSPECIFIED_QUOTA_OUT = 100 MiB
37# LOOPBACK
38LOOPBACK_QUOTA_IN = 100 MiB
39LOOPBACK_QUOTA_OUT = 100 MiB
40# LAN
41LAN_QUOTA_IN = 100 MiB
42LAN_QUOTA_OUT = 100 MiB
43# WAN
44WAN_QUOTA_IN = 100 MiB
45WAN_QUOTA_OUT = 100 MiB
46# WLAN
47WLAN_QUOTA_IN = 100 MiB
48WLAN_QUOTA_OUT = 100 MiB
49
50
51[core]
52PORT = 12490
53UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-core.sock
54
diff --git a/src/service/core/test_core_quota_asymmetric_recv_limited_peer2.conf b/src/service/core/test_core_quota_asymmetric_recv_limited_peer2.conf
new file mode 100644
index 000000000..30c0bb81f
--- /dev/null
+++ b/src/service/core/test_core_quota_asymmetric_recv_limited_peer2.conf
@@ -0,0 +1,57 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-recv-lim-peer-2/
4
5
6[arm]
7PORT = 22486
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-arm.sock
9
10[statistics]
11PORT = 22487
12UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-statistics.sock
13
14[resolver]
15PORT = 22484
16UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-resolver.sock
17
18[peerinfo]
19PORT = 22489
20UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-peerinfo.sock
21
22[transport]
23PORT = 22485
24UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-transport.sock
25
26[core]
27PORT = 22490
28UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-core.sock
29
30[ats]
31PORT = 22491
32UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-ats.sock
33# UNSPECIFIED
34UNSPECIFIED_QUOTA_IN = 10 MiB
35UNSPECIFIED_QUOTA_OUT = 10 MiB
36# LOOPBACK
37LOOPBACK_QUOTA_IN = 10 MiB
38LOOPBACK_QUOTA_OUT = 10 MiB
39# LAN
40LAN_QUOTA_IN = 10 MiB
41LAN_QUOTA_OUT = 10 MiB
42# WAN
43WAN_QUOTA_IN = 10 MiB
44WAN_QUOTA_OUT = 10 MiB
45# WLAN
46WLAN_QUOTA_IN = 10 MiB
47WLAN_QUOTA_OUT = 10 MiB
48
49[transport-tcp]
50PORT = 22467
51
52[transport-http]
53PORT = 22469
54
55[transport-tcp]
56PORT = 22468
57
diff --git a/src/service/core/test_core_quota_asymmetric_send_limit_peer1.conf b/src/service/core/test_core_quota_asymmetric_send_limit_peer1.conf
new file mode 100644
index 000000000..4a9f483d6
--- /dev/null
+++ b/src/service/core/test_core_quota_asymmetric_send_limit_peer1.conf
@@ -0,0 +1,52 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-send-lim-peer-1/
4
5[transport-tcp]
6PORT = 12488
7
8[transport-udp]
9PORT = 12492
10
11[arm]
12PORT = 12486
13UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-arm.sock
14
15[statistics]
16PORT = 12487
17UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-statistics.sock
18
19[resolver]
20PORT = 12484
21UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-1-service-resolver.sock
22
23[peerinfo]
24PORT = 12489
25UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-peerinfo.sock
26
27[transport]
28PORT = 12485
29UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-transport.sock
30
31[ats]
32PORT = 12491
33UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-ats.sock
34# UNSPECIFIED
35UNSPECIFIED_QUOTA_IN = 10 MiB
36UNSPECIFIED_QUOTA_OUT = 10 MiB
37# LOOPBACK
38LOOPBACK_QUOTA_IN = 10 MiB
39LOOPBACK_QUOTA_OUT = 10 MiB
40# LAN
41LAN_QUOTA_IN = 10 MiB
42LAN_QUOTA_OUT = 10 MiB
43# WAN
44WAN_QUOTA_IN = 10 MiB
45WAN_QUOTA_OUT = 10 MiB
46# WLAN
47WLAN_QUOTA_IN = 10 MiB
48WLAN_QUOTA_OUT = 10 MiB
49
50[core]
51PORT = 12490
52UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-core.sock
diff --git a/src/service/core/test_core_quota_asymmetric_send_limit_peer2.conf b/src/service/core/test_core_quota_asymmetric_send_limit_peer2.conf
new file mode 100644
index 000000000..36434461c
--- /dev/null
+++ b/src/service/core/test_core_quota_asymmetric_send_limit_peer2.conf
@@ -0,0 +1,61 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-send-lim-peer-2/
4
5[arm]
6PORT = 22486
7UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-arm.sock
8
9[statistics]
10PORT = 22487
11UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-statistics.sock
12
13[resolver]
14PORT = 22484
15UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-resolver.sock
16
17[peerinfo]
18PORT = 22489
19UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-peerinfo.sock
20
21[transport]
22PORT = 22485
23UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-transport.sock
24
25[core]
26PORT = 22490
27UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-core.sock
28
29[ats]
30PORT = 22491
31UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-ats.sock
32WAN_QUOTA_IN = 100 MiB
33WAN_QUOTA_OUT = 100 MiB
34
35[ats]
36PORT = 12471
37UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-sym-p1-service-ats.sock
38# UNSPECIFIED
39UNSPECIFIED_QUOTA_IN = 100 MiB
40UNSPECIFIED_QUOTA_OUT = 100 MiB
41# LOOPBACK
42LOOPBACK_QUOTA_IN = 100 MiB
43LOOPBACK_QUOTA_OUT = 100 MiB
44# LAN
45LAN_QUOTA_IN = 100 MiB
46LAN_QUOTA_OUT = 100 MiB
47# WAN
48WAN_QUOTA_IN = 100 MiB
49WAN_QUOTA_OUT = 100 MiB
50# WLAN
51WLAN_QUOTA_IN = 100 MiB
52WLAN_QUOTA_OUT = 100 MiB
53
54[transport-tcp]
55PORT = 22467
56
57[transport-udp]
58PORT = 22468
59
60[transport-http]
61PORT = 22469
diff --git a/src/service/core/test_core_quota_compliance.c b/src/service/core/test_core_quota_compliance.c
new file mode 100644
index 000000000..099c6fa3b
--- /dev/null
+++ b/src/service/core/test_core_quota_compliance.c
@@ -0,0 +1,788 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file core/test_core_quota_compliance.c
22 * @brief testcase for core_api.c focusing quota compliance on core level
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_arm_service.h"
27#include "gnunet_core_service.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_ats_service.h"
30#include "gnunet_transport_service.h"
31#include "gnunet_transport_hello_service.h"
32#include "gnunet_statistics_service.h"
33
34
35#define SYMMETRIC 0
36#define ASYMMETRIC_SEND_LIMITED 1
37#define ASYMMETRIC_RECV_LIMITED 2
38
39/**
40 * Note that this value must not significantly exceed
41 * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
42 * messages may be dropped even for a reliable transport.
43 */
44#define TOTAL_MSGS (60000 * 10)
45
46/**
47 * How long until we give up on transmitting the message?
48 */
49#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
50
51/**
52 * What delay do we request from the core service for transmission?
53 */
54#define FAST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
55 150)
56
57#define MTYPE 12345
58#define MESSAGESIZE (1024 - 8)
59#define MEASUREMENT_LENGTH GNUNET_TIME_relative_multiply ( \
60 GNUNET_TIME_UNIT_SECONDS, 30)
61
62static unsigned long long total_bytes_sent;
63static unsigned long long total_bytes_recv;
64
65static struct GNUNET_TIME_Absolute start_time;
66
67static struct GNUNET_SCHEDULER_Task *err_task;
68
69static struct GNUNET_SCHEDULER_Task *measure_task;
70
71
72struct PeerContext
73{
74 struct GNUNET_CONFIGURATION_Handle *cfg;
75 struct GNUNET_CORE_Handle *ch;
76 struct GNUNET_MQ_Handle *mq;
77 struct GNUNET_TRANSPORT_OfferHelloHandle *oh;
78 struct GNUNET_PeerIdentity id;
79 struct GNUNET_MessageHeader *hello;
80 struct GNUNET_STATISTICS_Handle *stats;
81 struct GNUNET_TRANSPORT_HelloGetHandle *ghh;
82 struct GNUNET_ATS_ConnectivityHandle *ats;
83 struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh;
84 int connect_status;
85 struct GNUNET_OS_Process *arm_proc;
86};
87
88static struct PeerContext p1;
89static struct PeerContext p2;
90
91static unsigned long long current_quota_p1_in;
92static unsigned long long current_quota_p1_out;
93static unsigned long long current_quota_p2_in;
94static unsigned long long current_quota_p2_out;
95
96static int ok;
97static int test;
98static int32_t tr_n;
99
100static int running;
101
102
103#if VERBOSE
104#define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, \
105 "Now at stage %u at %s:%u\n", ok, __FILE__, \
106 __LINE__); } while (0)
107#else
108#define OKPP do { ok++; } while (0)
109#endif
110
111struct TestMessage
112{
113 struct GNUNET_MessageHeader header;
114 uint32_t num GNUNET_PACKED;
115 uint8_t pad[MESSAGESIZE];
116};
117
118
119static void
120terminate_peer (struct PeerContext *p)
121{
122 if (NULL != p->ch)
123 {
124 GNUNET_CORE_disconnect (p->ch);
125 p->ch = NULL;
126 }
127 if (NULL != p->ghh)
128 {
129 GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
130 p->ghh = NULL;
131 }
132 if (NULL != p->oh)
133 {
134 GNUNET_TRANSPORT_offer_hello_cancel (p->oh);
135 p->oh = NULL;
136 }
137 if (NULL != p->ats_sh)
138 {
139 GNUNET_ATS_connectivity_suggest_cancel (p->ats_sh);
140 p->ats_sh = NULL;
141 }
142 if (NULL != p->ats)
143 {
144 GNUNET_ATS_connectivity_done (p->ats);
145 p->ats = NULL;
146 }
147 if (NULL != p->stats)
148 {
149 GNUNET_STATISTICS_destroy (p->stats, GNUNET_NO);
150 p->stats = NULL;
151 }
152 if (NULL != p->hello)
153 {
154 GNUNET_free (p->hello);
155 p->hello = NULL;
156 }
157}
158
159
160static void
161shutdown_task (void *cls)
162{
163 if (NULL != err_task)
164 {
165 GNUNET_SCHEDULER_cancel (err_task);
166 err_task = NULL;
167 }
168 if (NULL != measure_task)
169 {
170 GNUNET_SCHEDULER_cancel (measure_task);
171 measure_task = NULL;
172 }
173 terminate_peer (&p1);
174 terminate_peer (&p2);
175}
176
177
178static void
179terminate_task_error (void *cls)
180{
181 err_task = NULL;
182 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
183 "Testcase failed (timeout)!\n");
184 GNUNET_SCHEDULER_shutdown ();
185 ok = 42;
186}
187
188
189/**
190 * Callback function to process statistic values.
191 *
192 * @param cls closure
193 * @param subsystem name of subsystem that created the statistic
194 * @param name the name of the datum
195 * @param value the current value
196 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
197 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
198 */
199static int
200print_stat (void *cls,
201 const char *subsystem,
202 const char *name,
203 uint64_t value,
204 int is_persistent)
205{
206 if (cls == &p1)
207 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
208 "Peer1 %50s = %12llu\n",
209 name,
210 (unsigned long long) value);
211 if (cls == &p2)
212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213 "Peer2 %50s = %12llu\n",
214 name,
215 (unsigned long long) value);
216 return GNUNET_OK;
217}
218
219
220static void
221measurement_stop (void *cls)
222{
223 unsigned long long delta;
224 unsigned long long throughput_out;
225 unsigned long long throughput_in;
226 unsigned long long max_quota_in;
227 unsigned long long max_quota_out;
228 unsigned long long quota_delta;
229 enum GNUNET_ErrorType kind = GNUNET_ERROR_TYPE_DEBUG;
230
231 measure_task = NULL;
232 fprintf (stdout, "%s", "\n");
233 running = GNUNET_NO;
234
235 delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us;
236 if (0 == delta)
237 delta = 1;
238 throughput_out = total_bytes_sent * 1000000LL / delta; /* convert to bytes/s */
239 throughput_in = total_bytes_recv * 1000000LL / delta; /* convert to bytes/s */
240
241 max_quota_in = GNUNET_MIN (current_quota_p1_in, current_quota_p2_in);
242 max_quota_out = GNUNET_MIN (current_quota_p1_out, current_quota_p2_out);
243 if (max_quota_out < max_quota_in)
244 quota_delta = max_quota_in / 3;
245 else
246 quota_delta = max_quota_out / 3;
247
248 if ((throughput_out > (max_quota_out + quota_delta)) ||
249 (throughput_in > (max_quota_in + quota_delta)))
250 ok = 1; /* fail */
251 else
252 ok = 0; /* pass */
253 GNUNET_STATISTICS_get (p1.stats,
254 "core",
255 "# discarded CORE_SEND requests",
256 NULL,
257 &print_stat,
258 &p1);
259 GNUNET_STATISTICS_get (p1.stats,
260 "core",
261 "# discarded CORE_SEND request bytes",
262 NULL,
263 &print_stat,
264 &p1);
265 GNUNET_STATISTICS_get (p1.stats,
266 "core",
267 "# discarded lower priority CORE_SEND requests",
268 NULL,
269 &print_stat,
270 NULL);
271 GNUNET_STATISTICS_get (p1.stats,
272 "core",
273 "# discarded lower priority CORE_SEND request bytes",
274 NULL,
275 &print_stat,
276 &p1);
277 GNUNET_STATISTICS_get (p2.stats,
278 "core",
279 "# discarded CORE_SEND requests",
280 NULL,
281 &print_stat,
282 &p2);
283
284 GNUNET_STATISTICS_get (p2.stats,
285 "core",
286 "# discarded CORE_SEND request bytes",
287 NULL,
288 &print_stat,
289 &p2);
290 GNUNET_STATISTICS_get (p2.stats,
291 "core",
292 "# discarded lower priority CORE_SEND requests",
293 NULL,
294 &print_stat,
295 &p2);
296 GNUNET_STATISTICS_get (p2.stats,
297 "core",
298 "# discarded lower priority CORE_SEND request bytes",
299 NULL,
300 &print_stat,
301 &p2);
302
303 if (ok != 0)
304 kind = GNUNET_ERROR_TYPE_ERROR;
305 switch (test)
306 {
307 case SYMMETRIC:
308 GNUNET_log (kind,
309 "Core quota compliance test with symmetric quotas: %s\n",
310 (0 == ok) ? "PASSED" : "FAILED");
311 break;
312
313 case ASYMMETRIC_SEND_LIMITED:
314 GNUNET_log (kind,
315 "Core quota compliance test with limited sender quota: %s\n",
316 (0 == ok) ? "PASSED" : "FAILED");
317 break;
318
319 case ASYMMETRIC_RECV_LIMITED:
320 GNUNET_log (kind,
321 "Core quota compliance test with limited receiver quota: %s\n",
322 (0 == ok) ? "PASSED" : "FAILED");
323 break;
324 }
325 ;
326 GNUNET_log (kind,
327 "Peer 1 send rate: %llu b/s (%llu bytes in %llu ms)\n",
328 throughput_out,
329 total_bytes_sent,
330 delta);
331 GNUNET_log (kind,
332 "Peer 1 send quota: %llu b/s\n",
333 current_quota_p1_out);
334 GNUNET_log (kind,
335 "Peer 2 receive rate: %llu b/s (%llu bytes in %llu ms)\n",
336 throughput_in,
337 total_bytes_recv,
338 delta);
339 GNUNET_log (kind,
340 "Peer 2 receive quota: %llu b/s\n",
341 current_quota_p2_in);
342/*
343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. inbound quota allowed: %llu b/s\n",max_quota_in );
344 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. outbound quota allowed: %llu b/s\n",max_quota_out);
345 */
346 GNUNET_SCHEDULER_shutdown ();
347}
348
349
350static void
351do_transmit (void *cls)
352{
353 struct TestMessage *hdr;
354 struct GNUNET_MQ_Envelope *env;
355
356 env = GNUNET_MQ_msg (hdr,
357 MTYPE);
358 hdr->num = htonl (tr_n);
359 memset (&hdr->pad,
360 tr_n,
361 MESSAGESIZE);
362 tr_n++;
363 GNUNET_SCHEDULER_cancel (err_task);
364 err_task =
365 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
366 &terminate_task_error,
367 NULL);
368 total_bytes_sent += sizeof(struct TestMessage);
369 GNUNET_MQ_send (p1.mq,
370 env);
371}
372
373
374static void *
375connect_notify (void *cls,
376 const struct GNUNET_PeerIdentity *peer,
377 struct GNUNET_MQ_Handle *mq)
378{
379 struct PeerContext *pc = cls;
380
381 if (0 == memcmp (&pc->id,
382 peer,
383 sizeof(struct GNUNET_PeerIdentity)))
384 return NULL; /* loopback */
385 GNUNET_assert (0 == pc->connect_status);
386 pc->connect_status = 1;
387 pc->mq = mq;
388 if (pc == &p1)
389 {
390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
391 "Encrypted connection established to peer `%s'\n",
392 GNUNET_i2s (peer));
393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394 "Asking core (1) for transmission to peer `%s'\n",
395 GNUNET_i2s (&p2.id));
396 GNUNET_SCHEDULER_cancel (err_task);
397 err_task =
398 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
399 &terminate_task_error,
400 NULL);
401 start_time = GNUNET_TIME_absolute_get ();
402 running = GNUNET_YES;
403 measure_task =
404 GNUNET_SCHEDULER_add_delayed (MEASUREMENT_LENGTH,
405 &measurement_stop,
406 NULL);
407 do_transmit (NULL);
408 }
409 return pc;
410}
411
412
413static void
414disconnect_notify (void *cls,
415 const struct GNUNET_PeerIdentity *peer,
416 void *internal_cls)
417{
418 struct PeerContext *pc = cls;
419
420 if (NULL == internal_cls)
421 return; /* loopback */
422 pc->connect_status = 0;
423 pc->mq = NULL;
424 if (NULL != measure_task)
425 {
426 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
427 "Measurement aborted due to disconnect!\n");
428 GNUNET_SCHEDULER_cancel (measure_task);
429 measure_task = NULL;
430 }
431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
432 "Encrypted connection to `%s' cut\n",
433 GNUNET_i2s (peer));
434}
435
436
437static void
438handle_test (void *cls,
439 const struct TestMessage *hdr)
440{
441 static int n;
442
443 total_bytes_recv += sizeof(struct TestMessage);
444 if (ntohl (hdr->num) != n)
445 {
446 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
447 "Expected message %u, got message %u\n",
448 n,
449 ntohl (hdr->num));
450 GNUNET_SCHEDULER_cancel (err_task);
451 err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error,
452 NULL);
453 return;
454 }
455 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
456 "Got message %u\n",
457 ntohl (hdr->num));
458 n++;
459 if (0 == (n % 10))
460 fprintf (stderr, "%s", ".");
461
462 if (GNUNET_YES == running)
463 do_transmit (NULL);
464}
465
466
467static void
468init_notify (void *cls,
469 const struct GNUNET_PeerIdentity *my_identity)
470{
471 struct PeerContext *p = cls;
472 struct GNUNET_MQ_MessageHandler handlers[] = {
473 GNUNET_MQ_hd_fixed_size (test,
474 MTYPE,
475 struct TestMessage,
476 NULL),
477 GNUNET_MQ_handler_end ()
478 };
479
480 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481 "Connection to CORE service of `%s' established\n",
482 GNUNET_i2s (my_identity));
483 GNUNET_assert (NULL != my_identity);
484 p->id = *my_identity;
485 if (cls == &p1)
486 {
487 GNUNET_assert (ok == 2);
488 OKPP;
489 /* connect p2 */
490 p2.ch = GNUNET_CORE_connect (p2.cfg,
491 &p2,
492 &init_notify,
493 &connect_notify,
494 &disconnect_notify,
495 handlers);
496 }
497 else
498 {
499 GNUNET_assert (ok == 3);
500 OKPP;
501 GNUNET_assert (cls == &p2);
502 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503 "Asking core (1) to connect to peer `%s' and vice-versa\n",
504 GNUNET_i2s (&p2.id));
505 p1.ats_sh = GNUNET_ATS_connectivity_suggest (p1.ats,
506 &p2.id,
507 1);
508 p2.ats_sh = GNUNET_ATS_connectivity_suggest (p2.ats,
509 &p1.id,
510 1);
511 }
512}
513
514
515static void
516offer_hello_done (void *cls)
517{
518 struct PeerContext *p = cls;
519
520 p->oh = NULL;
521}
522
523
524static void
525process_hello (void *cls,
526 const struct GNUNET_MessageHeader *message)
527{
528 struct PeerContext *p = cls;
529
530 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
531 "Received (my) HELLO from transport service\n");
532 GNUNET_assert (message != NULL);
533 if (NULL != p->hello) GNUNET_free (p->hello);
534 p->hello = GNUNET_malloc (ntohs (message->size));
535 GNUNET_memcpy (p->hello, message, ntohs (message->size));
536 if ((p == &p1) &&
537 (NULL == p2.oh))
538 p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg,
539 message,
540 &offer_hello_done,
541 &p2);
542 if ((p == &p2) &&
543 (NULL == p1.oh))
544 p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg, message,
545 &offer_hello_done,
546 &p1);
547
548 if ((p == &p1) &&
549 (NULL != p2.hello) &&
550 (NULL == p1.oh))
551 p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg,
552 p2.hello,
553 &offer_hello_done,
554 &p1);
555 if ((p == &p2) &&
556 (NULL != p1.hello) &&
557 (NULL == p2.oh))
558 p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg,
559 p1.hello,
560 &offer_hello_done,
561 &p2);
562}
563
564
565static void
566setup_peer (struct PeerContext *p,
567 const char *cfgname)
568{
569 char *binary;
570
571 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
572 p->cfg = GNUNET_CONFIGURATION_create ();
573 p->arm_proc =
574 GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
575 | GNUNET_OS_USE_PIPE_CONTROL,
576 NULL, NULL, NULL,
577 binary,
578 "gnunet-service-arm",
579 "-c",
580 cfgname,
581 NULL);
582 GNUNET_assert (GNUNET_OK ==
583 GNUNET_CONFIGURATION_load (p->cfg,
584 cfgname));
585 p->stats = GNUNET_STATISTICS_create ("core",
586 p->cfg);
587 GNUNET_assert (NULL != p->stats);
588 p->ats = GNUNET_ATS_connectivity_init (p->cfg);
589 GNUNET_assert (NULL != p->ats);
590 p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
591 GNUNET_TRANSPORT_AC_ANY,
592 &process_hello,
593 p);
594 GNUNET_free (binary);
595}
596
597
598static void
599run (void *cls,
600 char *const *args,
601 const char *cfgfile,
602 const struct GNUNET_CONFIGURATION_Handle *cfg)
603{
604 struct GNUNET_MQ_MessageHandler handlers[] = {
605 GNUNET_MQ_hd_fixed_size (test,
606 MTYPE,
607 struct TestMessage,
608 NULL),
609 GNUNET_MQ_handler_end ()
610 };
611
612 GNUNET_assert (ok == 1);
613 OKPP;
614 err_task =
615 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
616 &terminate_task_error,
617 NULL);
618 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
619 NULL);
620 if (test == SYMMETRIC)
621 {
622 setup_peer (&p1,
623 "test_core_quota_peer1.conf");
624 setup_peer (&p2,
625 "test_core_quota_peer2.conf");
626 }
627 else if (test == ASYMMETRIC_SEND_LIMITED)
628 {
629 setup_peer (&p1,
630 "test_core_quota_asymmetric_send_limit_peer1.conf");
631 setup_peer (&p2,
632 "test_core_quota_asymmetric_send_limit_peer2.conf");
633 }
634 else if (test == ASYMMETRIC_RECV_LIMITED)
635 {
636 setup_peer (&p1,
637 "test_core_quota_asymmetric_recv_limited_peer1.conf");
638 setup_peer (&p2,
639 "test_core_quota_asymmetric_recv_limited_peer2.conf");
640 }
641
642 GNUNET_assert (test != -1);
643 GNUNET_assert (GNUNET_SYSERR !=
644 GNUNET_CONFIGURATION_get_value_size (p1.cfg,
645 "ATS",
646 "WAN_QUOTA_IN",
647 &current_quota_p1_in));
648 GNUNET_assert (GNUNET_SYSERR !=
649 GNUNET_CONFIGURATION_get_value_size (p2.cfg,
650 "ATS",
651 "WAN_QUOTA_IN",
652 &current_quota_p2_in));
653 GNUNET_assert (GNUNET_SYSERR !=
654 GNUNET_CONFIGURATION_get_value_size (p1.cfg,
655 "ATS",
656 "WAN_QUOTA_OUT",
657 &current_quota_p1_out));
658 GNUNET_assert (GNUNET_SYSERR !=
659 GNUNET_CONFIGURATION_get_value_size (p2.cfg,
660 "ATS",
661 "WAN_QUOTA_OUT",
662 &current_quota_p2_out));
663
664 p1.ch = GNUNET_CORE_connect (p1.cfg,
665 &p1,
666 &init_notify,
667 &connect_notify,
668 &disconnect_notify,
669 handlers);
670}
671
672
673static void
674stop_arm (struct PeerContext *p)
675{
676 if (0 != GNUNET_OS_process_kill (p->arm_proc,
677 GNUNET_TERM_SIG))
678 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
679 "kill");
680 if (GNUNET_OK !=
681 GNUNET_OS_process_wait (p->arm_proc))
682 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
683 "waitpid");
684 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685 "ARM process %u stopped\n",
686 GNUNET_OS_process_get_pid (p->arm_proc));
687 GNUNET_OS_process_destroy (p->arm_proc);
688 p->arm_proc = NULL;
689 GNUNET_CONFIGURATION_destroy (p->cfg);
690}
691
692
693static int
694check ()
695{
696 char *const argv[] = {
697 "test-core-quota-compliance",
698 "-c",
699 "test_core_api_data.conf",
700 NULL
701 };
702 struct GNUNET_GETOPT_CommandLineOption options[] = {
703 GNUNET_GETOPT_OPTION_END
704 };
705
706 ok = 1;
707 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
708 argv,
709 "test-core-quota-compliance",
710 "nohelp",
711 options,
712 &run,
713 &ok);
714 stop_arm (&p1);
715 stop_arm (&p2);
716 return ok;
717}
718
719
720static void
721cleanup_directory (int test)
722{
723 switch (test)
724 {
725 case SYMMETRIC:
726 GNUNET_DISK_purge_cfg_dir
727 ("test_core_quota_peer1.conf",
728 "GNUNET_TEST_HOME");
729 GNUNET_DISK_purge_cfg_dir
730 ("test_core_quota_peer2.conf",
731 "GNUNET_TEST_HOME");
732 break;
733
734 case ASYMMETRIC_SEND_LIMITED:
735 GNUNET_DISK_purge_cfg_dir
736 ("test_core_quota_asymmetric_send_limit_peer1.conf",
737 "GNUNET_TEST_HOME");
738 GNUNET_DISK_purge_cfg_dir
739 ("test_core_quota_asymmetric_send_limit_peer2.conf",
740 "GNUNET_TEST_HOME");
741 break;
742
743 case ASYMMETRIC_RECV_LIMITED:
744 GNUNET_DISK_purge_cfg_dir
745 ("test_core_quota_asymmetric_recv_limited_peer1.conf",
746 "GNUNET_TEST_HOME");
747 GNUNET_DISK_purge_cfg_dir
748 ("test_core_quota_asymmetric_recv_limited_peer2.conf",
749 "GNUNET_TEST_HOME");
750 break;
751 }
752}
753
754
755int
756main (int argc,
757 char *argv[])
758{
759 int ret;
760
761 test = -1;
762 if (NULL != strstr (argv[0],
763 "_symmetric"))
764 {
765 test = SYMMETRIC;
766 }
767 else if (NULL != strstr (argv[0],
768 "_asymmetric_send"))
769 {
770 test = ASYMMETRIC_SEND_LIMITED;
771 }
772 else if (NULL != strstr (argv[0],
773 "_asymmetric_recv"))
774 {
775 test = ASYMMETRIC_RECV_LIMITED;
776 }
777 GNUNET_assert (test != -1);
778 cleanup_directory (test);
779 GNUNET_log_setup ("test-core-quota-compliance",
780 "WARNING",
781 NULL);
782 ret = check ();
783 cleanup_directory (test);
784 return ret;
785}
786
787
788/* end of test_core_quota_compliance.c */
diff --git a/src/service/core/test_core_quota_peer1.conf b/src/service/core/test_core_quota_peer1.conf
new file mode 100644
index 000000000..ec592f778
--- /dev/null
+++ b/src/service/core/test_core_quota_peer1.conf
@@ -0,0 +1,58 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-1/
4
5[arm]
6PORT = 12460
7UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock
8
9[statistics]
10PORT = 12461
11
12[resolver]
13PORT = 12462
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock
15
16[peerinfo]
17PORT = 12463
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock
19
20[transport]
21PORT = 12464
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock
23
24[core]
25PORT = 12475
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-core.sock
27
28[ats]
29PORT = 12476
30UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock
31# UNSPECIFIED
32UNSPECIFIED_QUOTA_IN = 10 MiB
33UNSPECIFIED_QUOTA_OUT = 10 MiB
34# LOOPBACK
35LOOPBACK_QUOTA_IN = 10 MiB
36LOOPBACK_QUOTA_OUT = 10 MiB
37# LAN
38LAN_QUOTA_IN = 10 MiB
39LAN_QUOTA_OUT = 10 MiB
40# WAN
41WAN_QUOTA_IN = 10 MiB
42WAN_QUOTA_OUT = 10 MiB
43# WLAN
44WLAN_QUOTA_IN = 10 MiB
45WLAN_QUOTA_OUT = 10 MiB
46
47
48[transport-tcp]
49PORT = 12467
50
51[transport-udp]
52PORT = 12468
53
54[transport-unix]
55PORT = 12469
56
57[transport-http]
58PORT = 12470
diff --git a/src/service/core/test_core_quota_peer2.conf b/src/service/core/test_core_quota_peer2.conf
new file mode 100644
index 000000000..65d0710bb
--- /dev/null
+++ b/src/service/core/test_core_quota_peer2.conf
@@ -0,0 +1,59 @@
1@INLINE@ test_core_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-2/
4
5[arm]
6PORT = 22460
7UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock
8
9[statistics]
10PORT = 22461
11UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock
12
13[resolver]
14PORT = 22462
15UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock
16
17[peerinfo]
18PORT = 22463
19UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock
20
21[transport]
22PORT = 22464
23UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock
24
25[core]
26PORT = 22475
27UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-core.sock
28
29[ats]
30PORT = 22476
31UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock
32# UNSPECIFIED
33UNSPECIFIED_QUOTA_IN = 10 MiB
34UNSPECIFIED_QUOTA_OUT = 10 MiB
35# LOOPBACK
36LOOPBACK_QUOTA_IN = 10 MiB
37LOOPBACK_QUOTA_OUT = 10 MiB
38# LAN
39LAN_QUOTA_IN = 10 MiB
40LAN_QUOTA_OUT = 10 MiB
41# WAN
42WAN_QUOTA_IN = 10 MiB
43WAN_QUOTA_OUT = 10 MiB
44# WLAN
45WLAN_QUOTA_IN = 10 MiB
46WLAN_QUOTA_OUT = 10 MiB
47
48
49[transport-tcp]
50PORT = 22467
51
52[transport-udp]
53PORT = 22468
54
55[transport-unix]
56PORT = 22469
57
58[transport-http]
59PORT = 22470
diff --git a/src/service/core/test_core_start_testcase.sh b/src/service/core/test_core_start_testcase.sh
new file mode 100755
index 000000000..78e67dbf5
--- /dev/null
+++ b/src/service/core/test_core_start_testcase.sh
@@ -0,0 +1,15 @@
1#!/bin/bash
2echo gaga1 > gaga.txt
3read -p "Test case configuration to use:" conf
4if ! [ -d "/run/netns" ]; then
5 echo You have to create the directory /run/netns.
6fi
7if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then
8 if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then
9 echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n"
10 exit 78
11 fi
12fi
13echo gaga2 >> gaga.txt
14exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; /usr/local/lib/gnunet/libexec/test_testing_start_with_config $conf"
15echo gaga3 >> gaga.txt
diff --git a/src/service/identity/.gitignore b/src/service/identity/.gitignore
new file mode 100644
index 000000000..634a0bdd6
--- /dev/null
+++ b/src/service/identity/.gitignore
@@ -0,0 +1,4 @@
1gnunet-service-identity
2gnunet-identity
3test_identity
4test_identity_defaults
diff --git a/src/service/identity/Makefile.am b/src/service/identity/Makefile.am
new file mode 100644
index 000000000..079a9af2e
--- /dev/null
+++ b/src/service/identity/Makefile.am
@@ -0,0 +1,67 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6if USE_COVERAGE
7 AM_CFLAGS = --coverage -O0
8 XLIB = -lgcov
9endif
10
11pkgcfgdir= $(pkgdatadir)/config.d/
12
13libexecdir= $(pkglibdir)/libexec/
14
15pkgcfg_DATA = \
16 identity.conf
17
18lib_LTLIBRARIES = \
19 libgnunetidentity.la
20
21
22libgnunetidentity_la_SOURCES = \
23 identity_api.c \
24 identity_api_lookup.c \
25 identity_api_suffix_lookup.c \
26 identity.h
27libgnunetidentity_la_LIBADD = \
28 $(top_builddir)/src/lib/util/libgnunetutil.la \
29 $(GN_LIBINTL) $(XLIB)
30libgnunetidentity_la_LDFLAGS = \
31 $(GN_LIB_LDFLAGS) \
32 -lsodium \
33 -version-info 1:0:0
34
35libexec_PROGRAMS = \
36 gnunet-service-identity
37
38gnunet_service_identity_SOURCES = \
39 gnunet-service-identity.c
40gnunet_service_identity_LDADD = \
41 libgnunetidentity.la \
42 $(top_builddir)/src/statistics/libgnunetstatistics.la \
43 $(top_builddir)/src/lib/util/libgnunetutil.la \
44 $(GN_LIBINTL)
45
46
47check_PROGRAMS = \
48 test_identity
49
50# if ENABLE_TEST_RUN
51# AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
52# TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
53# endif
54
55
56test_identity_SOURCES = \
57 test_identity.c
58test_identity_LDADD = \
59 libgnunetidentity.la \
60 $(top_builddir)/src/lib/testing/libgnunettesting.la \
61 $(top_builddir)/src/lib/util/libgnunetutil.la
62
63EXTRA_DIST = \
64 test_identity.conf \
65 test_identity_messages.sh
66
67
diff --git a/src/service/identity/gnunet-service-identity.c b/src/service/identity/gnunet-service-identity.c
new file mode 100644
index 000000000..7ac4bf2b9
--- /dev/null
+++ b/src/service/identity/gnunet-service-identity.c
@@ -0,0 +1,1035 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file identity/gnunet-service-identity.c
23 * @brief identity management service
24 * @author Christian Grothoff
25 *
26 * The purpose of this service is to manage private keys that
27 * represent the various egos/pseudonyms/identities of a GNUnet user.
28 *
29 * Todo:
30 * - auto-initialze default egos; maybe trigger default
31 * initializations (such as gnunet-gns-import.sh?)
32 */
33#include "platform.h"
34#include "gnunet_util_lib.h"
35#include "gnunet_constants.h"
36#include "gnunet_protocols.h"
37#include "gnunet_statistics_service.h"
38#include "gnunet_identity_service.h"
39#include "identity.h"
40
41
42/**
43 * Information we keep about each ego.
44 */
45struct Ego
46{
47 /**
48 * We keep egos in a DLL.
49 */
50 struct Ego *next;
51
52 /**
53 * We keep egos in a DLL.
54 */
55 struct Ego *prev;
56
57 /**
58 * Private key of the ego.
59 */
60 struct GNUNET_CRYPTO_PrivateKey pk;
61
62 /**
63 * String identifier for the ego.
64 */
65 char *identifier;
66};
67
68
69/**
70 * Handle to our current configuration.
71 */
72static const struct GNUNET_CONFIGURATION_Handle *cfg;
73
74/**
75 * Handle to subsystem configuration which for each subsystem contains
76 * the name of the default ego.
77 */
78static struct GNUNET_CONFIGURATION_Handle *subsystem_cfg;
79
80/**
81 * Handle to the statistics service.
82 */
83static struct GNUNET_STATISTICS_Handle *stats;
84
85/**
86 * Notification context, simplifies client broadcasts.
87 */
88static struct GNUNET_NotificationContext *nc;
89
90/**
91 * Directory where we store the identities.
92 */
93static char *ego_directory;
94
95/**
96 * Configuration file name where subsystem information is kept.
97 */
98static char *subsystem_cfg_file;
99
100/**
101 * Head of DLL of all egos.
102 */
103static struct Ego *ego_head;
104
105/**
106 * Tail of DLL of all egos.
107 */
108static struct Ego *ego_tail;
109
110
111/**
112 * Get the name of the file we use to store a given ego.
113 *
114 * @param ego ego for which we need the filename
115 * @return full filename for the given ego
116 */
117static char *
118get_ego_filename (struct Ego *ego)
119{
120 char *filename;
121
122 GNUNET_asprintf (&filename,
123 "%s%s%s",
124 ego_directory,
125 DIR_SEPARATOR_STR,
126 ego->identifier);
127 return filename;
128}
129
130
131/**
132 * Called whenever a client is disconnected.
133 *
134 * @param cls closure
135 * @param client identification of the client
136 * @param app_ctx @a client
137 */
138static void
139client_disconnect_cb (void *cls,
140 struct GNUNET_SERVICE_Client *client,
141 void *app_ctx)
142{
143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
144 "Client %p disconnected\n",
145 client);
146}
147
148
149/**
150 * Add a client to our list of active clients.
151 *
152 * @param cls NULL
153 * @param client client to add
154 * @param mq message queue for @a client
155 * @return internal namestore client structure for this client
156 */
157static void *
158client_connect_cb (void *cls,
159 struct GNUNET_SERVICE_Client *client,
160 struct GNUNET_MQ_Handle *mq)
161{
162 return client;
163}
164
165
166/**
167 * Task run during shutdown.
168 *
169 * @param cls unused
170 */
171static void
172shutdown_task (void *cls)
173{
174 struct Ego *e;
175
176 if (NULL != nc)
177 {
178 GNUNET_notification_context_destroy (nc);
179 nc = NULL;
180 }
181 if (NULL != stats)
182 {
183 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
184 stats = NULL;
185 }
186 GNUNET_CONFIGURATION_destroy (subsystem_cfg);
187 subsystem_cfg = NULL;
188 GNUNET_free (subsystem_cfg_file);
189 subsystem_cfg_file = NULL;
190 GNUNET_free (ego_directory);
191 ego_directory = NULL;
192 while (NULL != (e = ego_head))
193 {
194 GNUNET_CONTAINER_DLL_remove (ego_head,
195 ego_tail,
196 e);
197 GNUNET_free (e->identifier);
198 GNUNET_free (e);
199 }
200}
201
202
203/**
204 * Send a result code back to the client.
205 *
206 * @param client client that should receive the result code
207 * @param result_code code to transmit
208 */
209static void
210send_result_code (struct GNUNET_SERVICE_Client *client,
211 uint32_t result_code)
212{
213 struct ResultCodeMessage *rcm;
214 struct GNUNET_MQ_Envelope *env;
215
216 env =
217 GNUNET_MQ_msg (rcm, GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE);
218 rcm->result_code = htonl (result_code);
219 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
220 "Sending result %d (%s) to client\n",
221 (int) result_code,
222 GNUNET_ErrorCode_get_hint (result_code));
223 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
224}
225
226
227/**
228 * Create an update message with information about the current state of an ego.
229 *
230 * @param ego ego to create message for
231 * @return corresponding update message
232 */
233static struct GNUNET_MQ_Envelope *
234create_update_message (struct Ego *ego)
235{
236 struct UpdateMessage *um;
237 struct GNUNET_MQ_Envelope *env;
238 size_t name_len;
239 ssize_t key_len;
240
241 key_len = GNUNET_CRYPTO_private_key_get_length (&ego->pk);
242 name_len = (NULL == ego->identifier) ? 0 : (strlen (ego->identifier) + 1);
243 env = GNUNET_MQ_msg_extra (um, name_len + key_len,
244 GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE);
245 um->name_len = htons (name_len);
246 um->end_of_list = htons (GNUNET_NO);
247 um->key_len = htons (key_len);
248 GNUNET_memcpy (&um[1], ego->identifier, name_len);
249 GNUNET_CRYPTO_write_private_key_to_buffer (&ego->pk,
250 ((char*) &um[1]) + name_len,
251 key_len);
252 return env;
253}
254
255
256/**
257 * Handler for START message from client, sends information
258 * about all identities to the client immediately and
259 * adds the client to the notification context for future
260 * updates.
261 *
262 * @param cls a `struct GNUNET_SERVICE_Client *`
263 * @param message the message received
264 */
265static void
266handle_start_message (void *cls,
267 const struct GNUNET_MessageHeader *message)
268{
269 struct GNUNET_SERVICE_Client *client = cls;
270
271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272 "Received START message from client\n");
273 GNUNET_SERVICE_client_mark_monitor (client);
274 GNUNET_SERVICE_client_disable_continue_warning (client);
275 GNUNET_notification_context_add (nc,
276 GNUNET_SERVICE_client_get_mq (client));
277 for (struct Ego *ego = ego_head; NULL != ego; ego = ego->next)
278 {
279 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
280 create_update_message (ego));
281 }
282 {
283 struct UpdateMessage *ume;
284 struct GNUNET_MQ_Envelope *env;
285
286 env = GNUNET_MQ_msg_extra (ume,
287 0,
288 GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE);
289 ume->end_of_list = htons (GNUNET_YES);
290 ume->name_len = htons (0);
291 ume->key_len = htons (0);
292 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
293 env);
294 }
295 GNUNET_SERVICE_client_continue (client);
296}
297
298
299/**
300 * Handler for LOOKUP message from client, sends information
301 * about ONE identity to the client immediately.
302 *
303 * @param cls unused
304 * @param message the message received
305 * @return #GNUNET_SYSERR if message was ill-formed
306 */
307static int
308check_lookup_message (void *cls,
309 const struct LookupMessage *message)
310{
311 GNUNET_MQ_check_zero_termination (message);
312 return GNUNET_OK;
313}
314
315
316/**
317 * Handler for LOOKUP message from client, sends information
318 * about ONE identity to the client immediately.
319 *
320 * @param cls a `struct GNUNET_SERVICE_Client *`
321 * @param message the message received
322 */
323static void
324handle_lookup_message (void *cls,
325 const struct LookupMessage *message)
326{
327 struct GNUNET_SERVICE_Client *client = cls;
328 const char *name;
329 struct GNUNET_MQ_Envelope *env;
330 struct Ego *ego;
331
332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333 "Received LOOKUP message from client\n");
334 name = (const char *) &message[1];
335 for (ego = ego_head; NULL != ego; ego = ego->next)
336 {
337 if (0 != strcasecmp (name, ego->identifier))
338 continue;
339 env = create_update_message (ego);
340 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
341 GNUNET_SERVICE_client_continue (client);
342 return;
343 }
344 send_result_code (client, GNUNET_EC_IDENTITY_NOT_FOUND);
345 GNUNET_SERVICE_client_continue (client);
346}
347
348
349/**
350 * Handler for LOOKUP message from client, sends information
351 * about ONE identity to the client immediately.
352 *
353 * @param cls unused
354 * @param message the message received
355 * @return #GNUNET_SYSERR if message was ill-formed
356 */
357static int
358check_lookup_by_suffix_message (void *cls,
359 const struct LookupMessage *message)
360{
361 GNUNET_MQ_check_zero_termination (message);
362 return GNUNET_OK;
363}
364
365
366/**
367 * Handler for LOOKUP_BY_SUFFIX message from client, sends information
368 * about ONE identity to the client immediately.
369 *
370 * @param cls a `struct GNUNET_SERVICE_Client *`
371 * @param message the message received
372 */
373static void
374handle_lookup_by_suffix_message (void *cls,
375 const struct LookupMessage *message)
376{
377 struct GNUNET_SERVICE_Client *client = cls;
378 const char *name;
379 struct GNUNET_MQ_Envelope *env;
380 struct Ego *lprefix;
381
382 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
383 "Received LOOKUP_BY_SUFFIX message from client\n");
384 name = (const char *) &message[1];
385 lprefix = NULL;
386 for (struct Ego *ego = ego_head; NULL != ego; ego = ego->next)
387 {
388 if ((strlen (ego->identifier) <= strlen (name)) &&
389 (0 == strcmp (ego->identifier,
390 &name[strlen (name) - strlen (ego->identifier)])) &&
391 ((strlen (name) == strlen (ego->identifier)) ||
392 ('.' == name[strlen (name) - strlen (ego->identifier) - 1])) &&
393 ((NULL == lprefix) ||
394 (strlen (ego->identifier) > strlen (lprefix->identifier))))
395 {
396 /* found better match, update! */
397 lprefix = ego;
398 }
399 }
400 if (NULL != lprefix)
401 {
402 env = create_update_message (lprefix);
403 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
404 GNUNET_SERVICE_client_continue (client);
405 return;
406 }
407 send_result_code (client, GNUNET_EC_IDENTITY_NOT_FOUND);
408 GNUNET_SERVICE_client_continue (client);
409}
410
411
412/**
413 * Send an updated message for the given ego to all listeners.
414 *
415 * @param ego ego to send the update for
416 */
417static void
418notify_listeners (struct Ego *ego)
419{
420 struct UpdateMessage *um;
421 size_t name_len;
422 ssize_t key_len;
423
424 name_len = (NULL == ego->identifier) ? 0 : (strlen (ego->identifier) + 1);
425 key_len = GNUNET_CRYPTO_private_key_get_length (&ego->pk);
426 um = GNUNET_malloc (sizeof(struct UpdateMessage) + name_len + key_len);
427 um->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE);
428 um->header.size = htons (sizeof(struct UpdateMessage) + name_len + key_len);
429 um->name_len = htons (name_len);
430 um->end_of_list = htons (GNUNET_NO);
431 um->key_len = htons (key_len);
432 GNUNET_memcpy (&um[1], ego->identifier, name_len);
433 GNUNET_CRYPTO_write_private_key_to_buffer (&ego->pk,
434 ((char*) &um[1]) + name_len,
435 key_len);
436 GNUNET_notification_context_broadcast (nc, &um->header, GNUNET_NO);
437 GNUNET_free (um);
438}
439
440
441/**
442 * Checks a #GNUNET_MESSAGE_TYPE_IDENTITY_CREATE message
443 *
444 * @param cls client sending the message
445 * @param msg message of type `struct CreateRequestMessage`
446 * @return #GNUNET_OK if @a msg is well-formed
447 */
448static int
449check_create_message (void *cls,
450 const struct CreateRequestMessage *msg)
451{
452 uint16_t size;
453 uint16_t name_len;
454 size_t key_len;
455 const char *str;
456
457 size = ntohs (msg->header.size);
458 if (size <= sizeof(struct CreateRequestMessage))
459 {
460 GNUNET_break (0);
461 return GNUNET_SYSERR;
462 }
463 name_len = ntohs (msg->name_len);
464 key_len = ntohs (msg->key_len);
465 if (name_len + key_len + sizeof(struct CreateRequestMessage) != size)
466 {
467 GNUNET_break (0);
468 return GNUNET_SYSERR;
469 }
470 str = (const char *) &msg[1] + key_len;
471 if ('\0' != str[name_len - 1])
472 {
473 GNUNET_break (0);
474 return GNUNET_SYSERR;
475 }
476 return GNUNET_OK;
477}
478
479
480/**
481 * Handler for CREATE message from client, creates new identity.
482 *
483 * @param cls unused
484 * @param crm the message received
485 */
486static void
487handle_create_message (void *cls,
488 const struct CreateRequestMessage *crm)
489{
490 struct GNUNET_CRYPTO_PrivateKey private_key;
491 struct GNUNET_SERVICE_Client *client = cls;
492 struct Ego *ego;
493 char *str;
494 char *fn;
495 size_t key_len;
496 size_t kb_read;
497
498 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received CREATE message from client\n");
499 key_len = ntohs (crm->key_len);
500 if ((GNUNET_SYSERR ==
501 GNUNET_CRYPTO_read_private_key_from_buffer (&crm[1],
502 key_len,
503 &private_key,
504 &kb_read)) ||
505 (kb_read != key_len))
506 {
507 GNUNET_SERVICE_client_drop (client);
508 return;
509 }
510 str = GNUNET_strdup ((const char *) &crm[1] + key_len);
511 GNUNET_STRINGS_utf8_tolower ((const char *) &crm[1] + key_len, str);
512 for (ego = ego_head; NULL != ego; ego = ego->next)
513 {
514 if (0 == strcmp (ego->identifier, str))
515 {
516 send_result_code (client,
517 GNUNET_EC_IDENTITY_NAME_CONFLICT);
518 GNUNET_SERVICE_client_continue (client);
519 GNUNET_free (str);
520 return;
521 }
522 }
523 ego = GNUNET_new (struct Ego);
524 ego->pk = private_key;
525 ego->identifier = GNUNET_strdup (str);
526 GNUNET_CONTAINER_DLL_insert (ego_head,
527 ego_tail,
528 ego);
529 send_result_code (client, GNUNET_EC_NONE);
530 fn = get_ego_filename (ego);
531 if (GNUNET_OK !=
532 GNUNET_DISK_fn_write (fn,
533 &private_key,
534 sizeof(struct GNUNET_CRYPTO_PrivateKey),
535 GNUNET_DISK_PERM_USER_READ
536 | GNUNET_DISK_PERM_USER_WRITE))
537 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", fn);
538 GNUNET_free (fn);
539 GNUNET_free (str);
540 notify_listeners (ego);
541 GNUNET_SERVICE_client_continue (client);
542}
543
544
545/**
546 * Closure for 'handle_ego_rename'.
547 */
548struct RenameContext
549{
550 /**
551 * Old name.
552 */
553 const char *old_name;
554
555 /**
556 * New name.
557 */
558 const char *new_name;
559};
560
561/**
562 * An ego was renamed; rename it in all subsystems where it is
563 * currently set as the default.
564 *
565 * @param cls the 'struct RenameContext'
566 * @param section a section in the configuration to process
567 */
568static void
569handle_ego_rename (void *cls, const char *section)
570{
571 struct RenameContext *rc = cls;
572 char *id;
573
574 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (subsystem_cfg,
575 section,
576 "DEFAULT_IDENTIFIER",
577 &id))
578 return;
579 if (0 != strcmp (id, rc->old_name))
580 {
581 GNUNET_free (id);
582 return;
583 }
584 GNUNET_CONFIGURATION_set_value_string (subsystem_cfg,
585 section,
586 "DEFAULT_IDENTIFIER",
587 rc->new_name);
588 GNUNET_free (id);
589}
590
591
592/**
593 * Checks a #GNUNET_MESSAGE_TYPE_IDENTITY_RENAME message
594 *
595 * @param cls client sending the message
596 * @param msg message of type `struct RenameMessage`
597 * @return #GNUNET_OK if @a msg is well-formed
598 */
599static int
600check_rename_message (void *cls, const struct RenameMessage *msg)
601{
602 uint16_t size;
603 uint16_t old_name_len;
604 uint16_t new_name_len;
605 const char *old_name;
606 const char *new_name;
607
608 size = ntohs (msg->header.size);
609 if (size <= sizeof(struct RenameMessage))
610 {
611 GNUNET_break (0);
612 return GNUNET_SYSERR;
613 }
614 old_name_len = ntohs (msg->old_name_len);
615 new_name_len = ntohs (msg->new_name_len);
616 old_name = (const char *) &msg[1];
617 new_name = &old_name[old_name_len];
618 if ((old_name_len + new_name_len + sizeof(struct RenameMessage) != size) ||
619 ('\0' != old_name[old_name_len - 1]) ||
620 ('\0' != new_name[new_name_len - 1]))
621 {
622 GNUNET_break (0);
623 return GNUNET_SYSERR;
624 }
625
626 return GNUNET_OK;
627}
628
629
630/**
631 * Handler for RENAME message from client, creates
632 * new identity.
633 *
634 * @param cls unused
635 * @param rm the message received
636 */
637static void
638handle_rename_message (void *cls, const struct RenameMessage *rm)
639{
640 uint16_t old_name_len;
641 struct Ego *ego;
642 char *old_name;
643 char *new_name;
644 struct RenameContext rename_ctx;
645 struct GNUNET_SERVICE_Client *client = cls;
646 char *fn_old;
647 char *fn_new;
648 const char *old_name_tmp;
649
650 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received RENAME message from client\n");
651 old_name_len = ntohs (rm->old_name_len);
652 old_name_tmp = (const char *) &rm[1];
653 old_name = GNUNET_strdup (old_name_tmp);
654 GNUNET_STRINGS_utf8_tolower (old_name_tmp, old_name);
655 new_name = GNUNET_strdup (&old_name_tmp[old_name_len]);
656 GNUNET_STRINGS_utf8_tolower (&old_name_tmp[old_name_len], new_name);
657
658 /* check if new name is already in use */
659 for (ego = ego_head; NULL != ego; ego = ego->next)
660 {
661 if (0 == strcmp (ego->identifier, new_name))
662 {
663 send_result_code (client, GNUNET_EC_IDENTITY_NAME_CONFLICT);
664 GNUNET_SERVICE_client_continue (client);
665 GNUNET_free (old_name);
666 GNUNET_free (new_name);
667 return;
668 }
669 }
670
671 /* locate old name and, if found, perform rename */
672 for (ego = ego_head; NULL != ego; ego = ego->next)
673 {
674 if (0 == strcmp (ego->identifier, old_name))
675 {
676 fn_old = get_ego_filename (ego);
677 GNUNET_free (ego->identifier);
678 rename_ctx.old_name = old_name;
679 rename_ctx.new_name = new_name;
680 GNUNET_CONFIGURATION_iterate_sections (subsystem_cfg,
681 &handle_ego_rename,
682 &rename_ctx);
683 if (GNUNET_OK !=
684 GNUNET_CONFIGURATION_write (subsystem_cfg, subsystem_cfg_file))
685 GNUNET_log (
686 GNUNET_ERROR_TYPE_ERROR,
687 _ ("Failed to write subsystem default identifier map to `%s'.\n"),
688 subsystem_cfg_file);
689 ego->identifier = GNUNET_strdup (new_name);
690 fn_new = get_ego_filename (ego);
691 if (0 != rename (fn_old, fn_new))
692 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rename", fn_old);
693 GNUNET_free (fn_old);
694 GNUNET_free (fn_new);
695 GNUNET_free (old_name);
696 GNUNET_free (new_name);
697 notify_listeners (ego);
698 send_result_code (client, GNUNET_EC_NONE);
699 GNUNET_SERVICE_client_continue (client);
700 return;
701 }
702 }
703
704 /* failed to locate old name */
705 send_result_code (client, GNUNET_EC_IDENTITY_NOT_FOUND);
706 GNUNET_free (old_name);
707 GNUNET_free (new_name);
708 GNUNET_SERVICE_client_continue (client);
709}
710
711
712/**
713 * An ego was removed, remove it from all subsystems where it is
714 * currently set as the default.
715 *
716 * @param cls name of the removed ego (const char *)
717 * @param section a section in the configuration to process
718 */
719static void
720handle_ego_delete (void *cls, const char *section)
721{
722 const char *identifier = cls;
723 char *id;
724
725 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (subsystem_cfg,
726 section,
727 "DEFAULT_IDENTIFIER",
728 &id))
729 return;
730 if (0 != strcmp (id, identifier))
731 {
732 GNUNET_free (id);
733 return;
734 }
735 GNUNET_CONFIGURATION_set_value_string (subsystem_cfg,
736 section,
737 "DEFAULT_IDENTIFIER",
738 NULL);
739 GNUNET_free (id);
740}
741
742
743/**
744 * Checks a #GNUNET_MESSAGE_TYPE_IDENTITY_DELETE message
745 *
746 * @param cls client sending the message
747 * @param msg message of type `struct DeleteMessage`
748 * @return #GNUNET_OK if @a msg is well-formed
749 */
750static int
751check_delete_message (void *cls, const struct DeleteMessage *msg)
752{
753 uint16_t size;
754 uint16_t name_len;
755 const char *name;
756
757 size = ntohs (msg->header.size);
758 if (size <= sizeof(struct DeleteMessage))
759 {
760 GNUNET_break (0);
761 return GNUNET_SYSERR;
762 }
763 name = (const char *) &msg[1];
764 name_len = ntohs (msg->name_len);
765 if ((name_len + sizeof(struct DeleteMessage) != size) ||
766 (0 != ntohs (msg->reserved)) || ('\0' != name[name_len - 1]))
767 {
768 GNUNET_break (0);
769 return GNUNET_SYSERR;
770 }
771 return GNUNET_OK;
772}
773
774
775/**
776 * Handler for DELETE message from client, creates
777 * new identity.
778 *
779 * @param cls unused
780 * @param dm the message received
781 */
782static void
783handle_delete_message (void *cls, const struct DeleteMessage *dm)
784{
785 struct Ego *ego;
786 char *name;
787 char *fn;
788 struct GNUNET_SERVICE_Client *client = cls;
789
790 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received DELETE message from client\n");
791 name = GNUNET_strdup ((const char *) &dm[1]);
792 GNUNET_STRINGS_utf8_tolower ((const char *) &dm[1], name);
793
794 for (ego = ego_head; NULL != ego; ego = ego->next)
795 {
796 if (0 == strcmp (ego->identifier, name))
797 {
798 GNUNET_CONTAINER_DLL_remove (ego_head, ego_tail, ego);
799 GNUNET_CONFIGURATION_iterate_sections (subsystem_cfg,
800 &handle_ego_delete,
801 ego->identifier);
802 if (GNUNET_OK !=
803 GNUNET_CONFIGURATION_write (subsystem_cfg, subsystem_cfg_file))
804 GNUNET_log (
805 GNUNET_ERROR_TYPE_ERROR,
806 _ ("Failed to write subsystem default identifier map to `%s'.\n"),
807 subsystem_cfg_file);
808 fn = get_ego_filename (ego);
809 if (0 != unlink (fn))
810 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
811 GNUNET_free (fn);
812 GNUNET_free (ego->identifier);
813 ego->identifier = NULL;
814 notify_listeners (ego);
815 GNUNET_free (ego);
816 GNUNET_free (name);
817 send_result_code (client, GNUNET_EC_NONE);
818 GNUNET_SERVICE_client_continue (client);
819 return;
820 }
821 }
822
823 send_result_code (client, GNUNET_EC_IDENTITY_NOT_FOUND);
824 GNUNET_free (name);
825 GNUNET_SERVICE_client_continue (client);
826}
827
828
829static int
830read_from_file (const char *filename,
831 void *buf,
832 size_t buf_size)
833{
834 int fd;
835 struct stat sb;
836
837 fd = open (filename,
838 O_RDONLY);
839 if (-1 == fd)
840 {
841 memset (buf,
842 0,
843 buf_size);
844 return GNUNET_SYSERR;
845 }
846 if (0 != fstat (fd,
847 &sb))
848 {
849 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
850 "stat",
851 filename);
852 GNUNET_assert (0 == close (fd));
853 memset (buf,
854 0,
855 buf_size);
856 return GNUNET_SYSERR;
857 }
858 if (sb.st_size != buf_size)
859 {
860 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
861 "File `%s' has wrong size (%llu), expected %llu bytes\n",
862 filename,
863 (unsigned long long) sb.st_size,
864 (unsigned long long) buf_size);
865 GNUNET_assert (0 == close (fd));
866 memset (buf,
867 0,
868 buf_size);
869 return GNUNET_SYSERR;
870 }
871 if (buf_size !=
872 read (fd,
873 buf,
874 buf_size))
875 {
876 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
877 "read",
878 filename);
879 GNUNET_assert (0 == close (fd));
880 memset (buf,
881 0,
882 buf_size);
883 return GNUNET_SYSERR;
884 }
885 GNUNET_assert (0 == close (fd));
886 return GNUNET_OK;
887}
888
889
890/**
891 * Process the given file from the "EGODIR". Parses the file
892 * and creates the respective 'struct Ego' in memory.
893 *
894 * @param cls NULL
895 * @param filename name of the file to parse
896 * @return #GNUNET_OK to continue to iterate,
897 * #GNUNET_NO to stop iteration with no error,
898 * #GNUNET_SYSERR to abort iteration with error!
899 */
900static int
901process_ego_file (void *cls,
902 const char *filename)
903{
904 struct Ego *ego;
905 const char *fn;
906
907 fn = strrchr (filename, (int) DIR_SEPARATOR);
908 if (NULL == fn)
909 {
910 GNUNET_break (0);
911 return GNUNET_OK;
912 }
913 ego = GNUNET_new (struct Ego);
914 if (GNUNET_OK !=
915 read_from_file (filename,
916 &ego->pk,
917 sizeof (ego->pk)))
918 {
919 GNUNET_free (ego);
920 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
921 _ ("Failed to parse ego information in `%s'\n"),
922 filename);
923 return GNUNET_OK;
924 }
925 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
926 "Loaded ego `%s'\n",
927 fn + 1);
928 ego->identifier = GNUNET_strdup (fn + 1);
929 GNUNET_CONTAINER_DLL_insert (ego_head, ego_tail, ego);
930 return GNUNET_OK;
931}
932
933
934/**
935 * Handle network size estimate clients.
936 *
937 * @param cls closure
938 * @param server the initialized server
939 * @param c configuration to use
940 */
941static void
942run (void *cls,
943 const struct GNUNET_CONFIGURATION_Handle *c,
944 struct GNUNET_SERVICE_Handle *service)
945{
946 cfg = c;
947 nc = GNUNET_notification_context_create (1);
948 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
949 "identity",
950 "EGODIR",
951 &ego_directory))
952 {
953 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "identity", "EGODIR");
954 GNUNET_SCHEDULER_shutdown ();
955 return;
956 }
957 if (GNUNET_OK !=
958 GNUNET_CONFIGURATION_get_value_filename (cfg,
959 "identity",
960 "SUBSYSTEM_CFG",
961 &subsystem_cfg_file))
962 {
963 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
964 "identity",
965 "SUBSYSTEM_CFG");
966 GNUNET_SCHEDULER_shutdown ();
967 return;
968 }
969 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
970 "Loading subsystem configuration `%s'\n",
971 subsystem_cfg_file);
972 subsystem_cfg = GNUNET_CONFIGURATION_create ();
973 if ((GNUNET_YES == GNUNET_DISK_file_test (subsystem_cfg_file)) &&
974 (GNUNET_OK !=
975 GNUNET_CONFIGURATION_parse (subsystem_cfg, subsystem_cfg_file)))
976 {
977 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
978 _ (
979 "Failed to parse subsystem identity configuration file `%s'\n"),
980 subsystem_cfg_file);
981 GNUNET_SCHEDULER_shutdown ();
982 return;
983 }
984 stats = GNUNET_STATISTICS_create ("identity", cfg);
985 if (GNUNET_OK != GNUNET_DISK_directory_create (ego_directory))
986 {
987 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
988 _ ("Failed to create directory `%s' for storing egos\n"),
989 ego_directory);
990 }
991 GNUNET_DISK_directory_scan (ego_directory,
992 &process_ego_file,
993 NULL);
994 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
995}
996
997
998/**
999 * Define "main" method using service macro.
1000 */
1001GNUNET_SERVICE_MAIN (
1002 "identity",
1003 GNUNET_SERVICE_OPTION_NONE,
1004 &run,
1005 &client_connect_cb,
1006 &client_disconnect_cb,
1007 NULL,
1008 GNUNET_MQ_hd_fixed_size (start_message,
1009 GNUNET_MESSAGE_TYPE_IDENTITY_START,
1010 struct GNUNET_MessageHeader,
1011 NULL),
1012 GNUNET_MQ_hd_var_size (lookup_message,
1013 GNUNET_MESSAGE_TYPE_IDENTITY_LOOKUP,
1014 struct LookupMessage,
1015 NULL),
1016 GNUNET_MQ_hd_var_size (lookup_by_suffix_message,
1017 GNUNET_MESSAGE_TYPE_IDENTITY_LOOKUP_BY_SUFFIX,
1018 struct LookupMessage,
1019 NULL),
1020 GNUNET_MQ_hd_var_size (create_message,
1021 GNUNET_MESSAGE_TYPE_IDENTITY_CREATE,
1022 struct CreateRequestMessage,
1023 NULL),
1024 GNUNET_MQ_hd_var_size (rename_message,
1025 GNUNET_MESSAGE_TYPE_IDENTITY_RENAME,
1026 struct RenameMessage,
1027 NULL),
1028 GNUNET_MQ_hd_var_size (delete_message,
1029 GNUNET_MESSAGE_TYPE_IDENTITY_DELETE,
1030 struct DeleteMessage,
1031 NULL),
1032 GNUNET_MQ_handler_end ());
1033
1034
1035/* end of gnunet-service-identity.c */
diff --git a/src/service/identity/identity.conf.in b/src/service/identity/identity.conf.in
new file mode 100644
index 000000000..f5d454323
--- /dev/null
+++ b/src/service/identity/identity.conf.in
@@ -0,0 +1,17 @@
1[identity]
2START_ON_DEMAND = @START_ON_DEMAND@
3RUN_PER_USER = YES
4@JAVAPORT@PORT = 2108
5HOSTNAME = localhost
6BINARY = gnunet-service-identity
7ACCEPT_FROM = 127.0.0.1;
8ACCEPT_FROM6 = ::1;
9UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-identity.sock
10UNIX_MATCH_UID = NO
11UNIX_MATCH_GID = YES
12
13# Directory where we store information about our egos
14EGODIR = $GNUNET_DATA_HOME/identity/egos/
15
16# File where we store default identities for subsystems
17SUBSYSTEM_CFG = $GNUNET_CONFIG_HOME/identity/subsystem_defaults.conf
diff --git a/src/service/identity/identity.h b/src/service/identity/identity.h
new file mode 100644
index 000000000..acb403736
--- /dev/null
+++ b/src/service/identity/identity.h
@@ -0,0 +1,226 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Christian Grothoff
23 * @file identity/identity.h
24 *
25 * @brief Common type definitions for the identity
26 * service and API.
27 */
28#ifndef IDENTITY_H
29#define IDENTITY_H
30
31#include "gnunet_common.h"
32
33/**
34 * Handle for an ego.
35 */
36struct GNUNET_IDENTITY_Ego
37{
38 /**
39 * Hash of the private key of this ego.
40 */
41 struct GNUNET_HashCode id;
42
43 /**
44 * The identity key pair
45 */
46 struct GNUNET_CRYPTO_PublicKey pub;
47
48 /**
49 * The identity key pair
50 */
51 struct GNUNET_CRYPTO_PrivateKey pk;
52
53 /**
54 * Current name associated with this ego.
55 */
56 char *name;
57
58 /**
59 * Client context associated with this ego.
60 */
61 void *ctx;
62
63 /**
64 * Set to true once @e pub was initialized
65 */
66 bool pub_initialized;
67};
68
69
70
71
72GNUNET_NETWORK_STRUCT_BEGIN
73
74
75/**
76 * Answer from service to client about last operation;
77 * GET_DEFAULT maybe answered with this message on failure;
78 * CREATE and RENAME will always be answered with this message.
79 */
80struct ResultCodeMessage
81{
82 /**
83 * Type: #GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE
84 */
85 struct GNUNET_MessageHeader header;
86
87 /**
88 * Status code for the last operation, in NBO.
89 * (currently not used).
90 */
91 uint32_t result_code GNUNET_PACKED;
92};
93
94
95/**
96 * Client informs service about desire to lookup a (single) pseudonym.
97 */
98struct LookupMessage
99{
100 /**
101 * Type: #GNUNET_MESSAGE_TYPE_IDENTITY_LOOKUP or
102 * #GNUNET_MESSAGE_TYPE_IDENTITY_LOOKUP_BY_SUFFIX
103 */
104 struct GNUNET_MessageHeader header;
105
106 /* followed by 0-terminated ego name */
107};
108
109
110/**
111 * Service informs client about status of a pseudonym.
112 */
113struct UpdateMessage
114{
115 /**
116 * Type: #GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE
117 */
118 struct GNUNET_MessageHeader header;
119
120 /**
121 * Number of bytes in ego name string including 0-termination, in NBO;
122 * 0 if the ego was deleted.
123 */
124 uint16_t name_len GNUNET_PACKED;
125
126 /**
127 * Usually #GNUNET_NO, #GNUNET_YES to signal end of list.
128 */
129 uint16_t end_of_list GNUNET_PACKED;
130
131 /**
132 * Key length
133 */
134 uint16_t key_len GNUNET_PACKED;
135
136 /**
137 * Reserved (alignment)
138 */
139 uint16_t reserved GNUNET_PACKED;
140
141 /* followed by 0-terminated ego name */
142 /* followed by the private key */
143};
144
145
146/**
147 * Client requests creation of an identity. Service
148 * will respond with a result code.
149 */
150struct CreateRequestMessage
151{
152 /**
153 * Type: #GNUNET_MESSAGE_TYPE_IDENTITY_CREATE
154 */
155 struct GNUNET_MessageHeader header;
156
157 /**
158 * Number of bytes in identity name string including 0-termination, in NBO.
159 */
160 uint16_t name_len GNUNET_PACKED;
161
162 /**
163 * Key length
164 */
165 uint16_t key_len GNUNET_PACKED;
166
167 /*
168 * Followed by the private key
169 * followed by 0-terminated identity name */
170};
171
172
173/**
174 * Client requests renaming of an identity. Service
175 * will respond with a result code.
176 */
177struct RenameMessage
178{
179 /**
180 * Type: #GNUNET_MESSAGE_TYPE_IDENTITY_RENAME
181 */
182 struct GNUNET_MessageHeader header;
183
184 /**
185 * Number of characters in the old name including 0-termination, in NBO.
186 */
187 uint16_t old_name_len GNUNET_PACKED;
188
189 /**
190 * Number of characters in the new name including 0-termination, in NBO.
191 */
192 uint16_t new_name_len GNUNET_PACKED;
193
194 /* followed by 0-terminated old name */
195 /* followed by 0-terminated new name */
196};
197
198
199/**
200 * Client requests deletion of an identity. Service
201 * will respond with a result code.
202 */
203struct DeleteMessage
204{
205 /**
206 * Type: #GNUNET_MESSAGE_TYPE_IDENTITY_DELETE
207 */
208 struct GNUNET_MessageHeader header;
209
210 /**
211 * Number of characters in the name including 0-termination, in NBO.
212 */
213 uint16_t name_len GNUNET_PACKED;
214
215 /**
216 * Always zero.
217 */
218 uint16_t reserved GNUNET_PACKED;
219
220 /* followed by 0-terminated name */
221};
222
223GNUNET_NETWORK_STRUCT_END
224
225
226#endif
diff --git a/src/service/identity/identity_api.c b/src/service/identity/identity_api.c
new file mode 100644
index 000000000..fe789d643
--- /dev/null
+++ b/src/service/identity/identity_api.c
@@ -0,0 +1,768 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2016, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file identity/identity_api.c
23 * @brief api to interact with the identity service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_error_codes.h"
30#include "gnunet_protocols.h"
31#include "gnunet_identity_service.h"
32#include "identity.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "identity-api", __VA_ARGS__)
35
36
37/**
38 * Handle for an operation with the identity service.
39 */
40struct GNUNET_IDENTITY_Operation
41{
42 /**
43 * Main identity handle.
44 */
45 struct GNUNET_IDENTITY_Handle *h;
46
47 /**
48 * We keep operations in a DLL.
49 */
50 struct GNUNET_IDENTITY_Operation *next;
51
52 /**
53 * We keep operations in a DLL.
54 */
55 struct GNUNET_IDENTITY_Operation *prev;
56
57 /**
58 * Message to send to the identity service.
59 * Allocated at the end of this struct.
60 */
61 const struct GNUNET_MessageHeader *msg;
62
63 /**
64 * Continuation to invoke with the result of the transmission; @e cb
65 * and @e create_cont will be NULL in this case.
66 */
67 GNUNET_IDENTITY_Continuation cont;
68
69 /**
70 * Continuation to invoke with the result of the transmission; @e cb
71 * and @a cb will be NULL in this case.
72 */
73 GNUNET_IDENTITY_CreateContinuation create_cont;
74
75 /**
76 * Private key to return to @e create_cont, or NULL.
77 */
78 struct GNUNET_CRYPTO_PrivateKey pk;
79
80 /**
81 * Continuation to invoke with the result of the transmission for
82 * 'get' operations (@e cont and @a create_cont will be NULL in this case).
83 */
84 GNUNET_IDENTITY_Callback cb;
85
86 /**
87 * Closure for @e cont or @e cb.
88 */
89 void *cls;
90};
91
92
93/**
94 * Handle for the service.
95 */
96struct GNUNET_IDENTITY_Handle
97{
98 /**
99 * Configuration to use.
100 */
101 const struct GNUNET_CONFIGURATION_Handle *cfg;
102
103 /**
104 * Connection to service.
105 */
106 struct GNUNET_MQ_Handle *mq;
107
108 /**
109 * Hash map from the hash of the private key to the
110 * respective `GNUNET_IDENTITY_Ego` handle.
111 */
112 struct GNUNET_CONTAINER_MultiHashMap *egos;
113
114 /**
115 * Function to call when we receive updates.
116 */
117 GNUNET_IDENTITY_Callback cb;
118
119 /**
120 * Closure for @e cb.
121 */
122 void *cb_cls;
123
124 /**
125 * Head of active operations.
126 */
127 struct GNUNET_IDENTITY_Operation *op_head;
128
129 /**
130 * Tail of active operations.
131 */
132 struct GNUNET_IDENTITY_Operation *op_tail;
133
134 /**
135 * Task doing exponential back-off trying to reconnect.
136 */
137 struct GNUNET_SCHEDULER_Task *reconnect_task;
138
139 /**
140 * Time for next connect retry.
141 */
142 struct GNUNET_TIME_Relative reconnect_delay;
143
144 /**
145 * Are we polling for incoming messages right now?
146 */
147 int in_receive;
148};
149
150
151/**
152 * Obtain the ego representing 'anonymous' users.
153 *
154 * @return handle for the anonymous user, MUST NOT be freed
155 */
156struct GNUNET_IDENTITY_Ego *
157GNUNET_IDENTITY_ego_get_anonymous ()
158{
159 static struct GNUNET_IDENTITY_Ego anon;
160 static int setup;
161 ssize_t key_len;
162
163 if (setup)
164 return &anon;
165 anon.pk.type = htonl (GNUNET_PUBLIC_KEY_TYPE_ECDSA);
166 anon.pub.type = htonl (GNUNET_PUBLIC_KEY_TYPE_ECDSA);
167 anon.pk.ecdsa_key = *GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
168 key_len = GNUNET_CRYPTO_private_key_get_length (&anon.pk);
169 GNUNET_assert (0 < key_len);
170 GNUNET_CRYPTO_hash (&anon.pk,
171 key_len,
172 &anon.id);
173 setup = 1;
174 return &anon;
175}
176
177
178
179/**
180 * Try again to connect to the identity service.
181 *
182 * @param cls handle to the identity service.
183 */
184static void
185reconnect (void *cls);
186
187
188/**
189 * Free ego from hash map.
190 *
191 * @param cls identity service handle
192 * @param key unused
193 * @param value ego to free
194 * @return #GNUNET_OK (continue to iterate)
195 */
196static int
197free_ego (void *cls,
198 const struct GNUNET_HashCode *key,
199 void *value)
200{
201 struct GNUNET_IDENTITY_Handle *h = cls;
202 struct GNUNET_IDENTITY_Ego *ego = value;
203
204 if (NULL != h->cb)
205 h->cb (h->cb_cls, ego,
206 &ego->ctx,
207 NULL);
208 GNUNET_free (ego->name);
209 GNUNET_assert (GNUNET_YES ==
210 GNUNET_CONTAINER_multihashmap_remove (h->egos,
211 key,
212 value));
213 GNUNET_free (ego);
214 return GNUNET_OK;
215}
216
217
218/**
219 * Reschedule a connect attempt to the service.
220 *
221 * @param h transport service to reconnect
222 */
223static void
224reschedule_connect (struct GNUNET_IDENTITY_Handle *h)
225{
226 struct GNUNET_IDENTITY_Operation *op;
227
228 GNUNET_assert (NULL == h->reconnect_task);
229
230 if (NULL != h->mq)
231 {
232 GNUNET_MQ_destroy (h->mq);
233 h->mq = NULL;
234 }
235 while (NULL != (op = h->op_head))
236 {
237 GNUNET_CONTAINER_DLL_remove (h->op_head,
238 h->op_tail,
239 op);
240 if (NULL != op->cont)
241 op->cont (op->cls,
242 GNUNET_EC_SERVICE_COMMUNICATION_FAILED);
243 else if (NULL != op->cb)
244 op->cb (op->cls, NULL, NULL, NULL);
245 else if (NULL != op->create_cont)
246 op->create_cont (op->cls,
247 NULL,
248 GNUNET_EC_SERVICE_COMMUNICATION_FAILED);
249 GNUNET_free (op);
250 }
251 GNUNET_CONTAINER_multihashmap_iterate (h->egos,
252 &free_ego,
253 h);
254 LOG (GNUNET_ERROR_TYPE_DEBUG,
255 "Scheduling task to reconnect to identity service in %s.\n",
256 GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay,
257 GNUNET_YES));
258 h->reconnect_task =
259 GNUNET_SCHEDULER_add_delayed (h->reconnect_delay,
260 &reconnect,
261 h);
262 h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
263}
264
265
266/**
267 * Generic error handler, called with the appropriate error code and
268 * the same closure specified at the creation of the message queue.
269 * Not every message queue implementation supports an error handler.
270 *
271 * @param cls closure with the `struct GNUNET_IDENTITY_Handle *`
272 * @param error error code
273 */
274static void
275mq_error_handler (void *cls,
276 enum GNUNET_MQ_Error error)
277{
278 struct GNUNET_IDENTITY_Handle *h = cls;
279
280 reschedule_connect (h);
281}
282
283
284/**
285 * We received a result code from the service.
286 *
287 * @param cls closure
288 * @param rcm result message received
289 */
290static void
291handle_identity_result_code (void *cls,
292 const struct ResultCodeMessage *rcm)
293{
294 struct GNUNET_IDENTITY_Handle *h = cls;
295 struct GNUNET_IDENTITY_Operation *op;
296 enum GNUNET_ErrorCode ec = ntohl (rcm->result_code);
297
298 op = h->op_head;
299 if (NULL == op)
300 {
301 GNUNET_break (0);
302 reschedule_connect (h);
303 return;
304 }
305 GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op);
306 if (NULL != op->cont)
307 op->cont (op->cls, ec);
308 else if (NULL != op->cb)
309 op->cb (op->cls, NULL, NULL, NULL);
310 else if (NULL != op->create_cont)
311 op->create_cont (op->cls, (GNUNET_EC_NONE == ec) ? &op->pk : NULL, ec);
312 GNUNET_free (op);
313}
314
315
316/**
317 * Check validity of identity update message.
318 *
319 * @param cls closure
320 * @param um message received
321 * @return #GNUNET_OK if the message is well-formed
322 */
323static int
324check_identity_update (void *cls,
325 const struct UpdateMessage *um)
326{
327 uint16_t size = ntohs (um->header.size);
328 uint16_t name_len = ntohs (um->name_len);
329 const char *str = (const char *) &um[1];
330
331 if ((size < name_len + sizeof(struct UpdateMessage)) ||
332 ((0 != name_len) && ('\0' != str[name_len - 1])))
333 {
334 GNUNET_break (0);
335 return GNUNET_SYSERR;
336 }
337 return GNUNET_OK;
338}
339
340
341/**
342 * Handle identity update message.
343 *
344 * @param cls closure
345 * @param um message received
346 */
347static void
348handle_identity_update (void *cls,
349 const struct UpdateMessage *um)
350{
351 struct GNUNET_IDENTITY_Handle *h = cls;
352 uint16_t name_len = ntohs (um->name_len);
353 const char *str;
354 size_t key_len;
355 size_t kb_read;
356 struct GNUNET_HashCode id;
357 struct GNUNET_IDENTITY_Ego *ego;
358 struct GNUNET_CRYPTO_PrivateKey private_key;
359 const char *tmp;
360
361 if (GNUNET_YES == ntohs (um->end_of_list))
362 {
363 /* end of initial list of data */
364 if (NULL != h->cb)
365 h->cb (h->cb_cls, NULL, NULL, NULL);
366 return;
367 }
368 tmp = (const char*) &um[1];
369 str = (0 == name_len) ? NULL : tmp;
370 memset (&private_key, 0, sizeof (private_key));
371 key_len = ntohs (um->key_len);
372 GNUNET_assert (GNUNET_SYSERR !=
373 GNUNET_CRYPTO_read_private_key_from_buffer (tmp + name_len,
374 key_len,
375 &private_key,
376 &kb_read));
377 GNUNET_assert (0 <= GNUNET_CRYPTO_private_key_get_length (&private_key));
378 GNUNET_CRYPTO_hash (&private_key,
379 GNUNET_CRYPTO_private_key_get_length (&private_key),
380 &id);
381 ego = GNUNET_CONTAINER_multihashmap_get (h->egos,
382 &id);
383 if (NULL == ego)
384 {
385 /* ego was created */
386 if (NULL == str)
387 {
388 /* deletion of unknown ego? not allowed */
389 GNUNET_break (0);
390 reschedule_connect (h);
391 return;
392 }
393 ego = GNUNET_new (struct GNUNET_IDENTITY_Ego);
394 ego->pub_initialized = GNUNET_NO;
395 ego->pk = private_key;
396 ego->name = GNUNET_strdup (str);
397 ego->id = id;
398 GNUNET_assert (GNUNET_YES ==
399 GNUNET_CONTAINER_multihashmap_put (
400 h->egos,
401 &ego->id,
402 ego,
403 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
404 }
405 if (NULL == str)
406 {
407 /* ego was deleted */
408 GNUNET_assert (GNUNET_YES ==
409 GNUNET_CONTAINER_multihashmap_remove (h->egos,
410 &ego->id,
411 ego));
412 }
413 else
414 {
415 /* ego changed name */
416 GNUNET_free (ego->name);
417 ego->name = GNUNET_strdup (str);
418 }
419 /* inform application about change */
420 if (NULL != h->cb)
421 h->cb (h->cb_cls,
422 ego,
423 &ego->ctx,
424 str);
425 /* complete deletion */
426 if (NULL == str)
427 {
428 GNUNET_free (ego->name);
429 GNUNET_free (ego);
430 }
431}
432
433
434/**
435 * Try again to connect to the identity service.
436 *
437 * @param cls handle to the identity service.
438 */
439static void
440reconnect (void *cls)
441{
442 struct GNUNET_IDENTITY_Handle *h = cls;
443 struct GNUNET_MQ_MessageHandler handlers[] = {
444 GNUNET_MQ_hd_fixed_size (identity_result_code,
445 GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE,
446 struct ResultCodeMessage,
447 h),
448 GNUNET_MQ_hd_var_size (identity_update,
449 GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE,
450 struct UpdateMessage,
451 h),
452 GNUNET_MQ_handler_end ()
453 };
454 struct GNUNET_MQ_Envelope *env;
455 struct GNUNET_MessageHeader *msg;
456
457 h->reconnect_task = NULL;
458 LOG (GNUNET_ERROR_TYPE_DEBUG,
459 "Connecting to identity service.\n");
460 GNUNET_assert (NULL == h->mq);
461 h->mq = GNUNET_CLIENT_connect (h->cfg,
462 "identity",
463 handlers,
464 &mq_error_handler,
465 h);
466 if (NULL == h->mq)
467 return;
468 if (NULL != h->cb)
469 {
470 env = GNUNET_MQ_msg (msg,
471 GNUNET_MESSAGE_TYPE_IDENTITY_START);
472 GNUNET_MQ_send (h->mq,
473 env);
474 }
475}
476
477
478/**
479 * Connect to the identity service.
480 *
481 * @param cfg the configuration to use
482 * @param cb function to call on all identity events, can be NULL
483 * @param cb_cls closure for @a cb
484 * @return handle to use
485 */
486struct GNUNET_IDENTITY_Handle *
487GNUNET_IDENTITY_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
488 GNUNET_IDENTITY_Callback cb,
489 void *cb_cls)
490{
491 struct GNUNET_IDENTITY_Handle *h;
492
493 h = GNUNET_new (struct GNUNET_IDENTITY_Handle);
494 h->cfg = cfg;
495 h->cb = cb;
496 h->cb_cls = cb_cls;
497 h->egos = GNUNET_CONTAINER_multihashmap_create (16,
498 GNUNET_YES);
499 reconnect (h);
500 if (NULL == h->mq)
501 {
502 GNUNET_free (h);
503 return NULL;
504 }
505 return h;
506}
507
508
509
510/**
511 * Obtain the ECC key associated with a ego.
512 *
513 * @param ego the ego
514 * @return associated ECC key, valid as long as the ego is valid
515 */
516const struct GNUNET_CRYPTO_PrivateKey *
517GNUNET_IDENTITY_ego_get_private_key (const struct GNUNET_IDENTITY_Ego *ego)
518{
519 return &ego->pk;
520}
521
522/**
523 * Get the identifier (public key) of an ego.
524 *
525 * @param ego identity handle with the private key
526 * @param pk set to ego's public key
527 */
528void
529GNUNET_IDENTITY_ego_get_public_key (struct GNUNET_IDENTITY_Ego *ego,
530 struct GNUNET_CRYPTO_PublicKey *pk)
531{
532 if (GNUNET_NO == ego->pub_initialized)
533 {
534 GNUNET_CRYPTO_key_get_public (&ego->pk, &ego->pub);
535 ego->pub_initialized = GNUNET_YES;
536 }
537 *pk = ego->pub;
538}
539
540static enum GNUNET_GenericReturnValue
541private_key_create (enum GNUNET_CRYPTO_KeyType ktype,
542 struct GNUNET_CRYPTO_PrivateKey *key)
543{
544 key->type = htonl (ktype);
545 switch (ktype)
546 {
547 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
548 GNUNET_CRYPTO_ecdsa_key_create (&key->ecdsa_key);
549 break;
550 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
551 GNUNET_CRYPTO_eddsa_key_create (&key->eddsa_key);
552 break;
553 default:
554 GNUNET_break (0);
555 return GNUNET_SYSERR;
556 }
557 return GNUNET_OK;
558}
559
560struct GNUNET_IDENTITY_Operation *
561GNUNET_IDENTITY_create (struct GNUNET_IDENTITY_Handle *h,
562 const char *name,
563 const struct GNUNET_CRYPTO_PrivateKey *privkey,
564 enum GNUNET_CRYPTO_KeyType ktype,
565 GNUNET_IDENTITY_CreateContinuation cont,
566 void *cont_cls)
567{
568 struct GNUNET_CRYPTO_PrivateKey private_key;
569 struct GNUNET_IDENTITY_Operation *op;
570 struct GNUNET_MQ_Envelope *env;
571 struct CreateRequestMessage *crm;
572 size_t slen;
573 size_t key_len;
574
575 if (NULL == h->mq)
576 return NULL;
577 slen = strlen (name) + 1;
578 if (slen >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct CreateRequestMessage))
579 {
580 GNUNET_break (0);
581 return NULL;
582 }
583 op = GNUNET_new (struct GNUNET_IDENTITY_Operation);
584 op->h = h;
585 op->create_cont = cont;
586 op->cls = cont_cls;
587 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
588 if (NULL == privkey)
589 {
590 GNUNET_assert (GNUNET_OK ==
591 private_key_create (ktype, &private_key));
592 }
593 else
594 private_key = *privkey;
595 key_len = GNUNET_CRYPTO_private_key_get_length (&private_key);
596 env = GNUNET_MQ_msg_extra (crm, slen + key_len,
597 GNUNET_MESSAGE_TYPE_IDENTITY_CREATE);
598 crm->name_len = htons (slen);
599 GNUNET_CRYPTO_write_private_key_to_buffer (&private_key,
600 &crm[1],
601 key_len);
602 crm->key_len = htons (key_len);
603 op->pk = private_key;
604 GNUNET_memcpy ((char*) &crm[1] + key_len, name, slen);
605 GNUNET_MQ_send (h->mq, env);
606 return op;
607}
608
609
610/**
611 * Renames an existing identity.
612 *
613 * @param h identity service to use
614 * @param old_name old name
615 * @param new_name desired new name
616 * @param cb function to call with the result (will only be called once)
617 * @param cb_cls closure for @a cb
618 * @return handle to abort the operation
619 */
620struct GNUNET_IDENTITY_Operation *
621GNUNET_IDENTITY_rename (struct GNUNET_IDENTITY_Handle *h,
622 const char *old_name,
623 const char *new_name,
624 GNUNET_IDENTITY_Continuation cb,
625 void *cb_cls)
626{
627 struct GNUNET_IDENTITY_Operation *op;
628 struct GNUNET_MQ_Envelope *env;
629 struct RenameMessage *grm;
630 size_t slen_old;
631 size_t slen_new;
632 char *dst;
633
634 if (NULL == h->mq)
635 return NULL;
636 slen_old = strlen (old_name) + 1;
637 slen_new = strlen (new_name) + 1;
638 if ((slen_old >= GNUNET_MAX_MESSAGE_SIZE) ||
639 (slen_new >= GNUNET_MAX_MESSAGE_SIZE) ||
640 (slen_old + slen_new >=
641 GNUNET_MAX_MESSAGE_SIZE - sizeof(struct RenameMessage)))
642 {
643 GNUNET_break (0);
644 return NULL;
645 }
646 op = GNUNET_new (struct GNUNET_IDENTITY_Operation);
647 op->h = h;
648 op->cont = cb;
649 op->cls = cb_cls;
650 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
651 env = GNUNET_MQ_msg_extra (grm,
652 slen_old + slen_new,
653 GNUNET_MESSAGE_TYPE_IDENTITY_RENAME);
654 grm->old_name_len = htons (slen_old);
655 grm->new_name_len = htons (slen_new);
656 dst = (char *) &grm[1];
657 GNUNET_memcpy (dst, old_name, slen_old);
658 GNUNET_memcpy (&dst[slen_old], new_name, slen_new);
659 GNUNET_MQ_send (h->mq, env);
660 return op;
661}
662
663
664/**
665 * Delete an existing identity.
666 *
667 * @param h identity service to use
668 * @param name name of the identity to delete
669 * @param cb function to call with the result (will only be called once)
670 * @param cb_cls closure for @a cb
671 * @return handle to abort the operation
672 */
673struct GNUNET_IDENTITY_Operation *
674GNUNET_IDENTITY_delete (struct GNUNET_IDENTITY_Handle *h,
675 const char *name,
676 GNUNET_IDENTITY_Continuation cb,
677 void *cb_cls)
678{
679 struct GNUNET_IDENTITY_Operation *op;
680 struct GNUNET_MQ_Envelope *env;
681 struct DeleteMessage *gdm;
682 size_t slen;
683
684 if (NULL == h->mq)
685 return NULL;
686 slen = strlen (name) + 1;
687 if (slen >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct DeleteMessage))
688 {
689 GNUNET_break (0);
690 return NULL;
691 }
692 op = GNUNET_new (struct GNUNET_IDENTITY_Operation);
693 op->h = h;
694 op->cont = cb;
695 op->cls = cb_cls;
696 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
697 env = GNUNET_MQ_msg_extra (gdm, slen, GNUNET_MESSAGE_TYPE_IDENTITY_DELETE);
698 gdm->name_len = htons (slen);
699 gdm->reserved = htons (0);
700 GNUNET_memcpy (&gdm[1], name, slen);
701 GNUNET_MQ_send (h->mq, env);
702 return op;
703}
704
705
706/**
707 * Cancel an identity operation. Note that the operation MAY still
708 * be executed; this merely cancels the continuation; if the request
709 * was already transmitted, the service may still choose to complete
710 * the operation.
711 *
712 * @param op operation to cancel
713 */
714void
715GNUNET_IDENTITY_cancel (struct GNUNET_IDENTITY_Operation *op)
716{
717 op->cont = NULL;
718 op->cb = NULL;
719 op->create_cont = NULL;
720 memset (&op->pk,
721 0,
722 sizeof (op->pk));
723}
724
725
726/**
727 * Disconnect from identity service
728 *
729 * @param h handle to destroy
730 */
731void
732GNUNET_IDENTITY_disconnect (struct GNUNET_IDENTITY_Handle *h)
733{
734 struct GNUNET_IDENTITY_Operation *op;
735
736 GNUNET_assert (NULL != h);
737 if (h->reconnect_task != NULL)
738 {
739 GNUNET_SCHEDULER_cancel (h->reconnect_task);
740 h->reconnect_task = NULL;
741 }
742 if (NULL != h->egos)
743 {
744 GNUNET_CONTAINER_multihashmap_iterate (h->egos,
745 &free_ego,
746 h);
747 GNUNET_CONTAINER_multihashmap_destroy (h->egos);
748 h->egos = NULL;
749 }
750 while (NULL != (op = h->op_head))
751 {
752 GNUNET_break (NULL == op->cont);
753 GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op);
754 memset (&op->pk,
755 0,
756 sizeof (op->pk));
757 GNUNET_free (op);
758 }
759 if (NULL != h->mq)
760 {
761 GNUNET_MQ_destroy (h->mq);
762 h->mq = NULL;
763 }
764 GNUNET_free (h);
765}
766
767
768/* end of identity_api.c */
diff --git a/src/service/identity/identity_api_lookup.c b/src/service/identity/identity_api_lookup.c
new file mode 100644
index 000000000..03b229e08
--- /dev/null
+++ b/src/service/identity/identity_api_lookup.c
@@ -0,0 +1,244 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file identity/identity_api_lookup.c
23 * @brief api to lookup an ego
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_identity_service.h"
29#include "identity.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "identity-api", __VA_ARGS__)
32
33
34/**
35 * Handle for ego lookup.
36 */
37struct GNUNET_IDENTITY_EgoLookup
38{
39 /**
40 * Connection to service.
41 */
42 struct GNUNET_MQ_Handle *mq;
43
44 /**
45 * Name of the ego we are looking up.
46 */
47 char *name;
48
49 /**
50 * Function to call with the result.
51 */
52 GNUNET_IDENTITY_EgoCallback cb;
53
54 /**
55 * Closure for @e cb
56 */
57 void *cb_cls;
58};
59
60
61/**
62 * We received a result code from the service. Check the message
63 * is well-formed.
64 *
65 * @param cls closure
66 * @param rcm result message received
67 * @return #GNUNET_OK if the message is well-formed
68 */
69static int
70check_identity_result_code (void *cls, const struct ResultCodeMessage *rcm)
71{
72 if (sizeof(*rcm) != htons (rcm->header.size))
73 GNUNET_MQ_check_zero_termination (rcm);
74 return GNUNET_OK;
75}
76
77
78/**
79 * We received a result code from the service.
80 *
81 * @param cls closure
82 * @param rcm result message received
83 */
84static void
85handle_identity_result_code (void *cls, const struct ResultCodeMessage *rcm)
86{
87 struct GNUNET_IDENTITY_EgoLookup *el = cls;
88
89 el->cb (el->cb_cls, NULL);
90 GNUNET_IDENTITY_ego_lookup_cancel (el);
91}
92
93
94/**
95 * Check validity of identity update message.
96 *
97 * @param cls closure
98 * @param um message received
99 * @return #GNUNET_OK if the message is well-formed
100 */
101static int
102check_identity_update (void *cls, const struct UpdateMessage *um)
103{
104 uint16_t size = ntohs (um->header.size);
105 uint16_t name_len = ntohs (um->name_len);
106 const char *str = (const char *) &um[1];
107
108 if ((size < name_len + sizeof(struct UpdateMessage)) ||
109 ((0 != name_len) && ('\0' != str[name_len - 1])))
110 {
111 GNUNET_break (0);
112 return GNUNET_SYSERR;
113 }
114 return GNUNET_OK;
115}
116
117
118/**
119 * Handle identity update message.
120 *
121 * @param cls closure
122 * @param um message received
123 */
124static void
125handle_identity_update (void *cls, const struct UpdateMessage *um)
126{
127 struct GNUNET_IDENTITY_EgoLookup *el = cls;
128 uint16_t name_len = ntohs (um->name_len);
129 const char *str;
130 size_t key_len;
131 size_t kb_read;
132 struct GNUNET_HashCode id;
133 struct GNUNET_IDENTITY_Ego ego;
134 struct GNUNET_CRYPTO_PrivateKey private_key;
135 const char *tmp;
136
137 memset (&ego, 0, sizeof (ego));
138
139 GNUNET_break (GNUNET_YES != ntohs (um->end_of_list));
140 tmp = (const char*) &um[1];
141 str = (0 == name_len) ? NULL : tmp;
142 memset (&private_key, 0, sizeof (private_key));
143 key_len = ntohs (um->header.size) - sizeof (*um) - name_len;
144 GNUNET_assert (GNUNET_SYSERR !=
145 GNUNET_CRYPTO_read_private_key_from_buffer (tmp + name_len,
146 key_len,
147 &private_key,
148 &kb_read));
149 GNUNET_assert (key_len == kb_read);
150 GNUNET_CRYPTO_hash (&private_key, sizeof (private_key), &id);
151 ego.pk = private_key;
152 ego.name = (char *) str;
153 ego.id = id;
154 el->cb (el->cb_cls, &ego);
155 GNUNET_IDENTITY_ego_lookup_cancel (el);
156}
157
158
159/**
160 * Generic error handler, called with the appropriate error code and
161 * the same closure specified at the creation of the message queue.
162 * Not every message queue implementation supports an error handler.
163 *
164 * @param cls closure with the `struct GNUNET_IDENTITY_EgoLookup *`
165 * @param error error code
166 */
167static void
168mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
169{
170 struct GNUNET_IDENTITY_EgoLookup *el = cls;
171
172 el->cb (el->cb_cls, NULL);
173}
174
175
176/**
177 * Lookup an ego by name.
178 *
179 * @param cfg configuration to use
180 * @param name name to look up
181 * @param cb callback to invoke with the result
182 * @param cb_cls closure for @a cb
183 * @return NULL on error
184 */
185struct GNUNET_IDENTITY_EgoLookup *
186GNUNET_IDENTITY_ego_lookup (const struct GNUNET_CONFIGURATION_Handle *cfg,
187 const char *name,
188 GNUNET_IDENTITY_EgoCallback cb,
189 void *cb_cls)
190{
191 struct GNUNET_IDENTITY_EgoLookup *el;
192 struct GNUNET_MQ_Envelope *env;
193 struct GNUNET_MessageHeader *req;
194 size_t nlen;
195
196 GNUNET_assert (NULL != cb);
197 el = GNUNET_new (struct GNUNET_IDENTITY_EgoLookup);
198 el->cb = cb;
199 el->cb_cls = cb_cls;
200 {
201 struct GNUNET_MQ_MessageHandler handlers[] =
202 { GNUNET_MQ_hd_var_size (identity_result_code,
203 GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE,
204 struct ResultCodeMessage,
205 el),
206 GNUNET_MQ_hd_var_size (identity_update,
207 GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE,
208 struct UpdateMessage,
209 el),
210 GNUNET_MQ_handler_end () };
211
212 el->mq =
213 GNUNET_CLIENT_connect (cfg, "identity", handlers, &mq_error_handler, el);
214 }
215 if (NULL == el->mq)
216 {
217 GNUNET_break (0);
218 GNUNET_free (el);
219 return NULL;
220 }
221 el->name = GNUNET_strdup (name);
222 nlen = strlen (name) + 1;
223 env = GNUNET_MQ_msg_extra (req, nlen, GNUNET_MESSAGE_TYPE_IDENTITY_LOOKUP);
224 memcpy (&req[1], name, nlen);
225 GNUNET_MQ_send (el->mq, env);
226 return el;
227}
228
229
230/**
231 * Abort ego lookup attempt.
232 *
233 * @param el handle for lookup to abort
234 */
235void
236GNUNET_IDENTITY_ego_lookup_cancel (struct GNUNET_IDENTITY_EgoLookup *el)
237{
238 GNUNET_MQ_destroy (el->mq);
239 GNUNET_free (el->name);
240 GNUNET_free (el);
241}
242
243
244/* end of identity_api_lookup.c */
diff --git a/src/service/identity/identity_api_suffix_lookup.c b/src/service/identity/identity_api_suffix_lookup.c
new file mode 100644
index 000000000..7bc0d18f3
--- /dev/null
+++ b/src/service/identity/identity_api_suffix_lookup.c
@@ -0,0 +1,246 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file identity/identity_api_suffix_lookup.c
23 * @brief api to lookup an ego
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_identity_service.h"
29#include "identity.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "identity-api", __VA_ARGS__)
32
33
34/**
35 * Handle for ego lookup.
36 */
37struct GNUNET_IDENTITY_EgoSuffixLookup
38{
39 /**
40 * Connection to service.
41 */
42 struct GNUNET_MQ_Handle *mq;
43
44 /**
45 * Suffix we are looking up.
46 */
47 char *suffix;
48
49 /**
50 * Function to call with the result.
51 */
52 GNUNET_IDENTITY_EgoSuffixCallback cb;
53
54 /**
55 * Closure for @e cb
56 */
57 void *cb_cls;
58};
59
60
61/**
62 * We received a result code from the service. Check the message
63 * is well-formed.
64 *
65 * @param cls closure
66 * @param rcm result message received
67 * @return #GNUNET_OK if the message is well-formed
68 */
69static int
70check_identity_result_code (void *cls, const struct ResultCodeMessage *rcm)
71{
72 (void) cls;
73 if (sizeof(*rcm) != htons (rcm->header.size))
74 GNUNET_MQ_check_zero_termination (rcm);
75 return GNUNET_OK;
76}
77
78
79/**
80 * We received a result code from the service.
81 *
82 * @param cls closure
83 * @param rcm result message received
84 */
85static void
86handle_identity_result_code (void *cls, const struct ResultCodeMessage *rcm)
87{
88 struct GNUNET_IDENTITY_EgoSuffixLookup *el = cls;
89
90 (void) rcm;
91 el->cb (el->cb_cls, NULL, NULL);
92 GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (el);
93}
94
95
96/**
97 * Check validity of identity update message.
98 *
99 * @param cls closure
100 * @param um message received
101 * @return #GNUNET_OK if the message is well-formed
102 */
103static int
104check_identity_update (void *cls, const struct UpdateMessage *um)
105{
106 uint16_t size = ntohs (um->header.size);
107 uint16_t name_len = ntohs (um->name_len);
108 const char *str = (const char *) &um[1];
109
110 (void) cls;
111 if ((size < name_len + sizeof(struct UpdateMessage)) ||
112 ((0 != name_len) && ('\0' != str[name_len - 1])))
113 {
114 GNUNET_break (0);
115 return GNUNET_SYSERR;
116 }
117 return GNUNET_OK;
118}
119
120
121/**
122 * Handle identity update message.
123 *
124 * @param cls closure
125 * @param um message received
126 */
127static void
128handle_identity_update (void *cls, const struct UpdateMessage *um)
129{
130 struct GNUNET_IDENTITY_EgoSuffixLookup *el = cls;
131 uint16_t name_len = ntohs (um->name_len);
132 const char *str;
133 size_t key_len;
134 size_t kb_read;
135 struct GNUNET_CRYPTO_PrivateKey private_key;
136 const char *tmp;
137
138 tmp = (const char*) &um[1];
139 str = (0 == name_len) ? NULL : tmp;
140 memset (&private_key, 0, sizeof (private_key));
141 key_len = ntohs (um->header.size) - name_len - sizeof (*um);
142 if (0 < key_len)
143 {
144 GNUNET_assert (GNUNET_SYSERR !=
145 GNUNET_CRYPTO_read_private_key_from_buffer (tmp + name_len,
146 key_len,
147 &private_key,
148 &kb_read));
149 GNUNET_assert (key_len == kb_read);
150 }
151 el->cb (el->cb_cls, &private_key, str);
152 GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (el);
153}
154
155
156/**
157 * Generic error handler, called with the appropriate error code and
158 * the same closure specified at the creation of the message queue.
159 * Not every message queue implementation supports an error handler.
160 *
161 * @param cls closure with the `struct GNUNET_IDENTITY_EgoSuffixLookup *`
162 * @param error error code
163 */
164static void
165mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
166{
167 struct GNUNET_IDENTITY_EgoSuffixLookup *el = cls;
168
169 (void) error;
170 el->cb (el->cb_cls, NULL, NULL);
171 GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (el);
172}
173
174
175/**
176 * Lookup an ego by name.
177 *
178 * @param cfg configuration to use
179 * @param name name to look up
180 * @param cb callback to invoke with the result
181 * @param cb_cls closure for @a cb
182 * @return NULL on error
183 */
184struct GNUNET_IDENTITY_EgoSuffixLookup *
185GNUNET_IDENTITY_ego_lookup_by_suffix (const struct
186 GNUNET_CONFIGURATION_Handle *cfg,
187 const char *suffix,
188 GNUNET_IDENTITY_EgoSuffixCallback cb,
189 void *cb_cls)
190{
191 struct GNUNET_IDENTITY_EgoSuffixLookup *el;
192 struct GNUNET_MQ_Envelope *env;
193 struct GNUNET_MessageHeader *req;
194 size_t nlen;
195
196 GNUNET_assert (NULL != cb);
197 el = GNUNET_new (struct GNUNET_IDENTITY_EgoSuffixLookup);
198 el->cb = cb;
199 el->cb_cls = cb_cls;
200 {
201 struct GNUNET_MQ_MessageHandler handlers[] =
202 { GNUNET_MQ_hd_var_size (identity_result_code,
203 GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE,
204 struct ResultCodeMessage,
205 el),
206 GNUNET_MQ_hd_var_size (identity_update,
207 GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE,
208 struct UpdateMessage,
209 el),
210 GNUNET_MQ_handler_end () };
211
212 el->mq =
213 GNUNET_CLIENT_connect (cfg, "identity", handlers, &mq_error_handler, el);
214 }
215 if (NULL == el->mq)
216 {
217 GNUNET_break (0);
218 GNUNET_free (el);
219 return NULL;
220 }
221 el->suffix = GNUNET_strdup (suffix);
222 nlen = strlen (suffix) + 1;
223 env = GNUNET_MQ_msg_extra (req, nlen,
224 GNUNET_MESSAGE_TYPE_IDENTITY_LOOKUP_BY_SUFFIX);
225 memcpy (&req[1], suffix, nlen);
226 GNUNET_MQ_send (el->mq, env);
227 return el;
228}
229
230
231/**
232 * Abort ego lookup attempt.
233 *
234 * @param el handle for lookup to abort
235 */
236void
237GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (struct
238 GNUNET_IDENTITY_EgoSuffixLookup *el)
239{
240 GNUNET_MQ_destroy (el->mq);
241 GNUNET_free (el->suffix);
242 GNUNET_free (el);
243}
244
245
246/* end of identity_api_suffix_lookup.c */
diff --git a/src/service/identity/meson.build b/src/service/identity/meson.build
new file mode 100644
index 000000000..c9e4f9bb4
--- /dev/null
+++ b/src/service/identity/meson.build
@@ -0,0 +1,60 @@
1libgnunetidentity_src = ['identity_api.c',
2 'identity_api_lookup.c',
3 'identity_api_suffix_lookup.c']
4
5gnunetserviceidentity_src = ['gnunet-service-identity.c']
6
7configure_file(input : 'identity.conf.in',
8 output : 'identity.conf',
9 configuration : cdata,
10 install: true,
11 install_dir: pkgcfgdir)
12
13
14if get_option('monolith')
15 foreach p : libgnunetidentity_src + gnunetserviceidentity_src
16 gnunet_src += 'identity/' + p
17 endforeach
18 subdir_done()
19endif
20
21libgnunetidentity = library('gnunetidentity',
22 libgnunetidentity_src,
23 soversion: '1',
24 version: '1.0.0',
25 dependencies: [libgnunetutil_dep, sodium_dep],
26 include_directories: [incdir, configuration_inc],
27 install: true,
28 install_dir: get_option('libdir'))
29libgnunetidentity_dep = declare_dependency(link_with : libgnunetidentity)
30pkg.generate(libgnunetidentity, url: 'https://www.gnunet.org',
31 description : 'API to access and organize private keys of the user egos')
32shared_module('gnunet_plugin_rest_identity',
33 ['plugin_rest_identity.c'],
34 dependencies: [libgnunetrest_dep,
35 libgnunetidentity_dep,
36 libgnunetutil_dep,
37 json_dep,
38 mhd_dep],
39 include_directories: [incdir, configuration_inc],
40 install: true,
41 install_dir: get_option('libdir') / 'gnunet')
42
43
44executable ('gnunet-identity',
45 ['gnunet-identity.c'],
46 dependencies: [libgnunetidentity_dep,
47 libgnunetutil_dep,
48 libgnunetstatistics_dep],
49 include_directories: [incdir, configuration_inc],
50 install: true,
51 install_dir: get_option('bindir'))
52executable ('gnunet-service-identity',
53 gnunetserviceidentity_src,
54 dependencies: [libgnunetidentity_dep,
55 libgnunetutil_dep,
56 libgnunetstatistics_dep],
57 include_directories: [incdir, configuration_inc],
58 install: true,
59 install_dir: get_option('libdir')/'gnunet'/'libexec')
60
diff --git a/src/service/identity/test_identity.c b/src/service/identity/test_identity.c
new file mode 100644
index 000000000..d133e3ee4
--- /dev/null
+++ b/src/service/identity/test_identity.c
@@ -0,0 +1,324 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file identity/test_identity.c
23 * @brief testcase for identity service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_identity_service.h"
29#include "gnunet_testing_lib.h"
30
31
32#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
33
34
35/**
36 * Return value from 'main'.
37 */
38static int res;
39
40/**
41 * Handle to identity service.
42 */
43static struct GNUNET_IDENTITY_Handle *h;
44
45/**
46 * Handle to identity operation.
47 */
48static struct GNUNET_IDENTITY_Operation *op;
49
50/**
51 * Handle for task for timeout termination.
52 */
53static struct GNUNET_SCHEDULER_Task *endbadly_task;
54
55#define CHECK(cond) \
56 do \
57 { \
58 if (! (cond)) \
59 { \
60 GNUNET_break (0); \
61 end (); \
62 return; \
63 } \
64 } while (0)
65
66
67/**
68 * Clean up all resources used.
69 */
70static void
71cleanup (void *cls)
72{
73 (void) cls;
74 if (NULL != op)
75 {
76 GNUNET_IDENTITY_cancel (op);
77 op = NULL;
78 }
79 if (NULL != h)
80 {
81 GNUNET_IDENTITY_disconnect (h);
82 h = NULL;
83 }
84}
85
86
87/**
88 * Termiante the testcase (failure).
89 *
90 * @param cls NULL
91 */
92static void
93endbadly (void *cls)
94{
95 GNUNET_SCHEDULER_shutdown ();
96}
97
98
99/**
100 * Finish the testcase (successfully).
101 */
102static void
103end ()
104{
105 if (NULL != endbadly_task)
106 {
107 GNUNET_SCHEDULER_cancel (endbadly_task);
108 endbadly_task = NULL;
109 }
110 GNUNET_SCHEDULER_shutdown ();
111}
112
113
114/**
115 * Called with events about egos.
116 *
117 * @param cls NULL
118 * @param ego ego handle
119 * @param ego_ctx context for application to store data for this ego
120 * (during the lifetime of this process, initially NULL)
121 * @param identifier identifier assigned by the user for this ego,
122 * NULL if the user just deleted the ego and it
123 * must thus no longer be used
124 */
125static void
126notification_cb (void *cls,
127 struct GNUNET_IDENTITY_Ego *ego,
128 void **ctx,
129 const char *identifier)
130{
131 static struct GNUNET_IDENTITY_Ego *my_ego;
132 static int round;
133
134 switch (round)
135 {
136 case 0: /* end of initial iteration */
137 CHECK (NULL == ego);
138 CHECK (NULL == identifier);
139 break;
140
141 case 1: /* create */
142 CHECK (NULL != ego);
143 CHECK (NULL != identifier);
144 CHECK (0 == strcmp (identifier, "test-id"));
145 my_ego = ego;
146 *ctx = &round;
147 break;
148
149 case 2: /* rename */
150 CHECK (my_ego == ego);
151 CHECK (NULL != identifier);
152 CHECK (0 == strcmp (identifier, "test"));
153 CHECK (*ctx == &round);
154 break;
155
156 case 3: /* reconnect-down */
157 CHECK (my_ego == ego);
158 CHECK (NULL == identifier);
159 CHECK (*ctx == &round);
160 *ctx = NULL;
161 break;
162
163 case 4: /* reconnect-up */
164 CHECK (NULL != identifier);
165 CHECK (0 == strcmp (identifier, "test"));
166 my_ego = ego;
167 *ctx = &round;
168 break;
169
170 case 5: /* end of iteration after reconnect */
171 CHECK (NULL == ego);
172 CHECK (NULL == identifier);
173 break;
174
175 case 6: /* delete */
176 CHECK (my_ego == ego);
177 CHECK (*ctx == &round);
178 *ctx = NULL;
179 break;
180
181 default:
182 CHECK (0);
183 }
184 round++;
185}
186
187
188/**
189 * Continuation called from successful delete operation.
190 *
191 * @param cls NULL
192 * @param ec
193 */
194static void
195delete_cont (void *cls, enum GNUNET_ErrorCode ec)
196{
197 op = NULL;
198 CHECK (GNUNET_EC_NONE == ec);
199 res = 0;
200 end ();
201}
202
203
204/**
205 * Continue by deleting the "test" identity.
206 *
207 * @param cls NULL
208 */
209static void
210finally_delete (void *cls)
211{
212 op = GNUNET_IDENTITY_delete (h, "test", &delete_cont, NULL);
213}
214
215
216/**
217 * Continuation called from expected-to-fail rename operation.
218 *
219 * @param cls NULL
220 * @param ec
221 */
222static void
223fail_rename_cont (void *cls, enum GNUNET_ErrorCode ec)
224{
225 CHECK (GNUNET_EC_NONE != ec);
226 op = NULL;
227 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
228 &finally_delete,
229 NULL);
230}
231
232
233/**
234 * Continuation called from successful rename operation.
235 *
236 * @param cls NULL
237 * @param ec
238 */
239static void
240success_rename_cont (void *cls, enum GNUNET_ErrorCode ec)
241{
242 CHECK (GNUNET_EC_NONE == ec);
243 op = GNUNET_IDENTITY_rename (h, "test-id", "test", &fail_rename_cont, NULL);
244}
245
246
247/**
248 * Called with events about created ego.
249 *
250 * @param cls NULL
251 * @param pk private key of the ego, or NULL on error
252 * @param ec
253 */
254static void
255create_cb (void *cls,
256 const struct GNUNET_CRYPTO_PrivateKey *pk,
257 enum GNUNET_ErrorCode ec)
258{
259 CHECK (NULL != pk);
260 CHECK (GNUNET_EC_NONE == ec);
261 struct GNUNET_CRYPTO_PublicKey pub;
262 size_t pt_len = strlen ("test") + 1;
263 unsigned char ct[pt_len + GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES];
264 char pt[pt_len];
265 enum GNUNET_GenericReturnValue res;
266
267 GNUNET_CRYPTO_key_get_public (pk, &pub);
268 res = GNUNET_CRYPTO_encrypt ("test", pt_len, &pub, ct,
269 pt_len
270 + GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES);
271 CHECK (GNUNET_OK == res);
272 res = GNUNET_CRYPTO_decrypt (ct, pt_len
273 + GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES,
274 pk, pt, pt_len);
275 CHECK (GNUNET_OK == res);
276 CHECK (0 == strcmp (pt, "test"));
277 op =
278 GNUNET_IDENTITY_rename (h, "test-id", "test", &success_rename_cont, NULL);
279}
280
281
282/**
283 * Main function of the test, run from scheduler.
284 *
285 * @param cls NULL
286 * @param cfg configuration we use (also to connect to identity service)
287 * @param peer handle to access more of the peer (not used)
288 */
289static void
290run (void *cls,
291 const struct GNUNET_CONFIGURATION_Handle *cfg,
292 struct GNUNET_TESTING_Peer *peer)
293{
294 endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &endbadly, NULL);
295 GNUNET_SCHEDULER_add_shutdown (&cleanup, NULL);
296 h = GNUNET_IDENTITY_connect (cfg, &notification_cb, NULL);
297 CHECK (NULL != h);
298 op = GNUNET_IDENTITY_create (h,
299 "test-id",
300 NULL,
301 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
302 &create_cb, NULL);
303}
304
305
306int
307main (int argc, char *argv[])
308{
309 GNUNET_DISK_purge_cfg_dir ("test_identity.conf",
310 "GNUNET_TEST_HOME");
311 res = 1;
312 if (0 != GNUNET_TESTING_service_run ("test-identity",
313 "identity",
314 "test_identity.conf",
315 &run,
316 NULL))
317 return 1;
318 GNUNET_DISK_purge_cfg_dir ("test_identity.conf",
319 "GNUNET_TEST_HOME");
320 return res;
321}
322
323
324/* end of test_identity.c */
diff --git a/src/service/identity/test_identity.conf b/src/service/identity/test_identity.conf
new file mode 100644
index 000000000..14b915732
--- /dev/null
+++ b/src/service/identity/test_identity.conf
@@ -0,0 +1,11 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/test-identity-service/
3
4[arm]
5PORT = 12000
6UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock
7
8[identity]
9# need to overwrite paths to ensure they stay the same between runs...
10EGODIR = $GNUNET_TMP/test-identity-service/egos/
11SUBSYSTEM_CFG = $GNUNET_TMP/test-identity-service/s.conf
diff --git a/src/service/identity/test_identity_messages.sh b/src/service/identity/test_identity_messages.sh
new file mode 100755
index 000000000..edb4d5805
--- /dev/null
+++ b/src/service/identity/test_identity_messages.sh
@@ -0,0 +1,50 @@
1#!/bin/bash
2trap "gnunet-arm -e -c test_identity.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_identity.conf -s PATHS -o GNUNET_HOME -f`
17
18which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 30"
19
20TEST_MSG="This is a test message. 123"
21gnunet-arm -s -c test_identity.conf
22gnunet-identity -C recipientego -c test_identity.conf
23gnunet-identity -C recipientegoed -X -c test_identity.conf
24RECIPIENT_KEY=`gnunet-identity -d -e recipientego -q -c test_identity.conf`
25MSG_ENC=`gnunet-identity -W "$TEST_MSG" -k $RECIPIENT_KEY -c test_identity.conf`
26if [ $? == 0 ]
27then
28 MSG_DEC=`gnunet-identity -R "$MSG_ENC" -e recipientego -c test_identity.conf`
29fi
30RECIPIENT_KEY_ED=`gnunet-identity -d -e recipientegoed -q -c test_identity.conf`
31MSG_ENC_ED=`gnunet-identity -W "$TEST_MSG" -k $RECIPIENT_KEY_ED -c test_identity.conf`
32if [ $? == 0 ]
33then
34 MSG_DEC_ED=`gnunet-identity -R "$MSG_ENC_ED" -e recipientegoed -c test_identity.conf`
35fi
36gnunet-identity -D recipientego -c test_identity.conf
37gnunet-identity -D recipientegoed -c test_identity.conf
38gnunet-arm -e -c test_identity.conf
39if [ "$TEST_MSG" != "$MSG_DEC" ]
40then
41 diff <(echo "$TEST_MSG" ) <(echo "$MSG_DEC")
42 echo "Failed - \"$TEST_MSG\" != \"$MSG_DEC\""
43 exit 1
44fi
45if [ "$TEST_MSG" != "$MSG_DEC_ED" ]
46then
47 diff <(echo "$TEST_MSG" ) <(echo "$MSG_DEC_ED")
48 echo "Failed - \"$TEST_MSG\" != \"$MSG_DEC_ED\""
49 exit 1
50fi
diff --git a/src/service/identity/test_plugin_rest_identity.sh b/src/service/identity/test_plugin_rest_identity.sh
new file mode 100755
index 000000000..a5879dd7e
--- /dev/null
+++ b/src/service/identity/test_plugin_rest_identity.sh
@@ -0,0 +1,157 @@
1#!/usr/bin/bash
2
3#First, start gnunet-arm and the rest-service.
4#Exit 0 means success, exit 1 means failed test
5
6identity_link="http://localhost:7776/identity"
7wrong_link="http://localhost:7776/identityandmore"
8
9
10curl_get () {
11 #$1 is link
12 #$2 is grep
13 cache="$(curl -v "$1" 2>&1 | grep "$2")"
14 #echo $cache
15 if [ "" == "$cache" ]
16 then
17 exit 1
18 fi
19}
20
21curl_post () {
22 #$1 is link
23 #$2 is data
24 #$3 is grep
25 cache="$(curl -v -X "POST" "$1" --data "$2" 2>&1 | grep "$3")"
26 #echo $cache
27 if [ "" == "$cache" ]
28 then
29 exit 1
30 fi
31}
32
33curl_delete () {
34 #$1 is link
35 #$2 is grep
36 cache="$(curl -v -X "DELETE" "$1" 2>&1 | grep "$2")"
37 #echo $cache
38 if [ "" == "$cache" ]
39 then
40 exit 1
41 fi
42}
43
44curl_put () {
45 #$1 is link
46 #$2 is data
47 #$3 is grep
48 cache="$(curl -v -X "PUT" "$1" --data "$2" 2>&1 | grep "$3")"
49 #echo $cache
50 if [ "" == "$cache" ]
51 then
52 exit 1
53 fi
54}
55
56#Test GET
57test="$(gnunet-identity -d)"
58#if no identity exists
59if [ "" == "$test" ]
60then
61 curl_get "$identity_link/all" "error"
62 gnunet-identity -C "test_plugin_rest_identity"
63 name="$(gnunet-identity -d | awk 'NR==1{print $1}')"
64 public="$(gnunet-identity -d | awk 'NR==1{print $3}')"
65
66 curl_get "${identity_link}/name/$name" "$public"
67 curl_get "${identity_link}/name/$public" "error"
68 curl_get "${identity_link}/name/" "error"
69
70 curl_get "${identity_link}/pubkey/$public" "$name"
71 curl_get "${identity_link}/pubkey/$name" "error"
72 curl_get "${identity_link}/pubkey/" "error"
73
74 gnunet-identity -D "test_plugin_rest_identity"
75else
76 name="$(gnunet-identity -d | awk 'NR==1{print $1}')"
77 public="$(gnunet-identity -d | awk 'NR==1{print $3}')"
78
79 curl_get "${identity_link}/name/$name" "$public"
80 curl_get "${identity_link}/name/$public" "error"
81 curl_get "${identity_link}/name/" "error"
82
83 curl_get "${identity_link}/pubkey/$public" "$name"
84 curl_get "${identity_link}/pubkey/$name" "error"
85 curl_get "${identity_link}/pubkey/" "error"
86fi
87
88#Test POST
89gnunet-identity -D "test_plugin_rest_identity" > /dev/null 2>&1
90gnunet-identity -D "test_plugin_rest_identity1" > /dev/null 2>&1
91
92curl_post "${identity_link}" '{"name":"test_plugin_rest_identity"}' "HTTP/1.1 201 Created"
93curl_post "${identity_link}" '{"name":"test_plugin_rest_identity"}' "HTTP/1.1 409"
94curl_post "${identity_link}" '{"name":"Test_plugin_rest_identity"}' "HTTP/1.1 409"
95curl_post "${identity_link}" '{}' "error"
96curl_post "${identity_link}" '' "error"
97curl_post "${identity_link}" '{"name":""}' "error"
98curl_post "${identity_link}" '{"name":123}' "error"
99curl_post "${identity_link}" '{"name":[]}' "error"
100curl_post "${identity_link}" '{"name1":"test_plugin_rest_identity"}' "error"
101curl_post "${identity_link}" '{"other":""}' "error"
102curl_post "${identity_link}" '{"name":"test_plugin_rest_identity1", "other":"test_plugin_rest_identity2"}' "error"
103
104#Test PUT
105name="$(gnunet-identity -d | grep "test_plugin_rest_identity" | awk 'NR==1{print $1}')"
106public="$(gnunet-identity -d | grep "test_plugin_rest_identity" | awk 'NR==1{print $3}')"
107
108curl_put "${identity_link}/pubkey/$public" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 204"
109curl_put "${identity_link}/pubkey/$public" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 409"
110curl_put "${identity_link}/pubkey/${public}xx" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404"
111curl_put "${identity_link}/pubkey/" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404"
112curl_put "${identity_link}/pubke" '{"newname":"test_plugin_rest_identity1"}' "error"
113curl_put "${identity_link}" '{"newname":"test_plugin_rest_identity1","other":"sdfdsf"}' "error"
114curl_put "${identity_link}/pubkey/$name" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404"
115curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"test_plugin_rest_identity"}' "HTTP/1.1 204"
116curl_put "${identity_link}/pubkey/$public" '{"newnam":"test_plugin_rest_identity"}' "error"
117curl_put "${identity_link}/name/test_plugin_rest_identity" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 204"
118curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"TEST_plugin_rest_identity1"}' "HTTP/1.1 409"
119curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 409"
120curl_put "${identity_link}/name/test_plugin_rest_identityxxx" '{"newname":"test_plugin_rest_identity"}' "HTTP/1.1 404"
121curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"test_plugin_rest_identity"}' "HTTP/1.1 204"
122curl_put "${identity_link}/name/test_plugin_rest_identity" '{"newnam":"test_plugin_rest_identityfail"}' "error"
123
124#Test subsystem
125curl_put "${identity_link}/subsystem/test_plugin_rest_identity" '{"subsystem":"namestore"}' "HTTP/1.1 204"
126curl_put "${identity_link}/subsystem/test_plugin_rest_identity" '{"subsystem":"namestore"}' "HTTP/1.1 204"
127curl_get "${identity_link}/subsystem/namestore" "test_plugin_rest_identity"
128public="$(gnunet-identity -d | grep "test_plugin_rest_identity" | awk 'NR==1{print $3}')"
129curl_put "${identity_link}/subsystem/$public" '{"subsystem":"namestore"}' "HTTP/1.1 404"
130curl_post "${identity_link}" '{"name":"test_plugin_rest_identity1"}' "HTTP/1.1 201 Created"
131curl_get "${identity_link}/subsystem/test_plugin_rest_identity_no_subsystem" "error"
132curl_put "${identity_link}/subsystem/test_plugin_rest_identity1" '{"subsystem":"test_plugin_rest_identity_no_subsystem"}' "HTTP/1.1 204"
133curl_get "${identity_link}/subsystem/test_plugin_rest_identity_no_subsystem" "test_plugin_rest_identity1"
134
135curl_put "${identity_link}/subsystem/test_plugin_rest_identity1" '{"subsyste":"test_plugin_rest_identity_no_subsystem"}' "error"
136curl_put "${identity_link}/subsystem/test_plugin_rest_identity1" '{"subsystem":"test_plugin_rest_identity_no_subsystem"}' "HTTP/1.1 204"
137
138#Test DELETE
139curl_delete "${identity_link}/name/test_plugin_rest_identity" "HTTP/1.1 204"
140curl_get "${identity_link}/name/test_plugin_rest_identity" "error"
141curl_delete "${identity_link}/name/TEST_plugin_rest_identity1" "HTTP/1.1 204"
142curl_delete "${identity_link}/name/test_plugin_rest_identity1" "HTTP/1.1 404"
143curl_get "${identity_link}/name/test_plugin_rest_identity1" "error"
144curl_delete "${identity_link}/name/test_plugin_rest_identity_not_found" "HTTP/1.1 404"
145curl_post "${identity_link}" '{"name":"test_plugin_rest_identity1"}' "HTTP/1.1 201 Created"
146public="$(gnunet-identity -d | grep "test_plugin_rest_identity1" | awk 'NR==1{print $3}')"
147curl_delete "${identity_link}/pubkey/$public" "HTTP/1.1 204"
148curl_delete "${identity_link}/pubke/$public" "error"
149curl_delete "${identity_link}/pubkey/${public}other=232" "HTTP/1.1 404"
150
151#Test wrong_link
152curl_get "$wrong_link" "HTTP/1.1 404"
153curl_post "$wrong_link" '{"name":"test_plugin_rest_identity"}' "HTTP/1.1 404"
154curl_put "$wrong_link/name/test_plugin_rest_identity" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404"
155curl_delete "$wrong_link/name/test_plugin_rest_identity1" "HTTP/1.1 404"
156
157exit 0;
diff --git a/src/service/identity/test_plugin_rest_identity_signature.sh b/src/service/identity/test_plugin_rest_identity_signature.sh
new file mode 100755
index 000000000..a4d5fa5d7
--- /dev/null
+++ b/src/service/identity/test_plugin_rest_identity_signature.sh
@@ -0,0 +1,81 @@
1#!/usr/bin/bash
2
3# https://www.rfc-editor.org/rfc/rfc7515#appendix-A.3
4
5header='{"alg":"EdDSA"}'
6payload='Example of Ed25519 signing'
7key='{ "kty":"OKP",
8 "crv":"Ed25519",
9 "d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
10 "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
11 }'
12
13header_payload_test="eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc"
14signature_test="hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg"
15
16base64url_add_padding() {
17 for i in $( seq 1 $(( 4 - ${#1} % 4 )) ); do padding+="="; done
18 echo "$1""$padding"
19}
20
21base64url_encode () {
22 echo -n -e "$1" | base64 -w0 | tr '+/' '-_' | tr -d '='
23}
24
25base64url_decode () {
26 padded_input=$(base64url_add_padding "$1")
27 echo -n "$padded_input" | basenc --base64url -d
28}
29
30base32crockford_encode () {
31 echo -n -e "$1" | basenc --base32hex | tr 'IJKLMNOPQRSTUV' 'JKMNPQRSTVWXYZ'
32}
33
34echo -n "jwk: "
35echo $key | jq
36
37# Create Header
38# 65556 (decimal)
39# = 00000000-00000001-00000000-00010100 (binary little endian)
40# = 00-01-00-14 (hex little endian)
41header_hex=("00" "01" "00" "14")
42
43# Convert secret JWK to HEX array
44key_hex=( $( base64url_decode $( echo -n "$key" | jq -r '.d' ) | xxd -p | tr -d '\n' | fold -w 2 | tr '\n' ' ' ) )
45
46# Concat header and key
47header_key_hex=(${header_hex[@]} ${key_hex[@]})
48
49# Encode with Base32Crogford
50key_gnunet=$(echo -n "${header_key_hex[*]}" | tr -d " " | xxd -p -r | basenc --base32hex | tr 'IJKLMNOPQRSTUV' 'JKMNPQRSTVWXYZ' | tr -d "=")
51echo "gnunet skey: $key_gnunet"
52
53# Create ego
54gnunet-identity -C ego9696595726 -X -P "$key_gnunet"
55
56# Test base64url encoding and header.payload generation
57header_payload_enc="$(base64url_encode "$header").$(base64url_encode "$payload")"
58if [ $header_payload_enc != $header_payload_test ] ;
59then
60 exit 1
61fi
62echo "header.payload: $header_payload_enc"
63
64# Sign JWT
65signature_enc=$(curl -s "localhost:7776/sign?user=ego9696595726&data=$header_payload_enc" | jq -r '.signature')
66jwt="$header_payload_enc.$signature_enc"
67echo "header.payload.signature: $jwt"
68
69gnunet-identity -D ego9696595726
70
71if [ $signature_enc != $signature_test ]
72then
73 echo "Signature does not check out:"
74 echo "$signature_enc"
75 echo "$signature_test"
76 exit 1
77else
78 echo "Signature does check out!"
79 exit 1
80fi
81
diff --git a/src/service/rest/.gitignore b/src/service/rest/.gitignore
new file mode 100644
index 000000000..07e69218e
--- /dev/null
+++ b/src/service/rest/.gitignore
@@ -0,0 +1 @@
gnunet-rest-server
diff --git a/src/service/rest/Makefile.am b/src/service/rest/Makefile.am
new file mode 100644
index 000000000..401be36f4
--- /dev/null
+++ b/src/service/rest/Makefile.am
@@ -0,0 +1,42 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10pkgcfg_DATA = \
11 rest.conf
12
13if USE_COVERAGE
14 AM_CFLAGS = --coverage -O0
15 XLIBS = -lgcov
16endif
17
18lib_LTLIBRARIES = \
19 libgnunetrest.la
20
21libexec_PROGRAMS = \
22 gnunet-rest-server
23
24EXTRA_DIST = \
25 rest.conf
26
27gnunet_rest_server_SOURCES = \
28 gnunet-rest-server.c
29gnunet_rest_server_LDADD = \
30 $(top_builddir)/src/lib/util/libgnunetutil.la \
31 $(GN_LIBINTL) $(MHD_LIBS)
32gnunet_rest_server_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
33
34libgnunetrest_la_SOURCES = \
35 rest.c
36libgnunetrest_la_LIBADD = \
37 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIB) \
38 $(GN_LIBINTL) $(MHD_LIBS)
39libgnunetrest_la_LDFLAGS = \
40 $(GN_LIB_LDFLAGS) \
41 -version-info 0:0:0
42libgnunetrest_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
diff --git a/src/service/rest/gnunet-rest-server.c b/src/service/rest/gnunet-rest-server.c
new file mode 100644
index 000000000..5163c2271
--- /dev/null
+++ b/src/service/rest/gnunet-rest-server.c
@@ -0,0 +1,1381 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @file src/rest/gnunet-rest-server.c
23 * @brief REST service for GNUnet services
24 *
25 */
26#include "platform.h"
27#include <microhttpd.h>
28#include "gnunet_util_lib.h"
29#include "gnunet_rest_plugin.h"
30#include "gnunet_mhd_compat.h"
31
32/**
33 * Default Socks5 listen port.
34 */
35#define GNUNET_REST_SERVICE_PORT 7776
36
37/**
38 * Maximum supported length for a URI.
39 * Should die. @deprecated
40 */
41#define MAX_HTTP_URI_LENGTH 2048
42
43/**
44 * Port for plaintext HTTP.
45 */
46#define HTTP_PORT 80
47
48/**
49 * Port for HTTPS.
50 */
51#define HTTPS_PORT 443
52
53/**
54 * After how long do we clean up unused MHD SSL/TLS instances?
55 */
56#define MHD_CACHE_TIMEOUT \
57 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
58
59#define GN_REST_STATE_INIT 0
60#define GN_REST_STATE_PROCESSING 1
61
62/**
63 * The task ID
64 */
65static struct GNUNET_SCHEDULER_Task *httpd_task;
66
67/**
68 * The address to bind to
69 */
70static in_addr_t address;
71
72/**
73 * The IPv6 address to bind to
74 */
75static struct in6_addr address6;
76
77/**
78 * The port the service is running on (default 7776)
79 */
80static unsigned long long port = GNUNET_REST_SERVICE_PORT;
81
82/**
83 * The listen socket of the service for IPv4
84 */
85static struct GNUNET_NETWORK_Handle *lsock4;
86
87/**
88 * The listen socket of the service for IPv6
89 */
90static struct GNUNET_NETWORK_Handle *lsock6;
91
92/**
93 * The listen task ID for IPv4
94 */
95static struct GNUNET_SCHEDULER_Task *ltask4;
96
97/**
98 * The listen task ID for IPv6
99 */
100static struct GNUNET_SCHEDULER_Task *ltask6;
101
102/**
103 * Daemon for HTTP
104 */
105static struct MHD_Daemon *httpd;
106
107/**
108 * Response we return on failures.
109 */
110static struct MHD_Response *failure_response;
111
112/**
113 * Our configuration.
114 */
115static const struct GNUNET_CONFIGURATION_Handle *cfg;
116
117/**
118 * Echo request Origin in CORS
119 */
120static int echo_origin;
121
122/**
123 * Do basic auth of user
124 */
125static int basic_auth_enabled;
126
127/**
128 * Basic auth secret
129 */
130static char *basic_auth_secret;
131
132/**
133 * User of the service
134 */
135char cuser[_POSIX_LOGIN_NAME_MAX];
136
137/**
138 * Allowed Origins (CORS)
139 */
140static char *allow_origins;
141
142/**
143 * Allowed Headers (CORS)
144 */
145static char *allow_headers;
146
147/**
148 * Allowed Credentials (CORS)
149 */
150static char *allow_credentials;
151
152/**
153 * Plugin list head
154 */
155static struct PluginListEntry *plugins_head;
156
157/**
158 * Plugin list tail
159 */
160static struct PluginListEntry *plugins_tail;
161
162/**
163 * A plugin list entry
164 */
165struct PluginListEntry
166{
167 /* DLL */
168 struct PluginListEntry *next;
169
170 /* DLL */
171 struct PluginListEntry *prev;
172
173 /**
174 * libname (to cleanup)
175 */
176 char *libname;
177
178 /**
179 * The plugin
180 */
181 struct GNUNET_REST_Plugin *plugin;
182};
183
184/**
185 * MHD Connection handle
186 */
187struct MhdConnectionHandle
188{
189 struct MHD_Connection *con;
190
191 struct MHD_Response *response;
192
193 struct GNUNET_REST_RequestHandle *data_handle;
194
195 struct MHD_PostProcessor *pp;
196
197 int status;
198
199 int state;
200};
201
202/**
203 * Accepted requests
204 */
205struct AcceptedRequest
206{
207 /**
208 * DLL
209 */
210 struct AcceptedRequest *next;
211
212 /**
213 * DLL
214 */
215 struct AcceptedRequest *prev;
216
217 /**
218 * Socket
219 */
220 struct GNUNET_NETWORK_Handle *sock;
221
222 /**
223 * Connection
224 */
225 struct MhdConnectionHandle *con_handle;
226
227 /**
228 * State
229 */
230 int socket_with_mhd;
231};
232
233/**
234 * AcceptedRequest list head
235 */
236static struct AcceptedRequest *req_list_head;
237
238/**
239 * AcceptedRequest list tail
240 */
241static struct AcceptedRequest *req_list_tail;
242
243/* ************************* Global helpers ********************* */
244
245
246/**
247 * Task run whenever HTTP server operations are pending.
248 *
249 * @param cls NULL
250 */
251static void
252do_httpd (void *cls);
253
254
255/**
256 * Run MHD now, we have extra data ready for the callback.
257 */
258static void
259run_mhd_now ()
260{
261 if (NULL != httpd_task)
262 {
263 GNUNET_SCHEDULER_cancel (httpd_task);
264 httpd_task = NULL;
265 }
266 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
267}
268
269
270/**
271 * Plugin result callback
272 *
273 * @param cls closure (MHD connection handle)
274 * @param data the data to return to the caller
275 * @param len length of the data
276 * @param status #GNUNET_OK if successful
277 */
278static void
279plugin_callback (void *cls, struct MHD_Response *resp, int status)
280{
281 struct MhdConnectionHandle *handle = cls;
282
283 handle->status = status;
284 handle->response = resp;
285 MHD_resume_connection (handle->con);
286 run_mhd_now ();
287}
288
289
290static int
291cleanup_url_map (void *cls, const struct GNUNET_HashCode *key, void *value)
292{
293 GNUNET_free (value);
294 return GNUNET_YES;
295}
296
297static void
298cleanup_handle (struct MhdConnectionHandle *handle)
299{
300 if (NULL != handle->response)
301 MHD_destroy_response (handle->response);
302 if (NULL != handle->data_handle)
303 {
304 if (NULL != handle->data_handle->header_param_map)
305 {
306 GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle
307 ->header_param_map,
308 &cleanup_url_map,
309 NULL);
310 GNUNET_CONTAINER_multihashmap_destroy (
311 handle->data_handle->header_param_map);
312 }
313 if (NULL != handle->data_handle->url_param_map)
314 {
315 GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->url_param_map,
316 &cleanup_url_map,
317 NULL);
318 GNUNET_CONTAINER_multihashmap_destroy (
319 handle->data_handle->url_param_map);
320 }
321 GNUNET_free (handle->data_handle);
322 }
323 GNUNET_free (handle);
324}
325
326static void
327cleanup_ar (struct AcceptedRequest *ar)
328{
329 if (NULL != ar->con_handle)
330 {
331 cleanup_handle (ar->con_handle);
332 }
333 if (GNUNET_YES == ar->socket_with_mhd)
334 {
335 GNUNET_NETWORK_socket_free_memory_only_ (ar->sock);
336 }
337 else {
338 GNUNET_NETWORK_socket_close (ar->sock);
339 }
340 ar->sock = NULL;
341 GNUNET_CONTAINER_DLL_remove (req_list_head,
342 req_list_tail,
343 ar);
344 GNUNET_free (ar);
345}
346
347static int
348header_iterator (void *cls,
349 enum MHD_ValueKind kind,
350 const char *key,
351 const char *value)
352{
353 struct GNUNET_REST_RequestHandle *handle = cls;
354 struct GNUNET_HashCode hkey;
355 char *val;
356 char *lowerkey;
357
358 lowerkey = GNUNET_strdup (key);
359 GNUNET_STRINGS_utf8_tolower (key, lowerkey);
360 GNUNET_CRYPTO_hash (lowerkey, strlen (lowerkey), &hkey);
361 GNUNET_asprintf (&val, "%s", value);
362 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
363 handle->header_param_map,
364 &hkey,
365 val,
366 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
367 {
368 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
369 "Could not load add header `%s'=%s\n",
370 lowerkey,
371 value);
372 }
373 GNUNET_free (lowerkey);
374 return MHD_YES;
375}
376
377
378static int
379url_iterator (void *cls,
380 enum MHD_ValueKind kind,
381 const char *key,
382 const char *value)
383{
384 struct GNUNET_REST_RequestHandle *handle = cls;
385 struct GNUNET_HashCode hkey;
386 char *val;
387
388 GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
389 GNUNET_asprintf (&val, "%s", value);
390 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
391 handle->url_param_map,
392 &hkey,
393 val,
394 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
395 {
396 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
397 "Could not load add url param `%s'=%s\n",
398 key,
399 value);
400 }
401 return MHD_YES;
402}
403
404
405static MHD_RESULT
406post_data_iter (void *cls,
407 enum MHD_ValueKind kind,
408 const char *key,
409 const char *filename,
410 const char *content_type,
411 const char *transfer_encoding,
412 const char *data,
413 uint64_t off,
414 size_t size)
415{
416 struct GNUNET_REST_RequestHandle *handle = cls;
417 struct GNUNET_HashCode hkey;
418 char *val;
419
420 if (MHD_POSTDATA_KIND != kind)
421 return MHD_YES;
422
423 GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
424 val = GNUNET_CONTAINER_multihashmap_get (handle->url_param_map,
425 &hkey);
426 if (NULL == val)
427 {
428 val = GNUNET_malloc (65536);
429 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
430 handle->url_param_map,
431 &hkey,
432 val,
433 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
434 {
435 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
436 "Could not add url param '%s'\n",
437 key);
438 GNUNET_free (val);
439 }
440 }
441 memcpy (val + off, data, size);
442 return MHD_YES;
443}
444
445
446/* ********************************* MHD response generation ******************* */
447
448/**
449 * Main MHD callback for handling requests.
450 *
451 * @param cls unused
452 * @param con MHD connection handle
453 * @param url the url in the request
454 * @param meth the HTTP method used ("GET", "PUT", etc.)
455 * @param ver the HTTP version string ("HTTP/1.1" for version 1.1, etc.)
456 * @param upload_data the data being uploaded (excluding HEADERS,
457 * for a POST that fits into memory and that is encoded
458 * with a supported encoding, the POST data will NOT be
459 * given in upload_data and is instead available as
460 * part of MHD_get_connection_values; very large POST
461 * data *will* be made available incrementally in
462 * upload_data)
463 * @param upload_data_size set initially to the size of the
464 * @a upload_data provided; the method must update this
465 * value to the number of bytes NOT processed;
466 * @param con_cls pointer to location where we store the 'struct Request'
467 * @return #MHD_YES if the connection was handled successfully,
468 * #MHD_NO if the socket must be closed due to a serious
469 * error while handling the request
470 */
471static MHD_RESULT
472create_response (void *cls,
473 struct MHD_Connection *con,
474 const char *url,
475 const char *meth,
476 const char *ver,
477 const char *upload_data,
478 size_t *upload_data_size,
479 void **con_cls)
480{
481 char *origin;
482 char *pw;
483 char *user;
484 struct AcceptedRequest *ar;
485 struct GNUNET_HashCode key;
486 struct MhdConnectionHandle *con_handle;
487 struct GNUNET_REST_RequestHandle *rest_conndata_handle;
488 struct PluginListEntry *ple;
489
490 ar = *con_cls;
491 if (NULL == ar)
492 {
493 GNUNET_break (0);
494 return MHD_NO;
495 }
496
497 if (NULL == ar->con_handle)
498 {
499 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New connection %s\n", url);
500 con_handle = GNUNET_new (struct MhdConnectionHandle);
501 con_handle->con = con;
502 con_handle->state = GN_REST_STATE_INIT;
503 ar->con_handle = con_handle;
504 return MHD_YES;
505 }
506 con_handle = ar->con_handle;
507 if (GN_REST_STATE_INIT == con_handle->state)
508 {
509 rest_conndata_handle = GNUNET_new (struct GNUNET_REST_RequestHandle);
510 rest_conndata_handle->method = meth;
511 rest_conndata_handle->url = url;
512 rest_conndata_handle->data = upload_data;
513 rest_conndata_handle->data_size = *upload_data_size;
514 rest_conndata_handle->url_param_map =
515 GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
516 rest_conndata_handle->header_param_map =
517 GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
518 con_handle->data_handle = rest_conndata_handle;
519 MHD_get_connection_values (con,
520 MHD_GET_ARGUMENT_KIND,
521 (MHD_KeyValueIterator) & url_iterator,
522 rest_conndata_handle);
523 MHD_get_connection_values (con,
524 MHD_HEADER_KIND,
525 (MHD_KeyValueIterator) & header_iterator,
526 rest_conndata_handle);
527 if (GNUNET_YES == basic_auth_enabled)
528 {
529 pw = NULL;
530 user = MHD_basic_auth_get_username_password (con, &pw);
531 if ((NULL == user) ||
532 (0 != strcmp (user, cuser)))
533 {
534 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
535 "Unknown user %s\n", user);
536 MHD_queue_basic_auth_fail_response (con, "gnunet", failure_response);
537 return MHD_YES;
538 }
539 if ((NULL == pw) ||
540 (0 != strcmp (pw, basic_auth_secret)))
541 {
542 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
543 "Password incorrect\n");
544 MHD_queue_basic_auth_fail_response (con, "gnunet", failure_response);
545 GNUNET_free (pw);
546 return MHD_YES;
547 }
548 GNUNET_free (pw);
549 }
550
551 con_handle->pp = MHD_create_post_processor (con,
552 65536,
553 &post_data_iter,
554 rest_conndata_handle);
555 if (*upload_data_size)
556 {
557 MHD_post_process (con_handle->pp, upload_data, *upload_data_size);
558 }
559 MHD_destroy_post_processor (con_handle->pp);
560
561 con_handle->state = GN_REST_STATE_PROCESSING;
562 for (ple = plugins_head; NULL != ple; ple = ple->next)
563 {
564 if (GNUNET_YES == ple->plugin->process_request (rest_conndata_handle,
565 &plugin_callback,
566 con_handle))
567 break; /* Request handled */
568 }
569 if (NULL == ple)
570 {
571 /** Request not handled **/
572 MHD_queue_response (con, MHD_HTTP_NOT_FOUND, failure_response);
573 }
574 *upload_data_size = 0;
575 run_mhd_now ();
576 return MHD_YES;
577 }
578 if (NULL == con_handle->response)
579 {
580 // Suspend connection until plugin is done
581 MHD_suspend_connection (con_handle->con);
582 return MHD_YES;
583 }
584 // MHD_resume_connection (con_handle->con);
585 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
586 "Queueing response from plugin with MHD\n");
587 // Handle Preflights for extensions
588 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking origin\n");
589 GNUNET_CRYPTO_hash ("origin", strlen ("origin"), &key);
590 origin = GNUNET_CONTAINER_multihashmap_get (con_handle->data_handle
591 ->header_param_map,
592 &key);
593 if (NULL != origin)
594 {
595 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin: %s\n", origin);
596 // Only echo for browser plugins
597 if (GNUNET_YES == echo_origin)
598 {
599 if ((0 ==
600 strncmp ("moz-extension://", origin, strlen ("moz-extension://"))) ||
601 (0 == strncmp ("chrome-extension://",
602 origin,
603 strlen ("chrome-extension://"))))
604 {
605 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
606 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
607 origin));
608 }
609 }
610 if (NULL != allow_origins)
611 {
612 char *tmp = GNUNET_strdup (allow_origins);
613 char *allow_origin = strtok (tmp, ",");
614 while (NULL != allow_origin)
615 {
616 if (0 == strncmp (allow_origin, origin, strlen (allow_origin)))
617 {
618 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
619 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
620 allow_origin));
621 break;
622 }
623 allow_origin = strtok (NULL, ",");
624 }
625 GNUNET_free (tmp);
626 }
627 }
628 if (NULL != allow_credentials)
629 {
630 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
631 "Access-Control-Allow-Credentials",
632 allow_credentials));
633 }
634 if (NULL != allow_headers)
635 {
636 GNUNET_assert (MHD_NO != MHD_add_response_header (con_handle->response,
637 "Access-Control-Allow-Headers",
638 allow_headers));
639 }
640 run_mhd_now ();
641 {
642 MHD_RESULT ret = MHD_queue_response (con,
643 con_handle->status,
644 con_handle->response);
645 // cleanup_handle (con_handle);
646 return ret;
647 }
648}
649
650
651/* ******************** MHD HTTP setup and event loop ******************** */
652
653
654/**
655 * Kill the MHD daemon.
656 */
657static void
658kill_httpd ()
659{
660 if (NULL != httpd)
661 {
662 MHD_stop_daemon (httpd);
663 httpd = NULL;
664 }
665 if (NULL != httpd_task)
666 {
667 GNUNET_SCHEDULER_cancel (httpd_task);
668 httpd_task = NULL;
669 }
670 if (NULL != ltask4)
671 {
672 GNUNET_SCHEDULER_cancel (ltask4);
673 ltask4 = NULL;
674 }
675 if (NULL != ltask6)
676 {
677 GNUNET_SCHEDULER_cancel (ltask6);
678 ltask6 = NULL;
679 }
680
681 if (NULL != lsock4)
682 {
683 GNUNET_NETWORK_socket_close (lsock4);
684 lsock4 = NULL;
685 }
686 if (NULL != lsock6)
687 {
688 GNUNET_NETWORK_socket_close (lsock6);
689 lsock6 = NULL;
690 }
691}
692
693
694/**
695 * Schedule MHD. This function should be called initially when an
696 * MHD is first getting its client socket, and will then automatically
697 * always be called later whenever there is work to be done.
698 *
699 * @param hd the daemon to schedule
700 */
701static void
702schedule_httpd ()
703{
704 fd_set rs;
705 fd_set ws;
706 fd_set es;
707 struct GNUNET_NETWORK_FDSet *wrs;
708 struct GNUNET_NETWORK_FDSet *wws;
709 int max;
710 int haveto;
711 MHD_UNSIGNED_LONG_LONG timeout;
712 struct GNUNET_TIME_Relative tv;
713
714 FD_ZERO (&rs);
715 FD_ZERO (&ws);
716 FD_ZERO (&es);
717 max = -1;
718 if (MHD_YES != MHD_get_fdset (httpd, &rs, &ws, &es, &max))
719 {
720 kill_httpd ();
721 return;
722 }
723 haveto = MHD_get_timeout (httpd, &timeout);
724 if (MHD_YES == haveto)
725 tv.rel_value_us = (uint64_t) timeout * 1000LL;
726 else
727 tv = GNUNET_TIME_UNIT_FOREVER_REL;
728 if (-1 != max)
729 {
730 wrs = GNUNET_NETWORK_fdset_create ();
731 wws = GNUNET_NETWORK_fdset_create ();
732 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
733 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
734 }
735 else
736 {
737 wrs = NULL;
738 wws = NULL;
739 }
740 if (NULL != httpd_task)
741 {
742 GNUNET_SCHEDULER_cancel (httpd_task);
743 httpd_task = NULL;
744 }
745 if ((MHD_YES == haveto) || (-1 != max))
746 {
747 httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
748 tv,
749 wrs,
750 wws,
751 &do_httpd,
752 NULL);
753 }
754 if (NULL != wrs)
755 GNUNET_NETWORK_fdset_destroy (wrs);
756 if (NULL != wws)
757 GNUNET_NETWORK_fdset_destroy (wws);
758}
759
760/**
761 * Function called when MHD first processes an incoming connection.
762 * Gives us the respective URI information.
763 *
764 * We use this to associate the `struct MHD_Connection` with our
765 * internal `struct AcceptedRequest` data structure (by checking
766 * for matching sockets).
767 *
768 * @param cls the HTTP server handle (a `struct MhdHttpList`)
769 * @param url the URL that is being requested
770 * @param connection MHD connection object for the request
771 * @return the `struct Socks5Request` that this @a connection is for
772 */
773static void *
774mhd_log_callback (void *cls,
775 const char *url,
776 struct MHD_Connection *connection)
777{
778 struct AcceptedRequest *ar;
779 const union MHD_ConnectionInfo *ci;
780
781 ci = MHD_get_connection_info (connection,
782 MHD_CONNECTION_INFO_SOCKET_CONTEXT);
783 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing %s\n", url);
784 if (NULL == ci)
785 {
786 GNUNET_break (0);
787 return NULL;
788 }
789 ar = ci->socket_context;
790 return ar;
791}
792
793
794
795/**
796 * Function called when MHD decides that we are done with a connection.
797 *
798 * @param cls NULL
799 * @param connection connection handle
800 * @param con_cls value as set by the last call to
801 * the MHD_AccessHandlerCallback, should be our handle
802 * @param toe reason for request termination (ignored)
803 */
804static void
805mhd_completed_cb (void *cls,
806 struct MHD_Connection *connection,
807 void **con_cls,
808 enum MHD_RequestTerminationCode toe)
809{
810 struct AcceptedRequest *ar = *con_cls;
811 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
812 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
813 "MHD encountered error handling request: %d\n",
814 toe);
815 if (NULL == ar)
816 return;
817 if (NULL != ar->con_handle)
818 {
819 cleanup_handle (ar->con_handle);
820 ar->con_handle = NULL;
821 }
822 ar->socket_with_mhd = GNUNET_YES;
823 *con_cls = NULL;
824}
825
826/**
827 * Function called when MHD connection is opened or closed.
828 *
829 * @param cls NULL
830 * @param connection connection handle
831 * @param con_cls value as set by the last call to
832 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *`
833 * @param toe connection notification type
834 */
835static void
836mhd_connection_cb (void *cls,
837 struct MHD_Connection *connection,
838 void **con_cls,
839 enum MHD_ConnectionNotificationCode cnc)
840{
841 struct AcceptedRequest *ar;
842 const union MHD_ConnectionInfo *ci;
843 int sock;
844
845 switch (cnc)
846 {
847 case MHD_CONNECTION_NOTIFY_STARTED:
848 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection started...\n");
849 ci = MHD_get_connection_info (connection,
850 MHD_CONNECTION_INFO_CONNECTION_FD);
851 if (NULL == ci)
852 {
853 GNUNET_break (0);
854 return;
855 }
856 sock = ci->connect_fd;
857 for (ar = req_list_head; NULL != ar; ar = ar->next)
858 {
859 if (GNUNET_NETWORK_get_fd (ar->sock) == sock)
860 {
861 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
862 "Context set...\n");
863 *con_cls = ar;
864 break;
865 }
866 }
867 break;
868
869 case MHD_CONNECTION_NOTIFY_CLOSED:
870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
871 "Connection closed... cleaning up\n");
872 ar = *con_cls;
873 if (NULL == ar)
874 {
875 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
876 "Connection stale!\n");
877 return;
878 }
879 cleanup_ar (ar);
880 *con_cls = NULL;
881 break;
882
883 default:
884 GNUNET_break (0);
885 }
886}
887
888
889
890/**
891 * Task run whenever HTTP server operations are pending.
892 *
893 * @param cls NULL
894 */
895static void
896do_httpd (void *cls)
897{
898 httpd_task = NULL;
899 MHD_run (httpd);
900 schedule_httpd ();
901}
902
903
904/**
905 * Accept new incoming connections
906 *
907 * @param cls the closure with the lsock4 or lsock6
908 */
909static void
910do_accept (void *cls)
911{
912 struct GNUNET_NETWORK_Handle *lsock = cls;
913 struct AcceptedRequest *ar;
914 int fd;
915 const struct sockaddr *addr;
916 socklen_t len;
917
918 GNUNET_assert (NULL != lsock);
919 if (lsock == lsock4)
920 {
921 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
922 lsock,
923 &do_accept,
924 lsock);
925 }
926 else if (lsock == lsock6)
927 {
928 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
929 lsock,
930 &do_accept,
931 lsock);
932 }
933 else
934 GNUNET_assert (0);
935 ar = GNUNET_new (struct AcceptedRequest);
936 ar->socket_with_mhd = GNUNET_YES;
937 ar->sock = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
938 if (NULL == ar->sock)
939 {
940 GNUNET_free (ar);
941 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
942 return;
943 }
944 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
945 "Got an inbound connection, waiting for data\n");
946 fd = GNUNET_NETWORK_get_fd (ar->sock);
947 addr = GNUNET_NETWORK_get_addr (ar->sock);
948 len = GNUNET_NETWORK_get_addrlen (ar->sock);
949 GNUNET_CONTAINER_DLL_insert (req_list_head,
950 req_list_tail,
951 ar);
952 if (MHD_YES != MHD_add_connection (httpd, fd, addr, len))
953 {
954 GNUNET_NETWORK_socket_close (ar->sock);
955 GNUNET_free (ar);
956 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
957 _ ("Failed to pass client to MHD\n"));
958 return;
959 }
960 schedule_httpd ();
961}
962
963
964/**
965 * Task run on shutdown
966 *
967 * @param cls closure
968 */
969static void
970do_shutdown (void *cls)
971{
972 struct PluginListEntry *ple;
973
974 while (NULL != plugins_head)
975 {
976 ple = plugins_head;
977 GNUNET_CONTAINER_DLL_remove (plugins_head,
978 plugins_tail,
979 ple);
980 GNUNET_PLUGIN_unload (ple->libname, ple->plugin);
981 GNUNET_free (ple->libname);
982 GNUNET_free (ple);
983 }
984 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n");
985 kill_httpd ();
986 GNUNET_free (allow_credentials);
987 GNUNET_free (allow_headers);
988}
989
990
991/**
992 * Create an IPv4 listen socket bound to our port.
993 *
994 * @return NULL on error
995 */
996static struct GNUNET_NETWORK_Handle *
997bind_v4 ()
998{
999 struct GNUNET_NETWORK_Handle *ls;
1000 struct sockaddr_in sa4;
1001 int eno;
1002
1003 memset (&sa4, 0, sizeof(sa4));
1004 sa4.sin_family = AF_INET;
1005 sa4.sin_port = htons (port);
1006 sa4.sin_addr.s_addr = address;
1007#if HAVE_SOCKADDR_IN_SIN_LEN
1008 sa4.sin_len = sizeof(sa4);
1009#endif
1010 ls = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
1011 if (NULL == ls)
1012 return NULL;
1013 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
1014 (const struct sockaddr *) &sa4,
1015 sizeof(sa4)))
1016 {
1017 eno = errno;
1018 GNUNET_NETWORK_socket_close (ls);
1019 errno = eno;
1020 return NULL;
1021 }
1022 return ls;
1023}
1024
1025
1026/**
1027 * Create an IPv6 listen socket bound to our port.
1028 *
1029 * @return NULL on error
1030 */
1031static struct GNUNET_NETWORK_Handle *
1032bind_v6 ()
1033{
1034 struct GNUNET_NETWORK_Handle *ls;
1035 struct sockaddr_in6 sa6;
1036 int eno;
1037
1038 memset (&sa6, 0, sizeof(sa6));
1039 sa6.sin6_family = AF_INET6;
1040 sa6.sin6_port = htons (port);
1041 sa6.sin6_addr = address6;
1042#if HAVE_SOCKADDR_IN_SIN_LEN
1043 sa6.sin6_len = sizeof(sa6);
1044#endif
1045 ls = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0);
1046 if (NULL == ls)
1047 return NULL;
1048 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
1049 (const struct sockaddr *) &sa6,
1050 sizeof(sa6)))
1051 {
1052 eno = errno;
1053 GNUNET_NETWORK_socket_close (ls);
1054 errno = eno;
1055 return NULL;
1056 }
1057 return ls;
1058}
1059
1060
1061/**
1062 * Callback for plugin load
1063 *
1064 * @param cls NULL
1065 * @param libname the name of the library loaded
1066 * @param lib_ret the object returned by the plugin initializer
1067 */
1068static void
1069load_plugin (void *cls, const char *libname, void *lib_ret)
1070{
1071 struct GNUNET_REST_Plugin *plugin = lib_ret;
1072 struct PluginListEntry *ple;
1073
1074 if (NULL == lib_ret)
1075 {
1076 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1077 "Could not load plugin `%s'\n",
1078 libname);
1079 return;
1080 }
1081 GNUNET_assert (1 < strlen (plugin->name));
1082 GNUNET_assert ('/' == *plugin->name);
1083 ple = GNUNET_new (struct PluginListEntry);
1084 ple->libname = GNUNET_strdup (libname);
1085 ple->plugin = plugin;
1086 GNUNET_CONTAINER_DLL_insert (plugins_head,
1087 plugins_tail,
1088 ple);
1089 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded plugin `%s'\n", libname);
1090}
1091
1092
1093/**
1094 * Main function that will be run
1095 *
1096 * @param cls closure
1097 * @param args remaining command-line arguments
1098 * @param cfgfile name of the configuration file used (for saving, can be NULL)
1099 * @param c configuration
1100 */
1101static void
1102run (void *cls,
1103 char *const *args,
1104 const char *cfgfile,
1105 const struct GNUNET_CONFIGURATION_Handle *c)
1106{
1107 char *addr_str;
1108 char *basic_auth_file;
1109 uint64_t secret;
1110
1111 cfg = c;
1112 plugins_head = NULL;
1113 plugins_tail = NULL;
1114 /* Get port to bind to */
1115 if (GNUNET_OK !=
1116 GNUNET_CONFIGURATION_get_value_number (cfg, "rest", "HTTP_PORT", &port))
1117 {
1118 // No address specified
1119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Using default port...\n");
1120 port = GNUNET_REST_SERVICE_PORT;
1121 }
1122
1123 /* Get address to bind to */
1124 if (GNUNET_OK !=
1125 GNUNET_CONFIGURATION_get_value_string (cfg, "rest", "BIND_TO", &addr_str))
1126 {
1127 // No address specified
1128 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind to...\n");
1129 GNUNET_SCHEDULER_shutdown ();
1130 return;
1131 }
1132 if (1 != inet_pton (AF_INET, addr_str, &address))
1133 {
1134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1135 "Unable to parse address %s\n",
1136 addr_str);
1137 GNUNET_free (addr_str);
1138 GNUNET_SCHEDULER_shutdown ();
1139 return;
1140 }
1141 GNUNET_free (addr_str);
1142 /* Get address to bind to */
1143 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1144 "rest",
1145 "BIND_TO6",
1146 &addr_str))
1147 {
1148 // No address specified
1149 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind6 to...\n");
1150 GNUNET_SCHEDULER_shutdown ();
1151 return;
1152 }
1153 if (1 != inet_pton (AF_INET6, addr_str, &address6))
1154 {
1155 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1156 "Unable to parse IPv6 address %s\n",
1157 addr_str);
1158 GNUNET_free (addr_str);
1159 GNUNET_SCHEDULER_shutdown ();
1160 return;
1161 }
1162 GNUNET_free (addr_str);
1163
1164 basic_auth_enabled = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1165 "rest",
1166 "BASIC_AUTH_ENABLED");
1167 if (basic_auth_enabled)
1168 {
1169 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1170 "rest",
1171 "BASIC_AUTH_SECRET_FILE",
1172 &basic_auth_file))
1173 {
1174 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1175 "No basic auth secret file location set...\n");
1176 GNUNET_SCHEDULER_shutdown ();
1177 return;
1178 }
1179 if (GNUNET_YES != GNUNET_DISK_file_test (basic_auth_file))
1180 {
1181 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1182 "No basic auth secret found... generating\n");
1183 secret = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1184 UINT64_MAX);
1185 basic_auth_secret = GNUNET_STRINGS_data_to_string_alloc (&secret,
1186 sizeof(secret));
1187 if (GNUNET_OK !=
1188 GNUNET_DISK_fn_write (basic_auth_file,
1189 basic_auth_secret,
1190 strlen (basic_auth_secret),
1191 GNUNET_DISK_PERM_USER_READ
1192 | GNUNET_DISK_PERM_USER_WRITE))
1193 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1194 "write",
1195 basic_auth_file);
1196 GNUNET_free (basic_auth_file);
1197 }
1198 else
1199 {
1200 char basic_auth_secret_tmp[16]; // Should be more than enough
1201 memset (basic_auth_secret_tmp, 0, 16);
1202 if (GNUNET_SYSERR == GNUNET_DISK_fn_read (basic_auth_file,
1203 basic_auth_secret_tmp,
1204 sizeof (basic_auth_secret_tmp) - 1))
1205 {
1206 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1207 "Unable to read basic auth secret file.\n");
1208 GNUNET_SCHEDULER_shutdown ();
1209 GNUNET_free (basic_auth_file);
1210 return;
1211 }
1212 GNUNET_free (basic_auth_file);
1213 if (0 != getlogin_r (cuser, _POSIX_LOGIN_NAME_MAX))
1214 {
1215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1216 "Unable to get user.\n");
1217 GNUNET_SCHEDULER_shutdown ();
1218 return;
1219 }
1220 basic_auth_secret = GNUNET_strdup (basic_auth_secret_tmp);
1221 }
1222 }
1223
1224 /* Get CORS data from cfg */
1225 echo_origin =
1226 GNUNET_CONFIGURATION_get_value_yesno (cfg,
1227 "rest",
1228 "REST_ECHO_ORIGIN_WEBEXT");
1229 allow_origins = NULL;
1230 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1231 "rest",
1232 "REST_ALLOW_ORIGIN",
1233 &allow_origins))
1234 {
1235 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1236 "No CORS Access-Control-Allow-Origin header will be sent...\n");
1237 }
1238 if (GNUNET_OK !=
1239 GNUNET_CONFIGURATION_get_value_string (cfg,
1240 "rest",
1241 "REST_ALLOW_CREDENTIALS",
1242 &allow_credentials))
1243 {
1244 // No origin specified
1245 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1246 "No CORS Credential Header will be sent...\n");
1247 }
1248
1249 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1250 "rest",
1251 "REST_ALLOW_HEADERS",
1252 &allow_headers))
1253 {
1254 // No origin specified
1255 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1256 "No CORS Access-Control-Allow-Headers Header will be sent...\n");
1257 }
1258
1259/* Open listen socket proxy */
1260 lsock6 = bind_v6 ();
1261 if (NULL == lsock6)
1262 {
1263 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
1264 }
1265 else
1266 {
1267 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
1268 {
1269 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1270 GNUNET_NETWORK_socket_close (lsock6);
1271 lsock6 = NULL;
1272 }
1273 else
1274 {
1275 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1276 lsock6,
1277 &do_accept,
1278 lsock6);
1279 }
1280 }
1281 lsock4 = bind_v4 ();
1282 if (NULL == lsock4)
1283 {
1284 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
1285 }
1286 else
1287 {
1288 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
1289 {
1290 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1291 GNUNET_NETWORK_socket_close (lsock4);
1292 lsock4 = NULL;
1293 }
1294 else
1295 {
1296 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1297 lsock4,
1298 &do_accept,
1299 lsock4);
1300 }
1301 }
1302 if ((NULL == lsock4) && (NULL == lsock6))
1303 {
1304 GNUNET_SCHEDULER_shutdown ();
1305 return;
1306 }
1307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service listens on port %llu\n",
1308 port);
1309 httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET
1310 | MHD_ALLOW_SUSPEND_RESUME,
1311 0,
1312 NULL,
1313 NULL,
1314 &create_response,
1315 NULL,
1316 MHD_OPTION_CONNECTION_TIMEOUT,
1317 (unsigned int) 16,
1318 MHD_OPTION_NOTIFY_CONNECTION,
1319 &mhd_connection_cb,
1320 NULL,
1321 MHD_OPTION_URI_LOG_CALLBACK,
1322 mhd_log_callback,
1323 NULL,
1324 MHD_OPTION_NOTIFY_COMPLETED,
1325 &mhd_completed_cb,
1326 NULL,
1327 MHD_OPTION_END);
1328 if (NULL == httpd)
1329 {
1330 GNUNET_SCHEDULER_shutdown ();
1331 return;
1332 }
1333/* Load plugins */
1334 GNUNET_PLUGIN_load_all_in_context (GNUNET_OS_project_data_default (),
1335 "libgnunet_plugin_rest",
1336 (void *) cfg,
1337 &load_plugin,
1338 NULL);
1339 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1340}
1341
1342
1343/**
1344 *
1345 * The main function for gnunet-rest-service
1346 *
1347 * @param argc number of arguments from the cli
1348 * @param argv command line arguments
1349 * @return 0 ok, 1 on error
1350 *
1351 */
1352int
1353main (int argc, char *const *argv)
1354{
1355 struct GNUNET_GETOPT_CommandLineOption options[] =
1356 { GNUNET_GETOPT_OPTION_END };
1357 static const char *err_page = "{}";
1358 int ret;
1359
1360 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1361 return 2;
1362 GNUNET_log_setup ("gnunet-rest-server", "WARNING", NULL);
1363 failure_response = MHD_create_response_from_buffer (strlen (err_page),
1364 (void *) err_page,
1365 MHD_RESPMEM_PERSISTENT);
1366 ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc,
1367 argv,
1368 "gnunet-rest-server",
1369 _ ("GNUnet REST server"),
1370 options,
1371 &run,
1372 NULL))
1373 ? 0
1374 : 1;
1375 MHD_destroy_response (failure_response);
1376 GNUNET_free_nz ((char *) argv);
1377 return ret;
1378}
1379
1380
1381/* end of gnunet-rest-server.c */
diff --git a/src/service/rest/meson.build b/src/service/rest/meson.build
new file mode 100644
index 000000000..2dd3d8de4
--- /dev/null
+++ b/src/service/rest/meson.build
@@ -0,0 +1,58 @@
1libgnunetrest_src = ['rest.c']
2
3gnunetservicerest_src = ['gnunet-rest-server.c']
4
5configure_file(input : 'rest.conf',
6 output : 'rest.conf',
7 configuration : cdata,
8 install: true,
9 install_dir: pkgcfgdir)
10
11
12if get_option('monolith')
13 foreach p : libgnunetrest_src + gnunetservicerest_src
14 gnunet_src += 'rest/' + p
15 endforeach
16 subdir_done()
17endif
18
19
20libgnunetrest = library('gnunetrest',
21 libgnunetrest_src,
22 soversion: '0',
23 version: '0.0.0',
24 dependencies: [libgnunetutil_dep, mhd_dep],
25 include_directories: [incdir, configuration_inc],
26 install: true,
27 install_dir: get_option('libdir'))
28pkg.generate(libgnunetrest, url: 'https://www.gnunet.org',
29 description : 'Provides API for accessing the REST service')
30libgnunetrest_dep = declare_dependency(link_with : libgnunetrest)
31
32shared_module('gnunet_plugin_rest_config',
33 ['plugin_rest_config.c'],
34 dependencies: [libgnunetrest_dep,
35 libgnunetutil_dep,
36 json_dep,
37 mhd_dep],
38 include_directories: [incdir, configuration_inc],
39 install: true,
40 install_dir: get_option('libdir') / 'gnunet')
41shared_module('gnunet_plugin_rest_copying',
42 ['plugin_rest_copying.c'],
43 dependencies: [libgnunetrest_dep,
44 libgnunetutil_dep,
45 json_dep,
46 mhd_dep],
47 include_directories: [incdir, configuration_inc],
48 install: true,
49 install_dir: get_option('libdir') / 'gnunet')
50
51
52executable ('gnunet-rest-server',
53 gnunetservicerest_src,
54 dependencies: [libgnunetrest_dep, libgnunetutil_dep, mhd_dep],
55 include_directories: [incdir, configuration_inc],
56 install: true,
57 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
58
diff --git a/src/service/rest/rest.c b/src/service/rest/rest.c
new file mode 100644
index 000000000..757c0b979
--- /dev/null
+++ b/src/service/rest/rest.c
@@ -0,0 +1,99 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rest/rest.c
23 * @brief helper library to create JSON REST Objects and handle REST
24 * responses/requests.
25 * @author Martin Schanzenbach
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_rest_lib.h"
30#include "microhttpd.h"
31
32/**
33 * REST Utilities
34 */
35
36int
37GNUNET_REST_namespace_match (const char *url, const char *namespace)
38{
39 return 0 == strncmp (namespace, url, strlen (namespace));
40}
41
42
43struct MHD_Response*
44GNUNET_REST_create_response (const char *data)
45{
46 struct MHD_Response *resp;
47 size_t len;
48
49 if (NULL == data)
50 {
51 len = 0;
52 data = "";
53 }
54 else
55 len = strlen (data);
56 resp = MHD_create_response_from_buffer (len,
57 (void *) data,
58 MHD_RESPMEM_MUST_COPY);
59 return resp;
60}
61
62
63int
64GNUNET_REST_handle_request (struct GNUNET_REST_RequestHandle *conn,
65 const struct GNUNET_REST_RequestHandler *handlers,
66 struct GNUNET_REST_RequestHandlerError *err,
67 void *cls)
68{
69 int count;
70 int i;
71 char *url;
72
73 count = 0;
74 while (NULL != handlers[count].method)
75 count++;
76
77 GNUNET_asprintf (&url, "%s", conn->url);
78 if (url[strlen (url) - 1] == '/')
79 url[strlen (url) - 1] = '\0';
80 for (i = 0; i < count; i++)
81 {
82 if (0 != strcasecmp (conn->method, handlers[i].method))
83 continue;
84 if (strlen (url) < strlen (handlers[i].namespace))
85 continue;
86 if (GNUNET_NO == GNUNET_REST_namespace_match (url, handlers[i].namespace))
87 continue;
88 // Match
89 handlers[i].proc (conn, (const char *) url, cls);
90 GNUNET_free (url);
91 return GNUNET_YES;
92 }
93 GNUNET_free (url);
94 err->error_code = MHD_HTTP_BAD_REQUEST;
95 return GNUNET_NO;
96}
97
98
99/* end of rest.c */
diff --git a/src/service/rest/rest.conf b/src/service/rest/rest.conf
new file mode 100644
index 000000000..85c006c9b
--- /dev/null
+++ b/src/service/rest/rest.conf
@@ -0,0 +1,14 @@
1[rest]
2UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-rest.sock
3BINARY=gnunet-rest-server
4IMMEDIATE_START=YES
5HTTP_PORT=7776
6BIND_TO=127.0.0.1
7BIND_TO6=::1
8REST_ALLOW_HEADERS=Authorization,Accept,Content-Type
9REST_ECHO_ORIGIN_WEBEXT=YES
10REST_ALLOW_ORIGIN=http://localhost:4200
11REST_ALLOW_CREDENTIALS=true
12RUN_PER_USER=NO
13BASIC_AUTH_SECRET_FILE=$GNUNET_DATA_HOME/rest/secret
14BASIC_AUTH_ENABLED=YES