From a453ae105aff6436f3978f375fced0c7399b3030 Mon Sep 17 00:00:00 2001 From: rakslice Date: Sat, 14 Nov 2020 02:16:41 -0800 Subject: [PATCH 1/7] slirp: resolve .local suffix DNS requests using the host's name resolution (cherry picked from commit e2a6a4c1177e252bc38221c87bb99a599e8761a1) --- BasiliskII/src/slirp/socket.c | 286 ++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) diff --git a/BasiliskII/src/slirp/socket.c b/BasiliskII/src/slirp/socket.c index f3d10e53..afe72e9a 100755 --- a/BasiliskII/src/slirp/socket.c +++ b/BasiliskII/src/slirp/socket.c @@ -14,6 +14,26 @@ #include #endif +#define DEBUG_HOST_RESOLVED_DNS 0 + +/** + * DNS requests for these domain suffixes will be + * looked up on the host to allow for host-supported DNS alternatives + * (e.g. MDNS, hosts file, etc.) + **/ +static const char * host_resolved_domain_suffixes[] = { + ".local.", + NULL // list ending +}; + +#define DOT_LOCAL_TTL 60 // In seconds. Keep this short as data is very dynamic and requerying is cheap + +#if DEBUG_HOST_RESOLVED_DNS +#define D(...) printf(__VA_ARGS__); fflush(stdout); +#else +#define D(...) +#endif + void so_init() { @@ -482,6 +502,270 @@ sorecvfrom(so) } /* if ping packet */ } + +struct DNS_HEADER +{ + unsigned short id; // identification number + + unsigned char rd :1; // recursion desired + unsigned char tc :1; // truncated message + unsigned char aa :1; // authoritive answer + unsigned char opcode :4; // purpose of message + unsigned char qr :1; // query/response flag + + unsigned char rcode :4; // response code + unsigned char cd :1; // checking disabled + unsigned char ad :1; // authenticated data + unsigned char z :1; // its z! reserved + unsigned char ra :1; // recursion available + + unsigned short q_count; // number of question entries + unsigned short ans_count; // number of answer entries + unsigned short auth_count; // number of authority entries + unsigned short add_count; // number of resource entries +}; + +struct QUESTION +{ + unsigned short qtype; + unsigned short qclass; +}; + +#pragma pack(push, 1) +struct R_DATA +{ + unsigned short type; + unsigned short _class; + unsigned int ttl; + unsigned short data_len; +}; +#pragma pack(pop) + +#define POP_STRUCT(vartype, varname, data, len) \ + assert(len >= sizeof(vartype)); \ + vartype varname; \ + memcpy(&varname, data, sizeof(vartype)); \ + data += sizeof(vartype); \ + len -= sizeof(vartype) + + +static void inject_udp_packet_to_guest(struct socket * so, struct sockaddr_in addr, caddr_t packet_data, int packet_len) { + struct mbuf *m; + int len; + + if (!(m = m_get())) return; + m->m_data += if_maxlinkhdr; + + len = M_FREEROOM(m); + + if (packet_len > len) { + packet_len = (m->m_data - m->m_dat) + m->m_len + packet_len + 1; + m_inc(m, packet_len); + len = M_FREEROOM(m); + } + + assert(len >= packet_len); + m->m_len = packet_len; + memcpy(m->m_data, packet_data, packet_len); + + udp_output(so, m, &addr); +} + +/* Decode hostname from the format used in DNS + e.g. "\009something\004else\003com" for "something.else.com." */ +static char * decode_dns_name(char * data) { + + int query_str_len = strlen(data); + char * decoded_name_str = malloc(query_str_len + 1); + if (decoded_name_str == NULL) { + D("decode_dns_name(): out of memory\n"); + return NULL; // oom + } + + char * decoded_name_str_pos = decoded_name_str; + while (*data != '\0') { + int part_len = *data++; + query_str_len--; + if (query_str_len < part_len) { + D("decode_dns_name(): part went off the end of the string\n"); + free(decoded_name_str); + return NULL; + } + memcpy(decoded_name_str_pos, data, part_len); + decoded_name_str_pos[part_len] = '.'; + decoded_name_str_pos += part_len + 1; + query_str_len -= part_len; + data += part_len; + } + *decoded_name_str_pos = '\0'; + return decoded_name_str; +} + +/** Take a look at a UDP DNS request the client has made and see if we want to resolve it internally. + * Returns TRUE if the request has been internally and can be dropped, + * FALSE otherwise + **/ +static BOOL resolve_dns_request(struct socket * so, struct sockaddr_in addr, caddr_t data, int len) { + BOOL drop_dns_request = FALSE; + + D("Checking outgoing DNS UDP packet\n"); + + if (len < sizeof(struct DNS_HEADER)) { + D("Packet too short for DNS header\n"); + return FALSE; + } + + const caddr_t packet = data; + const int packet_len = len; + + POP_STRUCT(struct DNS_HEADER, h, data, len); + + if (h.qr != 0) { + D("DNS packet is not a request\n"); + return FALSE; + } + + if (ntohs(h.q_count) == 0) { + D("DNS request has no queries\n"); + return FALSE; + } + + if (ntohs(h.q_count) > 1) { + D("DNS request has multiple queries (not supported)\n"); + return FALSE; + } + + if (ntohs(h.ans_count != 0) || ntohs(h.auth_count != 0) || ntohs(h.add_count != 0)) { + D("DNS request has unsupported extra contents\n"); + return FALSE; + } + + if (len == 0) { + D("Packet too short for DNS query string\n"); + return FALSE; + } + + char * original_query_str = data; + int query_str_len = strnlen(data, len); + if (query_str_len == len) { // went off end of packet + D("Unterminated DNS query string\n"); + return FALSE; + } + + char * decoded_name_str = decode_dns_name(original_query_str); + if (decoded_name_str == NULL) { + D("Error while decoding DNS query string"); + return FALSE; + } + + D("DNS host query for %s\n", decoded_name_str); + + data += query_str_len + 1; + len -= query_str_len + 1; + + POP_STRUCT(struct QUESTION, qinfo, data, len); + + if (ntohs(qinfo.qtype) != 1 || ntohs(qinfo.qclass) != 1) { + D("DNS host query for %s: Request isn't the supported type (INET A query)\n", decoded_name_str); + free(decoded_name_str); + return FALSE; + } + + D("DNS host query for %s: Request is eligible to check for host resolution suffix\n", decoded_name_str); + + const char * matched_suffix = NULL; + + for (const char ** suffix_ptr = host_resolved_domain_suffixes; *suffix_ptr != NULL; suffix_ptr++) { + const char * suffix = *suffix_ptr; + + int suffix_pos = strlen(decoded_name_str) - strlen(suffix); + if (suffix_pos > 0 && strcmp(decoded_name_str + suffix_pos, suffix) == 0) { + matched_suffix = suffix; + break; + } + } + + if (matched_suffix == NULL) { + D("DNS host query for %s: No suffix matched\n", decoded_name_str); + } else { + + D("DNS host query for %s: Suffix matched: %s\n", decoded_name_str, matched_suffix); + + // we are going to take this request and resolve it on the host + drop_dns_request = TRUE; + + D("DNS host query for %s: Doing lookup on host\n", decoded_name_str); + + int results_count = 0; + struct hostent * host_lookup_result = gethostbyname(decoded_name_str); + + if (host_lookup_result && host_lookup_result->h_addrtype == AF_INET) { + + D("DNS host query for %s: Host response has results for AF_INET\n", decoded_name_str); + + for (char ** addr_entry = host_lookup_result->h_addr_list; *addr_entry != NULL; addr_entry++) { + ++results_count; + } + } + + D("DNS host query for %s: result count %d\n", decoded_name_str, results_count); + + int original_query_str_size = strlen(original_query_str) + 1; + int response_size = packet_len + results_count * (original_query_str_size + sizeof(struct R_DATA) + sizeof(struct in_addr)); + + caddr_t response_packet = malloc(response_size); + if (response_packet == NULL) { + D("DNS host query for %s: Out of memory while allocating DNS response packet\n", decoded_name_str); + } else { + D("DNS host query for %s: Preparing DNS response\n", decoded_name_str); + + // use the request DNS header as our starting point for the response + h.qr = 1; + h.ans_count = htons(results_count); + memcpy(response_packet, &h, sizeof(struct DNS_HEADER)); + + // other sections verbatim out of the request + memcpy(response_packet + sizeof(struct DNS_HEADER), packet + sizeof(struct DNS_HEADER), packet_len - sizeof(struct DNS_HEADER)); + + int response_pos = packet_len; + + if (results_count > 0) { + for (char ** addr_entry = host_lookup_result->h_addr_list; *addr_entry != NULL; addr_entry++) { + // answer string is verbatim from question + memcpy(response_packet + response_pos, original_query_str, original_query_str_size); + + response_pos += original_query_str_size; + + struct R_DATA resource; + resource.type = htons(1); + resource._class = htons(1); + resource.ttl = htonl(DOT_LOCAL_TTL); + resource.data_len = htons(sizeof(struct in_addr)); + + memcpy(response_packet + response_pos, &resource, sizeof(struct R_DATA)); + response_pos += sizeof(struct R_DATA); + + struct in_addr * cur_addr = (struct in_addr *)*addr_entry; + memcpy(response_packet + response_pos, cur_addr, sizeof(struct in_addr)); + response_pos += sizeof(struct in_addr); + } + assert(response_pos == response_size); + } + + D("DNS host query for %s: Injecting DNS response directly to guest\n", decoded_name_str); + inject_udp_packet_to_guest(so, addr, response_packet, response_size); + + free(response_packet); + } + + } + + free(decoded_name_str); + + D("DNS host request drop: %s\n", drop_dns_request? "yes" : "no"); + return drop_dns_request; +} + /* * sendto() a socket */ @@ -503,6 +787,8 @@ sosendto(so, m) switch(ntohl(so->so_faddr.s_addr) & 0xff) { case CTL_DNS: addr.sin_addr = dns_addr; + if (resolve_dns_request(so, addr, m->m_data, m->m_len)) + return 0; break; case CTL_ALIAS: default: From a1ef6be18a6686df970fd7765d78d0d819e25476 Mon Sep 17 00:00:00 2001 From: rakslice Date: Sat, 14 Nov 2020 02:23:42 -0800 Subject: [PATCH 2/7] make locally resolved DNS domains configurable through host_domain pref (multi allowed); also match exact domain --- BasiliskII/src/include/prefs.h | 1 + BasiliskII/src/prefs.cpp | 5 ++ BasiliskII/src/prefs_items.cpp | 1 + BasiliskII/src/slirp/slirp.c | 3 + BasiliskII/src/slirp/slirp.h | 3 + BasiliskII/src/slirp/socket.c | 131 ++++++++++++++++++++++++++------ SheepShaver/src/prefs_items.cpp | 1 + 7 files changed, 121 insertions(+), 24 deletions(-) diff --git a/BasiliskII/src/include/prefs.h b/BasiliskII/src/include/prefs.h index 216137f2..380f5590 100644 --- a/BasiliskII/src/include/prefs.h +++ b/BasiliskII/src/include/prefs.h @@ -48,6 +48,7 @@ extern void PrefsReplaceBool(const char *name, bool b); extern void PrefsReplaceInt32(const char *name, int32 val); extern const char *PrefsFindString(const char *name, int index = 0); +extern "C" const char *PrefsFindStringC(const char *name, int index = 0); extern bool PrefsFindBool(const char *name); extern int32 PrefsFindInt32(const char *name); diff --git a/BasiliskII/src/prefs.cpp b/BasiliskII/src/prefs.cpp index 434992ce..d6eb615d 100644 --- a/BasiliskII/src/prefs.cpp +++ b/BasiliskII/src/prefs.cpp @@ -328,6 +328,11 @@ const char *PrefsFindString(const char *name, int index) return NULL; } +extern "C" const char *PrefsFindStringC(const char *name, int index) +{ + return PrefsFindString(name, index); +} + bool PrefsFindBool(const char *name) { prefs_node *p = find_node(name, TYPE_BOOLEAN, 0); diff --git a/BasiliskII/src/prefs_items.cpp b/BasiliskII/src/prefs_items.cpp index b54be780..883c7a6e 100644 --- a/BasiliskII/src/prefs_items.cpp +++ b/BasiliskII/src/prefs_items.cpp @@ -81,6 +81,7 @@ prefs_desc common_prefs_items[] = { {"gammaramp", TYPE_STRING, false, "gamma ramp (on, off or fullscreen)"}, {"swap_opt_cmd", TYPE_BOOLEAN, false, "swap option and command key"}, {"ignoresegv", TYPE_BOOLEAN, false, "ignore illegal memory accesses"}, + {"host_domain", TYPE_STRING, true, "handle DNS requests for this domain on the host (slirp only)"}, {NULL, TYPE_END, false, NULL} // End of list }; diff --git a/BasiliskII/src/slirp/slirp.c b/BasiliskII/src/slirp/slirp.c index cd97e299..4aad7ba4 100755 --- a/BasiliskII/src/slirp/slirp.c +++ b/BasiliskII/src/slirp/slirp.c @@ -127,12 +127,15 @@ static int get_dns_addr(struct in_addr *pdns_addr) void slirp_cleanup(void) { WSACleanup(); + unload_host_domains(); } #endif int slirp_init(void) { // debug_init("/tmp/slirp.log", DEBUG_DEFAULT); + + load_host_domains(); #ifdef _WIN32 { diff --git a/BasiliskII/src/slirp/slirp.h b/BasiliskII/src/slirp/slirp.h index a677185e..235c4c0e 100755 --- a/BasiliskII/src/slirp/slirp.h +++ b/BasiliskII/src/slirp/slirp.h @@ -370,6 +370,9 @@ int tcp_emu _P((struct socket *, struct mbuf *)); int tcp_ctl _P((struct socket *)); struct tcpcb *tcp_drop(struct tcpcb *tp, int err); +void load_host_domains(); +void unload_host_domains(); + #ifdef USE_PPP #define MIN_MRU MINMRU #define MAX_MRU MAXMRU diff --git a/BasiliskII/src/slirp/socket.c b/BasiliskII/src/slirp/socket.c index afe72e9a..37de14e3 100755 --- a/BasiliskII/src/slirp/socket.c +++ b/BasiliskII/src/slirp/socket.c @@ -13,6 +13,8 @@ #ifdef __sun__ #include #endif +#include +#include #define DEBUG_HOST_RESOLVED_DNS 0 @@ -21,12 +23,9 @@ * looked up on the host to allow for host-supported DNS alternatives * (e.g. MDNS, hosts file, etc.) **/ -static const char * host_resolved_domain_suffixes[] = { - ".local.", - NULL // list ending -}; +static const char ** host_resolved_domain_suffixes = NULL; -#define DOT_LOCAL_TTL 60 // In seconds. Keep this short as data is very dynamic and requerying is cheap +#define HOST_DOMAIN_TTL 60 // In seconds. #if DEBUG_HOST_RESOLVED_DNS #define D(...) printf(__VA_ARGS__); fflush(stdout); @@ -34,6 +33,81 @@ static const char * host_resolved_domain_suffixes[] = { #define D(...) #endif +const char *PrefsFindStringC(const char *name, int index); + +int prepare_host_domain_suffixes(char * buf) { + /** + * Set up the list of domain suffixes to match from the host_domain prefs. + * Call first with buf NULL to figure out the size of buffer needed. + **/ + int pos = 0; + const char ** host_resolved_domain_suffixes_pos = NULL; + + if (buf) { + D("Setting up slirp host domain suffixes for matching:\n"); + host_resolved_domain_suffixes_pos = (const char **) buf; + } + + // find out how many values there are + int host_domain_count = 0; + while (PrefsFindStringC("host_domain", host_domain_count) != NULL) { + host_domain_count ++; + } + + // leave space for the top array + pos += (host_domain_count + 1) * sizeof(const char *); + + const char *str; + int host_domain_num = 0; + while ((str = PrefsFindStringC("host_domain", host_domain_num++)) != NULL) { + if (str[0] == '\0') continue; + if (buf) { + const char * cur_entry = (const char *) (buf + pos); + *host_resolved_domain_suffixes_pos = cur_entry; + host_resolved_domain_suffixes_pos++; + } + + // this is a suffix to match so it must have a leading dot + if (str[0] != '.') { + if (buf) buf[pos] = '.'; + pos++; + } + if (buf) strcpy(buf + pos, str); + pos += strlen(str); + // domain to be checked will be FQDN so suffix must have a trailing dot + if (str[strlen(str) - 1] != '.') { + if (buf) buf[pos] = '.'; + pos++; + } + if (buf) { + buf[pos] = '\0'; + D(" %d. %s\n", host_domain_num, *(host_resolved_domain_suffixes_pos-1)); + } + pos++; + } + + // end of list marker + if (buf) *host_resolved_domain_suffixes_pos = NULL; + + return pos; +} + +void load_host_domains() { + const int size = prepare_host_domain_suffixes(NULL); + char * buf = malloc(size); + if (buf) { + prepare_host_domain_suffixes(buf); + host_resolved_domain_suffixes = (const char **) buf; + } +} + +void unload_host_domains() { + if (host_resolved_domain_suffixes) { + free((char *) host_resolved_domain_suffixes); + host_resolved_domain_suffixes = NULL; + } +} + void so_init() { @@ -602,17 +676,17 @@ static char * decode_dns_name(char * data) { } /** Take a look at a UDP DNS request the client has made and see if we want to resolve it internally. - * Returns TRUE if the request has been internally and can be dropped, - * FALSE otherwise + * Returns true if the request has been internally and can be dropped, + * false otherwise **/ -static BOOL resolve_dns_request(struct socket * so, struct sockaddr_in addr, caddr_t data, int len) { - BOOL drop_dns_request = FALSE; +static bool resolve_dns_request(struct socket * so, struct sockaddr_in addr, caddr_t data, int len) { + bool drop_dns_request = false; D("Checking outgoing DNS UDP packet\n"); if (len < sizeof(struct DNS_HEADER)) { D("Packet too short for DNS header\n"); - return FALSE; + return false; } const caddr_t packet = data; @@ -622,40 +696,40 @@ static BOOL resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad if (h.qr != 0) { D("DNS packet is not a request\n"); - return FALSE; + return false; } if (ntohs(h.q_count) == 0) { D("DNS request has no queries\n"); - return FALSE; + return false; } if (ntohs(h.q_count) > 1) { D("DNS request has multiple queries (not supported)\n"); - return FALSE; + return false; } if (ntohs(h.ans_count != 0) || ntohs(h.auth_count != 0) || ntohs(h.add_count != 0)) { D("DNS request has unsupported extra contents\n"); - return FALSE; + return false; } if (len == 0) { D("Packet too short for DNS query string\n"); - return FALSE; + return false; } char * original_query_str = data; int query_str_len = strnlen(data, len); if (query_str_len == len) { // went off end of packet D("Unterminated DNS query string\n"); - return FALSE; + return false; } char * decoded_name_str = decode_dns_name(original_query_str); if (decoded_name_str == NULL) { D("Error while decoding DNS query string"); - return FALSE; + return false; } D("DNS host query for %s\n", decoded_name_str); @@ -665,10 +739,10 @@ static BOOL resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad POP_STRUCT(struct QUESTION, qinfo, data, len); - if (ntohs(qinfo.qtype) != 1 || ntohs(qinfo.qclass) != 1) { + if (ntohs(qinfo.qtype) != 1 /* type A */ || ntohs(qinfo.qclass) != 1 /* class IN */ ) { D("DNS host query for %s: Request isn't the supported type (INET A query)\n", decoded_name_str); free(decoded_name_str); - return FALSE; + return false; } D("DNS host query for %s: Request is eligible to check for host resolution suffix\n", decoded_name_str); @@ -678,21 +752,28 @@ static BOOL resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad for (const char ** suffix_ptr = host_resolved_domain_suffixes; *suffix_ptr != NULL; suffix_ptr++) { const char * suffix = *suffix_ptr; + // ends with suffix? int suffix_pos = strlen(decoded_name_str) - strlen(suffix); if (suffix_pos > 0 && strcmp(decoded_name_str + suffix_pos, suffix) == 0) { matched_suffix = suffix; break; } + + // also check if the domain exactly matched the one the suffix is for + if (strcmp(decoded_name_str, suffix + 1) == 0) { + matched_suffix = suffix; + break; + } } if (matched_suffix == NULL) { D("DNS host query for %s: No suffix matched\n", decoded_name_str); } else { - D("DNS host query for %s: Suffix matched: %s\n", decoded_name_str, matched_suffix); + D("DNS host query for %s: Matched for suffix: %s\n", decoded_name_str, matched_suffix); // we are going to take this request and resolve it on the host - drop_dns_request = TRUE; + drop_dns_request = true; D("DNS host query for %s: Doing lookup on host\n", decoded_name_str); @@ -739,7 +820,7 @@ static BOOL resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad struct R_DATA resource; resource.type = htons(1); resource._class = htons(1); - resource.ttl = htonl(DOT_LOCAL_TTL); + resource.ttl = htonl(HOST_DOMAIN_TTL); resource.data_len = htons(sizeof(struct in_addr)); memcpy(response_packet + response_pos, &resource, sizeof(struct R_DATA)); @@ -787,8 +868,10 @@ sosendto(so, m) switch(ntohl(so->so_faddr.s_addr) & 0xff) { case CTL_DNS: addr.sin_addr = dns_addr; - if (resolve_dns_request(so, addr, m->m_data, m->m_len)) - return 0; + if (host_resolved_domain_suffixes != NULL) { + if (resolve_dns_request(so, addr, m->m_data, m->m_len)) + return 0; + } break; case CTL_ALIAS: default: diff --git a/SheepShaver/src/prefs_items.cpp b/SheepShaver/src/prefs_items.cpp index 11581edd..f98424ec 100644 --- a/SheepShaver/src/prefs_items.cpp +++ b/SheepShaver/src/prefs_items.cpp @@ -68,6 +68,7 @@ prefs_desc common_prefs_items[] = { {"mag_rate", TYPE_INT32, 0, "rate of magnification"}, {"gammaramp", TYPE_STRING, false, "gamma ramp (on, off or fullscreen)"}, {"swap_opt_cmd", TYPE_BOOLEAN, false, "swap option and command key"}, + {"host_domain", TYPE_STRING, true, "handle DNS requests for this domain on the host (slirp only)"}, {NULL, TYPE_END, false, NULL} // End of list }; From 5163e17f14f71a935b8822581662d1654597fcfe Mon Sep 17 00:00:00 2001 From: rakslice Date: Thu, 19 Nov 2020 15:43:10 -0800 Subject: [PATCH 3/7] ignore upper case in pref --- BasiliskII/src/slirp/socket.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/BasiliskII/src/slirp/socket.c b/BasiliskII/src/slirp/socket.c index 37de14e3..699fda6a 100755 --- a/BasiliskII/src/slirp/socket.c +++ b/BasiliskII/src/slirp/socket.c @@ -72,8 +72,14 @@ int prepare_host_domain_suffixes(char * buf) { if (buf) buf[pos] = '.'; pos++; } - if (buf) strcpy(buf + pos, str); - pos += strlen(str); + if (buf) { + const char * str_pos = str; + while (*str_pos != '\0') { + buf[pos] = tolower(*str_pos); + ++pos; + ++str_pos; + } + }; // domain to be checked will be FQDN so suffix must have a trailing dot if (str[strlen(str) - 1] != '.') { if (buf) buf[pos] = '.'; From 6d74ff5600c4cea4a0961fcb2e51a0e8702ea413 Mon Sep 17 00:00:00 2001 From: rakslice Date: Thu, 19 Nov 2020 15:54:10 -0800 Subject: [PATCH 4/7] fix calculated size --- BasiliskII/src/slirp/socket.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/BasiliskII/src/slirp/socket.c b/BasiliskII/src/slirp/socket.c index 699fda6a..f6a6b6e8 100755 --- a/BasiliskII/src/slirp/socket.c +++ b/BasiliskII/src/slirp/socket.c @@ -72,14 +72,12 @@ int prepare_host_domain_suffixes(char * buf) { if (buf) buf[pos] = '.'; pos++; } - if (buf) { - const char * str_pos = str; - while (*str_pos != '\0') { - buf[pos] = tolower(*str_pos); - ++pos; - ++str_pos; - } - }; + const char * str_pos = str; + while (*str_pos != '\0') { + if (buf) buf[pos] = tolower(*str_pos); + ++pos; + ++str_pos; + } // domain to be checked will be FQDN so suffix must have a trailing dot if (str[strlen(str) - 1] != '.') { if (buf) buf[pos] = '.'; @@ -102,7 +100,8 @@ void load_host_domains() { const int size = prepare_host_domain_suffixes(NULL); char * buf = malloc(size); if (buf) { - prepare_host_domain_suffixes(buf); + const int second_size = prepare_host_domain_suffixes(buf); + assert(size == second_size); host_resolved_domain_suffixes = (const char **) buf; } } From ff0a825356dea7cecf5b60787c79ec13291bc633 Mon Sep 17 00:00:00 2001 From: rakslice Date: Thu, 19 Nov 2020 16:38:05 -0800 Subject: [PATCH 5/7] note about the source of the commented structs --- BasiliskII/src/slirp/socket.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BasiliskII/src/slirp/socket.c b/BasiliskII/src/slirp/socket.c index f6a6b6e8..bf26604e 100755 --- a/BasiliskII/src/slirp/socket.c +++ b/BasiliskII/src/slirp/socket.c @@ -581,6 +581,8 @@ sorecvfrom(so) } /* if ping packet */ } +// Commented structs from sil3rm00n's example code +// https://www.binarytides.com/dns-query-code-in-c-with-linux-sockets/ struct DNS_HEADER { From faeb5fa2e151840238fcb1805b64266071de3452 Mon Sep 17 00:00:00 2001 From: rakslice Date: Thu, 19 Nov 2020 18:29:06 -0800 Subject: [PATCH 6/7] make sure response size assert applies in the empty response case; cleanup; add more comments --- BasiliskII/src/slirp/socket.c | 45 +++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/BasiliskII/src/slirp/socket.c b/BasiliskII/src/slirp/socket.c index bf26604e..5647f536 100755 --- a/BasiliskII/src/slirp/socket.c +++ b/BasiliskII/src/slirp/socket.c @@ -621,8 +621,11 @@ struct R_DATA unsigned short data_len; }; #pragma pack(pop) - -#define POP_STRUCT(vartype, varname, data, len) \ + +/** Create local variable varname of type vartype, + * fill it from the buffer data, observing its length len, + * and adjust data and len to reflect the remaining data */ +#define POP_DATA(vartype, varname, data, len) \ assert(len >= sizeof(vartype)); \ vartype varname; \ memcpy(&varname, data, sizeof(vartype)); \ @@ -630,9 +633,29 @@ struct R_DATA len -= sizeof(vartype) +/** Create local const char * varname pointing + * to the C string in the buffer data, observing its length len, + * and adjust data and len to reflect the remaining data */ +#define POP_STR(varname, data, len) \ + const char * varname; \ + { \ + int pop_str_len = strnlen(data, len); \ + if (pop_str_len == len) { \ + varname = NULL; \ + } else { \ + varname = data; \ + } \ + data += pop_str_len + 1; \ + len -= pop_str_len + 1; \ + } + + static void inject_udp_packet_to_guest(struct socket * so, struct sockaddr_in addr, caddr_t packet_data, int packet_len) { struct mbuf *m; int len; + + /** This is like sorecvfrom(), but just adds a packet with the + * supplied data instead of reading the packet to add from the socket */ if (!(m = m_get())) return; m->m_data += if_maxlinkhdr; @@ -654,7 +677,7 @@ static void inject_udp_packet_to_guest(struct socket * so, struct sockaddr_in ad /* Decode hostname from the format used in DNS e.g. "\009something\004else\003com" for "something.else.com." */ -static char * decode_dns_name(char * data) { +static char * decode_dns_name(const char * data) { int query_str_len = strlen(data); char * decoded_name_str = malloc(query_str_len + 1); @@ -699,7 +722,7 @@ static bool resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad const caddr_t packet = data; const int packet_len = len; - POP_STRUCT(struct DNS_HEADER, h, data, len); + POP_DATA(struct DNS_HEADER, h, data, len); if (h.qr != 0) { D("DNS packet is not a request\n"); @@ -726,9 +749,9 @@ static bool resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad return false; } - char * original_query_str = data; - int query_str_len = strnlen(data, len); - if (query_str_len == len) { // went off end of packet + POP_STR(original_query_str, data, len); + if (original_query_str == NULL) { + // went off end of packet D("Unterminated DNS query string\n"); return false; } @@ -741,10 +764,7 @@ static bool resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad D("DNS host query for %s\n", decoded_name_str); - data += query_str_len + 1; - len -= query_str_len + 1; - - POP_STRUCT(struct QUESTION, qinfo, data, len); + POP_DATA(struct QUESTION, qinfo, data, len); if (ntohs(qinfo.qtype) != 1 /* type A */ || ntohs(qinfo.qclass) != 1 /* class IN */ ) { D("DNS host query for %s: Request isn't the supported type (INET A query)\n", decoded_name_str); @@ -837,9 +857,10 @@ static bool resolve_dns_request(struct socket * so, struct sockaddr_in addr, cad memcpy(response_packet + response_pos, cur_addr, sizeof(struct in_addr)); response_pos += sizeof(struct in_addr); } - assert(response_pos == response_size); } + assert(response_pos == response_size); + D("DNS host query for %s: Injecting DNS response directly to guest\n", decoded_name_str); inject_udp_packet_to_guest(so, addr, response_packet, response_size); From 62081d50d1b2da8d2d8c21971e497d453e941ff1 Mon Sep 17 00:00:00 2001 From: rakslice Date: Thu, 19 Nov 2020 21:28:05 -0800 Subject: [PATCH 7/7] fix typo --- BasiliskII/src/slirp/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BasiliskII/src/slirp/socket.c b/BasiliskII/src/slirp/socket.c index 5647f536..dfa39b00 100755 --- a/BasiliskII/src/slirp/socket.c +++ b/BasiliskII/src/slirp/socket.c @@ -581,7 +581,7 @@ sorecvfrom(so) } /* if ping packet */ } -// Commented structs from sil3rm00n's example code +// Commented structs from silv3rm00n's example code // https://www.binarytides.com/dns-query-code-in-c-with-linux-sockets/ struct DNS_HEADER