+ * @param cls closure
+ * @param s which session must be used
+ * @param msgbuf the message to transmit
+ * @param msgbuf_size number of bytes in 'msgbuf'
+ * @param priority how important is the message (most plugins will
+ * ignore message priority and just FIFO)
+ * @param to how long to wait at most for the transmission (does not
+ * require plugins to discard the message after the timeout,
+ * just advisory for the desired delay; most plugins will ignore
+ * this as well)
+ * @param cont continuation to call once the message has
+ * been transmitted (or if the transport is ready
+ * for the next transmission call; or if the
+ * peer disconnected...); can be NULL
+ * @param cont_cls closure for cont
+ * @return number of bytes used (on the physical network, with overheads);
+ * -1 on hard errors (i.e. address invalid); 0 is a legal value
+ * and does NOT mean that the message was not transmitted (DV)
+ */
+static ssize_t
+udp_plugin_send (void *cls,
+ struct Session *s,
+ const char *msgbuf, size_t msgbuf_size,
+ unsigned int priority,
+ struct GNUNET_TIME_Relative to,
+ GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
+{
+ struct Plugin *plugin = cls;
+ size_t mlen = msgbuf_size + sizeof (struct UDPMessage);
+ struct UDPMessageWrapper * udpw;
+ struct UDPMessage *udp;
+ char mbuf[mlen];
+ GNUNET_assert (plugin != NULL);
+ GNUNET_assert (s != NULL);
+
+ if ((s->addrlen == sizeof (struct sockaddr_in6)) && (plugin->sockv6 == NULL))
+ return GNUNET_SYSERR;
+ if ((s->addrlen == sizeof (struct sockaddr_in)) && (plugin->sockv4 == NULL))
+ return GNUNET_SYSERR;
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains_value(plugin->sessions, &s->target.hashPubKey, s))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "UDP transmits %u-byte message to `%s' using address `%s'\n",
+ mlen,
+ GNUNET_i2s (&s->target),
+ GNUNET_a2s(s->sock_addr, s->addrlen));
+
+ /* Message */
+ udp = (struct UDPMessage *) mbuf;
+ udp->header.size = htons (mlen);
+ udp->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_MESSAGE);
+ udp->reserved = htonl (0);
+ udp->sender = *plugin->env->my_identity;
+
+ reschedule_session_timeout(s);
+ if (mlen <= UDP_MTU)
+ {
+ udpw = GNUNET_malloc (sizeof (struct UDPMessageWrapper) + mlen);
+ udpw->session = s;
+ udpw->udp = (char *) &udpw[1];
+ udpw->msg_size = mlen;
+ udpw->timeout = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), to);
+ udpw->cont = cont;
+ udpw->cont_cls = cont_cls;
+ udpw->frag_ctx = NULL;
+ memcpy (udpw->udp, udp, sizeof (struct UDPMessage));
+ memcpy (&udpw->udp[sizeof (struct UDPMessage)], msgbuf, msgbuf_size);
+
+ enqueue (plugin, udpw);
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "UDP has to fragment message \n");
+ if (s->frag_ctx != NULL)
+ return GNUNET_SYSERR;
+ memcpy (&udp[1], msgbuf, msgbuf_size);
+ struct FragmentationContext * frag_ctx = GNUNET_malloc(sizeof (struct FragmentationContext));
+
+ frag_ctx->plugin = plugin;
+ frag_ctx->session = s;
+ frag_ctx->cont = cont;
+ frag_ctx->cont_cls = cont_cls;
+ frag_ctx->timeout = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), to);
+ frag_ctx->bytes_to_send = mlen;
+ frag_ctx->frag = GNUNET_FRAGMENT_context_create (plugin->env->stats,
+ UDP_MTU,
+ &plugin->tracker,
+ s->last_expected_delay,
+ &udp->header,
+ &enqueue_fragment,
+ frag_ctx);
+
+ s->frag_ctx = frag_ctx;
+ }
+
+ if (s->addrlen == sizeof (struct sockaddr_in))
+ {
+ if (plugin->with_v4_ws == GNUNET_NO)
+ {
+ if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel(plugin->select_task);
+
+ plugin->select_task =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ plugin->rs_v4,
+ plugin->ws_v4,
+ &udp_plugin_select, plugin);
+ plugin->with_v4_ws = GNUNET_YES;
+ }
+ }
+ else if (s->addrlen == sizeof (struct sockaddr_in6))
+ {
+ if (plugin->with_v6_ws == GNUNET_NO)
+ {
+ if (plugin->select_task_v6 != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel(plugin->select_task_v6);
+
+ plugin->select_task_v6 =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ plugin->rs_v6,
+ plugin->ws_v6,
+ &udp_plugin_select_v6, plugin);
+ plugin->with_v6_ws = GNUNET_YES;
+ }
+ }
+
+ return mlen;
+}
+
+
+/**
+ * Our external IP address/port mapping has changed.
+ *
+ * @param cls closure, the 'struct LocalAddrList'
+ * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
+ * the previous (now invalid) one
+ * @param addr either the previous or the new public IP address
+ * @param addrlen actual lenght of the address
+ */
+static void
+udp_nat_port_map_callback (void *cls, int add_remove,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ struct Plugin *plugin = cls;
+ struct IPv4UdpAddress u4;
+ struct IPv6UdpAddress u6;
+ void *arg;
+ size_t args;
+
+ /* convert 'addr' to our internal format */
+ switch (addr->sa_family)
+ {
+ case AF_INET:
+ GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
+ u4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+ u4.u4_port = ((struct sockaddr_in *) addr)->sin_port;
+ arg = &u4;
+ args = sizeof (u4);
+ break;
+ case AF_INET6:
+ GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
+ memcpy (&u6.ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
+ sizeof (struct in6_addr));
+ u6.u6_port = ((struct sockaddr_in6 *) addr)->sin6_port;
+ arg = &u6;
+ args = sizeof (u6);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ /* modify our published address list */
+ plugin->env->notify_address (plugin->env->cls, add_remove, arg, args);
+}
+
+
+
+/**
+ * Message tokenizer has broken up an incomming message. Pass it on
+ * to the service.
+ *
+ * @param cls the 'struct Plugin'
+ * @param client the 'struct SourceInformation'
+ * @param hdr the actual message
+ */