diff options
Diffstat (limited to 'src/messenger/gnunet-service-messenger_service.c')
-rw-r--r-- | src/messenger/gnunet-service-messenger_service.c | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/src/messenger/gnunet-service-messenger_service.c b/src/messenger/gnunet-service-messenger_service.c new file mode 100644 index 000000000..963314fd8 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_service.c | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2020 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 Tobias Frisch | ||
22 | * @file src/messenger/gnunet-service-messenger_service.c | ||
23 | * @brief GNUnet MESSENGER service | ||
24 | */ | ||
25 | |||
26 | #include "gnunet-service-messenger_service.h" | ||
27 | |||
28 | #include "gnunet-service-messenger_message_kind.h" | ||
29 | |||
30 | #include "gnunet-service-messenger.h" | ||
31 | #include "gnunet-service-messenger_util.h" | ||
32 | |||
33 | static void | ||
34 | callback_shutdown_service (void *cls) | ||
35 | { | ||
36 | struct GNUNET_MESSENGER_Service *service = cls; | ||
37 | |||
38 | if (service) | ||
39 | { | ||
40 | service->shutdown = NULL; | ||
41 | |||
42 | destroy_service (service); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | static void | ||
47 | callback_update_ego (void *cls, | ||
48 | struct GNUNET_IDENTITY_Ego *ego, | ||
49 | void **ctx, | ||
50 | const char *identifier) | ||
51 | { | ||
52 | if ((!ego) || (!identifier)) | ||
53 | return; | ||
54 | |||
55 | struct GNUNET_MESSENGER_Service *service = cls; | ||
56 | |||
57 | update_service_ego(service, identifier, GNUNET_IDENTITY_ego_get_private_key(ego)); | ||
58 | } | ||
59 | |||
60 | struct GNUNET_MESSENGER_Service* | ||
61 | create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct GNUNET_SERVICE_Handle *service_handle) | ||
62 | { | ||
63 | struct GNUNET_MESSENGER_Service *service = GNUNET_new(struct GNUNET_MESSENGER_Service); | ||
64 | |||
65 | service->config = config; | ||
66 | service->service = service_handle; | ||
67 | |||
68 | service->shutdown = GNUNET_SCHEDULER_add_shutdown (&callback_shutdown_service, service); | ||
69 | |||
70 | service->dir = NULL; | ||
71 | |||
72 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (service->config, | ||
73 | GNUNET_MESSENGER_SERVICE_NAME, | ||
74 | "MESSENGER_DIR", &(service->dir))) | ||
75 | { | ||
76 | if (service->dir) | ||
77 | GNUNET_free(service->dir); | ||
78 | |||
79 | service->dir = NULL; | ||
80 | } | ||
81 | else | ||
82 | { | ||
83 | if ((GNUNET_YES != GNUNET_DISK_directory_test (service->dir, GNUNET_YES)) && (GNUNET_OK | ||
84 | != GNUNET_DISK_directory_create (service->dir))) | ||
85 | { | ||
86 | GNUNET_free(service->dir); | ||
87 | |||
88 | service->dir = NULL; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | service->cadet = GNUNET_CADET_connect (service->config); | ||
93 | service->identity = GNUNET_IDENTITY_connect (service->config, &callback_update_ego, service); | ||
94 | |||
95 | service->egos = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); | ||
96 | |||
97 | init_list_handles (&(service->handles)); | ||
98 | |||
99 | service->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); | ||
100 | service->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); | ||
101 | |||
102 | return service; | ||
103 | } | ||
104 | |||
105 | static int | ||
106 | iterate_destroy_egos (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
107 | { | ||
108 | struct GNUNET_MESSENGER_Ego *ego = value; | ||
109 | GNUNET_free(ego); | ||
110 | return GNUNET_YES; | ||
111 | } | ||
112 | |||
113 | static int | ||
114 | iterate_destroy_rooms (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
115 | { | ||
116 | struct GNUNET_MESSENGER_SrvRoom *room = value; | ||
117 | destroy_room (room); | ||
118 | return GNUNET_YES; | ||
119 | } | ||
120 | |||
121 | static int | ||
122 | iterate_destroy_contacts (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
123 | { | ||
124 | struct GNUNET_MESSENGER_SrvContact *contact = value; | ||
125 | destroy_contact (contact); | ||
126 | return GNUNET_YES; | ||
127 | } | ||
128 | |||
129 | void | ||
130 | destroy_service (struct GNUNET_MESSENGER_Service *service) | ||
131 | { | ||
132 | if (service->shutdown) | ||
133 | { | ||
134 | GNUNET_SCHEDULER_cancel (service->shutdown); | ||
135 | |||
136 | service->shutdown = NULL; | ||
137 | } | ||
138 | |||
139 | GNUNET_CONTAINER_multihashmap_iterate (service->egos, iterate_destroy_egos, NULL); | ||
140 | |||
141 | clear_list_handles (&(service->handles)); | ||
142 | |||
143 | GNUNET_CONTAINER_multihashmap_iterate (service->rooms, iterate_destroy_rooms, NULL); | ||
144 | GNUNET_CONTAINER_multihashmap_iterate (service->contacts, iterate_destroy_contacts, NULL); | ||
145 | |||
146 | GNUNET_CONTAINER_multihashmap_destroy (service->egos); | ||
147 | GNUNET_CONTAINER_multihashmap_destroy (service->rooms); | ||
148 | GNUNET_CONTAINER_multihashmap_destroy (service->contacts); | ||
149 | |||
150 | if (service->cadet) | ||
151 | { | ||
152 | GNUNET_CADET_disconnect (service->cadet); | ||
153 | |||
154 | service->cadet = NULL; | ||
155 | } | ||
156 | |||
157 | if (service->identity) | ||
158 | { | ||
159 | GNUNET_IDENTITY_disconnect (service->identity); | ||
160 | |||
161 | service->identity = NULL; | ||
162 | } | ||
163 | |||
164 | if (service->dir) | ||
165 | { | ||
166 | GNUNET_free(service->dir); | ||
167 | |||
168 | service->dir = NULL; | ||
169 | } | ||
170 | |||
171 | GNUNET_SERVICE_shutdown (service->service); | ||
172 | |||
173 | GNUNET_free(service); | ||
174 | } | ||
175 | |||
176 | struct GNUNET_MESSENGER_Ego* | ||
177 | lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier) | ||
178 | { | ||
179 | GNUNET_assert(identifier); | ||
180 | |||
181 | struct GNUNET_HashCode hash; | ||
182 | |||
183 | GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash); | ||
184 | return GNUNET_CONTAINER_multihashmap_get(service->egos, &hash); | ||
185 | } | ||
186 | |||
187 | void | ||
188 | update_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier, | ||
189 | const struct GNUNET_IDENTITY_PrivateKey* key) | ||
190 | { | ||
191 | GNUNET_assert((identifier) && (key)); | ||
192 | |||
193 | struct GNUNET_HashCode hash; | ||
194 | |||
195 | GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash); | ||
196 | |||
197 | struct GNUNET_MESSENGER_Ego* ego = GNUNET_CONTAINER_multihashmap_get(service->egos, &hash); | ||
198 | |||
199 | if (!ego) | ||
200 | { | ||
201 | ego = GNUNET_new(struct GNUNET_MESSENGER_Ego); | ||
202 | GNUNET_CONTAINER_multihashmap_put(service->egos, &hash, ego, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
203 | } | ||
204 | |||
205 | GNUNET_memcpy(&(ego->priv), key, sizeof(*key)); | ||
206 | |||
207 | if (GNUNET_OK != GNUNET_IDENTITY_key_get_public(key, &(ego->pub))) | ||
208 | GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Updating invalid ego key failed!\n"); | ||
209 | } | ||
210 | |||
211 | struct GNUNET_MESSENGER_SrvHandle* | ||
212 | add_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle *mq) | ||
213 | { | ||
214 | struct GNUNET_MESSENGER_SrvHandle *handle = create_handle (service, mq); | ||
215 | |||
216 | if (handle) | ||
217 | { | ||
218 | add_list_handle (&(service->handles), handle); | ||
219 | } | ||
220 | |||
221 | return handle; | ||
222 | } | ||
223 | |||
224 | void | ||
225 | remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle) | ||
226 | { | ||
227 | if (!handle) | ||
228 | return; | ||
229 | |||
230 | if (GNUNET_YES == remove_list_handle (&(service->handles), handle)) | ||
231 | destroy_handle (handle); | ||
232 | } | ||
233 | |||
234 | int | ||
235 | get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, struct GNUNET_PeerIdentity *peer) | ||
236 | { | ||
237 | return GNUNET_CRYPTO_get_peer_identity (service->config, peer); | ||
238 | } | ||
239 | |||
240 | struct GNUNET_MESSENGER_SrvContact* | ||
241 | get_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_IDENTITY_PublicKey *pubkey) | ||
242 | { | ||
243 | struct GNUNET_HashCode hash; | ||
244 | |||
245 | GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash); | ||
246 | |||
247 | struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_CONTAINER_multihashmap_get (service->contacts, &hash); | ||
248 | |||
249 | if (contact) | ||
250 | return contact; | ||
251 | |||
252 | contact = create_contact (pubkey); | ||
253 | |||
254 | if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (service->contacts, &hash, contact, | ||
255 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) | ||
256 | return contact; | ||
257 | |||
258 | destroy_contact (contact); | ||
259 | return NULL; | ||
260 | } | ||
261 | |||
262 | void | ||
263 | swap_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvContact *contact, | ||
264 | const struct GNUNET_IDENTITY_PublicKey *pubkey) | ||
265 | { | ||
266 | const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact); | ||
267 | |||
268 | if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (service->contacts, hash, contact)) | ||
269 | { | ||
270 | GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey)); | ||
271 | |||
272 | hash = get_contact_id_from_key (contact); | ||
273 | |||
274 | GNUNET_CONTAINER_multihashmap_put (service->contacts, hash, contact, | ||
275 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | struct GNUNET_ShortHashCode* | ||
280 | generate_service_new_member_id (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key) | ||
281 | { | ||
282 | struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); | ||
283 | |||
284 | if (room) | ||
285 | { | ||
286 | return generate_room_member_id (room); | ||
287 | } | ||
288 | else | ||
289 | { | ||
290 | struct GNUNET_ShortHashCode *random_id = GNUNET_new(struct GNUNET_ShortHashCode); | ||
291 | generate_free_member_id (random_id, NULL); | ||
292 | return random_id; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | struct GNUNET_MESSENGER_SrvRoom* | ||
297 | get_service_room (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key) | ||
298 | { | ||
299 | return GNUNET_CONTAINER_multihashmap_get (service->rooms, key); | ||
300 | } | ||
301 | |||
302 | int | ||
303 | open_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, | ||
304 | const struct GNUNET_HashCode *key) | ||
305 | { | ||
306 | struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); | ||
307 | |||
308 | if (room) | ||
309 | return open_room (room, handle); | ||
310 | |||
311 | room = create_room (handle, key); | ||
312 | |||
313 | if ((GNUNET_YES == open_room (room, handle)) && (GNUNET_OK | ||
314 | == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) | ||
315 | return GNUNET_YES; | ||
316 | |||
317 | destroy_room (room); | ||
318 | return GNUNET_NO; | ||
319 | } | ||
320 | |||
321 | int | ||
322 | entry_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, | ||
323 | const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) | ||
324 | { | ||
325 | struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); | ||
326 | |||
327 | if (room) | ||
328 | { | ||
329 | if (GNUNET_YES == entry_room_at (room, handle, door)) | ||
330 | return GNUNET_YES; | ||
331 | else | ||
332 | return GNUNET_NO; | ||
333 | } | ||
334 | |||
335 | room = create_room (handle, key); | ||
336 | |||
337 | if ((GNUNET_YES == entry_room_at (room, handle, door)) && (GNUNET_OK | ||
338 | == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) | ||
339 | { | ||
340 | return GNUNET_YES; | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | destroy_room (room); | ||
345 | return GNUNET_NO; | ||
346 | } | ||
347 | |||
348 | } | ||
349 | |||
350 | int | ||
351 | close_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, | ||
352 | const struct GNUNET_HashCode *key) | ||
353 | { | ||
354 | struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); | ||
355 | |||
356 | if (!room) | ||
357 | return GNUNET_NO; | ||
358 | |||
359 | struct GNUNET_MESSENGER_Message *message = create_message_leave (); | ||
360 | |||
361 | if (message) | ||
362 | { | ||
363 | struct GNUNET_HashCode hash; | ||
364 | |||
365 | send_room_message (room, handle, message, &hash); | ||
366 | destroy_message (message); | ||
367 | } | ||
368 | |||
369 | const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key); | ||
370 | |||
371 | GNUNET_assert(id); | ||
372 | |||
373 | if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (handle->member_ids, key, id)) | ||
374 | return GNUNET_NO; | ||
375 | |||
376 | struct GNUNET_MESSENGER_SrvHandle *member_handle = (struct GNUNET_MESSENGER_SrvHandle*) find_list_handle_by_member ( | ||
377 | &(service->handles), key); | ||
378 | |||
379 | if (!member_handle) | ||
380 | { | ||
381 | if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (service->rooms, key, room)) | ||
382 | { | ||
383 | destroy_room (room); | ||
384 | return GNUNET_YES; | ||
385 | } | ||
386 | else | ||
387 | return GNUNET_NO; | ||
388 | } | ||
389 | |||
390 | if (room->host == handle) | ||
391 | room->host = member_handle; | ||
392 | |||
393 | return GNUNET_YES; | ||
394 | } | ||
395 | |||
396 | static void | ||
397 | get_room_data_subdir (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room, char **dir) | ||
398 | { | ||
399 | GNUNET_asprintf (dir, "%s%s%c%s%c", service->dir, "rooms", DIR_SEPARATOR, GNUNET_h2s (&(room->key)), DIR_SEPARATOR); | ||
400 | } | ||
401 | |||
402 | void | ||
403 | load_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room) | ||
404 | { | ||
405 | char *room_dir; | ||
406 | get_room_data_subdir (service, room, &room_dir); | ||
407 | |||
408 | if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES)) | ||
409 | { | ||
410 | load_message_store (&room->store, room_dir); | ||
411 | |||
412 | char *config_file; | ||
413 | GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg"); | ||
414 | |||
415 | struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); | ||
416 | |||
417 | if ((GNUNET_YES == GNUNET_DISK_file_test (config_file)) && (GNUNET_OK | ||
418 | == GNUNET_CONFIGURATION_parse (cfg, config_file))) | ||
419 | { | ||
420 | unsigned long long access; | ||
421 | |||
422 | if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "room", "access-rule", &access)) | ||
423 | room->strict_access = (int) (access); | ||
424 | |||
425 | char *message_string; | ||
426 | |||
427 | if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "room", "last-message", &message_string)) && (message_string)) | ||
428 | { | ||
429 | struct GNUNET_HashCode hash; | ||
430 | |||
431 | GNUNET_CRYPTO_hash_from_string(message_string, &hash); | ||
432 | |||
433 | const struct GNUNET_MESSENGER_Message *message = get_room_message (room, room->host, &hash, GNUNET_NO); | ||
434 | |||
435 | if (message) | ||
436 | update_room_last_messages (room, message, &hash); | ||
437 | |||
438 | GNUNET_free(message_string); | ||
439 | } | ||
440 | } | ||
441 | |||
442 | GNUNET_CONFIGURATION_destroy (cfg); | ||
443 | |||
444 | GNUNET_free(config_file); | ||
445 | } | ||
446 | |||
447 | GNUNET_free(room_dir); | ||
448 | } | ||
449 | |||
450 | void | ||
451 | save_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room) | ||
452 | { | ||
453 | if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (service->rooms, &(room->key))) | ||
454 | { | ||
455 | return; | ||
456 | } | ||
457 | |||
458 | char *room_dir; | ||
459 | get_room_data_subdir (service, room, &room_dir); | ||
460 | |||
461 | if ((GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_NO)) || (GNUNET_OK | ||
462 | == GNUNET_DISK_directory_create (room_dir))) | ||
463 | { | ||
464 | save_message_store (&room->store, room_dir); | ||
465 | |||
466 | char *config_file; | ||
467 | GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg"); | ||
468 | |||
469 | struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); | ||
470 | |||
471 | GNUNET_CONFIGURATION_set_value_number (cfg, "room", "access-rule", room->strict_access); | ||
472 | |||
473 | if (room->last_messages.head) | ||
474 | GNUNET_CONFIGURATION_set_value_string (cfg, "room", "last-message", | ||
475 | GNUNET_h2s_full (&(room->last_messages.head->hash))); | ||
476 | |||
477 | GNUNET_CONFIGURATION_write (cfg, config_file); | ||
478 | GNUNET_CONFIGURATION_destroy (cfg); | ||
479 | |||
480 | GNUNET_free(config_file); | ||
481 | } | ||
482 | |||
483 | GNUNET_free(room_dir); | ||
484 | } | ||
485 | |||
486 | void | ||
487 | handle_service_message (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room, | ||
488 | const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) | ||
489 | { | ||
490 | struct GNUNET_MESSENGER_ListHandle *element = service->handles.head; | ||
491 | |||
492 | const uint16_t length = get_message_size (message); | ||
493 | |||
494 | while (element) | ||
495 | { | ||
496 | struct GNUNET_MESSENGER_SrvHandle *handle = (struct GNUNET_MESSENGER_SrvHandle*) element->handle; | ||
497 | |||
498 | if ((handle->mq) && (get_handle_member_id (handle, &(room->key)))) | ||
499 | { | ||
500 | struct GNUNET_MESSENGER_RecvMessage *msg; | ||
501 | struct GNUNET_MQ_Envelope *env; | ||
502 | |||
503 | env = GNUNET_MQ_msg_extra(msg, length, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE); | ||
504 | |||
505 | GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); | ||
506 | GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash)); | ||
507 | |||
508 | char *buffer = ((char*) msg) + sizeof(*msg); | ||
509 | encode_message (message, length, buffer); | ||
510 | |||
511 | GNUNET_MQ_send (handle->mq, env); | ||
512 | } | ||
513 | |||
514 | element = element->next; | ||
515 | } | ||
516 | } | ||