+ struct GNUNET_MESH_Handle *h = cls;
+ struct GNUNET_MESH_TransmitHandle *th;
+ struct GNUNET_MESH_TransmitHandle *next;
+ struct GNUNET_MESH_Tunnel *t;
+ char *cbuf = buf;
+ size_t tsize;
+ size_t psize;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Send packet() Buffer %u\n", size);
+ if ((0 == size) || (NULL == buf))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Received NULL send callback on %p\n", h);
+ reconnect (h);
+ h->th = NULL;
+ return 0;
+ }
+ tsize = 0;
+ next = h->th_head;
+ while ((NULL != (th = next)) && (size >= th->size))
+ {
+ t = th->tunnel;
+ if (GNUNET_YES == th_is_payload (th))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " payload\n");
+ if (t->max_pid < t->pid && GNUNET_NO == PID_OVERFLOW (t->pid, t->max_pid))
+ {
+ /* This tunnel is not ready to transmit yet, try next message */
+ next = th->next;
+ continue;
+ }
+ t->packet_size = 0;
+ if (t->tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV)
+ {
+ /* traffic to origin */
+ struct GNUNET_MESH_ToOrigin to;
+ struct GNUNET_MessageHeader *mh;
+
+ GNUNET_assert (size >= th->size);
+ mh = (struct GNUNET_MessageHeader *) &cbuf[sizeof (to)];
+ psize = th->notify (th->notify_cls, size - sizeof (to), mh);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " to origin, type %s\n",
+ GNUNET_MESH_DEBUG_M2S (ntohs (mh->type)));
+ if (psize > 0)
+ {
+ psize += sizeof (to);
+ GNUNET_assert (size >= psize);
+ to.header.size = htons (psize);
+ to.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN);
+ to.tid = htonl (t->tid);
+ // FIXME pid?
+ memset (&to.oid, 0, sizeof (struct GNUNET_PeerIdentity));
+ memset (&to.sender, 0, sizeof (struct GNUNET_PeerIdentity));
+ memcpy (cbuf, &to, sizeof (to));
+ }
+ }
+ else if (th->target == 0)
+ {
+ /* multicast */
+ struct GNUNET_MESH_Multicast mc;
+ struct GNUNET_MessageHeader *mh;
+
+ GNUNET_assert (size >= th->size);
+ mh = (struct GNUNET_MessageHeader *) &cbuf[sizeof (mc)];
+ psize = th->notify (th->notify_cls, size - sizeof (mc), mh);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " multicast, type %s\n",
+ GNUNET_MESH_DEBUG_M2S (ntohs (mh->type)));
+ if (psize > 0)
+ {
+ psize += sizeof (mc);
+ GNUNET_assert (size >= psize);
+ mc.header.size = htons (psize);
+ mc.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_MULTICAST);
+ mc.tid = htonl (t->tid);
+ mc.pid = htonl (t->pid);
+ mc.ttl = 0;
+ memset (&mc.oid, 0, sizeof (struct GNUNET_PeerIdentity));
+ memcpy (cbuf, &mc, sizeof (mc));
+ }
+ }
+ else
+ {
+ /* unicast */
+ struct GNUNET_MESH_Unicast uc;
+ struct GNUNET_MessageHeader *mh;
+
+ GNUNET_assert (size >= th->size);
+ mh = (struct GNUNET_MessageHeader *) &cbuf[sizeof (uc)];
+ psize = th->notify (th->notify_cls, size - sizeof (uc), mh);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " unicast, type %s\n",
+ GNUNET_MESH_DEBUG_M2S (ntohs (mh->type)));
+ if (psize > 0)
+ {
+ psize += sizeof (uc);
+ GNUNET_assert (size >= psize);
+ uc.header.size = htons (psize);
+ uc.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_UNICAST);
+ uc.tid = htonl (t->tid);
+ uc.pid = htonl (t->pid);
+ memset (&uc.oid, 0, sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_PEER_resolve (th->target, &uc.destination);
+ memcpy (cbuf, &uc, sizeof (uc));
+ }
+ }
+ t->pid++;
+ }
+ else
+ {
+ struct GNUNET_MessageHeader *mh = (struct GNUNET_MessageHeader *) &th[1];
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " mesh traffic, type %s\n",
+ GNUNET_MESH_DEBUG_M2S (ntohs (mh->type)));
+ memcpy (cbuf, &th[1], th->size);
+ psize = th->size;
+ }
+ if (th->timeout_task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel (th->timeout_task);
+ GNUNET_CONTAINER_DLL_remove (h->th_head, h->th_tail, th);
+ GNUNET_free (th);
+ next = h->th_head;
+ cbuf += psize;
+ size -= psize;
+ tsize += psize;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " total size: %u\n", tsize);
+ h->th = NULL;
+ size = message_ready_size (h);
+ if (0 != size)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " next size: %u\n", size);
+ h->th =
+ GNUNET_CLIENT_notify_transmit_ready (h->client, size,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_YES, &send_callback, h);
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " nothing left to transmit\n");
+ }
+ if (GNUNET_NO == h->in_receive)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " start receiving from service\n");
+ h->in_receive = GNUNET_YES;
+ GNUNET_CLIENT_receive (h->client, &msg_received, h,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Send packet() END\n");
+ return tsize;
+}
+
+
+/**
+ * Auxiliary function to send an already constructed packet to the service.
+ * Takes care of creating a new queue element, copying the message and
+ * calling the tmt_rdy function if necessary.
+ *
+ * @param h mesh handle
+ * @param msg message to transmit
+ * @param tunnel tunnel this send is related to (NULL if N/A)
+ */
+static void
+send_packet (struct GNUNET_MESH_Handle *h,
+ const struct GNUNET_MessageHeader *msg,
+ struct GNUNET_MESH_Tunnel *tunnel)
+{
+ struct GNUNET_MESH_TransmitHandle *th;
+ size_t msize;
+
+ msize = ntohs (msg->size);
+ th = GNUNET_malloc (sizeof (struct GNUNET_MESH_TransmitHandle) + msize);
+ th->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
+ th->size = msize;
+ th->tunnel = tunnel;
+ memcpy (&th[1], msg, msize);
+ add_to_queue (h, th);
+ if (NULL != h->th)
+ return;
+ h->th =
+ GNUNET_CLIENT_notify_transmit_ready (h->client, msize,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_YES, &send_callback, h);
+}
+
+
+/******************************************************************************/
+/********************** API CALL DEFINITIONS *************************/
+/******************************************************************************/
+
+/**
+ * Connect to the mesh service.
+ *
+ * @param cfg configuration to use
+ * @param cls closure for the various callbacks that follow
+ * (including handlers in the handlers array)
+ * @param new_tunnel function called when an *inbound* tunnel is created
+ * @param cleaner function called when an *inbound* tunnel is destroyed by the
+ * remote peer, it is *not* called if GNUNET_MESH_tunnel_destroy
+ * is called on the tunnel
+ * @param handlers callbacks for messages we care about, NULL-terminated
+ * note that the mesh is allowed to drop notifications about
+ * inbound messages if the client does not process them fast
+ * enough (for this notification type, a bounded queue is used)
+ * @param stypes list of the applications that this client claims to provide
+ * @return handle to the mesh service NULL on error
+ * (in this case, init is never called)
+ */
+struct GNUNET_MESH_Handle *
+GNUNET_MESH_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, void *cls,
+ GNUNET_MESH_InboundTunnelNotificationHandler new_tunnel,
+ GNUNET_MESH_TunnelEndHandler cleaner,
+ const struct GNUNET_MESH_MessageHandler *handlers,
+ const GNUNET_MESH_ApplicationType *stypes)
+{
+ struct GNUNET_MESH_Handle *h;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_MESH_connect()\n");
+ h = GNUNET_malloc (sizeof (struct GNUNET_MESH_Handle));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " addr %p\n", h);
+ h->cfg = cfg;
+ h->new_tunnel = new_tunnel;
+ h->cleaner = cleaner;
+ h->client = GNUNET_CLIENT_connect ("mesh", cfg);
+ if (h->client == NULL)
+ {
+ GNUNET_break (0);
+ GNUNET_free (h);
+ return NULL;
+ }
+ h->cls = cls;
+ /* FIXME memdup? */
+ h->applications = stypes;
+ h->message_handlers = handlers;
+ h->next_tid = GNUNET_MESH_LOCAL_TUNNEL_ID_CLI;
+ h->reconnect_time = GNUNET_TIME_UNIT_MILLISECONDS;
+ h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+
+ /* count handlers and apps, calculate size */
+ for (h->n_applications = 0; stypes[h->n_applications]; h->n_applications++) ;
+ for (h->n_handlers = 0; handlers[h->n_handlers].type; h->n_handlers++) ;
+ send_connect (h);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_MESH_connect() END\n");
+ return h;