+
+ /* we're done writing, continue with state machine! */
+
+ switch (s5r->state)
+ {
+ case SOCKS5_INIT:
+ GNUNET_assert (0);
+ break;
+ case SOCKS5_REQUEST:
+ GNUNET_assert (NULL != s5r->rtask);
+ break;
+ case SOCKS5_DATA_TRANSFER:
+ setup_data_transfer (s5r);
+ return;
+ case SOCKS5_WRITE_THEN_CLEANUP:
+ cleanup_s5r (s5r);
+ return;
+ default:
+ GNUNET_break (0);
+ break;
+ }
+}
+
+
+/**
+ * Return a server response message indicating a failure to the client.
+ *
+ * @param s5r request to return failure code for
+ * @param sc status code to return
+ */
+static void
+signal_socks_failure (struct Socks5Request *s5r,
+ enum Socks5StatusCode sc)
+{
+ struct Socks5ServerResponseMessage *s_resp;
+
+ s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
+ memset (s_resp, 0, sizeof (struct Socks5ServerResponseMessage));
+ s_resp->version = SOCKS_VERSION_5;
+ s_resp->reply = sc;
+ s5r->state = SOCKS5_WRITE_THEN_CLEANUP;
+ if (NULL != s5r->wtask)
+ s5r->wtask =
+ GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ s5r->sock,
+ &do_write, s5r);
+}
+
+
+/**
+ * Return a server response message indicating success.
+ *
+ * @param s5r request to return success status message for
+ */
+static void
+signal_socks_success (struct Socks5Request *s5r)
+{
+ struct Socks5ServerResponseMessage *s_resp;
+
+ s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
+ s_resp->version = SOCKS_VERSION_5;
+ s_resp->reply = SOCKS5_STATUS_REQUEST_GRANTED;
+ s_resp->reserved = 0;
+ s_resp->addr_type = SOCKS5_AT_IPV4;
+ /* zero out IPv4 address and port */
+ memset (&s_resp[1],
+ 0,
+ sizeof (struct in_addr) + sizeof (uint16_t));
+ s5r->wbuf_len += sizeof (struct Socks5ServerResponseMessage) +
+ sizeof (struct in_addr) + sizeof (uint16_t);
+ if (NULL == s5r->wtask)
+ s5r->wtask =
+ GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ s5r->sock,
+ &do_write, s5r);
+}
+
+
+/**
+ * Process GNS results for target domain.
+ *
+ * @param cls the `struct Socks5Request *`
+ * @param rd_count number of records returned
+ * @param rd record data
+ */
+static void
+handle_gns_result (void *cls,
+ uint32_t rd_count,
+ const struct GNUNET_GNSRECORD_Data *rd)
+{
+ struct Socks5Request *s5r = cls;
+ uint32_t i;
+ const struct GNUNET_GNSRECORD_Data *r;
+ int got_ip;
+
+ s5r->gns_lookup = NULL;
+ got_ip = GNUNET_NO;
+ for (i=0;i<rd_count;i++)
+ {
+ r = &rd[i];
+ switch (r->record_type)
+ {
+ case GNUNET_DNSPARSER_TYPE_A:
+ {
+ struct sockaddr_in *in;
+
+ if (sizeof (struct in_addr) != r->data_size)
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ if (GNUNET_YES == got_ip)
+ break;
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_test_pf (PF_INET))
+ break;
+ got_ip = GNUNET_YES;
+ in = (struct sockaddr_in *) &s5r->destination_address;
+ in->sin_family = AF_INET;
+ GNUNET_memcpy (&in->sin_addr,
+ r->data,
+ r->data_size);
+ in->sin_port = htons (s5r->port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ in->sin_len = sizeof (*in);
+#endif
+ }
+ break;
+ case GNUNET_DNSPARSER_TYPE_AAAA:
+ {
+ struct sockaddr_in6 *in;
+
+ if (sizeof (struct in6_addr) != r->data_size)
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ if (GNUNET_YES == got_ip)
+ break;
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_test_pf (PF_INET))
+ break;
+ /* FIXME: allow user to disable IPv6 per configuration option... */
+ got_ip = GNUNET_YES;
+ in = (struct sockaddr_in6 *) &s5r->destination_address;
+ in->sin6_family = AF_INET6;
+ GNUNET_memcpy (&in->sin6_addr,
+ r->data,
+ r->data_size);
+ in->sin6_port = htons (s5r->port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ in->sin6_len = sizeof (*in);
+#endif
+ }
+ break;
+ case GNUNET_GNSRECORD_TYPE_VPN:
+ GNUNET_break (0); /* should have been translated within GNS */
+ break;
+ case GNUNET_GNSRECORD_TYPE_LEHO:
+ GNUNET_free_non_null (s5r->leho);
+ s5r->leho = GNUNET_strndup (r->data,
+ r->data_size);
+ break;
+ case GNUNET_GNSRECORD_TYPE_BOX:
+ {
+ const struct GNUNET_GNSRECORD_BoxRecord *box;
+
+ if (r->data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ box = r->data;
+ if ( (ntohl (box->record_type) != GNUNET_DNSPARSER_TYPE_TLSA) ||
+ (ntohs (box->protocol) != IPPROTO_TCP) ||
+ (ntohs (box->service) != s5r->port) )
+ break; /* BOX record does not apply */
+ GNUNET_free_non_null (s5r->dane_data);
+ s5r->dane_data_len = r->data_size - sizeof (struct GNUNET_GNSRECORD_BoxRecord);
+ s5r->dane_data = GNUNET_malloc (s5r->dane_data_len);
+ GNUNET_memcpy (s5r->dane_data,
+ &box[1],
+ s5r->dane_data_len);
+ break;
+ }
+ default:
+ /* don't care */
+ break;
+ }
+ }
+ if (GNUNET_YES != got_ip)