+ struct RecordLookupHandle* rlh = (struct RecordLookupHandle*) cls;
+
+ if (rd_count == 0)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_VPN-%llu: VPN returned no records. (status: %d)!\n",
+ rh->id,
+ rh->status);
+ /* give up, cannot resolve */
+ finish_lookup(rh, rlh, 0, NULL);
+ return;
+ }
+
+ /* results found yay */
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_VPN-%llu: Record resolved from VPN!", rh->id);
+
+ finish_lookup(rh, rlh, rd_count, rd);
+}
+
+
+/**
+ * Sends a UDP dns query to a nameserver specified in the rh
+ *
+ * @param rh the resolver handle
+ */
+static void
+send_dns_packet (struct ResolverHandle *rh);
+
+
+static void
+handle_dns_resolver (void *cls,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct ResolverHandle *rh = cls;
+ struct RecordLookupHandle *rlh = rh->proc_cls;
+ struct GNUNET_NAMESTORE_RecordData rd;
+ struct sockaddr_in *sai;
+ struct sockaddr_in6 *sai6;
+
+ if (NULL == addr)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No address found in DNS!\n");
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ if (addrlen == sizeof (struct sockaddr_in))
+ {
+ sai = (struct sockaddr_in*) addr;
+ rd.record_type = GNUNET_GNS_RECORD_A;
+ rd.data_size = sizeof (struct in_addr);
+ rd.data = &sai->sin_addr;
+ }
+ else
+ {
+ sai6 = (struct sockaddr_in6*) addr;
+ rd.record_type = GNUNET_GNS_RECORD_AAAA;
+ rd.data_size = sizeof (struct in6_addr);
+ rd.data = &sai6->sin6_addr;
+ }
+
+ rd.expiration_time = UINT64_MAX; /* FIXME: should probably pick something shorter */
+ rd.flags = 0;
+
+ finish_lookup (rh, rlh, 1, &rd);
+
+}
+
+/**
+ * Resolve DNS name via local stub resolver
+ *
+ * @param rh the resolver handle
+ */
+static void
+resolve_dns_name (struct ResolverHandle *rh)
+{
+ struct RecordLookupHandle *rlh = rh->proc_cls;
+ int af;
+
+ if ((rlh->record_type != GNUNET_GNS_RECORD_A) &&
+ (rlh->record_type != GNUNET_GNS_RECORD_AAAA))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Can only resolve A/AAAA via stub... abort\n");
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ if (rlh->record_type == GNUNET_GNS_RECORD_A)
+ af = AF_INET;
+ else
+ af = AF_INET6;
+
+ //GNUNET_RESOLVER_connect (cfg); FIXME into init
+
+ rh->dns_resolver_handle = GNUNET_RESOLVER_ip_get (rh->dns_name,
+ af,
+ rh->timeout,
+ &handle_dns_resolver,
+ rh);
+}
+
+
+/**
+ * Read DNS udp packet from socket
+ *
+ * @param cls the resolver handle
+ * @param tc task context
+ */
+static void
+read_dns_response (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ResolverHandle *rh = cls;
+ struct RecordLookupHandle *rlh = rh->proc_cls;
+ char buf[UINT16_MAX];
+ ssize_t r;
+ struct sockaddr_in addr;
+ socklen_t addrlen;
+ struct GNUNET_DNSPARSER_Packet *packet;
+ struct GNUNET_NAMESTORE_RecordData rd;
+ int found_delegation = GNUNET_NO;
+ int found_cname = GNUNET_NO;
+ char* delegation_name = NULL;
+ int zone_offset = 0;
+ int i;
+
+ rh->dns_read_task = GNUNET_SCHEDULER_NO_TASK;
+ if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
+ {
+ /* timeout or shutdown */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Terminating DNS query %d\n", tc->reason);
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ addrlen = sizeof (addr);
+ r = GNUNET_NETWORK_socket_recvfrom (rh->dns_sock,
+ buf, sizeof (buf),
+ (struct sockaddr*) &addr,
+ &addrlen);
+
+ if (-1 == r)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ packet = GNUNET_DNSPARSER_parse (buf, r);
+
+ if (NULL == packet)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to parse DNS reply!\n");
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ for (i = 0; i < packet->num_answers; i++)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got record type %d (want %d)\n",
+ packet->answers[i].type,
+ rlh->record_type);
+ /* http://tools.ietf.org/html/rfc1034#section-3.6.2 */
+ if (packet->answers[i].type == GNUNET_GNS_RECORD_CNAME)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "CNAME... restarting query with %s\n",
+ packet->answers[i].data.hostname
+ );
+ strcpy (rh->dns_name, packet->answers[i].data.hostname);
+ found_cname = GNUNET_YES;
+ //send_dns_packet (rh);
+ //GNUNET_DNSPARSER_free_packet (packet);
+ continue;
+ }
+
+ if ((packet->answers[i].type == rlh->record_type) &&
+ (0 == strcmp (packet->answers[i].name, rh->dns_name)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Found record!\n");
+ rd.data = packet->answers[i].data.raw.data;
+ rd.data_size = packet->answers[i].data.raw.data_len;
+ rd.record_type = packet->answers[i].type;
+ rd.flags = 0;
+ rd.expiration_time = packet->answers[i].expiration_time.abs_value;
+ finish_lookup (rh, rlh, 1, &rd);
+ GNUNET_DNSPARSER_free_packet (packet);
+ return;
+ }
+ }
+
+ if (GNUNET_YES == found_cname)
+ {
+ zone_offset = strlen (rh->dns_name) - strlen (rh->dns_zone) - 1;
+
+ if (0 > zone_offset)
+ zone_offset = 0;
+
+ /* restart query with CNAME */
+ if (0 == strcmp (rh->dns_name+zone_offset, rh->dns_zone))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Asking same server for %s\n", rh->dns_name);
+ send_dns_packet (rh);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying system resolver for %s\n", rh->dns_name);
+ resolve_dns_name (rh);
+ }
+
+ GNUNET_DNSPARSER_free_packet (packet);
+ return;
+ }
+
+ for (i = 0; i < packet->num_authority_records; i++)
+ {
+
+ if (packet->authority_records[i].type == GNUNET_GNS_RECORD_NS)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Found NS delegation!\n");
+ found_delegation = GNUNET_YES;
+ delegation_name = packet->authority_records[i].data.hostname;
+ break;
+ }
+ }
+
+ for (i = 0; i < packet->num_additional_records; i++)
+ {
+ if (found_delegation == GNUNET_NO)
+ break;
+
+ if ((packet->additional_records[i].type == GNUNET_GNS_RECORD_A) &&
+ (0 == strcmp (packet->additional_records[i].name, delegation_name)))
+ {
+ GNUNET_assert (sizeof (struct in_addr) ==
+ packet->authority_records[i].data.raw.data_len);
+
+ rh->dns_addr.sin_addr =
+ *((struct in_addr*)packet->authority_records[i].data.raw.data);
+ send_dns_packet (rh);
+ GNUNET_DNSPARSER_free_packet (packet);
+ return;
+ }
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Nothing useful in DNS reply!\n");
+ finish_lookup (rh, rlh, 0, NULL);
+ GNUNET_DNSPARSER_free_packet (packet);
+ return;
+}
+
+/**
+ * Sends a UDP dns query to a nameserver specified in the rh
+ *
+ * @param rh the request handle
+ */
+static void
+send_dns_packet (struct ResolverHandle *rh)
+{
+ struct GNUNET_NETWORK_FDSet *rset = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_set (rset, rh->dns_sock);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending %dbyte DNS query\n",
+ rh->dns_raw_packet_size);
+
+ GNUNET_NETWORK_socket_sendto (rh->dns_sock,
+ rh->dns_raw_packet,
+ rh->dns_raw_packet_size,
+ (struct sockaddr*)&rh->dns_addr,
+ sizeof (struct sockaddr_in));
+
+ rh->dns_read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ rh->timeout, //FIXME less?
+ rset,
+ NULL,
+ &read_dns_response,
+ rh);
+
+ GNUNET_NETWORK_fdset_destroy (rset);
+
+}
+
+/**
+ * The final phase of resoution.
+ * We found a NS RR and want to resolve via DNS
+ *
+ * @param rh the pending lookup handle
+ * @param rd_count length of record data
+ * @param rd record data containing VPN RR
+ */
+static void
+resolve_record_dns (struct ResolverHandle *rh,
+ int rd_count,
+ const struct GNUNET_NAMESTORE_RecordData *rd)
+{
+ struct GNUNET_DNSPARSER_Query query;
+ struct GNUNET_DNSPARSER_Packet packet;
+ struct GNUNET_DNSPARSER_Flags flags;
+ struct in_addr dnsip;
+ struct sockaddr_in addr;
+ struct sockaddr *sa;
+ int i;
+ struct RecordLookupHandle *rlh = rh->proc_cls;
+
+ memset (&packet, 0, sizeof (struct GNUNET_DNSPARSER_Packet));
+
+ /* We cancel here as to not include the ns lookup in the timeout */
+ if (rh->timeout_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel(rh->timeout_task);
+ rh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ /* Start shortening */
+ if ((rh->priv_key != NULL) &&
+ (is_canonical (rh->name) == GNUNET_YES))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_DNS-%llu: Trying to shorten authority chain\n",
+ rh->id);
+ start_shorten (rh->authority_chain_tail,
+ rh->priv_key);
+ }
+
+ for (i = 0; i < rd_count; i++)
+ {
+ /* Synthesize dns name */
+ if (rd[i].record_type == GNUNET_GNS_RECORD_NS)
+ {
+ strcpy (rh->dns_zone, (char*)rd[i].data);
+ if (0 == strcmp (rh->name, ""))
+ strcpy (rh->dns_name, (char*)rd[i].data);
+ else
+ sprintf (rh->dns_name, "%s.%s", rh->name, (char*)rd[i].data);
+ }
+ /* The glue */
+ if (rd[i].record_type == GNUNET_GNS_RECORD_A)
+ dnsip = *((struct in_addr*)rd[i].data);
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_DNS-%llu: Looking up %s from %s\n",
+ rh->id,
+ rh->dns_name,
+ inet_ntoa (dnsip));
+ rh->dns_sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0);
+ if (rh->dns_sock == NULL)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_DNS-%llu: Error creating udp socket for dns!\n",
+ rh->id);
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ memset (&addr, 0, sizeof (struct sockaddr_in));
+ sa = (struct sockaddr *) &addr;
+ sa->sa_family = AF_INET;
+ if (GNUNET_OK != GNUNET_NETWORK_socket_bind (rh->dns_sock,
+ sa,
+ sizeof (struct sockaddr_in)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_DNS-%llu: Error binding udp socket for dns!\n",
+ rh->id);
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ /*TODO create dnsparser query, serialize, sendto, handle reply*/
+ query.name = rh->dns_name;
+ query.type = rlh->record_type;
+ query.class = GNUNET_DNSPARSER_CLASS_INTERNET;
+ memset (&flags, 0, sizeof (flags));
+ flags.recursion_desired = 1;
+ flags.checking_disabled = 1;
+ packet.queries = &query;
+ packet.answers = NULL;
+ packet.authority_records = NULL;
+ packet.num_queries = 1;
+ packet.num_answers = 0;
+ packet.num_authority_records = 0;
+ packet.num_additional_records = 0;
+ packet.flags = flags;
+ packet.id = rh->id;
+ if (GNUNET_OK != GNUNET_DNSPARSER_pack (&packet,
+ UINT16_MAX,
+ &rh->dns_raw_packet,
+ &rh->dns_raw_packet_size))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_DNS-%llu: Creating raw dns packet!\n",
+ rh->id);
+ GNUNET_NETWORK_socket_close (rh->dns_sock);
+ finish_lookup (rh, rlh, 0, NULL);
+ return;
+ }
+
+ rh->dns_addr.sin_family = AF_INET;
+ rh->dns_addr.sin_port = htons (53); //domain
+ rh->dns_addr.sin_addr = dnsip;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ rh->dns_addr.sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+
+ send_dns_packet (rh);
+}
+
+
+/**
+ * The final phase of resoution.
+ * We found a VPN RR and want to request an IPv4/6 address
+ *
+ * @param rh the pending lookup handle
+ * @param rd_count length of record data
+ * @param rd record data containing VPN RR
+ */
+static void
+resolve_record_vpn (struct ResolverHandle *rh,
+ int rd_count,
+ const struct GNUNET_NAMESTORE_RecordData *rd)
+{
+ struct RecordLookupHandle *rlh = rh->proc_cls;
+ struct GNUNET_HashCode serv_desc;
+ struct vpn_data* vpn;
+ int af;
+
+ /* We cancel here as to not include the ns lookup in the timeout */
+ if (rh->timeout_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel(rh->timeout_task);
+ rh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ /* Start shortening */
+ if ((rh->priv_key != NULL) &&
+ (is_canonical (rh->name) == GNUNET_YES))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_VPN-%llu: Trying to shorten authority chain\n",
+ rh->id);
+ start_shorten (rh->authority_chain_tail,
+ rh->priv_key);
+ }
+
+ vpn = (struct vpn_data*)rd->data;
+
+
+ GNUNET_CRYPTO_hash ((char*)&vpn[1],
+ strlen ((char*)&vpn[1]) + 1,
+ &serv_desc);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_VPN-%llu: proto %hu peer %s!\n",
+ rh->id,
+ ntohs (vpn->proto),
+ GNUNET_h2s (&vpn->peer));
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_VPN-%llu: service %s -> %s!\n",
+ rh->id,
+ (char*)&vpn[1],
+ GNUNET_h2s (&serv_desc));
+ rh->proc = &handle_record_vpn;
+
+ if (NULL == vpn_handle)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC_VPN-%llu: VPN not connected!\n",
+ rh->id);
+ finish_lookup (rh, rh->proc_cls, 0, NULL);
+ return;
+ }
+
+ if (rlh->record_type == GNUNET_GNS_RECORD_A)
+ af = AF_INET;
+ else
+ af = AF_INET6;
+
+ //FIXME timeout??
+ if (NULL == vpn_handle)
+ {
+ vpn_handle = GNUNET_VPN_connect (cfg);
+ if (NULL == vpn_handle)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_INIT: Error connecting to VPN!\n");
+ finish_lookup (rh, rh->proc_cls, 0, NULL);
+ return;
+ }
+ }
+
+
+ rh->vpn_handle = GNUNET_VPN_redirect_to_peer (vpn_handle,
+ af, ntohs (vpn->proto),
+ (struct GNUNET_PeerIdentity *)&vpn->peer,
+ &serv_desc,
+ GNUNET_NO, //nac
+ GNUNET_TIME_UNIT_FOREVER_ABS, //FIXME
+ &process_record_result_vpn,
+ rh);
+
+}
+
+/**
+ * The final phase of resolution.
+ * rh->name is a name that is canonical and we do not have a delegation.
+ * Query namestore for this record
+ *
+ * @param rh the pending lookup handle
+ */
+static void
+resolve_record_ns(struct ResolverHandle *rh)
+{
+ struct RecordLookupHandle *rlh = (struct RecordLookupHandle *)rh->proc_cls;
+
+ /* We cancel here as to not include the ns lookup in the timeout */
+ if (rh->timeout_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel(rh->timeout_task);
+ rh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ /* Start shortening */
+ if ((rh->priv_key != NULL) &&
+ (is_canonical (rh->name) == GNUNET_YES))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "GNS_PHASE_REC-%llu: Trying to shorten authority chain\n",
+ rh->id);
+ start_shorten (rh->authority_chain_tail,
+ rh->priv_key);
+ }
+
+ /**
+ * Try to resolve this record in our namestore.
+ * The name to resolve is now in rh->authority_name
+ * since we tried to resolve it to an authority
+ * and failed.
+ **/
+ rh->namestore_task = GNUNET_NAMESTORE_lookup_record(namestore_handle,
+ &rh->authority,
+ rh->name,
+ rlh->record_type,
+ &process_record_result_ns,
+ rh);
+}
+
+
+
+/**
+ * Handle timeout for DHT requests
+ *
+ * @param cls the request handle as closure
+ * @param tc the task context
+ */
+static void
+dht_authority_lookup_timeout(void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ResolverHandle *rh = cls;
+ struct RecordLookupHandle *rlh = rh->proc_cls;