+
+/**
+ * Append our port and forward the result.
+ *
+ * @param cls a 'struct PrettyPrinterContext'
+ * @param hostname result from DNS resolver
+ */
+static void
+append_port (void *cls, const char *hostname)
+{
+ struct PrettyPrinterContext *ppc = cls;
+ char *ret;
+
+ if (hostname == NULL)
+ {
+ ppc->asc (ppc->asc_cls, NULL);
+ GNUNET_free (ppc);
+ return;
+ }
+ GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
+ ppc->asc (ppc->asc_cls, ret);
+ GNUNET_free (ret);
+}
+
+
+/**
+ * Convert the transports address to a nice, human-readable
+ * format.
+ *
+ * @param cls closure
+ * @param type name of the transport that generated the address
+ * @param addr one of the addresses of the host, NULL for the last address
+ * the specific address format depends on the transport
+ * @param addrlen length of the address
+ * @param numeric should (IP) addresses be displayed in numeric form?
+ * @param timeout after how long should we give up?
+ * @param asc function to call on each string
+ * @param asc_cls closure for asc
+ */
+static void
+udp_plugin_address_pretty_printer (void *cls, const char *type,
+ const void *addr, size_t addrlen,
+ int numeric,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_TRANSPORT_AddressStringCallback asc,
+ void *asc_cls)
+{
+ struct PrettyPrinterContext *ppc;
+ const void *sb;
+ size_t sbs;
+ struct sockaddr_in a4;
+ struct sockaddr_in6 a6;
+ const struct IPv4UdpAddress *u4;
+ const struct IPv6UdpAddress *u6;
+ uint16_t port;
+
+ if (addrlen == sizeof (struct IPv6UdpAddress))
+ {
+ u6 = addr;
+ memset (&a6, 0, sizeof (a6));
+ a6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ a6.sin6_len = sizeof (a6);
+#endif
+ a6.sin6_port = u6->u6_port;
+ memcpy (&a6.sin6_addr, &u6->ipv6_addr, sizeof (struct in6_addr));
+ port = ntohs (u6->u6_port);
+ sb = &a6;
+ sbs = sizeof (a6);
+ }
+ else if (addrlen == sizeof (struct IPv4UdpAddress))
+ {
+ u4 = addr;
+ memset (&a4, 0, sizeof (a4));
+ a4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ a4.sin_len = sizeof (a4);
+#endif
+ a4.sin_port = u4->u4_port;
+ a4.sin_addr.s_addr = u4->ipv4_addr;
+ port = ntohs (u4->u4_port);
+ sb = &a4;
+ sbs = sizeof (a4);
+ }
+ else if (0 == addrlen)
+ {
+ asc (asc_cls, "<inbound connection>");
+ asc (asc_cls, NULL);
+ return;
+ }
+ else
+ {
+ /* invalid address */
+ GNUNET_break_op (0);
+ asc (asc_cls, NULL);
+ return;
+ }
+ ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
+ ppc->asc = asc;
+ ppc->asc_cls = asc_cls;
+ ppc->port = port;
+ GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
+}
+
+
+static void
+call_continuation (struct UDPMessageWrapper *udpw, int result)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Calling continuation for %u byte message to `%s' with result %s\n",
+ udpw->msg_size, GNUNET_i2s (&udpw->session->target),
+ (GNUNET_OK == result) ? "OK" : "SYSERR");
+ if (NULL != udpw->cont)
+ {
+ udpw->cont (udpw->cont_cls, &udpw->session->target,result);
+ }
+
+}
+
+
+/**
+ * Check if the given port is plausible (must be either our listen
+ * port or our advertised port). If it is neither, we return
+ * GNUNET_SYSERR.
+ *
+ * @param plugin global variables
+ * @param in_port port number to check
+ * @return GNUNET_OK if port is either open_port or adv_port
+ */
+static int
+check_port (struct Plugin *plugin, uint16_t in_port)
+{
+ if ((in_port == plugin->port) || (in_port == plugin->aport))
+ return GNUNET_OK;
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Function that will be called to check if a binary address for this
+ * plugin is well-formed and corresponds to an address for THIS peer
+ * (as per our configuration). Naturally, if absolutely necessary,
+ * plugins can be a bit conservative in their answer, but in general
+ * plugins should make sure that the address does not redirect
+ * traffic to a 3rd party that might try to man-in-the-middle our
+ * traffic.
+ *
+ * @param cls closure, should be our handle to the Plugin
+ * @param addr pointer to the address
+ * @param addrlen length of addr
+ * @return GNUNET_OK if this is a plausible address for this peer
+ * and transport, GNUNET_SYSERR if not
+ *
+ */
+static int
+udp_plugin_check_address (void *cls, const void *addr, size_t addrlen)
+{
+ struct Plugin *plugin = cls;
+ struct IPv4UdpAddress *v4;
+ struct IPv6UdpAddress *v6;
+
+ if ((addrlen != sizeof (struct IPv4UdpAddress)) &&
+ (addrlen != sizeof (struct IPv6UdpAddress)))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (addrlen == sizeof (struct IPv4UdpAddress))
+ {
+ v4 = (struct IPv4UdpAddress *) addr;
+ if (GNUNET_OK != check_port (plugin, ntohs (v4->u4_port)))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_NAT_test_address (plugin->nat, &v4->ipv4_addr,
+ sizeof (struct in_addr)))
+ return GNUNET_SYSERR;
+ }
+ else
+ {
+ v6 = (struct IPv6UdpAddress *) addr;
+ if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != check_port (plugin, ntohs (v6->u6_port)))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_NAT_test_address (plugin->nat, &v6->ipv6_addr,
+ sizeof (struct in6_addr)))
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Task to free resources associated with a session.
+ *
+ * @param s session to free
+ */
+static void
+free_session (struct Session *s)
+{
+ if (s->frag_ctx != NULL)
+ {
+ GNUNET_FRAGMENT_context_destroy(s->frag_ctx->frag);
+ GNUNET_free (s->frag_ctx);
+ s->frag_ctx = NULL;
+ }
+ GNUNET_free (s);
+}
+
+
+/**
+ * Functions with this signature are called whenever we need
+ * to close a session due to a disconnect or failure to
+ * establish a connection.
+ *
+ * @param s session to close down
+ */
+static void
+disconnect_session (struct Session *s)
+{
+ struct UDPMessageWrapper *udpw;
+ struct UDPMessageWrapper *next;
+
+ GNUNET_assert (GNUNET_YES != s->in_destroy);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Session %p to peer `%s' address ended \n",
+ s,
+ GNUNET_i2s (&s->target),
+ GNUNET_a2s (s->sock_addr, s->addrlen));
+ stop_session_timeout (s);
+ next = plugin->ipv4_queue_head;
+ while (NULL != (udpw = next))
+ {
+ next = udpw->next;
+ if (udpw->session == s)
+ {
+ GNUNET_CONTAINER_DLL_remove(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
+ call_continuation(udpw, GNUNET_SYSERR);
+ GNUNET_free (udpw);
+ }
+ }
+ next = plugin->ipv6_queue_head;
+ while (NULL != (udpw = next))
+ {
+ next = udpw->next;
+ if (udpw->session == s)
+ {
+ GNUNET_CONTAINER_DLL_remove(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
+ call_continuation(udpw, GNUNET_SYSERR);
+ GNUNET_free (udpw);
+ }
+ udpw = next;
+ }
+ plugin->env->session_end (plugin->env->cls, &s->target, s);
+
+ if (NULL != s->frag_ctx)
+ {
+ if (NULL != s->frag_ctx->cont)
+ {
+ s->frag_ctx->cont (s->frag_ctx->cont_cls, &s->target, GNUNET_SYSERR);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Calling continuation for fragemented message to `%s' with result SYSERR\n",
+ GNUNET_i2s (&s->target));
+ }
+ }
+
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (plugin->sessions,
+ &s->target.hashPubKey,
+ s));
+ GNUNET_STATISTICS_set(plugin->env->stats,
+ "# UDP sessions active",
+ GNUNET_CONTAINER_multihashmap_size(plugin->sessions),
+ GNUNET_NO);
+ if (s->rc > 0)
+ s->in_destroy = GNUNET_YES;
+ else
+ free_session (s);
+}
+
+/**
+ * Destroy a session, plugin is being unloaded.
+ *
+ * @param cls unused
+ * @param key hash of public key of target peer
+ * @param value a 'struct PeerSession*' to clean up
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+disconnect_and_free_it (void *cls, const struct GNUNET_HashCode * key, void *value)
+{
+ disconnect_session(value);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Disconnect from a remote node. Clean up session if we have one for this peer
+ *
+ * @param cls closure for this call (should be handle to Plugin)
+ * @param target the peeridentity of the peer to disconnect
+ * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
+ */
+static void
+udp_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
+{
+ struct Plugin *plugin = cls;
+ GNUNET_assert (plugin != NULL);
+
+ GNUNET_assert (target != NULL);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Disconnecting from peer `%s'\n", GNUNET_i2s (target));
+ /* Clean up sessions */
+ GNUNET_CONTAINER_multihashmap_get_multiple (plugin->sessions, &target->hashPubKey, &disconnect_and_free_it, plugin);
+}
+
+