text: /* For crypt */
  #define _GNU_SOURCE
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <string.h>
  #include <unistd.h>
  #include <fcntl.h>
  #include <sys/time.h>
  #include <arpa/inet.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netdb.h>
  #include <sys/signal.h>
  #include <pthread.h>
  #include <libp2p/crypto/sha256.h>
  #include <libp2p/routing/kademlia.h>
  #include <libp2p/routing/dht.h>
  #include <multiaddr/multiaddr.h>
  
  extern FILE *dht_debug;
  
  #define MAX_BOOTSTRAP_NODES 20
  static struct sockaddr_storage bootstrap_nodes[MAX_BOOTSTRAP_NODES];
  static int num_bootstrap_nodes = 0;
  
  struct bs_struct {
      char *ip;
      uint16_t port;
  } bootstrap_list[] = {
  };
  
  pthread_t pth_kademlia, pth_announce;
  time_t tosleep = 0;
  int kfd = -1;
  int net_family = 0;
  volatile int8_t searching = 0; // search lock, -1 to busy, 0 to free, 1 to running.
  volatile char hash[20];     // hash to be search or announce.
  volatile uint16_t announce_port = 0;
  volatile int8_t closing = 0;
  
  #define ANNOUNCE_WAIT_TIME		(28 * 60) // Wait 28 minutes.
  #define ANNOUNCE_WAIT_TOLERANCE		60
  struct announce_struct {
      unsigned char hash[sizeof hash];
      uint16_t port;
      unsigned int time;
      struct announce_struct *next;
  } *announce_list = NULL;
  
  #define DHT_MAX_IPV4	50
  #define DHT_MAX_IPV6	10
  
  struct ipv4_struct {
      uint32_t ip;
      uint16_t port;
  };
  
  struct ipv6_struct {
      uint8_t  ip[16];
      uint16_t port;
  };
  
  struct search_struct {
      unsigned char hash[sizeof hash];
      uint8_t ipv4_count;
      uint8_t ipv6_count;
      struct ipv4_struct ipv4[DHT_MAX_IPV4];
      struct ipv6_struct ipv6[DHT_MAX_IPV6];
      struct search_struct *next;
  } *search_result = NULL;
  
  /***
   * The call-back function is called by the DHT whenever something
   * interesting happens.  Right now, it only happens when we get a new value or
   * when a search completes, but this may be extended in future versions.
   * @param closure
   * @param event the event
   * @param info_hash the hash to work with
   * @param data the new value to be added
   * @param data_len the length of the data
   */
  static void callback(void *closure, int event, const unsigned char *info_hash, const void *data, size_t data_len) {
      struct search_struct *sp, *rp = NULL; // struct pointer and result pointer
  
      switch (event) {
          case DHT_EVENT_VALUES:
          case DHT_EVENT_VALUES6:
              if (dht_debug) {
                  fprintf(dht_debug, "Received %d values.\n", (int)(data_len / 6));
              }
              // Find the item in the list.
              for (rp = search_result ; rp ; rp = rp->next) {
                  if (memcmp(rp->hash, info_hash, sizeof hash) == 0) { // Found.
                      int i;
                      if (event == DHT_EVENT_VALUES) { // IPv4
                          struct ipv4_struct ipv4;
                          if (rp->ipv4_count == DHT_MAX_IPV4) { // Full
                              return;
                          }
                          // Make sure the data is in struct format.
                          memcpy(&ipv4.ip, data, 4);
                          memcpy(&ipv4.port, data+4, 2);
                          ipv4.port = ntohs(ipv4.port);
                          for (i = 0 ; i < rp->ipv4_count ; i++) {
                              if (memcmp(&rp->ipv4[i], &ipv4, sizeof ipv4) == 0) {
                                  return; // Alread in the list.
                              }
                          }
                          // Not in the list, then add.
                          memcpy(&rp->ipv4[rp->ipv4_count], &ipv4, sizeof ipv4);
                          rp->ipv4_count++;
                      } else { // IPv6
                          struct ipv6_struct ipv6;
                          if (rp->ipv6_count == DHT_MAX_IPV6) { // Full
                              return;
                          }
                          // Make sure the data is in struct format.
                          memcpy(&ipv6.ip, data, 16);
                          memcpy(&ipv6.port, data+16, 2);
                          for (i = 0 ; i < rp->ipv6_count ; i++) {
                              if (memcmp(&rp->ipv6[i], &ipv6, sizeof ipv6) == 0) {
                                  return; // Alread in the list.
                              }
                          }
                          // Not in the list, then add.
                          memcpy(&rp->ipv6[rp->ipv6_count], &ipv6, sizeof ipv6);
                          rp->ipv6_count++;
                      }
                      return;
                  }
              }
              break; // Not found, how can EVENT_VALUE may occur before SEARCH_DONE?
          case DHT_EVENT_SEARCH_DONE:
          case DHT_EVENT_SEARCH_DONE6:
              if (dht_debug) {
                  fprintf(dht_debug, "Search done.\n");
              }
              if (search_result) {
                  // Try to find the item in the list.
                  for (sp = search_result ; sp->next ; sp = sp->next) {
                      if (memcmp(sp->hash, info_hash, sizeof hash) == 0) { // Found.
                          rp = sp;
                          break;
                      }
                  }
                  if (!rp) {
                      rp = malloc(sizeof(struct search_struct));
                      if (!rp) return; // Abort, out of memory.
                      memset(rp, 0, sizeof(struct search_struct));
                      memcpy(rp->hash, info_hash, sizeof hash);
                      sp->next = rp; // Insert in the list.
                  }
              } else {
                  rp = malloc(sizeof(struct search_struct));
                  if (!rp) return; // Abort, out of memory.
                  memset(rp, 0, sizeof(struct search_struct));
                  memcpy(rp->hash, info_hash, sizeof hash);
                  search_result = rp; // Insert first item in the list.
              }
              break;
          default:
              break;
      }
  }
  
  int start_kademlia_multiaddress(struct MultiAddress* address, char* peer_id, int timeout, struct Libp2pVector* bootstrap_addresses) {
  	int port = multiaddress_get_ip_port(address);
  	int family = multiaddress_get_ip_family(address);
  	int fd = socket(family, SOCK_DGRAM, 0);
  	if (fd < 0)
  		return 0;
  	struct sockaddr_in serv_addr;
  	serv_addr.sin_family = family;
  	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  	serv_addr.sin_port = port;
  	if (bind(fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0)
  		return 0;
  	return start_kademlia(fd, family, peer_id, timeout, bootstrap_addresses);
  }
  
  /***
   * Start the kademlia service
   * @param net_fd the file descriptor of the address/socket already bound
   * @param family ip4 or ip6
   * @param peer_id the first 20 chars of the public PeerID in a null terminated string
   * @param timeout seconds before a select() timeout
   */
  int start_kademlia(int net_fd, int family, char* peer_id, int timeout, struct Libp2pVector* bootstrap_addresses)
  {
      int rc, i, len;
      unsigned char id[sizeof hash];
      struct sockaddr_in sa;
  
      dht_debug = stderr;
  
      len = bootstrap_addresses->total;
  
      if (len > MAX_BOOTSTRAP_NODES) {
          len = MAX_BOOTSTRAP_NODES; // limit array length
      }
  
      memset(&sa, 0, sizeof sa);
      char* ip = NULL;
      for (i = 0 ; i < len ; i++) {
      	struct MultiAddress* addr = (struct MultiAddress*)libp2p_utils_vector_get(bootstrap_addresses, i);
      	if (ip != NULL)
      		free(ip);
      	if (multiaddress_is_ip(addr)) {
      		multiaddress_get_ip_address(addr, &ip);
      		if (family == AF_INET6 && multiaddress_is_ip6(addr)) {
      			if (inet_pton(AF_INET6, ip, &(sa.sin_addr.s_addr)) == 1)
      				sa.sin_family = AF_INET6;
      			else
      				continue;
      		} else {
      			if (inet_pton(AF_INET , ip, &(sa.sin_addr.s_addr)) == 1)
  					sa.sin_family = AF_INET;
      			else
      				continue;
      		}
      	} else {
              continue; // not an ipv6 or ipv4?
          }
  
          sa.sin_port = htons (multiaddress_get_ip_port(addr));
  
          memcpy(&bootstrap_nodes[num_bootstrap_nodes++], &sa, sizeof(sa));
      }
      if (ip != NULL)
      	free(ip);
  
      dht_hash (id, sizeof(id), peer_id, strlen(peer_id), NULL, 0, NULL, 0);
  
      if (family == AF_INET6) {
          rc = dht_init(-1, net_fd, id, NULL);
      } else {
          rc = dht_init(net_fd, -1, id, NULL);
      }
      if (rc < 0) {
          return rc;
      }
  
      /* For bootstrapping, we need an initial list of nodes.  This could be
         hard-wired, but can also be obtained from the nodes key of a torrent
         file, or from the PORT bittorrent message.
  
         Dht_ping_node is the brutal way of bootstrapping -- it actually
         sends a message to the peer.  If you're going to bootstrap from
         a massive number of nodes (for example because you're restoring from
         a dump) and you already know their ids, it's better to use
         dht_insert_node.  If the ids are incorrect, the DHT will recover. */
      for(i = 0; i < num_bootstrap_nodes; i++) {
      	// for debugging
      	int retVal =
          dht_ping_node((struct sockaddr*)&bootstrap_nodes[i],
                        sizeof (bootstrap_nodes[i]));
      	fprintf(stderr, "ping returned %d\n", retVal);
          usleep(random() % 100000);
      }
  
      // TODO: Read cache nodes from file and load using dht_insert_node.
  
      kfd = net_fd;
      net_family = family;
      tosleep = timeout;
  
      rc = pthread_create(&pth_kademlia, NULL, kademlia_thread, NULL);
      if (rc) {
          return rc; // error
      }
  
      return pthread_create(&pth_announce, NULL, announce_thread, NULL);
  }
  
  void stop_kademlia (void)
  {
      if (kfd != -1) {
          closing = 1;
  
          pthread_cancel(pth_announce);
  
          // Wait kademlia_thread finish.
          pthread_join(pth_kademlia, NULL);
  
          dht_uninit();
  
          close (kfd);
          kfd = -1;
      }
  }
  
  void *kademlia_thread (void *ptr)
  {
      int rc;
      struct timeval tv;
      fd_set readfds;
      char buf[4096];
      struct sockaddr from;
      socklen_t fromlen;
  
      for(;;) {
          tv.tv_sec = tosleep;
          tv.tv_usec = random() % 1000000;
  
          FD_ZERO(&readfds);
          FD_SET(kfd, &readfds);
          rc = select(kfd + 1, &readfds, NULL, NULL, &tv);
          if(rc < 0) {
              if(errno != EINTR) {
                  perror("select");
                  sleep(1);
              }
          }
  
          if(rc > 0 && FD_ISSET(kfd, &readfds)) {
              fromlen = sizeof(from);
              rc = recvfrom(kfd, buf, sizeof(buf) - 1, 0,
                            (struct sockaddr*)&from, &fromlen);
              if (rc < 0) {
              	if (errno == EAGAIN || errno == EWOULDBLOCK) {
              		continue;
              	} else {
              		fprintf(stderr, "kademlia_thread:recvfrom failed with %d\n", errno);
              		continue;
              	}
              }
              buf[rc] = '\0';
              rc = dht_periodic(buf, rc, (struct sockaddr*)&from, fromlen,
                                &tosleep, callback, NULL);
          } else {
              rc = dht_periodic(NULL, 0, NULL, 0, &tosleep, callback, NULL);
          }
          if(rc < 0) {
              if(errno == EINTR) {
                  continue;
              } else {
                  perror("dht_periodic");
                  if(rc == EINVAL || rc == EFAULT)
                      abort();
                  tosleep = 1;
              }
          }
  
          /* This is how you trigger a search for a torrent hash.  If port
             (the second argument) is non-zero, it also performs an announce.
             Since peers expire announced data after 30 minutes, it's a good
             idea to reannounce every 28 minutes or so. */
          if(searching > 0) {
              unsigned char h[sizeof hash];
              int i;
              for (i = 0 ; i < sizeof hash ; i++) {
                  h[i] = hash[i]; // Copy hash array to new array so can call
                                  // dht_search without volatile variable.
              }
              dht_search(h, announce_port, net_family, callback, NULL);
              searching = 0;
          }
          if(closing) {
              // TODO: Create a routine to save the cache nodes in the file sometimes and before closing.
              return 0; // end thread.
          }
      }
      return (void*)1;
  }
  
  /**
   * Search for a hash
   * @param id the hash to look for
   * @param port the port if it is available
   * @param to the time out
   * @returns the time left
   */
  int search_kademlia_internal (unsigned char* id, int port, int to)
  {
      int i;
  
      while (searching != 0) {
          i = random() % 100000;
          if (i > to) {
              return 0; // timeout waiting a chance
          }
          usleep(i);
          to -= i;
      }
  
      searching = -1; // lock.
  
      for (i = 0 ; i < sizeof hash ; i++) {
          hash[i] = id[i];
      }
  
      announce_port = port;
  
      searching = 1; // search.
  
      return to;
  }
  
  void *announce_thread (void *ptr)
  {
      unsigned int wait;
      struct announce_struct *n, *p;
  
      for(;;) {
          if (announce_list) {
              unsigned int now, minus_time = ((unsigned int) -1);
  
              p = NULL;
              // find max wait value.
              for (n = announce_list ; n ; n = n->next) {
                  if (n->time < minus_time) {
                      minus_time = n->time;
                      p = n;
                  }
              }
              if (p) {
                  now = time(NULL);
                  if ((minus_time + ANNOUNCE_WAIT_TIME) > (now + ANNOUNCE_WAIT_TOLERANCE)) {
                      wait = ANNOUNCE_WAIT_TIME - (now - minus_time);
                      sleep (wait);
                  } else {
                      if (p) {
                          search_kademlia_internal (p->hash, p->port, ANNOUNCE_WAIT_TOLERANCE * 1000000);
                          p->time = time(NULL);
                      }
                  }
                  continue;
              }
          }
          // Empty list, just wait.
          sleep (ANNOUNCE_WAIT_TIME);
      }
      return (void*)1;
  }
  
  int announce_kademlia (char* peer_id, uint16_t port)
  {
      unsigned char id[sizeof hash];
      struct announce_struct *n, *p;
  
      dht_hash (id, sizeof(id), peer_id, strlen(peer_id), NULL, 0, NULL, 0);
  
      for (n = announce_list ; n ; n = n->next) {
          if (memcmp(n->hash, id, sizeof id) == 0) {
              return 0; // Already on the list.
          }
          if (! (n->next)) {
              break; // Keep n->next at the insertion point.
          }
      }
  
  
      if ((p = malloc (sizeof(struct announce_struct))) == NULL) {
          return 0; // Fail to alloc.
      }
  
      search_kademlia_internal (id, port, ANNOUNCE_WAIT_TOLERANCE * 1000000);
  
      memcpy(p->hash, id, sizeof id);
      p->port = port;
      p->next = NULL;
      p->time = time(NULL);
  
      if (!announce_list) {
          announce_list = p;
      } else {
          n->next = p;
      }
  
      return 1; // Announced and added to the list.
  }
  
  struct MultiAddress** search_kademlia(char* peer_id, int timeout)
  {
      unsigned char id[sizeof hash];
      int i, to = timeout * 1000000;
      struct search_struct *rp; // result pointer
      struct MultiAddress **ret;
  
      if (kfd == -1) {
          return NULL; // start thread first.
      }
  
      dht_hash (id, sizeof(id), peer_id, strlen(peer_id), NULL, 0, NULL, 0);
  
      //TODO: Is this the right place to ask the net?
      dht_search((unsigned char*)peer_id, 0, AF_INET, NULL, NULL);
      to = search_kademlia_internal (id, 0, to);
      if (to == 0) return NULL; // time out.
  
      // Wait for search completion.
      for(;;) {
          i = random() % 100000;
          if (i > to) {
              return NULL; // timeout.
          }
          usleep(i);
          for (rp = search_result ; rp ; rp = rp->next) {
              if (memcmp(rp->hash, id, sizeof hash) == 0) { // Found.
                  char ipstr[INET6_ADDRSTRLEN + 1];
                  char str[sizeof ipstr + 16];
                  int c = 0;
  
                  // Wait for result or time out.
                  while (to > 0 &&
                         search_result->ipv4_count == 0 &&
                         search_result->ipv6_count == 0) {
                           to = search_kademlia_internal (id, 0, to); // Repeat search to collect result.
                           usleep(2000000); // Wait a few seconds for the result.
                           to -= 2000000;
                  }
  
                  if (search_result->ipv4_count == 0 ||
                      search_result->ipv6_count == 0) return NULL; // no result.
  
                  ret = calloc(search_result->ipv4_count + search_result->ipv6_count + 1, // IPv4 + IPv6 itens and a NULL terminator.
                               sizeof (struct MultiAddress*)); // array of pointer.
                  if (!ret) {
                      return NULL;
                  }
  
                  for (i = 0 ; i < search_result->ipv4_count ; i++) {
                      if (inet_ntop(AF_INET, &search_result->ipv4[i].ip, ipstr, sizeof ipstr)) {
                          snprintf (str, sizeof str, "/ip4/%s/tcp/%d", ipstr, search_result->ipv4[i].port);
                          if (dht_debug) {
                              fprintf(dht_debug, "SEARCH %s (%d) = %s\n", peer_id, c, str);
                          }
                          ret[c] = multiaddress_new_from_string (str);
                          if (ret[c] > 0) { // Sucess.
                              c++;
                          }
                      }
                  }
                  for (i = 0 ; i < search_result->ipv6_count ; i++) {
                      if (inet_ntop(AF_INET6, search_result->ipv6[i].ip, ipstr, sizeof ipstr)) {
                          snprintf (str, sizeof str, "/ip6/%s/tcp/%d", ipstr, search_result->ipv6[i].port);
                          if (dht_debug) {
                              fprintf(dht_debug, "SEARCH %s (%d) = %s\n", peer_id, c, str);
                          }
                          ret[c] = multiaddress_new_from_string (str);
                          if (ret[c] > 0) { // Sucess.
                              c++;
                          }
                      }
                  }
                  ret[c] = NULL; // NULL terminator.
                  return ret;
              }
          }
          to -= i;
      }
  
      return NULL;
  }
  
  int ping_kademlia (char *ip, uint16_t port)
  {
      struct sockaddr_in sa;
  
      if (inet_pton(AF_INET6, ip, &(sa.sin_addr.s_addr)) == 1) {
          sa.sin_family = AF_INET6;
      } if (inet_pton(AF_INET, ip, &(sa.sin_addr.s_addr)) == 1) {
          sa.sin_family = AF_INET;
      } else {
          return 0;
      }
  
      sa.sin_port = htons (port);
  
      dht_ping_node((struct sockaddr*)&sa, sizeof sa);
      //usleep(random() % 100000);
  
      return 1;
  }
  
  /* Functions called by the DHT. */
  
  int dht_blacklisted (const struct sockaddr *sa, int salen)
  {
      return 0;
  }
  
  /* We need to provide a reasonably strong cryptographic hashing function.
     libp2p_crypto_hashing_sha256 */
  void dht_hash (void *hash_return, int hash_size,
                 const void *v1, int len1,
                 const void *v2, int len2,
                 const void *v3, int len3)
  {
      int len = len1 + len2 + len3;
      unsigned char *in, out[32];
  
      if (!hash_return || hash_size==0 || len==0) {
          return; // invalid param.
      }
  
      in = malloc (len);
  
      if (in) {
          memcpy(in, v1, len1);
          memcpy(in+len1, v2, len2);
          memcpy(in+len1+len2, v3, len3);
          if ( libp2p_crypto_hashing_sha256 (in, len, out) ) {
              if (hash_size > sizeof(out)) {
                  memset ((char*)hash_return + sizeof(out), 0, hash_size - sizeof(out));
                  hash_size = sizeof(out);
              }
              memcpy(hash_return, out, hash_size);
          }
          free (in);
      }
  }
  
  int dht_random_bytes (void *buf, size_t size)
  {
      int fd, rc = 0, save;
      size_t len = 0;
  
      fd = open("/dev/urandom", O_RDONLY);
      if (fd < 0) {
          return fd;
      }
  
      while (len < size) {
          rc = read (fd, buf + len, size - len);
          if (rc < 0) {
              if (errno == EINTR || errno == EAGAIN) {
                  continue; // not fatal, try again.
              }
              if (errno == EWOULDBLOCK && len > 0) {
                  rc = len;
              }
  
              break; // fail.
          }
          len += rc;
      }
  
      if (len > 0 && rc >= 0) {
          rc = len;
      }
  
      save = errno;
      close(fd);
      errno = save;
  
      return rc;
  }
  