diff options
Diffstat (limited to 'src/service')
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 @@ | |||
1 | SUBDIRS = \ | 1 | SUBDIRS = \ |
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 @@ | |||
1 | arm.conf | ||
2 | mockup-service | ||
3 | gnunet-service-arm | ||
4 | test_arm_api | ||
5 | test_exponential_backoff | ||
6 | test_gnunet_arm.py | ||
7 | test_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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
5 | |||
6 | libexecdir= $(pkglibdir)/libexec/ | ||
7 | |||
8 | pkgcfg_DATA = \ | ||
9 | arm.conf | ||
10 | |||
11 | if USE_COVERAGE | ||
12 | AM_CFLAGS = --coverage -O0 | ||
13 | XLIB = -lgcov | ||
14 | endif | ||
15 | |||
16 | lib_LTLIBRARIES = libgnunetarm.la | ||
17 | |||
18 | libgnunetarm_la_SOURCES = \ | ||
19 | arm_api.c arm_monitor_api.c arm.h | ||
20 | libgnunetarm_la_LIBADD = \ | ||
21 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
22 | $(GN_LIBINTL) $(XLIB) | ||
23 | libgnunetarm_la_LDFLAGS = \ | ||
24 | $(GN_LIB_LDFLAGS) \ | ||
25 | -version-info 2:0:0 | ||
26 | |||
27 | |||
28 | noinst_PROGRAMS = \ | ||
29 | mockup-service | ||
30 | |||
31 | libexec_PROGRAMS = \ | ||
32 | gnunet-service-arm | ||
33 | |||
34 | gnunet_service_arm_SOURCES = \ | ||
35 | gnunet-service-arm.c | ||
36 | gnunet_service_arm_LDADD = \ | ||
37 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
38 | $(GN_LIBINTL) | ||
39 | |||
40 | mockup_service_SOURCES = \ | ||
41 | mockup-service.c | ||
42 | mockup_service_LDADD = \ | ||
43 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
44 | mockup_service_LDFLAGS = \ | ||
45 | $(GN_LIBINTL) | ||
46 | |||
47 | |||
48 | check_PROGRAMS = \ | ||
49 | test_arm_api \ | ||
50 | test_exponential_backoff \ | ||
51 | test_gnunet_service_arm | ||
52 | |||
53 | if ENABLE_TEST_RUN | ||
54 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
55 | TESTS = $(check_PROGRAMS) $(check_SCRIPTS) | ||
56 | endif | ||
57 | |||
58 | test_arm_api_SOURCES = \ | ||
59 | test_arm_api.c | ||
60 | test_arm_api_LDADD = \ | ||
61 | libgnunetarm.la \ | ||
62 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
63 | |||
64 | test_exponential_backoff_SOURCES = \ | ||
65 | test_exponential_backoff.c | ||
66 | test_exponential_backoff_LDADD = \ | ||
67 | libgnunetarm.la \ | ||
68 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
69 | |||
70 | test_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 | |||
76 | EXTRA_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 | ||
3 | HOSTNAME = localhost | ||
4 | BINARY = gnunet-service-arm | ||
5 | ACCEPT_FROM = 127.0.0.1; | ||
6 | ACCEPT_FROM6 = ::1; | ||
7 | |||
8 | # Special case, uses user runtime dir even for per-system service. | ||
9 | UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-arm.sock | ||
10 | UNIX_MATCH_UID = YES | ||
11 | UNIX_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 | ||
19 | GLOBAL_PREFIX = | ||
20 | |||
21 | # By default, when launching GNUnet via gnunet-arm, log | ||
22 | # everything to a simple flat log file. | ||
23 | OPTIONS = -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 | |||
36 | GNUNET_NETWORK_STRUCT_BEGIN | ||
37 | |||
38 | /** | ||
39 | * Status update from ARM to client. | ||
40 | */ | ||
41 | struct 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 | |||
56 | struct 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 | */ | ||
86 | struct 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 | |||
99 | struct 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 | */ | ||
144 | struct 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 | |||
162 | GNUNET_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 | */ | ||
40 | struct 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 | */ | ||
102 | struct 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 | */ | ||
173 | static int | ||
174 | reconnect_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 | */ | ||
182 | static void | ||
183 | reconnect_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 | */ | ||
198 | static void | ||
199 | reconnect_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 | */ | ||
242 | static struct GNUNET_ARM_Operation * | ||
243 | find_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 | */ | ||
261 | static void | ||
262 | handle_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 | */ | ||
324 | static const char * | ||
325 | pool_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 | */ | ||
349 | static int | ||
350 | check_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 | */ | ||
398 | static void | ||
399 | handle_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 | */ | ||
465 | static void | ||
466 | handle_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 | */ | ||
491 | static void | ||
492 | mq_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 | */ | ||
518 | static int | ||
519 | reconnect_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 | */ | ||
576 | struct GNUNET_ARM_Handle * | ||
577 | GNUNET_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 | */ | ||
601 | void | ||
602 | GNUNET_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 | */ | ||
651 | static enum GNUNET_ARM_Result | ||
652 | start_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 ("edbinary, | ||
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 | */ | ||
788 | void | ||
789 | GNUNET_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 | */ | ||
825 | static struct GNUNET_ARM_Operation * | ||
826 | change_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 | */ | ||
867 | static void | ||
868 | notify_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 | */ | ||
894 | static void | ||
895 | notify_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 | */ | ||
925 | struct GNUNET_ARM_Operation * | ||
926 | GNUNET_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 (¬ify_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 | ¬ify_starting, | ||
1011 | op); | ||
1012 | GNUNET_DISK_pipe_close (sig); | ||
1013 | } | ||
1014 | else | ||
1015 | { | ||
1016 | op->async = GNUNET_SCHEDULER_add_now (¬ify_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 | */ | ||
1037 | struct GNUNET_ARM_Operation * | ||
1038 | GNUNET_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 | */ | ||
1072 | struct GNUNET_ARM_Operation * | ||
1073 | GNUNET_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 | */ | ||
40 | struct 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 | */ | ||
80 | static int | ||
81 | reconnect_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 | */ | ||
89 | static void | ||
90 | reconnect_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 | */ | ||
107 | static void | ||
108 | reconnect_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 | */ | ||
130 | static int | ||
131 | check_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 | */ | ||
153 | static void | ||
154 | handle_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 | */ | ||
177 | static void | ||
178 | mq_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 | */ | ||
193 | static int | ||
194 | reconnect_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 | */ | ||
232 | struct GNUNET_ARM_MonitorHandle * | ||
233 | GNUNET_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 | */ | ||
257 | void | ||
258 | GNUNET_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 | */ | ||
42 | static char *wait_filename; | ||
43 | |||
44 | /** | ||
45 | * Handle for the file for writing resource summaries. | ||
46 | */ | ||
47 | static 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 | */ | ||
62 | struct ServiceList; | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Record with information about a listen socket we have open. | ||
67 | */ | ||
68 | struct 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 | */ | ||
110 | struct 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 | */ | ||
206 | static struct ServiceList *running_head; | ||
207 | |||
208 | /** | ||
209 | * List of running services. | ||
210 | */ | ||
211 | static struct ServiceList *running_tail; | ||
212 | |||
213 | /** | ||
214 | * Our configuration | ||
215 | */ | ||
216 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
217 | |||
218 | /** | ||
219 | * Command to prepend to each actual command. | ||
220 | */ | ||
221 | static char *prefix_command; | ||
222 | |||
223 | /** | ||
224 | * Option to append to each actual command. | ||
225 | */ | ||
226 | static char *final_option; | ||
227 | |||
228 | /** | ||
229 | * ID of task called whenever we get a SIGCHILD. | ||
230 | */ | ||
231 | static struct GNUNET_SCHEDULER_Task *child_death_task; | ||
232 | |||
233 | /** | ||
234 | * ID of task called whenever the timeout for restarting a child | ||
235 | * expires. | ||
236 | */ | ||
237 | static struct GNUNET_SCHEDULER_Task *child_restart_task; | ||
238 | |||
239 | /** | ||
240 | * Pipe used to communicate shutdown via signal. | ||
241 | */ | ||
242 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
243 | |||
244 | /** | ||
245 | * Are we in shutdown mode? | ||
246 | */ | ||
247 | static int in_shutdown; | ||
248 | |||
249 | /** | ||
250 | * Return value from main | ||
251 | */ | ||
252 | static int global_ret; | ||
253 | |||
254 | /** | ||
255 | * Are we starting user services? | ||
256 | */ | ||
257 | static int start_user = GNUNET_YES; | ||
258 | |||
259 | /** | ||
260 | * Are we starting system services? | ||
261 | */ | ||
262 | static 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 | */ | ||
274 | static struct GNUNET_SERVICE_Handle *service; | ||
275 | |||
276 | /** | ||
277 | * Context for notifications we need to send to our clients. | ||
278 | */ | ||
279 | static 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 | */ | ||
292 | static void | ||
293 | add_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 | */ | ||
341 | static int | ||
342 | get_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 | */ | ||
684 | static void | ||
685 | signal_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 | */ | ||
709 | static void | ||
710 | broadcast_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 | */ | ||
749 | static void | ||
750 | start_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 ("edbinary, "\"%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 ("edbinary, "\"%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 | */ | ||
982 | static struct ServiceList * | ||
983 | find_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 | */ | ||
1004 | static void | ||
1005 | accept_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 | */ | ||
1024 | static void | ||
1025 | create_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 | */ | ||
1147 | static void | ||
1148 | free_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 | */ | ||
1168 | static int | ||
1169 | check_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 | */ | ||
1183 | static void | ||
1184 | handle_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 | */ | ||
1229 | static void | ||
1230 | trigger_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 | */ | ||
1246 | static int | ||
1247 | check_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 | */ | ||
1261 | static void | ||
1262 | handle_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 | */ | ||
1344 | static int | ||
1345 | pool_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 | */ | ||
1363 | static void | ||
1364 | handle_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 | */ | ||
1450 | static void | ||
1451 | handle_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 | */ | ||
1468 | static void | ||
1469 | do_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 | */ | ||
1496 | static unsigned int | ||
1497 | list_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 | */ | ||
1513 | static void | ||
1514 | shutdown_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 | */ | ||
1577 | static void | ||
1578 | delayed_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 | */ | ||
1654 | static void | ||
1655 | maint_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 | */ | ||
1869 | static void | ||
1870 | sighandler_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 | */ | ||
1893 | static void | ||
1894 | setup_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 | */ | ||
2003 | static void * | ||
2004 | client_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 | */ | ||
2025 | static void | ||
2026 | client_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 | */ | ||
2046 | static void | ||
2047 | handle_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 | */ | ||
2069 | static void | ||
2070 | run (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 | */ | ||
2145 | int | ||
2146 | main (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 | */ | ||
2215 | void __attribute__ ((constructor)) | ||
2216 | GNUNET_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 @@ | |||
1 | libgnunetarm_src = ['arm_api.c', | ||
2 | 'arm_monitor_api.c'] | ||
3 | |||
4 | gnunetservicearm_src = ['gnunet-service-arm.c'] | ||
5 | |||
6 | gnunetarm_src = ['gnunet-arm.c'] | ||
7 | |||
8 | testarmapi_src = ['test_arm_api.c'] | ||
9 | testexpbo_src = ['test_exponential_backoff.c'] | ||
10 | testgnunetservice_src = ['test_gnunet_service_arm.c'] | ||
11 | |||
12 | configure_file(input : 'arm.conf.in', | ||
13 | output : 'arm.conf', | ||
14 | configuration : cdata, | ||
15 | install: true, | ||
16 | install_dir: pkgcfgdir) | ||
17 | |||
18 | if get_option('monolith') | ||
19 | foreach p : libgnunetarm_src + gnunetservicearm_src | ||
20 | gnunet_src += 'arm/' + p | ||
21 | endforeach | ||
22 | subdir_done() | ||
23 | endif | ||
24 | |||
25 | libgnunetarm = 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')) | ||
33 | pkg.generate(libgnunetarm, url: 'https://www.gnunet.org', | ||
34 | description : 'Provides API for accessing the Automated Restart Manager service') | ||
35 | libgnunetarm_dep = declare_dependency(link_with : libgnunetarm) | ||
36 | executable ('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 | |||
43 | executable ('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 | |||
50 | mockservice = executable ('mockup-service', | ||
51 | ['mockup-service.c'], | ||
52 | dependencies: [libgnunetarm_dep, libgnunetutil_dep], | ||
53 | include_directories: [incdir, configuration_inc], | ||
54 | install: false) | ||
55 | |||
56 | testarmapi = executable ('test_arm_api', | ||
57 | testarmapi_src, | ||
58 | dependencies: [libgnunetarm_dep, libgnunetutil_dep], | ||
59 | include_directories: [incdir, configuration_inc], | ||
60 | install: false) | ||
61 | testexpbo = executable ('test_exponential_backoff', | ||
62 | testexpbo_src, | ||
63 | dependencies: [libgnunetarm_dep, libgnunetutil_dep], | ||
64 | include_directories: [incdir, configuration_inc], | ||
65 | install: false) | ||
66 | testgnunetservice = executable ('test_gnunet_service', | ||
67 | testgnunetservice_src, | ||
68 | dependencies: [libgnunetarm_dep, libgnunetutil_dep], | ||
69 | include_directories: [incdir, configuration_inc], | ||
70 | install: false) | ||
71 | configure_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 | |||
80 | test('test_arm_api', testarmapi, workdir: meson.current_build_dir(), | ||
81 | suite: 'arm') | ||
82 | test('test_exponential_backoff', testexpbo, workdir: meson.current_build_dir(), | ||
83 | depends: mockservice, | ||
84 | timeout: 60, suite: 'arm') | ||
85 | test('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 | |||
27 | static 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 | */ | ||
35 | static void | ||
36 | handle_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 | */ | ||
60 | static void * | ||
61 | client_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 | */ | ||
78 | static void | ||
79 | client_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 | |||
88 | static void | ||
89 | run (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 | */ | ||
103 | GNUNET_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 | */ | ||
119 | void __attribute__ ((destructor)) | ||
120 | GNUNET_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. | ||
14 | Xsed='/usr/bin/sed -e 1s/^X//' | ||
15 | sed_quote_subst='s/\([`"$\\]\)/\\\1/g' | ||
16 | |||
17 | # Be Bourne compatible | ||
18 | if 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 | ||
25 | else | ||
26 | case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac | ||
27 | fi | ||
28 | BIN_SH=xpg4; export BIN_SH # for Tru64 | ||
29 | DUALCASE=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 | |||
35 | relink_command="" | ||
36 | |||
37 | # This environment variable determines our operation mode. | ||
38 | if 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' | ||
42 | else | ||
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 | ||
130 | fi | ||
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 | |||
33 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
34 | |||
35 | static struct GNUNET_ARM_Handle *arm; | ||
36 | |||
37 | static struct GNUNET_ARM_Operation *op; | ||
38 | |||
39 | static int ok = 1; | ||
40 | |||
41 | static int phase = 0; | ||
42 | |||
43 | |||
44 | static void | ||
45 | arm_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 | |||
63 | static void | ||
64 | resolver_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 | |||
87 | static void | ||
88 | dns_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 | |||
116 | static void | ||
117 | resolver_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 | |||
139 | static void | ||
140 | arm_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 | |||
177 | static void | ||
178 | arm_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 | |||
197 | static void | ||
198 | do_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 | |||
213 | static void | ||
214 | task (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 | |||
235 | int | ||
236 | main (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] | ||
5 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-arm/ | ||
6 | |||
7 | [arm] | ||
8 | BINARY = gnunet-service-arm | ||
9 | OPTIONS = -L ERROR | ||
10 | #PREFIX = valgrind --tool=memcheck --leak-check=yes | ||
11 | |||
12 | [resolver] | ||
13 | START_ON_DEMAND = YES | ||
14 | PORT = 23355 | ||
15 | # PREFIX = valgrind | ||
16 | |||
17 | [do-nothing] | ||
18 | START_ON_DEMAND = NO | ||
19 | PORT = 48223 | ||
20 | HOSTNAME = localhost | ||
21 | BINARY = /will/be/overwritten/by/test_exponential_backoff | ||
22 | ACCEPT_FROM = 127.0.0.1; | ||
23 | ACCEPT_FROM6 = ::1; | ||
24 | |||
25 | [statistics] | ||
26 | START_ON_DEMAND = YES | ||
27 | |||
28 | [core] | ||
29 | START_ON_DEMAND = NO | ||
30 | |||
31 | [transport] | ||
32 | START_ON_DEMAND = NO | ||
33 | |||
34 | [ats] | ||
35 | START_ON_DEMAND = NO | ||
36 | |||
37 | [peerinfo] | ||
38 | START_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 | |||
48 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
49 | |||
50 | static struct GNUNET_ARM_Handle *arm; | ||
51 | |||
52 | static struct GNUNET_ARM_MonitorHandle *mon; | ||
53 | |||
54 | static struct GNUNET_SCHEDULER_Task *kt; | ||
55 | |||
56 | static int ok = 1; | ||
57 | |||
58 | static int phase = 0; | ||
59 | |||
60 | static int trialCount; | ||
61 | |||
62 | static struct GNUNET_TIME_Absolute startedWaitingAt; | ||
63 | |||
64 | struct GNUNET_TIME_Relative waitedFor; | ||
65 | |||
66 | struct GNUNET_TIME_Relative waitedFor_prev; | ||
67 | |||
68 | #if LOG_BACKOFF | ||
69 | static FILE *killLogFilePtr; | ||
70 | |||
71 | static char *killLogFileName; | ||
72 | #endif | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Context for handling the shutdown of a service. | ||
77 | */ | ||
78 | struct 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 | |||
92 | static void | ||
93 | kill_task (void *cbData); | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Shutting down took too long, cancel receive and return error. | ||
98 | * | ||
99 | * @param cls closure | ||
100 | */ | ||
101 | static void | ||
102 | service_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 | */ | ||
116 | static void | ||
117 | mq_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 | |||
130 | static void | ||
131 | kill_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 | |||
173 | static void | ||
174 | trigger_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 | |||
186 | static void | ||
187 | arm_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 | |||
198 | static void | ||
199 | srv_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 | |||
259 | static void | ||
260 | arm_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 | |||
273 | static void | ||
274 | task (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 | |||
306 | static int | ||
307 | check () | ||
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 | |||
339 | static int | ||
340 | init () | ||
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 | |||
388 | static void | ||
389 | houseKeep () | ||
390 | { | ||
391 | #if LOG_BACKOFF | ||
392 | GNUNET_assert (0 == fclose (killLogFilePtr)); | ||
393 | GNUNET_free (killLogFileName); | ||
394 | #endif | ||
395 | (void) unlink (CFGFILENAME); | ||
396 | } | ||
397 | |||
398 | |||
399 | int | ||
400 | main (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 | |||
3 | import os | ||
4 | import sys | ||
5 | import shutil | ||
6 | import re | ||
7 | import subprocess | ||
8 | import time | ||
9 | |||
10 | # FIXME: There's too much repetition, move generally used parts into reusable modules. | ||
11 | if os.name == "nt": | ||
12 | tmp = os.getenv("TEMP") | ||
13 | else: | ||
14 | tmp = "/tmp" | ||
15 | |||
16 | if os.name == 'nt': | ||
17 | st = 'gnunet-statistics.exe' | ||
18 | arm = './gnunet-arm.exe' | ||
19 | else: | ||
20 | st = 'gnunet-statistics' | ||
21 | arm = './gnunet-arm' | ||
22 | |||
23 | run_arm = [arm, '-c', 'test_arm_api_data.conf', '--no-stdout', '--no-stderr'] | ||
24 | debug = os.getenv('DEBUG') | ||
25 | if debug: | ||
26 | run_arm += [debug.split(' ')] | ||
27 | |||
28 | |||
29 | def cleanup(): | ||
30 | shutil.rmtree(os.path.join(tmp, "test-gnunetd-arm"), True) | ||
31 | |||
32 | |||
33 | def 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 | |||
50 | def fail(result): | ||
51 | print(result) | ||
52 | r_arm(['-e'], want_stdo=False) | ||
53 | sys.exit(1) | ||
54 | |||
55 | |||
56 | def 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 | |||
72 | def 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 | |||
90 | def 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 | |||
99 | def r_arm(extra_args, **kw): | ||
100 | return r_something(run_arm, extra_args, **kw) | ||
101 | |||
102 | |||
103 | cleanup() | ||
104 | |||
105 | print("TEST: Bad argument checking...", end='') | ||
106 | r_arm(['-x'], normal=False, failure=print_only_failure) | ||
107 | print("PASS") | ||
108 | |||
109 | print("TEST: Start ARM...", end='') | ||
110 | r_arm(['-s'], failure=print_only_failure) | ||
111 | time.sleep(1) | ||
112 | print("PASS") | ||
113 | |||
114 | print("TEST: Start another service...", end='') | ||
115 | r_arm(['-i', 'resolver'], failure=end_arm_failure) | ||
116 | time.sleep(1) | ||
117 | print("PASS") | ||
118 | |||
119 | print("TEST: Stop a service...", end='') | ||
120 | r_arm(['-k', 'resolver'], failure=end_arm_failure) | ||
121 | time.sleep(1) | ||
122 | print("PASS") | ||
123 | |||
124 | print("TEST: Stop ARM...", end='') | ||
125 | r_arm(['-e'], failure=print_only_failure) | ||
126 | time.sleep(1) | ||
127 | print("PASS") | ||
128 | |||
129 | cleanup() | ||
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 | |||
42 | static int ret = 1; | ||
43 | |||
44 | static int resolved_ok; | ||
45 | |||
46 | static int asked_for_a_list; | ||
47 | |||
48 | static struct GNUNET_ARM_Handle *arm; | ||
49 | |||
50 | static const char hostname[] = "www.gnu.org"; /* any domain should do */ | ||
51 | |||
52 | |||
53 | static void | ||
54 | trigger_disconnect (void *cls) | ||
55 | { | ||
56 | GNUNET_ARM_disconnect (arm); | ||
57 | arm = NULL; | ||
58 | } | ||
59 | |||
60 | |||
61 | static void | ||
62 | arm_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 | |||
77 | static void | ||
78 | service_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 | |||
101 | stop_arm: | ||
102 | GNUNET_ARM_request_service_stop (arm, | ||
103 | "arm", | ||
104 | &arm_stop_cb, | ||
105 | NULL); | ||
106 | } | ||
107 | |||
108 | |||
109 | static void | ||
110 | hostname_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 | |||
141 | static void | ||
142 | arm_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 | |||
170 | static void | ||
171 | run (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 | |||
187 | int | ||
188 | main (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 @@ | |||
1 | gnunet-service-core | ||
2 | core.conf | ||
3 | test_core_api | ||
4 | test_core_api_reliability | ||
5 | test_core_api_send_to_self | ||
6 | test_core_api_start_only | ||
7 | test_core_quota_compliance_asymmetric_recv_limited | ||
8 | test_core_quota_compliance_asymmetric_send_limited | ||
9 | test_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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
5 | |||
6 | plugindir = $(libdir)/gnunet | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | pkgcfg_DATA = \ | ||
11 | core.conf | ||
12 | |||
13 | if USE_COVERAGE | ||
14 | AM_CFLAGS = --coverage -O0 | ||
15 | XLIB = -lgcov | ||
16 | endif | ||
17 | |||
18 | plugin_LTLIBRARIES = \ | ||
19 | libgnunet_test_core_plugin_cmd_just_run.la | ||
20 | |||
21 | TESTING_LIBS = \ | ||
22 | libgnunetcoretesting.la | ||
23 | |||
24 | lib_LTLIBRARIES = \ | ||
25 | libgnunetcore.la \ | ||
26 | $(TESTING_LIBS) | ||
27 | |||
28 | libgnunetcore_la_SOURCES = \ | ||
29 | core_api.c core.h \ | ||
30 | core_api_monitor_peers.c | ||
31 | libgnunetcore_la_LIBADD = \ | ||
32 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
33 | $(GN_LIBINTL) $(XLIB) | ||
34 | libgnunetcore_la_LDFLAGS = \ | ||
35 | $(GN_LIB_LDFLAGS) \ | ||
36 | -version-info 0:1:0 | ||
37 | |||
38 | libgnunet_test_core_plugin_cmd_just_run_la_SOURCES = \ | ||
39 | test_core_plugin_cmd_just_run.c | ||
40 | libgnunet_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) | ||
51 | libgnunet_test_core_plugin_cmd_just_run_la_LDFLAGS = \ | ||
52 | $(GN_PLUGIN_LDFLAGS) | ||
53 | |||
54 | libgnunetcoretesting_la_SOURCES = \ | ||
55 | core_api_cmd_connecting_peers.c | ||
56 | libgnunetcoretesting_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 | ||
62 | libgnunetcoretesting_la_LDFLAGS = \ | ||
63 | $(GN_LIBINTL) \ | ||
64 | $(GN_LIB_LDFLAGS) \ | ||
65 | -version-info 0:0:0 | ||
66 | |||
67 | |||
68 | libexec_PROGRAMS = \ | ||
69 | gnunet-service-core | ||
70 | |||
71 | gnunet_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 | ||
76 | gnunet_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 | |||
84 | TESTING_TESTS = \ | ||
85 | test_core_api_send_to_self | ||
86 | |||
87 | check_PROGRAMS = \ | ||
88 | test_core_api_start_only \ | ||
89 | $(TESTING_TESTS) | ||
90 | |||
91 | # Only test TNG if we run experimental | ||
92 | check_SCRIPTS= \ | ||
93 | test_core_start_testcase.sh | ||
94 | |||
95 | if ENABLE_TEST_RUN | ||
96 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
97 | TESTS = $(check_PROGRAMS) \ | ||
98 | $(check_SCRIPTS) | ||
99 | endif | ||
100 | |||
101 | test_core_api_send_to_self_SOURCES = \ | ||
102 | test_core_api_send_to_self.c | ||
103 | test_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 | |||
108 | test_core_api_start_only_SOURCES = \ | ||
109 | test_core_api_start_only.c | ||
110 | test_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 | |||
115 | EXTRA_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] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | @JAVAPORT@PORT = 2092 | ||
4 | HOSTNAME = localhost | ||
5 | BINARY = gnunet-service-core | ||
6 | ACCEPT_FROM = 127.0.0.1; | ||
7 | ACCEPT_FROM6 = ::1; | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-core.sock | ||
9 | UNIX_MATCH_UID = NO | ||
10 | UNIX_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!). | ||
23 | USE_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 | |||
73 | GNUNET_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 | */ | ||
81 | struct 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 | */ | ||
99 | struct 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 | */ | ||
122 | struct 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 | */ | ||
145 | struct 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 | */ | ||
173 | struct 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 | */ | ||
194 | struct 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 | */ | ||
239 | struct 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 | */ | ||
270 | struct 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 | */ | ||
300 | struct 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 | |||
324 | GNUNET_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 | */ | ||
38 | struct 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 | */ | ||
77 | struct 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 | */ | ||
153 | static void | ||
154 | reconnect (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 | */ | ||
162 | static void | ||
163 | reconnect_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 | */ | ||
182 | static int | ||
183 | disconnect_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 | */ | ||
213 | static void | ||
214 | reconnect_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 | */ | ||
239 | static void | ||
240 | handle_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 | */ | ||
257 | static void | ||
258 | core_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 | */ | ||
319 | static void | ||
320 | core_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 | */ | ||
336 | static void | ||
337 | core_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 | */ | ||
356 | static void | ||
357 | core_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 | */ | ||
374 | static void | ||
375 | connect_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 | */ | ||
412 | static void | ||
413 | handle_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 | */ | ||
459 | static void | ||
460 | handle_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 | */ | ||
492 | static void | ||
493 | handle_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 | */ | ||
526 | static int | ||
527 | check_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 | */ | ||
556 | static void | ||
557 | handle_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 | */ | ||
586 | static void | ||
587 | handle_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 | */ | ||
628 | static void | ||
629 | reconnect (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 | */ | ||
690 | struct GNUNET_CORE_Handle * | ||
691 | GNUNET_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 | */ | ||
728 | void | ||
729 | GNUNET_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 | */ | ||
759 | struct GNUNET_MQ_Handle * | ||
760 | GNUNET_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 | */ | ||
44 | static void | ||
45 | connect_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 | */ | ||
181 | static void | ||
182 | connect_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 | */ | ||
195 | enum GNUNET_GenericReturnValue | ||
196 | connect_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 | |||
213 | struct GNUNET_TESTING_Command | ||
214 | GNUNET_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 | */ | ||
35 | struct 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 | */ | ||
65 | static void | ||
66 | reconnect (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 | */ | ||
77 | static void | ||
78 | handle_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 | */ | ||
93 | static void | ||
94 | handle_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 | */ | ||
111 | static void | ||
112 | reconnect (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 | */ | ||
157 | struct GNUNET_CORE_MonitorHandle * | ||
158 | GNUNET_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 | */ | ||
184 | void | ||
185 | GNUNET_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 | */ | ||
54 | struct 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 | */ | ||
117 | struct GNUNET_PeerIdentity GSC_my_identity; | ||
118 | |||
119 | /** | ||
120 | * Our configuration. | ||
121 | */ | ||
122 | const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; | ||
123 | |||
124 | /** | ||
125 | * For creating statistics. | ||
126 | */ | ||
127 | struct GNUNET_STATISTICS_Handle *GSC_stats; | ||
128 | |||
129 | /** | ||
130 | * Big "or" of all client options. | ||
131 | */ | ||
132 | static uint32_t all_client_options; | ||
133 | |||
134 | /** | ||
135 | * Head of linked list of our clients. | ||
136 | */ | ||
137 | static struct GSC_Client *client_head; | ||
138 | |||
139 | /** | ||
140 | * Tail of linked list of our clients. | ||
141 | */ | ||
142 | static 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 | */ | ||
152 | static int | ||
153 | type_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 | */ | ||
174 | static int | ||
175 | check_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 | */ | ||
187 | static void | ||
188 | handle_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 | */ | ||
239 | void | ||
240 | GSC_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 | */ | ||
261 | void | ||
262 | GSC_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 | */ | ||
306 | static void | ||
307 | handle_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 | */ | ||
384 | struct 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 | */ | ||
410 | static int | ||
411 | tokenized_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 | */ | ||
473 | static int | ||
474 | check_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 | */ | ||
486 | static void | ||
487 | handle_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 | */ | ||
550 | static int | ||
551 | destroy_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 | */ | ||
576 | static void * | ||
577 | client_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 | */ | ||
599 | static void | ||
600 | client_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 | */ | ||
643 | void | ||
644 | GSC_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 | */ | ||
724 | void | ||
725 | GSC_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 | */ | ||
751 | void | ||
752 | GSC_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 | */ | ||
862 | static void | ||
863 | shutdown_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 | */ | ||
891 | static void | ||
892 | handle_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 | */ | ||
909 | static void | ||
910 | run (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 | */ | ||
962 | GNUNET_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 | */ | ||
38 | struct 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 | */ | ||
46 | struct 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 | */ | ||
109 | void | ||
110 | GSC_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 | */ | ||
125 | void | ||
126 | GSC_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 | */ | ||
140 | void | ||
141 | GSC_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 | */ | ||
159 | void | ||
160 | GSC_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 | */ | ||
175 | void | ||
176 | GSC_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 | */ | ||
185 | extern const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; | ||
186 | |||
187 | /** | ||
188 | * For creating statistics. | ||
189 | */ | ||
190 | extern struct GNUNET_STATISTICS_Handle *GSC_stats; | ||
191 | |||
192 | /** | ||
193 | * Our identity. | ||
194 | */ | ||
195 | extern 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 | |||
75 | GNUNET_NETWORK_STRUCT_BEGIN | ||
76 | |||
77 | /** | ||
78 | * Encapsulation for encrypted messages exchanged between | ||
79 | * peers. Followed by the actual encrypted data. | ||
80 | */ | ||
81 | struct 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 | }; | ||
119 | GNUNET_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 | */ | ||
133 | struct 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 | */ | ||
249 | static struct GNUNET_TRANSPORT_CoreHandle *transport; | ||
250 | |||
251 | /** | ||
252 | * Our private key. | ||
253 | */ | ||
254 | static struct GNUNET_CRYPTO_EddsaPrivateKey my_private_key; | ||
255 | |||
256 | /** | ||
257 | * Our ephemeral private key. | ||
258 | */ | ||
259 | static struct GNUNET_CRYPTO_EcdhePrivateKey my_ephemeral_key; | ||
260 | |||
261 | /** | ||
262 | * Current message we send for a key exchange. | ||
263 | */ | ||
264 | static struct EphemeralKeyMessage current_ekm; | ||
265 | |||
266 | /** | ||
267 | * DLL head. | ||
268 | */ | ||
269 | static struct GSC_KeyExchangeInfo *kx_head; | ||
270 | |||
271 | /** | ||
272 | * DLL tail. | ||
273 | */ | ||
274 | static struct GSC_KeyExchangeInfo *kx_tail; | ||
275 | |||
276 | /** | ||
277 | * Task scheduled for periodic re-generation (and thus rekeying) of our | ||
278 | * ephemeral key. | ||
279 | */ | ||
280 | static struct GNUNET_SCHEDULER_Task *rekey_task; | ||
281 | |||
282 | /** | ||
283 | * Notification context for broadcasting to monitors. | ||
284 | */ | ||
285 | static 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 | */ | ||
293 | static uint32_t | ||
294 | calculate_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 | */ | ||
309 | static void | ||
310 | monitor_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 | */ | ||
331 | static void | ||
332 | derive_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 | */ | ||
368 | static void | ||
369 | derive_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 | */ | ||
407 | static void | ||
408 | derive_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 | */ | ||
449 | static void | ||
450 | derive_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 | */ | ||
493 | static int | ||
494 | do_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 | */ | ||
540 | static int | ||
541 | do_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 | */ | ||
591 | static void | ||
592 | send_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 | */ | ||
600 | static void | ||
601 | set_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 | */ | ||
618 | static void | ||
619 | setup_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 | */ | ||
654 | static int | ||
655 | deliver_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 | */ | ||
705 | static void * | ||
706 | handle_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 | */ | ||
762 | static void | ||
763 | handle_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 | */ | ||
800 | static void | ||
801 | send_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 | */ | ||
819 | static void | ||
820 | derive_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 | */ | ||
849 | static void | ||
850 | handle_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 | */ | ||
1061 | static void | ||
1062 | handle_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 | */ | ||
1147 | static void | ||
1148 | send_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 | */ | ||
1191 | static void | ||
1192 | update_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 | */ | ||
1221 | static void | ||
1222 | handle_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 | */ | ||
1372 | static void | ||
1373 | send_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 (¤t_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 (¤t_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 | |||
1410 | void | ||
1411 | GSC_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 | */ | ||
1483 | static int | ||
1484 | check_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 | */ | ||
1504 | static void | ||
1505 | handle_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 | */ | ||
1708 | static void | ||
1709 | sign_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 | ¤t_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 | ¤t_ekm.purpose, | ||
1743 | ¤t_ekm.signature)); | ||
1744 | } | ||
1745 | |||
1746 | |||
1747 | /** | ||
1748 | * Task run to trigger rekeying. | ||
1749 | * | ||
1750 | * @param cls closure, NULL | ||
1751 | */ | ||
1752 | static void | ||
1753 | do_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 (¤t_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 | */ | ||
1793 | int | ||
1794 | GSC_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 (¤t_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 | */ | ||
1853 | void | ||
1854 | GSC_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 | */ | ||
1886 | unsigned int | ||
1887 | GSC_NEIGHBOURS_get_queue_length (const struct GSC_KeyExchangeInfo *kxinfo) | ||
1888 | { | ||
1889 | return GNUNET_MQ_get_length (kxinfo->mq); | ||
1890 | } | ||
1891 | |||
1892 | |||
1893 | int | ||
1894 | GSC_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 | */ | ||
1908 | void | ||
1909 | GSC_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 | */ | ||
35 | struct 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 | */ | ||
45 | void | ||
46 | GSC_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 | */ | ||
57 | int | ||
58 | GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk); | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Shutdown KX subsystem. | ||
63 | */ | ||
64 | void | ||
65 | GSC_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 | */ | ||
74 | int | ||
75 | GSC_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 | */ | ||
85 | unsigned int | ||
86 | GSC_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 | */ | ||
97 | void | ||
98 | GSC_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 | */ | ||
46 | struct 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 | */ | ||
92 | struct 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 | |||
156 | GNUNET_NETWORK_STRUCT_BEGIN | ||
157 | |||
158 | /** | ||
159 | * Message sent to confirm that a typemap was received. | ||
160 | */ | ||
161 | struct 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 | |||
179 | GNUNET_NETWORK_STRUCT_END | ||
180 | |||
181 | |||
182 | /** | ||
183 | * Map of peer identities to `struct Session`. | ||
184 | */ | ||
185 | static 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 | */ | ||
195 | static struct Session * | ||
196 | find_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 | */ | ||
210 | void | ||
211 | GSC_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 | */ | ||
267 | static void | ||
268 | transmit_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 | */ | ||
299 | static void | ||
300 | start_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 | */ | ||
317 | void | ||
318 | GSC_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 | */ | ||
352 | void | ||
353 | GSC_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 | */ | ||
375 | void | ||
376 | GSC_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 | */ | ||
432 | static int | ||
433 | notify_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 | */ | ||
453 | void | ||
454 | GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client) | ||
455 | { | ||
456 | /* notify new client about existing sessions */ | ||
457 | GNUNET_CONTAINER_multipeermap_iterate (sessions, | ||
458 | ¬ify_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 | */ | ||
469 | static void | ||
470 | try_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 | */ | ||
482 | void | ||
483 | GSC_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 | */ | ||
517 | void | ||
518 | GSC_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 | */ | ||
545 | static void | ||
546 | solicit_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 | */ | ||
592 | static void | ||
593 | pop_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 | */ | ||
609 | static void | ||
610 | try_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 | */ | ||
784 | static int | ||
785 | do_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 | */ | ||
825 | void | ||
826 | GSC_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 | */ | ||
843 | void | ||
844 | GSC_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 | |||
858 | void | ||
859 | GSC_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 | |||
899 | void | ||
900 | GSC_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 | */ | ||
960 | void | ||
961 | GSC_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 | */ | ||
983 | void | ||
984 | GSC_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 | */ | ||
999 | static int | ||
1000 | free_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 | */ | ||
1014 | void | ||
1015 | GSC_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 | */ | ||
39 | void | ||
40 | GSC_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 | */ | ||
51 | void | ||
52 | GSC_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 | */ | ||
62 | void | ||
63 | GSC_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 | */ | ||
73 | void | ||
74 | GSC_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 | */ | ||
84 | void | ||
85 | GSC_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 | */ | ||
97 | void | ||
98 | GSC_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 | */ | ||
107 | void | ||
108 | GSC_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 | */ | ||
119 | void | ||
120 | GSC_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 | */ | ||
131 | void | ||
132 | GSC_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 | */ | ||
140 | void | ||
141 | GSC_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 | */ | ||
151 | void | ||
152 | GSC_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 | */ | ||
164 | void | ||
165 | GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer, | ||
166 | uint16_t type); | ||
167 | |||
168 | |||
169 | /** | ||
170 | * Initialize sessions subsystem. | ||
171 | */ | ||
172 | void | ||
173 | GSC_SESSIONS_init (void); | ||
174 | |||
175 | |||
176 | /** | ||
177 | * Shutdown sessions subsystem. | ||
178 | */ | ||
179 | void | ||
180 | GSC_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 | */ | ||
38 | struct 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 | */ | ||
46 | static struct GSC_TypeMap my_type_map; | ||
47 | |||
48 | /** | ||
49 | * Counters for message types this peer is able to handle. | ||
50 | */ | ||
51 | static uint8_t map_counters[UINT16_MAX + 1]; | ||
52 | |||
53 | /** | ||
54 | * Current hash of our (uncompressed) type map. | ||
55 | * Lazily computed when needed. | ||
56 | */ | ||
57 | static struct GNUNET_HashCode my_tm_hash; | ||
58 | |||
59 | /** | ||
60 | * Is #my_tm_hash() current with respect to our type map? | ||
61 | */ | ||
62 | static int hash_current; | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Our type map changed, recompute its hash. | ||
67 | */ | ||
68 | static void | ||
69 | rehash_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 | */ | ||
81 | void | ||
82 | GSC_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 | */ | ||
94 | int | ||
95 | GSC_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 | */ | ||
113 | struct GNUNET_MessageHeader * | ||
114 | GSC_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 | */ | ||
157 | struct GSC_TypeMap * | ||
158 | GSC_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 | */ | ||
210 | static void | ||
211 | broadcast_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 | */ | ||
231 | void | ||
232 | GSC_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 | |||
255 | void | ||
256 | GSC_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 | */ | ||
286 | int | ||
287 | GSC_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 | */ | ||
310 | struct GSC_TypeMap * | ||
311 | GSC_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 | */ | ||
331 | struct GSC_TypeMap * | ||
332 | GSC_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 | */ | ||
343 | void | ||
344 | GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap) | ||
345 | { | ||
346 | GNUNET_free (tmap); | ||
347 | } | ||
348 | |||
349 | |||
350 | /** | ||
351 | * Initialize typemap subsystem. | ||
352 | */ | ||
353 | void | ||
354 | GSC_TYPEMAP_init () | ||
355 | { | ||
356 | /* nothing to do */ | ||
357 | } | ||
358 | |||
359 | |||
360 | /** | ||
361 | * Shutdown typemap subsystem. | ||
362 | */ | ||
363 | void | ||
364 | GSC_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 | */ | ||
34 | struct 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 | */ | ||
43 | void | ||
44 | GSC_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 | */ | ||
54 | void | ||
55 | GSC_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 | */ | ||
64 | struct GNUNET_MessageHeader * | ||
65 | GSC_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 | */ | ||
74 | int | ||
75 | GSC_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 | */ | ||
84 | void | ||
85 | GSC_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 | */ | ||
97 | struct GSC_TypeMap * | ||
98 | GSC_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 | */ | ||
110 | int | ||
111 | GSC_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 | */ | ||
124 | struct GSC_TypeMap * | ||
125 | GSC_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 | */ | ||
135 | struct GSC_TypeMap * | ||
136 | GSC_TYPEMAP_create (void); | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Free the given type map. | ||
141 | * | ||
142 | * @param tmap a type map | ||
143 | */ | ||
144 | void | ||
145 | GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap); | ||
146 | |||
147 | |||
148 | /** | ||
149 | * Initialize typemap subsystem. | ||
150 | */ | ||
151 | void | ||
152 | GSC_TYPEMAP_init (void); | ||
153 | |||
154 | |||
155 | /** | ||
156 | * Shutdown typemap subsystem. | ||
157 | */ | ||
158 | void | ||
159 | GSC_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 @@ | |||
1 | libgnunetcore_src = ['core_api.c', | ||
2 | 'core_api_monitor_peers.c'] | ||
3 | |||
4 | gnunetservicecore_src = ['gnunet-service-core.c', | ||
5 | 'gnunet-service-core_kx.c', | ||
6 | 'gnunet-service-core_sessions.c', | ||
7 | 'gnunet-service-core_typemap.c'] | ||
8 | |||
9 | configure_file(input : 'core.conf.in', | ||
10 | output : 'core.conf', | ||
11 | configuration : cdata, | ||
12 | install: true, | ||
13 | install_dir: pkgcfgdir) | ||
14 | |||
15 | |||
16 | if get_option('monolith') | ||
17 | foreach p : libgnunetcore_src + gnunetservicecore_src | ||
18 | gnunet_src += 'core/' + p | ||
19 | endforeach | ||
20 | subdir_done() | ||
21 | endif | ||
22 | |||
23 | libgnunetcore = 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')) | ||
31 | libgnunetcore_dep = declare_dependency(link_with : libgnunetcore) | ||
32 | pkg.generate(libgnunetcore, url: 'https://www.gnunet.org', | ||
33 | description : 'Provides API for (encrypted) P2P communication') | ||
34 | |||
35 | libgnunetcoretesting = 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')) | ||
49 | libgnunetcoretesting_dep = declare_dependency(link_with : libgnunetcoretesting) | ||
50 | |||
51 | shared_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 | |||
67 | executable ('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 | |||
74 | executable ('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 | |||
84 | configure_file(input : 'test_core_defaults.conf', | ||
85 | output : 'test_core_defaults.conf', | ||
86 | copy: true) | ||
87 | |||
88 | configure_file(input : 'test_core_api_send_to_self.conf', | ||
89 | output : 'test_core_api_send_to_self.conf', | ||
90 | copy: true) | ||
91 | |||
92 | configure_file(input : 'test_core_api_peer1.conf', | ||
93 | output : 'test_core_api_peer1.conf', | ||
94 | copy: true) | ||
95 | |||
96 | testcore_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 | |||
106 | testcore_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 | |||
116 | test('test_core_api_send_to_self', testcore_api_send_self, | ||
117 | suite: 'core', workdir: meson.current_build_dir()) | ||
118 | test('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 | |||
35 | struct 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 | |||
49 | static struct PeerContext p1; | ||
50 | |||
51 | static struct PeerContext p2; | ||
52 | |||
53 | static struct GNUNET_SCHEDULER_Task *err_task; | ||
54 | |||
55 | static 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 | |||
69 | static void | ||
70 | offer_hello_done (void *cls) | ||
71 | { | ||
72 | struct PeerContext *p = cls; | ||
73 | |||
74 | p->oh = NULL; | ||
75 | } | ||
76 | |||
77 | |||
78 | static void | ||
79 | process_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 | |||
95 | static void | ||
96 | terminate_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 | |||
126 | static void | ||
127 | terminate_task (void *cls) | ||
128 | { | ||
129 | GNUNET_assert (ok == 6); | ||
130 | terminate_peer (&p1); | ||
131 | terminate_peer (&p2); | ||
132 | ok = 0; | ||
133 | } | ||
134 | |||
135 | |||
136 | static void | ||
137 | terminate_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 | |||
147 | static void * | ||
148 | connect_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 | |||
182 | static void | ||
183 | disconnect_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 | |||
198 | static void | ||
199 | handle_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 | |||
213 | static void | ||
214 | init_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 | |||
247 | static void | ||
248 | setup_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 | |||
275 | static void | ||
276 | run (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 | |||
302 | static void | ||
303 | stop_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 | |||
318 | int | ||
319 | main (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] | ||
5 | WAN_QUOTA_IN = 64 kiB | ||
6 | WAN_QUOTA_OUT = 64 kiB | ||
7 | |||
8 | [core] | ||
9 | PORT = 52092 | ||
10 | UNIXPATH = $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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-1/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 12460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 12461 | ||
11 | |||
12 | [resolver] | ||
13 | PORT = 12462 | ||
14 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock | ||
15 | |||
16 | [peerinfo] | ||
17 | PORT = 12463 | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock | ||
19 | |||
20 | [transport] | ||
21 | PORT = 12464 | ||
22 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock | ||
23 | |||
24 | [core] | ||
25 | PORT = 12475 | ||
26 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-core.sock | ||
27 | |||
28 | [ats] | ||
29 | PORT = 12476 | ||
30 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock | ||
31 | |||
32 | [transport-tcp] | ||
33 | PORT = 12467 | ||
34 | |||
35 | [transport-udp] | ||
36 | PORT = 12468 | ||
37 | |||
38 | [transport-unix] | ||
39 | PORT = 12469 | ||
40 | |||
41 | [transport-http] | ||
42 | PORT = 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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-2/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 22460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 22461 | ||
11 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock | ||
12 | |||
13 | [resolver] | ||
14 | PORT = 22462 | ||
15 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock | ||
16 | |||
17 | [peerinfo] | ||
18 | PORT = 22463 | ||
19 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock | ||
20 | |||
21 | [transport] | ||
22 | PORT = 22464 | ||
23 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock | ||
24 | |||
25 | [core] | ||
26 | PORT = 22475 | ||
27 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-core.sock | ||
28 | |||
29 | [ats] | ||
30 | PORT = 22476 | ||
31 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock | ||
32 | |||
33 | [transport-tcp] | ||
34 | PORT = 22467 | ||
35 | |||
36 | [transport-udp] | ||
37 | PORT = 22468 | ||
38 | |||
39 | [transport-unix] | ||
40 | PORT = 22469 | ||
41 | |||
42 | [transport-http] | ||
43 | PORT = 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 | |||
49 | static unsigned long long total_bytes; | ||
50 | |||
51 | static struct GNUNET_TIME_Absolute start_time; | ||
52 | |||
53 | static struct GNUNET_SCHEDULER_Task *err_task; | ||
54 | |||
55 | |||
56 | struct 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 | |||
71 | static struct PeerContext p1; | ||
72 | |||
73 | static struct PeerContext p2; | ||
74 | |||
75 | static int ok; | ||
76 | |||
77 | static 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 | |||
84 | struct TestMessage | ||
85 | { | ||
86 | struct GNUNET_MessageHeader header; | ||
87 | uint32_t num GNUNET_PACKED; | ||
88 | }; | ||
89 | |||
90 | |||
91 | static unsigned int | ||
92 | get_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 | |||
103 | static void | ||
104 | terminate_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 | |||
134 | static void | ||
135 | terminate_task_error (void *cls) | ||
136 | { | ||
137 | err_task = NULL; | ||
138 | GNUNET_break (0); | ||
139 | GNUNET_SCHEDULER_shutdown (); | ||
140 | ok = 42; | ||
141 | } | ||
142 | |||
143 | |||
144 | static void | ||
145 | do_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 | |||
169 | static void | ||
170 | send_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 | |||
203 | static void * | ||
204 | connect_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 | |||
238 | static void | ||
239 | disconnect_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 | |||
257 | static int | ||
258 | check_test (void *cls, | ||
259 | const struct TestMessage *hdr) | ||
260 | { | ||
261 | return GNUNET_OK; /* accept all */ | ||
262 | } | ||
263 | |||
264 | |||
265 | static void | ||
266 | handle_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 | |||
324 | static void | ||
325 | init_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 | |||
369 | static void | ||
370 | offer_hello_done (void *cls) | ||
371 | { | ||
372 | struct PeerContext *p = cls; | ||
373 | |||
374 | p->oh = NULL; | ||
375 | } | ||
376 | |||
377 | |||
378 | static void | ||
379 | process_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 | |||
413 | static void | ||
414 | setup_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 | |||
443 | static void | ||
444 | run (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 | |||
480 | static void | ||
481 | stop_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 | |||
499 | int | ||
500 | main (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 | */ | ||
37 | static int ret; | ||
38 | |||
39 | /** | ||
40 | * Handle to the cleanup task. | ||
41 | */ | ||
42 | static struct GNUNET_SCHEDULER_Task *die_task; | ||
43 | |||
44 | /** | ||
45 | * Identity of this peer. | ||
46 | */ | ||
47 | static struct GNUNET_PeerIdentity myself; | ||
48 | |||
49 | /** | ||
50 | * The handle to core | ||
51 | */ | ||
52 | static struct GNUNET_CORE_Handle *core; | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Function scheduled as very last function, cleans up after us | ||
57 | */ | ||
58 | static void | ||
59 | cleanup (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 | */ | ||
79 | static void | ||
80 | do_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 | |||
89 | static void | ||
90 | handle_test (void *cls, | ||
91 | const struct GNUNET_MessageHeader *message) | ||
92 | { | ||
93 | GNUNET_SCHEDULER_shutdown (); | ||
94 | ret = 0; | ||
95 | } | ||
96 | |||
97 | |||
98 | static void | ||
99 | init (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 | |||
116 | static void * | ||
117 | connect_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 | */ | ||
148 | static void | ||
149 | run (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 | */ | ||
183 | int | ||
184 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-core-api-send-to-self/ | ||
4 | |||
5 | [ats] | ||
6 | WAN_QUOTA_IN = 104857600 | ||
7 | WAN_QUOTA_OUT = 104757600 | ||
8 | |||
9 | [test-sts] | ||
10 | IMMEDIATE_START = YES | ||
11 | PORT = 59252 | ||
12 | HOSTNAME = localhost | ||
13 | BINARY = test_core_api_send_to_self | ||
14 | ACCEPT_FROM = 127.0.0.1; | ||
15 | ACCEPT_FROM6 = ::1; | ||
16 | TOTAL_QUOTA_IN = 65536 | ||
17 | TOTAL_QUOTA_OUT = 65536 | ||
18 | UNIXPATH = $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 | |||
35 | struct 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 | |||
43 | static struct PeerContext p1; | ||
44 | |||
45 | static struct PeerContext p2; | ||
46 | |||
47 | static struct GNUNET_SCHEDULER_Task *timeout_task_id; | ||
48 | |||
49 | static int ok; | ||
50 | |||
51 | |||
52 | static void * | ||
53 | connect_notify (void *cls, | ||
54 | const struct GNUNET_PeerIdentity *peer, | ||
55 | struct GNUNET_MQ_Handle *mq) | ||
56 | { | ||
57 | return NULL; | ||
58 | } | ||
59 | |||
60 | |||
61 | static void | ||
62 | disconnect_notify (void *cls, | ||
63 | const struct GNUNET_PeerIdentity *peer, | ||
64 | void *internal_cls) | ||
65 | { | ||
66 | } | ||
67 | |||
68 | |||
69 | static struct GNUNET_MQ_MessageHandler handlers[] = { | ||
70 | GNUNET_MQ_handler_end () | ||
71 | }; | ||
72 | |||
73 | |||
74 | static void | ||
75 | shutdown_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 | |||
85 | static void | ||
86 | init_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 | |||
112 | static void | ||
113 | setup_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 | |||
135 | static void | ||
136 | timeout_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 | |||
155 | static void | ||
156 | run (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 | |||
180 | static void | ||
181 | stop_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 | |||
202 | static int | ||
203 | check () | ||
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 | |||
238 | int | ||
239 | main (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] | ||
5 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core/ | ||
6 | |||
7 | [nat] | ||
8 | ENABLE_UPNP = NO | ||
9 | |||
10 | [ats] | ||
11 | WAN_QUOTA_IN = 1 GB | ||
12 | WAN_QUOTA_OUT = 1 GB | ||
13 | |||
14 | [transport-tcp] | ||
15 | BINDTO = 127.0.0.1 | ||
16 | |||
17 | [transport-udp] | ||
18 | BROADCAST = NO | ||
19 | |||
20 | [peerinfo] | ||
21 | NO_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 | |||
49 | static struct GNUNET_TESTING_Command block_script; | ||
50 | |||
51 | static struct GNUNET_TESTING_Command connect_peers; | ||
52 | |||
53 | static struct GNUNET_TESTING_Command local_prepared; | ||
54 | |||
55 | static struct GNUNET_TESTING_Command start_peer; | ||
56 | |||
57 | static struct GNUNET_TESTING_Interpreter *is; | ||
58 | |||
59 | static struct GNUNET_CONTAINER_MultiPeerMap *senders; | ||
60 | |||
61 | struct 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 | |||
92 | struct 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 | |||
111 | struct GNUNET_TESTING_BarrierList* | ||
112 | get_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 | */ | ||
139 | static void | ||
140 | all_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 | */ | ||
151 | static void | ||
152 | handle_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 | * | ||
173 | static void * | ||
174 | notify_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 | */ | ||
195 | static void | ||
196 | all_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 | |||
211 | static void | ||
212 | child_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 | */ | ||
225 | static int | ||
226 | check_encrypted (void *cls, struct GNUNET_MessageHeader *header) | ||
227 | { | ||
228 | return GNUNET_OK; | ||
229 | } | ||
230 | |||
231 | |||
232 | static void | ||
233 | core_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 | ||
248 | handle_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 | */ | ||
260 | static void | ||
261 | handle_encrypted (void *cls, struct GNUNET_MessageHeader *header) | ||
262 | { | ||
263 | struct GNUNET_PeerIdentity *peer = cls; | ||
264 | |||
265 | core_receive_continue (peer); | ||
266 | } | ||
267 | |||
268 | |||
269 | static void | ||
270 | handle_ephemeral_key (void *cls, struct GNUNET_MessageHeader *header) | ||
271 | { | ||
272 | struct GNUNET_PeerIdentity *peer = cls; | ||
273 | |||
274 | core_receive_continue (peer); | ||
275 | } | ||
276 | |||
277 | |||
278 | static void | ||
279 | handle_ping (void *cls, struct GNUNET_MessageHeader *header) | ||
280 | { | ||
281 | struct GNUNET_PeerIdentity *peer = cls; | ||
282 | |||
283 | core_receive_continue (peer); | ||
284 | } | ||
285 | |||
286 | |||
287 | static void | ||
288 | handle_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 | */ | ||
313 | static struct GNUNET_TESTING_Interpreter * | ||
314 | start_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 | */ | ||
492 | void * | ||
493 | libgnunet_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 | */ | ||
514 | void * | ||
515 | libgnunet_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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-recv-lim-peer-1/ | ||
4 | |||
5 | [transport-tcp] | ||
6 | PORT = 12488 | ||
7 | |||
8 | [arm] | ||
9 | PORT = 12486 | ||
10 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-arm.sock | ||
11 | |||
12 | [statistics] | ||
13 | PORT = 12487 | ||
14 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-statistics.sock | ||
15 | |||
16 | [resolver] | ||
17 | PORT = 12484 | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-1-service-resolver.sock | ||
19 | |||
20 | [peerinfo] | ||
21 | PORT = 12489 | ||
22 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-peerinfo.sock | ||
23 | |||
24 | [transport] | ||
25 | PORT = 12485 | ||
26 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-transport.sock | ||
27 | |||
28 | [transport-udp] | ||
29 | PORT = 12489 | ||
30 | |||
31 | [ats] | ||
32 | PORT = 12491 | ||
33 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-ats.sock | ||
34 | # UNSPECIFIED | ||
35 | UNSPECIFIED_QUOTA_IN = 100 MiB | ||
36 | UNSPECIFIED_QUOTA_OUT = 100 MiB | ||
37 | # LOOPBACK | ||
38 | LOOPBACK_QUOTA_IN = 100 MiB | ||
39 | LOOPBACK_QUOTA_OUT = 100 MiB | ||
40 | # LAN | ||
41 | LAN_QUOTA_IN = 100 MiB | ||
42 | LAN_QUOTA_OUT = 100 MiB | ||
43 | # WAN | ||
44 | WAN_QUOTA_IN = 100 MiB | ||
45 | WAN_QUOTA_OUT = 100 MiB | ||
46 | # WLAN | ||
47 | WLAN_QUOTA_IN = 100 MiB | ||
48 | WLAN_QUOTA_OUT = 100 MiB | ||
49 | |||
50 | |||
51 | [core] | ||
52 | PORT = 12490 | ||
53 | UNIXPATH = $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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-recv-lim-peer-2/ | ||
4 | |||
5 | |||
6 | [arm] | ||
7 | PORT = 22486 | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-arm.sock | ||
9 | |||
10 | [statistics] | ||
11 | PORT = 22487 | ||
12 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-statistics.sock | ||
13 | |||
14 | [resolver] | ||
15 | PORT = 22484 | ||
16 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-resolver.sock | ||
17 | |||
18 | [peerinfo] | ||
19 | PORT = 22489 | ||
20 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-peerinfo.sock | ||
21 | |||
22 | [transport] | ||
23 | PORT = 22485 | ||
24 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-transport.sock | ||
25 | |||
26 | [core] | ||
27 | PORT = 22490 | ||
28 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-core.sock | ||
29 | |||
30 | [ats] | ||
31 | PORT = 22491 | ||
32 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-ats.sock | ||
33 | # UNSPECIFIED | ||
34 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
35 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
36 | # LOOPBACK | ||
37 | LOOPBACK_QUOTA_IN = 10 MiB | ||
38 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
39 | # LAN | ||
40 | LAN_QUOTA_IN = 10 MiB | ||
41 | LAN_QUOTA_OUT = 10 MiB | ||
42 | # WAN | ||
43 | WAN_QUOTA_IN = 10 MiB | ||
44 | WAN_QUOTA_OUT = 10 MiB | ||
45 | # WLAN | ||
46 | WLAN_QUOTA_IN = 10 MiB | ||
47 | WLAN_QUOTA_OUT = 10 MiB | ||
48 | |||
49 | [transport-tcp] | ||
50 | PORT = 22467 | ||
51 | |||
52 | [transport-http] | ||
53 | PORT = 22469 | ||
54 | |||
55 | [transport-tcp] | ||
56 | PORT = 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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-send-lim-peer-1/ | ||
4 | |||
5 | [transport-tcp] | ||
6 | PORT = 12488 | ||
7 | |||
8 | [transport-udp] | ||
9 | PORT = 12492 | ||
10 | |||
11 | [arm] | ||
12 | PORT = 12486 | ||
13 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-arm.sock | ||
14 | |||
15 | [statistics] | ||
16 | PORT = 12487 | ||
17 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-statistics.sock | ||
18 | |||
19 | [resolver] | ||
20 | PORT = 12484 | ||
21 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-1-service-resolver.sock | ||
22 | |||
23 | [peerinfo] | ||
24 | PORT = 12489 | ||
25 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-peerinfo.sock | ||
26 | |||
27 | [transport] | ||
28 | PORT = 12485 | ||
29 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-transport.sock | ||
30 | |||
31 | [ats] | ||
32 | PORT = 12491 | ||
33 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-ats.sock | ||
34 | # UNSPECIFIED | ||
35 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
36 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
37 | # LOOPBACK | ||
38 | LOOPBACK_QUOTA_IN = 10 MiB | ||
39 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
40 | # LAN | ||
41 | LAN_QUOTA_IN = 10 MiB | ||
42 | LAN_QUOTA_OUT = 10 MiB | ||
43 | # WAN | ||
44 | WAN_QUOTA_IN = 10 MiB | ||
45 | WAN_QUOTA_OUT = 10 MiB | ||
46 | # WLAN | ||
47 | WLAN_QUOTA_IN = 10 MiB | ||
48 | WLAN_QUOTA_OUT = 10 MiB | ||
49 | |||
50 | [core] | ||
51 | PORT = 12490 | ||
52 | UNIXPATH = $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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-send-lim-peer-2/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 22486 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 22487 | ||
11 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-statistics.sock | ||
12 | |||
13 | [resolver] | ||
14 | PORT = 22484 | ||
15 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-resolver.sock | ||
16 | |||
17 | [peerinfo] | ||
18 | PORT = 22489 | ||
19 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-peerinfo.sock | ||
20 | |||
21 | [transport] | ||
22 | PORT = 22485 | ||
23 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-transport.sock | ||
24 | |||
25 | [core] | ||
26 | PORT = 22490 | ||
27 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-core.sock | ||
28 | |||
29 | [ats] | ||
30 | PORT = 22491 | ||
31 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-ats.sock | ||
32 | WAN_QUOTA_IN = 100 MiB | ||
33 | WAN_QUOTA_OUT = 100 MiB | ||
34 | |||
35 | [ats] | ||
36 | PORT = 12471 | ||
37 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-sym-p1-service-ats.sock | ||
38 | # UNSPECIFIED | ||
39 | UNSPECIFIED_QUOTA_IN = 100 MiB | ||
40 | UNSPECIFIED_QUOTA_OUT = 100 MiB | ||
41 | # LOOPBACK | ||
42 | LOOPBACK_QUOTA_IN = 100 MiB | ||
43 | LOOPBACK_QUOTA_OUT = 100 MiB | ||
44 | # LAN | ||
45 | LAN_QUOTA_IN = 100 MiB | ||
46 | LAN_QUOTA_OUT = 100 MiB | ||
47 | # WAN | ||
48 | WAN_QUOTA_IN = 100 MiB | ||
49 | WAN_QUOTA_OUT = 100 MiB | ||
50 | # WLAN | ||
51 | WLAN_QUOTA_IN = 100 MiB | ||
52 | WLAN_QUOTA_OUT = 100 MiB | ||
53 | |||
54 | [transport-tcp] | ||
55 | PORT = 22467 | ||
56 | |||
57 | [transport-udp] | ||
58 | PORT = 22468 | ||
59 | |||
60 | [transport-http] | ||
61 | PORT = 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 | |||
62 | static unsigned long long total_bytes_sent; | ||
63 | static unsigned long long total_bytes_recv; | ||
64 | |||
65 | static struct GNUNET_TIME_Absolute start_time; | ||
66 | |||
67 | static struct GNUNET_SCHEDULER_Task *err_task; | ||
68 | |||
69 | static struct GNUNET_SCHEDULER_Task *measure_task; | ||
70 | |||
71 | |||
72 | struct 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 | |||
88 | static struct PeerContext p1; | ||
89 | static struct PeerContext p2; | ||
90 | |||
91 | static unsigned long long current_quota_p1_in; | ||
92 | static unsigned long long current_quota_p1_out; | ||
93 | static unsigned long long current_quota_p2_in; | ||
94 | static unsigned long long current_quota_p2_out; | ||
95 | |||
96 | static int ok; | ||
97 | static int test; | ||
98 | static int32_t tr_n; | ||
99 | |||
100 | static 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 | |||
111 | struct TestMessage | ||
112 | { | ||
113 | struct GNUNET_MessageHeader header; | ||
114 | uint32_t num GNUNET_PACKED; | ||
115 | uint8_t pad[MESSAGESIZE]; | ||
116 | }; | ||
117 | |||
118 | |||
119 | static void | ||
120 | terminate_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 | |||
160 | static void | ||
161 | shutdown_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 | |||
178 | static void | ||
179 | terminate_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 | */ | ||
199 | static int | ||
200 | print_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 | |||
220 | static void | ||
221 | measurement_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 | |||
350 | static void | ||
351 | do_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 | |||
374 | static void * | ||
375 | connect_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 | |||
413 | static void | ||
414 | disconnect_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 | |||
437 | static void | ||
438 | handle_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 | |||
467 | static void | ||
468 | init_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 | |||
515 | static void | ||
516 | offer_hello_done (void *cls) | ||
517 | { | ||
518 | struct PeerContext *p = cls; | ||
519 | |||
520 | p->oh = NULL; | ||
521 | } | ||
522 | |||
523 | |||
524 | static void | ||
525 | process_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 | |||
565 | static void | ||
566 | setup_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 | |||
598 | static void | ||
599 | run (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 | ¤t_quota_p1_in)); | ||
648 | GNUNET_assert (GNUNET_SYSERR != | ||
649 | GNUNET_CONFIGURATION_get_value_size (p2.cfg, | ||
650 | "ATS", | ||
651 | "WAN_QUOTA_IN", | ||
652 | ¤t_quota_p2_in)); | ||
653 | GNUNET_assert (GNUNET_SYSERR != | ||
654 | GNUNET_CONFIGURATION_get_value_size (p1.cfg, | ||
655 | "ATS", | ||
656 | "WAN_QUOTA_OUT", | ||
657 | ¤t_quota_p1_out)); | ||
658 | GNUNET_assert (GNUNET_SYSERR != | ||
659 | GNUNET_CONFIGURATION_get_value_size (p2.cfg, | ||
660 | "ATS", | ||
661 | "WAN_QUOTA_OUT", | ||
662 | ¤t_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 | |||
673 | static void | ||
674 | stop_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 | |||
693 | static int | ||
694 | check () | ||
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 | |||
720 | static void | ||
721 | cleanup_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 | |||
755 | int | ||
756 | main (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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-1/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 12460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 12461 | ||
11 | |||
12 | [resolver] | ||
13 | PORT = 12462 | ||
14 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock | ||
15 | |||
16 | [peerinfo] | ||
17 | PORT = 12463 | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock | ||
19 | |||
20 | [transport] | ||
21 | PORT = 12464 | ||
22 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock | ||
23 | |||
24 | [core] | ||
25 | PORT = 12475 | ||
26 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-core.sock | ||
27 | |||
28 | [ats] | ||
29 | PORT = 12476 | ||
30 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock | ||
31 | # UNSPECIFIED | ||
32 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
33 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
34 | # LOOPBACK | ||
35 | LOOPBACK_QUOTA_IN = 10 MiB | ||
36 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
37 | # LAN | ||
38 | LAN_QUOTA_IN = 10 MiB | ||
39 | LAN_QUOTA_OUT = 10 MiB | ||
40 | # WAN | ||
41 | WAN_QUOTA_IN = 10 MiB | ||
42 | WAN_QUOTA_OUT = 10 MiB | ||
43 | # WLAN | ||
44 | WLAN_QUOTA_IN = 10 MiB | ||
45 | WLAN_QUOTA_OUT = 10 MiB | ||
46 | |||
47 | |||
48 | [transport-tcp] | ||
49 | PORT = 12467 | ||
50 | |||
51 | [transport-udp] | ||
52 | PORT = 12468 | ||
53 | |||
54 | [transport-unix] | ||
55 | PORT = 12469 | ||
56 | |||
57 | [transport-http] | ||
58 | PORT = 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] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-2/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 22460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 22461 | ||
11 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock | ||
12 | |||
13 | [resolver] | ||
14 | PORT = 22462 | ||
15 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock | ||
16 | |||
17 | [peerinfo] | ||
18 | PORT = 22463 | ||
19 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock | ||
20 | |||
21 | [transport] | ||
22 | PORT = 22464 | ||
23 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock | ||
24 | |||
25 | [core] | ||
26 | PORT = 22475 | ||
27 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-core.sock | ||
28 | |||
29 | [ats] | ||
30 | PORT = 22476 | ||
31 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock | ||
32 | # UNSPECIFIED | ||
33 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
34 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
35 | # LOOPBACK | ||
36 | LOOPBACK_QUOTA_IN = 10 MiB | ||
37 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
38 | # LAN | ||
39 | LAN_QUOTA_IN = 10 MiB | ||
40 | LAN_QUOTA_OUT = 10 MiB | ||
41 | # WAN | ||
42 | WAN_QUOTA_IN = 10 MiB | ||
43 | WAN_QUOTA_OUT = 10 MiB | ||
44 | # WLAN | ||
45 | WLAN_QUOTA_IN = 10 MiB | ||
46 | WLAN_QUOTA_OUT = 10 MiB | ||
47 | |||
48 | |||
49 | [transport-tcp] | ||
50 | PORT = 22467 | ||
51 | |||
52 | [transport-udp] | ||
53 | PORT = 22468 | ||
54 | |||
55 | [transport-unix] | ||
56 | PORT = 22469 | ||
57 | |||
58 | [transport-http] | ||
59 | PORT = 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 | ||
2 | echo gaga1 > gaga.txt | ||
3 | read -p "Test case configuration to use:" conf | ||
4 | if ! [ -d "/run/netns" ]; then | ||
5 | echo You have to create the directory /run/netns. | ||
6 | fi | ||
7 | if [ -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 | ||
12 | fi | ||
13 | echo gaga2 >> gaga.txt | ||
14 | exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; /usr/local/lib/gnunet/libexec/test_testing_start_with_config $conf" | ||
15 | echo 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 @@ | |||
1 | gnunet-service-identity | ||
2 | gnunet-identity | ||
3 | test_identity | ||
4 | test_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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | if USE_COVERAGE | ||
7 | AM_CFLAGS = --coverage -O0 | ||
8 | XLIB = -lgcov | ||
9 | endif | ||
10 | |||
11 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
12 | |||
13 | libexecdir= $(pkglibdir)/libexec/ | ||
14 | |||
15 | pkgcfg_DATA = \ | ||
16 | identity.conf | ||
17 | |||
18 | lib_LTLIBRARIES = \ | ||
19 | libgnunetidentity.la | ||
20 | |||
21 | |||
22 | libgnunetidentity_la_SOURCES = \ | ||
23 | identity_api.c \ | ||
24 | identity_api_lookup.c \ | ||
25 | identity_api_suffix_lookup.c \ | ||
26 | identity.h | ||
27 | libgnunetidentity_la_LIBADD = \ | ||
28 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
29 | $(GN_LIBINTL) $(XLIB) | ||
30 | libgnunetidentity_la_LDFLAGS = \ | ||
31 | $(GN_LIB_LDFLAGS) \ | ||
32 | -lsodium \ | ||
33 | -version-info 1:0:0 | ||
34 | |||
35 | libexec_PROGRAMS = \ | ||
36 | gnunet-service-identity | ||
37 | |||
38 | gnunet_service_identity_SOURCES = \ | ||
39 | gnunet-service-identity.c | ||
40 | gnunet_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 | |||
47 | check_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 | |||
56 | test_identity_SOURCES = \ | ||
57 | test_identity.c | ||
58 | test_identity_LDADD = \ | ||
59 | libgnunetidentity.la \ | ||
60 | $(top_builddir)/src/lib/testing/libgnunettesting.la \ | ||
61 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
62 | |||
63 | EXTRA_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 | */ | ||
45 | struct 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 | */ | ||
72 | static 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 | */ | ||
78 | static struct GNUNET_CONFIGURATION_Handle *subsystem_cfg; | ||
79 | |||
80 | /** | ||
81 | * Handle to the statistics service. | ||
82 | */ | ||
83 | static struct GNUNET_STATISTICS_Handle *stats; | ||
84 | |||
85 | /** | ||
86 | * Notification context, simplifies client broadcasts. | ||
87 | */ | ||
88 | static struct GNUNET_NotificationContext *nc; | ||
89 | |||
90 | /** | ||
91 | * Directory where we store the identities. | ||
92 | */ | ||
93 | static char *ego_directory; | ||
94 | |||
95 | /** | ||
96 | * Configuration file name where subsystem information is kept. | ||
97 | */ | ||
98 | static char *subsystem_cfg_file; | ||
99 | |||
100 | /** | ||
101 | * Head of DLL of all egos. | ||
102 | */ | ||
103 | static struct Ego *ego_head; | ||
104 | |||
105 | /** | ||
106 | * Tail of DLL of all egos. | ||
107 | */ | ||
108 | static 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 | */ | ||
117 | static char * | ||
118 | get_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 | */ | ||
138 | static void | ||
139 | client_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 | */ | ||
157 | static void * | ||
158 | client_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 | */ | ||
171 | static void | ||
172 | shutdown_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 | */ | ||
209 | static void | ||
210 | send_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 | */ | ||
233 | static struct GNUNET_MQ_Envelope * | ||
234 | create_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 | */ | ||
265 | static void | ||
266 | handle_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 | */ | ||
307 | static int | ||
308 | check_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 | */ | ||
323 | static void | ||
324 | handle_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 | */ | ||
357 | static int | ||
358 | check_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 | */ | ||
373 | static void | ||
374 | handle_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 | */ | ||
417 | static void | ||
418 | notify_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 | */ | ||
448 | static int | ||
449 | check_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 | */ | ||
486 | static void | ||
487 | handle_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 | */ | ||
548 | struct 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 | */ | ||
568 | static void | ||
569 | handle_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 | */ | ||
599 | static int | ||
600 | check_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 | */ | ||
637 | static void | ||
638 | handle_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 | */ | ||
719 | static void | ||
720 | handle_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 | */ | ||
750 | static int | ||
751 | check_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 | */ | ||
782 | static void | ||
783 | handle_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 | |||
829 | static int | ||
830 | read_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 | */ | ||
900 | static int | ||
901 | process_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 | */ | ||
941 | static void | ||
942 | run (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 | */ | ||
1001 | GNUNET_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] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | RUN_PER_USER = YES | ||
4 | @JAVAPORT@PORT = 2108 | ||
5 | HOSTNAME = localhost | ||
6 | BINARY = gnunet-service-identity | ||
7 | ACCEPT_FROM = 127.0.0.1; | ||
8 | ACCEPT_FROM6 = ::1; | ||
9 | UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-identity.sock | ||
10 | UNIX_MATCH_UID = NO | ||
11 | UNIX_MATCH_GID = YES | ||
12 | |||
13 | # Directory where we store information about our egos | ||
14 | EGODIR = $GNUNET_DATA_HOME/identity/egos/ | ||
15 | |||
16 | # File where we store default identities for subsystems | ||
17 | SUBSYSTEM_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 | */ | ||
36 | struct 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 | |||
72 | GNUNET_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 | */ | ||
80 | struct 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 | */ | ||
98 | struct 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 | */ | ||
113 | struct 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 | */ | ||
150 | struct 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 | */ | ||
177 | struct 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 | */ | ||
203 | struct 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 | |||
223 | GNUNET_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 | */ | ||
40 | struct 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 | */ | ||
96 | struct 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 | */ | ||
156 | struct GNUNET_IDENTITY_Ego * | ||
157 | GNUNET_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 | */ | ||
184 | static void | ||
185 | reconnect (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 | */ | ||
196 | static int | ||
197 | free_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 | */ | ||
223 | static void | ||
224 | reschedule_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 | */ | ||
274 | static void | ||
275 | mq_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 | */ | ||
290 | static void | ||
291 | handle_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 | */ | ||
323 | static int | ||
324 | check_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 | */ | ||
347 | static void | ||
348 | handle_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 | */ | ||
439 | static void | ||
440 | reconnect (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 | */ | ||
486 | struct GNUNET_IDENTITY_Handle * | ||
487 | GNUNET_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 | */ | ||
516 | const struct GNUNET_CRYPTO_PrivateKey * | ||
517 | GNUNET_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 | */ | ||
528 | void | ||
529 | GNUNET_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 | |||
540 | static enum GNUNET_GenericReturnValue | ||
541 | private_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 | |||
560 | struct GNUNET_IDENTITY_Operation * | ||
561 | GNUNET_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 | */ | ||
620 | struct GNUNET_IDENTITY_Operation * | ||
621 | GNUNET_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 | */ | ||
673 | struct GNUNET_IDENTITY_Operation * | ||
674 | GNUNET_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 | */ | ||
714 | void | ||
715 | GNUNET_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 | */ | ||
731 | void | ||
732 | GNUNET_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 | */ | ||
37 | struct 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 | */ | ||
69 | static int | ||
70 | check_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 | */ | ||
84 | static void | ||
85 | handle_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 | */ | ||
101 | static int | ||
102 | check_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 | */ | ||
124 | static void | ||
125 | handle_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 | */ | ||
167 | static void | ||
168 | mq_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 | */ | ||
185 | struct GNUNET_IDENTITY_EgoLookup * | ||
186 | GNUNET_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 | */ | ||
235 | void | ||
236 | GNUNET_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 | */ | ||
37 | struct 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 | */ | ||
69 | static int | ||
70 | check_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 | */ | ||
85 | static void | ||
86 | handle_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 | */ | ||
103 | static int | ||
104 | check_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 | */ | ||
127 | static void | ||
128 | handle_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 | */ | ||
164 | static void | ||
165 | mq_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 | */ | ||
184 | struct GNUNET_IDENTITY_EgoSuffixLookup * | ||
185 | GNUNET_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 | */ | ||
236 | void | ||
237 | GNUNET_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 @@ | |||
1 | libgnunetidentity_src = ['identity_api.c', | ||
2 | 'identity_api_lookup.c', | ||
3 | 'identity_api_suffix_lookup.c'] | ||
4 | |||
5 | gnunetserviceidentity_src = ['gnunet-service-identity.c'] | ||
6 | |||
7 | configure_file(input : 'identity.conf.in', | ||
8 | output : 'identity.conf', | ||
9 | configuration : cdata, | ||
10 | install: true, | ||
11 | install_dir: pkgcfgdir) | ||
12 | |||
13 | |||
14 | if get_option('monolith') | ||
15 | foreach p : libgnunetidentity_src + gnunetserviceidentity_src | ||
16 | gnunet_src += 'identity/' + p | ||
17 | endforeach | ||
18 | subdir_done() | ||
19 | endif | ||
20 | |||
21 | libgnunetidentity = 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')) | ||
29 | libgnunetidentity_dep = declare_dependency(link_with : libgnunetidentity) | ||
30 | pkg.generate(libgnunetidentity, url: 'https://www.gnunet.org', | ||
31 | description : 'API to access and organize private keys of the user egos') | ||
32 | shared_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 | |||
44 | executable ('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')) | ||
52 | executable ('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 | */ | ||
38 | static int res; | ||
39 | |||
40 | /** | ||
41 | * Handle to identity service. | ||
42 | */ | ||
43 | static struct GNUNET_IDENTITY_Handle *h; | ||
44 | |||
45 | /** | ||
46 | * Handle to identity operation. | ||
47 | */ | ||
48 | static struct GNUNET_IDENTITY_Operation *op; | ||
49 | |||
50 | /** | ||
51 | * Handle for task for timeout termination. | ||
52 | */ | ||
53 | static 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 | */ | ||
70 | static void | ||
71 | cleanup (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 | */ | ||
92 | static void | ||
93 | endbadly (void *cls) | ||
94 | { | ||
95 | GNUNET_SCHEDULER_shutdown (); | ||
96 | } | ||
97 | |||
98 | |||
99 | /** | ||
100 | * Finish the testcase (successfully). | ||
101 | */ | ||
102 | static void | ||
103 | end () | ||
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 | */ | ||
125 | static void | ||
126 | notification_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 | */ | ||
194 | static void | ||
195 | delete_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 | */ | ||
209 | static void | ||
210 | finally_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 | */ | ||
222 | static void | ||
223 | fail_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 | */ | ||
239 | static void | ||
240 | success_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 | */ | ||
254 | static void | ||
255 | create_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 | */ | ||
289 | static void | ||
290 | run (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, ¬ification_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 | |||
306 | int | ||
307 | main (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] | ||
2 | GNUNET_TEST_HOME = $GNUNET_TMP/test-identity-service/ | ||
3 | |||
4 | [arm] | ||
5 | PORT = 12000 | ||
6 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock | ||
7 | |||
8 | [identity] | ||
9 | # need to overwrite paths to ensure they stay the same between runs... | ||
10 | EGODIR = $GNUNET_TMP/test-identity-service/egos/ | ||
11 | SUBSYSTEM_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 | ||
2 | trap "gnunet-arm -e -c test_identity.conf" SIGINT | ||
3 | |||
4 | LOCATION=$(which gnunet-config) | ||
5 | if [ -z $LOCATION ] | ||
6 | then | ||
7 | LOCATION="gnunet-config" | ||
8 | fi | ||
9 | $LOCATION --version 1> /dev/null | ||
10 | if test $? != 0 | ||
11 | then | ||
12 | echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" | ||
13 | exit 77 | ||
14 | fi | ||
15 | |||
16 | rm -rf `gnunet-config -c test_identity.conf -s PATHS -o GNUNET_HOME -f` | ||
17 | |||
18 | which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 30" | ||
19 | |||
20 | TEST_MSG="This is a test message. 123" | ||
21 | gnunet-arm -s -c test_identity.conf | ||
22 | gnunet-identity -C recipientego -c test_identity.conf | ||
23 | gnunet-identity -C recipientegoed -X -c test_identity.conf | ||
24 | RECIPIENT_KEY=`gnunet-identity -d -e recipientego -q -c test_identity.conf` | ||
25 | MSG_ENC=`gnunet-identity -W "$TEST_MSG" -k $RECIPIENT_KEY -c test_identity.conf` | ||
26 | if [ $? == 0 ] | ||
27 | then | ||
28 | MSG_DEC=`gnunet-identity -R "$MSG_ENC" -e recipientego -c test_identity.conf` | ||
29 | fi | ||
30 | RECIPIENT_KEY_ED=`gnunet-identity -d -e recipientegoed -q -c test_identity.conf` | ||
31 | MSG_ENC_ED=`gnunet-identity -W "$TEST_MSG" -k $RECIPIENT_KEY_ED -c test_identity.conf` | ||
32 | if [ $? == 0 ] | ||
33 | then | ||
34 | MSG_DEC_ED=`gnunet-identity -R "$MSG_ENC_ED" -e recipientegoed -c test_identity.conf` | ||
35 | fi | ||
36 | gnunet-identity -D recipientego -c test_identity.conf | ||
37 | gnunet-identity -D recipientegoed -c test_identity.conf | ||
38 | gnunet-arm -e -c test_identity.conf | ||
39 | if [ "$TEST_MSG" != "$MSG_DEC" ] | ||
40 | then | ||
41 | diff <(echo "$TEST_MSG" ) <(echo "$MSG_DEC") | ||
42 | echo "Failed - \"$TEST_MSG\" != \"$MSG_DEC\"" | ||
43 | exit 1 | ||
44 | fi | ||
45 | if [ "$TEST_MSG" != "$MSG_DEC_ED" ] | ||
46 | then | ||
47 | diff <(echo "$TEST_MSG" ) <(echo "$MSG_DEC_ED") | ||
48 | echo "Failed - \"$TEST_MSG\" != \"$MSG_DEC_ED\"" | ||
49 | exit 1 | ||
50 | fi | ||
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 | |||
6 | identity_link="http://localhost:7776/identity" | ||
7 | wrong_link="http://localhost:7776/identityandmore" | ||
8 | |||
9 | |||
10 | curl_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 | |||
21 | curl_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 | |||
33 | curl_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 | |||
44 | curl_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 | ||
57 | test="$(gnunet-identity -d)" | ||
58 | #if no identity exists | ||
59 | if [ "" == "$test" ] | ||
60 | then | ||
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" | ||
75 | else | ||
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" | ||
86 | fi | ||
87 | |||
88 | #Test POST | ||
89 | gnunet-identity -D "test_plugin_rest_identity" > /dev/null 2>&1 | ||
90 | gnunet-identity -D "test_plugin_rest_identity1" > /dev/null 2>&1 | ||
91 | |||
92 | curl_post "${identity_link}" '{"name":"test_plugin_rest_identity"}' "HTTP/1.1 201 Created" | ||
93 | curl_post "${identity_link}" '{"name":"test_plugin_rest_identity"}' "HTTP/1.1 409" | ||
94 | curl_post "${identity_link}" '{"name":"Test_plugin_rest_identity"}' "HTTP/1.1 409" | ||
95 | curl_post "${identity_link}" '{}' "error" | ||
96 | curl_post "${identity_link}" '' "error" | ||
97 | curl_post "${identity_link}" '{"name":""}' "error" | ||
98 | curl_post "${identity_link}" '{"name":123}' "error" | ||
99 | curl_post "${identity_link}" '{"name":[]}' "error" | ||
100 | curl_post "${identity_link}" '{"name1":"test_plugin_rest_identity"}' "error" | ||
101 | curl_post "${identity_link}" '{"other":""}' "error" | ||
102 | curl_post "${identity_link}" '{"name":"test_plugin_rest_identity1", "other":"test_plugin_rest_identity2"}' "error" | ||
103 | |||
104 | #Test PUT | ||
105 | name="$(gnunet-identity -d | grep "test_plugin_rest_identity" | awk 'NR==1{print $1}')" | ||
106 | public="$(gnunet-identity -d | grep "test_plugin_rest_identity" | awk 'NR==1{print $3}')" | ||
107 | |||
108 | curl_put "${identity_link}/pubkey/$public" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 204" | ||
109 | curl_put "${identity_link}/pubkey/$public" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 409" | ||
110 | curl_put "${identity_link}/pubkey/${public}xx" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404" | ||
111 | curl_put "${identity_link}/pubkey/" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404" | ||
112 | curl_put "${identity_link}/pubke" '{"newname":"test_plugin_rest_identity1"}' "error" | ||
113 | curl_put "${identity_link}" '{"newname":"test_plugin_rest_identity1","other":"sdfdsf"}' "error" | ||
114 | curl_put "${identity_link}/pubkey/$name" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404" | ||
115 | curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"test_plugin_rest_identity"}' "HTTP/1.1 204" | ||
116 | curl_put "${identity_link}/pubkey/$public" '{"newnam":"test_plugin_rest_identity"}' "error" | ||
117 | curl_put "${identity_link}/name/test_plugin_rest_identity" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 204" | ||
118 | curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"TEST_plugin_rest_identity1"}' "HTTP/1.1 409" | ||
119 | curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 409" | ||
120 | curl_put "${identity_link}/name/test_plugin_rest_identityxxx" '{"newname":"test_plugin_rest_identity"}' "HTTP/1.1 404" | ||
121 | curl_put "${identity_link}/name/test_plugin_rest_identity1" '{"newname":"test_plugin_rest_identity"}' "HTTP/1.1 204" | ||
122 | curl_put "${identity_link}/name/test_plugin_rest_identity" '{"newnam":"test_plugin_rest_identityfail"}' "error" | ||
123 | |||
124 | #Test subsystem | ||
125 | curl_put "${identity_link}/subsystem/test_plugin_rest_identity" '{"subsystem":"namestore"}' "HTTP/1.1 204" | ||
126 | curl_put "${identity_link}/subsystem/test_plugin_rest_identity" '{"subsystem":"namestore"}' "HTTP/1.1 204" | ||
127 | curl_get "${identity_link}/subsystem/namestore" "test_plugin_rest_identity" | ||
128 | public="$(gnunet-identity -d | grep "test_plugin_rest_identity" | awk 'NR==1{print $3}')" | ||
129 | curl_put "${identity_link}/subsystem/$public" '{"subsystem":"namestore"}' "HTTP/1.1 404" | ||
130 | curl_post "${identity_link}" '{"name":"test_plugin_rest_identity1"}' "HTTP/1.1 201 Created" | ||
131 | curl_get "${identity_link}/subsystem/test_plugin_rest_identity_no_subsystem" "error" | ||
132 | curl_put "${identity_link}/subsystem/test_plugin_rest_identity1" '{"subsystem":"test_plugin_rest_identity_no_subsystem"}' "HTTP/1.1 204" | ||
133 | curl_get "${identity_link}/subsystem/test_plugin_rest_identity_no_subsystem" "test_plugin_rest_identity1" | ||
134 | |||
135 | curl_put "${identity_link}/subsystem/test_plugin_rest_identity1" '{"subsyste":"test_plugin_rest_identity_no_subsystem"}' "error" | ||
136 | curl_put "${identity_link}/subsystem/test_plugin_rest_identity1" '{"subsystem":"test_plugin_rest_identity_no_subsystem"}' "HTTP/1.1 204" | ||
137 | |||
138 | #Test DELETE | ||
139 | curl_delete "${identity_link}/name/test_plugin_rest_identity" "HTTP/1.1 204" | ||
140 | curl_get "${identity_link}/name/test_plugin_rest_identity" "error" | ||
141 | curl_delete "${identity_link}/name/TEST_plugin_rest_identity1" "HTTP/1.1 204" | ||
142 | curl_delete "${identity_link}/name/test_plugin_rest_identity1" "HTTP/1.1 404" | ||
143 | curl_get "${identity_link}/name/test_plugin_rest_identity1" "error" | ||
144 | curl_delete "${identity_link}/name/test_plugin_rest_identity_not_found" "HTTP/1.1 404" | ||
145 | curl_post "${identity_link}" '{"name":"test_plugin_rest_identity1"}' "HTTP/1.1 201 Created" | ||
146 | public="$(gnunet-identity -d | grep "test_plugin_rest_identity1" | awk 'NR==1{print $3}')" | ||
147 | curl_delete "${identity_link}/pubkey/$public" "HTTP/1.1 204" | ||
148 | curl_delete "${identity_link}/pubke/$public" "error" | ||
149 | curl_delete "${identity_link}/pubkey/${public}other=232" "HTTP/1.1 404" | ||
150 | |||
151 | #Test wrong_link | ||
152 | curl_get "$wrong_link" "HTTP/1.1 404" | ||
153 | curl_post "$wrong_link" '{"name":"test_plugin_rest_identity"}' "HTTP/1.1 404" | ||
154 | curl_put "$wrong_link/name/test_plugin_rest_identity" '{"newname":"test_plugin_rest_identity1"}' "HTTP/1.1 404" | ||
155 | curl_delete "$wrong_link/name/test_plugin_rest_identity1" "HTTP/1.1 404" | ||
156 | |||
157 | exit 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 | |||
5 | header='{"alg":"EdDSA"}' | ||
6 | payload='Example of Ed25519 signing' | ||
7 | key='{ "kty":"OKP", | ||
8 | "crv":"Ed25519", | ||
9 | "d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", | ||
10 | "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" | ||
11 | }' | ||
12 | |||
13 | header_payload_test="eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc" | ||
14 | signature_test="hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg" | ||
15 | |||
16 | base64url_add_padding() { | ||
17 | for i in $( seq 1 $(( 4 - ${#1} % 4 )) ); do padding+="="; done | ||
18 | echo "$1""$padding" | ||
19 | } | ||
20 | |||
21 | base64url_encode () { | ||
22 | echo -n -e "$1" | base64 -w0 | tr '+/' '-_' | tr -d '=' | ||
23 | } | ||
24 | |||
25 | base64url_decode () { | ||
26 | padded_input=$(base64url_add_padding "$1") | ||
27 | echo -n "$padded_input" | basenc --base64url -d | ||
28 | } | ||
29 | |||
30 | base32crockford_encode () { | ||
31 | echo -n -e "$1" | basenc --base32hex | tr 'IJKLMNOPQRSTUV' 'JKMNPQRSTVWXYZ' | ||
32 | } | ||
33 | |||
34 | echo -n "jwk: " | ||
35 | echo $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) | ||
41 | header_hex=("00" "01" "00" "14") | ||
42 | |||
43 | # Convert secret JWK to HEX array | ||
44 | key_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 | ||
47 | header_key_hex=(${header_hex[@]} ${key_hex[@]}) | ||
48 | |||
49 | # Encode with Base32Crogford | ||
50 | key_gnunet=$(echo -n "${header_key_hex[*]}" | tr -d " " | xxd -p -r | basenc --base32hex | tr 'IJKLMNOPQRSTUV' 'JKMNPQRSTVWXYZ' | tr -d "=") | ||
51 | echo "gnunet skey: $key_gnunet" | ||
52 | |||
53 | # Create ego | ||
54 | gnunet-identity -C ego9696595726 -X -P "$key_gnunet" | ||
55 | |||
56 | # Test base64url encoding and header.payload generation | ||
57 | header_payload_enc="$(base64url_encode "$header").$(base64url_encode "$payload")" | ||
58 | if [ $header_payload_enc != $header_payload_test ] ; | ||
59 | then | ||
60 | exit 1 | ||
61 | fi | ||
62 | echo "header.payload: $header_payload_enc" | ||
63 | |||
64 | # Sign JWT | ||
65 | signature_enc=$(curl -s "localhost:7776/sign?user=ego9696595726&data=$header_payload_enc" | jq -r '.signature') | ||
66 | jwt="$header_payload_enc.$signature_enc" | ||
67 | echo "header.payload.signature: $jwt" | ||
68 | |||
69 | gnunet-identity -D ego9696595726 | ||
70 | |||
71 | if [ $signature_enc != $signature_test ] | ||
72 | then | ||
73 | echo "Signature does not check out:" | ||
74 | echo "$signature_enc" | ||
75 | echo "$signature_test" | ||
76 | exit 1 | ||
77 | else | ||
78 | echo "Signature does check out!" | ||
79 | exit 1 | ||
80 | fi | ||
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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | pkgcfg_DATA = \ | ||
11 | rest.conf | ||
12 | |||
13 | if USE_COVERAGE | ||
14 | AM_CFLAGS = --coverage -O0 | ||
15 | XLIBS = -lgcov | ||
16 | endif | ||
17 | |||
18 | lib_LTLIBRARIES = \ | ||
19 | libgnunetrest.la | ||
20 | |||
21 | libexec_PROGRAMS = \ | ||
22 | gnunet-rest-server | ||
23 | |||
24 | EXTRA_DIST = \ | ||
25 | rest.conf | ||
26 | |||
27 | gnunet_rest_server_SOURCES = \ | ||
28 | gnunet-rest-server.c | ||
29 | gnunet_rest_server_LDADD = \ | ||
30 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
31 | $(GN_LIBINTL) $(MHD_LIBS) | ||
32 | gnunet_rest_server_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) | ||
33 | |||
34 | libgnunetrest_la_SOURCES = \ | ||
35 | rest.c | ||
36 | libgnunetrest_la_LIBADD = \ | ||
37 | $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIB) \ | ||
38 | $(GN_LIBINTL) $(MHD_LIBS) | ||
39 | libgnunetrest_la_LDFLAGS = \ | ||
40 | $(GN_LIB_LDFLAGS) \ | ||
41 | -version-info 0:0:0 | ||
42 | libgnunetrest_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 | */ | ||
65 | static struct GNUNET_SCHEDULER_Task *httpd_task; | ||
66 | |||
67 | /** | ||
68 | * The address to bind to | ||
69 | */ | ||
70 | static in_addr_t address; | ||
71 | |||
72 | /** | ||
73 | * The IPv6 address to bind to | ||
74 | */ | ||
75 | static struct in6_addr address6; | ||
76 | |||
77 | /** | ||
78 | * The port the service is running on (default 7776) | ||
79 | */ | ||
80 | static unsigned long long port = GNUNET_REST_SERVICE_PORT; | ||
81 | |||
82 | /** | ||
83 | * The listen socket of the service for IPv4 | ||
84 | */ | ||
85 | static struct GNUNET_NETWORK_Handle *lsock4; | ||
86 | |||
87 | /** | ||
88 | * The listen socket of the service for IPv6 | ||
89 | */ | ||
90 | static struct GNUNET_NETWORK_Handle *lsock6; | ||
91 | |||
92 | /** | ||
93 | * The listen task ID for IPv4 | ||
94 | */ | ||
95 | static struct GNUNET_SCHEDULER_Task *ltask4; | ||
96 | |||
97 | /** | ||
98 | * The listen task ID for IPv6 | ||
99 | */ | ||
100 | static struct GNUNET_SCHEDULER_Task *ltask6; | ||
101 | |||
102 | /** | ||
103 | * Daemon for HTTP | ||
104 | */ | ||
105 | static struct MHD_Daemon *httpd; | ||
106 | |||
107 | /** | ||
108 | * Response we return on failures. | ||
109 | */ | ||
110 | static struct MHD_Response *failure_response; | ||
111 | |||
112 | /** | ||
113 | * Our configuration. | ||
114 | */ | ||
115 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
116 | |||
117 | /** | ||
118 | * Echo request Origin in CORS | ||
119 | */ | ||
120 | static int echo_origin; | ||
121 | |||
122 | /** | ||
123 | * Do basic auth of user | ||
124 | */ | ||
125 | static int basic_auth_enabled; | ||
126 | |||
127 | /** | ||
128 | * Basic auth secret | ||
129 | */ | ||
130 | static char *basic_auth_secret; | ||
131 | |||
132 | /** | ||
133 | * User of the service | ||
134 | */ | ||
135 | char cuser[_POSIX_LOGIN_NAME_MAX]; | ||
136 | |||
137 | /** | ||
138 | * Allowed Origins (CORS) | ||
139 | */ | ||
140 | static char *allow_origins; | ||
141 | |||
142 | /** | ||
143 | * Allowed Headers (CORS) | ||
144 | */ | ||
145 | static char *allow_headers; | ||
146 | |||
147 | /** | ||
148 | * Allowed Credentials (CORS) | ||
149 | */ | ||
150 | static char *allow_credentials; | ||
151 | |||
152 | /** | ||
153 | * Plugin list head | ||
154 | */ | ||
155 | static struct PluginListEntry *plugins_head; | ||
156 | |||
157 | /** | ||
158 | * Plugin list tail | ||
159 | */ | ||
160 | static struct PluginListEntry *plugins_tail; | ||
161 | |||
162 | /** | ||
163 | * A plugin list entry | ||
164 | */ | ||
165 | struct 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 | */ | ||
187 | struct 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 | */ | ||
205 | struct 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 | */ | ||
236 | static struct AcceptedRequest *req_list_head; | ||
237 | |||
238 | /** | ||
239 | * AcceptedRequest list tail | ||
240 | */ | ||
241 | static 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 | */ | ||
251 | static void | ||
252 | do_httpd (void *cls); | ||
253 | |||
254 | |||
255 | /** | ||
256 | * Run MHD now, we have extra data ready for the callback. | ||
257 | */ | ||
258 | static void | ||
259 | run_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 | */ | ||
278 | static void | ||
279 | plugin_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 | |||
290 | static int | ||
291 | cleanup_url_map (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
292 | { | ||
293 | GNUNET_free (value); | ||
294 | return GNUNET_YES; | ||
295 | } | ||
296 | |||
297 | static void | ||
298 | cleanup_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 | |||
326 | static void | ||
327 | cleanup_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 | |||
347 | static int | ||
348 | header_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 | |||
378 | static int | ||
379 | url_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 | |||
405 | static MHD_RESULT | ||
406 | post_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 | */ | ||
471 | static MHD_RESULT | ||
472 | create_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 | */ | ||
657 | static void | ||
658 | kill_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 | */ | ||
701 | static void | ||
702 | schedule_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 | */ | ||
773 | static void * | ||
774 | mhd_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 | */ | ||
804 | static void | ||
805 | mhd_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 | */ | ||
835 | static void | ||
836 | mhd_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 | */ | ||
895 | static void | ||
896 | do_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 | */ | ||
909 | static void | ||
910 | do_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 | */ | ||
969 | static void | ||
970 | do_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 | */ | ||
996 | static struct GNUNET_NETWORK_Handle * | ||
997 | bind_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 | */ | ||
1031 | static struct GNUNET_NETWORK_Handle * | ||
1032 | bind_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 | */ | ||
1068 | static void | ||
1069 | load_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 | */ | ||
1101 | static void | ||
1102 | run (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 | */ | ||
1352 | int | ||
1353 | main (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 @@ | |||
1 | libgnunetrest_src = ['rest.c'] | ||
2 | |||
3 | gnunetservicerest_src = ['gnunet-rest-server.c'] | ||
4 | |||
5 | configure_file(input : 'rest.conf', | ||
6 | output : 'rest.conf', | ||
7 | configuration : cdata, | ||
8 | install: true, | ||
9 | install_dir: pkgcfgdir) | ||
10 | |||
11 | |||
12 | if get_option('monolith') | ||
13 | foreach p : libgnunetrest_src + gnunetservicerest_src | ||
14 | gnunet_src += 'rest/' + p | ||
15 | endforeach | ||
16 | subdir_done() | ||
17 | endif | ||
18 | |||
19 | |||
20 | libgnunetrest = 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')) | ||
28 | pkg.generate(libgnunetrest, url: 'https://www.gnunet.org', | ||
29 | description : 'Provides API for accessing the REST service') | ||
30 | libgnunetrest_dep = declare_dependency(link_with : libgnunetrest) | ||
31 | |||
32 | shared_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') | ||
41 | shared_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 | |||
52 | executable ('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 | |||
36 | int | ||
37 | GNUNET_REST_namespace_match (const char *url, const char *namespace) | ||
38 | { | ||
39 | return 0 == strncmp (namespace, url, strlen (namespace)); | ||
40 | } | ||
41 | |||
42 | |||
43 | struct MHD_Response* | ||
44 | GNUNET_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 | |||
63 | int | ||
64 | GNUNET_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] | ||
2 | UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-rest.sock | ||
3 | BINARY=gnunet-rest-server | ||
4 | IMMEDIATE_START=YES | ||
5 | HTTP_PORT=7776 | ||
6 | BIND_TO=127.0.0.1 | ||
7 | BIND_TO6=::1 | ||
8 | REST_ALLOW_HEADERS=Authorization,Accept,Content-Type | ||
9 | REST_ECHO_ORIGIN_WEBEXT=YES | ||
10 | REST_ALLOW_ORIGIN=http://localhost:4200 | ||
11 | REST_ALLOW_CREDENTIALS=true | ||
12 | RUN_PER_USER=NO | ||
13 | BASIC_AUTH_SECRET_FILE=$GNUNET_DATA_HOME/rest/secret | ||
14 | BASIC_AUTH_ENABLED=YES | ||