Skip to content

Commit c1043c8

Browse files
committed
Added '-4' and '-6' options to bind to a specific AF
Fixes issue #18
1 parent 7dce1f0 commit c1043c8

1 file changed

Lines changed: 77 additions & 21 deletions

File tree

src/dohd.c

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -826,14 +826,27 @@ static void dohd_reply(int fd, short __attribute__((unused)) revents,
826826
req = (struct req_slot *)arg;
827827
cd = req->owner;
828828

829-
/* Cancel timeout timer - we got a response */
829+
/* Cancel timeout timer - we got a response (or error) */
830830
if (req->timeout_timer) {
831831
evquick_deltimer(req->timeout_timer);
832832
req->timeout_timer = NULL;
833833
}
834834

835835
len = recv(fd, buff, bufsz, 0);
836+
if (len <= 0) {
837+
/* recv() error or connection closed */
838+
if (len < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
839+
/* Socket timeout (SO_RCVTIMEO) - not a real error, just no data yet.
840+
* This shouldn't happen with epoll (event only fires when data ready),
841+
* but handle gracefully. */
842+
dohprint(DOH_WARN, "DNS socket recv timeout (fd %d)", fd);
843+
}
844+
/* Don't log for normal close (len == 0) or transient errors */
845+
goto destroy;
846+
}
836847
if (len < 16) {
848+
/* DNS response too short to be valid */
849+
dohprint(DOH_WARN, "DNS response too short (%d bytes)", len);
837850
goto destroy;
838851
}
839852

@@ -1220,10 +1233,12 @@ static void usage(const char *name)
12201233

12211234
fprintf(stderr, "%s, DNSoverHTTPS minimalist daemon.\n", name);
12221235
fprintf(stderr, "License: AGPL\n");
1223-
fprintf(stderr, "Usage: %s -c cert -k key [-p port] [-d dnsserver] [-F] [-u user] [-V] [-v] [-h]\n", name);
1236+
fprintf(stderr, "Usage: %s -c cert -k key [-p port] [-d dnsserver] [-4|-6] [-F] [-u user] [-V] [-v] [-h]\n", name);
12241237
fprintf(stderr, "\t'cert' and 'key': certificate and its private key.\n");
12251238
fprintf(stderr, "\t'user' : login name (when running as root) to switch to (dropping permissions)\n");
12261239
fprintf(stderr, "\tDefault values: port=8053 dnsserver=\"::1\"\n");
1240+
fprintf(stderr, "\tUse '-4' to force IPv4 only\n");
1241+
fprintf(stderr, "\tUse '-6' to force IPv6 only (default: dual-stack)\n");
12271242
fprintf(stderr, "\tUse '-h' for help\n");
12281243
fprintf(stderr, "\tUse '-V' to show version\n");
12291244
fprintf(stderr, "\tUse '-v' for verbose mode\n");
@@ -1237,7 +1252,9 @@ int main(int argc, char *argv[])
12371252
char *cert = NULL, *key = NULL;
12381253
char *user = NULL;
12391254
uint16_t port = DOH_PORT;
1240-
struct sockaddr_in6 serv_addr;
1255+
int ip_version = 0; /* 0 = dual-stack (default), 4 = IPv4 only, 6 = IPv6 only */
1256+
struct sockaddr_in6 serv_addr6;
1257+
struct sockaddr_in serv_addr4;
12411258
int option_idx;
12421259
int c;
12431260
int foreground = 0;
@@ -1253,10 +1270,12 @@ int main(int argc, char *argv[])
12531270
{"user", 1, 0, 'u'},
12541271
{"verbose", 0, 0, 'v'},
12551272
{"do-not-fork", 0, 0, 'F'},
1273+
{"ipv4", 0, 0, '4'},
1274+
{"ipv6", 0, 0, '6'},
12561275
{NULL, 0, 0, '\0' }
12571276
};
12581277
while(1) {
1259-
c = getopt_long(argc, argv, "hvVc:k:p:d:u:F" , long_options, &option_idx);
1278+
c = getopt_long(argc, argv, "46hvVc:k:p:d:u:F" , long_options, &option_idx);
12601279
if (c < 0)
12611280
break;
12621281
switch(c) {
@@ -1324,6 +1343,12 @@ int main(int argc, char *argv[])
13241343
case 'F':
13251344
foreground = 1;
13261345
break;
1346+
case '4':
1347+
ip_version = 4;
1348+
break;
1349+
case '6':
1350+
ip_version = 6;
1351+
break;
13271352
default:
13281353
fprintf(stderr, "Unrecognized option '%c'\n\n\n",c);
13291354
usage(argv[0]);
@@ -1388,25 +1413,56 @@ int main(int argc, char *argv[])
13881413
}
13891414
dohprint(DOH_NOTICE, "Using %u DNS servers", n_resolvers);
13901415

1391-
/* Create listening socket */
1392-
lfd = socket(AF_INET6, SOCK_STREAM, 0);
1393-
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(int));
1394-
setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, (char *) &yes, sizeof(int));
1395-
if (lfd < 0) {
1396-
dohprint(DOH_ERR, "ERROR: failed to create DoH socket\n");
1397-
return -1;
1398-
}
1399-
dohprint(DOH_DEBUG, "Main HTTPS socket created.");
1416+
/* Create listening socket based on IP version preference */
1417+
if (ip_version == 4) {
1418+
/* IPv4 only */
1419+
lfd = socket(AF_INET, SOCK_STREAM, 0);
1420+
if (lfd < 0) {
1421+
dohprint(DOH_ERR, "ERROR: failed to create IPv4 DoH socket\n");
1422+
return -1;
1423+
}
1424+
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(int));
1425+
setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, (char *) &yes, sizeof(int));
14001426

1401-
/* Fill in the server address */
1402-
memset(&serv_addr, 0, sizeof(serv_addr));
1403-
serv_addr.sin6_family = AF_INET6; /* using IPv4 */
1404-
serv_addr.sin6_port = htons(port); /* on DEFAULT_PORT */
1427+
memset(&serv_addr4, 0, sizeof(serv_addr4));
1428+
serv_addr4.sin_family = AF_INET;
1429+
serv_addr4.sin_addr.s_addr = INADDR_ANY;
1430+
serv_addr4.sin_port = htons(port);
14051431

1406-
/* Bind the server socket to our port */
1407-
if (bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
1408-
fprintf(stderr, "ERROR: failed to bind\n");
1409-
return -1;
1432+
if (bind(lfd, (struct sockaddr*)&serv_addr4, sizeof(serv_addr4)) == -1) {
1433+
dohprint(DOH_ERR, "ERROR: failed to bind IPv4 socket\n");
1434+
return -1;
1435+
}
1436+
dohprint(DOH_NOTICE, "Listening on IPv4 only, port %u", port);
1437+
} else {
1438+
/* IPv6 (with or without dual-stack) */
1439+
lfd = socket(AF_INET6, SOCK_STREAM, 0);
1440+
if (lfd < 0) {
1441+
dohprint(DOH_ERR, "ERROR: failed to create IPv6 DoH socket\n");
1442+
return -1;
1443+
}
1444+
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(int));
1445+
setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, (char *) &yes, sizeof(int));
1446+
1447+
if (ip_version == 6) {
1448+
/* IPv6 only - disable dual-stack */
1449+
int ipv6only = 1;
1450+
setsockopt(lfd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only));
1451+
dohprint(DOH_NOTICE, "Listening on IPv6 only, port %u", port);
1452+
} else {
1453+
/* Dual-stack (default) */
1454+
dohprint(DOH_NOTICE, "Listening on IPv4+IPv6 (dual-stack), port %u", port);
1455+
}
1456+
1457+
memset(&serv_addr6, 0, sizeof(serv_addr6));
1458+
serv_addr6.sin6_family = AF_INET6;
1459+
serv_addr6.sin6_addr = in6addr_any;
1460+
serv_addr6.sin6_port = htons(port);
1461+
1462+
if (bind(lfd, (struct sockaddr*)&serv_addr6, sizeof(serv_addr6)) == -1) {
1463+
dohprint(DOH_ERR, "ERROR: failed to bind IPv6 socket\n");
1464+
return -1;
1465+
}
14101466
}
14111467
dohprint(DOH_DEBUG, "Main HTTPS socket is bound.");
14121468

0 commit comments

Comments
 (0)