+/**
+ * Task to connect to the NAT service.
+ *
+ * @param cls our `struct GNUNET_NAT_Handle *`
+ */
+static void
+do_connect (void *cls);
+
+
+/**
+ * Task to connect to the NAT service.
+ *
+ * @param nh handle to reconnect
+ */
+static void
+reconnect (struct GNUNET_NAT_Handle *nh)
+{
+ struct AddrEntry *ae;
+
+ if (NULL != nh->mq)
+ {
+ GNUNET_MQ_destroy (nh->mq);
+ nh->mq = NULL;
+ }
+ while (NULL != (ae = nh->ae_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (nh->ae_head,
+ nh->ae_tail,
+ ae);
+ nh->address_callback (nh->callback_cls,
+ GNUNET_NO,
+ ae->ac,
+ (const struct sockaddr *) &ae[1],
+ ae->addrlen);
+ GNUNET_free (ae);
+ }
+ nh->reconnect_delay
+ = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay);
+ nh->reconnect_task
+ = GNUNET_SCHEDULER_add_delayed (nh->reconnect_delay,
+ &do_connect,
+ nh);
+}
+
+
+/**
+ * Check connection reversal request.
+ *
+ * @param cls our `struct GNUNET_NAT_Handle`
+ * @param crm the message
+ * @return #GNUNET_OK if @a crm is well-formed
+ */
+static int
+check_connection_reversal_request (void *cls,
+ const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
+{
+ if (ntohs (crm->header.size) !=
+ sizeof (*crm) +
+ sizeof (struct sockaddr_in) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle connection reversal request.
+ *
+ * @param cls our `struct GNUNET_NAT_Handle`
+ * @param crm the message
+ */
+static void
+handle_connection_reversal_request (void *cls,
+ const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
+{
+ struct GNUNET_NAT_Handle *nh = cls;
+
+ nh->reversal_callback (nh->callback_cls,
+ (const struct sockaddr *) &crm[1],
+ sizeof (struct sockaddr_in));
+}
+
+
+/**
+ * Check address change notification.
+ *
+ * @param cls our `struct GNUNET_NAT_Handle`
+ * @param acn the message
+ * @return #GNUNET_OK if @a crm is well-formed
+ */
+static int
+check_address_change_notification (void *cls,
+ const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
+{
+ size_t alen = ntohs (acn->header.size) - sizeof (*acn);
+
+ switch (alen)
+ {
+ case sizeof (struct sockaddr_in):
+ {
+ const struct sockaddr_in *s4
+ = (const struct sockaddr_in *) &acn[1];
+ if (AF_INET != s4->sin_family)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ break;
+ case sizeof (struct sockaddr_in6):
+ {
+ const struct sockaddr_in6 *s6
+ = (const struct sockaddr_in6 *) &acn[1];
+ if (AF_INET6 != s6->sin6_family)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle connection reversal request.
+ *
+ * @param cls our `struct GNUNET_NAT_Handle`
+ * @param acn the message
+ */
+static void
+handle_address_change_notification (void *cls,
+ const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
+{
+ struct GNUNET_NAT_Handle *nh = cls;
+ size_t alen = ntohs (acn->header.size) - sizeof (*acn);
+ const struct sockaddr *sa = (const struct sockaddr *) &acn[1];
+ enum GNUNET_NAT_AddressClass ac;
+ struct AddrEntry *ae;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received address change notification\n");
+ ac = (enum GNUNET_NAT_AddressClass) ntohl (acn->addr_class);
+ if (GNUNET_YES == ntohl (acn->add_remove))
+ {
+ ae = GNUNET_malloc (sizeof (*ae) + alen);
+ ae->ac = ac;
+ ae->addrlen = alen;
+ GNUNET_memcpy (&ae[1],
+ sa,
+ alen);
+ GNUNET_CONTAINER_DLL_insert (nh->ae_head,
+ nh->ae_tail,
+ ae);
+ }
+ else
+ {
+ for (ae = nh->ae_head; NULL != ae; ae = ae->next)
+ if ( (ae->addrlen == alen) &&
+ (0 == memcmp (&ae[1],
+ sa,
+ alen)) )
+ break;
+ if (NULL == ae)
+ {
+ GNUNET_break (0);
+ reconnect (nh);
+ return;
+ }
+ GNUNET_CONTAINER_DLL_remove (nh->ae_head,
+ nh->ae_tail,
+ ae);
+ GNUNET_free (ae);
+ }
+ nh->address_callback (nh->callback_cls,
+ ntohl (acn->add_remove),
+ ac,
+ sa,
+ alen);
+}
+
+
+/**
+ * Handle queue errors by reconnecting to NAT.
+ *
+ * @param cls the `struct GNUNET_NAT_Handle *`
+ * @param error details about the error
+ */
+static void
+mq_error_handler (void *cls,
+ enum GNUNET_MQ_Error error)
+{
+ struct GNUNET_NAT_Handle *nh = cls;
+
+ reconnect (nh);
+}
+
+
+/**
+ * Task to connect to the NAT service.
+ *
+ * @param cls our `struct GNUNET_NAT_Handle *`
+ */
+static void
+do_connect (void *cls)
+{
+ struct GNUNET_NAT_Handle *nh = cls;
+ struct GNUNET_MQ_MessageHandler handlers[] = {
+ GNUNET_MQ_hd_var_size (connection_reversal_request,
+ GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED,
+ struct GNUNET_NAT_ConnectionReversalRequestedMessage,
+ nh),
+ GNUNET_MQ_hd_var_size (address_change_notification,
+ GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE,
+ struct GNUNET_NAT_AddressChangeNotificationMessage,
+ nh),
+ GNUNET_MQ_handler_end ()
+ };
+ struct GNUNET_MQ_Envelope *env;
+
+ nh->reconnect_task = NULL;
+ nh->mq = GNUNET_CLIENT_connect (nh->cfg,
+ "nat",
+ handlers,
+ &mq_error_handler,
+ nh);
+ if (NULL == nh->mq)
+ {
+ reconnect (nh);
+ return;
+ }
+ env = GNUNET_MQ_msg_copy (nh->reg);
+ GNUNET_MQ_send (nh->mq,
+ env);
+}
+
+