Opened 9 years ago

Closed 8 years ago

Last modified 7 years ago

#2602 closed enhancement (worksforme)

DNS Relay

Reported by: F_Ruta Owned by:
Priority: Medium Milestone:
Component: Core Tor/Tor Version:
Severity: Keywords: DNS relay tor-relay
Cc: Actual Points:
Parent ID: Points:
Reviewer: Sponsor:

Description

Dear All,
Most likely a ticket is not the right way to convey this info, but I haven't been able to find any other way.
I had several apps leaking DNS packets so I wrote this simple dnsToTor relay (See below) that if you are interested I could release to the public domain.
I needed it on a window machine so it is written specifically for window; however, porting should be trivial.
On a Win machine, once you have the executable running, it is sufficient to change in the "Internet Protocol (TCP/IP) Properties the DNS setting to "Use the following DNS server address" and setting as IP "127.0.0.1". At this point all apps leaking DNS request will send their DNS request to the relay that will send a SOCKS5 Request to Tor and then generate a reply to the resolver providing the info returned by Tor. This is completely transparent for the apps that doesn't realize that the DNS query is handled by Tor.
The relay only handles the basic DNS query (Type A Ipv4), but it should be sufficient for the most common usage scenarios.
Hope it helps
F.

/*Copyright F.Ruta 2011 */
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <mstcpip.h>
#include <iostream>
#include <string.h>
#include <stdint.h>
using namespace std;

#define DEBUG 0

#define BUF_LEN 1024
#define MAX_QUERY 512
#define MAX_NAME 512

A Query name is in the form of length octet+domain segment
so a query for www.mydomain.com would be encoded as [3]www[8]mydomain[3]com
int convertName( uint8_t const * const query, int8_t * const name, int32_t maxLen ) {

int i,j,len, totalLen;

i = 0;
j = 0;

do {

len = (uint8_t)query[i];
if ( len > 0 ) {

totalLen += len;
if ((j + len) < maxLen ) {

memcpy(&(name[j]), &(query[i+1]), len );
i += len+1;
j += len;
name[j++] = '.';

} else {

return 0;

}

}

} while (len > 0);
name[--j] = '\0';
return strlen((const char *)name);

}

int parseDnsQuery( uint8_t const * const packet, uint16_t *qid, uint8_t *rec, uint8_t * const query, int32_t *queryLen ) {

uint16_t sv;

uint16_t qdcount;

uint8_t qr;
uint8_t opcode;
uint8_t len;
int i;

memcpy( &sv, &packet[0], 2 );
*qid = ntohs(sv);
qr = (packet[2]&0x80)>>7;

opcode = (packet[2]&0x78)>>3;

*rec = (packet[2]&0x01);
Make sure it is a standard query
if (( 0 == qr ) && (0 == opcode)) {

memcpy( &sv, &packet[4], 2 );
qdcount = ntohs(sv);

i = 12;
len = 0;
store the query
while( packet[i++] != '\0' ){len++;}
if ( len < *queryLen ) {

memcpy(query, &(packet[12]), len+1 );

*queryLen = len+1;

} else {

*queryLen = 0;

}

memcpy( &sv, &packet[12+len+1], 2 );

if ( 1 != ntohs(sv)) { Type A, IPv4 address

cout << "DNS request is not for type A, IPv4 address" <<endl;
return -1;

}

memcpy( &sv, &packet[12+len+1+2], 2 );

if ( 1 != ntohs(sv)) { Class IN, Internet

cout << "DNS request is not for class IN" <<endl;
return -1;

}
return len;

} else {

return -1;

}
return 0;

}

int parseDns( uint8_t const * const packet ) {

uint16_t sv;
uint8_t cv;
uint32_t lv;
uint16_t len;
uint16_t type;
uint16_t questions, answers, authority, additional;
int i,j,k;

memcpy( &sv, &packet[0], 2 );
cout << "ID = "<< (int)ntohs(sv) <<endl;
cv = (packet[2]&0x80)>>7;
cout << "QR = "<< (int)cv << " ";
switch (cv) {

case 0 : {

cout << "(Query)";
break;

}
case 1 : {

cout << "(Response)";
break;

}
default: {

break;

}

}
cout << endl;
cv = (packet[2]&0x78)>>3;
cout << "Opcode = "<< (int)cv <<" ";
switch (cv) {

case 0 : {

cout << "QUERY, Standard query"<<endl;
break;

}
case 1 : {

cout << "IQUERY, Inverse query"<<endl;
break;

}
case 2 : {

cout << "STATUS, Server status request"<<endl;
break;

}
case 3 : {

cout << "NA"<<endl;
break;

}
case 4 : {

cout << "Notify"<<endl;
break;

}
case 5 : {

cout << "Update"<<endl;
break;

}
default: {

cout << "Reserved"<<endl;

break;

}

}

cv = (packet[2]&0x04)>>2;

cout << "AA= "<< (int)cv;
switch (cv) {

case 0 : {

cout << "(Not authoritative)";
break;

}
case 1 : {

cout << "(Authoritative)";
break;

}
default: {

break;

}

}

cv = (packet[2]&0x02)>>1;
cout << " TC= "<< (int)cv;
switch (cv) {

case 0 : {

cout << "(Not truncated)";
break;

}
case 1 : {

cout << "(Truncated)";
break;

}
default: {

break;

}

}

cv = (packet[2]&0x01);

cout << " RD= "<< (int)cv;
switch (cv) {

case 0 : {

cout << "(No Recursion)";
break;

}
case 1 : {

cout << "(Recursion)";
break;

}
default: {

break;

}

}

cv = (packet[3]&0x80)>>7;

cout << " RA= "<< (int)cv;
switch (cv) {

case 0 : {

cout << "(Recursion Not Avail)";
break;

}
case 1 : {

cout << "(Recursion Avail)";
break;

}
default: {

break;

}

}
cv = (packet[3]&0x40)>>6;
cout << " Z= "<< (int)cv;

cv = (packet[2]&0x20)>>5;
cout << " AD= "<< (int)cv;
switch (cv) {

case 0 : {

cout << "(Not Auth)";
break;

}
case 1 : {

cout << "(Auth)";
break;

}
default: {

break;

}

}
cv = (packet[2]&0x10)>>4;
cout << " CD= "<< (int)cv;
switch (cv) {

case 0 : {

cout << "(Not Check)";
break;

}
case 1 : {

cout << "(Check)";
break;

}
default: {

break;

}

}
cout << endl;

cv = (packet[3]&0x0F);
cout << "RCODE= "<< (int)cv << " ";
switch (cv) {

case 0 : {

cout << "No Error"<<endl;
break;

}
case 1 : {

cout << "Format Error"<<endl;
break;

}
case 2 : {

cout << "Server failure"<<endl;
break;

}
case 3 : {

cout << "Name Error"<<endl;
break;

}
case 4 : {

cout << "Not Implemented"<<endl;
break;

}
case 5 : {

cout << "Refused"<<endl;
break;

}
default: {

cout << "Undef"<<endl;

break;

}

}

memcpy( &sv, &packet[4], 2 );
questions = ntohs(sv);
cout << "QDCOUNT = "<< questions <<endl;
memcpy( &sv, &packet[6], 2 );
answers = ntohs(sv);
cout << "ANCOUNT = "<< answers <<endl;
memcpy( &sv, &packet[8], 2 );
authority = ntohs(sv);
cout << "NSCOUNT = "<< authority <<endl;
memcpy( &sv, &packet[10], 2 );
additional = ntohs(sv);
cout << "ARCOUNT = "<< additional <<endl;

i = 12;
j = 12;
uint8_t buf[BUF_LEN];
int8_t name[MAX_NAME];

cout << "Questions : " <<endl;

for (k=0; k < questions;k++ ) {

len = 0;
j = i;
while( packet[j++] != '\0' ){len++;}

memcpy(buf, &(packet[i]), len+1 );

convertName(buf, name, MAX_NAME-1);

cout << k << ") Name: " << name << " ";

i += len+1;

memcpy( &sv, &packet[i], 2 );
cout << "Type = "<< ntohs(sv) <<" ";

i += 2;
memcpy( &sv, &packet[i], 2 );

cout << "Class = "<< ntohs(sv) <<endl;

i += 2;

}

cout << "Answers : " <<endl;

for (k=0; k < answers;k++ ) {

len = 0;
j = i;
while( packet[j++] != '\0' ){len++;}

memcpy(buf, &(packet[i]), len+1 );

buf[len+2] = '\0'; redundant
convertName(buf, name, MAX_NAME-1);

cout << k << ") Name: " << name << " ";

i += len+1;

memcpy( &sv, &packet[i], 2 );

type = ntohs(sv);

cout << "Type = "<< ntohs(sv) <<" ";

i += 2;
memcpy( &sv, &packet[i], 2 );

cout << "Class = "<< ntohs(sv) <<" ";

i += 2;
memcpy( &lv, &packet[i], 4 );

cout << "TTL = "<< ntohl(lv) <<endl;

i += 4;

memcpy( &sv, &packet[i], 2 );

len = ntohs(sv);

cout << " RdataLen = "<< ntohs(sv) <<" ";

i += 2;
memcpy( buf, &packet[i], len );
i += len;
switch (type) {

case 1: A records
{

cout << "Data: " << (unsigned int)((unsigned char)buf[0])<<"."<< (unsigned int)((unsigned char)buf[1])<<"."<< (unsigned int)((unsigned char)buf[2])<<"."<< (unsigned int)((unsigned char)buf[3]) << endl;
break;

}
default :
{

cout << "Data: ";
for(j=0;j<len;j++) cout << hex <<buf[j];
cout << endl;
break;

}

}

}

return 0;

}

int createDnsAnswer( uint16_t status, uint8_t const * const query, uint16_t qid, int8_t rec, uint32_t addr, uint8_t * const packet, int32_t maxLen ) {

uint16_t sv;
uint8_t cv;
uint32_t lv;
int i,len;

i = 0;

if ( (12 + (int)strlen((const char *)query)+1 + 10 + 4) > maxLen ) {

cerr << "Insufficient buffer space to build DNS answer"<<endl;
return -1;
}

sv = htons(qid);

memcpy( &packet[i], &sv, 2 ); ID
i+= 2;

cv = 0;
cv |= 1 << 7; Response
cv |= 1 << 2;
This is an authoritative answer

cv |= rec; Recursion was requested

packet[i++] = cv;

cv = 0;

cv |= rec << 7; Recursion is available

cv |= 1 << 5; Answer is authenticated
if ( 0 != status ) {

cv |= 0x02; Hardcode error to Server Failure

}
packet[i++] = cv;

sv = htons(1); 1 question was present
memcpy( &packet[i], &sv, 2 );
i+=2;
sv = htons(1);
Only 1 answer is present
memcpy( &packet[i], &sv, 2 );
i+=2;
sv = 0; Zero Authority Responses are present
memcpy( &packet[i], &sv, 2 );
i+=2;
sv = 0;
Zero Additional Responses are present
memcpy( &packet[i], &sv, 2 );
i+=2;
Populate the Queries portion
len = strlen((const char *)query);
DNS query ends with a trailing zero

memcpy( &packet[i], query, len+1 );Name
i+=len+1;

sv = htons(1); Type A
memcpy( &packet[i], &sv, 2 );
i+= 2;
sv = htons(1);
Class IN
memcpy( &packet[i], &sv, 2 );
i+= 2;

Populate the Answers portion
len = strlen((const char *)query);
DNS query ends with a trailing zero

memcpy( &packet[i], query, len+1 );Name
i+=len+1;

sv = htons(1); Type A
memcpy( &packet[i], &sv, 2 );
i+= 2;
sv = htons(1);
Class IN
memcpy( &packet[i], &sv, 2 );
i+= 2;

lv = htonl(60); TTL Hardcoded to 1 min
memcpy( &packet[i], &lv, 4 );
i+= 4;

As this is a response only for class A query, the rdata len is 4
sv = htons(4);
RDLENGTH
memcpy( &packet[i], &sv, 2 );
i+= 2;

memcpy( &packet[i], &addr, sizeof(addr));
i+= sizeof(addr);

return i;

}

int connectToSockProxy( int8_t const * const address, int8_t const * const port, SOCKET *outSocket ) {

int status;

uint8_t buf[BUF_LEN];

ADDRINFO hints, *addrInfo;
SOCKET sock;
int optval;

if ( INVALID_SOCKET == *outSocket ) {

if ( DEBUG ) cout << "Initializing TOR socket" << endl;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;PF_UNSPEC
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
hints.ai_protocol = IPPROTO_TCP;

status = getaddrinfo((char *)address, (char *)port, &hints, &addrInfo);
if ( 0 != status ) {

cerr << "getaddrinfo: " << WSAGetLastError() << endl;
WSACleanup();
return -1;

}
sock = socket(addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol);
if ( INVALID_SOCKET == sock ) {

cerr << "socket -TOR client: " << WSAGetLastError() << endl;
freeaddrinfo(addrInfo);
return -1;

}

Set SO_REUSEADDR to true (1):
optval = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof optval);


if ( DEBUG ) cout << "Connect to the TOR proxy" << endl;

status = connect(sock, addrInfo->ai_addr, (int)addrInfo->ai_addrlen);
if ( SOCKET_ERROR == status ) {

cerr << "connect: " << WSAGetLastError() << endl;

freeaddrinfo(addrInfo);

closesocket(sock);
return -1;

}

freeaddrinfo(addrInfo);


} else {

if ( DEBUG ) cout << "Reusing previous socket " << endl;
sock = *outSocket;

}

Send the initial SOCKS 5 connect request

buf[0] = 0x5; Ver 5
buf[1] = 0x1;
1 Method
buf[2] = 0x0; No authentication

if ( DEBUG ) cout << "Sending connect request to the TOR Proxy" << endl;
status = send(sock, (const char *) buf, (int) 3, 0);
if ( SOCKET_ERROR == status ) {

cerr << "send -TOR client: " << WSAGetLastError() << endl;
closesocket(sock);
return -1;

}

status = recv(sock, (char *) buf, BUF_LEN, 0);
if ( SOCKET_ERROR == status ) {

if ( 0x5 != buf[0] ) {

cerr << "Received invalid header in SOCKS-5 Connection Established response" << endl;

closesocket(sock);
return -1;

} else if ( 0xFF == buf[1] ) {

cerr << "Received invalid auth method in Connection Established response" << endl;

closesocket(sock);
return -1;

}

} else if (0 == status) {
cerr << "Connection closed" << endl;
closesocket(sock);
return -1;
}
if ( DEBUG ) cout << "Received connection ack from TOR Proxy" << endl;
*outSocket = sock;

return 0;

}

int bindToDnsPort( int8_t const * const address, int8_t const * const port, SOCKET *outSocket ) {

int status;
ADDRINFO hints, *addrInfo;
SOCKET sock;

int optval;

*outSocket = INVALID_SOCKET;

Bind to localhost and default DNS port
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
PF_UNSPEC
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;

hints.ai_protocol = IPPROTO_UDP;

status = getaddrinfo((char *)address, (char *)port, &hints, &addrInfo);
if ( 0 != status ) {

cerr << "getaddrinfo -Local DNS Listener: " << WSAGetLastError() << endl;

freeaddrinfo(addrInfo);

return -1;

}

sock = socket(addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol);PF_UNSPEC
if ( INVALID_SOCKET == sock ) {

cerr << "socket -Local DNS Listener: " << WSAGetLastError() << endl;
freeaddrinfo(addrInfo);
return -1;

}

Set SO_REUSEADDR to true (1):
optval = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof optval);

status = bind( sock, addrInfo->ai_addr, (int)addrInfo->ai_addrlen);

if ( SOCKET_ERROR == status ) {

cerr << "bind -Local DNS Listener: " << WSAGetLastError() << endl;
freeaddrinfo(addrInfo);

closesocket(sock);
return -1;

}

freeaddrinfo(addrInfo);

*outSocket = sock;
return 0;

}

int main(int argc, char* argv[])
{

WSADATA wsaData;
int status;

struct sockaddr_in from;
int len, queryLen, nameLen;

uint32_t addr;

SOCKET sockIn = INVALID_SOCKET;

SOCKET sockOut = INVALID_SOCKET;
int n;

FD_SET fds;

int8_t name[MAX_NAME];
uint8_t query[MAX_QUERY];
uint16_t qid;
uint8_t recursive;

uint8_t buf[BUF_LEN+1];
struct timeval tv;

const char *localAddress = "127.0.0.1";

const char *dnsPort = "53"; 53 - DNS

char socksPort[6] = {'9','0','5','0','\0'}; SOCKS 5 Proxy - 9050

if ( 3 < argc ) {

cerr << "Invalid number of arguments" << endl;
cerr << argv[0] << " [-p proxy-port]" << endl;
return -1;

} else if ( 3 == argc ) {

if (!strncmp("-p", argv[1], strlen("-p"))) {

if ( 0 != atoi(argv[2])) {

(void)strncpy(socksPort, argv[2], sizeof(socksPort)-1);

} else {
cerr << "Invalid argument:" << argv[2] << endl;
cerr << argv[0] << " [-p proxy-port]" << endl;

return -1;

}

} else {

cerr << "Invalid argument:" << argv[1] << endl;
cerr << argv[0] << " [-p proxy-port]" << endl;
return -1;

}

} else if ( 2 == argc ) {

cerr << "Invalid argument:" << argv[1] << endl;
cerr << argv[0] << " [-p proxy-port]" << endl;
return -1;

}

status = WSAStartup(MAKEWORD(2, 2), &wsaData);
if ( 0 != status ) {

cerr << "Winsock dll not found:"<< status <<endl;

return -1;

}

if (LOBYTE(wsaData.wVersion) != 2
HIBYTE(wsaData.wVersion) != 2 ) {

cerr << "Winsock version 2.2 not supported"<<endl;
WSACleanup();
return -1;

}

status = bindToDnsPort((int8_t *)localAddress, (int8_t *)dnsPort, &sockIn );
if ( 0 != status ) {

cerr << "Unable to bind as a local DNS server" << endl;

WSACleanup();
return -1;
}


do {

FD_ZERO(&fds);

FD_SET(sockIn, &fds);
FD_SET(sockOut, &fds);

n = sockIn +1;
status = select(n, &fds, NULL, NULL, NULL);

if ( SOCKET_ERROR == status ) {

cerr << "select: " << WSAGetLastError() << endl;
closesocket(sockIn);closesocket(sockOut);
WSACleanup();
return -1;

}

if (FD_ISSET(sockIn, &fds)) {

Received a DNS Query

if ( DEBUG ) cout << "Received a DNS request" <<endl;

len = sizeof(struct sockaddr_in);
status = recvfrom(sockIn, (char *) buf, BUF_LEN, 0, (struct sockaddr *) &from, &len );

if ( SOCKET_ERROR == status ) {

if ( 10054 != WSAGetLastError()) { Ignore reset connection

cerr << "recvfrom -Local DNS Listener: " << WSAGetLastError() << endl;

}

status = 0;

continue;

}

if ( DEBUG ) parseDns( buf);
queryLen = MAX_QUERY-1;

status = parseDnsQuery( buf, &qid, &recursive, query, &queryLen );
if ( 0 > status ) {

status = 0;
cerr << "Unsupported request" << endl;
continue;

}
if ( 0 < queryLen ) {

nameLen = convertName( query, name, MAX_NAME-1);
if ( UCHAR_MAX < nameLen ) {

cerr << "Queried Domain name length exceeds max length" << endl;
continue;

}
if (DEBUG) cout << "Connecting to the TOR proxy" << endl;

Connect to the SOCKS5 Server at localhost and Tor-Socks proxy port

status = connectToSockProxy((int8_t *)localAddress, (int8_t *)socksPort, &sockOut );
if ( 0 != status ) {

cerr << "Unable to connect to the Tor server" << endl;

continue;

}

Send the UDP Resolve request

buf[0] = 0x05; Ver 5

buf[1] = 0xF0; Cmd Resolve -- TOR Extension

buf[2] = 0x00; Reserved
buf[3] = 0x03;
ATYP = DomainName
buf[4] = (uint8_t) nameLen;

memcpy( &(buf[5]), name, nameLen);

buf[5+nameLen] = 0; Still need to add a fake port
buf[5+nameLen+1] = 0;

len = nameLen + 5 + 2;

if (DEBUG) cout << "Sending Resolve for << name <<? " << len << " to the TOR proxy" << endl;
FD_ZERO(&fds);
FD_SET(sockOut, &fds);
Forward the DNS request to the Socks proxy

status = send(sockOut, (char *) buf, len, 0);

if ( SOCKET_ERROR == status ) {

cerr << "send -TOR client: " << WSAGetLastError() << endl;

cerr <<"Unable to forward request" << endl;
status = 0;
continue;

}
There's no way to correlate the TOR response with a request, we need to wait
for the answer to this request before handling any new query.
Wait for response/requests on the Tor sockets

if ( DEBUG ) cout << "Waiting for TOR response" <<endl;

tv.tv_sec = 10;

tv.tv_usec = 500000;

n = sockOut +1;

status = select(n, &fds, NULL, NULL, &tv );
if ( SOCKET_ERROR == status ) {

cerr << "select: " << WSAGetLastError() << endl;
closesocket(sockIn);closesocket(sockOut);
WSACleanup();
return -1;

} else if ( 0 == status ) {

cerr << "No response received from Tor Proxy" << endl;
closesocket(sockOut);
sockOut = INVALID_SOCKET;
continue;

}

if (FD_ISSET(sockOut, &fds)) {

if ( DEBUG ) cout << "Received data from TOR " << endl;

Receive a response from the TOR proxy
status = recv(sockOut, (char *) buf, BUF_LEN, 0 );

if ( SOCKET_ERROR == status ) {

cerr << "recv -TOR client: " << WSAGetLastError() << endl;
status = 0;

closesocket(sockOut);

sockOut = INVALID_SOCKET;

continue;

}

status = 0;

if ( 0x05 != buf[0] ) {

status = 1;
cerr <<"Invalid Header in Tor Response" << endl;

}

else if ( 0x00 != buf[1] ) {

status = 1;
cerr <<"The Tor Proxy returned an error:" <<(int)((unsigned char)buf[1]) << endl;

}

else if ( 0x01 != buf[3] ) {

status = 1;
cerr <<"The Tor Proxy returned an invalid ATYP:" <<(int)((unsigned char)buf[3]) << endl;

}

memcpy( &addr, &buf[4], sizeof(addr));

if ( DEBUG ) cout << "Creating a DNS response " << endl;

len = createDnsAnswer((uint16_t)status, query, qid, recursive, addr, buf, BUF_LEN);
if ( DEBUG) parseDns( buf );

Forward the DNS response

sendto( sockIn, (const char *) buf, len, 0, (struct sockaddr *) &from, sizeof(struct sockaddr_in));

Disconnect from the Socks proxy
if (DEBUG) cout << "Disconnecting from the TOR proxy" <<endl;

status = shutdown(sockOut, SD_SEND);

if ( SOCKET_ERROR == status ) {

cerr << "shutdown: " << WSAGetLastError() << endl;

}

if (INVALID_SOCKET != sockOut) closesocket(sockOut);
sockOut = INVALID_SOCKET;


}

}

/*

} else if (FD_ISSET(sockOut, &fds)) {

This should not happen unless the Tor proxy die

status = recv(sockOut, (char *)buf, BUF_LEN, 0 );
if ( SOCKET_ERROR == status ) {

cerr << "Connection with Tor server dropped";
status = 0;
continue;

}
discard

}

*/

}

} while (status >= 0);

shutdown the send half of the connection since no more data will be sent
status = shutdown(sockOut, SD_SEND);
if ( SOCKET_ERROR == status ) {

cerr << "shutdown: " << WSAGetLastError() << endl;

}

if (INVALID_SOCKET != sockIn) closesocket(sockIn);

if (INVALID_SOCKET != sockOut) closesocket(sockOut);

WSACleanup();
return 0;

}

Child Tickets

Change History (6)

comment:1 Changed 9 years ago by Sebastian

Hey there, and thanks for your contribution. Did you see Tor's DNSPort config option? Setting "DNSPort 53" in your torrc should work very much like using your proxy, unless I am missing something. Please advise?

comment:2 in reply to:  1 Changed 9 years ago by cypherpunks

Setting "DNSPort 53" in your torrc should work very much like using your proxy, unless I am missing something. Please advise?

I needed it on a window machine so it is written specifically for window

comment:3 Changed 8 years ago by nickm

DNSPort is supposed to work on Windows. Does it not work on Windows for you?

comment:4 Changed 8 years ago by nickm

Resolution: worksforme
Status: newclosed

This looks like functionality already available in Tor via the DNSPort configuration option. If that isn't true, or if dnsport isn't working for you, please let us know. Closing for now.

comment:5 Changed 7 years ago by nickm

Keywords: tor-relay added

comment:6 Changed 7 years ago by nickm

Component: Tor RelayTor
Note: See TracTickets for help on using tickets.