Subject: please add SOCKS support and enforced TCP to enable DNS resolution through Tor

From: Daniel Kahn Gillmor <[email-masked]>
Date: Fri, 12 Aug 2016 11:17:36 -0400

Package: adns

Hi Ian and other adns folks--

It would be great to finally land Tor support in adns.

I'm attaching Werner Koch's series of seven patches for tor support in
adns here.  If you prefer to pull them from git, they can also be found
on the "upstream-tor-work" branch at git://git.gnupg.org/adns

If there are any blockers that prevent adns from merging these changes,
i'd be happy to hear about them and to try to help work through them.

Regards,

        --dkg

From 3d6d0f04bba61856953fe9287353ad7181b72982 Mon Sep 17 00:00:00 2001
From: Werner Koch <[email-masked]>
Date: Wed, 11 Nov 2015 18:54:05 +0100
Subject: [PATCH 1/7] Nuke trailing spaces from 3 files

Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
---
 src/adns.h  | 56 ++++++++++++++++++++++++++++----------------------------
 src/event.c | 34 +++++++++++++++++-----------------
 src/query.c | 36 ++++++++++++++++++------------------
 3 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/src/adns.h b/src/adns.h
index a6599f6..d50f951 100644
--- a/src/adns.h
+++ b/src/adns.h
@@ -16,25 +16,25 @@
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 3, or (at your option)
  *  any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
- * 
+ *
  *  For the benefit of certain LGPL'd `omnibus' software which
  *  provides a uniform interface to various things including adns, I
  *  make the following additional licence.  I do this because the GPL
  *  would otherwise force either the omnibus software to be GPL'd or
  *  the adns-using part to be distributed separately.
- *  
+ *
  *  So: you may also redistribute and/or modify adns.h (but only the
  *  public header file adns.h and not any other part of adns) under the
  *  terms of the GNU Library General Public License as published by the
  *  Free Software Foundation; either version 2 of the License, or (at
  *  your option) any later version.
- *  
+ *
  *  Note that adns itself is GPL'd.  Authors of adns-using applications
  *  with GPL-incompatible licences, and people who distribute adns with
  *  applications where the whole distribution is not GPL'd, are still
@@ -183,27 +183,27 @@ typedef enum {
     * Don't forget adns_qf_quoteok if that's what you want. */
 
  adns_r_none=             0,
- 		     
+
  adns_r_a=                1,
- 		     
+
  adns_r_ns_raw=           2,
  adns_r_ns=                  adns_r_ns_raw|adns__qtf_deref,
- 		     
+
  adns_r_cname=            5,
- 		     
+
  adns_r_soa_raw=          6,
- adns_r_soa=                 adns_r_soa_raw|adns__qtf_mail822, 
- 		     
+ adns_r_soa=                 adns_r_soa_raw|adns__qtf_mail822,
+
  adns_r_ptr_raw=         12, /* do not mind PTR with wrong or missing addr */
  adns_r_ptr=                 adns_r_ptr_raw|adns__qtf_deref,
- 		     
- adns_r_hinfo=           13,  
- 		     
+
+ adns_r_hinfo=           13,
+
  adns_r_mx_raw=          15,
  adns_r_mx=                  adns_r_mx_raw|adns__qtf_deref,
- 		     
+
  adns_r_txt=             16,
- 		     
+
  adns_r_rp_raw=          17,
  adns_r_rp=                  adns_r_rp_raw|adns__qtf_mail822,
 
@@ -214,11 +214,11 @@ typedef enum {
   * _quoteok_query, any query domain is allowed. */
  adns_r_srv_raw=         33,
  adns_r_srv=                 adns_r_srv_raw|adns__qtf_deref,
-		     
+
  adns_r_addr=                adns_r_a|adns__qtf_deref,
 
  adns__rrt_sizeforce= 0x7fffffff,
- 
+
 } adns_rrtype;
 
 /*
@@ -226,7 +226,7 @@ typedef enum {
  * legal syntax, or you get adns_s_querydomainvalid (if the query
  * domain contains bad characters) or adns_s_answerdomaininvalid (if
  * the answer contains bad characters).
- * 
+ *
  * In queries _with_ qf_quoteok_*, domains in the query or response
  * may contain any characters, quoted according to RFC1035 5.1.  On
  * input to adns, the char* is a pointer to the interior of a "
@@ -298,7 +298,7 @@ typedef enum {
  adns_s_systemfail,
 
  adns_s_max_localfail= 29,
- 
+
  /* remotely induced errors, detected locally */
  adns_s_timeout,
  adns_s_allservfail,
@@ -307,7 +307,7 @@ typedef enum {
  adns_s_unknownformat,
 
  adns_s_max_remotefail= 59,
- 
+
  /* remotely induced errors, reported by remote server to us */
  adns_s_rcodeservfail,
  adns_s_rcodeformaterror,
@@ -323,14 +323,14 @@ typedef enum {
  adns_s_answerdomaininvalid,
  adns_s_answerdomaintoolong,
  adns_s_invaliddata,
- 
+
  adns_s_max_misconfig= 199,
 
  /* permanent problems with the query */
  adns_s_querydomainwrong,
  adns_s_querydomaininvalid,
  adns_s_querydomaintoolong,
- 
+
  adns_s_max_misquery= 299,
 
  /* permanent errors */
@@ -338,7 +338,7 @@ typedef enum {
  adns_s_nodata,
 
  adns_s_max_permfail= 499
- 
+
 } adns_status;
 
 typedef union {
@@ -460,7 +460,7 @@ typedef struct {
  *  (eg, failure to create sockets, malloc failure, etc.) return errno
  *  values.  EINVAL from _init et al means the configuration file
  *  is erroneous and cannot be parsed.
- * 
+ *
  *  For _wait and _check failures are reported in the answer
  *  structure, and only 0, ESRCH or (for _check) EAGAIN is
  *  returned: if no (appropriate) requests are done adns_check returns
@@ -516,7 +516,7 @@ int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
  *  is set later overrides any that is set earlier.
  *
  * Standard directives understood in resolv[-adns].conf:
- * 
+ *
  *  nameserver <address>
  *   Must be followed by the IP address of a nameserver.  Several
  *   nameservers may be specified, and they will be tried in the order
@@ -595,7 +595,7 @@ int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
  *   logging them.  To be effective, appear in the configuration
  *   before the unknown options.  ADNS_RES_OPTIONS is generally early
  *   enough.
- * 
+ *
  * There are a number of environment variables which can modify the
  * behaviour of adns.  They take effect only if adns_init is used, and
  * the caller of adns_init can disable them using adns_if_noenv.  In
@@ -846,7 +846,7 @@ void adns_firsttimeout(adns_state ads,
 		       struct timeval now);
 /* Asks adns when it would first like the opportunity to time
  * something out.  now must be the current time, from gettimeofday.
- * 
+ *
  * If tv_mod points to 0 then tv_buf must be non-null, and
  * _firsttimeout will fill in *tv_buf with the time until the first
  * timeout, and make *tv_mod point to tv_buf.  If adns doesn't have
@@ -935,7 +935,7 @@ int adns_beforepoll(adns_state ads, struct pollfd *fds,
 /* Finds out which fd's adns is interested in, and when it would like
  * to be able to time things out.  This is in a form suitable for use
  * with poll(2).
- * 
+ *
  * On entry, usually fds should point to at least *nfds_io structs.
  * adns will fill up to that many structs will information for poll,
  * and record in *nfds_io how many structs it filled.  If it wants to
diff --git a/src/event.c b/src/event.c
index eec4b6a..f26bd52 100644
--- a/src/event.c
+++ b/src/event.c
@@ -11,17 +11,17 @@
  *    Copyright (C) 1999-2000,2003,2006  Tony Finch
  *    Copyright (C) 1991 Massachusetts Institute of Technology
  *  (See the file INSTALL for full details.)
- *  
+ *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 3, or (at your option)
  *  any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software Foundation.
  */
@@ -51,7 +51,7 @@ static void tcp_close(adns_state ads) {
 void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
   int serv;
   adns_query qu;
-  
+
   assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok);
   serv= ads->tcpserver;
   if (what) adns__warn(ads,serv,0,"TCP connection failed: %s: %s",what,why);
@@ -69,7 +69,7 @@ void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
 
 static void tcp_connected(adns_state ads, struct timeval now) {
   adns_query qu, nqu;
-  
+
   adns__debug(ads,ads->tcpserver,0,"TCP connected");
   ads->tcpstate= server_ok;
   for (qu= ads->tcpw.head; qu && ads->tcpstate == server_ok; qu= nqu) {
@@ -81,7 +81,7 @@ static void tcp_connected(adns_state ads, struct timeval now) {
 
 static void tcp_broken_events(adns_state ads) {
   adns_query qu, nqu;
-  
+
   assert(ads->tcpstate == server_broken);
   for (qu= ads->tcpw.head; qu; qu= nqu) {
     nqu= qu->next;
@@ -110,7 +110,7 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
     default:
       abort();
     }
-    
+
     assert(!ads->tcpsend.used);
     assert(!ads->tcprecv.used);
     assert(!ads->tcprecv_skip);
@@ -172,7 +172,7 @@ static void inter_immed(struct timeval **tv_io, struct timeval *tvbuf) {
 
   timerclear(rbuf);
 }
-    
+
 static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
 			struct timeval maxto) {
   struct timeval *rbuf;
@@ -209,7 +209,7 @@ static void timeouts_queue(adns_state ads, int act,
 			   struct timeval **tv_io, struct timeval *tvbuf,
 			   struct timeval now, struct query_queue *queue) {
   adns_query qu, nqu;
-  
+
   for (qu= queue->head; qu; qu= nqu) {
     nqu= qu->next;
     if (!timercmp(&now,&qu->timeout,>)) {
@@ -345,7 +345,7 @@ int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
   char addrbuf[ADNS_ADDR2TEXT_BUFLEN];
   struct udpsocket *udp;
   adns_sockaddr udpaddr;
-  
+
   adns__consistency(ads,0,cc_entex);
 
   switch (ads->tcpstate) {
@@ -432,7 +432,7 @@ xit:
 
 int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
   int r;
-  
+
   adns__consistency(ads,0,cc_entex);
 
   switch (ads->tcpstate) {
@@ -486,7 +486,7 @@ xit:
   adns__returning(ads,0);
   return r;
 }
-  
+
 int adns_processexceptional(adns_state ads, int fd,
 			    const struct timeval *now) {
   adns__consistency(ads,0,cc_entex);
@@ -513,7 +513,7 @@ static void fd_event(adns_state ads, int fd,
 				 const struct timeval *now),
 		     struct timeval now, int *r_r) {
   int r;
-  
+
   if (!(revent & pollflag)) return;
   if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return;
   r= func(ads,fd,&now);
@@ -557,7 +557,7 @@ void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io,
   struct timeval tv_nowbuf;
   struct pollfd pollfds[MAX_POLLFDS];
   int i, fd, maxfd, npollfds;
-  
+
   adns__consistency(ads,0,cc_entex);
 
   if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
@@ -611,7 +611,7 @@ void adns_globalsystemfailure(adns_state ads) {
 
   while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail);
   while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail);
-  
+
   switch (ads->tcpstate) {
   case server_connecting:
   case server_ok:
@@ -690,7 +690,7 @@ int adns_wait(adns_state ads,
   int r, maxfd, rsel;
   fd_set readfds, writefds, exceptfds;
   struct timeval tvbuf, *tvp;
-  
+
   adns__consistency(ads,*query_io,cc_entex);
   for (;;) {
     r= adns__internal_check(ads,query_io,answer_r,context_r);
@@ -722,7 +722,7 @@ int adns_check(adns_state ads,
 	       void **context_r) {
   struct timeval now;
   int r;
-  
+
   adns__consistency(ads,*query_io,cc_entex);
   r= gettimeofday(&now,0);
   if (!r) adns__autosys(ads,now);
diff --git a/src/query.c b/src/query.c
index ff56fc3..0f5c1a5 100644
--- a/src/query.c
+++ b/src/query.c
@@ -11,17 +11,17 @@
  *    Copyright (C) 1999-2000,2003,2006  Tony Finch
  *    Copyright (C) 1991 Massachusetts Institute of Technology
  *  (See the file INSTALL for full details.)
- *  
+ *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 3, or (at your option)
  *  any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software Foundation.
  */
@@ -41,11 +41,11 @@ static adns_query query_alloc(adns_state ads,
 			      adns_queryflags flags, struct timeval now) {
   /* Allocate a virgin query and return it. */
   adns_query qu;
-  
+
   qu= malloc(sizeof(*qu));  if (!qu) return 0;
   qu->answer= malloc(sizeof(*qu->answer));
   if (!qu->answer) { free(qu); return 0; }
-  
+
   qu->ads= ads;
   qu->state= query_tosend;
   qu->back= qu->next= qu->parent= 0;
@@ -100,7 +100,7 @@ static void query_submit(adns_state ads, adns_query qu,
 
   qu->query_dgram= malloc(qu->vb.used);
   if (!qu->query_dgram) { adns__query_fail(qu,adns_s_nomemory); return; }
-  
+
   qu->id= id;
   qu->query_dglen= qu->vb.used;
   memcpy(qu->query_dgram,qu->vb.buf,qu->vb.used);
@@ -167,7 +167,7 @@ adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
   LIST_LINK_TAIL_PART(parent->children,qu,siblings.);
   memcpy(&qu->ctx,ctx,sizeof(qu->ctx));
   query_submit(ads,qu, typei,qumsg_vb,id,flags,now);
-  
+
   return adns_s_ok;
 
 x_err:
@@ -206,7 +206,7 @@ static void query_simple(adns_state ads, adns_query qu,
 void adns__search_next(adns_state ads, adns_query qu, struct timeval now) {
   const char *nextentry;
   adns_status st;
-  
+
   if (qu->search_doneabs<0) {
     nextentry= 0;
     qu->search_doneabs= 1;
@@ -286,7 +286,7 @@ int adns_submit(adns_state ads,
 
   r= gettimeofday(&now,0); if (r) goto x_errno;
   qu= query_alloc(ads,typei,type,flags,now); if (!qu) goto x_errno;
-  
+
   qu->ctx.ext= context;
   qu->ctx.callback= 0;
   memset(&qu->ctx.pinfo,0,sizeof(qu->ctx.pinfo));
@@ -297,7 +297,7 @@ int adns_submit(adns_state ads,
   ol= strlen(owner);
   if (!ol) { st= adns_s_querydomaininvalid; goto x_adnsfail; }
   if (ol>DNS_MAXDOMAIN+1) { st= adns_s_querydomaintoolong; goto x_adnsfail; }
-				 
+
   if (ol>=1 && owner[ol-1]=='.' && (ol<2 || owner[ol-2]!='\\')) {
     flags &= ~adns_qf_search;
     qu->flags= flags;
@@ -374,7 +374,7 @@ int adns_synchronous(adns_state ads,
 		     adns_answer **answer_r) {
   adns_query qu;
   int r;
-  
+
   r= adns_submit(ads,owner,type,flags,0,&qu);
   if (r) return r;
 
@@ -398,7 +398,7 @@ static void *alloc_common(adns_query qu, size_t sz) {
 
 void *adns__alloc_interim(adns_query qu, size_t sz) {
   void *rv;
-  
+
   sz= MEM_ROUND(sz);
   rv= alloc_common(qu,sz);
   if (!rv) return 0;
@@ -408,7 +408,7 @@ void *adns__alloc_interim(adns_query qu, size_t sz) {
 
 void *adns__alloc_preserved(adns_query qu, size_t sz) {
   void *rv;
-  
+
   sz= MEM_ROUND(sz);
   rv= adns__alloc_interim(qu,sz);
   if (!rv) return 0;
@@ -449,7 +449,7 @@ void adns__transfer_interim(adns_query from, adns_query to, void *block) {
 
   assert(!to->final_allocspace);
   assert(!from->final_allocspace);
-  
+
   LIST_UNLINK(from->allocations,an);
   LIST_LINK_TAIL(to->allocations,an);
 
@@ -585,17 +585,17 @@ static void makefinal_query(adns_query qu) {
   qu->final_allocspace= (byte*)ans + MEM_ROUND(sizeof(*ans));
   adns__makefinal_str(qu,&ans->cname);
   adns__makefinal_str(qu,&ans->owner);
-  
+
   if (ans->nrrs) {
     adns__makefinal_block(qu, &ans->rrs.untyped, ans->nrrs*ans->rrsz);
 
     for (rrn=0; rrn<ans->nrrs; rrn++)
       qu->typei->makefinal(qu, ans->rrs.bytes + rrn*ans->rrsz);
   }
-  
+
   free_query_allocs(qu);
   return;
-  
+
  x_nomem:
   qu->preserved_allocd= 0;
   qu->answer->cname= 0;
@@ -663,7 +663,7 @@ void adns__makefinal_str(adns_query qu, char **strp) {
   l= strlen(before)+1;
   after= adns__alloc_final(qu,l);
   memcpy(after,before,l);
-  *strp= after;  
+  *strp= after;
 }
 
 void adns__makefinal_block(adns_query qu, void **blpp, size_t sz) {
-- 
2.8.1

From ab194ca0c362b0bbb77997189ed453a088d7ecb3 Mon Sep 17 00:00:00 2001
From: Werner Koch <[email-masked]>
Date: Wed, 11 Nov 2015 19:22:47 +0100
Subject: [PATCH 2/7] Remove return value from the GET_W and GET_L macros

The return value of the macros is nowhere used.  This patch silences a
pretty noisy compiler warning about an unused value.

Signed-off-by: Werner Koch <[email-masked]>
Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
---
 src/internal.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/internal.h b/src/internal.h
index 9334a6f..c466aff 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -936,12 +936,12 @@ static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; }
 
 #define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff)
 #define GET_B(cb,tv) ((tv)= GETIL_B((cb)))
-#define GET_W(cb,tv) ((tv)=0,(tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv))
+#define GET_W(cb,tv) ((tv)=0,(tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb))
 #define GET_L(cb,tv) ( (tv)=0,				\
 		       (tv)|=(GETIL_B((cb))<<24),	\
 		       (tv)|=(GETIL_B((cb))<<16),	\
 		       (tv)|=(GETIL_B((cb))<<8),	\
-		       (tv)|=GETIL_B(cb),		\
-		       (tv) )
+		       (tv)|=GETIL_B(cb)		\
+		     )
 
 #endif
-- 
2.8.1

From 260d51202e965a8f000b439d2645a014b28b7580 Mon Sep 17 00:00:00 2001
From: Werner Koch <[email-masked]>
Date: Mon, 9 Nov 2015 18:01:43 +0100
Subject: [PATCH 3/7] Add macro to safely clear memory.

* src/internal.h (WIPEMEMORY): New.
--

This kind of platform neutral code has been in use by GnuPG and
Libgcrypt for ages.  I am still waiting for some C committee f^D
experts to figure that this makes use of undefined behaviour for
volatile and they tell their optimizing-for-the-flat-world compiler
to remove such code and thereby unveil passwords in memory (which
actually happened for the standard memset).

Signed-off-by: Werner Koch <[email-masked]>
Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
---
 src/internal.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/internal.h b/src/internal.h
index c466aff..e5c41b1 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -944,4 +944,12 @@ static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; }
 		       (tv)|=GETIL_B(cb)		\
 		     )
 
+/* To avoid that a compiler optimizes certain memset calls away, this
+   macro may be used instead.  */
+#define WIPEMEMORY(_ptr,_len) do {                          \
+              volatile char *_vptr=(volatile char *)(_ptr); \
+              size_t _vlen=(_len);                          \
+              while(_vlen) { *_vptr=0; _vptr++; _vlen--; }  \
+              } while(0)
+
 #endif
-- 
2.8.1

From 0bf5b8b6314667f83ca24cc1734fc94909120b44 Mon Sep 17 00:00:00 2001
From: Werner Koch <[email-masked]>
Date: Wed, 11 Nov 2015 19:27:41 +0100
Subject: [PATCH 4/7] Add flag adns_if_tormode to provide a basic TOR mode.

* src/adns.h (adns_if_tormode): New.
* src/query.c (adns_submit): Force use of a VC in tormode.
(adns_submit_reverse_any): Ditto.
(adns_submit_reverse): Ditto.
(adns_synchronous): Ditto.
* src/event.c (use_socks_p, socks_connect): New.  Based on code from
Libassuan.
(adns__tcp_tryconnect): Move setnonblock after the init of ADDR.  Call
socks_connect if needed.

--

This patch has the problem that connecting to the Tor server and, more
important, establishing the Tor connection will block.  Changing this
would require quite some rework of the TCP code.  In fact it has
always been the case that when falling back to TCP mode and the
connect would have blocked the connection won't be established but
times outs.  There is no retry code for the case that connect returns
with EWOULDBLOCK or EINPROGRESS.

Signed-off-by: Werner Koch <[email-masked]>
Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
---
 src/adns.h     |   2 +
 src/event.c    | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/internal.h |   2 +-
 src/query.c    |  10 +++
 4 files changed, 208 insertions(+), 9 deletions(-)

diff --git a/src/adns.h b/src/adns.h
index d50f951..d2748bc 100644
--- a/src/adns.h
+++ b/src/adns.h
@@ -115,6 +115,8 @@ typedef enum { /* In general, or together the desired flags: */
     * all) then the query flags take precedence; otherwise only records which
     * satisfy all of the stated requirements are allowed.
     */
+ adns_if_tormode=     0x1000,/* route all traffic via Tor */
+
  adns__if_sizeforce= 0x7fff,
 } adns_initflags;
 
diff --git a/src/event.c b/src/event.c
index f26bd52..0b2f17b 100644
--- a/src/event.c
+++ b/src/event.c
@@ -94,6 +94,176 @@ static void tcp_broken_events(adns_state ads) {
   ads->tcpstate= server_disconnected;
 }
 
+
+/* Return true if SOCKS shall be used.  This is the case if
+   adns_if_tormode is set and the desired address is not the loopback
+   address.  */
+static int
+use_socks_p (adns_state ads, const adns_rr_addr *addr)
+{
+  if (!(ads->iflags & adns_if_tormode))
+    return 0;
+  else if (addr->addr.sa.sa_family == AF_INET6)
+    {
+      const struct sockaddr_in6 *addr_in6 = &addr->addr.inet6;
+      const unsigned char *s;
+      int i;
+
+      s = (unsigned char *)&addr_in6->sin6_addr.s6_addr;
+      if (s[15] != 1)
+        return 1;   /* Last octet is not 1 - not the loopback address.  */
+      for (i=0; i < 15; i++, s++)
+        if (*s)
+          return 1; /* Non-zero octet found - not the loopback address.  */
+
+      return 0; /* This is the loopback address.  */
+    }
+  else if (addr->addr.sa.sa_family == AF_INET)
+    {
+      const struct sockaddr_in *addr_in = &addr->addr.inet;
+
+      if (*(const unsigned char*)&addr_in->sin_addr.s_addr == 127)
+        return 0; /* Loopback (127.0.0.0/8) */
+
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+/* Connect to TOR using the SOCKS5 protocol.  We assume that the
+   connection to the SOCKS proxy (TOR server) does not block; if it
+   would block we return and the the usual retry logic of the caller
+   kicks in.  */
+static int
+socks_connect (adns_state ads, int fd, const adns_rr_addr *addr)
+{
+  int ret;
+  struct sockaddr_in  proxyaddr_in;
+  struct sockaddr *proxyaddr;
+  size_t proxyaddrlen;
+  const struct sockaddr_in6 *addr_in6;
+  const struct sockaddr_in  *addr_in;
+  unsigned char buffer[22];
+  size_t buflen;
+
+  memset (&proxyaddr_in, 0, sizeof proxyaddr_in);
+
+  /* Connect to local host.  */
+  /* Fixme: First try to use IPv6.  */
+  proxyaddr_in.sin_family = AF_INET;
+  proxyaddr_in.sin_port = htons (9050);
+  proxyaddr_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+  proxyaddr = (struct sockaddr *)&proxyaddr_in;
+  proxyaddrlen = sizeof proxyaddr_in;
+  ret = connect(fd, proxyaddr, proxyaddrlen);
+  if (ret)
+    return ret;
+
+  /* Negotiate method.  */
+  buffer[0] = 5; /* RFC-1928 VER field.  */
+  buffer[1] = 1; /* NMETHODS */
+  buffer[2] = 0; /* Method: No authentication required. */
+  adns__sigpipe_protect(ads);
+  ret = write(fd, buffer, 3);
+  adns__sigpipe_unprotect(ads);
+  if (ret != 3)
+    {
+      if (ret >= 0)
+        errno = EIO;
+      return -1;
+    }
+  ret = read(fd, buffer, 2);
+  if (ret < 0)
+    return ret;
+  if (ret != 2 || buffer[0] != 5 || buffer[1] != 0 )
+    {
+      /* Socks server returned wrong version or does not support our
+         requested method.  */
+      errno = ENOTSUP; /* Fixme: Is there a better errno? */
+      return -1;
+    }
+
+  /* Send request details (rfc-1928, 4).  */
+  buffer[0] = 5; /* VER  */
+  buffer[1] = 1; /* CMD = CONNECT  */
+  buffer[2] = 0; /* RSV  */
+  if (addr->addr.sa.sa_family == AF_INET6)
+    {
+      addr_in6 = &addr->addr.inet6;
+
+      buffer[3] = 4; /* ATYP = IPv6 */
+      memcpy (buffer+ 4, &addr_in6->sin6_addr.s6_addr, 16); /* DST.ADDR */
+      memcpy (buffer+20, &addr_in6->sin6_port, 2);          /* DST.PORT */
+      buflen = 22;
+    }
+  else
+    {
+      addr_in = &addr->addr.inet;
+
+      buffer[3] = 1; /* ATYP = IPv4 */
+      memcpy (buffer+4, &addr_in->sin_addr.s_addr, 4); /* DST.ADDR */
+      memcpy (buffer+8, &addr_in->sin_port, 2);        /* DST.PORT */
+      buflen = 10;
+    }
+  adns__sigpipe_protect(ads);
+  ret = write(fd, buffer, buflen);
+  adns__sigpipe_unprotect(ads);
+  if (ret != buflen)
+    {
+      if (ret >= 0)
+        errno = EIO;
+      return -1;
+    }
+  ret = read(fd, buffer, buflen);
+  if (ret < 0)
+    return ret;
+  if (ret != buflen || buffer[0] != 5 || buffer[2] != 0 )
+    {
+      /* Socks server returned wrong version or the reserved field is
+         not zero.  */
+      errno = EPROTO;
+      return -1;
+    }
+  if (buffer[1])
+    {
+      switch (buffer[1])
+        {
+        case 0x01: /* general SOCKS server failure.  */
+          errno = ENETDOWN;
+          break;
+        case 0x02: /* connection not allowed by ruleset.  */
+          errno = EACCES;
+          break;
+        case 0x03: /* Network unreachable */
+          errno = ENETUNREACH;
+          break;
+        case 0x04: /* Host unreachable */
+          errno = EHOSTUNREACH;
+          break;
+        case 0x05: /* Connection refused */
+          errno = ECONNREFUSED;
+          break;
+        case 0x06: /* TTL expired */
+          errno = ETIMEDOUT;
+          break;
+        case 0x08: /* Address type not supported */
+          errno = EPROTONOSUPPORT;
+          break;
+        case 0x07: /* Command not supported */
+        default:
+          errno = ENOTSUP; /* Fixme: Is there a better errno? */
+        }
+      return -1;
+    }
+  /* We have not way to store the actual address used by the server.
+     Fortunately it is of no real use.  */
+
+  return 0;
+}
+
+
 void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
   int r, fd, tries;
   adns_rr_addr *addr;
@@ -126,14 +296,31 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
       adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno));
       return;
     }
-    r= adns__setnonblock(ads,fd);
-    if (r) {
-      adns__diag(ads,-1,0,"cannot make TCP socket nonblocking:"
-		 " %s",strerror(r));
-      close(fd);
-      return;
-    }
-    r= connect(fd,&addr->addr.sa,addr->len);
+    if (use_socks_p(ads,addr))
+      {
+        r= socks_connect(ads,fd,addr);
+        if (!r)
+          {
+            r= adns__setnonblock(ads,fd);
+            if (r) {
+              adns__diag(ads,-1,0,"cannot make TCP socket nonblocking:"
+                         " %s",strerror(r));
+              close(fd);
+              return;
+            }
+          }
+      }
+    else
+      {
+        r= adns__setnonblock(ads,fd);
+        if (r) {
+          adns__diag(ads,-1,0,"cannot make TCP socket nonblocking:"
+                     " %s",strerror(r));
+          close(fd);
+          return;
+        }
+        r= connect(fd,&addr->addr.sa,addr->len);
+      }
     ads->tcpsocket= fd;
     ads->tcpstate= server_connecting;
     if (r==0) { tcp_connected(ads,now); return; }
diff --git a/src/internal.h b/src/internal.h
index e5c41b1..71782da 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -331,7 +331,7 @@ struct adns__query {
    *        too big for UDP /             UDP timeout  \  \ send via UDP
    *        send via TCP   /              more retries  \  \
    *        when conn'd   /                  desired     \  \
-   *                     |     	       	       	       	  |  |
+   *        or Tor-mode  |     	       	       	       	  |  |
    *                     v				  |  v
    *              +-----------+         	    	+-------------+
    *              | tcpw/tcpw | ________                | tosend/udpw |
diff --git a/src/query.c b/src/query.c
index 0f5c1a5..0fc4d07 100644
--- a/src/query.c
+++ b/src/query.c
@@ -275,6 +275,9 @@ int adns_submit(adns_state ads,
 
   adns__consistency(ads,0,cc_entex);
 
+  if ((ads->iflags & adns_if_tormode))
+    flags |= adns_qf_usevc;
+
   if (flags & ~(adns_queryflags)0x4009ffff)
     /* 0x40080000 are reserved for `harmless' future expansion
      * 0x00000020 used to be adns_qf_quoteok_cname, now the default;
@@ -346,6 +349,8 @@ int adns_submit_reverse_any(adns_state ads,
   int r;
 
   flags &= ~adns_qf_search;
+  if ((ads->iflags & adns_if_tormode))
+    flags |= adns_qf_usevc;
 
   buf = shortbuf;
   r= adns__make_reverse_domain(addr,zone, &buf,sizeof(shortbuf),&buf_free);
@@ -364,6 +369,8 @@ int adns_submit_reverse(adns_state ads,
   if (((type^adns_r_ptr) & adns_rrt_reprmask) &&
       ((type^adns_r_ptr_raw) & adns_rrt_reprmask))
     return EINVAL;
+  if ((ads->iflags & adns_if_tormode))
+    flags |= adns_qf_usevc;
   return adns_submit_reverse_any(ads,addr,0,type,flags,context,query_r);
 }
 
@@ -375,6 +382,9 @@ int adns_synchronous(adns_state ads,
   adns_query qu;
   int r;
 
+  if ((ads->iflags & adns_if_tormode))
+    flags |= adns_qf_usevc;
+
   r= adns_submit(ads,owner,type,flags,0,&qu);
   if (r) return r;
 
-- 
2.8.1

From b8b8f90c85a7e194f0260cc98479dd97601cb04b Mon Sep 17 00:00:00 2001
From: Werner Koch <[email-masked]>
Date: Mon, 9 Nov 2015 18:08:03 +0100
Subject: [PATCH 5/7] Make handling of returned SOCKS bound address more
 robust.

* src/event.c (socks_connect): Allow proxy to return a v6 address
instead of the provided v4 and vice versa.
--

The specs say nothing about this but doing it this way is likely more
robust that assuming the same family will be returned.

Signed-off-by: Werner Koch <[email-masked]>
Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
---
 src/event.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/event.c b/src/event.c
index 0b2f17b..8d5adde 100644
--- a/src/event.c
+++ b/src/event.c
@@ -216,7 +216,7 @@ socks_connect (adns_state ads, int fd, const adns_rr_addr *addr)
         errno = EIO;
       return -1;
     }
-  ret = read(fd, buffer, buflen);
+  ret = read(fd, buffer, 10 /*(v4 length)*/);
   if (ret < 0)
     return ret;
   if (ret != buflen || buffer[0] != 5 || buffer[2] != 0 )
@@ -254,10 +254,24 @@ socks_connect (adns_state ads, int fd, const adns_rr_addr *addr)
         case 0x07: /* Command not supported */
         default:
           errno = ENOTSUP; /* Fixme: Is there a better errno? */
+          break;
         }
       return -1;
     }
-  /* We have not way to store the actual address used by the server.
+  if (buffer[3] == 4)
+    {
+      /* ATYP indicates a v6 address.  We need to read the remaining
+         12 bytes to finialize the SOCKS5 intro.  */
+      ret = read(fd, buffer, 12 /*(v6-v4 length)*/);
+      if (ret != 12)
+        {
+          if (ret >= 0)
+            errno = EIO;
+          return -1;
+        }
+    }
+
+  /* We have no way to store the actual address used by the server.
      Fortunately it is of no real use.  */
 
   return 0;
-- 
2.8.1

From 2ca1bd9a52824b87a416e7da1aa1d73cdedf174f Mon Sep 17 00:00:00 2001
From: Werner Koch <[email-masked]>
Date: Sun, 8 Nov 2015 18:57:56 +0100
Subject: [PATCH 6/7] Add config options adns_tormode and adns_sockscred.

* src/internal.h (struct adns__state): Add field "sockscred".
* src/setup.c (init_begin): Clear SOCKSCRED.
(init_finish): Free SOCKSCRED.
(ccf_options): Implement new options.
* src/adns.h: Describe options.

Signed-off-by: Werner Koch <[email-masked]>
Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
---
 src/adns.h     |  8 ++++++++
 src/internal.h |  1 +
 src/setup.c    | 19 +++++++++++++++++++
 3 files changed, 28 insertions(+)

diff --git a/src/adns.h b/src/adns.h
index d2748bc..f4b9718 100644
--- a/src/adns.h
+++ b/src/adns.h
@@ -598,6 +598,14 @@ int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
  *   before the unknown options.  ADNS_RES_OPTIONS is generally early
  *   enough.
  *
+ *  adns_tormode
+ *   Forces the use of virtual circuits over a SOCKS5 proxy running at
+ *   port 9050.  No UDP based communication is done.
+ *
+ *  adns_sockscred:username:password
+ *   Use username and password for SOCKS5 authentication.  Default is
+ *   not to use any authentication.
+ *
  * There are a number of environment variables which can modify the
  * behaviour of adns.  They take effect only if adns_init is used, and
  * the caller of adns_init can disable them using adns_if_noenv.  In
diff --git a/src/internal.h b/src/internal.h
index 71782da..35ca8a3 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -396,6 +396,7 @@ struct adns__state {
   char **searchlist;
   unsigned config_report_unknown:1;
   unsigned short rand48xsubi[3];
+  char *sockscred; /* Malloced string with the SOCKS5 credentials or NULL.  */
 };
 
 /* From addrfam.c: */
diff --git a/src/setup.c b/src/setup.c
index c67b042..83c8bd6 100644
--- a/src/setup.c
+++ b/src/setup.c
@@ -328,6 +328,21 @@ static void ccf_options(adns_state ads, const char *fn,
       }
       continue;
     }
+    if (WORD_IS("adns_tormode")) {
+      ads->iflags |= adns_if_tormode;
+      continue;
+    }
+    if (WORD_STARTS("adns_sockscred:")) {
+      l -= 15;
+      ads->sockscred = malloc (l + 1);
+      if (!ads->sockscred) {
+        saveerr(ads,errno);
+        continue;
+      }
+      memcpy (ads->sockscred, word, l);
+      ads->sockscred[l] = 0;
+      continue;
+    }
     if (WORD_IS("adns_ignoreunkcfg")) {
       ads->config_report_unknown=0;
       continue;
@@ -640,6 +655,8 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
   ads->rand48xsubi[1]= (unsigned long)pid >> 16;
   ads->rand48xsubi[2]= pid ^ ((unsigned long)pid >> 16);
 
+  ads->sockscred = NULL;
+
   *ads_r= ads;
   return 0;
 }
@@ -681,6 +698,8 @@ static int init_finish(adns_state ads) {
  x_closeudp:
   for (i=0; i<ads->nudpsockets; i++) close(ads->udpsockets[i].fd);
  x_free:
+  if (ads->sockscred)
+    free (ads->sockscred);
   free(ads);
   return r;
 }
-- 
2.8.1

From 59371e4c138fa9fde27352d4d35e3f321d41d4e4 Mon Sep 17 00:00:00 2001
From: Werner Koch <[email-masked]>
Date: Mon, 9 Nov 2015 18:10:27 +0100
Subject: [PATCH 7/7] Allow SOCKS5 authentication with username/password.

* src/event.c (socks_connect): Implemedn authentication method 2.
--

The credentials are given by the new config option adns_sockscred.
Changing the credentials is an indication to Tor to use a new circuit.
Tor ignores the actual value of the credentials.

Signed-off-by: Werner Koch <[email-masked]>
Signed-off-by: Daniel Kahn Gillmor <[email-masked]>
---
 src/event.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 69 insertions(+), 3 deletions(-)

diff --git a/src/event.c b/src/event.c
index 8d5adde..02842a2 100644
--- a/src/event.c
+++ b/src/event.c
@@ -145,8 +145,10 @@ socks_connect (adns_state ads, int fd, const adns_rr_addr *addr)
   size_t proxyaddrlen;
   const struct sockaddr_in6 *addr_in6;
   const struct sockaddr_in  *addr_in;
-  unsigned char buffer[22];
+  unsigned char buffer[22+512]; /* The extra 512 bytes are used as
+                                   space for username:password.  */
   size_t buflen;
+  int method;
 
   memset (&proxyaddr_in, 0, sizeof proxyaddr_in);
 
@@ -164,7 +166,11 @@ socks_connect (adns_state ads, int fd, const adns_rr_addr *addr)
   /* Negotiate method.  */
   buffer[0] = 5; /* RFC-1928 VER field.  */
   buffer[1] = 1; /* NMETHODS */
-  buffer[2] = 0; /* Method: No authentication required. */
+  if (ads->sockscred)
+    method = 2;  /* Method: username/password authentication. */
+  else
+    method = 0;  /* Method: No authentication required. */
+  buffer[2] = method;
   adns__sigpipe_protect(ads);
   ret = write(fd, buffer, 3);
   adns__sigpipe_unprotect(ads);
@@ -177,7 +183,7 @@ socks_connect (adns_state ads, int fd, const adns_rr_addr *addr)
   ret = read(fd, buffer, 2);
   if (ret < 0)
     return ret;
-  if (ret != 2 || buffer[0] != 5 || buffer[1] != 0 )
+  if (ret != 2 || buffer[0] != 5 || buffer[1] != method )
     {
       /* Socks server returned wrong version or does not support our
          requested method.  */
@@ -185,6 +191,66 @@ socks_connect (adns_state ads, int fd, const adns_rr_addr *addr)
       return -1;
     }
 
+  if (ads->sockscred)
+    {
+      /* Username/Password sub-negotiation.  */
+      const char *password;
+      int ulen, plen;
+
+      password = strchr (ads->sockscred, ':');
+      if (!password)
+        {
+          errno = EINVAL; /* No password given.  */
+          return -1;
+        }
+      ulen = password - ads->sockscred;
+      password++;
+      plen = strlen (password);
+      if (!ulen || ulen > 255 || !plen || plen > 255)
+        {
+          errno = EINVAL; /* Credentials too long or too short.  */
+          return -1;
+        }
+
+      buffer[0] = 1; /* VER of the sub-negotiation. */
+      buffer[1] = ulen;
+      buflen = 2;
+      memcpy (buffer+buflen, ads->sockscred, ulen);
+      buflen += ulen;
+      buffer[buflen++] = plen;
+      memcpy (buffer+buflen, password, plen);
+      buflen += plen;
+      adns__sigpipe_protect(ads);
+      ret = write(fd, buffer, buflen);
+      adns__sigpipe_unprotect(ads);
+      WIPEMEMORY (buffer, buflen);
+      if (ret != buflen)
+        {
+          if (ret >= 0)
+            errno = EIO;
+          return -1;
+        }
+      ret = read(fd, buffer, 2);
+      if (ret != 2)
+        {
+          if (ret >= 0)
+            errno = EIO;
+          return -1;
+        }
+      if (buffer[0] != 1)
+        {
+          /* SOCKS server returned wrong version.  */
+          errno = EPROTO;
+          return -1;
+        }
+      if (buffer[1])
+        {
+          /* SOCKS server denied access.  */
+          errno = EACCES;
+          return -1;
+        }
+    }
+
   /* Send request details (rfc-1928, 4).  */
   buffer[0] = 5; /* VER  */
   buffer[1] = 1; /* CMD = CONNECT  */
-- 
2.8.1