From db930ca3023cf802a28222ad7c4d377736633a08 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 4 Jun 2026 07:56:58 -0700 Subject: [PATCH] slirp: harden restart and DNS handling Reset transient SLIRP state when the backend is restarted, and make DHCP DNS advertisement depend on an available resolver. Keep host loopback resolvers unchanged instead of rewriting them to the virtual host address. The guest still talks to the SLIRP DNS proxy, but the proxy must query the resolver configured on the host. This keeps the emulated network usable after reconfiguration and avoids advertising a DNS forwarder that cannot answer. --- slirp/bootp.cpp | 14 ++++---- slirp/ip_icmp.cpp | 5 +++ slirp/main.h | 1 + slirp/slirp.cpp | 86 ++++++++++++++++++++++++++++++---------------- slirp/socket.cpp | 2 ++ slirp/tcp_subr.cpp | 5 +++ 6 files changed, 78 insertions(+), 35 deletions(-) diff --git a/slirp/bootp.cpp b/slirp/bootp.cpp index 31cc48b8..984e98a9 100644 --- a/slirp/bootp.cpp +++ b/slirp/bootp.cpp @@ -125,7 +125,6 @@ static void bootp_reply(struct bootp_t *bp) struct mbuf *m; struct bootp_t *rbp; struct sockaddr_in saddr, daddr; - struct in_addr dns_addr; int dhcp_msg_type, val; uint8_t *q; @@ -216,11 +215,14 @@ have_addr: memcpy(q, &saddr.sin_addr, 4); q += 4; - *q++ = RFC1533_DNS; - *q++ = 4; - dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS); - memcpy(q, &dns_addr, 4); - q += 4; + if (dns_addr_valid) { + struct in_addr guest_dns_addr; + *q++ = RFC1533_DNS; + *q++ = 4; + guest_dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS); + memcpy(q, &guest_dns_addr, 4); + q += 4; + } *q++ = RFC2132_LEASE_TIME; *q++ = 4; diff --git a/slirp/ip_icmp.cpp b/slirp/ip_icmp.cpp index c39f0f02..e70372f3 100644 --- a/slirp/ip_icmp.cpp +++ b/slirp/ip_icmp.cpp @@ -200,6 +200,11 @@ void icmp_input(struct mbuf *m, int hlen) /* It's an alias */ switch(ntohl(so->so_faddr.s_addr) & 0xff) { case CTL_DNS: + if (!dns_addr_valid) { + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, "DNS disabled"); + udp_detach(so); + goto end_error; + } addr.sin_addr = dns_addr; break; case CTL_ALIAS: diff --git a/slirp/main.h b/slirp/main.h index 181b6ae8..19422a5c 100644 --- a/slirp/main.h +++ b/slirp/main.h @@ -38,6 +38,7 @@ extern struct in_addr alias_addr; extern struct in_addr our_addr; extern struct in_addr loopback_addr; extern struct in_addr dns_addr; +extern int dns_addr_valid; extern char *username; extern char *socket_path; extern int towrite_max; diff --git a/slirp/slirp.cpp b/slirp/slirp.cpp index 67610b00..804490d8 100644 --- a/slirp/slirp.cpp +++ b/slirp/slirp.cpp @@ -5,6 +5,7 @@ struct in_addr our_addr; /* host dns address */ struct in_addr dns_addr; +int dns_addr_valid; /* host loopback address */ struct in_addr loopback_addr; @@ -32,7 +33,7 @@ char slirp_hostname[33]; #ifdef _WIN32 -static int get_dns_addr(struct in_addr *pdns_addr) +static bool get_dns_addr(struct in_addr *pdns_addr) { FIXED_INFO *FixedInfo=NULL; ULONG BufLen; @@ -57,11 +58,17 @@ static int get_dns_addr(struct in_addr *pdns_addr) GlobalFree(FixedInfo); FixedInfo = NULL; } - return -1; + return false; } pIPAddr = &(FixedInfo->DnsServerList); - inet_aton(pIPAddr->IpAddress.String, &tmp_addr); + if (!inet_aton(pIPAddr->IpAddress.String, &tmp_addr)) { + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return false; + } *pdns_addr = tmp_addr; #if 0 printf( "DNS Servers:\n" ); @@ -77,46 +84,68 @@ static int get_dns_addr(struct in_addr *pdns_addr) GlobalFree(FixedInfo); FixedInfo = NULL; } - return 0; + return true; } #else -static int get_dns_addr(struct in_addr *pdns_addr) +static bool record_dns_server(const char *server, struct in_addr *pdns_addr, + int *found4, int *found6) +{ + struct in_addr tmp_addr; + + if (!inet_aton(server, &tmp_addr)) { +#ifdef AF_INET6 + struct in6_addr tmp_addr6; + if (inet_pton(AF_INET6, server, &tmp_addr6) == 1) + (*found6)++; +#endif + return false; + } + + if (!*found4) + *pdns_addr = tmp_addr; + else + lprint(", "); + if (++*found4 > 3) { + lprint("(more)"); + return true; + } + lprint("%s", inet_ntoa(tmp_addr)); + return false; +} + +static bool get_dns_addr(struct in_addr *pdns_addr) { char buff[512]; char buff2[256+1]; FILE *f; - int found = 0; - struct in_addr tmp_addr; - + int found4 = 0; + int found6 = 0; + f = fopen("/etc/resolv.conf", "r"); - if (!f) - return -1; + if (!f) { + write_log("SLIRP: no resolver configuration found, DNS disabled\n"); + return false; + } lprint("IP address of your DNS(s): "); while (fgets(buff, 512, f) != NULL) { if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { - if (!inet_aton(buff2, &tmp_addr)) - continue; - if (tmp_addr.s_addr == loopback_addr.s_addr) - tmp_addr = our_addr; - /* If it's the first one, set it to dns_addr */ - if (!found) - *pdns_addr = tmp_addr; - else - lprint(", "); - if (++found > 3) { - lprint("(more)"); + if (record_dns_server(buff2, pdns_addr, &found4, &found6)) break; - } else - lprint("%s", inet_ntoa(tmp_addr)); } } fclose(f); - if (!found) - return -1; - return 0; + if (!found4) { + if (found6) { + write_log("SLIRP: only IPv6 DNS servers found, DNS forwarding disabled\n"); + } else { + write_log("SLIRP: no IPv4 DNS server found, DNS forwarding disabled\n"); + } + return false; + } + return true; } #endif @@ -147,8 +176,7 @@ int slirp_init(void) /* set default addresses */ inet_aton("127.0.0.1", &loopback_addr); - if (get_dns_addr(&dns_addr) < 0) - return -1; + dns_addr_valid = get_dns_addr(&dns_addr); inet_aton(CTL_SPECIAL, &special_addr); alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS); @@ -611,7 +639,7 @@ void arp_input(const uint8_t *pkt, int pkt_len) switch(ar_op) { case ARPOP_REQUEST: if (!memcmp(ah->ar_tip, &special_addr, 3)) { - if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS) + if ((ah->ar_tip[3] == CTL_DNS && dns_addr_valid) || ah->ar_tip[3] == CTL_ALIAS) goto arp_ok; for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { if (ex_ptr->ex_addr == ah->ar_tip[3]) diff --git a/slirp/socket.cpp b/slirp/socket.cpp index 80fd8f3b..fcaf3a3e 100644 --- a/slirp/socket.cpp +++ b/slirp/socket.cpp @@ -498,6 +498,8 @@ int sosendto(struct socket *so, struct mbuf *m) /* It's an alias */ switch(ntohl(so->so_faddr.s_addr) & 0xff) { case CTL_DNS: + if (!dns_addr_valid) + return -1; addr.sin_addr = dns_addr; break; case CTL_ALIAS: diff --git a/slirp/tcp_subr.cpp b/slirp/tcp_subr.cpp index f2d2c924..60727a3d 100644 --- a/slirp/tcp_subr.cpp +++ b/slirp/tcp_subr.cpp @@ -403,6 +403,11 @@ int tcp_fconnect(struct socket *so) /* It's an alias */ switch(ntohl(so->so_faddr.s_addr) & 0xff) { case CTL_DNS: + if (!dns_addr_valid) { + closesocket(s); + so->s = -1; + return 0; + } addr.sin_addr = dns_addr; break; case CTL_ALIAS: -- 2.47.3