aboutsummaryrefslogtreecommitdiff
path: root/src/service/identity/identity_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/identity/identity_api.c')
-rw-r--r--src/service/identity/identity_api.c768
1 files changed, 768 insertions, 0 deletions
diff --git a/src/service/identity/identity_api.c b/src/service/identity/identity_api.c
new file mode 100644
index 000000000..fe789d643
--- /dev/null
+++ b/src/service/identity/identity_api.c
@@ -0,0 +1,768 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2016, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file identity/identity_api.c
23 * @brief api to interact with the identity service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_error_codes.h"
30#include "gnunet_protocols.h"
31#include "gnunet_identity_service.h"
32#include "identity.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "identity-api", __VA_ARGS__)
35
36
37/**
38 * Handle for an operation with the identity service.
39 */
40struct GNUNET_IDENTITY_Operation
41{
42 /**
43 * Main identity handle.
44 */
45 struct GNUNET_IDENTITY_Handle *h;
46
47 /**
48 * We keep operations in a DLL.
49 */
50 struct GNUNET_IDENTITY_Operation *next;
51
52 /**
53 * We keep operations in a DLL.
54 */
55 struct GNUNET_IDENTITY_Operation *prev;
56
57 /**
58 * Message to send to the identity service.
59 * Allocated at the end of this struct.
60 */
61 const struct GNUNET_MessageHeader *msg;
62
63 /**
64 * Continuation to invoke with the result of the transmission; @e cb
65 * and @e create_cont will be NULL in this case.
66 */
67 GNUNET_IDENTITY_Continuation cont;
68
69 /**
70 * Continuation to invoke with the result of the transmission; @e cb
71 * and @a cb will be NULL in this case.
72 */
73 GNUNET_IDENTITY_CreateContinuation create_cont;
74
75 /**
76 * Private key to return to @e create_cont, or NULL.
77 */
78 struct GNUNET_CRYPTO_PrivateKey pk;
79
80 /**
81 * Continuation to invoke with the result of the transmission for
82 * 'get' operations (@e cont and @a create_cont will be NULL in this case).
83 */
84 GNUNET_IDENTITY_Callback cb;
85
86 /**
87 * Closure for @e cont or @e cb.
88 */
89 void *cls;
90};
91
92
93/**
94 * Handle for the service.
95 */
96struct GNUNET_IDENTITY_Handle
97{
98 /**
99 * Configuration to use.
100 */
101 const struct GNUNET_CONFIGURATION_Handle *cfg;
102
103 /**
104 * Connection to service.
105 */
106 struct GNUNET_MQ_Handle *mq;
107
108 /**
109 * Hash map from the hash of the private key to the
110 * respective `GNUNET_IDENTITY_Ego` handle.
111 */
112 struct GNUNET_CONTAINER_MultiHashMap *egos;
113
114 /**
115 * Function to call when we receive updates.
116 */
117 GNUNET_IDENTITY_Callback cb;
118
119 /**
120 * Closure for @e cb.
121 */
122 void *cb_cls;
123
124 /**
125 * Head of active operations.
126 */
127 struct GNUNET_IDENTITY_Operation *op_head;
128
129 /**
130 * Tail of active operations.
131 */
132 struct GNUNET_IDENTITY_Operation *op_tail;
133
134 /**
135 * Task doing exponential back-off trying to reconnect.
136 */
137 struct GNUNET_SCHEDULER_Task *reconnect_task;
138
139 /**
140 * Time for next connect retry.
141 */
142 struct GNUNET_TIME_Relative reconnect_delay;
143
144 /**
145 * Are we polling for incoming messages right now?
146 */
147 int in_receive;
148};
149
150
151/**
152 * Obtain the ego representing 'anonymous' users.
153 *
154 * @return handle for the anonymous user, MUST NOT be freed
155 */
156struct GNUNET_IDENTITY_Ego *
157GNUNET_IDENTITY_ego_get_anonymous ()
158{
159 static struct GNUNET_IDENTITY_Ego anon;
160 static int setup;
161 ssize_t key_len;
162
163 if (setup)
164 return &anon;
165 anon.pk.type = htonl (GNUNET_PUBLIC_KEY_TYPE_ECDSA);
166 anon.pub.type = htonl (GNUNET_PUBLIC_KEY_TYPE_ECDSA);
167 anon.pk.ecdsa_key = *GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
168 key_len = GNUNET_CRYPTO_private_key_get_length (&anon.pk);
169 GNUNET_assert (0 < key_len);
170 GNUNET_CRYPTO_hash (&anon.pk,
171 key_len,
172 &anon.id);
173 setup = 1;
174 return &anon;
175}
176
177
178
179/**
180 * Try again to connect to the identity service.
181 *
182 * @param cls handle to the identity service.
183 */
184static void
185reconnect (void *cls);
186
187
188/**
189 * Free ego from hash map.
190 *
191 * @param cls identity service handle
192 * @param key unused
193 * @param value ego to free
194 * @return #GNUNET_OK (continue to iterate)
195 */
196static int
197free_ego (void *cls,
198 const struct GNUNET_HashCode *key,
199 void *value)
200{
201 struct GNUNET_IDENTITY_Handle *h = cls;
202 struct GNUNET_IDENTITY_Ego *ego = value;
203
204 if (NULL != h->cb)
205 h->cb (h->cb_cls, ego,
206 &ego->ctx,
207 NULL);
208 GNUNET_free (ego->name);
209 GNUNET_assert (GNUNET_YES ==
210 GNUNET_CONTAINER_multihashmap_remove (h->egos,
211 key,
212 value));
213 GNUNET_free (ego);
214 return GNUNET_OK;
215}
216
217
218/**
219 * Reschedule a connect attempt to the service.
220 *
221 * @param h transport service to reconnect
222 */
223static void
224reschedule_connect (struct GNUNET_IDENTITY_Handle *h)
225{
226 struct GNUNET_IDENTITY_Operation *op;
227
228 GNUNET_assert (NULL == h->reconnect_task);
229
230 if (NULL != h->mq)
231 {
232 GNUNET_MQ_destroy (h->mq);
233 h->mq = NULL;
234 }
235 while (NULL != (op = h->op_head))
236 {
237 GNUNET_CONTAINER_DLL_remove (h->op_head,
238 h->op_tail,
239 op);
240 if (NULL != op->cont)
241 op->cont (op->cls,
242 GNUNET_EC_SERVICE_COMMUNICATION_FAILED);
243 else if (NULL != op->cb)
244 op->cb (op->cls, NULL, NULL, NULL);
245 else if (NULL != op->create_cont)
246 op->create_cont (op->cls,
247 NULL,
248 GNUNET_EC_SERVICE_COMMUNICATION_FAILED);
249 GNUNET_free (op);
250 }
251 GNUNET_CONTAINER_multihashmap_iterate (h->egos,
252 &free_ego,
253 h);
254 LOG (GNUNET_ERROR_TYPE_DEBUG,
255 "Scheduling task to reconnect to identity service in %s.\n",
256 GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay,
257 GNUNET_YES));
258 h->reconnect_task =
259 GNUNET_SCHEDULER_add_delayed (h->reconnect_delay,
260 &reconnect,
261 h);
262 h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
263}
264
265
266/**
267 * Generic error handler, called with the appropriate error code and
268 * the same closure specified at the creation of the message queue.
269 * Not every message queue implementation supports an error handler.
270 *
271 * @param cls closure with the `struct GNUNET_IDENTITY_Handle *`
272 * @param error error code
273 */
274static void
275mq_error_handler (void *cls,
276 enum GNUNET_MQ_Error error)
277{
278 struct GNUNET_IDENTITY_Handle *h = cls;
279
280 reschedule_connect (h);
281}
282
283
284/**
285 * We received a result code from the service.
286 *
287 * @param cls closure
288 * @param rcm result message received
289 */
290static void
291handle_identity_result_code (void *cls,
292 const struct ResultCodeMessage *rcm)
293{
294 struct GNUNET_IDENTITY_Handle *h = cls;
295 struct GNUNET_IDENTITY_Operation *op;
296 enum GNUNET_ErrorCode ec = ntohl (rcm->result_code);
297
298 op = h->op_head;
299 if (NULL == op)
300 {
301 GNUNET_break (0);
302 reschedule_connect (h);
303 return;
304 }
305 GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op);
306 if (NULL != op->cont)
307 op->cont (op->cls, ec);
308 else if (NULL != op->cb)
309 op->cb (op->cls, NULL, NULL, NULL);
310 else if (NULL != op->create_cont)
311 op->create_cont (op->cls, (GNUNET_EC_NONE == ec) ? &op->pk : NULL, ec);
312 GNUNET_free (op);
313}
314
315
316/**
317 * Check validity of identity update message.
318 *
319 * @param cls closure
320 * @param um message received
321 * @return #GNUNET_OK if the message is well-formed
322 */
323static int
324check_identity_update (void *cls,
325 const struct UpdateMessage *um)
326{
327 uint16_t size = ntohs (um->header.size);
328 uint16_t name_len = ntohs (um->name_len);
329 const char *str = (const char *) &um[1];
330
331 if ((size < name_len + sizeof(struct UpdateMessage)) ||
332 ((0 != name_len) && ('\0' != str[name_len - 1])))
333 {
334 GNUNET_break (0);
335 return GNUNET_SYSERR;
336 }
337 return GNUNET_OK;
338}
339
340
341/**
342 * Handle identity update message.
343 *
344 * @param cls closure
345 * @param um message received
346 */
347static void
348handle_identity_update (void *cls,
349 const struct UpdateMessage *um)
350{
351 struct GNUNET_IDENTITY_Handle *h = cls;
352 uint16_t name_len = ntohs (um->name_len);
353 const char *str;
354 size_t key_len;
355 size_t kb_read;
356 struct GNUNET_HashCode id;
357 struct GNUNET_IDENTITY_Ego *ego;
358 struct GNUNET_CRYPTO_PrivateKey private_key;
359 const char *tmp;
360
361 if (GNUNET_YES == ntohs (um->end_of_list))
362 {
363 /* end of initial list of data */
364 if (NULL != h->cb)
365 h->cb (h->cb_cls, NULL, NULL, NULL);
366 return;
367 }
368 tmp = (const char*) &um[1];
369 str = (0 == name_len) ? NULL : tmp;
370 memset (&private_key, 0, sizeof (private_key));
371 key_len = ntohs (um->key_len);
372 GNUNET_assert (GNUNET_SYSERR !=
373 GNUNET_CRYPTO_read_private_key_from_buffer (tmp + name_len,
374 key_len,
375 &private_key,
376 &kb_read));
377 GNUNET_assert (0 <= GNUNET_CRYPTO_private_key_get_length (&private_key));
378 GNUNET_CRYPTO_hash (&private_key,
379 GNUNET_CRYPTO_private_key_get_length (&private_key),
380 &id);
381 ego = GNUNET_CONTAINER_multihashmap_get (h->egos,
382 &id);
383 if (NULL == ego)
384 {
385 /* ego was created */
386 if (NULL == str)
387 {
388 /* deletion of unknown ego? not allowed */
389 GNUNET_break (0);
390 reschedule_connect (h);
391 return;
392 }
393 ego = GNUNET_new (struct GNUNET_IDENTITY_Ego);
394 ego->pub_initialized = GNUNET_NO;
395 ego->pk = private_key;
396 ego->name = GNUNET_strdup (str);
397 ego->id = id;
398 GNUNET_assert (GNUNET_YES ==
399 GNUNET_CONTAINER_multihashmap_put (
400 h->egos,
401 &ego->id,
402 ego,
403 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
404 }
405 if (NULL == str)
406 {
407 /* ego was deleted */
408 GNUNET_assert (GNUNET_YES ==
409 GNUNET_CONTAINER_multihashmap_remove (h->egos,
410 &ego->id,
411 ego));
412 }
413 else
414 {
415 /* ego changed name */
416 GNUNET_free (ego->name);
417 ego->name = GNUNET_strdup (str);
418 }
419 /* inform application about change */
420 if (NULL != h->cb)
421 h->cb (h->cb_cls,
422 ego,
423 &ego->ctx,
424 str);
425 /* complete deletion */
426 if (NULL == str)
427 {
428 GNUNET_free (ego->name);
429 GNUNET_free (ego);
430 }
431}
432
433
434/**
435 * Try again to connect to the identity service.
436 *
437 * @param cls handle to the identity service.
438 */
439static void
440reconnect (void *cls)
441{
442 struct GNUNET_IDENTITY_Handle *h = cls;
443 struct GNUNET_MQ_MessageHandler handlers[] = {
444 GNUNET_MQ_hd_fixed_size (identity_result_code,
445 GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE,
446 struct ResultCodeMessage,
447 h),
448 GNUNET_MQ_hd_var_size (identity_update,
449 GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE,
450 struct UpdateMessage,
451 h),
452 GNUNET_MQ_handler_end ()
453 };
454 struct GNUNET_MQ_Envelope *env;
455 struct GNUNET_MessageHeader *msg;
456
457 h->reconnect_task = NULL;
458 LOG (GNUNET_ERROR_TYPE_DEBUG,
459 "Connecting to identity service.\n");
460 GNUNET_assert (NULL == h->mq);
461 h->mq = GNUNET_CLIENT_connect (h->cfg,
462 "identity",
463 handlers,
464 &mq_error_handler,
465 h);
466 if (NULL == h->mq)
467 return;
468 if (NULL != h->cb)
469 {
470 env = GNUNET_MQ_msg (msg,
471 GNUNET_MESSAGE_TYPE_IDENTITY_START);
472 GNUNET_MQ_send (h->mq,
473 env);
474 }
475}
476
477
478/**
479 * Connect to the identity service.
480 *
481 * @param cfg the configuration to use
482 * @param cb function to call on all identity events, can be NULL
483 * @param cb_cls closure for @a cb
484 * @return handle to use
485 */
486struct GNUNET_IDENTITY_Handle *
487GNUNET_IDENTITY_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
488 GNUNET_IDENTITY_Callback cb,
489 void *cb_cls)
490{
491 struct GNUNET_IDENTITY_Handle *h;
492
493 h = GNUNET_new (struct GNUNET_IDENTITY_Handle);
494 h->cfg = cfg;
495 h->cb = cb;
496 h->cb_cls = cb_cls;
497 h->egos = GNUNET_CONTAINER_multihashmap_create (16,
498 GNUNET_YES);
499 reconnect (h);
500 if (NULL == h->mq)
501 {
502 GNUNET_free (h);
503 return NULL;
504 }
505 return h;
506}
507
508
509
510/**
511 * Obtain the ECC key associated with a ego.
512 *
513 * @param ego the ego
514 * @return associated ECC key, valid as long as the ego is valid
515 */
516const struct GNUNET_CRYPTO_PrivateKey *
517GNUNET_IDENTITY_ego_get_private_key (const struct GNUNET_IDENTITY_Ego *ego)
518{
519 return &ego->pk;
520}
521
522/**
523 * Get the identifier (public key) of an ego.
524 *
525 * @param ego identity handle with the private key
526 * @param pk set to ego's public key
527 */
528void
529GNUNET_IDENTITY_ego_get_public_key (struct GNUNET_IDENTITY_Ego *ego,
530 struct GNUNET_CRYPTO_PublicKey *pk)
531{
532 if (GNUNET_NO == ego->pub_initialized)
533 {
534 GNUNET_CRYPTO_key_get_public (&ego->pk, &ego->pub);
535 ego->pub_initialized = GNUNET_YES;
536 }
537 *pk = ego->pub;
538}
539
540static enum GNUNET_GenericReturnValue
541private_key_create (enum GNUNET_CRYPTO_KeyType ktype,
542 struct GNUNET_CRYPTO_PrivateKey *key)
543{
544 key->type = htonl (ktype);
545 switch (ktype)
546 {
547 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
548 GNUNET_CRYPTO_ecdsa_key_create (&key->ecdsa_key);
549 break;
550 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
551 GNUNET_CRYPTO_eddsa_key_create (&key->eddsa_key);
552 break;
553 default:
554 GNUNET_break (0);
555 return GNUNET_SYSERR;
556 }
557 return GNUNET_OK;
558}
559
560struct GNUNET_IDENTITY_Operation *
561GNUNET_IDENTITY_create (struct GNUNET_IDENTITY_Handle *h,
562 const char *name,
563 const struct GNUNET_CRYPTO_PrivateKey *privkey,
564 enum GNUNET_CRYPTO_KeyType ktype,
565 GNUNET_IDENTITY_CreateContinuation cont,
566 void *cont_cls)
567{
568 struct GNUNET_CRYPTO_PrivateKey private_key;
569 struct GNUNET_IDENTITY_Operation *op;
570 struct GNUNET_MQ_Envelope *env;
571 struct CreateRequestMessage *crm;
572 size_t slen;
573 size_t key_len;
574
575 if (NULL == h->mq)
576 return NULL;
577 slen = strlen (name) + 1;
578 if (slen >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct CreateRequestMessage))
579 {
580 GNUNET_break (0);
581 return NULL;
582 }
583 op = GNUNET_new (struct GNUNET_IDENTITY_Operation);
584 op->h = h;
585 op->create_cont = cont;
586 op->cls = cont_cls;
587 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
588 if (NULL == privkey)
589 {
590 GNUNET_assert (GNUNET_OK ==
591 private_key_create (ktype, &private_key));
592 }
593 else
594 private_key = *privkey;
595 key_len = GNUNET_CRYPTO_private_key_get_length (&private_key);
596 env = GNUNET_MQ_msg_extra (crm, slen + key_len,
597 GNUNET_MESSAGE_TYPE_IDENTITY_CREATE);
598 crm->name_len = htons (slen);
599 GNUNET_CRYPTO_write_private_key_to_buffer (&private_key,
600 &crm[1],
601 key_len);
602 crm->key_len = htons (key_len);
603 op->pk = private_key;
604 GNUNET_memcpy ((char*) &crm[1] + key_len, name, slen);
605 GNUNET_MQ_send (h->mq, env);
606 return op;
607}
608
609
610/**
611 * Renames an existing identity.
612 *
613 * @param h identity service to use
614 * @param old_name old name
615 * @param new_name desired new name
616 * @param cb function to call with the result (will only be called once)
617 * @param cb_cls closure for @a cb
618 * @return handle to abort the operation
619 */
620struct GNUNET_IDENTITY_Operation *
621GNUNET_IDENTITY_rename (struct GNUNET_IDENTITY_Handle *h,
622 const char *old_name,
623 const char *new_name,
624 GNUNET_IDENTITY_Continuation cb,
625 void *cb_cls)
626{
627 struct GNUNET_IDENTITY_Operation *op;
628 struct GNUNET_MQ_Envelope *env;
629 struct RenameMessage *grm;
630 size_t slen_old;
631 size_t slen_new;
632 char *dst;
633
634 if (NULL == h->mq)
635 return NULL;
636 slen_old = strlen (old_name) + 1;
637 slen_new = strlen (new_name) + 1;
638 if ((slen_old >= GNUNET_MAX_MESSAGE_SIZE) ||
639 (slen_new >= GNUNET_MAX_MESSAGE_SIZE) ||
640 (slen_old + slen_new >=
641 GNUNET_MAX_MESSAGE_SIZE - sizeof(struct RenameMessage)))
642 {
643 GNUNET_break (0);
644 return NULL;
645 }
646 op = GNUNET_new (struct GNUNET_IDENTITY_Operation);
647 op->h = h;
648 op->cont = cb;
649 op->cls = cb_cls;
650 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
651 env = GNUNET_MQ_msg_extra (grm,
652 slen_old + slen_new,
653 GNUNET_MESSAGE_TYPE_IDENTITY_RENAME);
654 grm->old_name_len = htons (slen_old);
655 grm->new_name_len = htons (slen_new);
656 dst = (char *) &grm[1];
657 GNUNET_memcpy (dst, old_name, slen_old);
658 GNUNET_memcpy (&dst[slen_old], new_name, slen_new);
659 GNUNET_MQ_send (h->mq, env);
660 return op;
661}
662
663
664/**
665 * Delete an existing identity.
666 *
667 * @param h identity service to use
668 * @param name name of the identity to delete
669 * @param cb function to call with the result (will only be called once)
670 * @param cb_cls closure for @a cb
671 * @return handle to abort the operation
672 */
673struct GNUNET_IDENTITY_Operation *
674GNUNET_IDENTITY_delete (struct GNUNET_IDENTITY_Handle *h,
675 const char *name,
676 GNUNET_IDENTITY_Continuation cb,
677 void *cb_cls)
678{
679 struct GNUNET_IDENTITY_Operation *op;
680 struct GNUNET_MQ_Envelope *env;
681 struct DeleteMessage *gdm;
682 size_t slen;
683
684 if (NULL == h->mq)
685 return NULL;
686 slen = strlen (name) + 1;
687 if (slen >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct DeleteMessage))
688 {
689 GNUNET_break (0);
690 return NULL;
691 }
692 op = GNUNET_new (struct GNUNET_IDENTITY_Operation);
693 op->h = h;
694 op->cont = cb;
695 op->cls = cb_cls;
696 GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
697 env = GNUNET_MQ_msg_extra (gdm, slen, GNUNET_MESSAGE_TYPE_IDENTITY_DELETE);
698 gdm->name_len = htons (slen);
699 gdm->reserved = htons (0);
700 GNUNET_memcpy (&gdm[1], name, slen);
701 GNUNET_MQ_send (h->mq, env);
702 return op;
703}
704
705
706/**
707 * Cancel an identity operation. Note that the operation MAY still
708 * be executed; this merely cancels the continuation; if the request
709 * was already transmitted, the service may still choose to complete
710 * the operation.
711 *
712 * @param op operation to cancel
713 */
714void
715GNUNET_IDENTITY_cancel (struct GNUNET_IDENTITY_Operation *op)
716{
717 op->cont = NULL;
718 op->cb = NULL;
719 op->create_cont = NULL;
720 memset (&op->pk,
721 0,
722 sizeof (op->pk));
723}
724
725
726/**
727 * Disconnect from identity service
728 *
729 * @param h handle to destroy
730 */
731void
732GNUNET_IDENTITY_disconnect (struct GNUNET_IDENTITY_Handle *h)
733{
734 struct GNUNET_IDENTITY_Operation *op;
735
736 GNUNET_assert (NULL != h);
737 if (h->reconnect_task != NULL)
738 {
739 GNUNET_SCHEDULER_cancel (h->reconnect_task);
740 h->reconnect_task = NULL;
741 }
742 if (NULL != h->egos)
743 {
744 GNUNET_CONTAINER_multihashmap_iterate (h->egos,
745 &free_ego,
746 h);
747 GNUNET_CONTAINER_multihashmap_destroy (h->egos);
748 h->egos = NULL;
749 }
750 while (NULL != (op = h->op_head))
751 {
752 GNUNET_break (NULL == op->cont);
753 GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op);
754 memset (&op->pk,
755 0,
756 sizeof (op->pk));
757 GNUNET_free (op);
758 }
759 if (NULL != h->mq)
760 {
761 GNUNET_MQ_destroy (h->mq);
762 h->mq = NULL;
763 }
764 GNUNET_free (h);
765}
766
767
768/* end of identity_api.c */