diff options
Diffstat (limited to 'usr')
126 files changed, 18423 insertions, 8998 deletions
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version index 656a8a3d08..88ac6f56a0 100644 --- a/usr/src/boot/Makefile.version +++ b/usr/src/boot/Makefile.version @@ -34,4 +34,4 @@ LOADER_VERSION = 1.1 # Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes. # The version is processed from left to right, the version number can only # be increased. -BOOT_VERSION = $(LOADER_VERSION)-2021.03.16.1 +BOOT_VERSION = $(LOADER_VERSION)-2021.04.03.1 diff --git a/usr/src/boot/lib/libstand/zfs/zfsimpl.c b/usr/src/boot/lib/libstand/zfs/zfsimpl.c index 9ceb9b9794..e83a8a3983 100644 --- a/usr/src/boot/lib/libstand/zfs/zfsimpl.c +++ b/usr/src/boot/lib/libstand/zfs/zfsimpl.c @@ -175,10 +175,21 @@ nvlist_check_features_for_read(nvlist_t *nvl) nv_string_t *nvp_name; int rc; + /* + * We may have all features disabled. + */ rc = nvlist_find(nvl, ZPOOL_CONFIG_FEATURES_FOR_READ, DATA_TYPE_NVLIST, NULL, &features, NULL); - if (rc != 0) - return (rc); + switch (rc) { + case 0: + break; /* Continue with checks */ + + case ENOENT: + return (0); /* All features are disabled */ + + default: + return (rc); /* Error while reading nvlist */ + } data = (nvs_data_t *)features->nv_data; nvp = &data->nvl_pair; /* first pair in nvlist */ diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index bd426edbb5..a4bacee105 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -306,6 +306,7 @@ COMMON_SUBDIRS= \ pbind \ pcidb \ pcidr \ + pcieadm \ pcieb \ pcitool \ pfexec \ diff --git a/usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile index 8afabd17fe..23f5b3ba20 100644 --- a/usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile +++ b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile @@ -25,7 +25,7 @@ SRCS= ClientCommon.c dns-sd.c SRCDIR= $(SRC)/contrib/mDNSResponder CFLAGS += $(CSTD_GNU99) CPPFLAGS += -I$(SRCDIR)/mDNSShared -CPPFLAGS += -DmDNSResponderVersion=878.1.1 +CPPFLAGS += -DmDNSResponderVersion=1310.80.1 CPPFLAGS += -DMDNS_VERSIONSTR_NODTS LDLIBS += -lsocket -ldns_sd diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c b/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c index dd319ed40a..70002fb628 100644 --- a/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c +++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c @@ -24,6 +24,7 @@ #include <limits.h> #include <ctype.h> #include <krb5defs.h> +#include <stdarg.h> /* * If we want to write *to* the client rdist program, *from* the server @@ -33,15 +34,15 @@ */ int wrem = 1; -#define ack() (void) write(wrem, "\0\n", 2) -#define err() (void) write(wrem, "\1\n", 2) +#define ack() (void) write(wrem, "\0\n", 2) +#define err() (void) write(wrem, "\1\n", 2) /* * Set when a desread() is reqd. in response() */ struct linkbuf *ihead; /* list of files with more than one link */ -char buf[RDIST_BUFSIZ]; /* general purpose buffer */ +extern char buf[RDIST_BUFSIZ]; /* general purpose buffer */ char source[RDIST_BUFSIZ]; /* base source directory name */ char destination[RDIST_BUFSIZ]; /* base destination directory name */ char target[RDIST_BUFSIZ]; /* target/source directory name */ @@ -53,15 +54,15 @@ int oumask; /* old umask for creating files */ extern FILE *lfp; /* log file for mailing changes */ -void cleanup(); -struct linkbuf *savelink(); -char *strsub(); +void cleanup(int); +struct linkbuf *savelink(struct stat *, int); +char *strsub(char *, char *, char *); -static void comment(char *s); -static void note(); +static void comment(char *); +static void note(char *, ...); static void hardlink(char *cmd); -void error(); -void log(); +void error(char *, ...); +void log(FILE *, char *, ...); static void recursive_remove(struct stat *stp); static void recvf(char *cmd, int type); static void query(char *name); @@ -78,10 +79,10 @@ static void clean(char *cp); * Qname - Query if file exists. Return mtime & size if it does. */ void -server() +server(void) { char cmdbuf[RDIST_BUFSIZ]; - register char *cp; + char *cp; signal(SIGHUP, cleanup); signal(SIGINT, cleanup); @@ -105,7 +106,7 @@ server() } do { if (read(rem, cp, 1) != 1) - cleanup(); + cleanup(0); } while (*cp++ != '\n' && cp < &cmdbuf[RDIST_BUFSIZ]); *--cp = '\0'; cp = cmdbuf; @@ -231,9 +232,7 @@ server() * (i.e., more than one source is being copied to the same destination). */ void -install(src, dest, destdir, opts) - char *src, *dest; - int destdir, opts; +install(char *src, char *dest, int destdir, int opts) { char *rname; char destcopy[RDIST_BUFSIZ]; @@ -245,10 +244,10 @@ install(src, dest, destdir, opts) if (nflag || debug) { printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install", - opts & WHOLE ? " -w" : "", - opts & YOUNGER ? " -y" : "", - opts & COMPARE ? " -b" : "", - opts & REMOVE ? " -R" : "", src, dest); + opts & WHOLE ? " -w" : "", + opts & YOUNGER ? " -y" : "", + opts & COMPARE ? " -b" : "", + opts & REMOVE ? " -R" : "", src, dest); if (nflag) return; } @@ -311,11 +310,9 @@ install(src, dest, destdir, opts) * rname is the name of the file on the remote host. */ void -sendf(rname, opts) - char *rname; - int opts; +sendf(char *rname, int opts) { - register struct subcmd *sc; + struct subcmd *sc; struct stat stb; int sizerr, f, u, len; off_t i; @@ -348,14 +345,14 @@ sendf(rname, opts) if (pw == NULL || pw->pw_uid != stb.st_uid) if ((pw = getpwuid(stb.st_uid)) == NULL) { log(lfp, "%s: no password entry for uid %d \n", - target, stb.st_uid); + target, stb.st_uid); pw = NULL; sprintf(user, ":%d", stb.st_uid); } if (gr == NULL || gr->gr_gid != stb.st_gid) if ((gr = getgrgid(stb.st_gid)) == NULL) { log(lfp, "%s: no name for group %d\n", - target, stb.st_gid); + target, stb.st_gid); gr = NULL; sprintf(group, ":%d", stb.st_gid); } @@ -401,7 +398,7 @@ sendf(rname, opts) if ((int)(len + 1 + strlen(dp->d_name)) >= (int)(RDIST_BUFSIZ - 1)) { error("%.*s/%s: Name too long\n", len, target, - dp->d_name); + dp->d_name); continue; } tp = otp; @@ -505,8 +502,8 @@ sendf(rname, opts) return; } (void) snprintf(buf, sizeof (buf), "R%o %o %ld %ld %s %s %s\n", opts, - stb.st_mode & 07777, stb.st_size, stb.st_mtime, - protoname(), protogroup(), rname); + stb.st_mode & 07777, stb.st_size, stb.st_mtime, + protoname(), protogroup(), rname); if (debug) printf("buf = %s", buf); (void) deswrite(rem, buf, strlen(buf), 0); @@ -557,9 +554,7 @@ dospecial: } struct linkbuf * -savelink(stp, opts) - struct stat *stp; - int opts; +savelink(struct stat *stp, int opts) { struct linkbuf *lp; @@ -600,18 +595,15 @@ savelink(stp, opts) * and 3 if comparing binaries to determine if out of date. */ int -update(rname, opts, stp) - char *rname; - int opts; - struct stat *stp; +update(char *rname, int opts, struct stat *stp) { - register char *cp, *s; - register off_t size; - register time_t mtime; + char *cp, *s; + off_t size; + time_t mtime; if (debug) printf("update(%s, %x%s, %x)\n", rname, opts, - printb(opts, OBITS), stp); + printb(opts, OBITS), stp); /* * Check to see if the file exists on the remote machine. @@ -731,8 +723,7 @@ more: * ^Aerror message\n */ static void -query(name) - char *name; +query(char *name) { struct stat stb; @@ -774,11 +765,9 @@ query(name) } static void -recvf(cmd, type) - char *cmd; - int type; +recvf(char *cmd, int type) { - register char *cp; + char *cp; int f, mode, opts, wrerr, olderrno; off_t i, size; time_t mtime; @@ -843,7 +832,7 @@ recvf(cmd, type) isdot = 0; if (catname >= sizeof (stp) / sizeof (stp[0])) { error("%s:%s: too many directory levels\n", - host, target); + host, target); return; } stp[catname] = tp; @@ -915,7 +904,7 @@ recvf(cmd, type) cp = buf; for (i = 0; i < size; i += j) { if ((j = read(rem, cp, size - i)) <= 0) - cleanup(); + cleanup(0); cp += j; } *cp = '\0'; @@ -963,7 +952,7 @@ recvf(cmd, type) if (j <= 0) { (void) close(f); (void) unlink(new); - cleanup(); + cleanup(0); } amt -= j; cp += j; @@ -1052,10 +1041,9 @@ badt: * Creat a hard link to existing file. */ static void -hardlink(cmd) - char *cmd; +hardlink(char *cmd) { - register char *cp; + char *cp; struct stat stb; char *oldname; int opts, exists = 0; @@ -1097,7 +1085,7 @@ hardlink(cmd) } if (chkparent(target) < 0) { error("%s:%s: %s (no parent)\n", - host, target, strerror(errno)); + host, target, strerror(errno)); return; } if (opts & VERIFY) { @@ -1116,14 +1104,14 @@ hardlink(cmd) } if (exists && (unlink(target) < 0)) { error("%s:%s: %s (unlink)\n", - host, target, strerror(errno)); + host, target, strerror(errno)); return; } if (*oldname == '~') oldname = exptilde(oldnamebuf, sizeof (oldnamebuf), oldname); if (link(oldname, target) < 0) { error("%s:can't link %s to %s\n", - host, target, oldname); + host, target, oldname); return; } ack(); @@ -1133,10 +1121,9 @@ hardlink(cmd) * Check to see if parent directory exists and create one if not. */ int -chkparent(name) - char *name; +chkparent(char *name) { - register char *cp; + char *cp; struct stat stb; cp = rindex(name, '/'); @@ -1161,11 +1148,9 @@ chkparent(name) * Change owner, group and mode of file. */ int -chog(file, owner, group, mode) - char *file, *owner, *group; - int mode; +chog(char *file, char *owner, char *group, int mode) { - register int i; + int i; uid_t uid, gid; extern char user[]; @@ -1230,10 +1215,9 @@ ok: * machine and remove them. */ static void -rmchk(opts) - int opts; +rmchk(int opts) { - register char *cp, *s; + char *cp, *s; struct stat stb; if (debug) @@ -1312,11 +1296,10 @@ rmchk(opts) * for extraneous files and remove them. */ static void -clean(cp) - register char *cp; +clean(char *cp) { DIR *d; - register struct dirent *dp; + struct dirent *dp; struct stat stb; char *otp; int len, opts; @@ -1343,7 +1326,7 @@ clean(cp) if ((int)(len + 1 + strlen(dp->d_name)) >= (int)(RDIST_BUFSIZ - 1)) { error("%s:%s/%s: Name too long\n", - host, target, dp->d_name); + host, target, dp->d_name); continue; } tp = otp; @@ -1361,7 +1344,7 @@ clean(cp) cp = buf; do { if (read(rem, cp, 1) != 1) - cleanup(); + cleanup(0); } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]); *--cp = '\0'; cp = buf; @@ -1384,12 +1367,11 @@ clean(cp) * or an error message. */ static void -recursive_remove(stp) - struct stat *stp; +recursive_remove(struct stat *stp) { DIR *d; struct dirent *dp; - register char *cp; + char *cp; struct stat stb; char *otp; int len; @@ -1421,7 +1403,7 @@ recursive_remove(stp) if ((int)(len + 1 + strlen(dp->d_name)) >= (int)(RDIST_BUFSIZ - 1)) { error("%s:%s/%s: Name too long\n", - host, target, dp->d_name); + host, target, dp->d_name); continue; } tp = otp; @@ -1452,11 +1434,10 @@ removed: * Execute a shell command to handle special cases. */ static void -dospecial(cmd) - char *cmd; +dospecial(char *cmd) { int fd[2], status, pid, i; - register char *cp, *s; + char *cp, *s; char sbuf[RDIST_BUFSIZ]; if (pipe(fd) < 0) { @@ -1516,81 +1497,92 @@ dospecial(cmd) ack(); } -/*VARARGS2*/ void -log(fp, fmt, a1, a2, a3) - FILE *fp; - char *fmt; - int a1, a2, a3; +log(FILE *fp, char *fmt, ...) { + va_list ap; + /* Print changes locally if not quiet mode */ - if (!qflag) - printf(fmt, a1, a2, a3); + if (!qflag) { + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + } /* Save changes (for mailing) if really updating files */ + va_start(ap, fmt); if (!(options & VERIFY) && fp != NULL) - fprintf(fp, fmt, a1, a2, a3); + vfprintf(fp, fmt, ap); + va_end(ap); } -/*VARARGS1*/ void -error(fmt, a1, a2, a3) - char *fmt; - int a1, a2, a3; +error(char *fmt, ...) { + va_list ap; static FILE *fp; nerrs++; if (!fp && !(fp = fdopen(rem, "w"))) return; if (iamremote) { + va_start(ap, fmt); (void) fprintf(fp, "%crdist: ", 0x01); - (void) fprintf(fp, fmt, a1, a2, a3); + (void) vfprintf(fp, fmt, ap); fflush(fp); + va_end(ap); } else { + va_start(ap, fmt); fflush(stdout); (void) fprintf(stderr, "rdist: "); - (void) fprintf(stderr, fmt, a1, a2, a3); + (void) vfprintf(stderr, fmt, ap); fflush(stderr); + va_end(ap); } if (lfp != NULL) { + va_start(ap, fmt); (void) fprintf(lfp, "rdist: "); - (void) fprintf(lfp, fmt, a1, a2, a3); + (void) vfprintf(lfp, fmt, ap); fflush(lfp); + va_end(ap); } } -/*VARARGS1*/ void -fatal(fmt, a1, a2, a3) - char *fmt; - int a1, a2, a3; +fatal(char *fmt, ...) { + va_list ap; static FILE *fp; nerrs++; if (!fp && !(fp = fdopen(rem, "w"))) return; if (iamremote) { + va_start(ap, fmt); (void) fprintf(fp, "%crdist: ", 0x02); - (void) fprintf(fp, fmt, a1, a2, a3); + (void) vfprintf(fp, fmt, ap); fflush(fp); + va_end(ap); } else { + va_start(ap, fmt); fflush(stdout); (void) fprintf(stderr, "rdist: "); - (void) fprintf(stderr, fmt, a1, a2, a3); + (void) vfprintf(stderr, fmt, ap); fflush(stderr); + va_end(ap); } if (lfp != NULL) { + va_start(ap, fmt); (void) fprintf(lfp, "rdist: "); - (void) fprintf(lfp, fmt, a1, a2, a3); + (void) vfprintf(lfp, fmt, ap); fflush(lfp); + va_end(ap); } - cleanup(); + cleanup(0); } int -response() +response(void) { char *cp, *s; char resp[RDIST_BUFSIZ]; @@ -1648,25 +1640,26 @@ more: * Remove temporary files and do any cleanup operations before exiting. */ void -cleanup() +cleanup(int arg __unused) { (void) unlink(Tmpfile); exit(1); } static void -note(fmt, a1, a2, a3) -char *fmt; -int a1, a2, a3; +note(char *fmt, ...) { + va_list ap; static char buf[RDIST_BUFSIZ]; - (void) snprintf(buf, sizeof (buf) - 1, fmt, a1, a2, a3); + + va_start(ap, fmt); + (void) vsnprintf(buf, sizeof (buf) - 1, fmt, ap); comment(buf); + va_end(ap); } static void -comment(s) -char *s; +comment(char *s) { char three = '\3'; char nl = '\n'; @@ -1686,11 +1679,9 @@ char *s; * N.B.: uses buf[]. */ void -sendrem(fmt, a1, a2, a3) -char *fmt; -int a1, a2, a3; +sendrem(char *fmt, int a1, int a2, int a3) { - register int len; + int len; buf[0] = '\0'; len = snprintf(buf + 1, sizeof (buf) - 1, fmt, a1, a2, a3) + 2; @@ -1708,11 +1699,10 @@ int a1, a2, a3; * substring old. */ char * -strsub(old, new, s) - char *old, *new, *s; +strsub(char *old, char *new, char *s) { static char pbuf[PATH_MAX]; - register char *p, *q, *r, *plim; + char *p, *q, *r, *plim; /* prepend new to pbuf */ for (p = pbuf, q = new, plim = pbuf + sizeof (pbuf) - 1; diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile index 80ab750e70..a8c1280cc3 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile @@ -32,7 +32,8 @@ SRCDIR= $(SRC)/contrib/mDNSResponder OBJS= DNSCommon.o DNSDigest.o GenLinkedList.o \ PlatformCommon.o PosixDaemon.o \ mDNS.o mDNSDebug.o mDNSPosix.o mDNSUNP.o \ - uDNS.o uds_daemon.o CryptoAlg.o anonymous.o dnssd_ipc.o + uDNS.o uds_daemon.o dnssd_ipc.o posix_utilities.o \ + ClientRequests.o SRCS= $(OBJS:%.o=%.c) MDNSFLAGS= -DNOT_HAVE_SA_LEN \ @@ -40,12 +41,14 @@ MDNSFLAGS= -DNOT_HAVE_SA_LEN \ -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME \ -DHAVE_IPV6=1 -Dasm=__asm -DMDNSD_NOROOT \ -DPID_FILE=\"\" -DMDNSD_USER=\"noaccess\" \ - -DmDNSResponderVersion=878.260.1 + -DmDNSResponderVersion=1310.80.1 include ../../../Makefile.cmd CERRWARN += -_gcc=-Wno-unused-variable CERRWARN += -_gcc=-Wno-implicit-function-declaration +CERRWARN += -_gcc7=-Wno-expansion-to-defined +CERRWARN += -_gcc10=-Wno-expansion-to-defined CERRWARN += $(CNOWARN_UNINIT) # not linted diff --git a/usr/src/cmd/nvmeadm/Makefile b/usr/src/cmd/nvmeadm/Makefile index 984c25112f..bc554ec2b8 100644 --- a/usr/src/cmd/nvmeadm/Makefile +++ b/usr/src/cmd/nvmeadm/Makefile @@ -18,7 +18,7 @@ PROG= nvmeadm -OBJS= nvmeadm.o nvmeadm_dev.o nvmeadm_print.o +OBJS= nvmeadm.o nvmeadm_dev.o nvmeadm_ofmt.o nvmeadm_print.o SRCS= $(OBJS:%.o=%.c) include ../Makefile.cmd @@ -27,7 +27,7 @@ include ../Makefile.ctf .KEEP_STATE: CFLAGS += $(CCVERBOSE) -I$(SRC)/uts/common/io/nvme -LDLIBS += -ldevinfo +LDLIBS += -ldevinfo -lofmt CSTD= $(CSTD_GNU99) # diff --git a/usr/src/cmd/nvmeadm/nvmeadm.c b/usr/src/cmd/nvmeadm/nvmeadm.c index a13f0555ce..e177e04d82 100644 --- a/usr/src/cmd/nvmeadm/nvmeadm.c +++ b/usr/src/cmd/nvmeadm/nvmeadm.c @@ -49,27 +49,6 @@ #include "nvmeadm.h" -typedef struct nvme_process_arg nvme_process_arg_t; -typedef struct nvme_feature nvme_feature_t; -typedef struct nvmeadm_cmd nvmeadm_cmd_t; - -struct nvme_process_arg { - int npa_argc; - char **npa_argv; - char *npa_name; - char *npa_nsid; - int npa_found; - boolean_t npa_isns; - const nvmeadm_cmd_t *npa_cmd; - di_node_t npa_node; - di_minor_t npa_minor; - char *npa_path; - char *npa_dsk; - nvme_identify_ctrl_t *npa_idctl; - nvme_identify_nsid_t *npa_idns; - nvme_version_t *npa_version; -}; - struct nvme_feature { char *f_name; char *f_short; @@ -87,11 +66,12 @@ struct nvme_feature { struct nvmeadm_cmd { char *c_name; - char *c_desc; - char *c_flagdesc; + const char *c_desc; + const char *c_flagdesc; int (*c_func)(int, const nvme_process_arg_t *); void (*c_usage)(const char *); boolean_t c_multi; + void (*c_optparse)(nvme_process_arg_t *); }; @@ -121,6 +101,8 @@ static int do_firmware_load(int, const nvme_process_arg_t *); static int do_firmware_commit(int, const nvme_process_arg_t *); static int do_firmware_activate(int, const nvme_process_arg_t *); +static void optparse_list(nvme_process_arg_t *); + static void usage_list(const char *); static void usage_identify(const char *); static void usage_get_logpage(const char *); @@ -140,8 +122,9 @@ static const nvmeadm_cmd_t nvmeadm_cmds[] = { { "list", "list controllers and namespaces", - NULL, - do_list, usage_list, B_TRUE + " -p\t\tprint parsable output\n" + " -o field\tselect a field for parsable output\n", + do_list, usage_list, B_TRUE, optparse_list }, { "identify", @@ -257,12 +240,12 @@ int main(int argc, char **argv) { int c; - extern int optind; const nvmeadm_cmd_t *cmd; di_node_t node; nvme_process_arg_t npa = { 0 }; int help = 0; char *tmp, *lasts = NULL; + char *ctrl = NULL; while ((c = getopt(argc, argv, "dhv")) != -1) { switch (c) { @@ -309,25 +292,42 @@ main(int argc, char **argv) optind++; /* - * All commands but "list" require a ctl/ns argument. + * Store the remaining arguments for use by the command. Give the + * command a chance to process the options across the board before going + * into each controller. */ - if ((optind == argc || (strncmp(argv[optind], "nvme", 4) != 0)) && + npa.npa_argc = argc - optind; + npa.npa_argv = &argv[optind]; + + if (cmd->c_optparse != NULL) { + cmd->c_optparse(&npa); + } + + /* + * All commands but "list" require a ctl/ns argument. However, this + * should not be passed through to the command in its subsequent + * arguments. + */ + if ((npa.npa_argc == 0 || (strncmp(npa.npa_argv[0], "nvme", 4) != 0)) && cmd->c_func != do_list) { warnx("missing controller/namespace name"); usage(cmd); exit(-1); } - - /* Store the remaining arguments for use by the command. */ - npa.npa_argc = argc - optind - 1; - npa.npa_argv = &argv[optind + 1]; + if (npa.npa_argc > 0) { + ctrl = npa.npa_argv[0]; + npa.npa_argv++; + npa.npa_argc--; + } else { + ctrl = NULL; + } /* * Make sure we're not running commands on multiple controllers that * aren't allowed to do that. */ - if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL && + if (ctrl != NULL && strchr(ctrl, ',') != NULL && cmd->c_multi == B_FALSE) { warnx("%s not allowed on multiple controllers", cmd->c_name); @@ -338,7 +338,7 @@ main(int argc, char **argv) /* * Get controller/namespace arguments and run command. */ - npa.npa_name = strtok_r(argv[optind], ",", &lasts); + npa.npa_name = strtok_r(ctrl, ",", &lasts); do { if (npa.npa_name != NULL) { tmp = strchr(npa.npa_name, '/'); @@ -373,6 +373,15 @@ main(int argc, char **argv) } static void +nvme_oferr(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verrx(-1, fmt, ap); +} + +static void usage(const nvmeadm_cmd_t *cmd) { (void) fprintf(stderr, "usage:\n"); @@ -394,9 +403,9 @@ usage(const nvmeadm_cmd_t *cmd) cmd->c_name, cmd->c_desc); } (void) fprintf(stderr, "\nflags:\n" - " -h print usage information\n" - " -d print information useful for debugging %s\n" - " -v print verbose information\n", getprogname()); + " -h\t\tprint usage information\n" + " -d\t\tprint information useful for debugging %s\n" + " -v\t\tprint verbose information\n", getprogname()); if (cmd != NULL && cmd->c_flagdesc != NULL) (void) fprintf(stderr, "%s\n", cmd->c_flagdesc); } @@ -554,13 +563,63 @@ nvme_walk(nvme_process_arg_t *npa, di_node_t node) static void usage_list(const char *c_name) { - (void) fprintf(stderr, "%s [<ctl>[/<ns>][,...]\n\n" + (void) fprintf(stderr, "%s " + "[-p -o field[,...]] [<ctl>[/<ns>][,...]\n\n" " List NVMe controllers and their namespaces. If no " "controllers and/or name-\n spaces are specified, all " "controllers and namespaces in the system will be\n " "listed.\n", c_name); } +static void +optparse_list(nvme_process_arg_t *npa) +{ + int c; + uint_t oflags = 0; + boolean_t parse = B_FALSE; + const char *fields = NULL; + + optind = 0; + while ((c = getopt(npa->npa_argc, npa->npa_argv, ":o:p")) != -1) { + switch (c) { + case 'o': + fields = optarg; + break; + case 'p': + parse = B_TRUE; + oflags |= OFMT_PARSABLE; + break; + case '?': + errx(-1, "unknown list option: -%c", optopt); + break; + case ':': + errx(-1, "option -%c requires an argument", optopt); + default: + break; + } + } + + if (fields != NULL && !parse) { + errx(-1, "-o can only be used when in parsable mode (-p)"); + } + + if (parse && fields == NULL) { + errx(-1, "parsable mode (-p) requires one to specify output " + "fields with -o"); + } + + if (parse) { + ofmt_status_t oferr; + + oferr = ofmt_open(fields, nvme_list_ofmt, oflags, 0, + &npa->npa_ofmt); + ofmt_check(oferr, B_TRUE, npa->npa_ofmt, nvme_oferr, warnx); + } + + npa->npa_argc -= optind; + npa->npa_argv += optind; +} + static int do_list_nsid(int fd, const nvme_process_arg_t *npa) { @@ -575,10 +634,14 @@ do_list_nsid(int fd, const nvme_process_arg_t *npa) if ((bshift < 9 || npa->npa_idns->id_nsize == 0) && verbose == 0) return (0); - (void) printf(" %s/%s (%s): ", npa->npa_name, - di_minor_name(npa->npa_minor), - npa->npa_dsk != NULL ? npa->npa_dsk : "unattached"); - nvme_print_nsid_summary(npa->npa_idns); + if (npa->npa_ofmt == NULL) { + (void) printf(" %s/%s (%s): ", npa->npa_name, + di_minor_name(npa->npa_minor), + npa->npa_dsk != NULL ? npa->npa_dsk : "unattached"); + nvme_print_nsid_summary(npa->npa_idns); + } else { + ofmt_print(npa->npa_ofmt, (void *)npa); + } return (0); } @@ -596,8 +659,10 @@ do_list(int fd, const nvme_process_arg_t *npa) di_instance(npa->npa_node)) < 0) err(-1, "do_list()"); - (void) printf("%s: ", name); - nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version); + if (npa->npa_ofmt == NULL) { + (void) printf("%s: ", name); + nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version); + } ns_npa.npa_name = name; ns_npa.npa_isns = B_TRUE; @@ -605,6 +670,8 @@ do_list(int fd, const nvme_process_arg_t *npa) cmd = *(npa->npa_cmd); cmd.c_func = do_list_nsid; ns_npa.npa_cmd = &cmd; + ns_npa.npa_ofmt = npa->npa_ofmt; + ns_npa.npa_idctl = npa->npa_idctl; nvme_walk(&ns_npa, npa->npa_node); diff --git a/usr/src/cmd/nvmeadm/nvmeadm.h b/usr/src/cmd/nvmeadm/nvmeadm.h index 0ccd299980..ff6a21c87f 100644 --- a/usr/src/cmd/nvmeadm/nvmeadm.h +++ b/usr/src/cmd/nvmeadm/nvmeadm.h @@ -22,6 +22,7 @@ #include <libdevinfo.h> #include <sys/nvme.h> #include <nvme_reg.h> +#include <ofmt.h> #ifdef __cplusplus extern "C" { @@ -30,10 +31,34 @@ extern "C" { extern int verbose; extern int debug; +/* Common structures */ +typedef struct nvme_process_arg nvme_process_arg_t; +typedef struct nvme_feature nvme_feature_t; +typedef struct nvmeadm_cmd nvmeadm_cmd_t; + +struct nvme_process_arg { + int npa_argc; + char **npa_argv; + char *npa_name; + char *npa_nsid; + int npa_found; + boolean_t npa_isns; + const nvmeadm_cmd_t *npa_cmd; + di_node_t npa_node; + di_minor_t npa_minor; + char *npa_path; + char *npa_dsk; + nvme_identify_ctrl_t *npa_idctl; + nvme_identify_nsid_t *npa_idns; + nvme_version_t *npa_version; + ofmt_handle_t npa_ofmt; +}; + /* Version checking */ extern boolean_t nvme_version_check(nvme_version_t *, uint_t, uint_t); /* printing functions */ +extern int nvme_strlen(const char *, int); extern void nvme_print(int, const char *, int, const char *, ...); extern void nvme_print_ctrl_summary(nvme_identify_ctrl_t *, nvme_version_t *); extern void nvme_print_nsid_summary(nvme_identify_nsid_t *); @@ -91,6 +116,11 @@ extern boolean_t nvme_attach(int); extern boolean_t nvme_firmware_load(int, void *, size_t, offset_t); extern boolean_t nvme_firmware_commit(int fd, int, int, uint16_t *, uint16_t *); +/* + * ofmt related + */ +extern const ofmt_field_t nvme_list_ofmt[]; + #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/nvmeadm/nvmeadm_ofmt.c b/usr/src/cmd/nvmeadm/nvmeadm_ofmt.c new file mode 100644 index 0000000000..825417eb76 --- /dev/null +++ b/usr/src/cmd/nvmeadm/nvmeadm_ofmt.c @@ -0,0 +1,132 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2021 Oxide Computer Company + */ + +/* + * nvmeadm output formatting for ofmt based rendering + */ + +#include <strings.h> + +#include "nvmeadm.h" + +typedef enum nvme_list_ofmt_field { + NVME_LIST_MODEL, + NVME_LIST_SERIAL, + NVME_LIST_FWREV, + NVME_LIST_VERSION, + NVME_LIST_SIZE, + NVME_LIST_CAPACITY, + NVME_LIST_USED, + NVME_LIST_INSTANCE, + NVME_LIST_NAMESPACE, + NVME_LIST_DISK +} nvme_list_ofmt_field_t; + +static boolean_t +nvme_list_ofmt_cb(ofmt_arg_t *ofmt_arg, char *buf, uint_t buflen) +{ + const nvme_process_arg_t *npa = ofmt_arg->ofmt_cbarg; + nvme_idns_lbaf_t *lbaf; + uint_t blksize; + uint64_t val; + int nvmelen; + size_t ret; + + lbaf = &npa->npa_idns->id_lbaf[npa->npa_idns->id_flbas.lba_format]; + blksize = 1 << lbaf->lbaf_lbads; + + switch (ofmt_arg->ofmt_id) { + case NVME_LIST_MODEL: + nvmelen = nvme_strlen(npa->npa_idctl->id_model, + sizeof (npa->npa_idctl->id_model)); + if (nvmelen <= 0 || nvmelen > buflen) { + return (B_FALSE); + } + (void) memcpy(buf, npa->npa_idctl->id_model, nvmelen); + buf[nvmelen] = '\0'; + ret = nvmelen; + break; + case NVME_LIST_SERIAL: + nvmelen = nvme_strlen(npa->npa_idctl->id_serial, + sizeof (npa->npa_idctl->id_serial)); + if (nvmelen <= 0 || nvmelen >= buflen) { + return (B_FALSE); + } + (void) memcpy(buf, npa->npa_idctl->id_serial, nvmelen); + buf[nvmelen] = '\0'; + ret = nvmelen; + break; + case NVME_LIST_FWREV: + nvmelen = nvme_strlen(npa->npa_idctl->id_fwrev, + sizeof (npa->npa_idctl->id_fwrev)); + if (nvmelen <= 0 || nvmelen >= buflen) { + return (B_FALSE); + } + (void) memcpy(buf, npa->npa_idctl->id_fwrev, nvmelen); + buf[nvmelen] = '\0'; + ret = nvmelen; + break; + case NVME_LIST_VERSION: + ret = snprintf(buf, buflen, "%u.%u", npa->npa_version->v_major, + npa->npa_version->v_minor); + break; + case NVME_LIST_INSTANCE: + ret = strlcat(buf, npa->npa_name, buflen); + break; + case NVME_LIST_NAMESPACE: + ret = strlcat(buf, di_minor_name(npa->npa_minor), buflen); + break; + case NVME_LIST_DISK: + if (npa->npa_dsk != NULL) { + ret = strlcat(buf, npa->npa_dsk, buflen); + } else { + ret = strlcat(buf, "--", buflen); + } + break; + case NVME_LIST_SIZE: + val = npa->npa_idns->id_nsize * blksize; + ret = snprintf(buf, buflen, "%" PRIu64, val); + break; + case NVME_LIST_CAPACITY: + val = npa->npa_idns->id_ncap * blksize; + ret = snprintf(buf, buflen, "%" PRIu64, val); + break; + case NVME_LIST_USED: + val = npa->npa_idns->id_nuse * blksize; + ret = snprintf(buf, buflen, "%" PRIu64, val); + break; + default: + abort(); + } + + if (ret >= buflen) { + return (B_FALSE); + } + return (B_TRUE); +} + +const ofmt_field_t nvme_list_ofmt[] = { + { "MODEL", 30, NVME_LIST_MODEL, nvme_list_ofmt_cb }, + { "SERIAL", 30, NVME_LIST_SERIAL, nvme_list_ofmt_cb }, + { "FWREV", 10, NVME_LIST_FWREV, nvme_list_ofmt_cb }, + { "VERSION", 10, NVME_LIST_VERSION, nvme_list_ofmt_cb }, + { "SIZE", 15, NVME_LIST_SIZE, nvme_list_ofmt_cb }, + { "CAPACITY", 15, NVME_LIST_CAPACITY, nvme_list_ofmt_cb }, + { "USED", 15, NVME_LIST_USED, nvme_list_ofmt_cb }, + { "INSTANCE", 10, NVME_LIST_INSTANCE, nvme_list_ofmt_cb }, + { "NAMESPACE", 10, NVME_LIST_NAMESPACE, nvme_list_ofmt_cb }, + { "DISK", 15, NVME_LIST_DISK, nvme_list_ofmt_cb }, + { NULL, 0, 0, NULL } +}; diff --git a/usr/src/cmd/nvmeadm/nvmeadm_print.c b/usr/src/cmd/nvmeadm/nvmeadm_print.c index 34006bc253..9190131566 100644 --- a/usr/src/cmd/nvmeadm/nvmeadm_print.c +++ b/usr/src/cmd/nvmeadm/nvmeadm_print.c @@ -31,8 +31,6 @@ #include "nvmeadm.h" -static int nvme_strlen(const char *, int); - static void nvme_print_str(int, const char *, int, const char *, int); static void nvme_print_double(int, const char *, double, int, const char *); static void nvme_print_int64(int, const char *, uint64_t, const char *, @@ -244,7 +242,7 @@ nvme_print(int indent, const char *name, int index, const char *fmt, ...) /* * nvme_strlen -- return length of string without trailing whitespace */ -static int +int nvme_strlen(const char *str, int len) { if (len < 0) @@ -483,7 +481,7 @@ nvme_print_version(int indent, const char *name, uint32_t value) void nvme_print_ctrl_summary(nvme_identify_ctrl_t *idctl, nvme_version_t *version) { - (void) printf("model: %.*s, serial: %.*s, FW rev: %.*s, NVMe v%d.%d\n", + (void) printf("model: %.*s, serial: %.*s, FW rev: %.*s, NVMe v%u.%u\n", nvme_strlen(idctl->id_model, sizeof (idctl->id_model)), idctl->id_model, nvme_strlen(idctl->id_serial, sizeof (idctl->id_serial)), diff --git a/usr/src/cmd/pcieadm/Makefile b/usr/src/cmd/pcieadm/Makefile new file mode 100644 index 0000000000..c8b17c2a3a --- /dev/null +++ b/usr/src/cmd/pcieadm/Makefile @@ -0,0 +1,45 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2021 Oxide Computer Company +# + +PROG= pcieadm + +include ../Makefile.cmd +include ../Makefile.cmd.64 +include ../Makefile.ctf + +CFLAGS += $(CCVERBOSE) +CSTD = $(CSTD_GNU99) +LDLIBS += -ldevinfo -lpcidb -lofmt +OBJS = pcieadm.o pcieadm_cfgspace.o pcieadm_devs.o +ROOTCMDDIR = $(ROOTLIB)/pci + +.KEEP_STATE: + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +all: $(PROG) + +install: all $(ROOTCMD) + +clean: + $(RM) $(OBJS) + +include ../Makefile.targ diff --git a/usr/src/cmd/pcieadm/pcieadm.c b/usr/src/cmd/pcieadm/pcieadm.c new file mode 100644 index 0000000000..edcad6e4d8 --- /dev/null +++ b/usr/src/cmd/pcieadm/pcieadm.c @@ -0,0 +1,624 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2021 Oxide Computer Company + */ + +/* + * PCIe shenanigans + * + * Currently this implements several different views at seeing into PCIe devices + * and is designed to (hopefully) replace pcitool and be a vector for new system + * functionality such as dealing with multicast filtering, ACS, etc. + * + * While most subcommands have their own implementations, there are a couple of + * things that are worth bearing in mind: + * + * 1) Where possible, prefer the use of libofmt. In particular, having good, + * parsable output is important. New subcommands should strive to meet that. + * + * 2) Because we're often processing binary data (and it's good hygiene), + * subcommands should make sure to drop privileges as early as they can by + * calling pcieadm_init_privs(). More on privileges below. + * + * Privilege Management + * -------------------- + * + * In an attempt to minimize privilege exposure, but to allow subcommands + * flexibility when required (e.g. show-cfgspace needs full privs to read from + * the kernel), we have two privilege sets that we maintain. One which is the + * minimial privs, which basically is a set that has stripped everything. This + * is 'pia_priv_min'. The second is one that allows a subcommand to add in + * privileges that it requires which will be left in the permitted set. These + * are in 'pia_priv_eff'. It's important to know that this set is always + * intersected with what the user actually has, so this is not meant to be a way + * for a caller to get more privileges than they already have. + * + * A subcommand is expected to call pcieadm_init_privs() once they have + * processed enough arguments that they can set an upper bound on privileges. + * It's worth noting that a subcommand will be executed in an already minimial + * environment; however, we will have already set up a libdevinfo handle for + * them, which should make the need to do much more not so bad. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <err.h> +#include <libdevinfo.h> +#include <strings.h> +#include <sys/stat.h> +#include <sys/pci_tools.h> +#include <sys/pci.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/debug.h> +#include <upanic.h> +#include <libgen.h> + +#include "pcieadm.h" + +pcieadm_t pcieadm; +const char *pcieadm_progname; + +void +pcieadm_init_privs(pcieadm_t *pcip) +{ + static const char *msg = "attempted to re-initialize privileges"; + if (pcip->pia_priv_init == NULL) { + upanic(msg, strlen(msg)); + } + + priv_intersect(pcip->pia_priv_init, pcip->pia_priv_eff); + + if (setppriv(PRIV_SET, PRIV_PERMITTED, pcieadm.pia_priv_eff) != 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + if (setppriv(PRIV_SET, PRIV_LIMIT, pcieadm.pia_priv_eff) != 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + priv_freeset(pcip->pia_priv_init); + pcip->pia_priv_init = NULL; +} + +void +pcieadm_indent(void) +{ + pcieadm.pia_indent += 2; +} + +void +pcieadm_deindent(void) +{ + VERIFY3U(pcieadm.pia_indent, >, 0); + pcieadm.pia_indent -= 2; +} + +void +pcieadm_print(const char *fmt, ...) +{ + va_list ap; + + if (pcieadm.pia_indent > 0) { + (void) printf("%*s", pcieadm.pia_indent, ""); + } + + va_start(ap, fmt); + (void) vprintf(fmt, ap); + va_end(ap); +} + +void +pcieadm_ofmt_errx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verrx(EXIT_FAILURE, fmt, ap); +} + +boolean_t +pcieadm_di_node_is_pci(di_node_t node) +{ + const char *name; + + name = di_node_name(node); + return (strncmp("pci", name, 3) == 0); +} + +static int +pcieadm_di_walk_cb(di_node_t node, void *arg) +{ + pcieadm_di_walk_t *walk = arg; + + if (!pcieadm_di_node_is_pci(node)) { + return (DI_WALK_CONTINUE); + } + + /* + * We create synthetic nodes for the root of PCIe tree basically + * functions as all the resources available for one or more bridges. + * When we encounter that top-level node skip it. + */ + if (strcmp("pci", di_node_name(node)) == 0) { + return (DI_WALK_CONTINUE); + } + + return (walk->pdw_func(node, walk->pdw_arg)); +} + +void +pcieadm_di_walk(pcieadm_t *pcip, pcieadm_di_walk_t *arg) +{ + (void) di_walk_node(pcip->pia_root, DI_WALK_CLDFIRST, arg, + pcieadm_di_walk_cb); +} + +/* + * Attempt to find the nexus that corresponds to this device. To do this, we + * walk up and walk the minors until we find a "reg" minor. + */ +void +pcieadm_find_nexus(pcieadm_t *pia) +{ + di_node_t cur; + + for (cur = di_parent_node(pia->pia_devi); cur != DI_NODE_NIL; + cur = di_parent_node(cur)) { + di_minor_t minor = DI_MINOR_NIL; + + while ((minor = di_minor_next(cur, minor)) != DI_MINOR_NIL) { + if (di_minor_spectype(minor) == S_IFCHR && + strcmp(di_minor_name(minor), "reg") == 0) { + pia->pia_nexus = cur; + return; + } + } + } +} + +static int +pcieadm_find_dip_cb(di_node_t node, void *arg) +{ + char *path = NULL, *driver; + char dinst[128], bdf[128], altbdf[128]; + int inst, nprop, *regs; + pcieadm_t *pia = arg; + + path = di_devfs_path(node); + if (path == NULL) { + err(EXIT_FAILURE, "failed to construct devfs path for node: " + "%s (%s)", di_node_name(node)); + } + + driver = di_driver_name(node); + inst = di_instance(node); + if (driver != NULL && inst != -1) { + (void) snprintf(dinst, sizeof (dinst), "%s%d", driver, inst); + } + + nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®s); + if (nprop <= 0) { + errx(EXIT_FAILURE, "failed to lookup regs array for %s", + path); + } + (void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", PCI_REG_BUS_G(regs[0]), + PCI_REG_DEV_G(regs[0]), PCI_REG_FUNC_G(regs[0])); + (void) snprintf(bdf, sizeof (bdf), "%02x/%02x/%02x", + PCI_REG_BUS_G(regs[0]), PCI_REG_DEV_G(regs[0]), + PCI_REG_FUNC_G(regs[0])); + + if (strcmp(pia->pia_devstr, path) == 0 || + strcmp(pia->pia_devstr, bdf) == 0 || + strcmp(pia->pia_devstr, altbdf) == 0 || + (driver != NULL && inst != -1 && + strcmp(pia->pia_devstr, dinst) == 0)) { + if (pia->pia_devi != DI_NODE_NIL) { + errx(EXIT_FAILURE, "device name matched two device " + "nodes: %s and %s", di_node_name(pia->pia_devi), + di_node_name(node)); + } + + pia->pia_devi = node; + } + + if (path != NULL) { + di_devfs_path_free(path); + } + + return (DI_WALK_CONTINUE); +} + +void +pcieadm_find_dip(pcieadm_t *pcip, const char *device) +{ + pcieadm_di_walk_t walk; + + /* + * If someone specifies /devices, just skip over it. + */ + pcip->pia_devstr = device; + if (strncmp("/devices", device, strlen("/devices")) == 0) { + pcip->pia_devstr += strlen("/devices"); + } + + pcip->pia_devi = DI_NODE_NIL; + walk.pdw_arg = pcip; + walk.pdw_func = pcieadm_find_dip_cb; + pcieadm_di_walk(pcip, &walk); + + if (pcip->pia_devi == DI_NODE_NIL) { + errx(EXIT_FAILURE, "failed to find device node %s", device); + } + + pcip->pia_nexus = DI_NODE_NIL; + pcieadm_find_nexus(pcip); + if (pcip->pia_nexus == DI_NODE_NIL) { + errx(EXIT_FAILURE, "failed to find nexus for %s", device); + } +} + +typedef struct pcieadm_cfgspace_file { + int pcfi_fd; +} pcieadm_cfgspace_file_t; + +static boolean_t +pcieadm_read_cfgspace_file(uint32_t off, uint8_t len, void *buf, void *arg) +{ + uint32_t bufoff = 0; + pcieadm_cfgspace_file_t *pcfi = arg; + + while (len > 0) { + ssize_t ret = pread(pcfi->pcfi_fd, buf + bufoff, len, off); + if (ret < 0) { + err(EXIT_FAILURE, "failed to read %u bytes at %" + PRIu32, len, off); + } else if (ret == 0) { + warnx("hit unexpected EOF reading cfgspace from file " + "at offest %" PRIu32 ", still wanted to read %u " + "bytes", off, len); + return (B_FALSE); + } else { + len -= ret; + off += ret; + bufoff += ret; + } + + } + + return (B_TRUE); +} + +void +pcieadm_init_cfgspace_file(pcieadm_t *pcip, const char *path, + pcieadm_cfgspace_f *funcp, void **arg) +{ + int fd; + struct stat st; + pcieadm_cfgspace_file_t *pcfi; + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0) { + err(EXIT_FAILURE, "failed to raise privileges"); + } + + if ((fd = open(path, O_RDONLY)) < 0) { + err(EXIT_FAILURE, "failed to open input file %s", path); + } + + if (fstat(fd, &st) != 0) { + err(EXIT_FAILURE, "failed to get stat information for %s", + path); + } + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + if (S_ISDIR(st.st_mode)) { + errx(EXIT_FAILURE, "input file %s is a directory, unable " + "to read data", path); + } + + if (S_ISLNK(st.st_mode)) { + errx(EXIT_FAILURE, "input file %s is a symbolic link, unable " + "to read data", path); + } + + if (S_ISDOOR(st.st_mode)) { + errx(EXIT_FAILURE, "input file %s is a door, unable " + "to read data", path); + } + + if (S_ISPORT(st.st_mode)) { + errx(EXIT_FAILURE, "input file %s is an event port, unable " + "to read data", path); + } + + /* + * Assume if we were given a FIFO, character/block device, socket, or + * something else that it's probably fine. + */ + pcfi = calloc(1, sizeof (*pcfi)); + if (pcfi == NULL) { + err(EXIT_FAILURE, "failed to allocate memory for reading " + "cfgspace data from a file"); + } + + pcfi->pcfi_fd = fd; + *arg = pcfi; + *funcp = pcieadm_read_cfgspace_file; +} + +void +pcieadm_fini_cfgspace_file(void *arg) +{ + pcieadm_cfgspace_file_t *pcfi = arg; + VERIFY0(close(pcfi->pcfi_fd)); + free(pcfi); +} + +typedef struct pcieadm_cfgspace_kernel { + pcieadm_t *pck_pci; + int pck_fd; + uint8_t pck_bus; + uint8_t pck_dev; + uint8_t pck_func; +} pcieadm_cfgspace_kernel_t; + +static boolean_t +pcieadm_read_cfgspace_kernel(uint32_t off, uint8_t len, void *buf, void *arg) +{ + pcieadm_cfgspace_kernel_t *pck = arg; + pcieadm_t *pcip = pck->pck_pci; + pcitool_reg_t pci_reg; + + bzero(&pci_reg, sizeof (pci_reg)); + pci_reg.user_version = PCITOOL_VERSION; + pci_reg.bus_no = pck->pck_bus; + pci_reg.dev_no = pck->pck_dev; + pci_reg.func_no = pck->pck_func; + pci_reg.barnum = 0; + pci_reg.offset = off; + pci_reg.acc_attr = PCITOOL_ACC_ATTR_ENDN_LTL; + + switch (len) { + case 1: + pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_1; + break; + case 2: + pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_2; + break; + case 4: + pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_4; + break; + case 8: + pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_8; + break; + default: + errx(EXIT_FAILURE, "asked to read invalid size from kernel: %u", + len); + } + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0) { + err(EXIT_FAILURE, "failed to raise privileges"); + } + + if (ioctl(pck->pck_fd, PCITOOL_DEVICE_GET_REG, &pci_reg) != 0) { + err(EXIT_FAILURE, "failed to read device offset 0x%x", off); + } + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + switch (len) { + case 1: + *(uint8_t *)buf = (uint8_t)pci_reg.data; + break; + case 2: + *(uint16_t *)buf = (uint16_t)pci_reg.data; + break; + case 4: + *(uint32_t *)buf = (uint32_t)pci_reg.data; + break; + case 8: + *(uint64_t *)buf = (uint64_t)pci_reg.data; + break; + } + + return (B_TRUE); +} + +void +pcieadm_init_cfgspace_kernel(pcieadm_t *pcip, pcieadm_cfgspace_f *funcp, + void **arg) +{ + char *nexus_base; + char nexus_reg[PATH_MAX]; + int fd, nregs, *regs; + pcieadm_cfgspace_kernel_t *pck; + + if ((nexus_base = di_devfs_path(pcip->pia_nexus)) == NULL) { + err(EXIT_FAILURE, "failed to get path to nexus node"); + } + + if (snprintf(nexus_reg, sizeof (nexus_reg), "/devices%s:reg", + nexus_base) >= sizeof (nexus_reg)) { + errx(EXIT_FAILURE, "failed to construct nexus path, path " + "overflow"); + } + free(nexus_base); + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0) { + err(EXIT_FAILURE, "failed to raise privileges"); + } + + if ((fd = open(nexus_reg, O_RDONLY)) < 0) { + err(EXIT_FAILURE, "failed to open %s", nexus_reg); + } + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + nregs = di_prop_lookup_ints(DDI_DEV_T_ANY, pcip->pia_devi, "reg", + ®s); + if (nregs <= 0) { + errx(EXIT_FAILURE, "failed to lookup regs array for %s", + pcip->pia_devstr); + } + + pck = calloc(1, sizeof (pcieadm_cfgspace_kernel_t)); + if (pck == NULL) { + err(EXIT_FAILURE, "failed to allocate memory for reading " + "kernel cfgspace data"); + } + + pck->pck_pci = pcip; + pck->pck_fd = fd; + pck->pck_bus = PCI_REG_BUS_G(regs[0]); + pck->pck_dev = PCI_REG_DEV_G(regs[0]); + pck->pck_func = PCI_REG_FUNC_G(regs[0]); + + *funcp = pcieadm_read_cfgspace_kernel; + *arg = pck; +} + +void +pcieadm_fini_cfgspace_kernel(void *arg) +{ + pcieadm_cfgspace_kernel_t *pck = arg; + + VERIFY0(close(pck->pck_fd)); + free(pck); +} + +static const pcieadm_cmdtab_t pcieadm_cmds[] = { + { "save-cfgspace", pcieadm_save_cfgspace, pcieadm_save_cfgspace_usage }, + { "show-cfgspace", pcieadm_show_cfgspace, pcieadm_show_cfgspace_usage }, + { "show-devs", pcieadm_show_devs, pcieadm_show_devs_usage }, + { NULL } +}; + +static void +pcieadm_usage(const char *format, ...) +{ + uint_t cmd; + + if (format != NULL) { + va_list ap; + + va_start(ap, format); + vwarnx(format, ap); + va_end(ap); + } + + (void) fprintf(stderr, "usage: %s <subcommand> <args> ...\n\n", + pcieadm_progname); + + for (cmd = 0; pcieadm_cmds[cmd].pct_name != NULL; cmd++) { + if (pcieadm_cmds[cmd].pct_use != NULL) { + pcieadm_cmds[cmd].pct_use(stderr); + } + } +} + +int +main(int argc, char *argv[]) +{ + uint_t cmd; + + pcieadm_progname = basename(argv[0]); + + if (argc < 2) { + pcieadm_usage("missing required sub-command"); + exit(EXIT_USAGE); + } + + for (cmd = 0; pcieadm_cmds[cmd].pct_name != NULL; cmd++) { + if (strcmp(pcieadm_cmds[cmd].pct_name, argv[1]) == 0) { + break; + } + } + + if (pcieadm_cmds[cmd].pct_name == NULL) { + pcieadm_usage("unknown sub-command: %s", argv[1]); + exit(EXIT_USAGE); + } + argc -= 2; + argv += 2; + optind = 0; + pcieadm.pia_cmdtab = &pcieadm_cmds[cmd]; + + /* + * Set up common things that all of pcieadm needs before dispatching to + * a specific sub-command. + */ + pcieadm.pia_pcidb = pcidb_open(PCIDB_VERSION); + if (pcieadm.pia_pcidb == NULL) { + err(EXIT_FAILURE, "failed to open PCI ID database"); + } + + pcieadm.pia_root = di_init("/", DINFOCPYALL); + if (pcieadm.pia_root == DI_NODE_NIL) { + err(EXIT_FAILURE, "failed to initialize devinfo tree"); + } + + /* + * Set up privileges now that we have already opened our core libraries. + * We first set up the minimum actual privilege set that we use while + * running. We next set up a second privilege set that has additional + * privileges that are intersected with the users actual privileges and + * are appended to by the underlying command backends. + */ + if ((pcieadm.pia_priv_init = priv_allocset()) == NULL) { + err(EXIT_FAILURE, "failed to allocate privilege set"); + } + + if (getppriv(PRIV_EFFECTIVE, pcieadm.pia_priv_init) != 0) { + err(EXIT_FAILURE, "failed to get current privileges"); + } + + if ((pcieadm.pia_priv_min = priv_allocset()) == NULL) { + err(EXIT_FAILURE, "failed to allocate privilege set"); + } + + if ((pcieadm.pia_priv_eff = priv_allocset()) == NULL) { + err(EXIT_FAILURE, "failed to allocate privilege set"); + } + + /* + * Note, PRIV_FILE_READ is not removed from the basic set so that way we + * can still open libraries that are required due to lazy loading. + */ + priv_basicset(pcieadm.pia_priv_min); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_FILE_LINK_ANY)); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_INFO)); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_SESSION)); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_FORK)); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_NET_ACCESS)); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_FILE_WRITE)); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_EXEC)); + VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_EXEC)); + + priv_copyset(pcieadm.pia_priv_min, pcieadm.pia_priv_eff); + priv_intersect(pcieadm.pia_priv_init, pcieadm.pia_priv_eff); + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcieadm.pia_priv_min) != 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + return (pcieadm.pia_cmdtab->pct_func(&pcieadm, argc, argv)); +} diff --git a/usr/src/cmd/pcieadm/pcieadm.h b/usr/src/cmd/pcieadm/pcieadm.h new file mode 100644 index 0000000000..b5d44ef970 --- /dev/null +++ b/usr/src/cmd/pcieadm/pcieadm.h @@ -0,0 +1,112 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2021 Oxide Computer Company + */ + +#ifndef _PCIEADM_H +#define _PCIEADM_H + +/* + * Common definitions for pcieadm(1M). + */ + +#include <libdevinfo.h> +#include <pcidb.h> +#include <priv.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct pcieadm pcieadm_t; + +typedef struct pcieadm_cmdtab { + const char *pct_name; + int (*pct_func)(pcieadm_t *, int, char **); + void (*pct_use)(FILE *); +} pcieadm_cmdtab_t; + +struct pcieadm { + uint_t pia_indent; + di_node_t pia_root; + const char *pia_devstr; + di_node_t pia_devi; + di_node_t pia_nexus; + pcidb_hdl_t *pia_pcidb; + const pcieadm_cmdtab_t *pia_cmdtab; + priv_set_t *pia_priv_init; + priv_set_t *pia_priv_min; + priv_set_t *pia_priv_eff; +}; + +typedef struct { + void *pdw_arg; + int (*pdw_func)(di_node_t, void *); +} pcieadm_di_walk_t; + +/* + * Config space related + */ +typedef boolean_t (*pcieadm_cfgspace_f)(uint32_t, uint8_t, void *, void *); + +/* + * Utilities + */ +extern void pcieadm_di_walk(pcieadm_t *, pcieadm_di_walk_t *); +extern void pcieadm_init_cfgspace_kernel(pcieadm_t *, pcieadm_cfgspace_f *, + void **); +extern void pcieadm_fini_cfgspace_kernel(void *); +extern void pcieadm_init_cfgspace_file(pcieadm_t *, const char *, + pcieadm_cfgspace_f *, void **); +extern void pcieadm_fini_cfgspace_file(void *); +extern void pcieadm_find_nexus(pcieadm_t *); +extern void pcieadm_find_dip(pcieadm_t *, const char *); +extern boolean_t pcieadm_di_node_is_pci(di_node_t); + +/* + * Output related + */ +extern const char *pcieadm_progname; +extern void pcieadm_indent(void); +extern void pcieadm_deindent(void); +extern void pcieadm_print(const char *, ...); +extern void pcieadm_ofmt_errx(const char *, ...); + +/* + * Command tabs + */ +extern int pcieadm_save_cfgspace(pcieadm_t *, int, char *[]); +extern void pcieadm_save_cfgspace_usage(FILE *); +extern int pcieadm_show_cfgspace(pcieadm_t *, int, char *[]); +extern void pcieadm_show_cfgspace_usage(FILE *); +extern int pcieadm_show_devs(pcieadm_t *, int, char *[]); +extern void pcieadm_show_devs_usage(FILE *); + +#define EXIT_USAGE 2 + +/* + * Privilege related. Note there are no centralized functions around raising and + * lowering privs as that unfortunately makes ROPs more easy to execute. + */ +extern void pcieadm_init_privs(pcieadm_t *); + +/* + * XXX Maybe not here: + */ +#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU)) + +#ifdef __cplusplus +} +#endif + +#endif /* _PCIEADM_H */ diff --git a/usr/src/cmd/pcieadm/pcieadm_cfgspace.c b/usr/src/cmd/pcieadm/pcieadm_cfgspace.c new file mode 100644 index 0000000000..50d98c5ec9 --- /dev/null +++ b/usr/src/cmd/pcieadm/pcieadm_cfgspace.c @@ -0,0 +1,5046 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2021 Oxide Computer Company + */ + +/* + * This file contains logic to walk and print a large chunk of configuration + * space and many of the capabilities. There are multiple sub-commands that + * vector into the same logic (e.g. 'save-cfgspace' and 'show-cfgspace'). In + * general, there are a few major goals with this bit of code: + * + * o Every field should strive to be parsable and therefore selectable for + * output. This drove the idea that every field has both a short name and a + * human name. The short name is a dot-delineated name. When in parsable + * mode, the name will always refer to a single field. However, for + * convenience for humans, when not trying to be parsable, we show the + * parents in the tree. That is if you specify something like + * 'pcie.linkcap.maxspeed', in parsable mode you'll only get that; however, + * in non-parsable mode, you'll get an indication of the capability and + * register that field was in. + * + * o Related to the above, parsable mode always outputs a raw, uninterpreted + * value. This was done on purpose. Some fields require interpreting multiple + * registers to have meaning and long strings aren't always the most useful. + * + * o Every field isn't always pretty printed. This was generally just a + * decision based upon the field itself and how much work it'd be to fit it + * into the framework we have. In general, the ones we're mostly guilty of + * doing this with are related to cases where there's a scaling value in a + * subsequent register. If you find yourself wanting this, feel free to add + * it. + * + * o Currently designated vendor-specific capabilities aren't included here (or + * any specific vendor-specific capabilities for that matter). If they are + * added, they should follow the same angle of using a name to represent a + * sub-capability as we did with HyperTransport. + */ + +#include <err.h> +#include <strings.h> +#include <sys/sysmacros.h> +#include <sys/pci.h> +#include <sys/pcie.h> +#include <sys/debug.h> +#include <ofmt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "pcieadm.h" + +typedef enum pcieadm_cfgspace_op { + PCIEADM_CFGSPACE_OP_PRINT, + PCIEADM_CFGSPACE_OP_WRITE +} pcieadm_cfgspace_op_t; + +typedef enum piceadm_cfgspace_flag { + PCIEADM_CFGSPACE_F_PARSE = 1 << 0, + PCIEADM_CFGSPACE_F_SHORT = 1 << 1, +} pcieadm_cfgspace_flags_t; + +typedef enum pcieadm_cfgspace_otype { + PCIEADM_CFGSPACE_OT_SHORT, + PCIEADM_CFGSPACE_OT_HUMAN, + PCIEADM_CFGSPACE_OT_VALUE +} pcieadm_cfgsapce_otype_t; + +typedef struct pcieadm_cfgspace_ofmt { + const char *pco_base; + const char *pco_short; + const char *pco_human; + uint64_t pco_value; + const char *pco_strval; +} pcieadm_cfgspace_ofmt_t; + +typedef enum pcieadm_regdef_val { + PRDV_STRVAL, + PRDV_BITFIELD, + PRDV_HEX +} pcieadm_regdef_val_t; + +typedef struct pcieadm_regdef_addend { + uint8_t pra_shift; + int64_t pra_addend; +} pcieadm_regdef_addend_t; + +typedef struct pcieadm_regdef { + uint8_t prd_lowbit; + uint8_t prd_hibit; + const char *prd_short; + const char *prd_human; + pcieadm_regdef_val_t prd_valtype; + union { + /* + * Enough space for up to an 8-bit fields worth of values + * (though we expect most to be sparse). + */ + const char *prdv_strval[128]; + pcieadm_regdef_addend_t prdv_hex; + } prd_val; +} pcieadm_regdef_t; + +typedef struct pcieadm_unitdef { + const char *pcd_unit; + uint32_t pcd_mult; +} pcieadm_unitdef_t; + +typedef struct pcieadm_strmap { + const char *psr_str; + uint64_t psr_val; +} pcieadm_strmap_t; + +typedef struct pcieadm_cfgspace_filter { + const char *pcf_string; + size_t pcf_len; + boolean_t pcf_used; +} pcieadm_cfgspace_filter_t; + +typedef struct pcieadm_strfilt { + struct pcieadm_strfilt *pstr_next; + const char *pstr_str; + char pstr_curgen[256]; +} pcieadm_strfilt_t; + +/* + * Data is sized to be large enough that we can hold all of PCIe extended + * configuration space. + */ +typedef union pcieadm_cfgspace_data { + uint8_t pcb_u8[PCIE_CONF_HDR_SIZE]; + uint32_t pcb_u32[PCIE_CONF_HDR_SIZE / 4]; +} pcieadm_cfgspace_data_t; + +typedef struct pcieadm_cfgspace_walk { + pcieadm_t *pcw_pcieadm; + pcieadm_cfgspace_op_t pcw_op; + uint32_t pcw_valid; + pcieadm_cfgspace_data_t *pcw_data; + uint16_t pcw_capoff; + uint32_t pcw_caplen; + int pcw_outfd; + uint_t pcw_dtype; + uint_t pcw_nlanes; + uint_t pcw_pcietype; + uint_t pcw_nfilters; + pcieadm_cfgspace_filter_t *pcw_filters; + pcieadm_cfgspace_flags_t pcw_flags; + ofmt_handle_t pcw_ofmt; + pcieadm_strfilt_t *pcw_filt; +} pcieadm_cfgspace_walk_t; + +void +pcieadm_strfilt_pop(pcieadm_cfgspace_walk_t *walkp) +{ + pcieadm_strfilt_t *filt; + + VERIFY3P(walkp->pcw_filt, !=, NULL); + filt = walkp->pcw_filt; + walkp->pcw_filt = filt->pstr_next; + free(filt); +} + +void +pcieadm_strfilt_push(pcieadm_cfgspace_walk_t *walkp, const char *str) +{ + pcieadm_strfilt_t *filt; + size_t len; + + filt = calloc(1, sizeof (*filt)); + if (filt == NULL) { + errx(EXIT_FAILURE, "failed to allocate memory for string " + "filter"); + } + + filt->pstr_str = str; + if (walkp->pcw_filt == NULL) { + len = strlcat(filt->pstr_curgen, str, + sizeof (filt->pstr_curgen)); + } else { + len = snprintf(filt->pstr_curgen, sizeof (filt->pstr_curgen), + "%s.%s", walkp->pcw_filt->pstr_curgen, str); + filt->pstr_next = walkp->pcw_filt; + } + + if (len >= sizeof (filt->pstr_curgen)) { + errx(EXIT_FAILURE, "overflowed internal string buffer " + "appending %s", str); + } + + walkp->pcw_filt = filt; +} + +static boolean_t +pcieadm_cfgspace_filter(pcieadm_cfgspace_walk_t *walkp, const char *str) +{ + char buf[1024]; + size_t len; + + if (walkp->pcw_nfilters == 0) { + return (B_TRUE); + } + + if (str == NULL) { + return (B_FALSE); + } + + if (walkp->pcw_filt != NULL) { + len = snprintf(buf, sizeof (buf), "%s.%s", + walkp->pcw_filt->pstr_curgen, str); + } else { + len = snprintf(buf, sizeof (buf), "%s", str); + } + + if (len >= sizeof (buf)) { + abort(); + } + + for (uint_t i = 0; i < walkp->pcw_nfilters; i++) { + if (strcmp(buf, walkp->pcw_filters[i].pcf_string) == 0) { + walkp->pcw_filters[i].pcf_used = B_TRUE; + return (B_TRUE); + } + + /* + * If we're in non-parsable mode, we want to do a little bit + * more in a few cases. We want to make sure that we print the + * parents of more-specific entries. That is, if someone + * specified 'header.command.serr', then we want to print + * 'header', and 'header.command'. Similarly, if someone + * specifies an individual field, we want to print all of its + * subfields, that is asking for 'header.command', really gets + * that and all of 'header.command.*'. + */ + if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_PARSE) != 0) { + continue; + } + + if (len >= walkp->pcw_filters[i].pcf_len) { + if (strncmp(buf, walkp->pcw_filters[i].pcf_string, + walkp->pcw_filters[i].pcf_len) == 0 && + buf[walkp->pcw_filters[i].pcf_len] == '.') { + return (B_TRUE); + } + } else { + if (strncmp(buf, walkp->pcw_filters[i].pcf_string, + len) == 0 && + walkp->pcw_filters[i].pcf_string[len] == '.') { + return (B_TRUE); + } + } + } + + return (B_FALSE); +} + +static boolean_t +pcieadm_cfgspace_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) +{ + pcieadm_cfgspace_ofmt_t *pco = ofarg->ofmt_cbarg; + + switch (ofarg->ofmt_id) { + case PCIEADM_CFGSPACE_OT_SHORT: + if (snprintf(buf, buflen, "%s.%s", pco->pco_base, + pco->pco_short) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_CFGSPACE_OT_HUMAN: + if (strlcpy(buf, pco->pco_human, buflen) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_CFGSPACE_OT_VALUE: + if (pco->pco_strval != NULL) { + if (strlcpy(buf, pco->pco_strval, buflen) >= buflen) { + return (B_FALSE); + } + } else { + if (snprintf(buf, buflen, "0x%" PRIx64, + pco->pco_value) >= buflen) { + return (B_FALSE); + } + } + break; + default: + abort(); + } + + return (B_TRUE); +} + + +static const ofmt_field_t pcieadm_cfgspace_ofmt[] = { + { "SHORT", 30, PCIEADM_CFGSPACE_OT_SHORT, pcieadm_cfgspace_ofmt_cb }, + { "HUMAN", 30, PCIEADM_CFGSPACE_OT_HUMAN, pcieadm_cfgspace_ofmt_cb }, + { "VALUE", 20, PCIEADM_CFGSPACE_OT_VALUE, pcieadm_cfgspace_ofmt_cb }, + { NULL, 0, 0, NULL } +}; + +static void +pcieadm_cfgspace_print_parse(pcieadm_cfgspace_walk_t *walkp, + const char *sname, const char *human, uint64_t value) +{ + pcieadm_cfgspace_ofmt_t pco; + + VERIFY3P(walkp->pcw_filt, !=, NULL); + pco.pco_base = walkp->pcw_filt->pstr_curgen; + pco.pco_short = sname; + pco.pco_human = human; + pco.pco_value = value; + pco.pco_strval = NULL; + ofmt_print(walkp->pcw_ofmt, &pco); +} + +typedef struct pcieadm_cfgspace_print pcieadm_cfgspace_print_t; +typedef void (*pcieadm_cfgspace_print_f)(pcieadm_cfgspace_walk_t *, + pcieadm_cfgspace_print_t *, void *); + +struct pcieadm_cfgspace_print { + uint8_t pcp_off; + uint8_t pcp_len; + const char *pcp_short; + const char *pcp_human; + pcieadm_cfgspace_print_f pcp_print; + void *pcp_arg; +}; + +static void +pcieadm_field_printf(pcieadm_cfgspace_walk_t *walkp, const char *shortf, + const char *humanf, uint64_t val, const char *fmt, ...) +{ + va_list ap; + + if (!pcieadm_cfgspace_filter(walkp, shortf)) + return; + + if (walkp->pcw_ofmt != NULL) { + pcieadm_cfgspace_print_parse(walkp, shortf, humanf, val); + return; + } + + if (walkp->pcw_pcieadm->pia_indent > 0) { + (void) printf("%*s", walkp->pcw_pcieadm->pia_indent, ""); + } + + if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) { + (void) printf("|--> %s (%s.%s): ", humanf, + walkp->pcw_filt->pstr_curgen, shortf); + } else { + (void) printf("|--> %s: ", humanf); + } + + va_start(ap, fmt); + (void) vprintf(fmt, ap); + va_end(ap); + +} + +static void +pcieadm_cfgspace_printf(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, uint64_t val, const char *fmt, ...) +{ + va_list ap; + + if (!pcieadm_cfgspace_filter(walkp, print->pcp_short)) + return; + + if (walkp->pcw_ofmt != NULL) { + pcieadm_cfgspace_print_parse(walkp, print->pcp_short, + print->pcp_human, val); + return; + } + + if (walkp->pcw_pcieadm->pia_indent > 0) { + (void) printf("%*s", walkp->pcw_pcieadm->pia_indent, ""); + } + + if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) { + (void) printf("%s (%s.%s): ", print->pcp_human, + walkp->pcw_filt->pstr_curgen, print->pcp_short); + } else { + (void) printf("%s: ", print->pcp_human); + } + + va_start(ap, fmt); + (void) vprintf(fmt, ap); + va_end(ap); +} + +static void +pcieadm_cfgspace_puts(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, const char *str) +{ + if (!pcieadm_cfgspace_filter(walkp, print->pcp_short)) + return; + + if (walkp->pcw_ofmt != NULL) { + pcieadm_cfgspace_ofmt_t pco; + + VERIFY3P(walkp->pcw_filt, !=, NULL); + pco.pco_base = walkp->pcw_filt->pstr_curgen; + pco.pco_short = print->pcp_short; + pco.pco_human = print->pcp_human; + pco.pco_strval = str; + ofmt_print(walkp->pcw_ofmt, &pco); + return; + } + + if (walkp->pcw_pcieadm->pia_indent > 0) { + (void) printf("%*s", walkp->pcw_pcieadm->pia_indent, ""); + } + + if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) { + (void) printf("%s (%s.%s): %s\n", print->pcp_human, + walkp->pcw_filt->pstr_curgen, print->pcp_short, str); + } else { + (void) printf("%s: %s\n", print->pcp_human, str); + } +} + +static uint64_t +pcieadm_cfgspace_extract(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print) +{ + uint32_t val = 0; + + VERIFY3U(print->pcp_len, <=, 8); + VERIFY3U(print->pcp_off + print->pcp_len + walkp->pcw_capoff, <=, + walkp->pcw_valid); + for (uint8_t i = print->pcp_len; i > 0; i--) { + val <<= 8; + val |= walkp->pcw_data->pcb_u8[walkp->pcw_capoff + + print->pcp_off + i - 1]; + } + + return (val); +} + +static uint16_t +pcieadm_cfgspace_extract_u16(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print) +{ + VERIFY(print->pcp_len == 2); + return ((uint16_t)pcieadm_cfgspace_extract(walkp, print)); +} + +static void +pcieadm_cfgspace_print_unit(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + pcieadm_unitdef_t *unit = arg; + uint64_t rawval = pcieadm_cfgspace_extract(walkp, print); + uint64_t val = rawval; + + if (unit->pcd_mult > 1) { + val *= unit->pcd_mult; + } + pcieadm_cfgspace_printf(walkp, print, rawval, "0x%" PRIx64 " %s%s\n", + val, unit->pcd_unit, val != 1 ? "s" : ""); +} + +static void +pcieadm_cfgspace_print_regdef(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + pcieadm_regdef_t *regdef = arg; + uint64_t val = pcieadm_cfgspace_extract(walkp, print); + + pcieadm_cfgspace_printf(walkp, print, val, "0x%" PRIx64 "\n", val); + + pcieadm_indent(); + pcieadm_strfilt_push(walkp, print->pcp_short); + + for (regdef = arg; regdef->prd_short != NULL; regdef++) { + uint32_t nbits = regdef->prd_hibit - regdef->prd_lowbit + 1UL; + uint32_t bitmask = (1UL << nbits) - 1UL; + uint64_t regval = (val >> regdef->prd_lowbit) & bitmask; + const char *strval; + uint64_t actval; + + if (!pcieadm_cfgspace_filter(walkp, regdef->prd_short)) { + continue; + } + + switch (regdef->prd_valtype) { + case PRDV_STRVAL: + strval = regdef->prd_val.prdv_strval[regval]; + if (strval == NULL) { + strval = "reserved"; + } + pcieadm_field_printf(walkp, regdef->prd_short, + regdef->prd_human, regval, "%s (0x%" PRIx64 ")\n", + strval, regval << regdef->prd_lowbit); + break; + case PRDV_HEX: + actval = regval; + if (regdef->prd_val.prdv_hex.pra_shift > 0) { + actval <<= regdef->prd_val.prdv_hex.pra_shift; + } + actval += regdef->prd_val.prdv_hex.pra_addend; + + pcieadm_field_printf(walkp, regdef->prd_short, + regdef->prd_human, regval, "0x% " PRIx64 "\n", + actval); + break; + case PRDV_BITFIELD: + pcieadm_field_printf(walkp, regdef->prd_short, + regdef->prd_human, regval, "0x%" PRIx64 "\n", + regval << regdef->prd_lowbit); + + if (walkp->pcw_ofmt == NULL) { + pcieadm_indent(); + for (uint32_t i = 0; i < nbits; i++) { + if (((1 << i) & regval) == 0) + continue; + pcieadm_print("|--> %s (0x%x)\n", + regdef->prd_val.prdv_strval[i], + 1UL << (i + regdef->prd_lowbit)); + } + pcieadm_deindent(); + } + break; + } + } + + pcieadm_strfilt_pop(walkp); + pcieadm_deindent(); +} + +static void +pcieadm_cfgspace_print_strmap(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + pcieadm_strmap_t *strmap = arg; + uint64_t val = pcieadm_cfgspace_extract(walkp, print); + const char *str = "reserved"; + + for (uint_t i = 0; strmap[i].psr_str != NULL; i++) { + if (strmap[i].psr_val == val) { + str = strmap[i].psr_str; + break; + } + } + + pcieadm_cfgspace_printf(walkp, print, val, "0x%x -- %s\n", val, str); +} + +static void +pcieadm_cfgspace_print_hex(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint64_t val = pcieadm_cfgspace_extract(walkp, print); + + pcieadm_cfgspace_printf(walkp, print, val, "0x%" PRIx64 "\n", val); +} + +static void +pcieadm_cfgspace_print_vendor(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + pcidb_vendor_t *vend; + uint16_t vid = pcieadm_cfgspace_extract_u16(walkp, print); + + vend = pcidb_lookup_vendor(walkp->pcw_pcieadm->pia_pcidb, vid); + if (vend != NULL) { + pcieadm_cfgspace_printf(walkp, print, vid, "0x%x -- %s\n", vid, + pcidb_vendor_name(vend)); + } else { + pcieadm_cfgspace_printf(walkp, print, vid, "0x%x\n", vid); + } +} + +static void +pcieadm_cfgspace_print_device(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + pcidb_device_t *dev; + uint16_t did = pcieadm_cfgspace_extract_u16(walkp, print); + uint16_t vid = walkp->pcw_data->pcb_u8[PCI_CONF_VENID] + + (walkp->pcw_data->pcb_u8[PCI_CONF_VENID + 1] << 8); + + dev = pcidb_lookup_device(walkp->pcw_pcieadm->pia_pcidb, vid, did); + if (dev != NULL) { + pcieadm_cfgspace_printf(walkp, print, did, "0x%x -- %s\n", did, + pcidb_device_name(dev)); + } else { + pcieadm_cfgspace_printf(walkp, print, did, "0x%x\n", did); + } +} + +/* + * To print out detailed information about a subsystem vendor or device, we need + * all of the information about the vendor and device due to the organization of + * the PCI IDs db. + */ +static void +pcieadm_cfgspace_print_subid(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint16_t vid = walkp->pcw_data->pcb_u8[PCI_CONF_VENID] + + (walkp->pcw_data->pcb_u8[PCI_CONF_VENID + 1] << 8); + uint16_t did = walkp->pcw_data->pcb_u8[PCI_CONF_DEVID] + + (walkp->pcw_data->pcb_u8[PCI_CONF_DEVID + 1] << 8); + uint16_t svid = walkp->pcw_data->pcb_u8[PCI_CONF_SUBVENID] + + (walkp->pcw_data->pcb_u8[PCI_CONF_SUBVENID + 1] << 8); + uint16_t sdid = walkp->pcw_data->pcb_u8[PCI_CONF_SUBSYSID] + + (walkp->pcw_data->pcb_u8[PCI_CONF_SUBSYSID + 1] << 8); + uint16_t val = pcieadm_cfgspace_extract_u16(walkp, print); + boolean_t isvendor = print->pcp_off == PCI_CONF_SUBVENID; + + if (isvendor) { + pcidb_vendor_t *vend; + vend = pcidb_lookup_vendor(walkp->pcw_pcieadm->pia_pcidb, + svid); + if (vend != NULL) { + pcieadm_cfgspace_printf(walkp, print, val, + "0x%x -- %s\n", val, pcidb_vendor_name(vend)); + } else { + pcieadm_cfgspace_printf(walkp, print, val, + "0x%x\n", val); + } + } else { + pcidb_subvd_t *subvd; + subvd = pcidb_lookup_subvd(walkp->pcw_pcieadm->pia_pcidb, vid, + did, svid, sdid); + if (subvd != NULL) { + pcieadm_cfgspace_printf(walkp, print, val, + "0x%x -- %s\n", val, pcidb_subvd_name(subvd)); + } else { + pcieadm_cfgspace_printf(walkp, print, val, "0x%x\n", + val); + } + } +} + +/* + * The variable natures of BARs is a pain. This makes printing this out and the + * fields all a bit gross. + */ +static void +pcieadm_cfgspace_print_bars(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint32_t *barp = &walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + + print->pcp_off) / 4]; + char barname[32]; + const char *typestrs[2] = { "Memory Space", "I/O Space" }; + + for (uint_t i = 0; i < print->pcp_len / 4; i++) { + uint_t type; + (void) snprintf(barname, sizeof (barname), "%s%u", + print->pcp_short, i); + + type = barp[i] & PCI_BASE_SPACE_M; + + if (pcieadm_cfgspace_filter(walkp, barname) && + walkp->pcw_ofmt == NULL) { + if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != + 0) { + pcieadm_print("%s %u (%s.%s)\n", + print->pcp_human, i, + walkp->pcw_filt->pstr_curgen, barname); + } else { + pcieadm_print("%s %u\n", print->pcp_human, i); + } + } + + pcieadm_strfilt_push(walkp, barname); + pcieadm_indent(); + + pcieadm_field_printf(walkp, "space", "Space", type, + "%s (0x%x)\n", typestrs[type], type); + + if (type == PCI_BASE_SPACE_IO) { + uint32_t addr = barp[i] & PCI_BASE_IO_ADDR_M; + + pcieadm_field_printf(walkp, "addr", "Address", addr, + "0x%" PRIx32 "\n", addr); + } else { + uint8_t type, pre; + uint64_t addr; + const char *locstr; + + type = barp[i] & PCI_BASE_TYPE_M; + pre = barp[i] & PCI_BASE_PREF_M; + addr = barp[i] & PCI_BASE_M_ADDR_M; + + if (type == PCI_BASE_TYPE_ALL) { + addr += (uint64_t)barp[i+1] << 32; + i++; + } + + pcieadm_field_printf(walkp, "addr", "Address", addr, + "0x%" PRIx64 "\n", addr); + + switch (type) { + case PCI_BASE_TYPE_MEM: + locstr = "32-bit"; + break; + case PCI_BASE_TYPE_LOW: + locstr = "Sub-1 MiB"; + break; + case PCI_BASE_TYPE_ALL: + locstr = "64-bit"; + break; + case PCI_BASE_TYPE_RES: + default: + locstr = "Reserved"; + break; + } + + pcieadm_field_printf(walkp, "addr", "Address", addr, + "%s (0x%x)\n", locstr, type >> 1); + pcieadm_field_printf(walkp, "prefetch", "Prefetchable", + pre != 0, "%s (0x%x)\n", pre != 0 ? "yes" : "no", + pre != 0); + } + + pcieadm_deindent(); + pcieadm_strfilt_pop(walkp); + } +} + +static void +pcieadm_cfgspace_print_ecv(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint16_t bitlen, nwords; + + if (BITX(walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 4], 5, 5) == 0) { + return; + } + + bitlen = walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 5]; + if (bitlen == 0) { + bitlen = 256; + } + + nwords = bitlen / 32; + if ((bitlen % 8) != 0) { + nwords++; + } + + for (uint16_t i = 0; i < nwords; i++) { + char tshort[32], thuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(tshort, sizeof (tshort), "ecv%u", i); + (void) snprintf(thuman, sizeof (thuman), "Egress Control " + "Vector %u", i); + p.pcp_off = print->pcp_off + i * 4; + p.pcp_len = 4; + p.pcp_short = tshort; + p.pcp_human = thuman; + p.pcp_print = pcieadm_cfgspace_print_hex; + p.pcp_arg = NULL; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +static void +pcieadm_cfgspace_print_dpa_paa(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint8_t nents; + + nents = BITX(walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 4], 4, 0) + 1; + if (nents == 0) { + return; + } + + for (uint8_t i = 0; i < nents; i++) { + char tshort[32], thuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(tshort, sizeof (tshort), "%s%u", + print->pcp_short, i); + (void) snprintf(thuman, sizeof (thuman), "%s %u", + print->pcp_human, i); + + p.pcp_off = print->pcp_off + i; + p.pcp_len = 1; + p.pcp_short = tshort; + p.pcp_human = thuman; + p.pcp_print = pcieadm_cfgspace_print_hex; + p.pcp_arg = NULL; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +/* + * Config Space Header Table Definitions + */ +static pcieadm_regdef_t pcieadm_regdef_command[] = { + { 0, 0, "io", "I/O Space", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "mem", "Memory Space", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "bus", "Bus Master", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "spec", "Special Cycle", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "mwi", "Memory Write and Invalidate", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 5, 5, "vga", "VGA Palette Snoop", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 6, 6, "per", "Parity Error Response", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 7, 7, "idsel", "IDSEL Stepping/Wait Cycle Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 8, "serr", "SERR# Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } }, }, + { 9, 9, "fbtx", "Fast Back-to-Back Transactions", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } }, }, + { 10, 10, "intx", "Interrupt X", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "enabled", "disabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_status[] = { + { 0, 0, "imm", "Immediate Readiness", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } }, }, + { 3, 3, "istat", "Interrupt Status", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not pending", "pending" } }, }, + { 4, 4, "capsup", "Capabilities List", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } }, }, + { 5, 5, "66mhz", "66 MHz Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } }, }, + { 7, 7, "fbtxcap", "Fast Back-to-Back Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } }, }, + { 8, 8, "mdperr", "Master Data Parity Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no error", "error detected" } }, }, + { 9, 10, "devsel", "DEVSEL# Timing", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "fast", "medium", "slow", + "reserved" } } }, + { 11, 11, "sta", "Signaled Target Abort", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 12, 12, "rta", "Received Target Abort", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 13, 13, "rma", "Received Master Abort", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 14, 14, "sse", "Signaled System Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 15, 15, "dpe", "Detected Parity Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +/* + * It might be interesting to translate these into numbers at a future point. + */ +static pcieadm_regdef_t pcieadm_regdef_class[] = { + { 16, 23, "class", "Class Code", PRDV_HEX }, + { 7, 15, "sclass", "Sub-Class Code", PRDV_HEX }, + { 0, 7, "pi", "Programming Interface", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bridge_iobase[] = { + { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "16-bit", "32-bit" } } }, + { 4, 7, "base", "Base", PRDV_HEX, + .prd_val = { .prdv_hex = { 12 } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bridge_iolim[] = { + { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "16-bit", "32-bit" } } }, + { 4, 7, "limit", "Limit", PRDV_HEX, + .prd_val = { .prdv_hex = { 12, 0xfff } } }, + { -1, -1, NULL } +}; + + +static pcieadm_regdef_t pcieadm_regdef_bridgests[] = { + { 5, 5, "66mhz", "66 MHz", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 7, 7, "fastb2b", "Fast Back-to-Back Transactions", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 8, "mdperr", "Master Data Parity Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no error", "error detected" } } }, + { 9, 10, "devsel", "DEVSEL# Timing", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "fast", "medium", "slow" } } }, + { 11, 11, "sta", "Signaled Target Abort", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no abort", "aborted" } } }, + { 12, 12, "rta", "Received Target Abort", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no abort", "aborted" } } }, + { 13, 13, "rma", "Received Master Abort", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no abort", "aborted" } } }, + { 14, 14, "rsyserr", "Received System Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no error", "error received" } } }, + { 15, 15, "dperr", "Detected Parity Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no error", "error detected" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bridge_membase[] = { + { 4, 16, "base", "Base", PRDV_HEX, + .prd_val = { .prdv_hex = { 20 } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bridge_memlim[] = { + { 4, 16, "limit", "Limit", PRDV_HEX, + .prd_val = { .prdv_hex = { 20, 0xfffff } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bridge_pfbase[] = { + { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "32-bit", "64-bit" } } }, + { 4, 16, "base", "Base", PRDV_HEX, + .prd_val = { .prdv_hex = { 20 } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bridge_pflim[] = { + { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "32-bit", "64-bit" } } }, + { 4, 16, "limit", "Limit", PRDV_HEX, + .prd_val = { .prdv_hex = { 20, 0xfffff } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bridge_ctl[] = { + { 0, 0, "perrresp", "Parity Error Response", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "serr", "SERR#", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "isa", "ISA", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "vga", "VGA", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "vgadec", "VGA 16-bit Decode", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "10-bit", "16-bit" } } }, + { 5, 5, "mabort", "Master Abort", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 6, 6, "secrst", "Secondary Bus Reset", PRDV_HEX }, + { 7, 7, "fastb2b", "Fast Back-to-Back Transactions", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 8, 8, "pridisc", "Primary Discard Timer", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "2^15 cycles", "2^10 cycles" } } }, + { 9, 9, "secdisc", "Secondary Discard Timer", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "2^15 cycles", "2^10 cycles" } } }, + { 10, 10, "disctimer", "Discard Timer Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 11, 11, "discserr", "Discard Timer SERR#", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_unitdef_t pcieadm_unitdef_cache = { + "byte", 4 +}; + +static pcieadm_unitdef_t pcieadm_unitdef_latreg = { "cycle" }; + +static pcieadm_regdef_t pcieadm_regdef_header[] = { + { 0, 6, "layout", "Header Layout", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Device", "Bridge", "PC Card" } } }, + { 7, 7, "mfd", "Multi-Function Device", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_bist[] = { + { 0, 3, "code", "Completion Code", PRDV_HEX }, + { 6, 6, "start", "Start BIST", PRDV_HEX }, + { 7, 7, "cap", "BIST Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_exprom[] = { + { 0, 0, "enable", "Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 11, 31, "addr", "Base Address", PRDV_HEX, + .prd_val = { .prdv_hex = { 21 } } }, + { -1, -1, NULL } +}; + +static pcieadm_strmap_t pcieadm_strmap_ipin[] = { + { "none", 0 }, + { "INTA", PCI_INTA }, + { "INTB", PCI_INTB }, + { "INTC", PCI_INTC }, + { "INTD", PCI_INTD }, + { NULL } +}; + + +static pcieadm_cfgspace_print_t pcieadm_cfgspace_type0[] = { + { 0x0, 2, "vendor", "Vendor ID", pcieadm_cfgspace_print_vendor }, + { 0x2, 2, "device", "Device ID", pcieadm_cfgspace_print_device }, + { 0x4, 2, "command", "Command", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_command }, + { 0x6, 2, "status", "Status", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_status }, + { 0x8, 1, "revision", "Revision ID", pcieadm_cfgspace_print_hex }, + { 0x9, 3, "class", "Class Code", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_class }, + { 0xc, 1, "cache", "Cache Line Size", pcieadm_cfgspace_print_unit, + &pcieadm_unitdef_cache }, + { 0xd, 1, "latency", "Latency Timer", pcieadm_cfgspace_print_unit, + &pcieadm_unitdef_latreg }, + { 0xe, 1, "type", "Header Type", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_header }, + { 0xf, 1, "bist", "BIST", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_bist }, + { 0x10, 24, "bar", "Base Address Register", + pcieadm_cfgspace_print_bars }, + { 0x28, 4, "cis", "Cardbus CIS Pointer", pcieadm_cfgspace_print_hex }, + { 0x2c, 2, "subvid", "Subsystem Vendor ID", + pcieadm_cfgspace_print_subid }, + { 0x2e, 2, "subdev", "Subsystem Device ID", + pcieadm_cfgspace_print_subid }, + { 0x30, 4, "rom", "Expansion ROM", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_exprom }, + { 0x34, 1, "cap", "Capabilities Pointer", pcieadm_cfgspace_print_hex }, + { 0x3c, 1, "iline", "Interrupt Line", pcieadm_cfgspace_print_hex }, + { 0x3d, 1, "ipin", "Interrupt Pin", pcieadm_cfgspace_print_strmap, + pcieadm_strmap_ipin }, + { 0x3e, 1, "gnt", "Min_Gnt", pcieadm_cfgspace_print_hex }, + { 0x3f, 1, "lat", "Min_Lat", pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cfgspace_type1[] = { + { 0x0, 2, "vendor", "Vendor ID", pcieadm_cfgspace_print_vendor }, + { 0x2, 2, "device", "Device ID", pcieadm_cfgspace_print_device }, + { 0x4, 2, "command", "Command", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_command }, + { 0x6, 2, "status", "Status", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_status }, + { 0x8, 1, "revision", "Revision ID", pcieadm_cfgspace_print_hex }, + { 0x9, 3, "class", "Class Code", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_class }, + { 0xc, 1, "cache", "Cache Line Size", pcieadm_cfgspace_print_unit, + &pcieadm_unitdef_cache }, + { 0xd, 1, "latency", "Latency Timer", pcieadm_cfgspace_print_unit, + &pcieadm_unitdef_latreg }, + { 0xe, 1, "type", "Header Type", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_header }, + { 0xf, 1, "bist", "BIST", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_bist }, + { 0x10, 8, "bar", "Base Address Register", + pcieadm_cfgspace_print_bars }, + { PCI_BCNF_PRIBUS, 1, "pribus", "Primary Bus Number", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_SECBUS, 1, "secbus", "Secondary Bus Number", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_SUBBUS, 1, "subbus", "Subordinate Bus Number", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_LATENCY_TIMER, 1, "latency2", "Secondary Latency timer", + pcieadm_cfgspace_print_unit, &pcieadm_unitdef_latreg }, + { PCI_BCNF_IO_BASE_LOW, 1, "iobase", "I/O Base Low", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_iobase }, + { PCI_BCNF_IO_LIMIT_LOW, 1, "iolimit", "I/O Limit Low", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_iolim }, + { PCI_BCNF_SEC_STATUS, 2, "status2", "Secondary Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridgests }, + { PCI_BCNF_MEM_BASE, 2, "membase", "Memory Base", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_membase }, + { PCI_BCNF_MEM_LIMIT, 2, "memlimit", "Memory Limit", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_memlim }, + { PCI_BCNF_PF_BASE_LOW, 2, "pfbase", "Prefetchable Memory Base", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_pfbase }, + { PCI_BCNF_PF_LIMIT_LOW, 2, "pflimit", "Prefetchable Memory Limit", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_pflim }, + { PCI_BCNF_PF_BASE_HIGH, 4, "pfbasehi", + "Prefetchable Base Upper 32 bits", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_PF_LIMIT_HIGH, 4, "pflimihi", + "Prefetchable Limit Upper 32 bits", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_IO_BASE_HI, 2, "iobasehi", "I/O Base Upper 16 bits", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_IO_LIMIT_HI, 2, "iobasehi", "I/O Limit Upper 16 bits", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_CAP_PTR, 1, "cap", "Capabilities Pointer", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_ROM, 4, "rom", "Expansion ROM", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_exprom }, + { PCI_BCNF_ILINE, 1, "iline", "Interrupt Line", + pcieadm_cfgspace_print_hex }, + { PCI_BCNF_IPIN, 1, "ipin", "Interrupt Pin", + pcieadm_cfgspace_print_strmap, pcieadm_strmap_ipin }, + { PCI_BCNF_BCNTRL, 2, "bctl", "Bridge Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_ctl }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cfgspace_unknown[] = { + { 0x0, 2, "vendor", "Vendor ID", pcieadm_cfgspace_print_vendor }, + { 0x2, 2, "device", "Device ID", pcieadm_cfgspace_print_device }, + { 0x8, 1, "revision", "Revision ID", pcieadm_cfgspace_print_hex }, + { 0xe, 1, "type", "Header Type", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_header }, + { -1, -1, NULL } +}; + +/* + * Power Management Capability Version 3. Note versions two and three seem to be + * the same, but are used to indicate compliance to different revisions of the + * PCI power management specification. + */ +static pcieadm_regdef_t pcieadm_regdef_pmcap[] = { + { 0, 2, "vers", "Version", PRDV_HEX }, + { 3, 3, "clock", "PME Clock", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not required", "required" } } }, + { 4, 4, "irrd0", "Immediate Readiness on Return to D0", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 5, 5, "dsi", "Device Specific Initialization", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 6, 8, "auxcur", "Auxiliary Current", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "0", "55 mA", "100 mA", "160 mA", + "220 mA", "270 mA", "320 mA", "375 mA" } } }, + { 9, 9, "d1", "D1", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 10, 10, "d2", "D2", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 11, 15, "pme", "PME Support", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "D0", "D1", "D2", "D3hot", + "D3cold" } } }, + { -1, -1, NULL } +}; + + +static pcieadm_cfgspace_print_t pcieadm_cap_pcipm_v3[] = { + { PCI_PMCAP, 2, "pmcap", "Power Management Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pmcap }, + { -1, -1, NULL } +}; + +/* + * PCI Bridge Subsystem Capability + */ +static pcieadm_cfgspace_print_t pcieadm_cap_bridge_subsys[] = { + { 0x4, 2, "subvid", "Subsystem Vendor ID", pcieadm_cfgspace_print_hex }, + { 0x6, 2, "subdev", "Subsystem Device ID", pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +/* + * MSI Capability + */ +static pcieadm_regdef_t pcieadm_regdef_msictrl[] = { + { 0, 0, "enable", "MSI Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 3, "mmsgcap", "Multiple Message Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1 vector", "2 vectors", + "4 vectors", "8 vectors", "16 vectors", "32 vectors" } } }, + { 4, 6, "mmsgen", "Multiple Message Enabled", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1 vector", "2 vectors", + "4 vectors", "8 vectors", "16 vectors", "32 vectors" } } }, + { 7, 7, "addr64", "64-bit Address Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 8, "pvm", "Per-Vector Masking Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 9, 9, "extmdcap", "Extended Message Data Capable", + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 10, 10, "extmden", "extended Message Data Enable", + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_msi_32[] = { + { PCI_MSI_CTRL, 2, "ctrl", "Message Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl }, + { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_32BIT_DATA, 2, "data", "Message Data", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_msi_32ext[] = { + { PCI_MSI_CTRL, 2, "ctrl", "Message Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl }, + { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_32BIT_DATA, 2, "data", "Message Data", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_32BIT_EXTDATA, 2, "extdata", "Extended Message Data", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_msi_32pvm[] = { + { PCI_MSI_CTRL, 2, "ctrl", "Message Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl }, + { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_32BIT_DATA, 2, "data", "Message Data", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_32BIT_EXTDATA, 2, "extdata", "Extended Message Data", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_32BIT_MASK, 4, "mask", "Mask Bits", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_32BIT_PENDING, 4, "pend", "Pending Bits", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_msi_64[] = { + { PCI_MSI_CTRL, 2, "ctrl", "Message Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl }, + { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_ADDR, 4, "upadd", "Upper Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_DATA, 2, "data", "Message Data", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_msi_64ext[] = { + { PCI_MSI_CTRL, 2, "ctrl", "Message Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl }, + { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_ADDR, 4, "upadd", "Upper Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_DATA, 2, "data", "Message Data", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_EXTDATA, 2, "extdata", "Extended Message Data", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_msi_64pvm[] = { + { PCI_MSI_CTRL, 2, "ctrl", "Message Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl }, + { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_ADDR, 4, "upadd", "Upper Message Address", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_DATA, 2, "data", "Message Data", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_EXTDATA, 2, "extdata", "Extended Message Data", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_MASKBITS, 4, "mask", "Mask Bits", + pcieadm_cfgspace_print_hex }, + { PCI_MSI_64BIT_PENDING, 4, "pend", "Pending Bits", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +/* + * MSI-X Capability + */ +static pcieadm_regdef_t pcieadm_regdef_msixctrl[] = { + { 0, 10, "size", "Table Size", PRDV_HEX, + .prd_val = { .prdv_hex = { 0, 1 } } }, + { 14, 14, "mask", "Function Mask", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unmasked", "masked" } } }, + { 15, 15, "enable", "MSI-X Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_msixtable[] = { + { 0, 2, "bir", "Table BIR", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "BAR 0", "BAR 1", "BAR 2", "BAR 3", + "BAR 4", "BAR 5" } } }, + { 3, 31, "offset", "Table Offset", PRDV_HEX, + .prd_val = { .prdv_hex = { 3 } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_msixpba[] = { + { 0, 2, "bir", "PBA BIR", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "BAR 0", "BAR 1", "BAR 2", "BAR 3", + "BAR 4", "BAR 5" } } }, + { 3, 31, "offset", "PBA Offset", PRDV_HEX, + .prd_val = { .prdv_hex = { 3 } } }, + { -1, -1, NULL } +}; + + +static pcieadm_cfgspace_print_t pcieadm_cap_msix[] = { + { PCI_MSIX_CTRL, 2, "ctrl", "Control Register", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msixctrl }, + { PCI_MSIX_TBL_OFFSET, 4, "table", "Table Offset", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msixtable }, + { PCI_MSIX_PBA_OFFSET, 4, "pba", "PBA Offset", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_msixpba }, + { -1, -1, NULL } +}; + +/* + * PCI Express Capability + */ +static pcieadm_regdef_t pcieadm_regdef_pcie_cap[] = { + { 0, 3, "vers", "Version", PRDV_HEX }, + { 4, 7, "type", "Device/Port Type", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "PCIe Endpoint", + "Legacy PCIe Endpoint", NULL, NULL, + "Root Port of PCIe Root Complex", + "Upstream Port of PCIe Switch", + "Downstream Port of PCIe Switch", + "PCIe to PCI/PCI-X Bridge", + "PCI/PCI-x to PCIe Bridge", + "RCiEP", + "Root Complex Event Collector" } } }, + { 8, 8, "slot", "Slot Implemented", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "No", "Yes" } } }, + { 9, 13, "intno", "Interrupt Message Number", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_devcap[] = { + { 0, 2, "mps", "Max Payload Size Supported", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "128 bytes", "256 bytes", + "512 bytes", "1024 byes", "2048 bytes", "4096 bytes" } } }, + { 3, 4, "pfunc", "Phantom Functions Supported", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "No", "1-bit", "2-bits", + "3-bits" } } }, + { 5, 5, "exttag", "Extended Tag Field", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "5-bit", "8-bit" } } }, + { 6, 8, "l0slat", "L0s Acceptable Latency", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "64 ns", "128 ns", "256 ns", + "512 ns", "1 us", "2 us", "4 us", "No limit" } } }, + { 9, 11, "l1lat", "L1 Acceptable Latency", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1 us", "2 us", "4 us", "8 us", + "16 us", "32 us", "64 us", "No limit" } } }, + { 15, 15, "rber", "Role Based Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 16, 16, "errcor", "ERR_COR Subclass", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 18, 25, "csplv", "Captured Slot Power Limit", PRDV_HEX }, + { 26, 27, "cspls", "Captured Slot Power Limit Scale", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1.0x", "0.1x", "0.01x", + "0.001x" } } }, + { 28, 28, "flr", "Function Level Reset", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_devctl[] = { + { 0, 0, "corerr", "Correctable Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "nferr", "Non-Fatal Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "ferr", "Fatal Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "unsupreq", "Unsupported Request Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "relord", "Relaxed Ordering", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 5, 7, "mps", "Max Payload Size", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "128 bytes", "256 bytes", + "512 bytes", "1024 byes", "2048 bytes", "4096 bytes" } } }, + { 8, 8, "exttag", "Extended Tag Field", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 9, 9, "pfunc", "Phantom Functions", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 9, 9, "auxpm", "Aux Power PM", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 11, 11, "nosnoop", "No Snoop", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 12, 14, "mrrs", "Max Read Request Size", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "128 bytes", "256 bytes", + "512 bytes", "1024 byes", "2048 bytes", "4096 bytes" } } }, + { 15, 15, "bcrflr", "Bridge Configuration Retry / Function Level Reset", + PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_devsts[] = { + { 0, 0, "corerr", "Correctable Error Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 1, 1, "nferr", "Non-Fatal Error Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 2, 2, "ferr", "Fatal Error Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 3, 3, "unsupreq", "Unsupported Request Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 4, 4, "auxpm", "AUX Power Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 5, 5, "txpend", "Transactions Pending", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 6, 6, "eprd", "Emergency Power Reduction Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_linkcap[] = { + { 0, 3, "maxspeed", "Maximum Link Speed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { NULL, "2.5 GT/s", "5.0 GT/s", + "8.0 GT/s", "16.0 GT/s", "32.0 GT/s" } } }, + { 4, 9, "maxwidth", "Maximum Link Width", PRDV_HEX }, + { 10, 11, "aspm", "ASPM Support", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "None", "L0s", "L1", "L0s/L1" } } }, + { 12, 14, "l0slat", "L0s Exit Latency", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "<64ns", "64-128ns", "128-256ns", + "256-512ns", "512ns-1us", "1-2us", "2-4us", ">4us" } } }, + { 15, 17, "l1lat", "L1 Exit Latency", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "<1us", "1-2us", "2-4us", "4-8us", + "8-16us", "16-32us" "32-64us", ">64us" } } }, + { 18, 18, "clockpm", "Clock Power Management", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 19, 19, "supdown", "Surprise Down Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 20, 20, "dlact", "Data Link Layer Active Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 21, 21, "linkbw", "Link Bandwidth Notification Capability", + PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 22, 22, "aspmcomp", "ASPM Optionality Compliance", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not compliant", "compliant" } } }, + { 24, 31, "portno", "Port Number", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_linkctl[] = { + { 0, 1, "aspmctl", "ASPM Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "None", "L0s", "L1", "L0s/L1" } } }, + { 3, 3, "rcb", "Read Completion Boundary", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "64 byte", "128 byte" } } }, + { 4, 4, "disable", "Link Disable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not force disabled", + "force disabled" } } }, + { 5, 5, "retrain", "Retrain Link", PRDV_HEX }, + { 6, 6, "ccc", "Common Clock Configuration", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "asynchronous", "common" } } }, + { 7, 7, "extsync", "Extended Sync", PRDV_HEX, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 8, 8, "clkpm", "Clock Power Management", PRDV_HEX, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 9, 9, "hwawd", "Hardware Autonomous Width", PRDV_HEX, + .prd_val = { .prdv_strval = { "enabled", "disabled" } } }, + { 10, 10, "linkbwint", "Link Bandwidth Management Interrupt", PRDV_HEX, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 11, 11, "linkabwint", "Link Autonomous Bandwidth Interrupt", PRDV_HEX, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 14, 15, "drs", "DRS Signaling Control", PRDV_HEX, + .prd_val = { .prdv_strval = { "not reported", "Interrupt enabled", + "DRS->FRS enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_linksts[] = { + { 0, 3, "speed", "Link Speed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { NULL, "2.5 GT/s", "5.0 GT/s", + "8.0 GT/s", "16.0 GT/s", "32.0 GT/s" } } }, + { 4, 9, "width", "Link Width", PRDV_HEX }, + { 11, 11, "training", "Link Training", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 12, 12, "slotclk", "Slot Clock Configuration", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "asynchronous", "common" } } }, + { 13, 13, "dllact", "Data Link Layer Link Active", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 14, 14, "linkbw", "Link Bandwidth Management Status", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no change", "change occurred" } } }, + { 15, 15, "linkabw", "Link Autonomous Bandwidth Status", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no change", "change occurred" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_slotcap[] = { + { 0, 0, "attnbtn", "Attention Button Present", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 1, 1, "pwrctrl", "Power Controller Present", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 2, 2, "mrlsen", "MRL Sensor Present", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 3, 3, "attnind", "Attention Indicator Present", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 4, 4, "powind", "Power Indicator Present", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 5, 5, "hpsup", "Hot-Plug Surprise", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 6, 6, "hpcap", "Hot-Plug Capable ", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 7, 14, "slotplv", "Slot Power Limit Value", PRDV_HEX }, + { 15, 16, "slotpls", "Slot Power Limit Scale", PRDV_HEX }, + { 17, 17, "emi", "Electromechanical Interlock Present", + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 18, 18, "ncc", "No Command Completed", PRDV_HEX, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 19, 31, "slotno", "Physical Slot Number", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_slotctl[] = { + { 0, 0, "attnbtn", "Attention Button Pressed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "powflt", "Power Fault Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "mrlsen", "MRL Sensor Changed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "presdet", "Presence Detect Changed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "ccmpltint", "Command Complete Interrupt", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "Enabled" } } }, + { 5, 5, "hpi", "Hot Plug Interrupt Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 6, 7, "attnind", "Attention Indicator Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { NULL, "on", "blink", "off" } } }, + { 8, 9, "powin", "Power Indicator Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { NULL, "on", "blink", "off" } } }, + { 10, 10, "pwrctrl", "Power Controller Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "power on", "power off" } } }, + { 11, 11, "emi", "Electromechanical Interlock Control", PRDV_HEX }, + { 12, 12, "dll", "Data Link Layer State Changed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 13, 13, "autopowdis", "Auto Slot Power Limit", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "enabled", "disabled" } } }, + { 14, 14, "ibpddis", "In-Band PD", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "enabled", "disabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_slotsts[] = { + { 0, 0, "attnbtn", "Attention Button Pressed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 1, 1, "powflt", "Power Fault Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 2, 2, "mrlsen", "MRL Sensor Changed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 3, 3, "presdet", "Presence Detect Changed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 4, 4, "ccmplt", "Command Complete", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 5, 5, "mrlsen", "MRL Sensor State", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "closed", "open" } } }, + { 6, 6, "presdet", "Presence Detect State", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not present", "present" } } }, + { 7, 7, "emi", "Electromechanical Interlock", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disengaged", "engaged" } } }, + { 8, 8, "dll", "Data Link Layer State Changed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_rootcap[] = { + { 0, 0, "syscorerr", "System Error on Correctable Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "sysnonftl", "System Error on Non-Fatal Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "sysfatal", "System Error on Fatal Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "pmeie", "PME Interrupt", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "crssw", "CRS Software Visibility", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_rootctl[] = { + { 0, 0, "crssw", "CRS Software Visibility", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_rootsts[] = { + { 0, 15, "pmereqid", "PME Requester ID", PRDV_HEX }, + { 16, 16, "pmests", "PME Status", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "deasserted", "asserted" } } }, + { 17, 17, "pmepend", "PME Pending", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_devcap2[] = { + { 0, 3, "cmpto", "Completion Timeout Ranges Supported", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "50us-10ms", "10ms-250ms", + "250ms-4s", "4s-64s" } } }, + { 4, 4, "cmptodis", "Completion Timeout Disable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 5, 5, "ari", "ARI Forwarding", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 6, 6, "atomroute", "AtomicOp Routing", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 7, 7, "atom32", "32-bit AtomicOp Completer", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 8, "atom64", "64-bit AtomicOp Completer", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 9, 9, "cas128", "128-bit CAS Completer", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 10, 10, "norelord", "No Ro-enabld PR-PR Passing", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 11, 11, "ltr", "LTR Mechanism", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 12, 13, "tph", "TPH Completer", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "TPH supported", + NULL, "TPH and Extended TPH supported" } } }, + { 14, 15, "lncls", "LN System CLS", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", + "LN with 64-byte cachelines", "LN with 128-byte cachelines" } } }, + { 16, 16, "tag10comp", "10-bit Tag Completer", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 17, 17, "tag10req", "10-bit Tag Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 18, 19, "obff", "OBFF", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "Message Signaling", + "WAKE# Signaling", "WAKE# and Message Signaling" } } }, + { 20, 20, "extfmt", "Extended Fmt Field Supported", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 21, 21, "eetlp", "End-End TLP Prefix Supported", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 22, 23, "maxeetlp", "Max End-End TLP Prefixes", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "4", "1", "2", "3" } } }, + { 24, 25, "empr", "Emergency Power Reduction", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", + "supported, device-specific", + "supported, from factor or device-specific" } } }, + { 21, 21, "emprinit", + "Emergency Power Reduction Initialization Required", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 31, 31, "frs", "Function Readiness Status", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_devctl2[] = { + { 0, 3, "cmpto", "Completion Timeout", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "50us-50ms", "50us-100us", + "1ms-10ms", NULL, NULL, "16ms-55ms", "65ms-210ms", NULL, NULL, + "260ms-900ms", "1s-3.5s", NULL, NULL, "4s-13s", "17s-64s" } } }, + { 4, 4, "cmptodis", "Completion Timeout Disabled", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not disabled", "disabled" } } }, + { 5, 5, "ari", "ARI Forwarding", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 6, 6, "atomreq", "AtomicOp Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 7, 7, "atomblock", "AtomicOp Egress Blocking", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unblocked", "blocked" } } }, + { 8, 8, "idoreq", "ID-Based Ordering Request", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 9, 9, "idocomp", "ID-Based Ordering Completion", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 10, 10, "ltr", "LTR Mechanism", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 11, 11, "empowred", "Emergency Power Reduction", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not requested", "requested" } } }, + { 12, 12, "tag10req", "10-bit Tag Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 13, 14, "obff", "OBFF", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "message signaling - A", + "message signaling - B", "WAKE# signaling" } } }, + { 15, 15, "eetlpblk", "End-End TLP Prefix Blocking", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unblocked", "blocked" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_devsts2[] = { + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_linkcap2[] = { + { 1, 7, "supspeeds", "Supported Link Speeds", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s", + "16.0 GT/s", "32.0 GT/s" } } }, + { 8, 8, "crosslink", "Crosslink", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 9, 15, "skposgen", "Lower SKP OS Generation Supported Speeds Vector", + PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s", + "16.0 GT/s", "32.0 GT/s" } } }, + { 16, 22, "skposrecv", "Lower SKP OS Reception Supported Speeds Vector", + PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s", + "16.0 GT/s", "32.0 GT/s" } } }, + { 23, 23, "retimedet", "Retimer Presence Detect Supported", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 24, 24, "retime2det", "Two Retimers Presence Detect Supported", + PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 31, 31, "drs", "Device Readiness Status", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_linkctl2[] = { + { 0, 3, "targspeed", "Target Link Speed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { NULL, "2.5 GT/s", "5.0 GT/s", + "8.0 GT/s", "16.0 GT/s", "32.0 GT/s" } } }, + { 4, 4, "comp", "Enter Compliance", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 5, 5, "hwautosp", "Hardware Autonomous Speed Disable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not disabled", "disabled" } } }, + { 6, 6, "seldeemph", "Selectable De-emphasis", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "-6 dB", "-3.5 dB" } } }, + { 7, 9, "txmarg", "TX Margin", PRDV_HEX }, + { 10, 10, "modcomp", "Enter Modified Compliance", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 11, 11, "compsos", "Compliance SOS", + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 12, 15, "compemph", "Compliance Preset/De-emphasis", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_linksts2[] = { + { 0, 0, "curdeemph", "Current De-emphasis Level", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "-6 dB", "-3.5 dB" } } }, + { 1, 1, "eq8comp", "Equalization 8.0 GT/s Complete", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 2, 2, "eq8p1comp", "Equalization 8.0 GT/s Phase 1", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsuccessful", "successful" } } }, + { 3, 3, "eq8p2comp", "Equalization 8.0 GT/s Phase 2", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsuccessful", "successful" } } }, + { 4, 4, "eq8p3comp", "Equalization 8.0 GT/s Phase 3", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsuccessful", "successful" } } }, + { 5, 5, "linkeq8req", "Link Equalization Request 8.0 GT/s", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not requested", "requested" } } }, + { 6, 6, "retimedet", "Retimer Presence Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 7, 7, "retime2det", "Two Retimers Presence Detected", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 8, 9, "crosslink", "Crosslink Resolution", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "upstream port", + "downstream port", "incomplete" } } }, + { 12, 14, "dscomppres", "Downstream Component Presence", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "link down - undetermined", + "link down - not present", "link down - present", NULL, + "link up - present", "link up - present and DRS" } } }, + { 15, 15, "drsrx", "DRS Message Received", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_slotcap2[] = { + { 0, 0, "ibpddis", "In-Band PD Disable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_slotctl2[] = { + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie_slotsts2[] = { + { -1, -1, NULL } +}; + + +static pcieadm_cfgspace_print_t pcieadm_cap_pcie_v1[] = { + { PCIE_PCIECAP, 2, "cap", "Capability Register", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_cap }, + { PCIE_DEVCAP, 4, "devcap", "Device Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devcap }, + { PCIE_DEVSTS, 2, "devsts", "Device Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devsts }, + { PCIE_LINKCAP, 4, "linkcap", "Link Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkcap }, + { PCIE_LINKCTL, 2, "linkctl", "Link Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkctl }, + { PCIE_LINKSTS, 2, "linksts", "Link Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linksts }, + { PCIE_SLOTCAP, 4, "slotcap", "Slot Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotcap }, + { PCIE_SLOTCTL, 2, "slotctl", "Slot Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotctl }, + { PCIE_SLOTSTS, 2, "slotsts", "Slot Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotsts }, + { PCIE_ROOTCTL, 2, "rootctl", "Root control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootctl }, + { PCIE_ROOTCAP, 2, "rootcap", "Root Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootcap }, + { PCIE_ROOTSTS, 4, "rootsts", "Root Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootsts }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_pcie_v2[] = { + { PCIE_PCIECAP, 2, "cap", "Capability Register", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_cap }, + { PCIE_DEVCAP, 4, "devcap", "Device Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devcap }, + { PCIE_DEVCTL, 2, "devctl", "Device Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devctl }, + { PCIE_DEVSTS, 2, "devsts", "Device Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devsts }, + { PCIE_LINKCAP, 4, "linkcap", "Link Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkcap }, + { PCIE_LINKCTL, 2, "linkctl", "Link Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkctl }, + { PCIE_LINKSTS, 2, "linksts", "Link Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linksts }, + { PCIE_SLOTCAP, 4, "slotcap", "Slot Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotcap }, + { PCIE_SLOTCTL, 2, "slotctl", "Slot Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotctl }, + { PCIE_SLOTSTS, 2, "slotsts", "Slot Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotsts }, + { PCIE_ROOTCTL, 2, "rootctl", "Root Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootctl }, + { PCIE_ROOTCAP, 2, "rootcap", "Root Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootcap }, + { PCIE_ROOTSTS, 4, "rootsts", "Root Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootsts }, + { PCIE_DEVCAP2, 4, "devcap2", "Device Capabilities 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devcap2 }, + { PCIE_DEVCTL2, 2, "devctl2", "Device Control 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devctl2 }, + { PCIE_DEVSTS2, 2, "devsts2", "Device Status 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devsts2 }, + { PCIE_LINKCAP2, 4, "linkcap2", "Link Capabilities 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkcap2 }, + { PCIE_LINKCTL2, 2, "linkctl2", "Link Control 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkctl2 }, + { PCIE_LINKSTS2, 2, "linksts2", "Link Status 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linksts2 }, + { PCIE_SLOTCAP2, 4, "slotcap2", "Slot Capabilities 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotcap2 }, + { PCIE_SLOTCTL2, 2, "slotctl2", "Slot Control 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotctl2 }, + { PCIE_SLOTSTS2, 2, "slotsts2", "Slot Status 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotsts2 }, + { -1, -1, NULL } +}; + +/* + * PCIe Extended Capability Header + */ +static pcieadm_regdef_t pcieadm_regdef_pcie_caphdr[] = { + { 0, 15, "capid", "Capability ID", PRDV_HEX }, + { 16, 19, "version", "Capability Version", PRDV_HEX }, + { 20, 32, "offset", "Next Capability Offset", PRDV_HEX }, + { -1, -1, NULL } +}; + +/* + * VPD Capability + */ +static pcieadm_regdef_t pcieadm_regdef_vpd_addr[] = { + { 0, 14, "addr", "VPD Address", PRDV_HEX }, + { 15, 15, "flag", "Flag", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_vpd[] = { + { 0x2, 2, "addr", "VPD Address Register", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_vpd_addr }, + { 0x4, 4, "data", "VPD Data", pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +/* + * SATA Capability per AHCI 1.3.1 + */ +static pcieadm_regdef_t pcieadm_regdef_sata_cr0[] = { + { 0, 3, "minrev", "Minor Revision", PRDV_HEX }, + { 4, 7, "majrev", "Major Revision", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_sata_cr1[] = { + { 0, 3, "bar", "BAR Location", PRDV_HEX, + .prd_val = { .prdv_hex = { 2 } } }, + { 4, 23, "offset", "BAR Offset", PRDV_HEX, + .prd_val = { .prdv_hex = { 2 } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_sata[] = { + { 0x2, 2, "satacr0", "SATA Capability Register 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sata_cr0 }, + { 0x4, 4, "satacr1", "SATA Capability Register 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sata_cr1 }, + { -1, -1, NULL } +}; + +/* + * Debug Capability per EHCI + */ +static pcieadm_regdef_t pcieadm_regdef_debug[] = { + { 0, 12, "offset", "BAR Offset", PRDV_HEX }, + { 13, 15, "bar", "BAR Location ", PRDV_STRVAL, + .prd_val = { .prdv_strval = { NULL, "BAR 0", "BAR 1", "BAR 2", + "BAR 3", "BAR 4", "BAR 5" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_debug[] = { + { 0x2, 2, "port", "Debug Port", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_debug }, + { -1, -1, NULL } +}; + +/* + * AER Capability + */ +static pcieadm_regdef_t pcieadm_regdef_aer_ue[] = { + { 4, 4, "dlp", "Data Link Protocol Error", PRDV_HEX }, + { 5, 5, "sde", "Surprise Down Error", PRDV_HEX }, + { 12, 12, "ptlp", "Poisoned TLP Received", PRDV_HEX }, + { 13, 13, "fcp", "Flow Control Protocol Error", PRDV_HEX }, + { 14, 14, "cto", "Completion Timeout", PRDV_HEX }, + { 15, 15, "cab", "Completion Abort", PRDV_HEX }, + { 16, 16, "unco", "Unexpected Completion", PRDV_HEX }, + { 17, 17, "rxov", "Receiver Overflow", PRDV_HEX }, + { 18, 18, "maltlp", "Malformed TLP", PRDV_HEX }, + { 19, 19, "ecrc", "ECRC Error", PRDV_HEX }, + { 20, 20, "usuprx", "Unsupported Request Error", PRDV_HEX }, + { 21, 21, "acs", "ACS Violation", PRDV_HEX }, + { 22, 22, "ueint", "Uncorrectable Internal Error", PRDV_HEX }, + { 23, 23, "mcbtlp", "MC Blocked TLP", PRDV_HEX }, + { 24, 24, "atoomeb", "AtomicOp Egress Blocked", PRDV_HEX }, + { 25, 25, "tlppb", "TLP Prefix Blocked Error", PRDV_HEX }, + { 26, 26, "ptlpeb", "Poisoned TLP Egress Blocked", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_aer_ce[] = { + { 0, 0, "rxerr", "Receiver Error", PRDV_HEX }, + { 6, 6, "badtlp", "Bad TLP", PRDV_HEX }, + { 7, 7, "baddllp", "Bad DLLP", PRDV_HEX }, + { 8, 8, "replayro", "REPLAY_NUM Rollover", PRDV_HEX }, + { 12, 12, "rtto", "Replay timer Timeout", PRDV_HEX }, + { 13, 13, "advnfe", "Advisory Non-Fatal Error", PRDV_HEX }, + { 14, 14, "ceint", "Correctable Internal Error", PRDV_HEX }, + { 15, 15, "headlov", "Header Log Overflow", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_aer_ctrl[] = { + { 0, 4, "feptr", "First Error Pointer", PRDV_HEX }, + { 5, 5, "ecgencap", "ECRC Generation Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 6, 6, "ecgenen", "ECRC Generation Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 7, 7, "ecchkcap", "ECRC Check Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 8, "ecchken", "ECRC Check Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_aer_rootcom[] = { + { 0, 0, "corerr", "Correctable Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "nferr", "Non-Fatal Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "faterr", "Fatal Error Reporting", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_aer_rootsts[] = { + { 0, 0, "errcor", "ERR_COR Received", PRDV_HEX }, + { 1, 1, "merrcor", "Multiple ERR_COR Received", PRDV_HEX }, + { 2, 2, "errfnf", "ERR_FATAL/NONFATAL Received", PRDV_HEX }, + { 3, 3, "merrfnf", "Multiple ERR_FATAL/NONFATAL Received", PRDV_HEX }, + { 4, 4, "fuefat", "First Uncorrectable Fatal", PRDV_HEX }, + { 5, 5, "nferrrx", "Non-Fatal Error Messages Received", PRDV_HEX }, + { 6, 6, "faterrx", "Fatal Error Messages Received", PRDV_HEX }, + { 7, 8, "errcorsc", "ERR_COR Subclass", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "ECS Legacy", "ECS SIG_SFW", + "ECS SIG_OS", "ECS Extended" } } }, + { 27, 31, "inum", "Advanced Error Interrupt Message", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_aer_esi[] = { + { 0, 15, "errcorr", "ERR_COR Source", PRDV_HEX }, + { 16, 31, "errfnf", "ERR_FATAL/NONFATAL Source", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_aer_secue[] = { + { 0, 0, "taosc", "Target-Abort on Split Completion", PRDV_HEX }, + { 1, 1, "maosc", "Master-Abort on Split Completion", PRDV_HEX }, + { 2, 2, "rxta", "Received Target-Abort", PRDV_HEX }, + { 3, 3, "rxma", "Received Master-Abort", PRDV_HEX }, + { 5, 5, "unsce", "Unexpected Split Completion Error", PRDV_HEX }, + { 6, 6, "uescmd", "Uncorrectable Split Completion Message Data Error", + PRDV_HEX }, + { 7, 7, "uede", "Uncorrectable Data Error", PRDV_HEX }, + { 8, 8, "ueattre", "Uncorrectable Attribute Error", PRDV_HEX }, + { 9, 9, "ueaddre", "Uncorrectable Address Error", PRDV_HEX }, + { 10, 10, "dtdte", "Delayed Transaction Discard Timer Expired", + PRDV_HEX }, + { 11, 11, "perr", "PERR# Assertion", PRDV_HEX }, + { 12, 12, "serr", "SERR# Assertion", PRDV_HEX }, + { 13, 13, "internal", "Internal Bridge Error", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_aer_secctl[] = { + { 0, 4, "feptr", "Secondary Uncorrectable First Error Pointer", + PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_aer_v1[] = { + { PCIE_AER_CAP, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { PCIE_AER_UCE_STS, 4, "uestatus", "Uncorrectable Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_UCE_MASK, 4, "uemask", "Uncorrectable Error Mask", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_UCE_SERV, 4, "ueserv", "Uncorrectable Error Severity", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_CE_STS, 4, "cestatus", "Correctable Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce }, + { PCIE_AER_CE_MASK, 4, "cemask", "Correctable Error Mask", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce }, + { PCIE_AER_CTL, 4, "ctrl", "Advanced Error Capabilities and Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ctrl }, + { PCIE_AER_HDR_LOG + 4, 4, "hl0", "Header Log 0", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 8, 4, "hl1", "Header Log 1", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 12, 4, "hl2", "Header Log 2", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 12, 4, "hl3", "Header Log 3", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_CTL, 4, "rootcmd", "Root Error Command", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootcom }, + { PCIE_AER_RE_STS, 4, "rootsts", "Root Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootsts }, + { PCIE_AER_CE_SRC_ID, 4, "esi", "Error Source Identification", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_esi }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_aer_v2[] = { + { PCIE_AER_CAP, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { PCIE_AER_UCE_STS, 4, "uestatus", "Uncorrectable Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_UCE_MASK, 4, "uemask", "Uncorrectable Error Mask", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_UCE_SERV, 4, "ueserv", "Uncorrectable Error Severity", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_CE_STS, 4, "cestatus", "Correctable Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce }, + { PCIE_AER_CE_MASK, 4, "cemask", "Correctable Error Mask", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce }, + { PCIE_AER_CTL, 4, "ctrl", "Advanced Error Capabilities and Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ctrl }, + { PCIE_AER_HDR_LOG + 4, 4, "hl0", "Header Log 0", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 8, 4, "hl1", "Header Log 1", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 12, 4, "hl2", "Header Log 2", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 12, 4, "hl3", "Header Log 3", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_CTL, 4, "rootcmd", "Root Error Command", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootcom }, + { PCIE_AER_RE_STS, 4, "rootsts", "Root Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootsts }, + { PCIE_AER_TLP_PRE_LOG, 4, "tlplog0", "TLP Prefix Log 0", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_TLP_PRE_LOG + 4, 4, "tlplog1", "TLP Prefix Log 1", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_TLP_PRE_LOG + 8, 4, "tlplog2", "TLP Prefix Log 2", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_TLP_PRE_LOG + 12, 4, "tlplog3", "TLP Prefix Log 3", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_aer_bridge[] = { + { PCIE_AER_CAP, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { PCIE_AER_UCE_STS, 4, "uestatus", "Uncorrectable Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_UCE_MASK, 4, "uemask", "Uncorrectable Error Mask", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_UCE_SERV, 4, "ueserv", "Uncorrectable Error Severity", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue }, + { PCIE_AER_CE_STS, 4, "cestatus", "Correctable Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce }, + { PCIE_AER_CE_MASK, 4, "cemask", "Correctable Error Mask", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce }, + { PCIE_AER_CTL, 4, "ctrl", "Advanced Error Capabilities and Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ctrl }, + { PCIE_AER_HDR_LOG + 4, 4, "hl0", "Header Log 0", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 8, 4, "hl1", "Header Log 1", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 12, 4, "hl2", "Header Log 2", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_HDR_LOG + 12, 4, "hl3", "Header Log 3", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_CTL, 4, "rootcmd", "Root Error Command", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootcom }, + { PCIE_AER_RE_STS, 4, "rootsts", "Root Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootsts }, + { PCIE_AER_CE_SRC_ID, 4, "esi", "Error Source Identification", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_esi }, + { PCIE_AER_SUCE_STS, 4, "secuests", + "Secondary Uncorrectable Error Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secue }, + { PCIE_AER_SUCE_MASK, 4, "secuests", + "Secondary Uncorrectable Error Mask", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secue }, + { PCIE_AER_SUCE_SERV, 4, "secuests", + "Secondary Uncorrectable Error Severity", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secue }, + { PCIE_AER_SCTL, 4, "secctrl", + "Secondary Error Capabilityes and Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secctl }, + { PCIE_AER_SHDR_LOG, 4, "shl0", "Secondary Header Log 0", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_SHDR_LOG + 4, 4, "shl1", "Secondary Header Log 1", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_SHDR_LOG + 8, 4, "shl1", "Secondary Header Log 2", + pcieadm_cfgspace_print_hex }, + { PCIE_AER_SHDR_LOG + 12, 4, "shl1", "Secondary Header Log 3", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +/* + * Secondary PCI Express Extended Capability + */ +static pcieadm_regdef_t pcieadm_regdef_pcie2_linkctl3[] = { + { 0, 0, "peq", "Perform Equalization", PRDV_HEX }, + { 1, 1, "leqrie", "Link Equalization Request Interrupt Enable", + PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 9, 15, "elskpos", "Enable Lower SKP OS Generation Vector", + PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s", + "16.0 GT/s", "32.0 GT/s" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcie2_linkeq[] = { + { 0, 3, "dstxpre", "Downstream Port 8.0 GT/s Transmitter Preset", + PRDV_HEX }, + { 4, 6, "dstxhint", "Downstream Port 8.0 GT/s Receiver Hint", + PRDV_HEX }, + { 8, 11, "ustxpre", "Upstream Port 8.0 GT/s Transmitter Preset", + PRDV_HEX }, + { 12, 14, "ustxhint", "Upstream Port 8.0 GT/s Receiver Hint", + PRDV_HEX }, + { -1, -1, NULL } +}; + +static void +pcieadm_cfgspace_print_laneq(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + if (walkp->pcw_nlanes == 0) { + warnx("failed to capture lane count, but somehow have " + "secondary PCIe cap"); + return; + } + + for (uint_t i = 0; i < walkp->pcw_nlanes; i++) { + char eqshort[32], eqhuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(eqshort, sizeof (eqshort), "lane%u", i); + (void) snprintf(eqhuman, sizeof (eqhuman), "Lane %u EQ Control", + i); + p.pcp_off = print->pcp_off + i * 2; + p.pcp_len = 2; + p.pcp_short = eqshort; + p.pcp_human = eqhuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_pcie2_linkeq; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +static pcieadm_cfgspace_print_t pcieadm_cap_pcie2[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "linkctl3", "Link Control 3", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie2_linkctl3 }, + { 0x8, 4, "laneerr", "Lane Error Status", pcieadm_cfgspace_print_hex }, + { 0xc, 2, "eqctl", "Lane Equalization Control", + pcieadm_cfgspace_print_laneq }, + { -1, -1, NULL } +}; + +/* + * Access Control Services + */ +static pcieadm_regdef_t pcieadm_regdef_acs_cap[] = { + { 0, 0, "srcvd", "ACS Source Validation", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "tranblk", "ACS Transaction Blocking", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 2, 2, "p2prr", "ACS P2P Request Redirect", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 3, 3, "p2pcr", "ACS P2P Completion Redirect", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 4, 4, "upfwd", "ACS Upstream Forwarding", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 5, 5, "p2pegctl", "ACS P2P Egress Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 6, 6, "dtp2p", "ACS Direct Translated P2P", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 7, 7, "enhcap", "ACS Enhanced Capability", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 15, "ecvsz", "Egress Control Vector Size", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_acs_ctl[] = { + { 0, 0, "srcvd", "ACS Source Validation", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "tranblk", "ACS Transaction Blocking", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "p2prr", "ACS P2P Request Redirect", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "p2pcr", "ACS P2P Completion Redirect", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "upfwd", "ACS Upstream Forwarding", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 5, 5, "p2pegctl", "ACS P2P Egress Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 6, 6, "dtp2p", "ACS Direct Translated P2P", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 7, 7, "iorb", "ACS I/O Request Blocking", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 8, 9, "dspmta", "ACS DSP Memory Target Access Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Direct Request access", + "Request blocking", "Request redirect" } } }, + { 10, 11, "uspmta", "ACS USP Memory Target Access Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Direct Request access", + "Request blocking", "Request redirect" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_acs[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "cap", "ACS Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_acs_cap }, + { 0x6, 2, "ctl", "ACS Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_acs_ctl }, + { 0x8, 4, "ecv", "Egress Control Vector", pcieadm_cfgspace_print_ecv }, + { -1, -1, NULL } +}; + +/* + * L1 PM Substates + */ +static pcieadm_regdef_t pcieadm_regdef_l1pm_cap[] = { + { 0, 0, "pcil1.2", "PCI-PM L1.2", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "pcil1.1", "PCI-PM L1.1", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 2, 2, "aspml1.2", "ASPM L1.2", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 3, 3, "aspml1.1", "ASPM L1.1", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 4, 4, "l1pmsub", "L1 PM Substates", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 5, 5, "linkact", "Link Activation", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 15, "pcmrt", "Port Common_Mode_Restore_Time", PRDV_HEX }, + { 16, 17, "poscale", "Port T_POWER_ON Scale", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "2 us", "10 us", "100 us" } } }, + { 19, 23, "portpo", "Port T_POWER_ON Value", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_l1pm_ctl1[] = { + { 0, 0, "pcil1.2", "PCI-PM L1.2", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "pcil1.1", "PCI-PM L1.1", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "aspml1.2", "ASPM L1.2", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "aspml1.1", "ASPM L1.1", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "laie", "Link Activation Interrupt Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 5, 5, "lactl", "Link Activation Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 8, 15, "cmrt", "Common_Mode_Restore_Time", PRDV_HEX }, + { 16, 25, "ltrl1.2", "LTR L1.2 Threshold Value", PRDV_HEX }, + { 29, 31, "ltrl1.2s", "LTR L1.2 Threshold Scale", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1 ns", "32 ns", "1024 ns", + "32,768 ns", "1,048,576 ns", "33,554,432 ns" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_l1pm_ctl2[] = { + { 0, 1, "poscale", "T_POWER_ON Scale", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "2 us", "10 us", "100 us" } } }, + { 3, 7, "portpo", "T_POWER_ON Value", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_l1pm_sts[] = { + { 0, 0, "la", "Link Activation", PRDV_HEX }, + { -1, -1, NULL } +}; + + +static pcieadm_cfgspace_print_t pcieadm_cap_l1pm_v1[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "caps", "L1 PM Substates Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_cap }, + { 0x8, 4, "ctl1", "L1 PM Substates Control 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl1 }, + { 0xc, 4, "ctl1", "L1 PM Substates Control 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl2 }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_l1pm_v2[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "caps", "L1 PM Substates Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_cap }, + { 0x8, 4, "ctl1", "L1 PM Substates Control 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl1 }, + { 0xc, 4, "ctl1", "L1 PM Substates Control 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl2 }, + { 0x10, 4, "sts", "L1 PM Substates Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_sts }, + { -1, -1, NULL } +}; + +/* + * Latency Tolerance Reporting (LTR) + */ +static pcieadm_regdef_t pcieadm_regdef_ltr[] = { + { 0, 9, "latval", "Latency Value", PRDV_HEX }, + { 10, 12, "latscale", "Latency Scale", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1 ns", "32 ns", "1024 ns", + "32,768 ns", "1,048,576 ns", "33,554,432 ns" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_ltr[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "snoop", "Max Snoop Latency", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ltr }, + { 0x6, 2, "snoop", "Max No-Snoop Latency", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ltr }, + { -1, -1, NULL } +}; + +/* + * Alternative Routing ID + */ +static pcieadm_regdef_t pcieadm_regdef_ari_cap[] = { + { 0, 0, "mfvcfg", "MFVC Function Groups", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "acsfg", "ACS Function Groups", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 15, "nfunc", "Next Function Number", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ari_ctl[] = { + { 0, 0, "mfvcfg", "MFVC Function Groups", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "acsfg", "ACS Function Groups", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 6, "fgrp", "Function Group", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_ari[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "cap", "ARI Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ari_cap }, + { 0x6, 2, "ctl", "ARI Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ari_ctl }, + { -1, -1, NULL } +}; + +/* + * PASID + */ +static pcieadm_regdef_t pcieadm_regdef_pasid_cap[] = { + { 1, 1, "exec", "Execution Permission", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 2, 2, "priv", "Privileged Mode", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 12, "width", "Max PASID Width", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pasid_ctl[] = { + { 0, 0, "pasid", "PASID", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "exec", "Execution Permission", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "priv", "Privileged Mode", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + + +static pcieadm_cfgspace_print_t pcieadm_cap_pasid[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "cap", "PASID Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pasid_cap }, + { 0x6, 2, "ctl", "PASID Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pasid_ctl }, + { -1, -1, NULL } +}; + +/* + * "Advanced Features" + */ +static pcieadm_regdef_t pcieadm_regdef_af_cap[] = { + { 0, 0, "tp", "Transactions Pending", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "flr", "Function Level Reset", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_af_ctl[] = { + { 0, 0, "flr", "Function Level Reset", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_af_sts[] = { + { 0, 0, "tp", "Transactions Pending", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "none pending", "pending" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_af[] = { + { 0x2, 2, "cap", "AF Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_af_cap }, + { 0x4, 1, "ctl", "AF Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_af_ctl }, + { 0x5, 1, "sts", "AF Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_af_sts }, + { -1, -1, NULL } +}; + +/* + * Multicast + */ +static pcieadm_regdef_t pcieadm_regdef_mcast_cap[] = { + { 0, 5, "maxgrp", "Max Group", PRDV_HEX, + .prd_val = { .prdv_hex = { 0, 1 } } }, + { 8, 13, "winsize", "Window Size (raw)", PRDV_HEX }, + { 15, 15, "ecrc", "ECRC Regeneration", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_mcast_ctl[] = { + { 0, 5, "numgrp", "Number of Groups", PRDV_HEX, + .prd_val = { .prdv_hex = { 0, 1 } } }, + { 15, 15, "enable", "Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_mcast_base[] = { + { 0, 5, "index", "Multicast Index Position", PRDV_HEX }, + { 12, 63, "addr", "Base Address", PRDV_HEX, + .prd_val = { .prdv_hex = { 12 } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_mcast_overlay[] = { + { 0, 5, "size", "Overlay Size (raw)", PRDV_HEX }, + { 6, 63, "addr", "Overlay Base Address", PRDV_HEX, + .prd_val = { .prdv_hex = { 6 } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_mcast[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "cap", "Multicast Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_cap }, + { 0x6, 2, "ctl", "Multicast Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_ctl }, + { 0x8, 8, "base", "Multicast Base Address", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_base }, + { 0x10, 8, "rx", "Multicast Receive", pcieadm_cfgspace_print_hex }, + { 0x18, 8, "block", "Multicast Block All", pcieadm_cfgspace_print_hex }, + { 0x20, 8, "blockun", "Multicast Block Untranslated", + pcieadm_cfgspace_print_hex }, + { 0x28, 8, "overlay", "Multicast Overlay BAR", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_overlay }, + { -1, -1, NULL } +}; + +/* + * Various vendor extensions + */ +static pcieadm_regdef_t pcieadm_regdef_vsec[] = { + { 0, 15, "id", "ID", PRDV_HEX }, + { 16, 19, "rev", "Revision", PRDV_HEX }, + { 20, 31, "len", "Length", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_vs[] = { + { 0x2, 2, "length", "Length", pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_vsec[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "header", "Vendor-Specific Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_vsec }, + { -1, -1, NULL } +}; + +/* + * Data Link Feature + */ +static pcieadm_regdef_t pcieadm_regdef_dlf_cap[] = { + { 0, 0, "lsfc", "Local Scaled Flow Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 31, 31, "dlex", "Data Link Exchange", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_dlf_sts[] = { + { 0, 0, "rsfc", "Remote Scaled Flow Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 31, 31, "valid", "Remote Data Link Feature Valid", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "invalid", "valid" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_dlf[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "cap", "Data Link Feature Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dlf_cap }, + { 0x8, 4, "sts", "Data Link Feature Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dlf_sts }, + { -1, -1, NULL } +}; + +/* + * 16.0 GT/s cap + */ +static pcieadm_regdef_t pcieadm_regdef_16g_cap[] = { + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_16g_ctl[] = { + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_16g_sts[] = { + { 0, 0, "eqcomp", "Equalization 16.0 GT/s Complete", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "incomplete", "complete" } } }, + { 1, 1, "eqp1", "Equalization 16.0 GT/s Phase 1", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "incomplete", "complete" } } }, + { 2, 2, "eqp2", "Equalization 16.0 GT/s Phase 2", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "incomplete", "complete" } } }, + { 3, 3, "eqp3", "Equalization 16.0 GT/s Phase 3", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "incomplete", "complete" } } }, + { 4, 4, "req", "Link Equalization Request 16.0 GT/s", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_16g_eq[] = { + { 0, 3, "dstxpre", "Downstream Port 16.0 GT/s Transmitter Preset", + PRDV_HEX }, + { 4, 7, "ustxpre", "Upstream Port 16.0 GT/s Transmitter Preset", + PRDV_HEX }, + { -1, -1, NULL } +}; + +static void +pcieadm_cfgspace_print_16geq(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + if (walkp->pcw_nlanes == 0) { + warnx("failed to capture lane count, but somehow have " + "secondary PCIe cap"); + return; + } + + for (uint_t i = 0; i < walkp->pcw_nlanes; i++) { + char eqshort[32], eqhuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(eqshort, sizeof (eqshort), "lane%u", i); + (void) snprintf(eqhuman, sizeof (eqhuman), "Lane %u EQ Control", + i); + p.pcp_off = print->pcp_off + i * 1; + p.pcp_len = 1; + p.pcp_short = eqshort; + p.pcp_human = eqhuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_16g_eq; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +static pcieadm_cfgspace_print_t pcieadm_cap_16g[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "cap", "16.0 GT/s Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_16g_cap }, + { 0x8, 4, "ctl", "16.0 GT/s Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_16g_ctl }, + { 0xc, 4, "sts", "16.0 GT/s Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_16g_sts }, + { 0x10, 4, "ldpmis", "16.0 GT/s Local Data Parity Mismatch", + pcieadm_cfgspace_print_hex }, + { 0x14, 4, "frpmis", "16.0 GT/s First Retimer Data Parity Mismatch", + pcieadm_cfgspace_print_hex }, + { 0x18, 4, "srpmis", "16.0 GT/s Second Retimer Data Parity Mismatch", + pcieadm_cfgspace_print_hex }, + { 0x1c, 4, "rsvd", "16.0 GT/s Second Retimer Data Parity Mismatch", + pcieadm_cfgspace_print_hex }, + { 0x20, 1, "eqctl", "16.0 GT/s EQ Control", + pcieadm_cfgspace_print_16geq }, + { -1, -1, NULL } +}; + +/* + * Receiver Margining + */ +static pcieadm_regdef_t pcieadm_regdef_margin_cap[] = { + { 0, 0, "sw", "Margining uses Driver Software", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_margin_sts[] = { + { 0, 0, "ready", "Margining Ready", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 1, 1, "sw", "Margining Software Ready", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_margin_lane[] = { + { 0, 2, "rxno", "Receiver Number", PRDV_HEX }, + { 3, 5, "type", "Margin Type", PRDV_HEX }, + { 6, 6, "model", "Usage Model", PRDV_HEX }, + { 8, 15, "payload", "Margin Payload", PRDV_HEX }, + { -1, -1, NULL } +}; + +static void +pcieadm_cfgspace_print_margin(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + if (walkp->pcw_nlanes == 0) { + warnx("failed to capture lane count, but somehow have " + "lane margining capability"); + return; + } + + for (uint_t i = 0; i < walkp->pcw_nlanes; i++) { + char mshort[32], mhuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(mshort, sizeof (mshort), "lane%uctl", i); + (void) snprintf(mhuman, sizeof (mhuman), "Lane %u Margining " + "Control", i); + p.pcp_off = print->pcp_off + i * 4; + p.pcp_len = 2; + p.pcp_short = mshort; + p.pcp_human = mhuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_margin_lane; + + p.pcp_print(walkp, &p, p.pcp_arg); + + (void) snprintf(mshort, sizeof (mshort), "lane%usts", i); + (void) snprintf(mhuman, sizeof (mhuman), "Lane %u Margining " + "Status", i); + p.pcp_off = print->pcp_off + 2 + i * 4; + p.pcp_len = 2; + p.pcp_short = mshort; + p.pcp_human = mhuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_margin_lane; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +static pcieadm_cfgspace_print_t pcieadm_cap_margin[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "cap", "Margining Port Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_margin_cap }, + { 0x6, 2, "sts", "Margining Port Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_margin_sts }, + { 0x8, 4, "lane", "Margining Lane", pcieadm_cfgspace_print_margin }, + { -1, -1, NULL } +}; + +/* + * Serial Number Capability + */ +static void +pcieadm_cfgspace_print_sn(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + char sn[64]; + uint16_t off = walkp->pcw_capoff + print->pcp_off; + + (void) snprintf(sn, sizeof (sn), + "%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x", + walkp->pcw_data->pcb_u8[off + 7], walkp->pcw_data->pcb_u8[off + 6], + walkp->pcw_data->pcb_u8[off + 5], walkp->pcw_data->pcb_u8[off + 4], + walkp->pcw_data->pcb_u8[off + 3], walkp->pcw_data->pcb_u8[off + 2], + walkp->pcw_data->pcb_u8[off + 1], walkp->pcw_data->pcb_u8[off]); + + pcieadm_cfgspace_puts(walkp, print, sn); +} + +static pcieadm_cfgspace_print_t pcieadm_cap_sn[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 8, "sn", "Serial Number", pcieadm_cfgspace_print_sn }, + { -1, -1, NULL } +}; + +/* + * TLP Processing Hints (TPH) + */ +static pcieadm_regdef_t pcieadm_regdef_tph_cap[] = { + { 0, 0, "nost", "No ST Mode", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "ivec", "Interrupt Vector Mode", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 2, 2, "dev", "Device Specific Mode", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 8, "exttph", "Extended TPH Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 9, 10, "loc", "ST Table Location", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Not Present", + "In Capability Structure", "MSI-X" } } }, + { 16, 26, "size", "ST Table Size", PRDV_HEX, { .prdv_hex = { 0, 1 } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_tph_ctl[] = { + { 0, 2, "mode", "ST Mode Select", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "No ST", "Interrupt Vector", + "Device Specific" } } }, + { 8, 9, "en", "TPH Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Not Permitted", "TPH", NULL, + "TPH and Extended TPH" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_tph_st[] = { + { 0, 7, "low", "ST Lower", PRDV_HEX }, + { 8, 15, "up", "ST Upper", PRDV_HEX }, + { -1, -1, NULL } +}; + +/* + * The TPH ST table is only conditionally present in the capability. So we need + * to read the TPH capability register and then check if the table location and + * size are set here. + */ +static void +pcieadm_cfgspace_print_tphst(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint_t nents; + uint32_t tphcap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4]; + + if (BITX(tphcap, 10, 9) != 1) { + return; + } + + nents = BITX(tphcap, 26, 16) + 1; + for (uint_t i = 0; i < nents; i++) { + char tshort[32], thuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(tshort, sizeof (tshort), "st%u", i); + (void) snprintf(thuman, sizeof (thuman), "ST Table %u", + i); + p.pcp_off = print->pcp_off + i * 2; + p.pcp_len = 2; + p.pcp_short = tshort; + p.pcp_human = thuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_tph_st; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +static pcieadm_cfgspace_print_t pcieadm_cap_tph[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "cap", "TPH Requester Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_tph_cap }, + { 0x8, 4, "ctl", "TPH Requester Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_tph_ctl }, + { 0xc, 2, "table", "ST Table", pcieadm_cfgspace_print_tphst }, + { -1, -1, NULL } +}; + +/* + * SR-IOV + */ +static pcieadm_regdef_t pcieadm_regdef_sriov_cap[] = { + { 0, 0, "migration", "Migration", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "ari", "ARI Capable Hierarchy Preserved", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unpreserved", "preserved" } } }, + { 2, 2, "vf10b", "VF 10-bit Tag Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unpreserved", "preserved" } } }, + { 21, 31, "inum", "VF Migration Interrupt Message Number", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_sriov_ctl[] = { + { 0, 0, "vf", "VF", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "vfm", "VF Migration", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "vfmi", "VF Migration Interrupt", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "ari", "ARI Capable Hierarchy", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "vf10b", "VF 10-bit Tag Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_sriov_sts[] = { + { 0, 0, "vfm", "VF Migration", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "none", "requested" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_sriov_pgsup[] = { + { 0, 31, "pgsz", "Supported Page Sizes", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "4 KB", "8 KB", "16 KB", "32 KB", + "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB", + "8 MB", "16 MB", "32 MB", "64 MB", "128 MB", "256 MB", "512 MB", + "1 GB", "2 GB", "4 GB", "8 GB", "16 GB", "32 GB", "64 GB", + "128 GB", "256 GB", "512 GB", "1 TB", "2 TB", "4 TB", "8 TB" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_sriov_pgen[] = { + { 0, 31, "pgsz", "System Page Sizes", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "4 KB", "8 KB", "16 KB", "32 KB", + "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB", + "8 MB", "16 MB", "32 MB", "64 MB", "128 MB", "256 MB", "512 MB", + "1 GB", "2 GB", "4 GB", "8 GB", "16 GB", "32 GB", "64 GB", + "128 GB", "256 GB", "512 GB", "1 TB", "2 TB", "4 TB", "8 TB" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_sriov_mig[] = { + { 0, 2, "bir", "VF Migration State BIR", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "BAR 0", "BAR 1", "BAR 2", "BAR 3", + "BAR 4", "BAR 5" } } }, + { 3, 31, "offset", "VF Migration State Offset", PRDV_HEX, + .prd_val = { .prdv_hex = { 3 } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_sriov[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "cap", "SR-IOV Capabilities", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_cap }, + { 0x8, 2, "ctl", "SR-IOV Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_ctl }, + { 0xa, 2, "sts", "SR-IOV Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_sts }, + { 0xc, 2, "initvfs", "Initial VFs", pcieadm_cfgspace_print_hex }, + { 0xe, 2, "totvfs", "Total VFs", pcieadm_cfgspace_print_hex }, + { 0x10, 2, "numvfs", "Number VFs", pcieadm_cfgspace_print_hex }, + { 0x12, 1, "dep", "Function Dependency Link", + pcieadm_cfgspace_print_hex }, + { 0x14, 2, "offset", "First VF Offset", pcieadm_cfgspace_print_hex }, + { 0x16, 2, "stride", "VF Stride", pcieadm_cfgspace_print_hex }, + { 0x1a, 2, "devid", "VF Device ID", pcieadm_cfgspace_print_hex }, + { 0x1c, 4, "pgsz", "Supported Page Sizes", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_pgsup }, + { 0x20, 4, "pgsz", "System Page Sizes", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_pgen }, + { 0x24, 24, "vfbar", "Virtual Base Address Register", + pcieadm_cfgspace_print_bars }, + { 0x3c, 4, "migration", "VF Migration State Array", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_mig }, + { -1, -1, NULL } +}; + +/* + * PCI-X + */ +static pcieadm_regdef_t pcieadm_regdef_pcix_dev_ctl[] = { + { 0, 0, "dper", "Data Parity Error Recovery", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "ro", "Relaxed Ordering", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 3, "maxread", "Maximum Memory Read Byte Count", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "512 bytes", "1024 bytes", + "2048 byes", "4096 bytes" } } }, + { 4, 6, "maxsplit", "Maximum Outstanding Split Transactions", + PRDV_STRVAL, .prd_val = { .prdv_strval = { "1", "2", "3", "4", "8", + "12", "16", "32" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcix_dev_sts[] = { + { 0, 2, "func", "Function Number", PRDV_HEX }, + { 3, 7, "dev", "Device Number", PRDV_HEX }, + { 8, 15, "bus", "Bus Number", PRDV_HEX }, + { 16, 16, "64bit", "64-bit Device", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported (32-bit)", + "supported" } } }, + { 17, 17, "133mhz", "133 MHz Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported (66 MHz)", + "supported" } } }, + { 18, 18, "spcodis", "Split Completion Discarded", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 19, 19, "unspco", "Unexpected Split Completion", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 20, 20, "complex", "Device Complexity", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "simple", "bridge" } } }, + { 21, 22, "maxread", "Designed Maximum Memory Read Byte Count", + PRDV_STRVAL, .prd_val = { .prdv_strval = { "512 bytes", + "1024 bytes", "2048 byes", "4096 bytes" } } }, + { 23, 25, "maxsplit", "Designed Maximum Outstanding Split Transactions", + PRDV_STRVAL, .prd_val = { .prdv_strval = { "1", "2", "3", "4", "8", + "12", "16", "32" } } }, + { 26, 28, "maxcread", "Designed Maximum Cumulative Read Size", + PRDV_STRVAL, .prd_val = { .prdv_strval = { "8/1KB", "16/2KB", + "32/4KB", "64/8KB", "128/16KB", "256/32KB", "512/64KB", + "1024/128KB" } } }, + { 29, 29, "rxspcoer", "Received Split Completion Error Message", + PRDV_STRVAL, .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcix_sec_sts[] = { + { 0, 0, "64bit", "64-bit Device", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported (32-bit)", + "supported" } } }, + { 1, 1, "133mhz", "133 MHz Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported (66 MHz)", + "supported" } } }, + { 2, 2, "spcodis", "Split Completion Discarded", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 3, 3, "unspco", "Unexpected Split Completion", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 4, 4, "spcoor", "Split Completion Overrun", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 5, 5, "sprde", "Split Request Delayed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 6, 8, "freq", "Secondary Clock Frequency", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "conventional", "66 MHz", "100 Mhz", + "133 MHz" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcix_bridge_sts[] = { + { 0, 2, "func", "Function Number", PRDV_HEX }, + { 3, 7, "dev", "Device Number", PRDV_HEX }, + { 8, 15, "bus", "Bus Number", PRDV_HEX }, + { 16, 16, "64bit", "64-bit Device", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported (32-bit)", + "supported" } } }, + { 17, 17, "133mhz", "133 MHz Capable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported (66 MHz)", + "supported" } } }, + { 18, 18, "spcodis", "Split Completion Discarded", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 19, 19, "unspco", "Unexpected Split Completion", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 20, 20, "spcoor", "Split Completion Overrun", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 21, 21, "sprde", "Split Request Delayed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pcix_bridge_split[] = { + { 0, 15, "cap", "Split Transaction Capacity", PRDV_HEX }, + { 16, 31, "limit", "Split Transaction Commitment Limit", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_pcix_dev[] = { + { 0x2, 2, "ctl", "PCI-X Command", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_dev_ctl }, + { 0x4, 4, "sts", "PCI-X Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_dev_sts }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_pcix_bridge[] = { + { 0x2, 2, "secsts", "PCI-X Secondary Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_sec_sts }, + { 0x4, 4, "sts", "PCI-X Bridge Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_bridge_sts }, + { 0x8, 4, "ussplit", "Upstream Split Transaction", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_bridge_split }, + { 0x8, 4, "dssplit", "Downstream Split Transaction", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_bridge_split }, + { -1, -1, NULL } +}; + +/* + * Dynamic Power Allocation + */ +static pcieadm_regdef_t pcieadm_regdef_dpa_cap[] = { + { 0, 4, "substates", "Substate Max", PRDV_HEX, + { .prdv_hex = { 0, 1 } } }, + { 8, 9, "tlu", "Transition Latency Unit", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1 ms", "10 ms", "100 ms" } } }, + { 12, 13, "pas", "Power Allocation Scale", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "10.0x", "1.0x", "0.1x", + "0.01x" } } }, + { 16, 23, "tlv0", "Transition Latency Value 0", PRDV_HEX }, + { 24, 31, "tlv0", "Transition Latency Value 1", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_dpa_sts[] = { + { 0, 4, "substate", "Substate Status", PRDV_HEX }, + { 8, 8, "ctlen", "Substate Control Enabled", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_dpa_ctl[] = { + { 0, 4, "substate", "Substate Control", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_dpa[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "cap", "DPA Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpa_cap }, + { 0x8, 4, "lat", "DPA Latency Indicator", pcieadm_cfgspace_print_hex }, + { 0xc, 2, "sts", "DPA Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpa_sts }, + { 0xe, 2, "sts", "DPA Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpa_ctl }, + { 0x10, 1, "paa", "DPA Power Allocation Array", + pcieadm_cfgspace_print_dpa_paa }, + { -1, -1, NULL } +}; + +/* + * Power Budgeting + */ +static pcieadm_regdef_t pcieadm_regdef_powbudg_data[] = { + { 0, 7, "base", "Base Power", PRDV_HEX }, + { 8, 9, "scale", "Data Scale", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1.0x", "0.1x", "0.01x", + "0.001x" } } }, + { 10, 12, "pmsub", "PM Substate", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Default", "Device Specific", + "Device Specific", "Device Specific", "Device Specific", + "Device Specific", "Device Specific", "Device Specific" } } }, + { 13, 14, "pmstate", "PM State", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "D0", "D1", "D2", "D3" } } }, + { 15, 17, "type", "Type", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "PME Aux", "Axiliary", "Idle", + "Sustained", "Sustained - EPRS", "Maximum - EPRS", NULL, + "Maximum" } } }, + { 18, 20, "rail", "Power Rail", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Power (12V)", "Power (3.3V)", + "Power (1.5V or 1.8V)", NULL, NULL, NULL, NULL, "Thermal" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_powbudg_cap[] = { + { 0, 0, "sa", "System Allocated", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { -1, -1, NULL } +}; + + +static pcieadm_cfgspace_print_t pcieadm_cap_powbudg[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 1, "sel", "Data Select", pcieadm_cfgspace_print_hex }, + { 0x8, 4, "data", "Data Regiser", pcieadm_cfgspace_print_regdef, + pcieadm_regdef_powbudg_data }, + { 0xc, 0x1, "cap", "Power Budget Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_powbudg_cap }, + { -1, -1, NULL } +}; + +/* + * Precision Time Management + */ +static pcieadm_regdef_t pcieadm_regdef_ptm_cap[] = { + { 0, 0, "req", "PTM Requester", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "resp", "PTM Responder", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 2, 2, "root", "PTM Root", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 3, 3, "eptm", "ePTM", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 15, "gran", "Local Clock Granularity", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ptm_ctl[] = { + { 0, 0, "en", "PTM Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "root", "Root Select", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 8, 15, "gran", "Effective Granularity", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_info_ptm[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "cap", "PTM Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ptm_cap }, + { 0x8, 4, "cap", "PTM Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ptm_ctl }, + { -1, -1, NULL } +}; + +/* + * Address Translation Services (ATS) + */ +static pcieadm_regdef_t pcieadm_regdef_ats_cap[] = { + { 0, 4, "invqd", "Invalidate Queue Depth", PRDV_HEX }, + { 5, 5, "pgalign", "Page Aligned Request", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not required", "required" } } }, + { 6, 6, "glbinv", "Global Invalidate", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 7, 7, "relo", "Relaxed Ordering", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ats_ctl[] = { + { 0, 4, "stu", "Smallest Translation Unit", PRDV_HEX }, + { 15, 15, "en", "Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_ats[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "cap", "ATS Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ats_cap }, + { 0x6, 2, "cap", "ATS Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ats_ctl }, + { -1, -1, NULL } +}; + +/* + * Page Request + */ +static pcieadm_regdef_t pcieadm_regdef_pgreq_ctl[] = { + { 0, 0, "en", "Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "reset", "Reset", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_pgreq_sts[] = { + { 0, 0, "rf", "Response Failure", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 1, 1, "uprgi", "Unexpected Page Request Group Index", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 8, 8, "stopped", "Stopped", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 15, 15, "prgrpreq", "PRG Response PASID", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not required", "required" } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_pgreq[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "ctl", "Page Request Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pgreq_ctl }, + { 0x6, 2, "ctl", "Page Request Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pgreq_sts }, + { 0x8, 4, "cap", "Outstanding Page Request Capacity", + pcieadm_cfgspace_print_hex }, + { 0xc, 4, "alloc", "Outstanding Page Request Allocation", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +/* + * NULL Capability + */ +static pcieadm_cfgspace_print_t pcieadm_cap_null[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { -1, -1, NULL } +}; + +/* + * Downstream Port Containment + */ +static pcieadm_regdef_t pcieadm_regdef_dpc_cap[] = { + { 0, 4, "inum", "DPC Interrupt Message Number", PRDV_HEX }, + { 5, 5, "rpext", "Root Port Extensions", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 6, 6, "ptlpeb", "Poisoned TLP Egress Blocking", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 7, 7, "swtrig", "Software Triggering", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 11, "logsz", "RP PIO Log Size", PRDV_HEX }, + { 12, 12, "errcorr", "DL_Active ERR_COR Signaling", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_dpc_ctl[] = { + { 0, 1, "trigger", "DPC Trigger", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled, fatal", + "enabled, non-fatal" } } }, + { 2, 2, "comp", "Completion Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "Completer Abort", + "Unsupported Request" } } }, + { 3, 3, "intr", "Interrupt", + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "errcor", "ERR_COR", + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 5, 5, "ptlpeb", "Poisoned TLP Egress Blocking", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 6, 6, "swtrig", "Software Trigger", PRDV_HEX }, + { 7, 7, "corerr", "DL_Active ERR_COR", + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 8, 8, "sigsfw", "SIG_SFW", + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_dpc_sts[] = { + { 0, 0, "trigger", "Trigger Status", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not triggered", "triggered" } } }, + { 1, 2, "reason", "Trigger Reason", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unmasked uncorrectable", + "ERR_NONFATAL received", "ERR_FATAL received", + "see extension" } } }, + { 3, 3, "istatus", "Interrupt Status", PRDV_HEX }, + { 4, 4, "rpbusy", "RP Busy", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "no", "yes" } } }, + { 5, 6, "extreason", "Trigger Reason Extension", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "RP PIO", "Software Trigger" } } }, + { 8, 12, "feptr", "RP PIO, First Error Pointer", PRDV_HEX }, + { 13, 13, "sigsfw", "SIG_SFW Status", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_dpc_rppio_bits[] = { + { 0, 0, "cfgur", "Configuration Request UR Completion", PRDV_HEX }, + { 1, 1, "cfgca", "Configuration Request CA Completion", PRDV_HEX }, + { 2, 2, "cfgcto", "Configuration Request Completion Timeout", + PRDV_HEX }, + { 8, 8, "iour", "I/O UR Completion", PRDV_HEX }, + { 9, 9, "ioca", "I/O CA Completion", PRDV_HEX }, + { 10, 10, "iocto", "I/O Completion Timeout", PRDV_HEX }, + { 8, 8, "memur", "Memory UR Completion", PRDV_HEX }, + { 9, 9, "memca", "Memory CA Completion", PRDV_HEX }, + { 10, 10, "memcto", "Memory Completion Timeout", PRDV_HEX }, + { -1, -1, NULL } +}; + +static void +pcieadm_cfgspace_print_dpc_rppio(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4]; + + if (BITX(cap, 5, 5) == 0) { + return; + } + + pcieadm_cfgspace_print_regdef(walkp, print, arg); +} + +static void +pcieadm_cfgspace_print_dpc_piohead(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4]; + uint32_t nwords = BITX(cap, 11, 8); + + if (BITX(cap, 5, 5) == 0 || nwords < 4) { + return; + } + + pcieadm_cfgspace_print_hex(walkp, print, NULL); +} + +static void +pcieadm_cfgspace_print_dpc_impspec(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4]; + uint32_t nwords = BITX(cap, 11, 8); + + if (BITX(cap, 5, 5) == 0 || nwords < 5) { + return; + } + + pcieadm_cfgspace_print_hex(walkp, print, NULL); +} + +static void +pcieadm_cfgspace_print_dpc_tlplog(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4]; + int32_t nwords = BITX(cap, 11, 8); + + if (nwords == 0 || BITX(cap, 5, 5) == 0) { + return; + } + + if (nwords <= 9) { + nwords -= 5; + } else { + nwords -= 4; + } + + for (int32_t i = 0; i < nwords; i++) { + char tlpshort[32], tlphuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(tlpshort, sizeof (tlpshort), "%s%u", + print->pcp_short, i); + (void) snprintf(tlphuman, sizeof (tlphuman), "%s %u", + print->pcp_human, i); + p.pcp_off = print->pcp_off + i * 4; + p.pcp_len = 4; + p.pcp_short = tlpshort; + p.pcp_human = tlphuman; + p.pcp_print = pcieadm_cfgspace_print_hex; + p.pcp_arg = NULL; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +static pcieadm_cfgspace_print_t pcieadm_cap_dpc[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 2, "cap", "DPC Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpc_cap }, + { 0x6, 2, "ctl", "DPC Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpc_ctl }, + { 0x8, 2, "sts", "DPC Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpc_sts }, + { 0xa, 2, "srcid", "DPC Error Source ID", + pcieadm_cfgspace_print_hex }, + { 0x10, 4, "rppiosts", "RP PIO Status", + pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits }, + { 0x14, 4, "rppiomask", "RP PIO Mask ID", + pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits }, + { 0x14, 4, "rppiosev", "RP PIO Severity", + pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits }, + { 0x18, 4, "rppiose", "RP PIO SysError", + pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits }, + { 0x1c, 4, "rppioex", "RP PIO Exception", + pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits }, + { 0x20, 4, "rppiohl0", "RP PIO Header Log 0", + pcieadm_cfgspace_print_dpc_piohead }, + { 0x24, 4, "rppiohl1", "RP PIO Header Log 1", + pcieadm_cfgspace_print_dpc_piohead }, + { 0x28, 4, "rppiohl2", "RP PIO Header Log 2", + pcieadm_cfgspace_print_dpc_piohead }, + { 0x2c, 4, "rppiohl3", "RP PIO Header Log 3", + pcieadm_cfgspace_print_dpc_piohead }, + { 0x30, 4, "impspec", "RP PIO ImpSpec Log", + pcieadm_cfgspace_print_dpc_impspec }, + { 0x34, 16, "tlplog", "RP PIO TLP Prefix Log", + pcieadm_cfgspace_print_dpc_tlplog }, + { -1, -1, NULL } +}; + +/* + * Virtual Channel Capability + */ +static pcieadm_regdef_t pcieadm_regdef_vc_cap1[] = { + { 0, 2, "count", "Extended VC Count", PRDV_HEX }, + { 4, 6, "lpcount", "Low Priority Extended VC Count", PRDV_HEX }, + { 8, 9, "refclk", "Reference Clock", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "100ns" } } }, + { 10, 11, "patsz", "Port Arbitration Table Size", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "1 bit", "2 bits", "4 bits", + "8 bits" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_vc_cap2[] = { + { 0, 7, "arbcap", "VC Arbitration Capability", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "hardware fixed", + "32 phase weighted round robin", "64 phase weighted round robin", + "128 phase weighted round robin" } } }, + { 24, 31, "offset", "VC Arbitration Table Offset", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_vc_ctl[] = { + { 0, 0, "loadtbl", "Load VC Arbitration Table", PRDV_HEX }, + { 1, 3, "arbtype", "VC Arbitration Select", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "hardware fixed", + "32 phase weighted round robin", "64 phase weighted round robin", + "128 phase weighted round robin" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_vc_sts[] = { + { 0, 0, "table", "VC Arbitration Table Status", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_vc_rsrccap[] = { + { 0, 7, "arbcap", "Port Arbitration Capability", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "hardware fixed", + "32 phase weighted round robin", "64 phase weighted round robin", + "128 phase weighted round robin", + "128 phase time-based weighted round robin", + "256 phase weighted round robin" } } }, + { 14, 14, "aps", "Advanced Packet Switching", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 15, 15, "rstx", "Reject Snoop Transactions", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 16, 22, "nslots", "Maximum Time Slots", PRDV_HEX, + { .prdv_hex = { 0, 1 } } }, + { 24, 31, "offset", "VC Arbitration Table Offset", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_vc_rsrcctl[] = { + { 0, 7, "tcmap", "TC/VC Map", PRDV_HEX }, + { 16, 16, "loadtbl", "Load VC Arbitration Table", PRDV_HEX }, + { 17, 19, "arbtype", "Port Arbitration Select", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "hardware fixed", + "32 phase weighted round robin", "64 phase weighted round robin", + "128 phase weighted round robin", + "128 phase time-based weighted round robin", + "256 phase weighted round robin" } } }, + { 24, 26, "vcid", "VC ID", PRDV_HEX }, + { 31, 31, "en", "VC Enable", + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_vc_rsrcsts[] = { + { 0, 0, "table", "Port Arbitration Table Status", PRDV_HEX }, + { -1, -1, NULL } +}; + +static void +pcieadm_cfgspace_print_vc_rsrc(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4]; + uint32_t nents = BITX(cap, 2, 0) + 1; + + for (uint32_t i = 0; i < nents; i++) { + char vcshort[32], vchuman[128]; + pcieadm_cfgspace_print_t p; + + (void) snprintf(vcshort, sizeof (vcshort), "rsrccap%u", i); + (void) snprintf(vchuman, sizeof (vchuman), "VC Resource %u " + "Capability", i); + p.pcp_off = print->pcp_off + i * 0x10; + p.pcp_len = 4; + p.pcp_short = vcshort; + p.pcp_human = vchuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_vc_rsrccap; + + p.pcp_print(walkp, &p, p.pcp_arg); + + (void) snprintf(vcshort, sizeof (vcshort), "rsrcctl%u", i); + (void) snprintf(vchuman, sizeof (vchuman), "VC Resource %u " + "Control", i); + p.pcp_off = print->pcp_off + i * 0x10 + 4; + p.pcp_len = 4; + p.pcp_short = vcshort; + p.pcp_human = vchuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_vc_rsrcctl; + + p.pcp_print(walkp, &p, p.pcp_arg); + + (void) snprintf(vcshort, sizeof (vcshort), "rsrcsts%u", i); + (void) snprintf(vchuman, sizeof (vchuman), "VC Resource %u " + "Status", i); + p.pcp_off = print->pcp_off + i * 0x10 + 0xa; + p.pcp_len = 2; + p.pcp_short = vcshort; + p.pcp_human = vchuman; + p.pcp_print = pcieadm_cfgspace_print_regdef; + p.pcp_arg = pcieadm_regdef_vc_rsrcsts; + + p.pcp_print(walkp, &p, p.pcp_arg); + } +} + +static pcieadm_cfgspace_print_t pcieadm_cap_vc[] = { + { 0x0, 4, "caphdr", "Capability Header", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr }, + { 0x4, 4, "cap1", "Port VC Capability 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_cap1 }, + { 0x8, 4, "cap2", "Port VC Capability 2", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_cap2 }, + { 0xc, 2, "ctl", "Port VC Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_ctl }, + { 0xe, 2, "sts", "Port VC Status", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_sts }, + { 0x10, 12, "vcrec", "VC Resource", pcieadm_cfgspace_print_vc_rsrc }, + { -1, -1, NULL } +}; + +/* + * HyperTransport + */ +static pcieadm_cfgspace_print_t pcieadm_cap_ht_intr[] = { + { 0x2, 1, "index", "Interrupt Discovery Index", + pcieadm_cfgspace_print_hex }, + { 0x4, 4, "dataport", "Interrupt Dataport", + pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_command_pri[] = { + { 0, 4, "unitid", "Base Unit ID", PRDV_HEX }, + { 5, 9, "count", "Unit Count", PRDV_HEX }, + { 10, 10, "host", "Master Host", PRDV_HEX }, + { 11, 11, "dir", "Default Direction", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "towards host", + "away from host" } } }, + { 12, 12, "drop", "Drop on Uninitialized Link", PRDV_HEX }, + { 13, 15, "cap", "Capability ID", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_command_sec[] = { + { 0, 0, "reset", "Warm Reset", PRDV_HEX }, + { 1, 1, "de", "Double Ended", PRDV_HEX }, + { 2, 6, "devno", "Device Number", PRDV_HEX }, + { 7, 7, "chain", "Chain Side", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "from host", "from chain" } } }, + { 8, 8, "hide", "Host Hide", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "visible", "hidden" } } }, + { 10, 10, "target", "Act as Target", PRDV_HEX }, + { 11, 11, "eocerr", "Host Inbound End of Chain Error", PRDV_HEX }, + { 12, 12, "drop", "Drop on Uninitialized Link", PRDV_HEX }, + { 13, 15, "cap", "Capability ID", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_linkctl[] = { + { 0, 0, "srcid", "Source ID", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "cfl", "CRC Flood", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "cst", "CRC Start Test", PRDV_HEX }, + { 3, 3, "cfer", "CRC Force Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "linkfail", "Link Failure", PRDV_HEX }, + { 5, 5, "initcmp", "Initialization Complete", PRDV_HEX }, + { 6, 6, "eoc", "End of Chain", PRDV_HEX }, + { 7, 7, "txoff", "Transmitter Off", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "transmitter on", + "transmitter off" } } }, + { 8, 11, "crcerr", "CRC Error", PRDV_HEX }, + { 12, 12, "isoc", "Isochronous Flow Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 13, 13, "ls", "LDTSTOP# Tristate", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 14, 14, "extctl", "Extended CTL Time", PRDV_HEX }, + { 15, 15, "64b", "64-bit Addressing", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_linkcfg[] = { + { 0, 2, "maxin", "Maximum Link Width In", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits", + "2 bits", "4 bits", NULL, "not connected" } } }, + { 3, 3, "dwfcinsup", "Doubleword Flow Control In", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 4, 6, "maxout", "Maximum Link Width Out", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits", + "2 bits", "4 bits", NULL, "not connected" } } }, + { 7, 7, "dwfcoutsup", "Doubleword Flow Control Out", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 8, 10, "linkin", "Link Width In", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits", + "2 bits", "4 bits", NULL, "not connected" } } }, + { 11, 11, "dwfcin", "Doubleword Flow Control In", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 12, 14, "linkout", "Link Width Out", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits", + "2 bits", "4 bits", NULL, "not connected" } } }, + { 15, 15, "dwfcout", "Doubleword Flow Control Out", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_rev[] = { + { 0, 4, "minor", "Minor Revision", PRDV_HEX }, + { 5, 7, "major", "Major Revision", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_linkfreq[] = { + { 0, 4, "freq", "Link Frequency", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "200 MHz", "300 MHz", "400 MHz", + "500 MHz", "600 MHz", "800 MHz", "1000 MHz", "1200 MHz", "1400 MHz", + "1600 MHz", "1800 MHz", "2000 MHz", "2200 MHz", "2400 MHz", + "2600 MHz", "Vendor Specfic" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_linkerr[] = { + { 4, 4, "prot", "Protocol Error", PRDV_HEX }, + { 5, 5, "over", "Overflow Error", PRDV_HEX }, + { 6, 6, "eoc", "End of Chain Error", PRDV_HEX }, + { 7, 7, "ctl", "CTL Timeout", PRDV_HEX }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_linkcap[] = { + { 0, 15, "freq", "Link Frequency", PRDV_BITFIELD, + .prd_val = { .prdv_strval = { "200 MHz", "300 MHz", "400 MHz", + "500 MHz", "600 MHz", "800 MHz", "1000 MHz", "1200 MHz", "1400 MHz", + "1600 MHz", "1800 MHz", "2000 MHz", "2200 MHz", "2400 MHz", + "2600 MHz", "Vendor Specfic" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_feature[] = { + { 0, 0, "isofc", "Isochronous Flow Control", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 1, 1, "ls", "LDTSTOP#", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 2, 2, "crct", "CRC Test Mode", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 3, 3, "ectl", "Extended CTL Time", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not required", "required" } } }, + { 4, 4, "64b", "64-bit Addressing", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 5, 5, "unitid", "UnitID Reorder", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "enabled", "disabled" } } }, + { 6, 6, "srcid", "Source Identification Extension", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "not required", "required" } } }, + { 8, 8, "extreg", "Extended Register Set", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "unsupported", "supported" } } }, + { 9, 9, "uscfg", "Upstream Configuration", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_error[] = { + { 0, 0, "protfl", "Protocol Error Flood", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "ovfl", "Overflow Error Flood", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 2, 2, "protf", "Protocol Error Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 3, 3, "ovf", "Overflow Error Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 4, 4, "eocf", "End of Chain Fatal Error", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 5, 5, "respf", "Response Error Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 6, 6, "crcf", "CRC Error Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 7, 7, "sysf", "System Error Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 8, 8, "chain", "Chain Fail", PRDV_HEX }, + { 9, 9, "resp", "Response Error", PRDV_HEX }, + { 10, 10, "protnf", "Protocol Error Non-Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 11, 11, "ovfnf", "Overflow Error Non-Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 12, 12, "eocnf", "End of Chain Error Non-Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 13, 13, "respnf", "Response Error Non-Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 14, 14, "crcnf", "CRC Error Non-Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 15, 15, "sysnf", "System Error Non-Fatal", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_memory[] = { + { 0, 8, "base", "Memory Base Upper 8 Bits", PRDV_HEX, + .prd_val = { .prdv_hex = { 32 } } }, + { 9, 15, "limit", "Memory Limit Upper 8 Bits", PRDV_HEX, + .prd_val = { .prdv_hex = { 32 } } }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_ht_pri[] = { + { 0x2, 2, "command", "Command", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_command_pri }, + { 0x4, 2, "linkctl0", "Link Control 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkctl }, + { 0x6, 2, "linkcfg0", "Link Configuration 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcfg }, + { 0x8, 2, "linkctl1", "Link Control 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkctl }, + { 0xa, 2, "linkcfg1", "Link Configuration 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcfg }, + { 0xc, 1, "rev", "Revision", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_rev }, + { 0xd, 1, "linkfreq0", "Link Frequency 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkfreq }, + { 0xd, 1, "linkerr0", "Link Error 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkerr }, + { 0xe, 2, "linkfcap0", "Link Frequency Cap 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcap }, + { 0x10, 1, "feature", "Feature Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_feature }, + { 0x11, 1, "linkfreq1", "Link Frequency 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkfreq }, + { 0x11, 1, "linkerr1", "Link Error 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkerr }, + { 0x12, 2, "linkfcap1", "Link Frequency Cap 1", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcap }, + { 0x14, 2, "scratch", "Enumeration Scratchpad", + pcieadm_cfgspace_print_hex }, + { 0x16, 2, "error", "Error Handling", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_error }, + { 0x18, 2, "memory", "Memory", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_memory }, + { 0x1a, 1, "bus", "Bus Number", pcieadm_cfgspace_print_hex }, + { -1, -1, NULL } +}; + +static pcieadm_cfgspace_print_t pcieadm_cap_ht_sec[] = { + { 0x2, 2, "command", "Command", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_command_sec }, + { 0x4, 2, "linkctl", "Link Control", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkctl }, + { 0x6, 2, "linkcfg", "Link Configuration", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcfg }, + { 0x8, 1, "rev", "Revision", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_rev }, + { 0x9, 1, "linkfreq", "Link Frequency 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkfreq }, + { 0x9, 1, "linkerr", "Link Error 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkerr }, + { 0xa, 2, "linkfcap", "Link Frequency Cap 0", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcap }, + { 0xc, 2, "feature", "Feature Capability", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_feature }, + { 0x10, 2, "scratch", "Enumeration Scratchpad", + pcieadm_cfgspace_print_hex }, + { 0x12, 2, "error", "Error Handling", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_error }, + { 0x14, 2, "memory", "Memory", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_memory }, + { -1, -1, NULL } +}; + +static pcieadm_regdef_t pcieadm_regdef_ht_msi[] = { + { 0, 0, "en", "Enable", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { 1, 1, "fixed", "Fixed", PRDV_STRVAL, + .prd_val = { .prdv_strval = { "disabled", "enabled" } } }, + { -1, -1, NULL } +}; + +static void +pcieadm_cfgspace_print_ht_msi_addr(pcieadm_cfgspace_walk_t *walkp, + pcieadm_cfgspace_print_t *print, void *arg) +{ + uint8_t fixed = walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 2]; + + if (BITX(fixed, 1, 1) != 0) + return; + + pcieadm_cfgspace_print_hex(walkp, print, arg); +} + +static pcieadm_cfgspace_print_t pcieadm_cap_ht_msi[] = { + { 0x2, 2, "command", "Command", + pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_msi }, + { 0x4, 8, "address", "MSI Address", + pcieadm_cfgspace_print_ht_msi_addr }, + { -1, -1, NULL } +}; + +/* + * Capability related tables + */ +typedef struct pcieadm_cap_vers { + uint32_t ppr_vers; + uint32_t ppr_len; + pcieadm_cfgspace_print_t *ppr_print; +} pcieadm_cap_vers_t; + +typedef struct pcieadm_subcap { + const char *psub_short; + const char *psub_human; +} pcieadm_subcap_t; + +typedef struct pcieadm_pci_cap pcieadm_pci_cap_t; + +typedef void (*pcieadm_cap_info_f)(pcieadm_cfgspace_walk_t *, + const pcieadm_pci_cap_t *, uint32_t, const pcieadm_cap_vers_t **, + uint32_t *, const pcieadm_subcap_t **); + +struct pcieadm_pci_cap { + uint32_t ppc_id; + const char *ppc_short; + const char *ppc_human; + pcieadm_cap_info_f ppc_info; + pcieadm_cap_vers_t ppc_vers[4]; +}; + +/* + * Capability version determinations. + */ + +static void +pcieadm_cap_info_fixed(pcieadm_cfgspace_walk_t *walkp, + const pcieadm_pci_cap_t *cap, uint32_t off, + const pcieadm_cap_vers_t **versp, uint32_t *lenp, + const pcieadm_subcap_t **subcap) +{ + *versp = &cap->ppc_vers[0]; + *lenp = cap->ppc_vers[0].ppr_len; + *subcap = NULL; +} + +static void +pcieadm_cap_info_vers(pcieadm_cfgspace_walk_t *walkp, + const pcieadm_pci_cap_t *cap, uint32_t off, + const pcieadm_cap_vers_t **versp, uint32_t *lenp, + const pcieadm_subcap_t **subcap) +{ + uint8_t vers; + + *subcap = NULL; + vers = walkp->pcw_data->pcb_u8[off + 2] & 0xf; + for (uint32_t i = 0; i < ARRAY_SIZE(cap->ppc_vers); i++) { + if (vers == cap->ppc_vers[i].ppr_vers && + cap->ppc_vers[i].ppr_vers != 0) { + *versp = &cap->ppc_vers[i]; + *lenp = cap->ppc_vers[i].ppr_len; + return; + } + } + + *versp = NULL; + *lenp = 0; +} + +/* + * The PCI Power Management capability uses a 3-bit version ID as opposed to the + * standard 4-bit version. + */ +static void +pcieadm_cap_info_pcipm(pcieadm_cfgspace_walk_t *walkp, + const pcieadm_pci_cap_t *cap, uint32_t off, + const pcieadm_cap_vers_t **versp, uint32_t *lenp, + const pcieadm_subcap_t **subcap) +{ + uint8_t vers; + + *subcap = NULL; + vers = walkp->pcw_data->pcb_u8[off + 2] & 0x7; + for (uint32_t i = 0; i < ARRAY_SIZE(cap->ppc_vers); i++) { + if (vers == cap->ppc_vers[i].ppr_vers) { + *versp = &cap->ppc_vers[i]; + *lenp = cap->ppc_vers[i].ppr_len; + return; + } + } + + *versp = NULL; + *lenp = 0; +} + +/* + * The length of the MSI capability depends on bits in its control field. As + * such we use a custom function to extract the length and treat each of these + * variants as thought it were a different version. + */ +static pcieadm_cap_vers_t pcieadm_cap_vers_msi_32 = { + 0, 0xa, pcieadm_cap_msi_32 +}; + +static pcieadm_cap_vers_t pcieadm_cap_vers_msi_32ext = { + 0, 0xc, pcieadm_cap_msi_32ext +}; + +static pcieadm_cap_vers_t pcieadm_cap_vers_msi_64 = { + 0, 0xe, pcieadm_cap_msi_64 +}; + +static pcieadm_cap_vers_t pcieadm_cap_vers_msi_64ext = { + 0, 0x10, pcieadm_cap_msi_64ext +}; + +static pcieadm_cap_vers_t pcieadm_cap_vers_msi_32pvm = { + 0, 0x14, pcieadm_cap_msi_32pvm +}; + +static pcieadm_cap_vers_t pcieadm_cap_vers_msi_64pvm = { + 0, 0x18, pcieadm_cap_msi_64pvm +}; + +static void +pcieadm_cap_info_msi(pcieadm_cfgspace_walk_t *walkp, + const pcieadm_pci_cap_t *cap, uint32_t off, + const pcieadm_cap_vers_t **versp, uint32_t *lenp, + const pcieadm_subcap_t **subcap) +{ + uint16_t ctrl; + boolean_t addr64, pvm, ext; + + *subcap = NULL; + ctrl = walkp->pcw_data->pcb_u8[off + 2] | + (walkp->pcw_data->pcb_u8[off + 3] << 8); + if (ctrl == PCI_EINVAL16) { + warnx("failed to read MSI Message Control register"); + *lenp = 0; + *versp = NULL; + return; + } + + /* + * The MSI capability has three main things that control its size. + * 64-bit addressing adds 4 bytes. Per-Vector Masking adds 8 bytes and + * causes the Extended data addressing piece to always be present. + * Therefore we check first for pvm as it implies evt, effectively. + */ + addr64 = (ctrl & PCI_MSI_64BIT_MASK) != 0; + pvm = (ctrl & PCI_MSI_PVM_MASK) != 0; + ext = (ctrl & PCI_MSI_EMD_MASK) != 0; + + if (pvm && addr64) { + *versp = &pcieadm_cap_vers_msi_64pvm; + } else if (pvm) { + *versp = &pcieadm_cap_vers_msi_32pvm; + } else if (addr64 && ext) { + *versp = &pcieadm_cap_vers_msi_64ext; + } else if (addr64) { + *versp = &pcieadm_cap_vers_msi_64; + } else if (ext) { + *versp = &pcieadm_cap_vers_msi_32ext; + } else { + *versp = &pcieadm_cap_vers_msi_32; + } + + *lenp = (*versp)->ppr_len; +} + +/* + * The AER Capability is technically different for PCIe-PCI bridges. If we find + * that device type here, then we need to use a different version information + * rather than the actual set defined with the device (which have changed over + * time). + */ +static const pcieadm_cap_vers_t pcieadm_cap_vers_aer_bridge = { + 1, 0x4c, pcieadm_cap_aer_bridge +}; + +static void +pcieadm_cap_info_aer(pcieadm_cfgspace_walk_t *walkp, + const pcieadm_pci_cap_t *cap, uint32_t off, + const pcieadm_cap_vers_t **versp, uint32_t *lenp, + const pcieadm_subcap_t **subcap) +{ + if (walkp->pcw_dtype == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) { + uint8_t vers; + + *subcap = NULL; + vers = walkp->pcw_data->pcb_u8[off + 2] & 0xf; + if (vers != pcieadm_cap_vers_aer_bridge.ppr_vers) { + warnx("encountered PCIe to PCI bridge with unknown " + "AER capability version: %u", vers); + *lenp = 0; + *versp = NULL; + return; + } + *lenp = pcieadm_cap_vers_aer_bridge.ppr_len; + *versp = &pcieadm_cap_vers_aer_bridge; + } + + return (pcieadm_cap_info_vers(walkp, cap, off, versp, lenp, subcap)); +} + +/* + * The PCI-X capability varies depending on the header type of the device. + * Therefore we simply use the device type to figure out what to do. + */ +static pcieadm_cap_vers_t pcieadm_cap_vers_pcix_dev = { + 0, 0x8, pcieadm_cap_pcix_dev +}; + +static pcieadm_cap_vers_t pcieadm_cap_vers_pcix_bridge = { + 0, 0x10, pcieadm_cap_pcix_bridge +}; + +static void +pcieadm_cap_info_pcix(pcieadm_cfgspace_walk_t *walkp, + const pcieadm_pci_cap_t *cap, uint32_t off, + const pcieadm_cap_vers_t **versp, uint32_t *lenp, + const pcieadm_subcap_t **subcap) +{ + + *subcap = NULL; + switch (walkp->pcw_dtype) { + case PCI_HEADER_ZERO: + *versp = &pcieadm_cap_vers_pcix_dev; + break; + case PCI_HEADER_ONE: + *versp = &pcieadm_cap_vers_pcix_bridge; + break; + default: + warnx("encountered PCI-X capability with unsupported device " + "type: 0x%x\n", walkp->pcw_dtype); + *lenp = 0; + *versp = NULL; + return; + } + + *lenp = (*versp)->ppr_len; +} + +typedef struct pcieadm_cap_ht { + uint32_t pch_capid; + pcieadm_subcap_t pch_subcap; + pcieadm_cap_vers_t pch_vers; +} pcieadm_cap_ht_t; + +static pcieadm_cap_ht_t pcieadm_ht_cap_pri = { + 0x00, { "pri", "Primary" }, { 0, 0x1c, pcieadm_cap_ht_pri } +}; + +static pcieadm_cap_ht_t pcieadm_ht_cap_sec = { + 0x01, { "sec", "Secondary" }, { 0, 0x18, pcieadm_cap_ht_sec } +}; + +static pcieadm_cap_ht_t pcieadm_ht_caps[] = { + { 0x08, { "switch", "Switch" } }, + { 0x10, { "intr", "Interrupt Discovery and Configuration" }, + { 0, 8, pcieadm_cap_ht_intr } }, + { 0x11, { "rev", "Revision ID" } }, + { 0x12, { "unitid", "UnitID Clumping" } }, + { 0x13, { "extcfg", "Extended Configuration Space Access" } }, + { 0x14, { "addrmap", "Address Mapping" } }, + { 0x15, { "msi", "MSI Mapping" }, + { 0, 4, pcieadm_cap_ht_msi } }, + { 0x16, { "dir", "DirectRoute" } }, + { 0x17, { "vcset", "VCSet" } }, + { 0x18, { "retry", "Retry Mode" } }, + { 0x19, { "x86", "X86 Encoding" } }, + { 0x1a, { "gen3", "Gen3" } }, + { 0x1b, { "fle", "Function-Level Extension" } }, + { 0x1c, { "pm", "Power Management" } }, + { UINT32_MAX, NULL }, +}; + +static void +pcieadm_cap_info_ht(pcieadm_cfgspace_walk_t *walkp, + const pcieadm_pci_cap_t *cap, uint32_t off, + const pcieadm_cap_vers_t **versp, uint32_t *lenp, + const pcieadm_subcap_t **subcap) +{ + uint32_t base = walkp->pcw_data->pcb_u32[off / 4]; + uint32_t caplo = BITX(base, 31, 29); + pcieadm_cap_ht_t *htcap = NULL; + + *versp = NULL; + *lenp = 0; + *subcap = NULL; + + if (caplo > 1) { + uint32_t capid = BITX(base, 31, 27); + + for (uint32_t i = 0; pcieadm_ht_caps[i].pch_capid != UINT32_MAX; + i++) { + if (capid == pcieadm_ht_caps[i].pch_capid) { + htcap = &pcieadm_ht_caps[i]; + break; + } + } + } else if (caplo == 0) { + htcap = &pcieadm_ht_cap_pri; + } else if (caplo == 1) { + htcap = &pcieadm_ht_cap_sec; + } + + if (htcap == NULL) { + warnx("encountered unknown HyperTransport Capability 0x%x", + BITX(base, 31, 27)); + return; + } + + *subcap = &htcap->pch_subcap; + if (htcap->pch_vers.ppr_print != NULL) { + *versp = &htcap->pch_vers; + *lenp = htcap->pch_vers.ppr_len; + } +} + +pcieadm_pci_cap_t pcieadm_pci_caps[] = { + { PCI_CAP_ID_PM, "pcipm", "PCI Power Management", + pcieadm_cap_info_pcipm, { { 2, 8, pcieadm_cap_pcipm_v3 }, + { 3, 8, pcieadm_cap_pcipm_v3 } } }, + { PCI_CAP_ID_AGP, "agp", "Accelerated Graphics Port" }, + { PCI_CAP_ID_VPD, "vpd", "Vital Product Data", pcieadm_cap_info_fixed, + { { 0, 8, pcieadm_cap_vpd } } }, + { PCI_CAP_ID_SLOT_ID, "slot", "Slot Identification" }, + { PCI_CAP_ID_MSI, "msi", "Message Signaled Interrupts", + pcieadm_cap_info_msi }, + { PCI_CAP_ID_cPCI_HS, "cpci", "CompactPCI Hot Swap" }, + { PCI_CAP_ID_PCIX, "pcix", "PCI-X", pcieadm_cap_info_pcix }, + { PCI_CAP_ID_HT, "ht", "HyperTransport", pcieadm_cap_info_ht }, + { PCI_CAP_ID_VS, "vs", "Vendor Specific", pcieadm_cap_info_fixed, + { { 0, 3, pcieadm_cap_vs } } }, + { PCI_CAP_ID_DEBUG_PORT, "dbg", "Debug Port", pcieadm_cap_info_fixed, + { { 0, 4, pcieadm_cap_debug } } }, + { PCI_CAP_ID_cPCI_CRC, "cpcicrc", + "CompactPCI Central Resource Control" }, + { PCI_CAP_ID_PCI_HOTPLUG, "pcihp", "PCI Hot-Plug" }, + { PCI_CAP_ID_P2P_SUBSYS, "bdgsub", "PCI Bridge Subsystem Vendor ID", + pcieadm_cap_info_fixed, { 0, 8, pcieadm_cap_bridge_subsys } }, + { PCI_CAP_ID_AGP_8X, "agp8x", "AGP 8x" }, + { PCI_CAP_ID_SECURE_DEV, "secdev", "Secure Device" }, + { PCI_CAP_ID_PCI_E, "pcie", "PCI Express", pcieadm_cap_info_vers, + { { 1, 0x24, pcieadm_cap_pcie_v1 }, + { 2, 0x3c, pcieadm_cap_pcie_v2 } } }, + { PCI_CAP_ID_MSI_X, "msix", "MSI-X", pcieadm_cap_info_fixed, + { { 0, 12, pcieadm_cap_msix } } }, + { PCI_CAP_ID_SATA, "sata", "Serial ATA Configuration", + pcieadm_cap_info_fixed, { { 0, 8, pcieadm_cap_sata } } }, + /* + * Note, the AF feature doesn't have a version but encodes a length in + * the version field, so we cheat and use that. + */ + { PCI_CAP_ID_FLR, "af", "Advanced Features", pcieadm_cap_info_vers, + { { 6, 6, pcieadm_cap_af } } }, + { PCI_CAP_ID_EA, "ea", "Enhanced Allocation" }, + { PCI_CAP_ID_FPB, "fpb", "Flattening Portal Bridge" } +}; + +pcieadm_pci_cap_t pcieadm_pcie_caps[] = { + { 0, "null", "NULL Capability", pcieadm_cap_info_fixed, + { { 0, 0x4, pcieadm_cap_null } } }, + { PCIE_EXT_CAP_ID_AER, "aer", "Advanced Error Reporting", + pcieadm_cap_info_aer, { { 1, 0x38, pcieadm_cap_aer_v1 }, + { 2, 0x48, pcieadm_cap_aer_v2 } } }, + { PCIE_EXT_CAP_ID_VC, "vc", "Virtual Channel", pcieadm_cap_info_vers, + { { 0x1, 0x1c, pcieadm_cap_vc } } }, + { PCIE_EXT_CAP_ID_SER, "sn", "Serial Number", pcieadm_cap_info_vers, + { { 1, 0xc, pcieadm_cap_sn } } }, + { PCIE_EXT_CAP_ID_PWR_BUDGET, "powbudg", "Power Budgeting", + pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_powbudg } } }, + { PCIE_EXT_CAP_ID_RC_LINK_DECL, "rcld", + "Root Complex Link Declaration" }, + { PCIE_EXT_CAP_ID_RC_INT_LINKCTRL, "rcilc", + "Root Complex Internal Link Control" }, + { PCIE_EXT_CAP_ID_RC_EVNT_CEA, "rcecea", + "Root Complex Event Collector Endpoint Aggregation" }, + { PCIE_EXT_CAP_ID_MFVC, "mfvc", "Multi-Function Virtual Channel" }, + { PCIE_EXT_CAP_ID_VC_WITH_MFVC, "vcwmfvc", "Virtual Channel with MFVC", + pcieadm_cap_info_vers, { { 0x1, 0x1c, pcieadm_cap_vc } } }, + { PCIE_EXT_CAP_ID_RCRB, "rcrb", "Root Complex Register Block" }, + { PCIE_EXT_CAP_ID_VS, "vsec", "Vendor Specific Extended Capability", + pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_vsec } } }, + { PCIE_EXT_CAP_ID_CAC, "cac", "Configuration Access Correlation" }, + { PCIE_EXT_CAP_ID_ACS, "acs", "Access Control Services", + pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_acs } } }, + { PCIE_EXT_CAP_ID_ARI, "ari", "Alternative Routing-ID Interpretation", + pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_ari } } }, + { PCIE_EXT_CAP_ID_ATS, "ats", "Access Translation Services", + pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_ats } } }, + { PCIE_EXT_CAP_ID_SRIOV, "sriov", "Single Root I/O Virtualization", + pcieadm_cap_info_vers, { { 1, 0x40, pcieadm_cap_sriov } } }, + { PCIE_EXT_CAP_ID_MRIOV, "mriov", "Multi-Root I/O Virtualization" }, + { PCIE_EXT_CAP_ID_MULTICAST, "mcast", "Multicast", + pcieadm_cap_info_vers, { { 1, 0x30, pcieadm_cap_mcast } } }, + { PCIE_EXT_CAP_ID_PGREQ, "pgreq", "Page Request", + pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_pgreq } } }, + { PCIE_EXT_CAP_ID_EA, "ea", "Enhanced Allocation" }, + { PCIE_EXT_CAP_ID_RESIZE_BAR, "rbar", "Resizable Bar" }, + { PCIE_EXT_CAP_ID_DPA, "dpa", "Dynamic Power Allocation", + pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_dpa } } }, + { PCIE_EXT_CAP_ID_TPH_REQ, "tph", "TPH Requester", + pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_tph } } }, + { PCIE_EXT_CAP_ID_LTR, "ltr", "Latency Tolerance Reporting", + pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_ltr } } }, + { PCIE_EXT_CAP_ID_PCIE2, "pcie2", "Secondary PCI Express", + pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_pcie2 } } }, + { PCIE_EXT_CAP_ID_PASID, "pasid", "Process Address Space ID", + pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_pasid } } }, + { PCIE_EXT_CAP_ID_LNR, "lnr", "LN Requester" }, + { PCIE_EXT_CAP_ID_DPC, "dpc", "Downstream Port Containment", + pcieadm_cap_info_vers, { { 1, 0x30, pcieadm_cap_dpc } } }, + { PCIE_EXT_CAP_ID_L1PM, "l1pm", "L1 PM Substates", + pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_l1pm_v1 }, + { 2, 0x14, pcieadm_cap_l1pm_v2 } } }, + { PCIE_EXT_CAP_ID_PTM, "ptm", "Precision Time Management", + pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_info_ptm } } }, + { PCIE_EXT_CAP_ID_FRS, "frs", "FRS Queueing" }, + { PCIE_EXT_CAP_ID_RTR, "trt", "Readiness Time Reporting" }, + /* + * When we encounter a designated vendor specificaiton, in particular, + * for CXL, we'll want to set ppc_subcap so we can use reasonable + * filtering. + */ + { PCIE_EXT_CAP_ID_DVS, "dvsec", + "Designated Vendor-Specific Extended Capability" }, + { PCIE_EXT_CAP_ID_VFRBAR, "vfrbar", "Virtual Function Resizable BAR" }, + { PCIE_EXT_CAP_ID_DLF, "dlf", "Data Link Feature", + pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_dlf } } }, + { PCIE_EXT_CAP_ID_PL16GT, "pl16g", "Physical Layer 16.0 GT/s", + pcieadm_cap_info_vers, { { 1, 0x22, pcieadm_cap_16g } } }, + { PCIE_EXT_CAP_ID_LANE_MARGIN, "margin", + "Lane Margining at the Receiver", pcieadm_cap_info_vers, + { { 1, 0x8, pcieadm_cap_margin } } }, + { PCIE_EXT_CAP_ID_HIEARCHY_ID, "hierid", "Hierarchy ID" }, + { PCIE_EXT_CAP_ID_NPEM, "npem", "Native PCIe Enclosure Management" }, + { PCIE_EXT_CAP_ID_PL32GT, "pl32g", "Physical Layer 32.0 GT/s" }, + { PCIE_EXT_CAP_ID_AP, "ap", "Alternative Protocol" }, + { PCIE_EXT_CAP_ID_SFI, "sfi", "System Firmware Intermediary" } +}; + +static const pcieadm_pci_cap_t * +pcieadm_cfgspace_match_cap(uint32_t capid, boolean_t pcie) +{ + uint_t ncaps; + pcieadm_pci_cap_t *caps; + + if (pcie) { + ncaps = ARRAY_SIZE(pcieadm_pcie_caps); + caps = pcieadm_pcie_caps; + } else { + ncaps = ARRAY_SIZE(pcieadm_pci_caps); + caps = pcieadm_pci_caps; + } + + for (uint_t i = 0; i < ncaps; i++) { + if (caps[i].ppc_id == capid) { + return (&caps[i]); + } + } + + return (NULL); +} + +static void +pcieadm_cfgspace_print_cap(pcieadm_cfgspace_walk_t *walkp, uint_t capid, + const pcieadm_pci_cap_t *cap_info, const pcieadm_cap_vers_t *vers_info, + const pcieadm_subcap_t *subcap) +{ + boolean_t filter = B_FALSE; + + /* + * If we don't recognize the capability, print out the ID if we're not + * filtering and not in parsable mode. + */ + if (cap_info == NULL) { + if (walkp->pcw_ofmt == NULL && + pcieadm_cfgspace_filter(walkp, NULL)) { + warnx("encountered unknown capability ID 0x%x " + "unable to print or list", capid); + pcieadm_print("Unknown Capability (0x%x)\n", capid); + } + return; + } + + /* + * Check to see if we should print this and in particular, if there's + * both a capability or subcapability, we need to try and match both. + * The reason that the calls to check the filters are conditioned on + * pcw_ofmt is that when we're in parsable mode, we cannot match a + * top-level capability since it's an arbitrary number of fields. + */ + if (walkp->pcw_ofmt == NULL) { + filter = pcieadm_cfgspace_filter(walkp, cap_info->ppc_short); + } + pcieadm_strfilt_push(walkp, cap_info->ppc_short); + if (subcap != NULL) { + if (walkp->pcw_ofmt == NULL) { + boolean_t subfilt = pcieadm_cfgspace_filter(walkp, + subcap->psub_short); + filter = subfilt || filter; + } + pcieadm_strfilt_push(walkp, subcap->psub_short); + } + + + if (walkp->pcw_ofmt == NULL && filter) { + if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) { + if (subcap != NULL) { + pcieadm_print("%s Capability - %s (%s) " + "(0x%x)\n", cap_info->ppc_human, + subcap->psub_human, + walkp->pcw_filt->pstr_curgen, capid); + } else { + pcieadm_print("%s Capability (%s) (0x%x)\n", + cap_info->ppc_human, + walkp->pcw_filt->pstr_curgen, capid); + } + } else { + if (subcap != NULL) { + pcieadm_print("%s Capability - %s (0x%x)\n", + cap_info->ppc_human, subcap->psub_human, + capid); + } else { + pcieadm_print("%s Capability (0x%x)\n", + cap_info->ppc_human, capid); + } + } + } + + if (vers_info != NULL) { + pcieadm_cfgspace_print_t *print; + + pcieadm_indent(); + for (print = vers_info->ppr_print; + print->pcp_short != NULL; print++) { + VERIFY3P(print->pcp_print, !=, NULL); + print->pcp_print(walkp, print, + print->pcp_arg); + } + pcieadm_deindent(); + } else { + if (subcap != NULL) { + warnx("Unable to print or list %s - %s (no support or " + "missing version info)", cap_info->ppc_human, + subcap->psub_human); + } else { + warnx("Unable to print or list %s (no support or " + "missing version info)", cap_info->ppc_human); + } + } + + if (subcap != NULL) { + pcieadm_strfilt_pop(walkp); + } + pcieadm_strfilt_pop(walkp); +} + +static void +pcieadm_cfgspace_write(int fd, const uint8_t *source, size_t len) +{ + size_t off = 0; + + while (len > 0) { + ssize_t ret = write(fd, source + off, len - off); + if (ret < 0) { + err(EXIT_FAILURE, "failed to write config space to " + "output file"); + } + + off += ret; + len -= ret; + } +} + +void +pcieadm_cfgspace(pcieadm_t *pcip, pcieadm_cfgspace_op_t op, + pcieadm_cfgspace_f readf, int fd, void *readarg, uint_t nfilts, + pcieadm_cfgspace_filter_t *filters, pcieadm_cfgspace_flags_t flags, + ofmt_handle_t ofmt) +{ + uint_t type; + uint16_t cap; + pcieadm_cfgspace_data_t data; + pcieadm_cfgspace_walk_t walk; + const char *headstr, *headshort; + pcieadm_cfgspace_print_t *header; + boolean_t capsup = B_FALSE, extcfg = B_FALSE; + uint_t ncaps; + + walk.pcw_pcieadm = pcip; + walk.pcw_op = op; + walk.pcw_data = &data; + walk.pcw_outfd = fd; + walk.pcw_capoff = 0; + walk.pcw_nlanes = 0; + walk.pcw_nfilters = nfilts; + walk.pcw_filters = filters; + walk.pcw_flags = flags; + walk.pcw_ofmt = ofmt; + walk.pcw_filt = NULL; + + /* + * Start by reading all of the basic 40-byte config space header in one + * fell swoop. + */ + for (uint32_t i = 0; i < PCI_CAP_PTR_OFF / 4; i++) { + if (!readf(i * 4, 4, &data.pcb_u32[i], readarg)) { + errx(EXIT_FAILURE, "failed to read offset %u from " + "configuration space", i * 4); + } + } + walk.pcw_valid = PCI_CAP_PTR_OFF; + walk.pcw_caplen = PCI_CAP_PTR_OFF; + + /* + * Grab the information from the header that we need to figure out what + * kind of device this is, how to print it, if there are any + * capabilities, and go from there. + */ + type = data.pcb_u8[PCI_CONF_HEADER] & PCI_HEADER_TYPE_M; + switch (type) { + case PCI_HEADER_ZERO: + headstr = "Type 0 Header"; + headshort = "header0"; + header = pcieadm_cfgspace_type0; + capsup = (data.pcb_u8[PCI_CONF_STAT] & PCI_STAT_CAP) != 0; + break; + case PCI_HEADER_ONE: + headstr = "Type 1 Header"; + headshort = "header1"; + header = pcieadm_cfgspace_type1; + capsup = (data.pcb_u8[PCI_CONF_STAT] & PCI_STAT_CAP) != 0; + break; + case PCI_HEADER_TWO: + default: + headstr = "Unknown Header"; + headshort = "headerX"; + header = pcieadm_cfgspace_unknown; + warnx("unsupported PCI header type: 0x%x, output limited to " + "data configuration space"); + } + + walk.pcw_dtype = type; + + if (op == PCIEADM_CFGSPACE_OP_WRITE) { + pcieadm_cfgspace_write(fd, &data.pcb_u8[0], PCI_CAP_PTR_OFF); + } else if (op == PCIEADM_CFGSPACE_OP_PRINT) { + pcieadm_cfgspace_print_t *print; + + if (walk.pcw_ofmt == NULL && + pcieadm_cfgspace_filter(&walk, headshort)) { + if ((flags & PCIEADM_CFGSPACE_F_SHORT) != 0) { + pcieadm_print("Device %s -- %s (%s)\n", + pcip->pia_devstr, headstr, headshort); + } else { + pcieadm_print("Device %s -- %s\n", + pcip->pia_devstr, headstr); + } + } + + pcieadm_strfilt_push(&walk, headshort); + pcieadm_indent(); + for (print = header; print->pcp_short != NULL; print++) { + print->pcp_print(&walk, print, print->pcp_arg); + } + pcieadm_deindent(); + pcieadm_strfilt_pop(&walk); + } + + + if (!capsup) { + return; + } + + for (uint32_t i = PCI_CAP_PTR_OFF / 4; i < PCI_CONF_HDR_SIZE / 4; i++) { + if (!readf(i * 4, 4, &data.pcb_u32[i], readarg)) { + errx(EXIT_FAILURE, "failed to read offset %u from " + "configuration space", i * 4); + } + } + walk.pcw_valid = PCIE_EXT_CAP; + VERIFY3P(walk.pcw_filt, ==, NULL); + + if (op == PCIEADM_CFGSPACE_OP_WRITE) { + pcieadm_cfgspace_write(fd, &data.pcb_u8[PCI_CAP_PTR_OFF], + PCI_CONF_HDR_SIZE - PCI_CAP_PTR_OFF); + } + + ncaps = 0; + cap = data.pcb_u8[PCI_CONF_CAP_PTR]; + while (cap != 0 && cap != PCI_EINVAL8) { + const pcieadm_pci_cap_t *cap_info; + const pcieadm_cap_vers_t *vers_info = NULL; + const pcieadm_subcap_t *subcap = NULL; + uint8_t cap_id, nextcap; + uint32_t read_len = 0; + + /* + * The PCI specification requires that the caller mask off the + * bottom two bits. Always check for an invalid value (all 1s) + * before this. + */ + cap &= PCI_CAP_PTR_MASK; + cap_id = data.pcb_u8[cap + PCI_CAP_ID]; + nextcap = data.pcb_u8[cap + PCI_CAP_NEXT_PTR]; + cap_info = pcieadm_cfgspace_match_cap(cap_id, B_FALSE); + if (cap_info != NULL && cap_info->ppc_info != NULL) { + cap_info->ppc_info(&walk, cap_info, cap, &vers_info, + &read_len, &subcap); + } + + walk.pcw_caplen = read_len; + walk.pcw_capoff = cap; + + if (cap_id == PCI_CAP_ID_PCI_E) { + extcfg = B_TRUE; + if (walk.pcw_valid != 0) { + walk.pcw_pcietype = data.pcb_u8[cap + + PCIE_PCIECAP] & PCIE_PCIECAP_DEV_TYPE_MASK; + walk.pcw_nlanes = (data.pcb_u8[cap + + PCIE_LINKCAP] & 0xf0) >> 4; + walk.pcw_nlanes |= (data.pcb_u8[cap + + PCIE_LINKCAP + 1] & 0x01) << 4; + } else { + walk.pcw_pcietype = UINT_MAX; + } + } + + if (op == PCIEADM_CFGSPACE_OP_PRINT) { + pcieadm_cfgspace_print_cap(&walk, cap_id, cap_info, + vers_info, subcap); + } + + cap = nextcap; + ncaps++; + if (ncaps >= PCI_CAP_MAX_PTR) { + errx(EXIT_FAILURE, "encountered more PCI capabilities " + "than fit in configuration space"); + } + } + + if (!extcfg) { + return; + } + + for (uint_t i = PCIE_EXT_CAP / 4; i < PCIE_CONF_HDR_SIZE / 4; i++) { + if (!readf(i * 4, 4, &data.pcb_u32[i], readarg)) { + errx(EXIT_FAILURE, "failed to read offset %u from " + "configuration space", i * 4); + } + } + walk.pcw_valid = PCIE_CONF_HDR_SIZE; + + if (op == PCIEADM_CFGSPACE_OP_WRITE) { + pcieadm_cfgspace_write(fd, &data.pcb_u8[PCIE_EXT_CAP], + PCIE_CONF_HDR_SIZE - PCIE_EXT_CAP); + return; + } + + cap = PCIE_EXT_CAP; + ncaps = 0; + while (cap != 0 && cap != PCI_EINVAL16) { + uint16_t cap_id, nextcap; + const pcieadm_pci_cap_t *cap_info; + const pcieadm_cap_vers_t *vers_info = NULL; + const pcieadm_subcap_t *subcap = NULL; + uint32_t read_len = 0; + + /* + * PCIe has the same masking as PCI. Note, sys/pcie.h currently + * has PCIE_EXT_CAP_NEXT_PTR_MASK as 0xfff, instead of the + * below. This should be switched to PCIE_EXT_CAP_NEXT_PTR_MASK + * when the kernel headers are fixed. + */ + cap &= 0xffc; + + /* + * While this seems duplicative of the loop condition, a device + * without capabilities indicates it with a zero for the first + * cap. + */ + if (data.pcb_u32[cap / 4] == 0 || + data.pcb_u32[cap / 4] == PCI_EINVAL32) + break; + + cap_id = data.pcb_u32[cap / 4] & PCIE_EXT_CAP_ID_MASK; + nextcap = (data.pcb_u32[cap / 4] >> + PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK; + + cap_info = pcieadm_cfgspace_match_cap(cap_id, B_TRUE); + if (cap_info != NULL && cap_info->ppc_info != NULL) { + cap_info->ppc_info(&walk, cap_info, cap, &vers_info, + &read_len, &subcap); + } + + walk.pcw_caplen = read_len; + walk.pcw_capoff = cap; + + if (op == PCIEADM_CFGSPACE_OP_PRINT) { + pcieadm_cfgspace_print_cap(&walk, cap_id, cap_info, + vers_info, subcap); + } + + cap = nextcap; + ncaps++; + if (ncaps >= PCIE_EXT_CAP_MAX_PTR) { + errx(EXIT_FAILURE, "encountered more PCI capabilities " + "than fit in configuration space"); + } + } +} + +void +pcieadm_show_cfgspace_usage(FILE *f) +{ + (void) fprintf(f, "\tshow-cfgspace\t[-L] [-n] [-H] -d device | -f file " + "[filter...]\n"); + (void) fprintf(f, "\tshow-cfgspace\t-p -o field[,...] [-H] -d device | " + "-f file [filter...]\n"); +} + +static void +pcieadm_show_cfgspace_help(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + (void) fprintf(stderr, "\n"); + } + + (void) fprintf(stderr, "Usage: %s show-cfgspace [-L] [-n] [-H] -d " + "device | -f file [filter...]\n", pcieadm_progname); + (void) fprintf(stderr, " %s show-cfgspace -p -o field[,...] " + "[-H] -d device | -f file\n\t\t\t [filter...]\n", + pcieadm_progname); + + (void) fprintf(stderr, "\nPrint and decode PCI configuration space " + "data from a device or file. Each\n<filter> selects a given " + "capability, sub-capability, register, or field to print.\n\n" + "\t-d device\tread data from the specified device (driver instance," + "\n\t\t\t/devices path, or b/d/f)\n" + "\t-f file\t\tread data from the specified file\n" + "\t-L\t\tlist printable fields\n" + "\t-n\t\tshow printable short names\n" + "\t-H\t\tomit the column header (for -L and -p)\n" + "\t-p\t\tparsable output (requires -o)\n" + "\t-o field\toutput fields to print (required for -p)\n"); +} + +int +pcieadm_show_cfgspace(pcieadm_t *pcip, int argc, char *argv[]) +{ + int c, ret; + pcieadm_cfgspace_f readf; + void *readarg; + boolean_t list = B_FALSE, parse = B_FALSE; + const char *device = NULL, *file = NULL, *fields = NULL; + uint_t nfilts = 0; + pcieadm_cfgspace_filter_t *filts = NULL; + pcieadm_cfgspace_flags_t flags = 0; + uint_t oflags = 0; + ofmt_handle_t ofmt = NULL; + + while ((c = getopt(argc, argv, ":HLd:f:o:np")) != -1) { + switch (c) { + case 'd': + device = optarg; + break; + case 'L': + list = B_TRUE; + break; + case 'f': + file = optarg; + break; + case 'p': + parse = B_TRUE; + flags |= PCIEADM_CFGSPACE_F_PARSE; + oflags |= OFMT_PARSABLE; + break; + case 'n': + flags |= PCIEADM_CFGSPACE_F_SHORT; + break; + case 'H': + oflags |= OFMT_NOHEADER; + break; + case 'o': + fields = optarg; + break; + case ':': + pcieadm_show_cfgspace_help("Option -%c requires an " + "argument", optopt); + exit(EXIT_USAGE); + case '?': + default: + pcieadm_show_cfgspace_help("unknown option: -%c", + optopt); + exit(EXIT_USAGE); + } + } + + argc -= optind; + argv += optind; + + if (device == NULL && file == NULL) { + pcieadm_show_cfgspace_help("one of -d or -f must be specified"); + exit(EXIT_USAGE); + } + + if (device != NULL && file != NULL) { + pcieadm_show_cfgspace_help("only one of -d and -f must be " + "specified"); + exit(EXIT_USAGE); + } + + if (parse && fields == NULL) { + pcieadm_show_cfgspace_help("-p requires fields specified with " + "-o"); + exit(EXIT_USAGE); + } + + if (!parse && fields != NULL) { + pcieadm_show_cfgspace_help("-o can only be used with -p"); + exit(EXIT_USAGE); + } + + if ((oflags & OFMT_NOHEADER) && !(list || parse)) { + pcieadm_show_cfgspace_help("-H must be used with either -L or " + "-p"); + exit(EXIT_USAGE); + } + + if ((flags & PCIEADM_CFGSPACE_F_SHORT) && (list || parse)) { + pcieadm_show_cfgspace_help("-n cannot be used with either -L " + "or -p"); + exit(EXIT_USAGE); + } + + if (list && parse != 0) { + pcieadm_show_cfgspace_help("-L and -p cannot be used together"); + exit(EXIT_USAGE); + } + + if (list && fields != NULL) { + pcieadm_show_cfgspace_help("-L and -o cannot be used together"); + exit(EXIT_USAGE); + } + + if (list) { + fields = "short,human"; + } + + if (argc > 0) { + nfilts = argc; + filts = calloc(nfilts, sizeof (pcieadm_cfgspace_filter_t)); + + for (int i = 0; i < argc; i++) { + filts[i].pcf_string = argv[i]; + filts[i].pcf_len = strlen(argv[i]); + } + } + + if (list || parse) { + ofmt_status_t oferr; + oferr = ofmt_open(fields, pcieadm_cfgspace_ofmt, oflags, 0, + &ofmt); + ofmt_check(oferr, parse, ofmt, pcieadm_ofmt_errx, warnx); + } + + /* + * Initialize privileges that we require. For reading from the kernel + * we require all privileges. For a file, we just intersect with things + * that would allow someone to read from any file. + */ + if (device != NULL) { + /* + * We need full privileges if reading from a device, + * unfortunately. + */ + priv_fillset(pcip->pia_priv_eff); + } else { + VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_DAC_READ)); + VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_DAC_SEARCH)); + } + pcieadm_init_privs(pcip); + + if (device != NULL) { + pcieadm_find_dip(pcip, device); + pcieadm_init_cfgspace_kernel(pcip, &readf, &readarg); + } else { + pcip->pia_devstr = file; + pcieadm_init_cfgspace_file(pcip, file, &readf, &readarg); + } + pcieadm_cfgspace(pcip, PCIEADM_CFGSPACE_OP_PRINT, readf, -1, readarg, + nfilts, filts, flags, ofmt); + if (device != NULL) { + pcieadm_fini_cfgspace_kernel(readarg); + } else { + pcieadm_fini_cfgspace_file(readarg); + } + + ofmt_close(ofmt); + ret = EXIT_SUCCESS; + for (uint_t i = 0; i < nfilts; i++) { + if (!filts[i].pcf_used) { + warnx("filter '%s' did not match any fields", + filts[i].pcf_string); + ret = EXIT_FAILURE; + } + } + + return (ret); +} + +typedef struct pcieadm_save_cfgspace { + pcieadm_t *psc_pci; + int psc_dirfd; + uint_t psc_nsaved; + int psc_ret; +} pcieadm_save_cfgspace_t; + +static int +pcieadm_save_cfgspace_cb(di_node_t devi, void *arg) +{ + int fd, nregs, *regs; + pcieadm_save_cfgspace_t *psc = arg; + pcieadm_cfgspace_f readf; + void *readarg; + char fname[128]; + + psc->psc_pci->pia_devstr = di_node_name(devi); + psc->psc_pci->pia_devi = devi; + psc->psc_pci->pia_nexus = DI_NODE_NIL; + pcieadm_find_nexus(psc->psc_pci); + if (psc->psc_pci->pia_nexus == DI_NODE_NIL) { + warnx("failed to find nexus for %s", di_node_name(devi)); + psc->psc_ret = EXIT_FAILURE; + return (DI_WALK_CONTINUE); + } + + nregs = di_prop_lookup_ints(DDI_DEV_T_ANY, devi, "reg", ®s); + if (nregs <= 0) { + warnx("failed to lookup regs array for %s", + psc->psc_pci->pia_devstr); + psc->psc_ret = EXIT_FAILURE; + return (DI_WALK_CONTINUE); + } + + (void) snprintf(fname, sizeof (fname), "%02x-%02x-%02x.pci", + PCI_REG_BUS_G(regs[0]), PCI_REG_DEV_G(regs[0]), + PCI_REG_FUNC_G(regs[0])); + + if ((fd = openat(psc->psc_dirfd, fname, O_WRONLY | O_TRUNC | O_CREAT, + 0666)) < 0) { + warn("failed to create output file %s", fname); + psc->psc_ret = EXIT_FAILURE; + return (DI_WALK_CONTINUE); + } + + pcieadm_init_cfgspace_kernel(psc->psc_pci, &readf, &readarg); + pcieadm_cfgspace(psc->psc_pci, PCIEADM_CFGSPACE_OP_WRITE, readf, fd, + readarg, 0, NULL, 0, NULL); + pcieadm_fini_cfgspace_kernel(readarg); + + if (close(fd) != 0) { + warn("failed to close output fd for %s", fname); + psc->psc_ret = EXIT_FAILURE; + } else { + psc->psc_nsaved++; + } + + return (DI_WALK_CONTINUE); +} + +void +pcieadm_save_cfgspace_usage(FILE *f) +{ + (void) fprintf(f, "\tsave-devs\t-d device output-file\n"); + (void) fprintf(f, "\tsave-devs\t-a output-directory\n"); +} + +static void +pcieadm_save_cfgspace_help(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + (void) fprintf(stderr, "\n"); + } + + (void) fprintf(stderr, "Usage: %s save-cfgspace -d device " + "output-file\n", pcieadm_progname); + (void) fprintf(stderr, " %s save-cfgspace -a " + "output-directory\n", pcieadm_progname); + + (void) fprintf(stderr, "\nSave PCI configuration space data from a " + "device to a file or\nsave all devices to a specified directory." + "\n\n" + "\t-a\t\tsave data from all devices\n" + "\t-d device\tread data from the specified device (driver instance," + "\n\t\t\t/devices path, or b/d/f)\n"); +} + +int +pcieadm_save_cfgspace(pcieadm_t *pcip, int argc, char *argv[]) +{ + int c; + pcieadm_cfgspace_f readf; + void *readarg; + const char *device = NULL; + boolean_t do_all = B_FALSE; + + while ((c = getopt(argc, argv, ":ad:")) != -1) { + switch (c) { + case 'a': + do_all = B_TRUE; + break; + case 'd': + device = optarg; + break; + case ':': + pcieadm_save_cfgspace_help("Option -%c requires an " + "argument", optopt); + exit(EXIT_USAGE); + case '?': + default: + pcieadm_save_cfgspace_help("unknown option: -%c", + optopt); + exit(EXIT_USAGE); + } + } + + argc -= optind; + argv += optind; + + if (device == NULL && !do_all) { + pcieadm_save_cfgspace_help("missing required -d option to " + "indicate device to dump"); + exit(EXIT_USAGE); + } + + if (argc != 1) { + pcieadm_save_cfgspace_help("missing required output path"); + exit(EXIT_USAGE); + } + + /* + * For reading from devices, we need to full privileges, unfortunately. + */ + priv_fillset(pcip->pia_priv_eff); + pcieadm_init_privs(pcip); + + if (!do_all) { + int fd; + + pcieadm_find_dip(pcip, device); + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != + 0) { + err(EXIT_FAILURE, "failed to raise privileges"); + } + + if ((fd = open(argv[0], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < + 0) { + err(EXIT_FAILURE, "failed to open output file %s", + argv[0]); + } + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != + 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + pcieadm_init_cfgspace_kernel(pcip, &readf, &readarg); + pcieadm_cfgspace(pcip, PCIEADM_CFGSPACE_OP_WRITE, readf, fd, + readarg, 0, NULL, 0, NULL); + pcieadm_fini_cfgspace_kernel(readarg); + + if (close(fd) != 0) { + err(EXIT_FAILURE, "failed to close output file " + "descriptor"); + } + + return (EXIT_SUCCESS); + } else { + pcieadm_save_cfgspace_t psc; + pcieadm_di_walk_t walk; + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != + 0) { + err(EXIT_FAILURE, "failed to raise privileges"); + } + + if ((psc.psc_dirfd = open(argv[0], O_RDONLY | O_DIRECTORY)) < + 0) { + err(EXIT_FAILURE, "failed to open output directory %s", + argv[0]); + } + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != + 0) { + err(EXIT_FAILURE, "failed to reduce privileges"); + } + + psc.psc_nsaved = 0; + psc.psc_ret = EXIT_SUCCESS; + psc.psc_pci = pcip; + + walk.pdw_arg = &psc; + walk.pdw_func = pcieadm_save_cfgspace_cb; + pcieadm_di_walk(pcip, &walk); + + VERIFY0(close(psc.psc_dirfd)); + + if (psc.psc_nsaved == 0) { + warnx("failed to save any PCI devices"); + return (EXIT_FAILURE); + } + + pcieadm_print("successfully saved %u devices to %s\n", + psc.psc_nsaved, argv[0]); + return (psc.psc_ret); + } +} diff --git a/usr/src/cmd/pcieadm/pcieadm_devs.c b/usr/src/cmd/pcieadm/pcieadm_devs.c new file mode 100644 index 0000000000..5ca19ea1d9 --- /dev/null +++ b/usr/src/cmd/pcieadm/pcieadm_devs.c @@ -0,0 +1,568 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2021 Oxide Computer Company + */ + +#include <err.h> +#include <stdio.h> +#include <unistd.h> +#include <ofmt.h> +#include <strings.h> +#include <sys/pci.h> + +#include "pcieadm.h" + +typedef struct pcieadm_show_devs { + pcieadm_t *psd_pia; + ofmt_handle_t psd_ofmt; + boolean_t psd_funcs; + int psd_nfilts; + char **psd_filts; + uint_t psd_nprint; +} pcieadm_show_devs_t; + +typedef enum pcieadm_show_devs_otype { + PCIEADM_SDO_VID, + PCIEADM_SDO_DID, + PCIEADM_SDO_BDF, + PCIEADM_SDO_BDF_BUS, + PCIEADM_SDO_BDF_DEV, + PCIEADM_SDO_BDF_FUNC, + PCIEADM_SDO_DRIVER, + PCIEADM_SDO_TYPE, + PCIEADM_SDO_VENDOR, + PCIEADM_SDO_DEVICE, + PCIEADM_SDO_PATH, + PCIEADM_SDO_MAXSPEED, + PCIEADM_SDO_MAXWIDTH, + PCIEADM_SDO_CURSPEED, + PCIEADM_SDO_CURWIDTH, + PCIEADM_SDO_SUPSPEEDS +} pcieadm_show_devs_otype_t; + +typedef struct pcieadm_show_devs_ofmt { + int psdo_vid; + int psdo_did; + uint_t psdo_bus; + uint_t psdo_dev; + uint_t psdo_func; + const char *psdo_path; + const char *psdo_vendor; + const char *psdo_device; + const char *psdo_driver; + int psdo_instance; + int psdo_mwidth; + int psdo_cwidth; + int64_t psdo_mspeed; + int64_t psdo_cspeed; + int psdo_nspeeds; + int64_t *psdo_sspeeds; +} pcieadm_show_devs_ofmt_t; + +static uint_t +pcieadm_speed2gen(int64_t speed) +{ + if (speed == 2500000000LL) { + return (1); + } else if (speed == 5000000000LL) { + return (2); + } else if (speed == 8000000000LL) { + return (3); + } else if (speed == 16000000000LL) { + return (4); + } else if (speed == 32000000000LL) { + return (5); + } else { + return (0); + } +} + +static const char * +pcieadm_speed2str(int64_t speed) +{ + if (speed == 2500000000LL) { + return ("2.5"); + } else if (speed == 5000000000LL) { + return ("5.0"); + } else if (speed == 8000000000LL) { + return ("8.0"); + } else if (speed == 16000000000LL) { + return ("16.0"); + } else if (speed == 32000000000LL) { + return ("32.0"); + } else { + return (NULL); + } +} + +static boolean_t +pcieadm_show_devs_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) +{ + const char *str; + pcieadm_show_devs_ofmt_t *psdo = ofarg->ofmt_cbarg; + boolean_t first = B_TRUE; + + switch (ofarg->ofmt_id) { + case PCIEADM_SDO_BDF: + if (snprintf(buf, buflen, "%x/%x/%x", psdo->psdo_bus, + psdo->psdo_dev, psdo->psdo_func) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_BDF_BUS: + if (snprintf(buf, buflen, "%x", psdo->psdo_bus) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_BDF_DEV: + if (snprintf(buf, buflen, "%x", psdo->psdo_dev) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_BDF_FUNC: + if (snprintf(buf, buflen, "%x", psdo->psdo_func) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_DRIVER: + if (psdo->psdo_driver == NULL || psdo->psdo_instance == -1) { + (void) snprintf(buf, buflen, "--"); + } else if (snprintf(buf, buflen, "%s%d", psdo->psdo_driver, + psdo->psdo_instance) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_PATH: + if (strlcat(buf, psdo->psdo_path, buflen) >= buflen) { + return (B_TRUE); + } + break; + case PCIEADM_SDO_VID: + if (psdo->psdo_vid == -1) { + (void) strlcat(buf, "--", buflen); + } else if (snprintf(buf, buflen, "%x", psdo->psdo_vid) >= + buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_DID: + if (psdo->psdo_did == -1) { + (void) strlcat(buf, "--", buflen); + } else if (snprintf(buf, buflen, "%x", psdo->psdo_did) >= + buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_VENDOR: + if (strlcat(buf, psdo->psdo_vendor, buflen) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_DEVICE: + if (strlcat(buf, psdo->psdo_device, buflen) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_MAXWIDTH: + if (psdo->psdo_mwidth <= 0) { + (void) strlcat(buf, "--", buflen); + } else if (snprintf(buf, buflen, "x%u", psdo->psdo_mwidth) >= + buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_CURWIDTH: + if (psdo->psdo_cwidth <= 0) { + (void) strlcat(buf, "--", buflen); + } else if (snprintf(buf, buflen, "x%u", psdo->psdo_cwidth) >= + buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_MAXSPEED: + str = pcieadm_speed2str(psdo->psdo_mspeed); + if (str == NULL) { + (void) strlcat(buf, "--", buflen); + } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_CURSPEED: + str = pcieadm_speed2str(psdo->psdo_cspeed); + if (str == NULL) { + (void) strlcat(buf, "--", buflen); + } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) { + return (B_FALSE); + } + break; + case PCIEADM_SDO_SUPSPEEDS: + buf[0] = 0; + for (int i = 0; i < psdo->psdo_nspeeds; i++) { + const char *str; + + str = pcieadm_speed2str(psdo->psdo_sspeeds[i]); + if (str == NULL) { + continue; + } + + if (!first) { + if (strlcat(buf, ",", buflen) >= buflen) { + return (B_FALSE); + } + } + first = B_FALSE; + + if (strlcat(buf, str, buflen) >= buflen) { + return (B_FALSE); + } + } + break; + case PCIEADM_SDO_TYPE: + if (pcieadm_speed2gen(psdo->psdo_mspeed) == 0 || + psdo->psdo_mwidth == -1) { + if (strlcat(buf, "PCI", buflen) >= buflen) { + return (B_FALSE); + } + } else { + if (snprintf(buf, buflen, "PCIe Gen %ux%u", + pcieadm_speed2gen(psdo->psdo_mspeed), + psdo->psdo_mwidth) >= buflen) { + return (B_FALSE); + } + } + break; + default: + abort(); + } + return (B_TRUE); +} + +static const char *pcieadm_show_dev_fields = "bdf,type,driver,device"; +static const char *pcieadm_show_dev_speeds = + "bdf,driver,maxspeed,curspeed,maxwidth,curwidth,supspeeds"; +static const ofmt_field_t pcieadm_show_dev_ofmt[] = { + { "VID", 6, PCIEADM_SDO_VID, pcieadm_show_devs_ofmt_cb }, + { "DID", 6, PCIEADM_SDO_DID, pcieadm_show_devs_ofmt_cb }, + { "BDF", 8, PCIEADM_SDO_BDF, pcieadm_show_devs_ofmt_cb }, + { "DRIVER", 15, PCIEADM_SDO_DRIVER, pcieadm_show_devs_ofmt_cb }, + { "TYPE", 15, PCIEADM_SDO_TYPE, pcieadm_show_devs_ofmt_cb }, + { "VENDOR", 30, PCIEADM_SDO_VENDOR, pcieadm_show_devs_ofmt_cb }, + { "DEVICE", 30, PCIEADM_SDO_DEVICE, pcieadm_show_devs_ofmt_cb }, + { "PATH", 30, PCIEADM_SDO_PATH, pcieadm_show_devs_ofmt_cb }, + { "BUS", 4, PCIEADM_SDO_BDF_BUS, pcieadm_show_devs_ofmt_cb }, + { "DEV", 4, PCIEADM_SDO_BDF_DEV, pcieadm_show_devs_ofmt_cb }, + { "FUNC", 4, PCIEADM_SDO_BDF_FUNC, pcieadm_show_devs_ofmt_cb }, + { "MAXSPEED", 10, PCIEADM_SDO_MAXSPEED, pcieadm_show_devs_ofmt_cb }, + { "MAXWIDTH", 10, PCIEADM_SDO_MAXWIDTH, pcieadm_show_devs_ofmt_cb }, + { "CURSPEED", 10, PCIEADM_SDO_CURSPEED, pcieadm_show_devs_ofmt_cb }, + { "CURWIDTH", 10, PCIEADM_SDO_CURWIDTH, pcieadm_show_devs_ofmt_cb }, + { "SUPSPEEDS", 20, PCIEADM_SDO_SUPSPEEDS, pcieadm_show_devs_ofmt_cb }, + { NULL, 0, 0, NULL } +}; + +static boolean_t +pcieadm_show_devs_match(pcieadm_show_devs_t *psd, + pcieadm_show_devs_ofmt_t *psdo) +{ + char dinst[128], bdf[128]; + + if (psd->psd_nfilts == 0) { + return (B_TRUE); + } + + if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1) { + (void) snprintf(dinst, sizeof (dinst), "%s%d", + psdo->psdo_driver, psdo->psdo_instance); + } + (void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", psdo->psdo_bus, + psdo->psdo_dev, psdo->psdo_func); + + for (uint_t i = 0; i < psd->psd_nfilts; i++) { + const char *filt = psd->psd_filts[i]; + + if (strcmp(filt, psdo->psdo_path) == 0) { + return (B_TRUE); + } + + if (strcmp(filt, bdf) == 0) { + return (B_TRUE); + } + + if (psdo->psdo_driver != NULL && + strcmp(filt, psdo->psdo_driver) == 0) { + return (B_TRUE); + } + + if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1 && + strcmp(filt, dinst) == 0) { + return (B_TRUE); + } + + if (strncmp("/devices", filt, strlen("/devices")) == 0) { + filt += strlen("/devices"); + } + + if (strcmp(filt, psdo->psdo_path) == 0) { + return (B_TRUE); + } + } + return (B_FALSE); +} + +static int +pcieadm_show_devs_walk_cb(di_node_t node, void *arg) +{ + int nprop, *regs = NULL, *did, *vid, *mwidth, *cwidth; + int64_t *mspeed, *cspeed, *sspeeds; + char *path = NULL; + pcieadm_show_devs_t *psd = arg; + int ret = DI_WALK_CONTINUE; + pcieadm_show_devs_ofmt_t oarg; + pcidb_hdl_t *pcidb = psd->psd_pia->pia_pcidb; + + bzero(&oarg, sizeof (oarg)); + + path = di_devfs_path(node); + if (path == NULL) { + err(EXIT_FAILURE, "failed to construct devfs path for node: " + "%s (%s)", di_node_name(node)); + } + + nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®s); + if (nprop <= 0) { + errx(EXIT_FAILURE, "failed to lookup regs array for %s", + path); + } + + oarg.psdo_path = path; + oarg.psdo_bus = PCI_REG_BUS_G(regs[0]); + oarg.psdo_dev = PCI_REG_DEV_G(regs[0]); + oarg.psdo_func = PCI_REG_FUNC_G(regs[0]); + + if (oarg.psdo_func != 0 && !psd->psd_funcs) { + goto done; + } + + oarg.psdo_driver = di_driver_name(node); + oarg.psdo_instance = di_instance(node); + + nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did); + if (nprop != 1) { + oarg.psdo_did = -1; + } else { + oarg.psdo_did = (uint16_t)*did; + } + + nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid); + if (nprop != 1) { + oarg.psdo_vid = -1; + } else { + oarg.psdo_vid = (uint16_t)*vid; + } + + oarg.psdo_vendor = "--"; + if (oarg.psdo_vid != -1) { + pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb, + oarg.psdo_vid); + if (vend != NULL) { + oarg.psdo_vendor = pcidb_vendor_name(vend); + } + } + + oarg.psdo_device = "--"; + if (oarg.psdo_vid != -1 && oarg.psdo_did != -1) { + pcidb_device_t *dev = pcidb_lookup_device(pcidb, + oarg.psdo_vid, oarg.psdo_did); + if (dev != NULL) { + oarg.psdo_device = pcidb_device_name(dev); + + } + } + + nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "pcie-link-maximum-width", &mwidth); + if (nprop != 1) { + oarg.psdo_mwidth = -1; + } else { + oarg.psdo_mwidth = *mwidth; + } + + nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "pcie-link-current-width", &cwidth); + if (nprop != 1) { + oarg.psdo_cwidth = -1; + } else { + oarg.psdo_cwidth = *cwidth; + } + + nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, + "pcie-link-maximum-speed", &mspeed); + if (nprop != 1) { + oarg.psdo_mspeed = -1; + } else { + oarg.psdo_mspeed = *mspeed; + } + + nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, + "pcie-link-current-speed", &cspeed); + if (nprop != 1) { + oarg.psdo_cspeed = -1; + } else { + oarg.psdo_cspeed = *cspeed; + } + + nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, + "pcie-link-supported-speeds", &sspeeds); + if (nprop > 0) { + oarg.psdo_nspeeds = nprop; + oarg.psdo_sspeeds = sspeeds; + } else { + oarg.psdo_nspeeds = 0; + oarg.psdo_sspeeds = NULL; + } + + if (pcieadm_show_devs_match(psd, &oarg)) { + ofmt_print(psd->psd_ofmt, &oarg); + psd->psd_nprint++; + } + +done: + if (path != NULL) { + di_devfs_path_free(path); + } + + return (ret); +} + +void +pcieadm_show_devs_usage(FILE *f) +{ + (void) fprintf(f, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] " + "[filter...]\n"); +} + +static void +pcieadm_show_devs_help(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + (void) fprintf(stderr, "\n"); + } + + (void) fprintf(stderr, "Usage: %s show-devs [-F] [-H] [-s | -o " + "field[,...] [-p]] [filter...]\n", pcieadm_progname); + + (void) fprintf(stderr, "\nList PCI devices and functions in the " + "system. Each <filter> selects a set\nof devices to show and " + "can be a driver name, instance, /devices path, or\nb/d/f.\n\n" + "\t-F\t\tdo not display PCI functions\n" + "\t-H\t\tomit the column header\n" + "\t-o field\toutput fields to print\n" + "\t-p\t\tparsable output (requires -o)\n" + "\t-s\t\tlist speeds and widths\n"); + +} + +int +pcieadm_show_devs(pcieadm_t *pcip, int argc, char *argv[]) +{ + int c; + uint_t flags = 0; + const char *fields = NULL; + pcieadm_show_devs_t psd; + pcieadm_di_walk_t walk; + ofmt_status_t oferr; + boolean_t parse = B_FALSE; + boolean_t speeds = B_FALSE; + + /* + * show-devs relies solely on the devinfo snapshot we already took. + * Formalize our privs immediately. + */ + pcieadm_init_privs(pcip); + + bzero(&psd, sizeof (psd)); + psd.psd_pia = pcip; + psd.psd_funcs = B_TRUE; + + while ((c = getopt(argc, argv, ":FHo:ps")) != -1) { + switch (c) { + case 'F': + psd.psd_funcs = B_FALSE; + break; + case 'p': + parse = B_TRUE; + flags |= OFMT_PARSABLE; + break; + case 'H': + flags |= OFMT_NOHEADER; + break; + case 's': + speeds = B_TRUE; + break; + case 'o': + fields = optarg; + break; + case ':': + pcieadm_show_devs_help("option -%c requires an " + "argument", optopt); + exit(EXIT_USAGE); + case '?': + pcieadm_show_devs_help("unknown option: -%c", optopt); + exit(EXIT_USAGE); + } + } + + if (parse && fields == NULL) { + errx(EXIT_USAGE, "-p requires fields specified with -o"); + } + + if (fields != NULL && speeds) { + errx(EXIT_USAGE, "-s cannot be used with with -o"); + } + + if (fields == NULL) { + if (speeds) { + fields = pcieadm_show_dev_speeds; + } else { + fields = pcieadm_show_dev_fields; + } + } + + argc -= optind; + argv += optind; + + if (argc > 0) { + psd.psd_nfilts = argc; + psd.psd_filts = argv; + } + + oferr = ofmt_open(fields, pcieadm_show_dev_ofmt, flags, 0, + &psd.psd_ofmt); + ofmt_check(oferr, parse, psd.psd_ofmt, pcieadm_ofmt_errx, warnx); + + walk.pdw_arg = &psd; + walk.pdw_func = pcieadm_show_devs_walk_cb; + + pcieadm_di_walk(pcip, &walk); + + if (psd.psd_nprint > 0) { + return (EXIT_SUCCESS); + } else { + return (EXIT_FAILURE); + } +} diff --git a/usr/src/cmd/sendmail/src/daemon.c b/usr/src/cmd/sendmail/src/daemon.c index 983ad2fe3e..b048fc8ecf 100644 --- a/usr/src/cmd/sendmail/src/daemon.c +++ b/usr/src/cmd/sendmail/src/daemon.c @@ -4360,10 +4360,11 @@ anynet_ntoa(sap) (void) sm_snprintf(buf, sizeof(buf), "Family %d: ", sap->sa.sa_family); bp = &buf[strlen(buf)]; ap = sap->sa.sa_data; - for (l = sizeof(sap->sa.sa_data); --l >= 0; ) + for (l = sizeof(sap->sa.sa_data); --l >= 0 && SPACELEFT(buf, bp) > 3; ) { (void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:", *ap++ & 0377); + bp += 3; } *--bp = '\0'; diff --git a/usr/src/cmd/sgs/elfdump/Makefile.com b/usr/src/cmd/sgs/elfdump/Makefile.com index 59a1e75556..085591a246 100644 --- a/usr/src/cmd/sgs/elfdump/Makefile.com +++ b/usr/src/cmd/sgs/elfdump/Makefile.com @@ -57,7 +57,7 @@ LDFLAGS += $(VERSREF) $(MAPOPT) $(LLDFLAGS) LDLIBS += $(ELFLIBDIR) -lelf $(LDDBGLIBDIR) -llddbg \ $(CONVLIBDIR) -lconv -NATIVE_LDFLAGS = $(LDASSERTS) $(BDIRECT) $(ZASSERTDEFLIB)=libc.so +NATIVE_LDFLAGS = $(LDASSERTS) $(BDIRECT) CERRWARN += $(CNOWARN_UNINIT) diff --git a/usr/src/cmd/sgs/libld/common/syms.c b/usr/src/cmd/sgs/libld/common/syms.c index a40f8ae5cd..ff073c9aa7 100644 --- a/usr/src/cmd/sgs/libld/common/syms.c +++ b/usr/src/cmd/sgs/libld/common/syms.c @@ -1718,8 +1718,9 @@ ld_sym_validate(Ofl_desc *ofl) sym->st_name) && (st_insert(ofl->ofl_strtab, sdp->sd_name) == -1)) return (S_ERROR); - if (allow_ldynsym && sym->st_name && - ldynsym_symtype[type]) { + if (allow_ldynsym && ldynsym_symtype[type] && + ((sym->st_name != 0) || + (type == STT_FILE))) { ofl->ofl_dynscopecnt++; if (st_insert(ofl->ofl_dynstrtab, sdp->sd_name) == -1) @@ -2452,8 +2453,14 @@ ld_sym_process(Is_desc *isc, Ifl_desc *ifl, Ofl_desc *ofl) sdp->sd_name) == -1)) return (S_ERROR); - if (allow_ldynsym && sym->st_name && - ldynsym_symtype[type]) { + /* + * STT_FILE symbols must always remain, to + * maintain the ordering semantics of symbol + * tables. + */ + if (allow_ldynsym && ldynsym_symtype[type] && + ((sym->st_name != 0) || + (type == STT_FILE))) { ofl->ofl_dynlocscnt++; if (st_insert(ofl->ofl_dynstrtab, sdp->sd_name) == -1) diff --git a/usr/src/cmd/sgs/libld/common/update.c b/usr/src/cmd/sgs/libld/common/update.c index d61d4cfcd5..8ef1be8343 100644 --- a/usr/src/cmd/sgs/libld/common/update.c +++ b/usr/src/cmd/sgs/libld/common/update.c @@ -702,9 +702,11 @@ update_osym(Ofl_desc *ofl) enter_in_symtab = symtab && (!(ofl->ofl_flags & FLG_OF_REDLSYM) || sdp->sd_move); - enter_in_ldynsym = ldynsym && sdp->sd_name && + enter_in_ldynsym = ldynsym && + ((sym->st_name != 0) || (type == STT_FILE)) && ldynsym_symtype[type] && !(ofl->ofl_flags & FLG_OF_REDLSYM); + _symshndx = NULL; if (enter_in_symtab) { diff --git a/usr/src/cmd/sgs/tools/Makefile.com b/usr/src/cmd/sgs/tools/Makefile.com index 634fec820a..9d1c34949c 100644 --- a/usr/src/cmd/sgs/tools/Makefile.com +++ b/usr/src/cmd/sgs/tools/Makefile.com @@ -42,7 +42,7 @@ include $(SRC)/cmd/sgs/Makefile.com OBJECTS= piglatin.o NATIVECC_CFLAGS = -O -NATIVE_LDFLAGS = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) +NATIVE_LDFLAGS = $(LDASSERTS) $(BDIRECT) NATIVE= $(OBJECTS:%.o=%) SRCS= $(OBJECTS:%.o=../common/%.c) diff --git a/usr/src/cmd/sgs/tools/SUNWonld-README b/usr/src/cmd/sgs/tools/SUNWonld-README index a414a6bfe8..d29de0aa81 100644 --- a/usr/src/cmd/sgs/tools/SUNWonld-README +++ b/usr/src/cmd/sgs/tools/SUNWonld-README @@ -1670,3 +1670,4 @@ Bugid Risk Synopsis 11057 hidden undefined weak symbols should not leave relocations 11067 debug statistics crash ld(1) when -z allextract 13481 ld(1) should skip GCC local aliases when building symsort sections +13684 ld aborts when input object has no file name diff --git a/usr/src/contrib/mDNSResponder/Clients/dns-sd.c b/usr/src/contrib/mDNSResponder/Clients/dns-sd.c index b9fe853ce8..3a3cc0d82a 100644 --- a/usr/src/contrib/mDNSResponder/Clients/dns-sd.c +++ b/usr/src/contrib/mDNSResponder/Clients/dns-sd.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2015 Apple Inc. All rights reserved. + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. * ("Apple") in consideration of your agreement to the following terms, and your @@ -64,6 +64,14 @@ #include <errno.h> // For errno, EINTR #include <time.h> #include <sys/types.h> // For u_char +#ifdef APPLE_OSX_mDNSResponder +#include <inttypes.h> // For PRId64 +#endif // APPLE_OSX_mDNSResponder + +#if APPLE_OSX_mDNSResponder +#include <xpc/xpc.h> +#include "xpc_clients.h" +#endif #ifdef _WIN32 #include <winsock2.h> @@ -165,8 +173,10 @@ static const char kFilePathSep = '/'; #undef _DNS_SD_LIBDISPATCH #endif #include "dns_sd.h" -#include "dns_sd_internal.h" +#include "dns_sd_private.h" #include "ClientCommon.h" +#include <stdarg.h> + #if TEST_NEW_CLIENTSTUB #include "../mDNSShared/dnssd_ipc.c" @@ -174,6 +184,9 @@ static const char kFilePathSep = '/'; #include "../mDNSShared/dnssd_clientstub.c" #endif +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif //************************************************************************************************************* // Globals @@ -309,6 +322,8 @@ static uint16_t GetRRType(const char *s) else if (!strcasecmp(s, "ds" )) return(kDNSServiceType_DS); else if (!strcasecmp(s, "rrsig" )) return(kDNSServiceType_RRSIG); else if (!strcasecmp(s, "nsec" )) return(kDNSServiceType_NSEC); + else if (!strcasecmp(s, "SVCB" )) return(kDNSServiceType_SVCB); + else if (!strcasecmp(s, "HTTPS" )) return(kDNSServiceType_HTTPS); else if (!strcasecmp(s, "ANY" )) return(kDNSServiceType_ANY); else return(atoi(s)); } @@ -380,6 +395,8 @@ static char *DNSTypeName(unsigned short rr_type) case kDNSServiceType_AXFR: return("AXFR"); case kDNSServiceType_MAILB: return("MAILB"); case kDNSServiceType_MAILA: return("MAILA"); + case kDNSServiceType_SVCB: return("SVCB"); + case kDNSServiceType_HTTPS: return("HTTPS"); case kDNSServiceType_ANY: return("ANY"); default: { @@ -509,49 +526,50 @@ static void FormatTime(unsigned long te, unsigned char *buf, int bufsize) static void print_usage(const char *arg0, int print_all) { // Print the commonly used command line options. These are listed in "the order they have been in historically". - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", arg0); - fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", arg0); - fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", arg0); - fprintf(stderr, "%s -B <Type> <Domain> (Browse for service instances)\n", arg0); - fprintf(stderr, "%s -L <Name> <Type> <Domain> (Resolve a service instance)\n", arg0); - fprintf(stderr, "%s -Q <name> <rrtype> <rrclass> (Generic query for any record type)\n", arg0); - fprintf(stderr, "%s -Z <Type> <Domain> (Output results in Zone File format)\n", arg0); - fprintf(stderr, "%s -G v4/v6/v4v6 <name> (Get address information for hostname)\n", arg0); - fprintf(stderr, "%s -H (Print usage for complete command list)\n", arg0); - fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", arg0); + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", arg0); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", arg0); + fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", arg0); + fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Register Proxy)\n", arg0); + fprintf(stderr, "%s -B <Type> <Domain> (Browse for service instances)\n", arg0); + fprintf(stderr, "%s -Z <Type> <Domain> (Output results in Zone File format)\n", arg0); + fprintf(stderr, "%s -L <Name> <Type> <Domain> (Resolve (‘lookup’) a service instance)\n", arg0); + fprintf(stderr, "%s -Q <name> <rrtype> <rrclass> (Generic query for any record type)\n", arg0); + fprintf(stderr, "%s -q <name> <rrtype> <rrclass> (Generic query, using SuppressUnusable)\n", arg0); + fprintf(stderr, "%s -G v4/v6/v4v6 <hostname> (Get address information for hostname)\n", arg0); + fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL> (NAT Port Mapping)\n", arg0); + fprintf(stderr, "%s -H (Print usage for complete command list)\n", arg0); + fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", arg0); +#ifdef APPLE_OSX_mDNSResponder + fprintf(stderr, "%s -O [-compress|-stdout](Dump the state of mDNSResponder to file / STDOUT)\n", arg0); +#endif // APPLE_OSX_mDNSResponder if (print_all) // Print all available options for dns-sd tool. Keep these in alphabetical order for easier maintenance. { fprintf(stderr, "\n"); - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", arg0); - fprintf(stderr, "%s -C <FQDN> <rrtype> <rrclass> (Query; reconfirming each result)\n", arg0); - fprintf(stderr, "%s -D <name> <rrtype> <rrclass>(Validate query for any record type with DNSSEC)\n", arg0); - fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", arg0); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", arg0); - fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Proxy)\n", arg0); - fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", arg0); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); - fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL> (NAT Port Mapping)\n", arg0); - fprintf(stderr, "%s -ble (Use kDNSServiceInterfaceIndexBLE)\n", arg0); - fprintf(stderr, "%s -g v4/v6/v4v6 <name> (Validate address info for hostname with DNSSEC)\n", arg0); - fprintf(stderr, "%s -i <Interface> (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0); - fprintf(stderr, "%s -includep2p (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0); - fprintf(stderr, "%s -includeAWDL (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0); - fprintf(stderr, "%s -intermediates (Set kDNSServiceFlagsReturnIntermediates flag)\n", arg0); - fprintf(stderr, "%s -ku (Set kDNSServiceFlagsKnownUnique flag)\n", arg0); - fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); - fprintf(stderr, "%s -optional (Set kDNSServiceFlagsValidateOptional flag)\n", arg0); - fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); - fprintf(stderr, "%s -q <name> <rrtype> <rrclass> (Equivalent to -Q with kDNSServiceFlagsSuppressUnusable set)\n", arg0); - fprintf(stderr, "%s -tc (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0); - fprintf(stderr, "%s -test (Run basic API input range tests)\n", arg0); - fprintf(stderr, "%s -t1 (Set kDNSServiceFlagsThresholdOne flag)\n", arg0); - fprintf(stderr, "%s -tFinder (Set kDNSServiceFlagsThresholdFinder flag)\n", arg0); - fprintf(stderr, "%s -timeout (Set kDNSServiceFlagsTimeout flag)\n", arg0); - fprintf(stderr, "%s -unicastResponse (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0); - fprintf(stderr, "%s -autoTrigger (Set kDNSServiceFlagsAutoTrigger flag)\n", arg0); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", arg0); + fprintf(stderr, "%s -C <name> <rrtype> <rrclass> (Query; reconfirming each result)\n", arg0); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", arg0); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", arg0); + fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", arg0); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); + fprintf(stderr, "%s -ble (Use kDNSServiceInterfaceIndexBLE)\n", arg0); + fprintf(stderr, "%s -i <Interface> (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0); + fprintf(stderr, "%s -includep2p (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0); + fprintf(stderr, "%s -includeAWDL (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0); + fprintf(stderr, "%s -intermediates (Set kDNSServiceFlagsReturnIntermediates flag)\n", arg0); + fprintf(stderr, "%s -ku (Set kDNSServiceFlagsKnownUnique flag)\n", arg0); + fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); + fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); + fprintf(stderr, "%s -tc (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0); + fprintf(stderr, "%s -test (Run basic API input range tests)\n", arg0); + fprintf(stderr, "%s -t1 (Set kDNSServiceFlagsThresholdOne flag)\n", arg0); + fprintf(stderr, "%s -tFinder (Set kDNSServiceFlagsThresholdFinder flag)\n", arg0); + fprintf(stderr, "%s -timeout (Set kDNSServiceFlagsTimeout flag)\n", arg0); + fprintf(stderr, "%s -unicastResponse (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0); + fprintf(stderr, "%s -autoTrigger (Set kDNSServiceFlagsAutoTrigger flag)\n", arg0); + fprintf(stderr, "%s -enableDNSSEC (Enable DNSSEC validation for the '-Q' query)\n", arg0); } } @@ -896,15 +914,29 @@ static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } +static int snprintf_safe(char *str, size_t size, const char *format, ...) +{ + int length = 0; + va_list ptr; + va_start(ptr, format); + int result = vsnprintf(str, size, format, ptr); + va_end(ptr); + if (result > 0 && size > 0) + { + length = MIN((size_t)result, size-1); + } + return length; +} + // Output the wire-format domainname pointed to by rd static int snprintd(char *p, int max, const unsigned char **rd) { const char *const buf = p; const char *const end = p + max; - while (**rd) - { - p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); - *rd += 1 + **rd; + while (**rd) + { + p += snprintf_safe(p, end-p, "%.*s.", **rd, *rd+1); + *rd += 1 + **rd; } *rd += 1; // Advance over the final zero byte return(p-buf); @@ -920,18 +952,18 @@ static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, size_t rdb_size, unsi unsigned char *ptr; int i; rdataDS *rrds = (rdataDS *)rd; - p += snprintf(p, rdb + rdb_size - p, "%d %d %d ", + p += snprintf_safe(p, rdb + rdb_size - p, "%d %d %d ", rrds->alg, swap16(rrds->keyTag), rrds->digestType); ptr = (unsigned char *)(rd + DS_FIXED_SIZE); for (i = 0; i < (rdlen - DS_FIXED_SIZE); i++) - p += snprintf(p, rdb + rdb_size - p, "%x", ptr[i]); - break; - } - + p += snprintf_safe(p, rdb + rdb_size - p, "%x", ptr[i]); + break; + } + case kDNSServiceType_DNSKEY: { rdataDNSKey *rrkey = (rdataDNSKey *)rd; - p += snprintf(p, rdb + rdb_size - p, "%d %d %d %u ", swap16(rrkey->flags), rrkey->proto, + p += snprintf_safe(p, rdb + rdb_size - p, "%d %d %d %u ", swap16(rrkey->flags), rrkey->proto, rrkey->alg, (unsigned int)keytag((unsigned char *)rrkey, rdlen)); base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + DNSKEY_FIXED_SIZE), rdlen - DNSKEY_FIXED_SIZE); break; @@ -979,7 +1011,7 @@ static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, size_t rdb_size, unsi for (i = 0; i < wlen * 8; i++) { if (bmap[i>>3] & (128 >> (i&7))) - p += snprintf(p, rdb + rdb_size - p, " %s ", DNSTypeName(type + i)); + p += snprintf_safe(p, rdb + rdb_size - p, " %s ", DNSTypeName(type + i)); } bmap += wlen; bitmaplen -= wlen; @@ -1003,8 +1035,8 @@ static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, size_t rdb_size, unsi inceptClock = (unsigned long)swap32(rrsig->sigInceptTime); FormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf)); - - p += snprintf(p, rdb + rdb_size - p, " %-7s %d %d %d %s %s %7d ", + + p += snprintf_safe(p, rdb + rdb_size - p, " %-7s %d %d %d %s %s %7d ", DNSTypeName(swap16(rrsig->typeCovered)), rrsig->alg, rrsig->labels, swap32(rrsig->origTTL), expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag)); @@ -1035,8 +1067,14 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, int unknowntype = 0; char dnssec_status[15] = "Unknown"; char rr_type[RR_TYPE_SIZE]; - char rr_class[3]; + char rr_class[6]; DNSServiceFlags check_flags = flags;//local flags for dnssec status checking + int8_t enable_dnssec = ((check_flags & kDNSServiceFlagsEnableDNSSEC) != 0); + static int8_t enabled_dnssec_before = -1; + + if (enabled_dnssec_before == -1) { + enabled_dnssec_before = enable_dnssec; + } (void)sdref; // Unused (void)ifIndex; // Unused @@ -1046,10 +1084,7 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, if (num_printed++ == 0) { - if (operation == 'D') - printf("Timestamp A/R if %-30s%-6s%-7s%-18s Rdata\n", "Name", "Type", "Class", "DNSSECStatus"); - else - printf("Timestamp A/R Flags if %-30s%-6s%-7s Rdata\n", "Name", "Type", "Class"); + printf("Timestamp A/R Flags if %-30s%-6s%-7s%s Rdata\n", "Name", "Type", "Class", enable_dnssec ? " DNSSECResult " : ""); } printtimestamp(); @@ -1066,75 +1101,69 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, if (!errorCode) //to avoid printing garbage in rdata { - if (!(check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional))) + switch (rrtype) { - switch (rrtype) - { - case kDNSServiceType_A: - snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); - break; - - case kDNSServiceType_NS: - case kDNSServiceType_CNAME: - case kDNSServiceType_PTR: - case kDNSServiceType_DNAME: - snprintd(p, sizeof(rdb), &rd); - break; - - case kDNSServiceType_SOA: - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname - p += snprintf(p, rdb + sizeof(rdb) - p, " "); - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname - snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", - ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); - break; - - case kDNSServiceType_AAAA: - snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", - rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], - rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); - break; - - case kDNSServiceType_SRV: - p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port - ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); - rd += 6; - snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host - break; - - case kDNSServiceType_DS: - case kDNSServiceType_DNSKEY: - case kDNSServiceType_NSEC: - case kDNSServiceType_RRSIG: - ParseDNSSECRecords(rrtype, rdb, sizeof(rdb), rd, rdlen); - break; - - default: - snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); - unknowntype = 1; - break; - } - } - else - { - strncpy(rdb, "----", sizeof(rdb)); - //Clear all o/p bits, and then check for dnssec status - check_flags &= ~kDNSServiceOutputFlags; - if (check_flags & kDNSServiceFlagsSecure) - strncpy(dnssec_status, "Secure", sizeof(dnssec_status)); - else if (check_flags & kDNSServiceFlagsInsecure) - strncpy(dnssec_status, "Insecure", sizeof(dnssec_status)); - else if (check_flags & kDNSServiceFlagsIndeterminate) - strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status)); - else if (check_flags & kDNSServiceFlagsBogus) - strncpy(dnssec_status, "Bogus", sizeof(dnssec_status)); + case kDNSServiceType_A: + snprintf_safe(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); + break; + + case kDNSServiceType_NS: + case kDNSServiceType_CNAME: + case kDNSServiceType_PTR: + case kDNSServiceType_DNAME: + snprintd(p, sizeof(rdb), &rd); + break; + + case kDNSServiceType_SOA: + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname + p += snprintf_safe(p, rdb + sizeof(rdb) - p, " "); + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname + snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", + ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); + break; + + case kDNSServiceType_AAAA: + snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], + rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); + break; + + case kDNSServiceType_SRV: + p += snprintf_safe(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port + ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); + rd += 6; + snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host + break; + + case kDNSServiceType_DS: + case kDNSServiceType_DNSKEY: + case kDNSServiceType_NSEC: + case kDNSServiceType_RRSIG: + ParseDNSSECRecords(rrtype, rdb, sizeof(rdb), rd, rdlen); + break; + + default: + snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); + unknowntype = 1; + break; } } - if (operation == 'D') - printf("%s%3d %-30s%-6s%-7s%-18s %s", op, ifIndex, fullname, rr_type, rr_class, dnssec_status, rdb); + if (check_flags & kDNSServiceFlagsSecure) + strncpy(dnssec_status, "Secure ", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsInsecure) + strncpy(dnssec_status, "Insecure ", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsIndeterminate) + strncpy(dnssec_status, "Indeterminate ", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsBogus) + strncpy(dnssec_status, "Bogus ", sizeof(dnssec_status)); else - printf("%s%9X%3d %-30s%-7s%-6s %s", op, flags, ifIndex, fullname, rr_type, rr_class, rdb); + strncpy(dnssec_status, " ", sizeof(dnssec_status)); + + printf("%s%9X%3d %-30s%-7s%-6s %s%s", + op, flags, ifIndex, fullname, rr_type, rr_class, enabled_dnssec_before ? dnssec_status : "", rdb); + + if (unknowntype) { while (rd < end) @@ -1144,6 +1173,8 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, { if (errorCode == kDNSServiceErr_NoSuchRecord) printf(" No Such Record"); + else if (errorCode == kDNSServiceErr_NoAuth) + printf(" No Authorization"); else if (errorCode == kDNSServiceErr_Timeout) { printf(" No Such Record\n"); @@ -1191,15 +1222,13 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags DNSServiceFlags check_flags = flags; (void) sdref; (void) context; + unsigned char enable_dnssec = ((check_flags & kDNSServiceFlagsEnableDNSSEC) != 0); EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); if (num_printed++ == 0) { - if (operation == 'g') - printf("Timestamp A/R if %-25s %-44s %-18s\n", "Hostname", "Address", "DNSSECStatus"); - else - printf("Timestamp A/R Flags if %-38s %-44s %s\n", "Hostname", "Address", "TTL"); + printf("Timestamp A/R Flags if %-38s %-44s %s%s\n", "Hostname", "Address", "TTL", enable_dnssec ? "DNSSECResult" : ""); } printtimestamp(); @@ -1220,26 +1249,20 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name); } - //go through this only if you have a dnssec validation status - if (!errorCode && (check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional))) + if (enable_dnssec) { - strncpy(addr, "----", sizeof(addr)); - //Clear all o/p bits, and then check for dnssec status - check_flags &= ~kDNSServiceOutputFlags; if (check_flags & kDNSServiceFlagsSecure) - strncpy(dnssec_status, "Secure", sizeof(dnssec_status)); + strncpy(dnssec_status, " Secure", sizeof(dnssec_status)); else if (check_flags & kDNSServiceFlagsInsecure) - strncpy(dnssec_status, "Insecure", sizeof(dnssec_status)); + strncpy(dnssec_status, " Insecure", sizeof(dnssec_status)); else if (check_flags & kDNSServiceFlagsIndeterminate) - strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status)); + strncpy(dnssec_status, " Indeterminate", sizeof(dnssec_status)); else if (check_flags & kDNSServiceFlagsBogus) - strncpy(dnssec_status, "Bogus", sizeof(dnssec_status)); + strncpy(dnssec_status, " Bogus", sizeof(dnssec_status)); } - if (operation == 'g') - printf("%s%3d %-25s %-44s %-18s", op, interfaceIndex, hostname, addr, dnssec_status); - else - printf("%s%9X%3d %-38s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); + printf("%s%9X%3d %-38s %-44s %d%s", op, flags, interfaceIndex, hostname, addr, ttl, enable_dnssec ? dnssec_status : ""); + if (errorCode) { if (errorCode == kDNSServiceErr_NoSuchRecord) @@ -1401,12 +1424,14 @@ static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const #define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1])) +#define MAXTXTRecordSize 8900 static DNSServiceErrorType RegisterService(DNSServiceRef *sdref, const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags) { uint16_t PortAsNumber = atoi(port); Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; - unsigned char txt[2048] = ""; + unsigned char txt[MAXTXTRecordSize]; + txt[0] = '\0'; unsigned char *ptr = txt; int i; @@ -1422,9 +1447,13 @@ static DNSServiceErrorType RegisterService(DNSServiceRef *sdref, for (i = 0; i < argc; i++) { const char *p = argv[i]; + if (ptr >= txt + sizeof(txt)) + return kDNSServiceErr_BadParam; *ptr = 0; - while (*p && *ptr < 255 && ptr + 1 + *ptr < txt+sizeof(txt)) + while (*p && *ptr < 255) { + if (ptr + 1 + *ptr >= txt + sizeof(txt)) + return kDNSServiceErr_BadParam; if (p[0] != '\\' || p[1] == 0) { ptr[++*ptr] = *p; p+=1; } else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; } else { ptr[++*ptr] = p[1]; p+=2; } @@ -1836,13 +1865,16 @@ static int API_input_range_test() return 0; } +#ifdef APPLE_OSX_mDNSResponder +static void handle_state_dump_request(uint8_t if_compress_state_dump, uint8_t if_dump_to_stdout); +#endif // APPLE_OSX_mDNSResponder int main(int argc, char **argv) { DNSServiceErrorType err; char buffer[TypeBufferSize], *typ, *dom; int opi; DNSServiceFlags flags = 0; - int optional = 0; + unsigned char enable_dnssec = 0; // Extract the program name from argv[0], which by convention contains the path to this executable. // Note that this is just a voluntary convention, not enforced by the kernel -- @@ -2005,12 +2037,12 @@ int main(int argc, char **argv) printf("Setting kDNSServiceFlagsAutoTrigger\n"); } - if (argc > 1 && !strcasecmp(argv[1], "-optional")) + if (argc > 1 && !strcasecmp(argv[1], "-enableDNSSEC")) { argc--; argv++; - optional = 1; - printf("Setting DNSSEC optional flag\n"); + enable_dnssec = 1; + printf("Enable DNSSEC validation for the '-Q' query\n"); } if (argc > 2 && !strcmp(argv[1], "-i")) @@ -2099,7 +2131,6 @@ int main(int argc, char **argv) err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags); break; - case 'D': case 'q': case 'Q': case 'C': { @@ -2107,20 +2138,14 @@ int main(int argc, char **argv) flags |= kDNSServiceFlagsReturnIntermediates; if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable; + if (enable_dnssec) + flags |= kDNSServiceFlagsEnableDNSSEC; if (argc < opi+1) goto Fail; rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]); rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : GetRRClass(argv[opi+2]); if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; - if (operation == 'D') - { - flags |= kDNSServiceFlagsSuppressUnusable; - if (optional) - flags |= kDNSServiceFlagsValidateOptional; - else - flags |= kDNSServiceFlagsValidate; - } err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL); break; } @@ -2184,19 +2209,10 @@ int main(int argc, char **argv) break; } - case 'g': case 'G': { flags |= kDNSServiceFlagsReturnIntermediates; - if (operation == 'g') - { - flags |= kDNSServiceFlagsSuppressUnusable; - if (optional) - flags |= kDNSServiceFlagsValidateOptional; - else - flags |= kDNSServiceFlagsValidate; - } - if (argc != opi+2) + if (argc != opi+2) goto Fail; else err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); @@ -2245,11 +2261,43 @@ int main(int argc, char **argv) else printf("Currently running daemon (system service) is version %d.%d.%d\n", v / 10000, v / 100 % 100, v % 100); exit(0); } +#ifdef APPLE_OSX_mDNSResponder + case 'O': { + // check if the user specifies the flag "-compress" + uint8_t if_compress_state_dump = 0; + uint8_t if_dump_to_stdout = 0; + + if (argc > opi+1) { + printf("dns-sd: illegal option count\n"); + goto Fail; + } + + if (argc == opi+1) { + const char *param = argv[opi]; + if (strcasecmp("-compress", param) == 0) { + if_compress_state_dump = 1; + } else if (strcasecmp("-stdout", param) == 0) { + if_dump_to_stdout = 1; + } else { + printf("dns-sd: illegal option %s \n", param); + goto Fail; + } + } + handle_state_dump_request(if_compress_state_dump, if_dump_to_stdout); + err = kDNSServiceErr_NoError; + break; + } +#endif // APPLE_OSX_mDNSResponder case 'H': goto Fail; default: goto Fail; } +#ifdef APPLE_OSX_mDNSResponder + // state dump does not need to create DNSServiceRef, so we can return directly here without cleaning up. + if (operation == 'O') + return 0; +#endif // APPLE_OSX_mDNSResponder if (!client || err != kDNSServiceErr_NoError) { @@ -2270,8 +2318,72 @@ Fail: if (operation == 'H') print_usage(a0,1); else print_usage(a0,0); return 0; +} + +#ifdef APPLE_OSX_mDNSResponder +/* + * if_compress_state_dump and if_dump_to_stdout cannot be set at the same time. + */ +static void handle_state_dump_request(uint8_t if_compress_state_dump, uint8_t if_dump_to_stdout) +{ + // create xpc connection to the xpc server for log utility + xpc_connection_t log_utility_connection = xpc_connection_create_mach_service(kDNSLogUtilityService, dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + xpc_connection_set_event_handler(log_utility_connection, ^(xpc_object_t event){ + printf("Connecting to %s, status: %s\n", kDNSLogUtilityService, xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + }); + xpc_connection_resume(log_utility_connection); + + // set option for the state dump + xpc_object_t xpc_dict = xpc_dictionary_create(NULL, NULL, 0); + uint64_t dump_option; + if (if_compress_state_dump) { + dump_option = full_state_with_compression; + } + else if (if_dump_to_stdout) { + // we pass the stdout directly to xpc server + dump_option = full_state_to_stdout; + xpc_dictionary_set_fd(xpc_dict, kDNSStateDumpFD, STDOUT_FILENO); + } + else { + dump_option = full_state; + } + + xpc_dictionary_set_uint64(xpc_dict, kDNSStateDump, dump_option); + + // send the request and handle the response from xpc server + xpc_connection_send_message_with_reply(log_utility_connection, xpc_dict, dispatch_get_main_queue(), ^(xpc_object_t recv_msg){ + xpc_type_t msg_type = xpc_get_type(recv_msg); + + if (msg_type != XPC_TYPE_DICTIONARY) { + printf("Received unexpected reply from daemon, error: \"%s\"\nUnexpected reply Contents:\n%s\n", + xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION), xpc_copy_description(recv_msg)); + exit(1); + } + + // get the response dictionary + uint32_t return_code = (uint32_t)xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply); + if (return_code != kDNSMsg_NoError) { + const char *error_description = xpc_dictionary_get_string(recv_msg, kDNSErrorDescription); + printf("XPC service returns error, description: %s\n", error_description); + exit(1); + } + + // print the state information returned from the XPC server + if (dump_option != full_state_to_stdout) { + const char *path = xpc_dictionary_get_string(recv_msg, kDNSDumpFilePath); + printf("State Dump Is Saved to: %s\n", path); + } + + int64_t time_used = xpc_dictionary_get_int64(recv_msg, kDNSStateDumpTimeUsed); + printf(" Time Used: %" PRId64 " ms\n", time_used); + xpc_release(xpc_dict); + exit(0); + }); + + dispatch_main(); } +#endif // APPLE_OSX_mDNSResponder // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" diff --git a/usr/src/contrib/mDNSResponder/README b/usr/src/contrib/mDNSResponder/README index 73c8188fba..33d3d0b263 100644 --- a/usr/src/contrib/mDNSResponder/README +++ b/usr/src/contrib/mDNSResponder/README @@ -24,23 +24,24 @@ The mdns vendor source repository is at https://github.com/illumos/mdns/. +Updated from upstream version mDNSResponder-1310.80.1 Updated from upstream version mDNSResponder-878.260.1 Updated from upstream version mDNSResponder-878.1.1 Updated from upstream version mDNSResponder-625.41.2 Updated from upstream version mDNSResponder-576.30.4 -Multicast DNS and Service Discovery support in Solaris using the +Multicast DNS and Service Discovery support in illumos using the Apple Bonjour source code (v107.6). Apple Bonjour source can be downloaded from: - http://developer.apple.com/networking/bonjour/download/ + https://opensource.apple.com/tarballs/mDNSResponder/ The following components are integrated from the Apple Bonjour -source in Solaris: +source in illumos: libdns_sd: usr/src/lib/libdns_sd <dns_sd.h> mdnsd: usr/src/cmd/cmd-inet/usr.lib/mdnsd dns-sd: usr/src/cmd/cmd-inet/usr.bin/dns-sd Following fixes have been made to the Apple Bonjour source -integrated in Solaris: +integrated in illumos: * 64-bit support by adding pad bytes in ipc_msg_hdr_struct * 64-bit support in libjdns_sd, dnssd.jar (JNISupport.c, DNSSD.java) * mdnsd switches to user 'noaccess' and not 'nobody' after init diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.c b/usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.c deleted file mode 100644 index 0008405ddd..0000000000 --- a/usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.c +++ /dev/null @@ -1,280 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2011-2012 Apple Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// *************************************************************************** -// CryptoAlg.c: -// Interface to DNSSEC cryptographic algorithms. The crypto support itself is -// provided by the platform and the functions in this file just provide an -// interface to access them in a more generic way. -// *************************************************************************** - -#include "mDNSEmbeddedAPI.h" -#include "CryptoAlg.h" - -AlgFuncs *DigestAlgFuncs[DIGEST_TYPE_MAX]; -AlgFuncs *CryptoAlgFuncs[CRYPTO_ALG_MAX]; -AlgFuncs *EncAlgFuncs[ENC_ALG_MAX]; - -mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func) -{ - if (digestType >= DIGEST_TYPE_MAX) - { - LogMsg("DigestAlgInit: digestType %d exceeds bounds", digestType); - return mStatus_BadParamErr; - } - // As digestTypes may not be consecutive, check for specific digest types - // that we support - if (digestType != SHA1_DIGEST_TYPE && - digestType != SHA256_DIGEST_TYPE) - { - LogMsg("DigestAlgInit: digestType %d not supported", digestType); - return mStatus_BadParamErr; - } - DigestAlgFuncs[digestType] = func; - return mStatus_NoError; -} - -mDNSexport mStatus CryptoAlgInit(mDNSu8 alg, AlgFuncs *func) -{ - if (alg >= CRYPTO_ALG_MAX) - { - LogMsg("CryptoAlgInit: alg %d exceeds bounds", alg); - return mStatus_BadParamErr; - } - // As algs may not be consecutive, check for specific algorithms - // that we support - if (alg != CRYPTO_RSA_SHA1 && alg != CRYPTO_RSA_SHA256 && alg != CRYPTO_RSA_SHA512 && - alg != CRYPTO_DSA_NSEC3_SHA1 && alg != CRYPTO_RSA_NSEC3_SHA1) - { - LogMsg("CryptoAlgInit: alg %d not supported", alg); - return mStatus_BadParamErr; - } - - CryptoAlgFuncs[alg] = func; - return mStatus_NoError; -} - -mDNSexport mStatus EncAlgInit(mDNSu8 alg, AlgFuncs *func) -{ - if (alg >= ENC_ALG_MAX) - { - LogMsg("EncAlgInit: alg %d exceeds bounds", alg); - return mStatus_BadParamErr; - } - - // As algs may not be consecutive, check for specific algorithms - // that we support - if (alg != ENC_BASE32 && alg != ENC_BASE64) - { - LogMsg("EncAlgInit: alg %d not supported", alg); - return mStatus_BadParamErr; - } - - EncAlgFuncs[alg] = func; - return mStatus_NoError; -} - -mDNSexport AlgContext *AlgCreate(AlgType type, mDNSu8 alg) -{ - AlgFuncs *func = mDNSNULL; - AlgContext *ctx; - - if (type == CRYPTO_ALG) - { - if (alg >= CRYPTO_ALG_MAX) return mDNSNULL; - func = CryptoAlgFuncs[alg]; - } - else if (type == DIGEST_ALG) - { - if (alg >= DIGEST_TYPE_MAX) return mDNSNULL; - func = DigestAlgFuncs[alg]; - } - else if (type == ENC_ALG) - { - if (alg >= ENC_ALG_MAX) return mDNSNULL; - func = EncAlgFuncs[alg]; - } - - if (!func) - { - // If there is no support from the platform, this case can happen. - LogInfo("AlgCreate: func is NULL"); - return mDNSNULL; - } - - if (func->Create) - { - mStatus err; - ctx = mDNSPlatformMemAllocate(sizeof(AlgContext)); - if (!ctx) return mDNSNULL; - // Create expects ctx->alg to be initialized - ctx->alg = alg; - err = func->Create(ctx); - if (err == mStatus_NoError) - { - ctx->type = type; - return ctx; - } - mDNSPlatformMemFree(ctx); - } - return mDNSNULL; -} - -mDNSexport mStatus AlgDestroy(AlgContext *ctx) -{ - AlgFuncs *func = mDNSNULL; - - if (ctx->type == CRYPTO_ALG) - func = CryptoAlgFuncs[ctx->alg]; - else if (ctx->type == DIGEST_ALG) - func = DigestAlgFuncs[ctx->alg]; - else if (ctx->type == ENC_ALG) - func = EncAlgFuncs[ctx->alg]; - - if (!func) - { - LogMsg("AlgDestroy: ERROR!! func is NULL"); - mDNSPlatformMemFree(ctx); - return mStatus_BadParamErr; - } - - if (func->Destroy) - func->Destroy(ctx); - - mDNSPlatformMemFree(ctx); - return mStatus_NoError; -} - -mDNSexport mDNSu32 AlgLength(AlgContext *ctx) -{ - AlgFuncs *func = mDNSNULL; - - if (ctx->type == CRYPTO_ALG) - func = CryptoAlgFuncs[ctx->alg]; - else if (ctx->type == DIGEST_ALG) - func = DigestAlgFuncs[ctx->alg]; - else if (ctx->type == ENC_ALG) - func = EncAlgFuncs[ctx->alg]; - - // This should never happen as AlgCreate would have failed - if (!func) - { - LogMsg("AlgLength: ERROR!! func is NULL"); - return 0; - } - - if (func->Length) - return (func->Length(ctx)); - else - return 0; -} - -mDNSexport mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len) -{ - AlgFuncs *func = mDNSNULL; - - if (ctx->type == CRYPTO_ALG) - func = CryptoAlgFuncs[ctx->alg]; - else if (ctx->type == DIGEST_ALG) - func = DigestAlgFuncs[ctx->alg]; - else if (ctx->type == ENC_ALG) - func = EncAlgFuncs[ctx->alg]; - - // This should never happen as AlgCreate would have failed - if (!func) - { - LogMsg("AlgAdd: ERROR!! func is NULL"); - return mStatus_BadParamErr; - } - - if (func->Add) - return (func->Add(ctx, data, len)); - else - return mStatus_BadParamErr; -} - -mDNSexport mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) -{ - AlgFuncs *func = mDNSNULL; - - if (ctx->type == CRYPTO_ALG) - func = CryptoAlgFuncs[ctx->alg]; - else if (ctx->type == DIGEST_ALG) - func = DigestAlgFuncs[ctx->alg]; - else if (ctx->type == ENC_ALG) - func = EncAlgFuncs[ctx->alg]; - - // This should never happen as AlgCreate would have failed - if (!func) - { - LogMsg("AlgVerify: ERROR!! func is NULL"); - return mStatus_BadParamErr; - } - - if (func->Verify) - return (func->Verify(ctx, key, keylen, signature, siglen)); - else - return mStatus_BadParamErr; -} - -mDNSexport mDNSu8* AlgEncode(AlgContext *ctx) -{ - AlgFuncs *func = mDNSNULL; - - if (ctx->type == CRYPTO_ALG) - func = CryptoAlgFuncs[ctx->alg]; - else if (ctx->type == DIGEST_ALG) - func = DigestAlgFuncs[ctx->alg]; - else if (ctx->type == ENC_ALG) - func = EncAlgFuncs[ctx->alg]; - - // This should never happen as AlgCreate would have failed - if (!func) - { - LogMsg("AlgEncode: ERROR!! func is NULL"); - return mDNSNULL; - } - - if (func->Encode) - return (func->Encode(ctx)); - else - return mDNSNULL; -} - -mDNSexport mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len) -{ - AlgFuncs *func = mDNSNULL; - - if (ctx->type == CRYPTO_ALG) - func = CryptoAlgFuncs[ctx->alg]; - else if (ctx->type == DIGEST_ALG) - func = DigestAlgFuncs[ctx->alg]; - else if (ctx->type == ENC_ALG) - func = EncAlgFuncs[ctx->alg]; - - // This should never happen as AlgCreate would have failed - if (!func) - { - LogMsg("AlgEncode: ERROR!! func is NULL"); - return mDNSNULL; - } - - if (func->Final) - return (func->Final(ctx, data, len)); - else - return mStatus_BadParamErr; -} diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.h b/usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.h deleted file mode 100644 index c21507e4b1..0000000000 --- a/usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2011-2012 Apple Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __CRYPTO_ALG_H -#define __CRYPTO_ALG_H - -typedef enum -{ - CRYPTO_ALG, - DIGEST_ALG, - ENC_ALG, -} AlgType; - -typedef struct -{ - void *context; - AlgType type; - mDNSu8 alg; -} AlgContext; - -typedef struct -{ - mStatus (*Create)(AlgContext *ctx); - mStatus (*Destroy)(AlgContext *ctx); - mDNSu32 (*Length)(AlgContext *ctx); - mStatus (*Add)(AlgContext *ctx, const void *data, mDNSu32 len); - // Verify the ctx using the key and compare it against signature/siglen - mStatus (*Verify)(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); - // Encode the data and return the encoded data - mDNSu8* (*Encode)(AlgContext *ctx); - // Return the finalized data in data whose length is len (used by hash algorithms) - mStatus (*Final)(AlgContext *ctx, void *data, mDNSu32 len); -} AlgFuncs; - -mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func); -mDNSexport mStatus CryptoAlgInit(mDNSu8 algType, AlgFuncs *func); -mDNSexport mStatus EncAlgInit(mDNSu8 algType, AlgFuncs *func); - - -extern AlgContext *AlgCreate(AlgType type, mDNSu8 alg); -extern mStatus AlgDestroy(AlgContext *ctx); -extern mDNSu32 AlgLength(AlgContext *ctx); -extern mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len); -extern mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); -extern mDNSu8* AlgEncode(AlgContext *ctx); -extern mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len); - -#endif // __CRYPTO_ALG_H diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.c b/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.c index 35d4e2649c..057981dcfc 100644 --- a/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.c +++ b/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.c @@ -1,6 +1,6 @@ -/* -*- Mode: C; tab-width: 4 -*- +/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*- * - * Copyright (c) 2002-2018 Apple Inc. All rights reserved. + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,13 @@ * limitations under the License. */ +#ifndef STANDALONE // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary #define mDNS_InstantiateInlines 1 #include "DNSCommon.h" -#include "CryptoAlg.h" -#include "anonymous.h" - -#ifdef UNIT_TEST -#include "unittest.h" -#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#include "dnssec_v2.h" +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) // Disable certain benign warnings with Microsoft compilers #if (defined(_MSC_VER)) @@ -44,10 +42,9 @@ mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1; mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2; -mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3; -mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4; -mDNSexport const mDNSInterfaceID uDNSInterfaceMark = (mDNSInterfaceID)-5; -mDNSexport const mDNSInterfaceID mDNSInterface_BLE = (mDNSInterfaceID)-6; +mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-3; +mDNSexport const mDNSInterfaceID uDNSInterfaceMark = (mDNSInterfaceID)-4; +mDNSexport const mDNSInterfaceID mDNSInterface_BLE = (mDNSInterfaceID)-5; // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP @@ -108,16 +105,15 @@ mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } }; mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } }; -mDNSexport const mDNSOpaque16 DNSSecQFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, kDNSFlag1_CD } }; mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; -mDNSexport const mDNSOpaque16 SubscribeFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Subscribe, 0 } }; -mDNSexport const mDNSOpaque16 UnSubscribeFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_UnSubscribe, 0 } }; mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } }; mDNSexport const mDNSOpaque128 zeroOpaque128 = { { 0 } }; +extern mDNS mDNSStorage; + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -132,6 +128,21 @@ mDNSexport mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr) (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16 } +mDNSexport const char *DNSScopeToString(mDNSu32 scope) +{ + switch (scope) + { + case kScopeNone: + return "Unscoped"; + case kScopeInterfaceID: + return "InterfaceScoped"; + case kScopeServiceID: + return "ServiceScoped"; + default: + return "Unknown"; + } +} + mDNSexport void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out) { out->l[0] = 0; @@ -200,6 +211,8 @@ mDNSexport char *DNSTypeName(mDNSu16 rrtype) case kDNSType_RRSIG: return("RRSIG"); case kDNSType_DNSKEY: return("DNSKEY"); case kDNSType_DS: return("DS"); + case kDNSType_SVCB: return("SVCB"); + case kDNSType_HTTPS: return("HTTPS"); case kDNSQType_ANY: return("ANY"); default: { static char buffer[16]; @@ -209,36 +222,23 @@ mDNSexport char *DNSTypeName(mDNSu16 rrtype) } } -mDNSlocal char *DNSSECAlgName(mDNSu8 alg) +mDNSexport const char *mStatusDescription(mStatus error) { - switch (alg) - { - case CRYPTO_RSA_SHA1: return "RSA_SHA1"; - case CRYPTO_DSA_NSEC3_SHA1: return "DSA_NSEC3_SHA1"; - case CRYPTO_RSA_NSEC3_SHA1: return "RSA_NSEC3_SHA1"; - case CRYPTO_RSA_SHA256: return "RSA_SHA256"; - case CRYPTO_RSA_SHA512: return "RSA_SHA512"; - default: { - static char algbuffer[16]; - mDNS_snprintf(algbuffer, sizeof(algbuffer), "ALG%d", alg); - return(algbuffer); - } - } -} + const char *error_description; + switch (error) { + case mStatus_NoError: + error_description = "mStatus_NoError"; + break; + case mStatus_BadParamErr: + error_description = "mStatus_BadParamErr"; + break; -mDNSlocal char *DNSSECDigestName(mDNSu8 digest) -{ - switch (digest) - { - case SHA1_DIGEST_TYPE: return "SHA1"; - case SHA256_DIGEST_TYPE: return "SHA256"; - default: - { - static char digbuffer[16]; - mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest); - return(digbuffer); - } + default: + error_description = "mStatus_UnknownDescription"; + break; } + + return error_description; } mDNSexport mDNSu32 swap32(mDNSu32 x) @@ -253,53 +253,6 @@ mDNSexport mDNSu16 swap16(mDNSu16 x) return (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); } -// RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted -// explicitly on the wire. -// -// Note: This just helps narrow down the list of keys to look at. It is possible -// for two DNS keys to have the same ID i.e., key ID is not a unqiue tag. We ignore -// MD5 keys. -// -// 1st argument - the RDATA part of the DNSKEY RR -// 2nd argument - the RDLENGTH -// -mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize) -{ - unsigned long ac; - unsigned int i; - - for (ac = 0, i = 0; i < keysize; ++i) - ac += (i & 1) ? key[i] : key[i] << 8; - ac += (ac >> 16) & 0xFFFF; - return ac & 0xFFFF; -} - -mDNSexport int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg) -{ - AlgContext *ctx; - mDNSu8 *outputBuffer; - int length; - - ctx = AlgCreate(ENC_ALG, encAlg); - if (!ctx) - { - LogMsg("baseEncode: AlgCreate failed\n"); - return 0; - } - AlgAdd(ctx, data, len); - outputBuffer = AlgEncode(ctx); - length = 0; - if (outputBuffer) - { - // Note: don't include any spaces in the format string below. This - // is also used by NSEC3 code for proving non-existence where it - // needs the base32 encoding without any spaces etc. - length = mDNS_snprintf(buffer, blen, "%s", outputBuffer); - } - AlgDestroy(ctx); - return length; -} - mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const buffer, mDNSu32 length) { int win, wlen, type; @@ -338,36 +291,6 @@ mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const bu } } -// Parse the fields beyond the base header. NSEC3 should have been validated. -mDNSexport void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap) -{ - const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; - rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data; - mDNSu8 *p = (mDNSu8 *)&nsec3->salt; - int hlen; - - if (salt) - { - if (nsec3->saltLength) - *salt = p; - else - *salt = mDNSNULL; - } - p += nsec3->saltLength; - // p is pointing at hashLength - hlen = (int)*p; - if (hashLength) - *hashLength = hlen; - p++; - if (nxtName) - *nxtName = p; - p += hlen; - if (bitmaplen) - *bitmaplen = rr->rdlength - (int)(p - rdb->data); - if (bitmap) - *bitmap = p; -} - // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as // long as this routine is only used for debugging messages, it probably isn't a big problem. @@ -396,12 +319,27 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD case kDNSType_HINFO: // Display this the same as TXT (show all constituent strings) case kDNSType_TXT: { const mDNSu8 *t = rd->txt.c; - while (t < rd->txt.c + rr->rdlength) + const mDNSu8 *const rdLimit = rd->data + rr->rdlength; + const char *separator = ""; + + while (t < rdLimit) { - length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t); - t += 1 + t[0]; + mDNSu32 characterStrLength = *t; + if (characterStrLength + 1 > (mDNSu32)(rdLimit - t)) // Character string goes out of boundary. + { + const mDNSu8 *const remainderStart = t + 1; + const mDNSu32 remainderLength = (mDNSu32)(rdLimit - remainderStart); + length += mDNS_snprintf(buffer + length, RemSpc, "%s%.*s<<OUT OF BOUNDARY CHARACTER STRING>>", separator, + remainderLength, remainderStart); + (void)length; // Acknowledge "dead store" analyzer warning. + break; + } + length += mDNS_snprintf(buffer+length, RemSpc, "%s%.*s", separator, characterStrLength, t + 1); + separator = "¦"; + t += 1 + characterStrLength; } - } break; + } + break; case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break; case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s", @@ -465,95 +403,13 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD } break; - case kDNSType_NSEC3: { - rdataNSEC3 *nsec3 = (rdataNSEC3 *)rd->data; - const mDNSu8 *p = (mDNSu8 *)&nsec3->salt; - int hashLength, bitmaplen, i; - - length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %d %d ", - DNSSECDigestName(nsec3->alg), nsec3->flags, swap16(nsec3->iterations)); - - if (!nsec3->saltLength) - { - length += mDNS_snprintf(buffer+length, RemSpc, "-"); - } - else - { - for (i = 0; i < nsec3->saltLength; i++) - { - length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]); - } - } - - // put a space at the end - length += mDNS_snprintf(buffer+length, RemSpc, " "); - - p += nsec3->saltLength; - // p is pointing at hashLength - hashLength = (int)*p++; - - length += baseEncode(buffer + length, RemSpc, p, hashLength, ENC_BASE32); - - // put a space at the end - length += mDNS_snprintf(buffer+length, RemSpc, " "); - - p += hashLength; - bitmaplen = rr->rdlength - (int)(p - rd->data); - PrintTypeBitmap(p, bitmaplen, buffer, length); - } - break; - case kDNSType_RRSIG: { - rdataRRSig *rrsig = (rdataRRSig *)rd->data; - mDNSu8 expTimeBuf[64]; - mDNSu8 inceptTimeBuf[64]; - unsigned long inceptClock; - unsigned long expClock; - int len; - - expClock = (unsigned long)swap32(rrsig->sigExpireTime); - mDNSPlatformFormatTime(expClock, expTimeBuf, sizeof(expTimeBuf)); - - inceptClock = (unsigned long)swap32(rrsig->sigInceptTime); - mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf)); - - length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s ", - DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL), - expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), rrsig->signerName); - - len = DomainNameLength((domainname *)&rrsig->signerName); - baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE), - rr->rdlength - (len + RRSIG_FIXED_SIZE), ENC_BASE64); - } - break; - case kDNSType_DNSKEY: { - rdataDNSKey *rrkey = (rdataDNSKey *)rd->data; - length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u ", swap16(rrkey->flags), rrkey->proto, - DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength)); - baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE), - rr->rdlength - DNSKEY_FIXED_SIZE, ENC_BASE64); - } - break; - case kDNSType_DS: { - mDNSu8 *p; - int i; - rdataDS *rrds = (rdataDS *)rd->data; - - length += mDNS_snprintf(buffer+length, RemSpc, "\t%s\t%d\t%s ", DNSSECAlgName(rrds->alg), swap16(rrds->keyTag), - DNSSECDigestName(rrds->digestType)); - - p = (mDNSu8 *)(rd->data + DS_FIXED_SIZE); - for (i = 0; i < (rr->rdlength - DS_FIXED_SIZE); i++) - { - length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]); - } - } - break; default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %.*s", rr->rdlength, rr->rdlength, rd->data); // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.'; break; } + return(buffer); } @@ -994,7 +850,7 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, // In the case where there is no name (and ONLY in that case), // a single-label subtype is allowed as the first label of a three-part "type" - if (!name && type) + if (!name) { const mDNSu8 *s0 = type->c; if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) @@ -1160,57 +1016,6 @@ mDNSexport mStatus DNSNameToLowerCase(domainname *d, domainname *result) return mStatus_NoError; } -mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen, - const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen) -{ - AlgContext *ctx; - unsigned int i; - unsigned int iterations; - domainname lname; - mDNSu8 *p = (mDNSu8 *)&nsec3->salt; - const mDNSu8 *digest; - int digestlen; - mDNSBool first = mDNStrue; - - if (DNSNameToLowerCase((domainname *)name, &lname) != mStatus_NoError) - { - LogMsg("NSEC3HashName: ERROR!! DNSNameToLowerCase failed"); - return mDNSNULL; - } - - digest = lname.c; - digestlen = DomainNameLength(&lname); - - // Note that it is "i <=". The first iteration is for digesting the name and salt. - // The iteration count does not include that. - iterations = swap16(nsec3->iterations); - for (i = 0; i <= iterations; i++) - { - ctx = AlgCreate(DIGEST_ALG, nsec3->alg); - if (!ctx) - { - LogMsg("NSEC3HashName: ERROR!! Cannot allocate context"); - return mDNSNULL; - } - - AlgAdd(ctx, digest, digestlen); - if (nsec3->saltLength) - AlgAdd(ctx, p, nsec3->saltLength); - if (AnonDataLen) - AlgAdd(ctx, AnonData, AnonDataLen); - if (first) - { - first = mDNSfalse; - digest = hash; - digestlen = AlgLength(ctx); - } - AlgFinal(ctx, (void *)digest, digestlen); - AlgDestroy(ctx); - } - *dlen = digestlen; - return digest; -} - // Notes on UTF-8: // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F // 10xxxxxx is a continuation byte of a multi-byte character @@ -1391,8 +1196,11 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->resrec.rrtype = rrtype; rr->resrec.rrclass = kDNSClass_IN; rr->resrec.rroriginalttl = ttl; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + rr->resrec.dnsservice = NULL; +#else rr->resrec.rDNSServer = mDNSNULL; - rr->resrec.AnonInfo = mDNSNULL; +#endif // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal // rr->resrec.rdestimate = set in mDNS_Register_internal // rr->resrec.rdata = MUST be set by client @@ -1456,7 +1264,6 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I { q->InterfaceID = InterfaceID; q->flags = 0; - q->Target = zeroAddr; AssignDomainName(&q->qname, name); q->qtype = qtype; q->qclass = kDNSClass_IN; @@ -1465,20 +1272,14 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNSfalse; q->SuppressUnusable = mDNSfalse; - q->SearchListIndex = 0; q->AppendSearchDomains = 0; - q->RetryWithSearchDomains = mDNSfalse; q->TimeoutQuestion = 0; q->WakeOnResolve = 0; - q->UseBackgroundTrafficClass = mDNSfalse; - q->ValidationRequired = 0; - q->ValidatingResponse = 0; + q->UseBackgroundTraffic = mDNSfalse; q->ProxyQuestion = 0; - q->qnameOrig = mDNSNULL; - q->AnonInfo = mDNSNULL; q->pid = mDNSPlatformGetPID(); q->euid = 0; - q->DisallowPID = mDNSfalse; + q->BlockedByPolicy = mDNSfalse; q->ServiceID = -1; q->QuestionCallback = callback; q->QuestionContext = context; @@ -1532,6 +1333,7 @@ mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr) sum = DomainNameHashValue((domainname *)rdb->data); ptr += dlen; len -= dlen; + /* FALLTHROUGH */ } /* FALLTHROUGH */ @@ -1698,70 +1500,6 @@ mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu1 return !RRAssertsExistence(rr, type); } -// Checks whether the RRSIG or NSEC record answers the question "q". -mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q, mDNSBool *checkType) -{ - *checkType = mDNStrue; - - // This function is called for all questions and as long as the type matches, - // return true. For the types (RRSIG and NSEC) that are specifically checked in - // this function, returning true still holds good. - if (q->qtype == rr->rrtype) - return mDNStrue; - - // For DS and DNSKEY questions, the types should match i.e., don't answer using CNAME - // records as it answers any question type. - // - // - DS record comes from the parent zone where CNAME record cannot coexist and hence - // cannot possibly answer it. - // - // - For DNSKEY, one could potentially follow CNAME but there could be a DNSKEY at - // the "qname" itself. To keep it simple, we don't follow CNAME. - - if ((q->qtype == kDNSType_DS || q->qtype == kDNSType_DNSKEY) && (q->qtype != rr->rrtype)) - { - debugf("DNSSECRecordAnswersQuestion: %d type resource record matched question %##s (%s), ignoring", rr->rrtype, - q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; - } - - // If we are validating a response using DNSSEC, we might already have the records - // for the "q->qtype" in the cache but we issued a query with DO bit set - // to get the RRSIGs e.g., if you have two questions one of which does not require - // DNSSEC validation. When the RRSIG is added to the cache, we need to deliver - // the response to the question. The RRSIG type won't match the q->qtype and hence - // we need to bypass the check in that case. - if (rr->rrtype == kDNSType_RRSIG && q->ValidatingResponse) - { - const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; - rdataRRSig *rrsig = (rdataRRSig *)rdb->data; - mDNSu16 typeCovered = swap16(rrsig->typeCovered); - debugf("DNSSECRecordAnswersQuestion: Matching RRSIG typeCovered %s", DNSTypeName(typeCovered)); - if (typeCovered != kDNSType_CNAME && typeCovered != q->qtype) - { - debugf("DNSSECRecordAnswersQuestion: RRSIG did not match question %##s (%s)", q->qname.c, - DNSTypeName(q->qtype)); - return mDNSfalse; - } - LogInfo("DNSSECRecordAnswersQuestion: RRSIG matched question %##s (%s)", q->qname.c, - DNSTypeName(q->qtype)); - *checkType = mDNSfalse; - return mDNStrue; - } - // If the NSEC record asserts the non-existence of a name looked up by the question, we would - // typically answer that e.g., the bitmap asserts that q->qtype does not exist. If we have - // to prove the non-existence as required by ValidatingResponse and ValidationRequired question, - // then we should not answer that as it may not be the right one always. We may need more than - // one NSEC to prove the non-existence. - if (rr->rrtype == kDNSType_NSEC && DNSSECQuestion(q)) - { - debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (NSEC)", q->qname.c, - DNSTypeName(q->qtype), rr->name->c); - return mDNSfalse; - } - return mDNStrue; -} - // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question. // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call. // SameDomainName() is generally cheap when the names don't match, but expensive when they do match, @@ -1769,7 +1507,7 @@ mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, c // In cases where we know in advance that the names match it's especially advantageous to skip the // SameDomainName() call because that's precisely the time when it's most expensive and least useful. -mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +mDNSlocal mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, mDNSBool isAuthRecord, const DNSQuestion *const q) { mDNSBool checkType = mDNStrue; @@ -1780,7 +1518,7 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); return mDNSfalse; } - if (QuerySuppressed(q)) + if (q->Suppressed) return mDNSfalse; if (rr->InterfaceID && @@ -1788,12 +1526,16 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr rr->InterfaceID != q->InterfaceID) return(mDNSfalse); // Resource record received via unicast, the resolver group ID should match ? - if (!rr->InterfaceID) + if (!isAuthRecord && !rr->InterfaceID) { - mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0); - mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0); + if (mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (rr->dnsservice != q->dnsservice) return(mDNSfalse); +#else + const mDNSu32 idr = rr->rDNSServer ? rr->rDNSServer->resGroupID : 0; + const mDNSu32 idq = q->qDNSServer ? q->qDNSServer->resGroupID : 0; if (idr != idq) return(mDNSfalse); - if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse; +#endif } // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question @@ -1802,7 +1544,11 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr // CNAME answers question of any type and a negative cache record should not prevent us from querying other // valid types at the same name. if (rr->rrtype == kDNSType_CNAME && rr->RecordType == kDNSRecordTypePacketNegative && rr->rrtype != q->qtype) - return mDNSfalse; + return mDNSfalse; + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + if (enables_dnssec_validation(q) && record_type_answers_dnssec_question(rr, q->qtype)) checkType = mDNSfalse; +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); @@ -1813,20 +1559,37 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr return mDNSfalse; #endif // APPLE_OSX_mDNSResponder - if (!AnonInfoAnswersQuestion(rr, q)) - return mDNSfalse; - return(mDNStrue); } -mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +mDNSexport mDNSBool SameNameCacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q) +{ + return SameNameRecordAnswersQuestion(&cr->resrec, mDNSfalse, q); +} + +mDNSlocal mDNSBool RecordAnswersQuestion(const ResourceRecord *const rr, mDNSBool isAuthRecord, const DNSQuestion *const q) { - if (!SameNameRecordAnswersQuestion(rr, q)) + if (!SameNameRecordAnswersQuestion(rr, isAuthRecord, q)) return mDNSfalse; return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); } +mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + return RecordAnswersQuestion(rr, mDNSfalse, q); +} + +mDNSexport mDNSBool AuthRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q) +{ + return RecordAnswersQuestion(&ar->resrec, mDNStrue, q); +} + +mDNSexport mDNSBool CacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q) +{ + return RecordAnswersQuestion(&cr->resrec, mDNSfalse, q); +} + // We have a separate function to handle LocalOnly AuthRecords because they can be created with // a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike // multicast resource records (which has a valid InterfaceID) which can't be used to answer @@ -1852,12 +1615,11 @@ mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const D // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly, // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against // the InterfaceID in the resource record. - // - // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any. if (rr->InterfaceID && - q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast && - rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + q->InterfaceID != mDNSInterface_LocalOnly && + ((q->InterfaceID && rr->InterfaceID != q->InterfaceID) || + (!q->InterfaceID && !LocalOnlyOrP2PInterface(rr->InterfaceID)))) return(mDNSfalse); // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set @@ -1900,14 +1662,12 @@ mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const D if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); - if (!AnonInfoAnswersQuestion(rr, q)) - return mDNSfalse; - return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); } -mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q) { + const ResourceRecord *const rr = &ar->resrec; // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records // are handled in LocalOnlyRecordAnswersQuestion if (LocalOnlyOrP2PInterface(rr->InterfaceID)) @@ -1924,9 +1684,16 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, // both the DNSServers are assumed to be NULL in that case if (!rr->InterfaceID) { - mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0); - mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (rr->dnsservice != q->dnsservice) return(mDNSfalse); +#else + const mDNSu32 idr = rr->rDNSServer ? rr->rDNSServer->resGroupID : 0; + const mDNSu32 idq = q->qDNSServer ? q->qDNSServer->resGroupID : 0; if (idr != idq) return(mDNSfalse); +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (!mDNSPlatformValidRecordForInterface(ar, q->InterfaceID)) return(mDNSfalse); +#endif } // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question @@ -1934,9 +1701,6 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); - if (!AnonInfoAnswersQuestion(rr, q)) - return mDNSfalse; - return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); } @@ -1949,7 +1713,7 @@ mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *c { mDNSBool checkType = mDNStrue; - if (QuerySuppressed(q)) + if (q->Suppressed) return mDNSfalse; // For resource records created using multicast, the InterfaceIDs have to match @@ -1959,7 +1723,9 @@ mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *c // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); - if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse; +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + if (enables_dnssec_validation(q) && record_type_answers_dnssec_question(rr, q->qtype)) checkType = mDNSfalse; +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); @@ -2002,6 +1768,7 @@ mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate case kDNSType_RT: case kDNSType_KX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name)); + case kDNSType_MINFO: case kDNSType_RP: return (mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) + CompressedDomainNameLength(&rd->rp.txt, name)); @@ -2096,6 +1863,8 @@ mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSO h->numAdditionals = 0; } +#endif // !STANDALONE + mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname) { const mDNSu8 *result = end - *domname - 1; @@ -2202,6 +1971,8 @@ mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, return(ptr); } +#ifndef STANDALONE + mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) { ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); @@ -2433,7 +2204,8 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update) -mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) +mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, + const ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) { mDNSu8 *endofrdata; mDNSu16 actualLength; @@ -2442,13 +2214,17 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 * if (rr->RecordType == kDNSRecordTypeUnregistered) { - LogMsg("PutResourceRecordTTLWithLimit ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "Attempt to put kDNSRecordTypeUnregistered " PRI_DM_NAME " (" PUB_S ")", + DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype)); return(ptr); } if (!ptr) { - LogMsg("PutResourceRecordTTLWithLimit ptr is null %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "Pointer to message is NULL while filling resource record " PRI_DM_NAME " (" PUB_S ")", + DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype)); return(mDNSNULL); } @@ -2456,8 +2232,10 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 * // If we're out-of-space, return mDNSNULL if (!ptr || ptr + 10 >= limit) { - LogInfo("PutResourceRecordTTLWithLimit: can't put name, out of space %##s (%s), ptr %p, limit %p", rr->name->c, - DNSTypeName(rr->rrtype), ptr, limit); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "Can't put more names into current message, will possibly put it into the next message - " + "name: " PRI_DM_NAME " (" PUB_S "), remaining space: %ld", + DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype), (long)(limit - ptr)); return(mDNSNULL); } ptr[0] = (mDNSu8)(rr->rrtype >> 8); @@ -2473,8 +2251,10 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 * endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr); if (!endofrdata) { - LogInfo("PutResourceRecordTTLWithLimit: Ran out of space in PutResourceRecord for %##s (%s), ptr %p, limit %p", rr->name->c, - DNSTypeName(rr->rrtype), ptr+10, limit); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "Can't put more rdata into current message, will possibly put it into the next message - " + "name: " PRI_DM_NAME " (" PUB_S "), remaining space: %ld", + DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype), (long)(limit - ptr - 10)); return(mDNSNULL); } @@ -2484,8 +2264,16 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 * ptr[8] = (mDNSu8)(actualLength >> 8); ptr[9] = (mDNSu8)(actualLength & 0xFF); - if (count) (*count)++; - else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + if (count) + { + (*count)++; + } + else + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "No target count to update for " PRI_DM_NAME " (" PUB_S ")", + DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype)); + } return(endofrdata); } @@ -2628,48 +2416,6 @@ mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 return ptr; } -mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit) -{ - AuthRecord rr; - mDNSu32 ttl = 0; - - mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - // It is still not clear what the right size is. We will have to fine tune this once we do - // a lot of testing with DNSSEC. - rr.resrec.rrclass = 4096; - rr.resrec.rdlength = 0; - rr.resrec.rdestimate = 0; - // set the DO bit - ttl |= 0x8000; - end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, ttl, limit); - if (!end) { LogMsg("ERROR: putDNSSECOption - PutResourceRecordTTLWithLimit"); return mDNSNULL; } - return end; -} - -mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit) -{ - if (authInfo && authInfo->AutoTunnel) - { - AuthRecord hinfo; - mDNSu8 *h = hinfo.rdatastorage.u.data; - mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0]; - mDNSu8 *newptr; - mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - AppendDomainLabel(&hinfo.namestorage, &m->hostlabel); - AppendDomainName (&hinfo.namestorage, &authInfo->domain); - hinfo.resrec.rroriginalttl = 0; - mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); - h += 1 + (int)h[0]; - mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); - hinfo.resrec.rdlength = len; - hinfo.resrec.rdestimate = len; - newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit); - return newptr; - } - else - return end; -} - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -2835,20 +2581,34 @@ mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int l return (mDNSu8 *)bmap; } +mDNSlocal mDNSBool AssignDomainNameWithLimit(domainname *const dst, const domainname *src, const mDNSu8 *const end) +{ + const mDNSu32 len = DomainNameLengthLimit(src, end); + if ((len >= 1) && (len <= MAX_DOMAIN_NAME)) + { + mDNSPlatformMemCopy(dst->c, src->c, len); + return mDNStrue; + } + else + { + dst->c[0] = 0; + return mDNSfalse; + } +} + // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded -// (domainnames are expanded to 255 bytes) when stored in memory. +// (domainnames are expanded to 256 bytes) when stored in memory. // // This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr. // The caller can do this only if the names in the resource records are not compressed and validity of the -// resource record has already been done before. DNSSEC currently uses it this way. -mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, - LargeCacheRecord *const largecr, mDNSu16 rdlength) +// resource record has already been done before. +mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, ResourceRecord *const rr, + const mDNSu16 rdlength) { - CacheRecord *const rr = &largecr->r; - RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; + RDataBody2 *const rdb = (RDataBody2 *)&rr->rdata->u; - switch (rr->resrec.rrtype) + switch (rr->rrtype) { case kDNSType_A: if (rdlength != sizeof(mDNSv4Addr)) @@ -2875,7 +2635,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->name, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->name, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->name); } if (ptr != end) @@ -2892,7 +2655,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->soa.mname, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->soa.mname, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->soa.mname); } if (!ptr) @@ -2906,7 +2672,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->soa.rname, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->soa.rname, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->soa.rname); } if (!ptr) @@ -2926,14 +2695,51 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); break; - case kDNSType_NULL: case kDNSType_HINFO: + // See https://tools.ietf.org/html/rfc1035#section-3.3.2 for HINFO RDATA format. + { + // HINFO should contain RDATA. + if (end <= ptr || rdlength != (mDNSu32)(end - ptr)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "SetRData: Malformed HINFO RDATA - invalid RDATA length: %u", rdlength); + goto fail; + } + + const mDNSu8 *currentPtr = ptr; + // CPU character string length should be less than the RDATA length. + mDNSu32 cpuCharacterStrLength = currentPtr[0]; + if (1 + cpuCharacterStrLength >= (mDNSu32)(end - currentPtr)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "SetRData: Malformed HINFO RDATA - CPU character string goes out of boundary"); + goto fail; + } + currentPtr += 1 + cpuCharacterStrLength; + + // OS character string should end at the RDATA ending. + mDNSu32 osCharacterStrLength = currentPtr[0]; + if (1 + osCharacterStrLength != (mDNSu32)(end - currentPtr)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "SetRData: Malformed HINFO RDATA - OS character string does not end at the RDATA ending"); + goto fail; + } + + // Copy the validated RDATA. + rr->rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + case kDNSType_NULL: case kDNSType_TXT: case kDNSType_X25: case kDNSType_ISDN: case kDNSType_LOC: case kDNSType_DHCID: - rr->resrec.rdlength = rdlength; + case kDNSType_SVCB: + case kDNSType_HTTPS: + rr->rdlength = rdlength; mDNSPlatformMemCopy(rdb->data, ptr, rdlength); break; @@ -2952,7 +2758,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->mx.exchange, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->mx.exchange, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->mx.exchange); } if (ptr != end) @@ -2971,7 +2780,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->rp.mbox, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->rp.mbox, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->rp.mbox); } if (!ptr) @@ -2985,7 +2797,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->rp.txt, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->rp.txt, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->rp.txt); } if (ptr != end) @@ -3007,7 +2822,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->px.map822, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->px.map822, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->px.map822); } if (!ptr) @@ -3021,7 +2839,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->px.mapx400, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->px.mapx400, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->px.mapx400); } if (ptr != end) @@ -3052,7 +2873,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&rdb->srv.target, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&rdb->srv.target, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&rdb->srv.target); } if (ptr != end) @@ -3068,8 +2892,7 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con domainname name; const mDNSu8 *orig = ptr; - // Make sure the data is parseable and within the limits. DNSSEC code looks at - // the domain name in the end for a valid domainname. + // Make sure the data is parseable and within the limits. // // Fixed length: Order, preference (4 bytes) // Variable length: flags, service, regexp, domainname @@ -3106,7 +2929,7 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con goto fail; } - savelen = ptr - orig; + savelen = (int)(ptr - orig); // RFC 2915 states that name compression is not allowed for this field. But RFC 3597 // states that for NAPTR we should decompress. We make sure that we store the full @@ -3117,7 +2940,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&name, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&name); } if (ptr != end) @@ -3126,12 +2952,12 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con goto fail; } - rr->resrec.rdlength = savelen + DomainNameLength(&name); + rr->rdlength = savelen + DomainNameLength(&name); // The uncompressed size should not exceed the limits - if (rr->resrec.rdlength > MaximumRDSize) + if (rr->rdlength > MaximumRDSize) { - LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->resrec.rdlength %d, " - "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c); goto fail; } mDNSPlatformMemCopy(rdb->data, orig, savelen); @@ -3139,10 +2965,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con break; } case kDNSType_OPT: { - mDNSu8 *dataend = rr->resrec.rdata->u.data; - rdataOPT *opt = rr->resrec.rdata->u.opt; - rr->resrec.rdlength = 0; - while (ptr < end && (mDNSu8 *)(opt+1) < &dataend[MaximumRDSize]) + const mDNSu8 * const dataend = &rr->rdata->u.data[rr->rdata->MaxRDLength]; + rdataOPT *opt = rr->rdata->u.opt; + rr->rdlength = 0; + while ((ptr < end) && ((dataend - ((const mDNSu8 *)opt)) >= ((mDNSs32)sizeof(*opt)))) { const rdataOPT *const currentopt = opt; if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; } @@ -3210,7 +3036,7 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } ptr += currentopt->optlen; } - rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data); + rr->rdlength = (mDNSu16)((mDNSu8*)opt - rr->rdata->u.data); if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; } break; } @@ -3228,7 +3054,10 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&name, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&name); } if (!ptr) @@ -3257,69 +3086,19 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con // Initialize the right length here. When we call SetNewRData below which in turn calls // GetRDLength and for NSEC case, it assumes that rdlength is intitialized - rr->resrec.rdlength = DomainNameLength(&name) + bmaplen; + rr->rdlength = DomainNameLength(&name) + bmaplen; // Do we have space after the name expansion ? - if (rr->resrec.rdlength > MaximumRDSize) + if (rr->rdlength > MaximumRDSize) { - LogInfo("SetRData: Malformed NSEC rdlength %d, rr->resrec.rdlength %d, " - "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + LogInfo("SetRData: Malformed NSEC rdlength %d, rr->rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c); goto fail; } AssignDomainName((domainname *)rdb->data, &name); mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen); break; } - case kDNSType_NSEC3: - { - rdataNSEC3 *nsec3 = (rdataNSEC3 *)ptr; - mDNSu8 *p = (mDNSu8 *)&nsec3->salt; - int hashLength, bitmaplen; - - if (rdlength < NSEC3_FIXED_SIZE + 1) - { - LogInfo("SetRData: NSEC3 too small length %d", rdlength); - goto fail; - } - if (nsec3->alg != SHA1_DIGEST_TYPE) - { - LogInfo("SetRData: nsec3 alg %d not supported", nsec3->alg); - goto fail; - } - if (swap16(nsec3->iterations) > NSEC3_MAX_ITERATIONS) - { - LogInfo("SetRData: nsec3 iteration count %d too big", swap16(nsec3->iterations)); - goto fail; - } - p += nsec3->saltLength; - // There should at least be one byte beyond saltLength - if (p >= end) - { - LogInfo("SetRData: nsec3 too small, at saltlength %d, p %p, end %p", nsec3->saltLength, p, end); - goto fail; - } - // p is pointing at hashLength - hashLength = (int)*p++; - if (!hashLength) - { - LogInfo("SetRData: hashLength zero"); - goto fail; - } - p += hashLength; - if (p > end) - { - LogInfo("SetRData: nsec3 too small, at hashLength %d, p %p, end %p", hashLength, p, end); - goto fail; - } - - bitmaplen = rdlength - (int)(p - ptr); - p = SanityCheckBitMap(p, end, bitmaplen); - if (!p) - goto fail; - rr->resrec.rdlength = rdlength; - mDNSPlatformMemCopy(rdb->data, ptr, rdlength); - break; - } case kDNSType_TKEY: case kDNSType_TSIG: { @@ -3334,98 +3113,39 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con } else { - AssignDomainName(&name, (domainname *)ptr); + if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end)) + { + goto fail; + } ptr += DomainNameLength(&name); } if (!ptr || ptr >= end) { - LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->resrec.rrtype); + LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->rrtype); goto fail; } dlen = DomainNameLength(&name); - rlen = end - ptr; - rr->resrec.rdlength = dlen + rlen; - if (rr->resrec.rdlength > MaximumRDSize) + rlen = (int)(end - ptr); + rr->rdlength = dlen + rlen; + if (rr->rdlength > MaximumRDSize) { - LogInfo("SetRData: Malformed TSIG/TKEY rdlength %d, rr->resrec.rdlength %d, " - "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + LogInfo("SetRData: Malformed TSIG/TKEY rdlength %d, rr->rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c); goto fail; } AssignDomainName((domainname *)rdb->data, &name); mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen); break; } - case kDNSType_RRSIG: - { - const mDNSu8 *sig = ptr + RRSIG_FIXED_SIZE; - const mDNSu8 *orig = sig; - domainname name; - if (rdlength < RRSIG_FIXED_SIZE + 1) - { - LogInfo("SetRData: RRSIG too small length %d", rdlength); - goto fail; - } - if (msg) - { - sig = getDomainName(msg, sig, end, &name); - } - else - { - AssignDomainName(&name, (domainname *)sig); - sig += DomainNameLength(&name); - } - if (!sig) - { - LogInfo("SetRData: Malformed RRSIG record"); - goto fail; - } - - if ((sig - orig) != DomainNameLength(&name)) - { - LogInfo("SetRData: Malformed RRSIG record, signer name compression"); - goto fail; - } - // Just ensure that we have at least one byte of the signature - if (sig + 1 >= end) - { - LogInfo("SetRData: Not enough bytes for signature type %d", rr->resrec.rrtype); - goto fail; - } - rr->resrec.rdlength = rdlength; - mDNSPlatformMemCopy(rdb->data, ptr, rdlength); - break; - } - case kDNSType_DNSKEY: - { - if (rdlength < DNSKEY_FIXED_SIZE + 1) - { - LogInfo("SetRData: DNSKEY too small length %d", rdlength); - goto fail; - } - rr->resrec.rdlength = rdlength; - mDNSPlatformMemCopy(rdb->data, ptr, rdlength); - break; - } - case kDNSType_DS: - { - if (rdlength < DS_FIXED_SIZE + 1) - { - LogInfo("SetRData: DS too small length %d", rdlength); - goto fail; - } - rr->resrec.rdlength = rdlength; - mDNSPlatformMemCopy(rdb->data, ptr, rdlength); - break; - } default: debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data", - rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); + rr->rrtype, DNSTypeName(rr->rrtype)); // Note: Just because we don't understand the record type, that doesn't // mean we fail. The DNS protocol specifies rdlength, so we can // safely skip over unknown records and ignore them. // We also grab a binary copy of the rdata anyway, since the caller // might know how to interpret it even if we don't. - rr->resrec.rdlength = rdlength; + rr->rdlength = rdlength; mDNSPlatformMemCopy(rdb->data, ptr, rdlength); break; } @@ -3439,6 +3159,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage { CacheRecord *const rr = &largecr->r; mDNSu16 pktrdlength; + mDNSu32 maxttl = (!InterfaceID) ? mDNSMaximumUnicastTTLSeconds : mDNSMaximumMulticastTTLSeconds; if (largecr == &m->rec && m->rec.r.resrec.RecordType) LogFatalError("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); @@ -3450,13 +3171,20 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->TimeRcvd = m ? m->timenow : 0; rr->DelayDelivery = 0; rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord() +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + rr->LastCachedAnswerTime = 0; +#endif rr->CRActiveQuestion = mDNSNULL; rr->UnansweredQueries = 0; rr->LastUnansweredTime= 0; rr->NextInCFList = mDNSNULL; rr->resrec.InterfaceID = InterfaceID; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_forget(&rr->resrec.dnsservice); +#else rr->resrec.rDNSServer = mDNSNULL; +#endif ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); } @@ -3467,8 +3195,8 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask); rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); - if (rr->resrec.rroriginalttl > mDNSMaximumTTLSeconds && (mDNSs32)rr->resrec.rroriginalttl != -1) - rr->resrec.rroriginalttl = mDNSMaximumTTLSeconds; + if (rr->resrec.rroriginalttl > maxttl && (mDNSs32)rr->resrec.rroriginalttl != -1) + rr->resrec.rroriginalttl = maxttl; // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly. pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); @@ -3500,8 +3228,13 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ. if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136) rr->resrec.rdlength = 0; - else if (!SetRData(msg, ptr, end, largecr, pktrdlength)) + else if (!SetRData(msg, ptr, end, &rr->resrec, pktrdlength)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "GetLargeResourceRecord: SetRData failed for " PRI_DM_NAME " (" PUB_S ")", + DM_NAME_PARAM(rr->resrec.name), DNSTypeName(rr->resrec.rrtype)); goto fail; + } SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us @@ -3636,23 +3369,23 @@ mDNSexport mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, cons (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \ (X) == kDNSFlag0_OP_Notify ? "Notify " : \ (X) == kDNSFlag0_OP_Update ? "Update " : \ - (X) == kDNSFlag0_OP_Subscribe? "Subscribe": \ - (X) == kDNSFlag0_OP_UnSubscribe? "UnSubscribe" : "?? " ) + (X) == kDNSFlag0_OP_DSO ? "DSO " : "?? " ) #define DNS_RC_Name(X) ( \ - (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ - (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \ - (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \ - (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \ - (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \ - (X) == kDNSFlag1_RC_Refused ? "Refused" : \ - (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \ - (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \ - (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \ - (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ - (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" ) - -mDNSlocal void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, ...) + (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ + (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \ + (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \ + (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \ + (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \ + (X) == kDNSFlag1_RC_Refused ? "Refused" : \ + (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \ + (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \ + (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \ + (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ + (X) == kDNSFlag1_RC_NotZone ? "NotZone" : \ + (X) == kDNSFlag1_RC_DSOTypeNI ? "DSOTypeNI" : "??" ) + +mDNSexport void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, ...) { va_list args; mDNSu32 buflen, n; @@ -3678,34 +3411,17 @@ mDNSlocal void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, . (((mDNSu32)((mDNSu8 *)(PTR))[2]) << 8) | \ ((mDNSu32)((mDNSu8 *)(PTR))[3]))) -mDNSlocal void DNSMessageDump(const DNSMessage *const msg, const mDNSu8 *const end, char *buffer, mDNSu32 buflen) +mDNSlocal void DNSMessageDumpToLog(const DNSMessage *const msg, const mDNSu8 *const end) { - domainname *name; - const mDNSu8 *ptr; + domainname *name = mDNSNULL; + const mDNSu8 *ptr = msg->data; domainname nameStorage[2]; - char *dst = buffer; - const char *const lim = &buffer[buflen]; - mDNSu32 i; - const mDNSu32 rrcount = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; - mDNS_snprintf_add(&dst, lim, "DNS %s%s (%lu) (flags %02X%02X) RCODE: %s (%d)%s%s%s%s%s%s ID: %u:", - DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), - (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "Response" : "Query", - (unsigned long)(end - (const mDNSu8 *)msg), - msg->h.flags.b[0], msg->h.flags.b[1], - DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), - msg->h.flags.b[1] & kDNSFlag1_RC_Mask, - (msg->h.flags.b[0] & kDNSFlag0_AA) ? " AA" : "", - (msg->h.flags.b[0] & kDNSFlag0_TC) ? " TC" : "", - (msg->h.flags.b[0] & kDNSFlag0_RD) ? " RD" : "", - (msg->h.flags.b[1] & kDNSFlag1_RA) ? " RA" : "", - (msg->h.flags.b[1] & kDNSFlag1_AD) ? " AD" : "", - (msg->h.flags.b[1] & kDNSFlag1_CD) ? " CD" : "", - mDNSVal16(msg->h.id)); - - name = mDNSNULL; - ptr = msg->data; - for (i = 0; i < msg->h.numQuestions; i++) + char questions[512]; + questions[0] = '\0'; + char *questions_dst = questions; + const char *const questions_lim = &questions[512]; + for (mDNSu32 i = 0; i < msg->h.numQuestions; i++) { mDNSu16 qtype, qclass; @@ -3718,13 +3434,17 @@ mDNSlocal void DNSMessageDump(const DNSMessage *const msg, const mDNSu8 *const e qclass = ReadField16(&ptr[2]); ptr += 4; - mDNS_snprintf_add(&dst, lim, " %##s %s", name->c, DNSTypeString(qtype)); - if (qclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", qclass); - mDNS_snprintf_add(&dst, lim, "?"); + mDNS_snprintf_add(&questions_dst, questions_lim, " %##s %s", name->c, DNSTypeString(qtype)); + if (qclass != kDNSClass_IN) mDNS_snprintf_add(&questions_dst, questions_lim, "/%u", qclass); + mDNS_snprintf_add(&questions_dst, questions_lim, "?"); } - mDNS_snprintf_add(&dst, lim, " %u/%u/%u", msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals); - for (i = 0; i < rrcount; i++) + char rrs[512]; + rrs[0] = '\0'; + char *rrs_dst = rrs; + const char *const rrs_lim = &rrs[512]; + const mDNSu32 rrcount = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; + for (mDNSu32 i = 0; i < rrcount; i++) { mDNSu16 rrtype, rrclass, rdlength; mDNSu32 ttl; @@ -3746,104 +3466,119 @@ mDNSlocal void DNSMessageDump(const DNSMessage *const msg, const mDNSu8 *const e if ((end - ptr) < rdlength) goto exit; rdata = ptr; - if (i > 0) mDNS_snprintf_add(&dst, lim, ","); - if (!previousName || !SameDomainName(name, previousName)) mDNS_snprintf_add(&dst, lim, " %##s", name); + if (i > 0) mDNS_snprintf_add(&rrs_dst, rrs_lim, ","); + if (!previousName || !SameDomainName(name, previousName)) mDNS_snprintf_add(&rrs_dst, rrs_lim, " %##s", name); - mDNS_snprintf_add(&dst, lim, " %s", DNSTypeString(rrtype)); - if (rrclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", rrclass); - mDNS_snprintf_add(&dst, lim, " "); + mDNS_snprintf_add(&rrs_dst, rrs_lim, " %s", DNSTypeString(rrtype)); + if (rrclass != kDNSClass_IN) mDNS_snprintf_add(&rrs_dst, rrs_lim, "/%u", rrclass); + mDNS_snprintf_add(&rrs_dst, rrs_lim, " "); handled = mDNSfalse; switch (rrtype) { - case kDNSType_A: - if (rdlength == 4) - { - mDNS_snprintf_add(&dst, lim, "%.4a", rdata); - handled = mDNStrue; - } - break; + case kDNSType_A: + if (rdlength == 4) + { + mDNS_snprintf_add(&rrs_dst, rrs_lim, "%.4a", rdata); + handled = mDNStrue; + } + break; - case kDNSType_AAAA: - if (rdlength == 16) - { - mDNS_snprintf_add(&dst, lim, "%.16a", rdata); - handled = mDNStrue; - } - break; + case kDNSType_AAAA: + if (rdlength == 16) + { + mDNS_snprintf_add(&rrs_dst, rrs_lim, "%.16a", rdata); + handled = mDNStrue; + } + break; - case kDNSType_CNAME: - ptr = getDomainName(msg, rdata, end, name); - if (!ptr) goto exit; + case kDNSType_CNAME: + ptr = getDomainName(msg, rdata, end, name); + if (!ptr) goto exit; - mDNS_snprintf_add(&dst, lim, "%##s", name); - handled = mDNStrue; - break; + mDNS_snprintf_add(&rrs_dst, rrs_lim, "%##s", name); + handled = mDNStrue; + break; - case kDNSType_SOA: - { - mDNSu32 serial, refresh, retry, expire, minimum; - domainname *const mname = &nameStorage[0]; - domainname *const rname = &nameStorage[1]; - name = mDNSNULL; + case kDNSType_SOA: + { + mDNSu32 serial, refresh, retry, expire, minimum; + domainname *const mname = &nameStorage[0]; + domainname *const rname = &nameStorage[1]; + name = mDNSNULL; - ptr = getDomainName(msg, rdata, end, mname); - if (!ptr) goto exit; + ptr = getDomainName(msg, rdata, end, mname); + if (!ptr) goto exit; - ptr = getDomainName(msg, ptr, end, rname); - if (!ptr) goto exit; + ptr = getDomainName(msg, ptr, end, rname); + if (!ptr) goto exit; - if ((end - ptr) < 20) goto exit; - serial = ReadField32(&ptr[0]); - refresh = ReadField32(&ptr[4]); - retry = ReadField32(&ptr[8]); - expire = ReadField32(&ptr[12]); - minimum = ReadField32(&ptr[16]); + if ((end - ptr) < 20) goto exit; + serial = ReadField32(&ptr[0]); + refresh = ReadField32(&ptr[4]); + retry = ReadField32(&ptr[8]); + expire = ReadField32(&ptr[12]); + minimum = ReadField32(&ptr[16]); - mDNS_snprintf_add(&dst, lim, "%##s %##s %lu %lu %lu %lu %lu", mname, rname, (unsigned long)serial, - (unsigned long)refresh, (unsigned long)retry, (unsigned long)expire, (unsigned long)minimum); + mDNS_snprintf_add(&rrs_dst, rrs_lim, "%##s %##s %lu %lu %lu %lu %lu", mname, rname, (unsigned long)serial, + (unsigned long)refresh, (unsigned long)retry, (unsigned long)expire, (unsigned long)minimum); - handled = mDNStrue; - break; - } + handled = mDNStrue; + break; + } - default: - break; + default: + break; } - if (!handled) mDNS_snprintf_add(&dst, lim, "RDATA[%u]: %.*H", rdlength, rdlength, rdata); - mDNS_snprintf_add(&dst, lim, " (%lu)", (unsigned long)ttl); + if (!handled) mDNS_snprintf_add(&rrs_dst, rrs_lim, "RDATA[%u]: %.*H", rdlength, rdlength, rdata); + mDNS_snprintf_add(&rrs_dst, rrs_lim, " (%lu)", (unsigned long)ttl); ptr = rdata + rdlength; } + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[Q%u] DNS " PUB_S PUB_S " (%lu) (flags %02X%02X) RCODE: " PUB_S " (%d)" PUB_S PUB_S PUB_S PUB_S PUB_S PUB_S ":" + PRI_S " %u/%u/%u " PRI_S, + mDNSVal16(msg->h.id), + DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), + (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "Response" : "Query", + (unsigned long)(end - (const mDNSu8 *)msg), + msg->h.flags.b[0], msg->h.flags.b[1], + DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), + msg->h.flags.b[1] & kDNSFlag1_RC_Mask, + (msg->h.flags.b[0] & kDNSFlag0_AA) ? " AA" : "", + (msg->h.flags.b[0] & kDNSFlag0_TC) ? " TC" : "", + (msg->h.flags.b[0] & kDNSFlag0_RD) ? " RD" : "", + (msg->h.flags.b[1] & kDNSFlag1_RA) ? " RA" : "", + (msg->h.flags.b[1] & kDNSFlag1_AD) ? " AD" : "", + (msg->h.flags.b[1] & kDNSFlag1_CD) ? " CD" : "", + questions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, rrs); + exit: return; } // Note: DumpPacket expects the packet header fields in host byte order, not network byte order -mDNSexport void DumpPacket(mStatus status, mDNSBool sent, char *transport, - const mDNSAddr *srcaddr, mDNSIPPort srcport, - const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) +mDNSexport void DumpPacket(mStatus status, mDNSBool sent, const char *transport, + const mDNSAddr *srcaddr, mDNSIPPort srcport,const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, + const mDNSu8 *const end, mDNSInterfaceID interfaceID) { - char buffer[512]; - char *dst = buffer; - const char *const lim = &buffer[512]; + const mDNSAddr zeroIPv4Addr = { mDNSAddrType_IPv4, {{{ 0 }}} }; + char action[32]; + const char* interfaceName = "interface"; - buffer[0] = '\0'; - if (!status) mDNS_snprintf_add(&dst, lim, sent ? "Sent" : "Received"); - else mDNS_snprintf_add(&dst, lim, "ERROR %d %sing", status, sent ? "Send" : "Receiv"); + if (!status) mDNS_snprintf(action, sizeof(action), sent ? "Sent" : "Received"); + else mDNS_snprintf(action, sizeof(action), "ERROR %d %sing", status, sent ? "Send" : "Receiv"); - mDNS_snprintf_add(&dst, lim, " %s DNS Message %u bytes from ", transport, (unsigned long)(end - (const mDNSu8 *)msg)); - - if (sent) mDNS_snprintf_add(&dst, lim, "port %d", mDNSVal16(srcport)); - else mDNS_snprintf_add(&dst, lim, "%#a:%d", srcaddr, mDNSVal16(srcport)); - - if (dstaddr || !mDNSIPPortIsZero(dstport)) mDNS_snprintf_add(&dst, lim, " to %#a:%d", dstaddr, mDNSVal16(dstport)); - - LogInfo("%s", buffer); +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + interfaceName = InterfaceNameForID(&mDNSStorage, interfaceID); +#endif - buffer[0] = '\0'; - DNSMessageDump(msg, end, buffer, (mDNSu32)sizeof(buffer)); - LogInfo("%s", buffer); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[Q%u] " PUB_S " " PUB_S " DNS Message %lu bytes from " PRI_IP_ADDR ":%d to " PRI_IP_ADDR ":%d via " PUB_S " (%p)", + mDNSVal16(msg->h.id), action, transport, (unsigned long)(end - (const mDNSu8 *)msg), + srcaddr ? srcaddr : &zeroIPv4Addr, mDNSVal16(srcport), dstaddr ? dstaddr : &zeroIPv4Addr, mDNSVal16(dstport), + interfaceName, interfaceID); + DNSMessageDumpToLog(msg, end); } // *************************************************************************** @@ -3852,26 +3587,19 @@ mDNSexport void DumpPacket(mStatus status, mDNSBool sent, char *transport, #pragma mark - Packet Sending Functions #endif -#ifdef UNIT_TEST -// Run the unit test of mDNSSendDNSMessage -UNITTEST_SENDDNSMESSAGE -#else // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.) -struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; +struct TCPSocket_struct { mDNSIPPort port; TCPSocketFlags flags; /* ... */ }; // Stub definition of UDPSocket_struct so we can access port field. (Rest of UDPSocket_struct is platform-dependent.) struct UDPSocket_struct { mDNSIPPort port; /* ... */ }; // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible. mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, - mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo, - mDNSBool useBackgroundTrafficClass) + mDNSInterfaceID InterfaceID, TCPSocket *tcpSrc, UDPSocket *udpSrc, const mDNSAddr *dst, + mDNSIPPort dstport, DomainAuthInfo *authInfo, mDNSBool useBackgroundTrafficClass) { mStatus status = mStatus_NoError; const mDNSu16 numAdditionals = msg->h.numAdditionals; - mDNSu8 *newend; - mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; #if APPLE_OSX_mDNSResponder // maintain outbound packet statistics @@ -3888,10 +3616,6 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS return mStatus_BadParamErr; } - newend = putHINFO(m, msg, end, authInfo, limit); - if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal - else end = newend; - // Put all the integer values in IETF byte-order (MSB first, LSB second) SwapDNSHeaderBytes(msg); @@ -3900,8 +3624,8 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS else { // Send the packet on the wire - if (!sock) - status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport, useBackgroundTrafficClass); + if (!tcpSrc) + status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, udpSrc, dst, dstport, useBackgroundTrafficClass); else { mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg); @@ -3910,13 +3634,13 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS long nsent; // Try to send them in one packet if we can allocate enough memory - buf = mDNSPlatformMemAllocate(msglen + 2); + buf = (char *) mDNSPlatformMemAllocate(msglen + 2); if (buf) { buf[0] = lenbuf[0]; buf[1] = lenbuf[1]; mDNSPlatformMemCopy(buf+2, msg, msglen); - nsent = mDNSPlatformWriteTCP(sock, buf, msglen+2); + nsent = mDNSPlatformWriteTCP(tcpSrc, buf, msglen+2); if (nsent != (msglen + 2)) { LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen); @@ -3926,7 +3650,7 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS } else { - nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); + nsent = mDNSPlatformWriteTCP(tcpSrc, (char*)lenbuf, 2); if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); @@ -3934,7 +3658,7 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS } else { - nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen); + nsent = mDNSPlatformWriteTCP(tcpSrc, (char *)msg, msglen); if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); @@ -3950,14 +3674,25 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS // Dump the packet with the HINFO and TSIG if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) - DumpPacket(status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); + { + char *transport = "UDP"; + mDNSIPPort portNumber = udpSrc ? udpSrc->port : MulticastDNSPort; + if (tcpSrc) + { + if (tcpSrc->flags) + transport = "TLS"; + else + transport = "TCP"; + portNumber = tcpSrc->port; + } + DumpPacket(status, mDNStrue, transport, mDNSNULL, portNumber, dst, dstport, msg, end, InterfaceID); + } // put the number of additionals back the way it was msg->h.numAdditionals = numAdditionals; return(status); } -#endif // UNIT_TEST // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -4038,9 +3773,9 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA; -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) if (m->NextBonjourDisableTime && (e - m->NextBonjourDisableTime > 0)) e = m->NextBonjourDisableTime; -#endif // BONJOUR_ON_DEMAND +#endif // NextScheduledSPRetry only valid when DelaySleep not set if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; @@ -4486,7 +4221,7 @@ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); default: s = mDNS_VACB; i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c); - /* FALLTHROUGH */ + break; case '%': *sbuffer++ = (char)c; if (++nwritten >= buflen) goto exit; @@ -4553,3 +4288,53 @@ mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, return(length); } + +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +mDNSexport mDNSu32 mDNS_GetNextResolverGroupID(void) +{ + static mDNSu32 lastID = 0; + if (++lastID == 0) lastID = 1; // Valid resolver group IDs are non-zero. + return(lastID); +} +#endif + +#define kReverseIPv6Domain ((const domainname *) "\x3" "ip6" "\x4" "arpa") + +mDNSexport mDNSBool GetReverseIPv6Addr(const domainname *name, mDNSu8 outIPv6[16]) +{ + const mDNSu8 * ptr; + int i; + mDNSu8 ipv6[16]; + + // If the name is of the form "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa.", where each x + // is a hex digit, then the sequence of 32 hex digit labels represents the nibbles of an IPv6 address in reverse order. + // See <https://tools.ietf.org/html/rfc3596#section-2.5>. + + ptr = name->c; + for (i = 0; i < 32; i++) + { + unsigned int c, nibble; + const int j = 15 - (i / 2); + if (*ptr++ != 1) return (mDNSfalse); // If this label's length is not 1, then fail. + c = *ptr++; // Get label byte. + if ( (c >= '0') && (c <= '9')) nibble = c - '0'; // If it's a hex digit, get its numeric value. + else if ((c >= 'a') && (c <= 'f')) nibble = (c - 'a') + 10; + else if ((c >= 'A') && (c <= 'F')) nibble = (c - 'A') + 10; + else return (mDNSfalse); // Otherwise, fail. + if ((i % 2) == 0) + { + ipv6[j] = (mDNSu8)nibble; + } + else + { + ipv6[j] |= (mDNSu8)(nibble << 4); + } + } + + // The rest of the name needs to be "ip6.arpa.". If it isn't, fail. + + if (!SameDomainName((const domainname *)ptr, kReverseIPv6Domain)) return (mDNSfalse); + if (outIPv6) mDNSPlatformMemCopy(outIPv6, ipv6, 16); + return (mDNStrue); +} +#endif // !STANDALONE diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.h b/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.h index 6e468cdb81..48de85f609 100644 --- a/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.h +++ b/usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2018 Apple Inc. All rights reserved. + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,15 +44,14 @@ typedef enum kDNSFlag0_QR_Query = 0x00, kDNSFlag0_QR_Response = 0x80, - kDNSFlag0_OP_Mask = 0x78, // Operation type - kDNSFlag0_OP_StdQuery = 0x00, - kDNSFlag0_OP_Subscribe = 0x06, - kDNSFlag0_OP_UnSubscribe = 0x07, - kDNSFlag0_OP_Iquery = 0x08, - kDNSFlag0_OP_Status = 0x10, - kDNSFlag0_OP_Unused3 = 0x18, - kDNSFlag0_OP_Notify = 0x20, - kDNSFlag0_OP_Update = 0x28, + kDNSFlag0_OP_Mask = 0xF << 3, // Operation type + kDNSFlag0_OP_StdQuery = 0x0 << 3, + kDNSFlag0_OP_Iquery = 0x1 << 3, + kDNSFlag0_OP_Status = 0x2 << 3, + kDNSFlag0_OP_Unused3 = 0x3 << 3, + kDNSFlag0_OP_Notify = 0x4 << 3, + kDNSFlag0_OP_Update = 0x5 << 3, + kDNSFlag0_OP_DSO = 0x6 << 3, kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, @@ -76,7 +75,8 @@ typedef enum kDNSFlag1_RC_YXRRSet = 0x07, kDNSFlag1_RC_NXRRSet = 0x08, kDNSFlag1_RC_NotAuth = 0x09, - kDNSFlag1_RC_NotZone = 0x0A + kDNSFlag1_RC_NotZone = 0x0A, + kDNSFlag1_RC_DSOTypeNI = 0x0B } DNS_Flags; typedef enum @@ -98,6 +98,10 @@ extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf extern mDNSu32 mDNSRandom(mDNSu32 max); // Returns pseudo-random result from zero to max inclusive +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +extern mDNSu32 mDNS_GetNextResolverGroupID(void); +#endif + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -116,7 +120,8 @@ extern mDNSu32 mDNSRandom(mDNSu32 max); // Returns pseudo-random result from // We set the maximum allowable TTL to one hour. // With the 25% correction factor to avoid the DNS Zeno's paradox bug, that gives us an actual maximum lifetime of 75 minutes. -#define mDNSMaximumTTLSeconds (mDNSu32)3600 +#define mDNSMaximumMulticastTTLSeconds (mDNSu32)4500 +#define mDNSMaximumUnicastTTLSeconds (mDNSu32)3600 #define mDNSValidHostChar(X, notfirst, notlast) (mDNSIsLetter(X) || mDNSIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') ) @@ -174,9 +179,11 @@ extern void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBo extern mDNSu32 RDataHashValue(const ResourceRecord *const rr); extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename); -extern mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); +extern mDNSBool SameNameCacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q); extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); -extern mDNSBool AnyTypeRecordAnswersQuestion (const ResourceRecord *const rr, const DNSQuestion *const q); +extern mDNSBool AuthRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q); +extern mDNSBool CacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q); +extern mDNSBool AnyTypeRecordAnswersQuestion (const AuthRecord *const ar, const DNSQuestion *const q); extern mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q); extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q); extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); @@ -206,7 +213,8 @@ extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 * #define AllowedRRSpace(msg) (((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) -extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit); +extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, const ResourceRecord *rr, + mDNSu32 ttl, const mDNSu8 *limit); #define PutResourceRecordTTL(msg, ptr, count, rr, ttl) \ PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AllowedRRSpace(msg)) @@ -233,14 +241,9 @@ extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease); extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit); -extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit); -extern mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit); extern int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg); extern void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap); -extern const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen, - const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen); - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -256,8 +259,8 @@ extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *pt extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); extern const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr); -extern mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, - LargeCacheRecord *const largecr, mDNSu16 rdlength); +extern mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, ResourceRecord *rr, + mDNSu16 rdlength); extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); extern const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, DNSQuestion *question); @@ -267,9 +270,9 @@ extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 extern const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize); extern const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end); extern mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, mDNSu32 *const lease); -extern void DumpPacket(mStatus status, mDNSBool sent, char *transport, - const mDNSAddr *srcaddr, mDNSIPPort srcport, - const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end); +extern void DumpPacket(mStatus status, mDNSBool sent, const char *transport, const mDNSAddr *srcaddr, mDNSIPPort srcport, + const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end, + mDNSInterfaceID interfaceID); extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type); extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type); extern mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type); @@ -277,16 +280,16 @@ extern mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type); extern mDNSu16 swap16(mDNSu16 x); extern mDNSu32 swap32(mDNSu32 x); +extern mDNSBool GetReverseIPv6Addr(const domainname *inQName, mDNSu8 outIPv6[16]); + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Packet Sending Functions #endif - extern mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, - mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo, - mDNSBool useBackgroundTrafficClass); + mDNSInterfaceID InterfaceID, TCPSocket *tcpSrc, UDPSocket *udpSrc, const mDNSAddr *dst, + mDNSIPPort dstport, DomainAuthInfo *authInfo, mDNSBool useBackgroundTrafficClass); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -297,7 +300,7 @@ extern mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 * extern void ShowTaskSchedulingError(mDNS *const m); extern void mDNS_Lock_(mDNS *const m, const char * const functionname); extern void mDNS_Unlock_(mDNS *const m, const char * const functionname); - + #if defined(_WIN32) #define __func__ __FUNCTION__ #endif diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/DNSDigest.c b/usr/src/contrib/mDNSResponder/mDNSCore/DNSDigest.c index 6520ac6f6e..5b1c228957 100644 --- a/usr/src/contrib/mDNSResponder/mDNSCore/DNSDigest.c +++ b/usr/src/contrib/mDNSResponder/mDNSCore/DNSDigest.c @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2002-2011 Apple Inc. All rights reserved. +/* + * Copyright (c) 2002-2019 Apple Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -571,27 +570,34 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); l|=(((unsigned long)(*((c)++)))<< 8), \ l|=(((unsigned long)(*((c)++))) ), \ l) -#define HOST_p_c2l(c,l,n) { \ - switch (n) { \ +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ case 0: l =((unsigned long)(*((c)++)))<<24; \ + /* FALLTHROUGH */ \ case 1: l|=((unsigned long)(*((c)++)))<<16; \ + /* FALLTHROUGH */ \ case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + /* FALLTHROUGH */ \ case 3: l|=((unsigned long)(*((c)++))); \ } } #define HOST_p_c2l_p(c,l,sc,len) { \ - switch (sc) { \ + switch (sc) { \ case 0: l =((unsigned long)(*((c)++)))<<24; \ - if (--len == 0) break; \ + if (--len == 0) break; \ + /* FALLTHROUGH */ \ case 1: l|=((unsigned long)(*((c)++)))<<16; \ - if (--len == 0) break; \ + if (--len == 0) break; \ + /* FALLTHROUGH */ \ case 2: l|=((unsigned long)(*((c)++)))<< 8; \ } } /* NOTE the pointer is not incremented at the end of this */ -#define HOST_c2l_p(c,l,n) { \ - l=0; (c)+=n; \ - switch (n) { \ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ case 3: l =((unsigned long)(*(--(c))))<< 8; \ + /* FALLTHROUGH */ \ case 2: l|=((unsigned long)(*(--(c))))<<16; \ + /* FALLTHROUGH */ \ case 1: l|=((unsigned long)(*(--(c))))<<24; \ } } #define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ @@ -607,34 +613,34 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); l|=(((unsigned long)(*((c)++)))<<16), \ l|=(((unsigned long)(*((c)++)))<<24), \ l) -#define HOST_p_c2l(c,l,n) { \ - switch (n) { \ +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ case 0: l =((unsigned long)(*((c)++))); \ - /* FALLTHROUGH */ \ + /* FALLTHROUGH */ \ case 1: l|=((unsigned long)(*((c)++)))<< 8; \ - /* FALLTHROUGH */ \ + /* FALLTHROUGH */ \ case 2: l|=((unsigned long)(*((c)++)))<<16; \ - /* FALLTHROUGH */ \ + /* FALLTHROUGH */ \ case 3: l|=((unsigned long)(*((c)++)))<<24; \ } } #define HOST_p_c2l_p(c,l,sc,len) { \ - switch (sc) { \ + switch (sc) { \ case 0: l =((unsigned long)(*((c)++))); \ - if (--len == 0) break; \ - /* FALLTHROUGH */ \ + if (--len == 0) break; \ + /* FALLTHROUGH */ \ case 1: l|=((unsigned long)(*((c)++)))<< 8; \ - if (--len == 0) break; \ - /* FALLTHROUGH */ \ + if (--len == 0) break; \ + /* FALLTHROUGH */ \ case 2: l|=((unsigned long)(*((c)++)))<<16; \ } } /* NOTE the pointer is not incremented at the end of this */ -#define HOST_c2l_p(c,l,n) { \ - l=0; (c)+=n; \ - switch (n) { \ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ case 3: l =((unsigned long)(*(--(c))))<<16; \ - /* FALLTHROUGH */ \ + /* FALLTHROUGH */ \ case 2: l|=((unsigned long)(*(--(c))))<< 8; \ - /* FALLTHROUGH */ \ + /* FALLTHROUGH */ \ case 1: l|=((unsigned long)(*(--(c)))); \ } } #define _HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ @@ -652,6 +658,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) { const unsigned char *data=(const unsigned char *)data_; + const unsigned char * const data_end=(const unsigned char *)data_; register HASH_LONG * p; register unsigned long l; int sw,sc,ew,ec; @@ -675,7 +682,7 @@ int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) if ((c->num+len) >= HASH_CBLOCK) { l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; - for (; sw<HASH_LBLOCK; sw++) + for (; (sw < HASH_LBLOCK) && ((data_end - data) >= 4); sw++) { HOST_c2l(data,l); p[sw]=l; } @@ -699,7 +706,7 @@ int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; - for (; sw < ew; sw++) + for (; (sw < ew) && ((data_end - data) >= 4); sw++) { HOST_c2l(data,l); p[sw]=l; } @@ -755,7 +762,7 @@ int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) c->num = (int)len; ew=(int)(len>>2); /* words to copy */ ec=(int)(len&0x03); - for (; ew; ew--,p++) + for (; ew && ((data_end - data) >= 4); ew--,p++) { HOST_c2l(data,l); *p=l; } @@ -1048,6 +1055,10 @@ void md5_block_data_order (MD5_CTX *c, const void *data_, int num) C=c->C; D=c->D; +#if defined(__clang_analyzer__) + // Get rid of false positive analyzer warning. + for (const unsigned char *_ptr = data; _ptr < &data[num * HASH_CBLOCK]; ++_ptr) {} +#endif for (; num--;) { HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l; @@ -1283,7 +1294,7 @@ mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 #define HMAC_OPAD 0x5c #define MD5_LEN 16 -#define HMAC_MD5_AlgName (*(const domainname*) "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int") +#define HMAC_MD5_AlgName "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int" // Adapted from Appendix, RFC 2104 mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *key, mDNSu32 len) @@ -1361,10 +1372,10 @@ mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthI MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl)); // alg name - AssignDomainName(&tsig.resrec.rdata->u.name, &HMAC_MD5_AlgName); - len = DomainNameLength(&HMAC_MD5_AlgName); + AssignConstStringDomainName(&tsig.resrec.rdata->u.name, HMAC_MD5_AlgName); + len = DomainNameLengthLimit((domainname *)HMAC_MD5_AlgName, (mDNSu8 *)HMAC_MD5_AlgName + sizeof HMAC_MD5_AlgName); rdata = tsig.resrec.rdata->u.data + len; - MD5_Update(&c, HMAC_MD5_AlgName.c, len); + MD5_Update(&c, (mDNSu8 *)HMAC_MD5_AlgName, len); // time // get UTC (universal time), convert to 48-bit unsigned in network byte order @@ -1445,7 +1456,7 @@ mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeC algo = (domainname*) ptr; - if (!SameDomainName(algo, &HMAC_MD5_AlgName)) + if (!SameDomainName(algo, (domainname *)HMAC_MD5_AlgName)) { LogMsg("ERROR: DNSDigest_VerifyMessage - TSIG algorithm not supported: %##s", algo->c); *rcode = kDNSFlag1_RC_NotAuth; diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/anonymous.c b/usr/src/contrib/mDNSResponder/mDNSCore/anonymous.c deleted file mode 100644 index 107dc177ac..0000000000 --- a/usr/src/contrib/mDNSResponder/mDNSCore/anonymous.c +++ /dev/null @@ -1,623 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2012-2013 Apple Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mDNSEmbeddedAPI.h" -#include "CryptoAlg.h" -#include "anonymous.h" -#include "DNSCommon.h" - -// Define ANONYMOUS_DISABLED to remove all the anonymous functionality -// and use the stub functions implemented later in this file. - -#ifndef ANONYMOUS_DISABLED - -#define ANON_NSEC3_ITERATIONS 1 - -struct AnonInfoResourceRecord_struct -{ - ResourceRecord resrec; - RData rdatastorage; -}; - -typedef struct AnonInfoResourceRecord_struct AnonInfoResourceRecord; - -mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt) -{ - const mDNSu8 *ptr; - rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data; - mDNSu8 *tmp, *nxt; - unsigned short iter = ANON_NSEC3_ITERATIONS; - int hlen; - const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; - - // Construct the RDATA first and construct the owner name based on that. - ptr = (const mDNSu8 *)&salt; - debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c); - - // Set the RDATA - nsec3->alg = SHA1_DIGEST_TYPE; - nsec3->flags = 0; - nsec3->iterations = swap16(iter); - nsec3->saltLength = 4; - tmp = (mDNSu8 *)&nsec3->salt; - *tmp++ = ptr[0]; - *tmp++ = ptr[1]; - *tmp++ = ptr[2]; - *tmp++ = ptr[3]; - - // hashLength, nxt, bitmap - *tmp++ = SHA1_HASH_LENGTH; // hash length - nxt = tmp; - tmp += SHA1_HASH_LENGTH; - *tmp++ = 0; // window number - *tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length - mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE); - tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7); - - // Hash the base service name + salt + AnonData - if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen)) - { - LogMsg("InitializeNSEC3Record: NSEC3HashName failed for %##s", rr->name->c); - return mDNSfalse; - } - if (hlen != SHA1_HASH_LENGTH) - { - LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen); - return mDNSfalse; - } - mDNSPlatformMemCopy(nxt, hashName, hlen); - - return mDNStrue; -} - -mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt) -{ - ResourceRecord *rr; - int dlen; - domainname *name; - - // We are just allocating an RData which has StandardAuthRDSize - if (StandardAuthRDSize < MCAST_NSEC3_RDLENGTH) - { - LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH); - return mDNSNULL; - } - - dlen = DomainNameLength(service); - - // Allocate space for the name and RData. - rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + dlen + sizeof(RData)); - if (!rr) - return mDNSNULL; - name = (domainname *)((mDNSu8 *)rr + sizeof(ResourceRecord)); - rr->RecordType = kDNSRecordTypePacketAuth; - rr->InterfaceID = mDNSInterface_Any; - rr->name = (const domainname *)name; - rr->rrtype = kDNSType_NSEC3; - rr->rrclass = kDNSClass_IN; - rr->rroriginalttl = kStandardTTL; - rr->rDNSServer = mDNSNULL; - rr->rdlength = MCAST_NSEC3_RDLENGTH; - rr->rdestimate = MCAST_NSEC3_RDLENGTH; - rr->rdata = (RData *)((mDNSu8 *)rr->name + dlen); - - AssignDomainName(name, service); - if (!InitializeNSEC3Record(rr, AnonData, len, salt)) - { - mDNSPlatformMemFree(rr); - return mDNSNULL; - } - return rr; -} - -mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr) -{ - AnonInfoResourceRecord *anonRR; - domainname *name; - mDNSu32 neededLen; - mDNSu32 extraLen; - - if (rr->rdlength < MCAST_NSEC3_RDLENGTH) - { - LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH); - return mDNSNULL; - } - // Allocate space for the name and the rdata along with the ResourceRecord - neededLen = rr->rdlength + DomainNameLength(rr->name); - extraLen = (neededLen > sizeof(RDataBody)) ? (neededLen - sizeof(RDataBody)) : 0; - anonRR = (AnonInfoResourceRecord *)mDNSPlatformMemAllocate(sizeof(AnonInfoResourceRecord) + extraLen); - if (!anonRR) - return mDNSNULL; - - anonRR->resrec = *rr; - - anonRR->rdatastorage.MaxRDLength = rr->rdlength; - mDNSPlatformMemCopy(anonRR->rdatastorage.u.data, rr->rdata->u.data, rr->rdlength); - - name = (domainname *)(anonRR->rdatastorage.u.data + rr->rdlength); - AssignDomainName(name, rr->name); - - anonRR->resrec.name = name; - anonRR->resrec.rdata = &anonRR->rdatastorage; - - si->nsec3RR = (ResourceRecord *)anonRR; - - return si->nsec3RR; -} - -// When a service is started or a browse is started with the Anonymous data, we allocate a new random -// number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and -// the anonymous data. -// -// If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can -// check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received. -mDNSexport AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr) -{ - AnonymousInfo *ai; - ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo)); - if (!ai) - { - return mDNSNULL; - } - mDNSPlatformMemZero(ai, sizeof(AnonymousInfo)); - if (rr) - { - if (!CopyNSEC3ResourceRecord(ai, rr)) - { - mDNSPlatformMemFree(ai); - return mDNSNULL; - } - return ai; - } - ai->salt = mDNSRandom(0xFFFFFFFF); - ai->AnonData = mDNSPlatformMemAllocate(len); - if (!ai->AnonData) - { - mDNSPlatformMemFree(ai); - return mDNSNULL; - } - ai->AnonDataLen = len; - mDNSPlatformMemCopy(ai->AnonData, data, len); - ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt); - if (!ai->nsec3RR) - { - mDNSPlatformMemFree(ai); - return mDNSNULL; - } - return ai; -} - -mDNSexport void FreeAnonInfo(AnonymousInfo *ai) -{ - if (ai->nsec3RR) - mDNSPlatformMemFree(ai->nsec3RR); - if (ai->AnonData) - mDNSPlatformMemFree(ai->AnonData); - mDNSPlatformMemFree(ai); -} - -mDNSexport void ReInitAnonInfo(AnonymousInfo **AnonInfo, const domainname *name) -{ - if (*AnonInfo) - { - AnonymousInfo *ai = *AnonInfo; - *AnonInfo = AllocateAnonInfo(name, ai->AnonData, ai->AnonDataLen, mDNSNULL); - if (!(*AnonInfo)) - *AnonInfo = ai; - else - FreeAnonInfo(ai); - } -} - -// This function should be used only if you know that the question and -// the resource record belongs to the same set. The main usage is -// in ProcessQuery where we find the question to be part of the same -// set as the resource record, but it needs the AnonData to be -// initialized so that it can walk the cache records to see if they -// answer the question. -mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) -{ - if (!q->AnonInfo || !rr->AnonInfo) - { - LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); - return; - } - - debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); - if (ForQuestion) - { - if (q->AnonInfo->AnonDataLen < rr->AnonInfo->AnonDataLen) - { - mDNSPlatformMemFree(q->AnonInfo->AnonData); - q->AnonInfo->AnonData = mDNSNULL; - } - - if (!q->AnonInfo->AnonData) - { - q->AnonInfo->AnonData = mDNSPlatformMemAllocate(rr->AnonInfo->AnonDataLen); - if (!q->AnonInfo->AnonData) - return; - } - mDNSPlatformMemCopy(q->AnonInfo->AnonData, rr->AnonInfo->AnonData, rr->AnonInfo->AnonDataLen); - q->AnonInfo->AnonDataLen = rr->AnonInfo->AnonDataLen; - } - else - { - if (rr->AnonInfo->AnonDataLen < q->AnonInfo->AnonDataLen) - { - mDNSPlatformMemFree(rr->AnonInfo->AnonData); - rr->AnonInfo->AnonData = mDNSNULL; - } - - if (!rr->AnonInfo->AnonData) - { - rr->AnonInfo->AnonData = mDNSPlatformMemAllocate(q->AnonInfo->AnonDataLen); - if (!rr->AnonInfo->AnonData) - return; - } - mDNSPlatformMemCopy(rr->AnonInfo->AnonData, q->AnonInfo->AnonData, q->AnonInfo->AnonDataLen); - rr->AnonInfo->AnonDataLen = q->AnonInfo->AnonDataLen; - } -} - -// returns -1 if the caller should ignore the result -// returns 1 if the record answers the question -// returns 0 if the record does not answer the question -mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) -{ - mDNSexport mDNS mDNSStorage; - ResourceRecord *nsec3RR; - int i; - AnonymousInfo *qai, *rai; - mDNSu8 *AnonData; - int AnonDataLen; - rdataNSEC3 *nsec3; - int hlen; - int nxtLength; - mDNSu8 *nxtName; - mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; - mDNSPlatformMemZero(hashName, sizeof(hashName)); - - debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c); - - // Currently only PTR records can have anonymous information - if (q->qtype != kDNSType_PTR) - { - return -1; - } - - // We allow anonymous questions to be answered by both normal services (without the - // anonymous information) and anonymous services that are part of the same set. And - // normal questions discover normal services and all anonymous services. - // - // The three cases have been enumerated clearly even though they all behave the - // same way. - if (!q->AnonInfo) - { - debugf("AnonInfoAnswersQuestion: not a anonymous type question"); - if (!rr->AnonInfo) - { - // case 1 - return -1; - } - else - { - // case 2 - debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c); - return -1; - } - } - else - { - // case 3 - if (!rr->AnonInfo) - { - debugf("AnonInfoAnswersQuestion: not a anonymous type record"); - return -1; - } - } - - // case 4: We have the anonymous information both in the question and the record. We need - // two sets of information to validate. - // - // 1) Anonymous data that identifies the set/group - // 2) NSEC3 record that contains the hash and the salt - // - // If the question is a remote one, it does not have the anonymous information to validate (just - // the NSEC3 record) and hence the anonymous data should come from the local resource record. If the - // question is local, it can come from either of them and if there is a mismatch between the - // question and record, it won't validate. - - qai = q->AnonInfo; - rai = rr->AnonInfo; - - if (qai->AnonData && rai->AnonData) - { - // Before a cache record is created, if there is a matching question i.e., part - // of the same set, then when the cache is created we also set the anonymous - // information. Otherwise, the cache record contains just the NSEC3 record and we - // won't be here for that case. - // - // It is also possible that a local question is matched against the local AuthRecord - // as that is also the case for which the AnonData would be non-NULL for both. - // We match questions against AuthRecords (rather than the cache) for LocalOnly case and - // to see whether a .local query should be suppressed or not. The latter never happens - // because PTR queries are never suppressed. - - // If they don't belong to the same anonymous set, then no point in validating. - if ((qai->AnonDataLen != rai->AnonDataLen) || - mDNSPlatformMemCmp(qai->AnonData, rai->AnonData, qai->AnonDataLen) != 0) - { - debugf("AnonInfoAnswersQuestion: AnonData mis-match for record %s question %##s ", - RRDisplayString(&mDNSStorage, rr), q->qname.c); - return 0; - } - // AnonData matches i.e they belong to the same group and the same service. - LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c, - rr->name->c); - return 1; - } - else - { - debugf("AnonInfoAnswersQuestion: question %p, record %p", qai->AnonData, rai->AnonData); - } - - if (qai->AnonData) - { - // If there is AnonData, then this is a local question. The - // NSEC3 RR comes from the resource record which could be part - // of the cache or local auth record. The cache entry could - // be from a remote host or created when we heard our own - // announcements. In any case, we use that to see if it matches - // the question. - AnonData = qai->AnonData; - AnonDataLen = qai->AnonDataLen; - nsec3RR = rai->nsec3RR; - } - else - { - // Remote question or hearing our own question back - AnonData = rai->AnonData; - AnonDataLen = rai->AnonDataLen; - nsec3RR = qai->nsec3RR; - } - - if (!AnonData || !nsec3RR) - { - // AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for - // that too and we can end up here for that case. - debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR, - q->qname.c, RRDisplayString(&mDNSStorage, rr)); - return 0; - } - debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR)); - - - nsec3 = (rdataNSEC3 *)nsec3RR->rdata->u.data; - - if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen)) - { - LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for %##s", nsec3RR->name->c); - return mDNSfalse; - } - if (hlen != SHA1_HASH_LENGTH) - { - LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen); - return mDNSfalse; - } - - NSEC3Parse(nsec3RR, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL); - - if (hlen != nxtLength) - { - LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen, nxtLength); - return mDNSfalse; - } - - for (i = 0; i < nxtLength; i++) - { - if (nxtName[i] != hashName[i]) - { - debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i); - return 0; - } - } - LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype)); - return 1; -} - -// Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order. -// Similarly we also parse the NSEC3 records in order and this mapping to the questions and records -// respectively. -mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name) -{ - CacheRecord *cr; - CacheRecord **prev = nsec3; - - (void) m; - - for (cr = *nsec3; cr; cr = cr->next) - { - if (SameDomainName(cr->resrec.name, name)) - { - debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c); - *prev = cr->next; - cr->next = mDNSNULL; - return cr; - } - prev = &cr->next; - } - return mDNSNULL; -} - -mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) -{ - CacheRecord *nsec3CR; - - if (q->qtype != kDNSType_PTR) - return; - - nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, &q->qname); - if (nsec3CR) - { - q->AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); - if (q->AnonInfo) - { - debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)", - RRDisplayString(m, q->AnonInfo->nsec3RR), q->qname.c, DNSTypeName(q->qtype)); - } - ReleaseCacheRecord(m, nsec3CR); - } -} - -mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) -{ - CacheRecord *nsec3CR; - - if (!(*McastNSEC3Records)) - return; - - // If already initialized or not a PTR type, we don't have to do anything - if (cr->resrec.AnonInfo || cr->resrec.rrtype != kDNSType_PTR) - return; - - nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, cr->resrec.name); - if (nsec3CR) - { - cr->resrec.AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); - if (cr->resrec.AnonInfo) - { - debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)", - RRDisplayString(m, cr->resrec.AnonInfo->nsec3RR), cr->resrec.name->c, - DNSTypeName(cr->resrec.rrtype)); - } - ReleaseCacheRecord(m, nsec3CR); - } -} - -mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) -{ - // if a1 is NULL and a2 is not NULL AND vice-versa - // return false as there is a change. - if ((a1 != mDNSNULL) != (a2 != mDNSNULL)) - return mDNSfalse; - - // Both could be NULL or non-NULL - if (a1 && a2) - { - // The caller already verified that the owner name is the same. - // Check whether the RData is same. - if (!IdenticalSameNameRecord(a1->nsec3RR, a2->nsec3RR)) - { - debugf("IdenticalAnonInfo: nsec3RR mismatch"); - return mDNSfalse; - } - } - return mDNStrue; -} - -mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) -{ - AnonymousInfo *aifrom = crfrom->resrec.AnonInfo; - AnonymousInfo *aito = crto->resrec.AnonInfo; - - (void) m; - - if (!aifrom) - return; - - if (aito) - { - crto->resrec.AnonInfo = aifrom; - FreeAnonInfo(aito); - crfrom->resrec.AnonInfo = mDNSNULL; - } - else - { - FreeAnonInfo(aifrom); - crfrom->resrec.AnonInfo = mDNSNULL; - } -} - -#else // !ANONYMOUS_DISABLED - -mDNSexport void ReInitAnonInfo(AnonymousInfo **si, const domainname *name) -{ - (void)si; - (void)name; -} - -mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr) -{ - (void)service; - (void)AnonData; - (void)len; - (void)rr; - - return mDNSNULL; -} - -mDNSexport void FreeAnonInfo(AnonymousInfo *ai) -{ - (void)ai; -} - -mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) -{ - (void)q; - (void)rr; - (void)ForQuestion; -} - -mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) -{ - (void)rr; - (void)q; - - return mDNSfalse; -} - -mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) -{ - (void)m; - (void)McastNSEC3Records; - (void)q; -} - -mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) -{ - (void)m; - (void)McastNSEC3Records; - (void)cr; -} - -mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) -{ - (void)m; - (void)crto; - (void)crfrom; -} - -mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) -{ - (void)a1; - (void)a2; - - return mDNStrue; -} - -#endif // !ANONYMOUS_DISABLED diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/anonymous.h b/usr/src/contrib/mDNSResponder/mDNSCore/anonymous.h deleted file mode 100644 index b60812ea0d..0000000000 --- a/usr/src/contrib/mDNSResponder/mDNSCore/anonymous.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2012 Apple Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ANONYMOUS_H_ -#define __ANONYMOUS_H_ - -extern void ReInitAnonInfo(AnonymousInfo **si, const domainname *name); -extern AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr); -extern void FreeAnonInfo(AnonymousInfo *ai); -extern void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion); -extern int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); -extern void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr); -extern void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q); -extern void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom); -extern mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2); - -#endif diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/dnsproxy.h b/usr/src/contrib/mDNSResponder/mDNSCore/dnsproxy.h index 7008c058da..dcb7d55faf 100644 --- a/usr/src/contrib/mDNSResponder/mDNSCore/dnsproxy.h +++ b/usr/src/contrib/mDNSResponder/mDNSCore/dnsproxy.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011-2013 Apple Inc. All rights reserved. + * Copyright (c) 2011-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +24,13 @@ extern void ProxyUDPCallback(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); extern void ProxyTCPCallback(void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, - const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64) +extern void DNSProxyInit(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf, const mDNSu8 IPv6Prefix[16], int IPv6PrefixLen, + mDNSBool alwaysSynthesize); +#else extern void DNSProxyInit(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf); +#endif extern void DNSProxyTerminate(void); #endif // __DNS_PROXY_H diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/dnssec.h b/usr/src/contrib/mDNSResponder/mDNSCore/dnssec.h deleted file mode 100644 index b770af8de0..0000000000 --- a/usr/src/contrib/mDNSResponder/mDNSCore/dnssec.h +++ /dev/null @@ -1,158 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2011-2013 Apple Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DNSSEC_H -#define __DNSSEC_H - -#include "CryptoAlg.h" -#include "mDNSDebug.h" - -typedef enum -{ - RRVS_rr, RRVS_rrsig, RRVS_key, RRVS_rrsig_key, RRVS_ds, RRVS_done, -} RRVerifierSet; - -typedef struct RRVerifier_struct RRVerifier; -typedef struct DNSSECVerifier_struct DNSSECVerifier; -typedef struct AuthChain_struct AuthChain; -typedef struct InsecureContext_struct InsecureContext; - -struct RRVerifier_struct -{ - RRVerifier *next; - mDNSu16 rrtype; - mDNSu16 rrclass; - mDNSu32 rroriginalttl; - mDNSu16 rdlength; - mDNSu16 found; - mDNSu32 namehash; - mDNSu32 rdatahash; - domainname name; - mDNSu8 *rdata; -}; - -// Each AuthChain element has one rrset (with multiple resource records of same type), rrsig and key -// that validates the rrset. -struct AuthChain_struct -{ - AuthChain *next; // Next element in the chain - RRVerifier *rrset; // RRSET that is authenticated - RRVerifier *rrsig; // Signature for that RRSET - RRVerifier *key; // Public key for that RRSET -}; - -#define ResetAuthChain(dv) { \ - (dv)->ac = mDNSNULL; \ - (dv)->actail = &((dv)->ac); \ -} - -typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); -// -// When we do a validation for a question, there might be additional validations that needs to be done e.g., -// wildcard expanded answer. It is also possible that in the case of nsec we need to prove both that a wildcard -// does not apply and the closest encloser proves that name does not exist. We identify these with the following -// flags. -// -// Note: In the following, by "marking the validation", we mean that as part of validation we need to prove -// the ones that are marked with. -// -// A wildcard may be used to answer a question. In that case, we need to verify that the right wildcard was -// used in answering the question. This is done by marking the validation with WILDCARD_PROVES_ANSWER_EXPANDED. -// -// Sometimes we get a NXDOMAIN response. In this case, we may have a wildcard where we need to prove -// that the wildcard proves that the name does not exist. This is done by marking the validation with -// WILDCARD_PROVES_NONAME_EXISTS. -// -// In the case of NODATA error, sometimes the name may exist but the query type does not exist. This is done by -// marking the validation with NSEC_PROVES_NOTYPE_EXISTS. -// -// In both NXDOMAIN and NODATA proofs, we may have to prove that the NAME does not exist. This is done by marking -// the validation with NSEC_PROVES_NONAME_EXISTS. -// -#define WILDCARD_PROVES_ANSWER_EXPANDED 0x00000001 -#define WILDCARD_PROVES_NONAME_EXISTS 0x00000002 -#define NSEC_PROVES_NOTYPE_EXISTS 0x00000004 -#define NSEC_PROVES_NONAME_EXISTS 0x00000008 -#define NSEC3_OPT_OUT 0x00000010 // OptOut was set in NSEC3 - -struct DNSSECVerifier_struct -{ - domainname origName; // Original question name that needs verification - mDNSu16 origType; // Original question type corresponding to origName - mDNSu16 currQtype; // Current question type that is being verified - mDNSInterfaceID InterfaceID; // InterfaceID of the question - DNSQuestion q; - mDNSu8 recursed; // Number of times recursed during validation - mDNSu8 ValidationRequired; // Copy of the question's ValidationRequired status - mDNSu8 InsecureProofDone; - mDNSu8 NumPackets; // Number of packets that we send on the wire for DNSSEC verification. - mDNSs32 StartTime; // Time the DNSSEC verification starts - mDNSu32 flags; - RRVerifierSet next; - domainname *wildcardName; // set if the answer is wildcard expanded - RRVerifier *pendingNSEC; - DNSSECVerifierCallback *DVCallback; - DNSSECVerifier *parent; - RRVerifier *rrset; // rrset for which we have to verify - RRVerifier *rrsig; // RRSIG for rrset - RRVerifier *key; // DNSKEY for rrset - RRVerifier *rrsigKey; // RRSIG for DNSKEY - RRVerifier *ds; // DS for DNSKEY set in parent zone - AuthChain *saveac; - AuthChain *ac; - AuthChain **actail; - AlgContext *ctx; -}; - - -struct InsecureContext_struct -{ - DNSSECVerifier *dv; // dv for which we are doing the insecure proof - mDNSu8 skip; // labels to skip for forming the name from origName - DNSSECStatus status; // status to deliver when done - mDNSu8 triggerLabelCount; // Label count of the name that triggered the insecure proof - DNSQuestion q; -}; - -#define LogDNSSEC LogOperation - -#define DNS_SERIAL_GT(a, b) ((int)((a) - (b)) > 0) -#define DNS_SERIAL_LT(a, b) ((int)((a) - (b)) < 0) - -extern void StartDNSSECVerification(mDNS *const m, void *context); -extern RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status); -extern mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set); -extern void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q); -extern void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv); -extern DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID, - mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback); -extern void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, - mDNSu16 qtype, mDNSQuestionCallback *callback, void *context); -extern void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr); -extern void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae); -extern mStatus DNSNameToLowerCase(domainname *d, domainname *result); -extern int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len); -extern int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain); -extern void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger); -extern void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value); -extern char *DNSSECStatusName(DNSSECStatus status); - -// DNSSECProbe belongs in DNSSECSupport.h but then we don't want to expose yet another plaform specific dnssec file -// to other platforms where dnssec is not supported. -extern void DNSSECProbe(mDNS *const m); - -#endif // __DNSSEC_H diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/mDNS.c b/usr/src/contrib/mDNSResponder/mDNSCore/mDNS.c index 0d94aae681..bc51cc4144 100755 --- a/usr/src/contrib/mDNSResponder/mDNSCore/mDNS.c +++ b/usr/src/contrib/mDNSResponder/mDNSCore/mDNS.c @@ -1,6 +1,6 @@ -/* -*- Mode: C; tab-width: 4 -*- +/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*- * - * Copyright (c) 2002-2018 Apple Inc. All rights reserved. + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,9 +25,22 @@ #include "DNSCommon.h" // Defines general DNS utility routines #include "uDNS.h" // Defines entry points into unicast-specific routines -#include "nsec.h" -#include "dnssec.h" -#include "anonymous.h" + +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) +#include "D2D.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) +#include <bsm/libbsm.h> +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) +#include "dnssd_analytics.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +#include "QuerierSupport.h" +#endif // Disable certain benign warnings with Microsoft compilers #if (defined(_MSC_VER)) @@ -48,54 +61,49 @@ #include "dns_sd_internal.h" #if APPLE_OSX_mDNSResponder -#include <WebFilterDNS/WebFilterDNS.h> - // Delay in seconds before disabling multicast after there are no active queries or registrations. #define BONJOUR_DISABLE_DELAY 60 +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) +#include <WebFilterDNS/WebFilterDNS.h> -#if !NO_WCF WCFConnection *WCFConnectionNew(void) __attribute__((weak_import)); void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); +#endif -// Do we really need to define a macro for "if"? -#define CHECK_WCF_FUNCTION(X) if (X) -#endif // ! NO_WCF - -#else - -#define NO_WCF 1 -#endif // APPLE_OSX_mDNSResponder - -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) #include "Metrics.h" #endif -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) #include "DNS64.h" #endif -#ifdef UNIT_TEST -#include "unittest.h" -#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#include "dnssec_v2.h" +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) // Forward declarations mDNSlocal void BeginSleepProcessing(mDNS *const m); mDNSlocal void RetrySPSRegistrations(mDNS *const m); mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly); +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); +#endif mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q); -mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q); mDNSlocal void mDNS_SendKeepalives(mDNS *const m); mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win); -mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m); -mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m); -mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords); -mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, - const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records); -mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth); +typedef mDNSu32 DeadvertiseFlags; +#define kDeadvertiseFlag_NormalHostname (1U << 0) +#define kDeadvertiseFlag_RandHostname (1U << 1) +#define kDeadvertiseFlag_All (kDeadvertiseFlag_NormalHostname | kDeadvertiseFlag_RandHostname) +mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set, DeadvertiseFlags flags); +mDNSlocal void AdvertiseInterfaceIfNeeded(mDNS *const m, NetworkInterfaceInfo *set); +mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -105,8 +113,6 @@ mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *et // To Turn OFF mDNS_Tracer set MDNS_TRACER to 0 or undef it #define MDNS_TRACER 1 -#define NO_HINFO 1 - // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 @@ -174,8 +180,89 @@ mDNSexport const char *const mDNS_DomainTypeNames[] = #pragma mark - General Utility Functions #endif -// Returns true if this is a unique, authoritative LocalOnly record that answers questions of type -// A, AAAA , CNAME, or PTR. The caller should answer the question with this record and not send out +#if MDNS_MALLOC_DEBUGGING +// When doing memory allocation debugging, this function traverses all lists in the mDNS query +// structures and caches and checks each entry in the list to make sure it's still good. +mDNSlocal void mDNS_ValidateLists(void *context) +{ + mDNS *m = context; +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) + mDNSu32 NumAllInterfaceRecords = 0; + mDNSu32 NumAllInterfaceQuestions = 0; +#endif + + // Check core mDNS lists + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); + if (rr->resrec.name != &rr->namestorage) + LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s", + rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c); +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) + if (!AuthRecord_uDNS(rr) && !RRLocalOnly(rr)) NumAllInterfaceRecords++; +#endif + } + + for (rr = m->DuplicateRecords; rr; rr=rr->next) + { + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) + if (!AuthRecord_uDNS(rr) && !RRLocalOnly(rr)) NumAllInterfaceRecords++; +#endif + } + + rr = m->NewLocalRecords; + if (rr) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType); + + rr = m->CurrentRecord; + if (rr) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType); + + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + { + if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32) ~0) + LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next); + if (q->DuplicateOf && q->LocalSocket) + LogMemCorruption("Questions list: Duplicate Question %p should not have LocalSocket set %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) + if (!LocalOnlyOrP2PInterface(q->InterfaceID) && mDNSOpaque16IsZero(q->TargetQID)) + NumAllInterfaceQuestions++; +#endif + } + + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) + LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType); + if (cr->CRActiveQuestion) + { + for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break; + if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr)); + } + } + +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) + if (m->NumAllInterfaceRecords != NumAllInterfaceRecords) + LogMemCorruption("NumAllInterfaceRecords is %d should be %d", m->NumAllInterfaceRecords, NumAllInterfaceRecords); + + if (m->NumAllInterfaceQuestions != NumAllInterfaceQuestions) + LogMemCorruption("NumAllInterfaceQuestions is %d should be %d", m->NumAllInterfaceQuestions, NumAllInterfaceQuestions); +#endif // MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) +} +#endif // MDNS_MALLOC_DEBUGGING + +// Returns true if this is a unique, authoritative LocalOnly record that answers questions of type +// A, AAAA , CNAME, or PTR. The caller should answer the question with this record and not send out // the question on the wire if LocalOnlyRecordAnswersQuestion() also returns true. // Main use is to handle /etc/hosts records and the LocalOnly PTR records created for localhost. #define UniqueLocalOnlyRecord(rr) ((rr)->ARType == AuthRecordLocalOnly && \ @@ -209,7 +296,7 @@ mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) mDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e) { -#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 +#if MDNS_MALLOC_DEBUGGING >= 1 unsigned int i; for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; #endif @@ -243,7 +330,7 @@ mDNSlocal AuthEntity *GetAuthEntity(AuthHash *r, const AuthGroup *const Preserve // free them all individually which normally happens when we parse /etc/hosts into // AuthHash where we add the "new" entries and discard (free) the already added // entries. If we allocate as chunks, we can't free them individually. - AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity)); + AuthEntity *storage = (AuthEntity *) mDNSPlatformMemAllocateClear(sizeof(*storage)); storage->next = mDNSNULL; r->rrauth_free = storage; } @@ -314,7 +401,7 @@ mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const ResourceRecord *const rr) ag->rrauth_tail = &ag->members; ag->NewLocalOnlyRecords = mDNSNULL; if (namelen > sizeof(ag->namestorage)) - ag->name = mDNSPlatformMemAllocate(namelen); + ag->name = (domainname *) mDNSPlatformMemAllocate(namelen); else ag->name = (domainname*)ag->namestorage; if (!ag->name) @@ -452,14 +539,17 @@ mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID Interfa } // Caller should hold the lock -mDNSlocal void GenerateNegativeResponse(mDNS *const m, mDNSInterfaceID InterfaceID, QC_result qc) +mDNSlocal void GenerateNegativeResponseEx(mDNS *const m, mDNSInterfaceID InterfaceID, QC_result qc, mDNSBool noData) { DNSQuestion *q; if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; } q = m->CurrentQuestion; - LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] GenerateNegativeResponse: Generating negative response for question " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, InterfaceID, mDNSNULL); + m->rec.r.resrec.negativeRecordType = noData ? kNegativeRecordType_NoData : kNegativeRecordType_Unspecified; // We need to force the response through in the following cases // @@ -473,21 +563,24 @@ mDNSlocal void GenerateNegativeResponse(mDNS *const m, mDNSInterfaceID Interface // Don't touch the question after this m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } +#define GenerateNegativeResponse(M, INTERFACE_ID, QC) GenerateNegativeResponseEx(M, INTERFACE_ID, QC, mDNSfalse) mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr) { const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name); if (q->CNAMEReferrals >= 10 || selfref) { - LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s", - q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] AnswerQuestionByFollowingCNAME: %p " PRI_DM_NAME " (" PUB_S ") NOT following CNAME referral %d" PUB_S " for " PRI_S, + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), + q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr)); + } else { - const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value UDPSocket *sock = q->LocalSocket; mDNSOpaque16 id = q->TargetQID; -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) uDNSMetrics metrics; #endif @@ -508,10 +601,19 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re // which we would subsequently cancel and retract if the CNAME referral record were removed. // In reality this is such a corner case we'll ignore it until someone actually needs it. - LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s", - q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] AnswerQuestionByFollowingCNAME: %p " PRI_DM_NAME " (" PUB_S ") following CNAME referral %d for " PRI_S, + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), + q->CNAMEReferrals, RRDisplayString(m, rr)); -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (!mDNSOpaque16IsZero(q->TargetQID)) + { + // Must be called before zeroing out q->metrics below. + Querier_PrepareQuestionForCNAMERestart(q); + } +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) if ((q->CNAMEReferrals == 0) && !q->metrics.originalQName) { domainname * qName; @@ -520,7 +622,7 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re qNameLen = DomainNameLength(&q->qname); if ((qNameLen > 0) && (qNameLen <= MAX_DOMAIN_NAME)) { - qName = mDNSPlatformMemAllocate(qNameLen); + qName = (domainname *) mDNSPlatformMemAllocate(qNameLen); if (qName) { mDNSPlatformMemCopy(qName->c, q->qname.c, qNameLen); @@ -529,6 +631,8 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re } } metrics = q->metrics; + // The metrics will be transplanted to the restarted question, so zero out the old copy instead of using + // uDNSMetricsClear(), which will free any pointers to allocated memory. mDNSPlatformMemZero(&q->metrics, sizeof(q->metrics)); #endif mDNS_StopQuery_internal(m, q); // Stop old query @@ -539,16 +643,20 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re // to try this as unicast query even though it is a .local name if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname)) { - LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s", - q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr)); - q->InterfaceID = mDNSInterface_Unicast; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p " PRI_DM_NAME " (" PUB_S ") Record " PRI_S, + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), RRDisplayString(m, rr)); + q->IsUnicastDotLocal = mDNStrue; } + q->CNAMEReferrals += 1; // Increment value before calling mDNS_StartQuery_internal + const mDNSu32 c = q->CNAMEReferrals; // Stash a copy of the new q->CNAMEReferrals value mDNS_StartQuery_internal(m, q); // start new query // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal, // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero q->CNAMEReferrals = c; -#if AWD_METRICS - metrics.expiredAnswerState = q->metrics.expiredAnswerState; // We want the newly initialized state for this value +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + metrics.expiredAnswerState = q->metrics.expiredAnswerState; // We want the newly initialized state for this value + metrics.dnsOverTCPState = q->metrics.dnsOverTCPState; // We want the newly initialized state for this value q->metrics = metrics; #endif if (sock) @@ -577,7 +685,7 @@ mDNSlocal mDNSu8 *PunycodeConvert(const mDNSu8 *const src, mDNSu8 *const dst, co UErrorCode errorCode = U_ZERO_ERROR; UIDNAInfo info = UIDNA_INFO_INITIALIZER; UIDNA *uts46 = uidna_openUTS46(UIDNA_USE_STD3_RULES|UIDNA_NONTRANSITIONAL_TO_UNICODE, &errorCode); - int32_t len = uidna_nameToASCII_UTF8(uts46, (const char *)src+1, src[0], (char *)dst+1, end-(dst+1), &info, &errorCode); + int32_t len = uidna_nameToASCII_UTF8(uts46, (const char *)src+1, src[0], (char *)dst+1, (int32_t)(end-(dst+1)), &info, &errorCode); uidna_close(uts46); #if DEBUG_PUNYCODE if (errorCode) LogMsg("uidna_nameToASCII_UTF8(%##s) failed errorCode %d", src, errorCode); @@ -628,7 +736,7 @@ mDNSlocal mDNSBool PerformNextPunycodeConversion(const DNSQuestion *const q, dom const mDNSu8 remainder = DomainNameLength((domainname*)src); if (dst + remainder > newname->c + MAX_DOMAIN_NAME) return mDNSfalse; // Name too long -- cannot be converted to Punycode - mDNSPlatformMemCopy(newname->c, q->qname.c, h - q->qname.c); // Fill in the leading part + mDNSPlatformMemCopy(newname->c, q->qname.c, (mDNSu32)(h - q->qname.c)); // Fill in the leading part mDNSPlatformMemCopy(dst, src, remainder); // Fill in the trailing part #if DEBUG_PUNYCODE LogMsg("PerformNextPunycodeConversion: %##s converted to %##s", q->qname.c, newname->c); @@ -692,7 +800,7 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again } -mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) +mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *ar, QC_result AddRecord) { if (m->CurrentQuestion) LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)", @@ -702,12 +810,12 @@ mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, Aut { mDNSBool answered; DNSQuestion *q = m->CurrentQuestion; - if (RRAny(rr)) - answered = ResourceRecordAnswersQuestion(&rr->resrec, q); + if (RRAny(ar)) + answered = AuthRecordAnswersQuestion(ar, q); else - answered = LocalOnlyRecordAnswersQuestion(rr, q); + answered = LocalOnlyRecordAnswersQuestion(ar, q); if (answered) - AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again + AnswerLocalQuestionWithLocalAuthRecord(m, ar, AddRecord); // MUST NOT dereference q again if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now m->CurrentQuestion = q->next; } @@ -725,7 +833,7 @@ mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, Aut // AnswerAllLocalQuestionsWithLocalAuthRecord is used by the m->NewLocalRecords loop in mDNS_Execute(), // and by mDNS_Deregister_internal() -mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) +mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *ar, QC_result AddRecord) { if (m->CurrentQuestion) LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)", @@ -737,12 +845,12 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec mDNSBool answered; DNSQuestion *q = m->CurrentQuestion; // We are called with both LocalOnly/P2P record or a regular AuthRecord - if (RRAny(rr)) - answered = ResourceRecordAnswersQuestion(&rr->resrec, q); + if (RRAny(ar)) + answered = AuthRecordAnswersQuestion(ar, q); else - answered = LocalOnlyRecordAnswersQuestion(rr, q); + answered = LocalOnlyRecordAnswersQuestion(ar, q); if (answered) - AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again + AnswerLocalQuestionWithLocalAuthRecord(m, ar, AddRecord); // MUST NOT dereference q again if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now m->CurrentQuestion = q->next; } @@ -750,8 +858,8 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec m->CurrentQuestion = mDNSNULL; // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions - if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P) - AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord); + if (ar->ARType == AuthRecordLocalOnly || ar->ARType == AuthRecordP2P) + AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, ar, AddRecord); } @@ -763,18 +871,45 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA) -#define ResourceRecordIsValidAnswer(RR) ( ((RR)->resrec.RecordType & kDNSRecordTypeActiveMask) && \ - ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ - ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ - ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) ) +mDNSlocal mDNSBool ResourceRecordIsValidAnswer(const AuthRecord *const rr) +{ + if ((rr->resrec.RecordType & kDNSRecordTypeActiveMask) && + ((rr->Additional1 == mDNSNULL) || (rr->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && + ((rr->Additional2 == mDNSNULL) || (rr->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && + ((rr->DependentOn == mDNSNULL) || (rr->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask))) + { + return mDNStrue; + } + else + { + return mDNSfalse; + } +} + +mDNSlocal mDNSBool IsInterfaceValidForAuthRecord(const AuthRecord *const rr, const mDNSInterfaceID InterfaceID) +{ + if (rr->resrec.InterfaceID == mDNSInterface_Any) + { + return mDNSPlatformValidRecordForInterface(rr, InterfaceID); + } + else + { + return ((rr->resrec.InterfaceID == InterfaceID) ? mDNStrue : mDNSfalse); + } +} -#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \ - (ResourceRecordIsValidAnswer(RR) && \ - ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) +mDNSlocal mDNSBool ResourceRecordIsValidInterfaceAnswer(const AuthRecord *const rr, const mDNSInterfaceID interfaceID) +{ + return ((IsInterfaceValidForAuthRecord(rr, interfaceID) && ResourceRecordIsValidAnswer(rr)) ? mDNStrue : mDNSfalse); +} #define DefaultProbeCountForTypeUnique ((mDNSu8)3) #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) +// Parameters for handling probing conflicts +#define kMaxAllowedMCastProbingConflicts 1 // Maximum number of conflicts to allow from mcast messages. +#define kProbingConflictPauseDuration mDNSPlatformOneSecond // Duration of probing pause after an allowed mcast conflict. + // See RFC 6762: "8.3 Announcing" // "The Multicast DNS responder MUST send at least two unsolicited responses, one second apart." // Send 4, which is really 8 since we send on both IPv4 and IPv6. @@ -812,7 +947,7 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox. // To avoid this, we extend the record's effective TTL to give it a little extra grace period. -// We adjust the 100 second TTL to 127. This means that when we do our 80% query at 102 seconds, +// We adjust the 100 second TTL to 127. This means that when we do our 80% query after 102 seconds, // the cached copy at our local caching server will already have expired, so the server will be forced // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds. @@ -937,6 +1072,7 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) if (rr->ProbeCount) { + rr->ProbingConflictCount = 0; // If we have no probe suppression time set, or it is in the past, set it now if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) { @@ -1007,11 +1143,7 @@ mDNSlocal const domainname *SetUnicastTargetToHostName(mDNS *const m, AuthRecord const domainname *target; if (rr->AutoTarget) { - // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other - // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate, - // with the port number in our advertised SRV record automatically tracking the external mapped port. - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - if (!AuthInfo || !AuthInfo->AutoTunnel) rr->AutoTarget = Target_AutoHostAndNATMAP; + rr->AutoTarget = Target_AutoHostAndNATMAP; } target = GetServiceTarget(m, rr); @@ -1029,13 +1161,30 @@ mDNSlocal const domainname *SetUnicastTargetToHostName(mDNS *const m, AuthRecord } } +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) +mDNSlocal mDNSBool AuthRecordIncludesOrIsAWDL(const AuthRecord *const ar) +{ + return ((AuthRecordIncludesAWDL(ar) || mDNSPlatformInterfaceIsAWDL(ar->resrec.InterfaceID)) ? mDNStrue : mDNSfalse); +} +#endif + // Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname // Eventually we should unify this with GetServiceTarget() in uDNS.c mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) { domainname *const target = GetRRDomainNameTarget(&rr->resrec); - const domainname *newname = &m->MulticastHostname; + const domainname *newname; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (AuthRecordIncludesOrIsAWDL(rr)) + { + newname = &m->RandomizedHostname; + } + else +#endif + { + newname = &m->MulticastHostname; + } if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype)); if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage))) @@ -1133,16 +1282,18 @@ mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to Pending", ARDisplayString(m, rr), rr->state); rr->state = regState_Pending; } - rr->ProbeCount = 0; - rr->ProbeRestartCount = 0; - rr->AnnounceCount = 0; - rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; - rr->LastAPTime = m->timenow - rr->ThisAPInterval; - rr->expire = 0; // Forget about all the leases, start fresh - rr->uselease = mDNStrue; - rr->updateid = zeroID; - rr->SRVChanged = mDNSfalse; - rr->updateError = mStatus_NoError; + rr->ProbingConflictCount = 0; + rr->LastConflictPktNum = 0; + rr->ProbeRestartCount = 0; + rr->ProbeCount = 0; + rr->AnnounceCount = 0; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + rr->expire = 0; // Forget about all the leases, start fresh + rr->uselease = mDNStrue; + rr->updateid = zeroID; + rr->SRVChanged = mDNSfalse; + rr->updateError = mStatus_NoError; // RestartRecordGetZoneData calls this function whenever a new interface gets registered with core. // The records might already be registered with the server and hence could have NAT state. if (rr->NATinfo.clientContext) @@ -1233,7 +1384,6 @@ mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) return (mDNSNULL); } - mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) { if (RRLocalOnly(rr)) @@ -1243,31 +1393,99 @@ mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) return; } - if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + if (!AuthRecord_uDNS(rr) && (rr->resrec.rrtype == kDNSType_SRV) && (rr->AutoTarget == Target_AutoHost)) { - // If about to get rid of the last advertised service - if (m->AutoTargetServices == 1) - DeadvertiseAllInterfaceRecords(m); - - m->AutoTargetServices--; - LogInfo("DecrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); + NetworkInterfaceInfo *intf; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + DeadvertiseFlags flags = 0; // DeadvertiseFlags for non-AWDL interfaces. + DeadvertiseFlags flagsAWDL = 0; // DeadvertiseFlags for AWDL interfaces. + if (AuthRecordIncludesOrIsAWDL(rr)) + { + if (AuthRecordIncludesAWDL(rr)) + { + m->AutoTargetAWDLIncludedCount--; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "DecrementAutoTargetServices: AutoTargetAWDLIncludedCount %u Record " PRI_S, + m->AutoTargetAWDLIncludedCount, ARDisplayString(m, rr)); + if (m->AutoTargetAWDLIncludedCount == 0) + { + flags |= kDeadvertiseFlag_RandHostname; + if (m->AutoTargetAWDLOnlyCount == 0) flagsAWDL |= kDeadvertiseFlag_RandHostname; + } + } + else + { + m->AutoTargetAWDLOnlyCount--; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "DecrementAutoTargetServices: AutoTargetAWDLOnlyCount %u Record " PRI_S, + m->AutoTargetAWDLOnlyCount, ARDisplayString(m, rr)); + if ((m->AutoTargetAWDLIncludedCount == 0) && (m->AutoTargetAWDLOnlyCount == 0)) + { + flagsAWDL |= kDeadvertiseFlag_RandHostname; + } + } + if (flags || flagsAWDL) + { + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (!intf->Advertise) continue; + if (mDNSPlatformInterfaceIsAWDL(intf->InterfaceID)) + { + if (flagsAWDL) DeadvertiseInterface(m, intf, flagsAWDL); + } + else + { + if (flags) DeadvertiseInterface(m, intf, flags); + } + } + } + if ((m->AutoTargetAWDLIncludedCount == 0) && (m->AutoTargetAWDLOnlyCount == 0)) + { + GetRandomUUIDLocalHostname(&m->RandomizedHostname); + } + } + else +#endif + { + m->AutoTargetServices--; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "DecrementAutoTargetServices: AutoTargetServices %u Record " PRI_S, + m->AutoTargetServices, ARDisplayString(m, rr)); + if (m->AutoTargetServices == 0) + { + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->Advertise) DeadvertiseInterface(m, intf, kDeadvertiseFlag_NormalHostname); + } + } + } } -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) if (!AuthRecord_uDNS(rr)) { if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) m->NextBonjourDisableTime = NonZeroTime(m->timenow + (BONJOUR_DISABLE_DELAY * mDNSPlatformOneSecond)); m->NumAllInterfaceRecords--; - LogInfo("DecrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "DecrementAutoTargetServices: NumAllInterfaceRecords %u NumAllInterfaceQuestions %u " PRI_S, m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); } -#endif // BONJOUR_ON_DEMAND +#endif +} + +mDNSlocal void AdvertiseNecessaryInterfaceRecords(mDNS *const m) +{ + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->Advertise) AdvertiseInterfaceIfNeeded(m, intf); + } } mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) { - mDNSBool enablingBonjour = 0; + mDNSBool enablingBonjour = mDNSfalse; if (RRLocalOnly(rr)) { @@ -1276,11 +1494,12 @@ mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) return; } -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) if (!AuthRecord_uDNS(rr)) { m->NumAllInterfaceRecords++; - LogInfo("IncrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "IncrementAutoTargetServices: NumAllInterfaceRecords %u NumAllInterfaceQuestions %u " PRI_S, m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) { @@ -1290,24 +1509,43 @@ mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) // Enable Bonjour immediately by scheduling network changed processing where // we will join the multicast group on each active interface. m->BonjourEnabled = 1; - enablingBonjour = 1; + enablingBonjour = mDNStrue; m->NetworkChanged = m->timenow; } } } -#endif // BONJOUR_ON_DEMAND +#endif - if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + if (!AuthRecord_uDNS(rr) && (rr->resrec.rrtype == kDNSType_SRV) && (rr->AutoTarget == Target_AutoHost)) { - m->AutoTargetServices++; - LogInfo("IncrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); - +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (AuthRecordIncludesAWDL(rr)) + { + m->AutoTargetAWDLIncludedCount++; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "IncrementAutoTargetServices: AutoTargetAWDLIncludedCount %u Record " PRI_S, + m->AutoTargetAWDLIncludedCount, ARDisplayString(m, rr)); + } + else if (mDNSPlatformInterfaceIsAWDL(rr->resrec.InterfaceID)) + { + m->AutoTargetAWDLOnlyCount++; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "IncrementAutoTargetServices: AutoTargetAWDLOnlyCount %u Record " PRI_S, + m->AutoTargetAWDLOnlyCount, ARDisplayString(m, rr)); + } + else +#endif + { + m->AutoTargetServices++; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "IncrementAutoTargetServices: AutoTargetServices %u Record " PRI_S, + m->AutoTargetServices, ARDisplayString(m, rr)); + } // If this is the first advertised service and we did not just enable Bonjour above, then // advertise all the interface records. If we did enable Bonjour above, the interface records will // be advertised during the network changed processing scheduled above, so no need // to do it here. - if ((m->AutoTargetServices == 1) && (enablingBonjour == 0)) - AdvertiseAllInterfaceRecords(m); + if (!enablingBonjour) AdvertiseNecessaryInterfaceRecords(m); } } @@ -1553,7 +1791,6 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) if (!m->NewLocalRecords) m->NewLocalRecords = rr; // When we called SetTargetToHostName, it may have caused mDNS_Register_internal to be re-entered, appending new // records to the list, so we now need to update p to advance to the new end to the list before appending our new record. - // Note that for AutoTunnel this should never happen, but this check makes the code future-proof. while (*p) p=&(*p)->next; *p = rr; if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; @@ -1792,7 +2029,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, // we need to retract that announcement before we delete the record // If this is a record (including mDNSInterface_LocalOnly records) for which we've given local-only answers then - // it's tempting to just do "AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse)" here, but that would not not be safe. + // it's tempting to just do "AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, QC_rmv)" here, but that would not not be safe. // The AnswerAllLocalQuestionsWithLocalAuthRecord routine walks the question list invoking client callbacks, using the "m->CurrentQuestion" // mechanism to cope with the client callback modifying the question list while that's happening. // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain) @@ -1819,7 +2056,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, } // Sometimes the records don't complete proper deregistration i.e., don't wait for a response // from the server. In that case, if the records have been part of a group update, clear the - // state here. Some recors e.g., AutoTunnel gets reused without ever being completely initialized + // state here. rr->updateid = zeroID; // We defer cleaning up NAT state only after sending goodbyes. This is important because @@ -1847,14 +2084,9 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, return(mStatus_BadReferenceErr); } - // <rdar://problem/7457925> Local-only questions don't get remove events for unique records - // We may want to consider changing this code so that we generate local-only question "rmv" - // events (and maybe goodbye packets too) for unique records as well as for shared records - // Note: If we change the logic for this "if" statement, need to ensure that the code in - // CompleteDeregistration() sets the appropriate state variables to gaurantee that "else" - // clause will execute here and the record will be cut from the list. if (rr->WakeUp.HMAC.l[0] || - (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ))) + (((RecordType == kDNSRecordTypeShared) || (rr->ARType == AuthRecordLocalOnly)) && + (rr->RequireGoodbye || rr->AnsweredLocalQ))) { verbosedebugf("mDNS_Deregister_internal: Starting deregistration for %s", ARDisplayString(m, rr)); rr->resrec.RecordType = kDNSRecordTypeDeregistering; @@ -1883,10 +2115,6 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; rr->next = mDNSNULL; - // Should we generate local remove events here? - // i.e. something like: - // if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } - verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr)); rr->resrec.RecordType = kDNSRecordTypeUnregistered; @@ -1929,7 +2157,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, } else { -#if APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) // See if this record was also registered with any D2D plugins. D2D_stop_advertising_record(r2); #endif @@ -2034,21 +2262,12 @@ mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseR } } -mDNSlocal int AnonInfoSpace(AnonymousInfo *info) -{ - ResourceRecord *rr = info->nsec3RR; - - // 2 bytes for compressed name + type (2) class (2) TTL (4) rdlength (2) rdata (n) - return (2 + 10 + rr->rdlength); -} - mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID) { AuthRecord *rr; AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - int AnoninfoSpace = 0; // Make a list of all our records that need to be unicast to this destination for (rr = m->ResourceRecords; rr; rr=rr->next) @@ -2097,17 +2316,10 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d while (ResponseRecords && ResponseRecords->NR_AnswerTo) { rr = ResponseRecords; - if (rr->resrec.AnonInfo) - { - AnoninfoSpace += AnonInfoSpace(rr->resrec.AnonInfo); - rr->resrec.AnonInfo->SendNow = mDNSInterfaceMark; - } if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - // Retract the limit by AnoninfoSpace which we need to put the AnoInfo option. - newptr = PutResourceRecordTTLWithLimit(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl, - m->omsg.data + (AllowedRRSpace(&m->omsg) - AnoninfoSpace)); + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state if (!newptr && m->omsg.h.numAnswers) @@ -2122,29 +2334,6 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d rr->RequireGoodbye = mDNStrue; } - // We have reserved the space for AnonInfo option. PutResourceRecord uses the - // standard limit (AllowedRRSpace) and we should have space now. - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == mDNSInterfaceMark) - { - ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR; - - newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAuthorities, nsec3RR); - if (newptr) - { - responseptr = newptr; - debugf("SendDelayedUnicastResponse: Added NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID); - } - else - { - // We allocated space and we should not fail. Don't break, we need to clear the SendNow flag. - LogMsg("SendDelayedUnicastResponse: ERROR!! Cannot Add NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID); - } - rr->resrec.AnonInfo->SendNow = mDNSNULL; - } - } - // Add additionals, if there's space while (ResponseRecords && !ResponseRecords->NR_AnswerTo) { @@ -2164,7 +2353,7 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d } if (m->omsg.h.numAnswers) - mDNSSendDNSMessage(m, &m->omsg, responseptr, InterfaceID, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + mDNSSendDNSMessage(m, &m->omsg, responseptr, InterfaceID, mDNSNULL, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSfalse); } } @@ -2497,22 +2686,6 @@ mDNSlocal mDNSBool ShouldSendGoodbyesBeforeSleep(mDNS *const m, const NetworkInt } } -mDNSlocal mDNSBool IsInterfaceValidForAuthRecord(const AuthRecord *ar, mDNSInterfaceID InterfaceID) -{ - mDNSBool result; - - if (ar->resrec.InterfaceID == mDNSInterface_Any) - { - result = mDNSPlatformValidRecordForInterface(ar, InterfaceID); - } - else - { - result = (ar->resrec.InterfaceID == InterfaceID); - } - - return(result); -} - // Note about acceleration of announcements to facilitate automatic coalescing of // multiple independent threads of announcements into a single synchronized thread: // The announcements in the packet may be at different stages of maturity; @@ -2745,7 +2918,6 @@ mDNSlocal void SendResponses(mDNS *const m) int numDereg = 0; int numAnnounce = 0; int numAnswer = 0; - int AnoninfoSpace = 0; mDNSu8 *responseptr = m->omsg.data; mDNSu8 *newptr; InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); @@ -2783,17 +2955,6 @@ mDNSlocal void SendResponses(mDNS *const m) SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); } - if (rr->resrec.AnonInfo) - { - int tmp = AnonInfoSpace(rr->resrec.AnonInfo); - - AnoninfoSpace += tmp; - // Adjust OwnerRecordSpace/TraceRecordSpace which is used by PutRR_OS_TTL below so that - // we have space to put in the NSEC3 record in the authority section. - OwnerRecordSpace += tmp; - TraceRecordSpace += tmp; - } - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); @@ -2816,13 +2977,6 @@ mDNSlocal void SendResponses(mDNS *const m) if (newptr) // If succeeded in sending, advance to next interface { - if (rr->resrec.AnonInfo) - { - debugf("SendResponses: Marking %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, - TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); - rr->resrec.AnonInfo->SendNow = intf->InterfaceID; - } - // If sending on all interfaces, go to next interface; else we're finished now if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) rr->SendRNow = GetNextActiveInterfaceID(intf); @@ -2832,31 +2986,6 @@ mDNSlocal void SendResponses(mDNS *const m) } } - // Get the reserved space back - OwnerRecordSpace -= AnoninfoSpace; - TraceRecordSpace -= AnoninfoSpace; - newptr = responseptr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == intf->InterfaceID) - { - ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR; - - newptr = PutRR_OS_TTL(newptr, &m->omsg.h.numAuthorities, nsec3RR, nsec3RR->rroriginalttl); - if (newptr) - { - responseptr = newptr; - debugf("SendResponses: Added NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, - TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); - } - else - { - LogMsg("SendResponses: Cannot add NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, - TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); - } - rr->resrec.AnonInfo->SendNow = mDNSNULL; - } - } // Second Pass. Add additional records, if there's space. newptr = responseptr; for (rr = m->ResourceRecords; rr; rr=rr->next) @@ -3005,8 +3134,8 @@ mDNSlocal void SendResponses(mDNS *const m) numAnswer, numAnswer == 1 ? "" : "s", m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); - if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); - if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSfalse); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSfalse); if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } // There might be more things to send on this interface, so go around one more time and try again. @@ -3039,7 +3168,7 @@ mDNSlocal void SendResponses(mDNS *const m) { if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P) LogInfo("SendResponses: No active interface %d to send: %d %02X %s", - (uint32_t)rr->SendRNow, (uint32_t)rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr)); + IIDPrintable(rr->SendRNow), IIDPrintable(rr->resrec.InterfaceID), rr->resrec.RecordType, ARDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } @@ -3076,13 +3205,13 @@ mDNSlocal void SendResponses(mDNS *const m) // so allow at most 1/10 second lateness // 5. For records with rroriginalttl set to zero, that means we really want to delete them immediately // (we have a new record with DelayDelivery set, waiting for the old record to go away before we can notify clients). -#define CacheCheckGracePeriod(RR) ( \ - ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ - ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ - ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : \ - ((RR)->resrec.rroriginalttl > 0 ) ? (mDNSPlatformOneSecond/10) : 0) +#define CacheCheckGracePeriod(CR) ( \ + ((CR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ + ((CR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(CR)/50) : \ + ((CR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : \ + ((CR)->resrec.rroriginalttl > 0 ) ? (mDNSPlatformOneSecond/10) : 0) -#define NextCacheCheckEvent(RR) ((RR)->NextRequiredQuery + CacheCheckGracePeriod(RR)) +#define NextCacheCheckEvent(CR) ((CR)->NextRequiredQuery + CacheCheckGracePeriod(CR)) mDNSexport void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event) { @@ -3156,8 +3285,7 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353 && intf->SupportsUnicastMDNSResponse; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; - mDNSu8 anoninfo_space = q->AnonInfo ? AnonInfoSpace(q->AnonInfo) : 0; - mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast - anoninfo_space, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); if (!newptr) { debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -3165,18 +3293,18 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf } else { - mDNSu32 forecast = *answerforecast + anoninfo_space; + mDNSu32 forecast = *answerforecast; const CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); - CacheRecord *rr; + CacheRecord *cr; CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, - if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface - !(rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && // which is a shared (i.e. not unique) record type - rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list - rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet - SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question - rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) // If we have a resource record in our cache, + if (cr->resrec.InterfaceID == q->SendQNow && // received on this interface + !(cr->resrec.RecordType & kDNSRecordTypeUniqueMask) && // which is a shared (i.e. not unique) record type + cr->NextInKAList == mDNSNULL && ka != &cr->NextInKAList && // which is not already in the known answer list + cr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + SameNameCacheRecordAnswersQuestion(cr, q) && // which answers our question + cr->TimeRcvd + TicksTTL(cr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away mDNSPlatformOneSecond) // (also ensures we never include goodbye records with TTL=1) { // We don't want to include unique records in the Known Answer section. The Known Answer section @@ -3185,10 +3313,10 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf // which we have a unique record already in our cache, then including that unique record as a // Known Answer, so as to suppress the only answer we were expecting to get, makes little sense. - *ka = rr; // Link this record into our known answer chain - ka = &rr->NextInKAList; + *ka = cr; // Link this record into our known answer chain + ka = &cr->NextInKAList; // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - forecast += 12 + rr->resrec.rdestimate; + forecast += 12 + cr->resrec.rdestimate; // If we're trying to put more than one question in this packet, and it doesn't fit // then undo that last question and try again next time if (query->h.numQuestions > 1 && newptr + forecast >= limit) @@ -3208,14 +3336,14 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf *kalistptrptr = ka; // Update the known answer list pointer if (ucast) q->ExpectUnicastResp = NonZeroTime(m->timenow); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache, - if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface - rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list - SameNameRecordAnswersQuestion(&rr->resrec, q)) // which answers our question + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) // For every resource record in our cache, + if (cr->resrec.InterfaceID == q->SendQNow && // received on this interface + cr->NextInKAList == mDNSNULL && ka != &cr->NextInKAList && // which is not in the known answer list + SameNameCacheRecordAnswersQuestion(cr, q)) // which answers our question { - rr->UnansweredQueries++; // indicate that we're expecting a response - rr->LastUnansweredTime = m->timenow; - SetNextCacheCheckTimeForRecord(m, rr); + cr->UnansweredQueries++; // indicate that we're expecting a response + cr->LastUnansweredTime = m->timenow; + SetNextCacheCheckTimeForRecord(m, cr); } return(mDNStrue); @@ -3279,7 +3407,7 @@ mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *c for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) if (cr->resrec.rrtype == kDNSType_PTR && cr->resrec.rdlength >= 6) // If record is PTR type, with long enough name, if (cr != c0 && cr != c1) // that's not one we've seen before, - if (SameNameRecordAnswersQuestion(&cr->resrec, q)) // and answers our browse query, + if (SameNameCacheRecordAnswersQuestion(cr, q)) // and answers our browse query, if (!IdenticalSameNameRecord(&cr->resrec, &m->SPSRecords.RR_PTR.resrec)) // and is not our own advertised service... { mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); @@ -3446,15 +3574,15 @@ mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) // We forecast: qname (n) type (2) class (2) mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; const CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); - const CacheRecord *rr; - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, - if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet - SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question - rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry - rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0) // and we'll ask at least once again before NextRequiredQuery + const CacheRecord *cr; + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) // If we have a resource record in our cache, + if (cr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + SameNameCacheRecordAnswersQuestion(cr, q) && // which answers our question + cr->TimeRcvd + TicksTTL(cr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry + cr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0) // and we'll ask at least once again before NextRequiredQuery { // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - forecast += 12 + rr->resrec.rdestimate; + forecast += 12 + cr->resrec.rdestimate; if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate } return(mDNStrue); @@ -3503,11 +3631,7 @@ mDNSlocal void SendQueries(mDNS *const m) ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(cr)/20, cr->resrec.InterfaceID); // For uDNS queries (TargetQID non-zero) we adjust LastQTime, // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly - if (q->Target.type) - { - q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it - } - else if (!mDNSOpaque16IsZero(q->TargetQID)) + if (!mDNSOpaque16IsZero(q->TargetQID)) { q->LastQTime = m->timenow - q->ThisQInterval; cr->UnansweredQueries++; @@ -3542,29 +3666,7 @@ mDNSlocal void SendQueries(mDNS *const m) while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) { q = m->CurrentQuestion; - if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) - { - mDNSu8 *qptr = m->omsg.data; - const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); - - // If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time - if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(zeroIPPort); - if (q->LocalSocket) - { - InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); - qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); - mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); - q->ThisQInterval *= QuestionIntervalStep; - } - if (q->ThisQInterval > MaxQuestionInterval) - q->ThisQInterval = MaxQuestionInterval; - q->LastQTime = m->timenow; - q->LastQTxTime = m->timenow; - q->RecentAnswerPkts = 0; - q->SendQNow = mDNSNULL; - q->ExpectUnicastResp = NonZeroTime(m->timenow); - } - else if (mDNSOpaque16IsZero(q->TargetQID) && !q->Target.type && TimeToSendThisQuestion(q, m->timenow)) + if (mDNSOpaque16IsZero(q->TargetQID) && TimeToSendThisQuestion(q, m->timenow)) { //LogInfo("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces @@ -3593,7 +3695,7 @@ mDNSlocal void SendQueries(mDNS *const m) for (q = m->Questions; q && q != m->NewQuestions; q=q->next) { if (mDNSOpaque16IsZero(q->TargetQID) - && (q->SendQNow || (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) + && (q->SendQNow || (ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) { // If at least halfway to next query time, advance to next interval // If less than halfway to next query time, then @@ -3792,21 +3894,9 @@ mDNSlocal void SendQueries(mDNS *const m) else if ((Suppress = SuppressOnThisInterface(q->DupSuppress, intf)) || BuildQuestion(m, intf, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) { - // We successfully added the question to the packet. Make sure that - // we also send the NSEC3 record if required. BuildQuestion accounted for - // the space. - // - // Note: We don't suppress anonymous questions and hence Suppress should always - // be zero. - if (Suppress) m->mDNSStats.DupQuerySuppressions++; - if (!Suppress && q->AnonInfo) - { - debugf("SendQueries: marking for question %##s, Suppress %d", q->qname.c, Suppress); - q->AnonInfo->SendNow = intf->InterfaceID; - } q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); if (q->WakeOnResolveCount) { @@ -3815,7 +3905,7 @@ mDNSlocal void SendQueries(mDNS *const m) } // use background traffic class if any included question requires it - if (q->UseBackgroundTrafficClass) + if (q->UseBackgroundTraffic) { useBackgroundTrafficClass = mDNStrue; } @@ -3920,24 +4010,6 @@ mDNSlocal void SendQueries(mDNS *const m) } } - for (q = m->Questions; q; q = q->next) - { - if (q->AnonInfo && q->AnonInfo->SendNow == intf->InterfaceID) - { - mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, q->AnonInfo->nsec3RR); - if (newptr) - { - debugf("SendQueries: Added NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID); - queryptr = newptr; - } - else - { - LogMsg("SendQueries: ERROR!! Cannot add NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID); - } - q->AnonInfo->SendNow = mDNSNULL; - } - } - if (queryptr > m->omsg.data) { // If we have data to send, add OWNER/TRACER/OWNER+TRACER option if necessary, then send packet @@ -3981,12 +4053,12 @@ mDNSlocal void SendQueries(mDNS *const m) if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); - debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", + debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %d (%s)", m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", - m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); - if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL, useBackgroundTrafficClass); - if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL, useBackgroundTrafficClass); + m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", IIDPrintable(intf->InterfaceID), intf->ifname); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, useBackgroundTrafficClass); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, useBackgroundTrafficClass); if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); if (++pktcount >= 1000) { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } @@ -4012,7 +4084,7 @@ mDNSlocal void SendQueries(mDNS *const m) { if (ar->ARType != AuthRecordLocalOnly && ar->ARType != AuthRecordP2P) LogInfo("SendQueries: No active interface %d to send probe: %d %s", - (uint32_t)ar->SendRNow, (uint32_t)ar->resrec.InterfaceID, ARDisplayString(m, ar)); + IIDPrintable(ar->SendRNow), IIDPrintable(ar->resrec.InterfaceID), ARDisplayString(m, ar)); ar->SendRNow = mDNSNULL; } @@ -4047,7 +4119,7 @@ mDNSlocal void SendQueries(mDNS *const m) // so don't log the warning in that case. if (q->InterfaceID != mDNSInterface_BLE) LogInfo("SendQueries: No active interface %d to send %s question: %d %##s (%s)", - (uint32_t)q->SendQNow, x ? "new" : "old", (uint32_t)q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + IIDPrintable(q->SendQNow), x ? "new" : "old", IIDPrintable(q->InterfaceID), q->qname.c, DNSTypeName(q->qtype)); q->SendQNow = mDNSNULL; } q->CachedAnswerNeedsUpdate = mDNSfalse; @@ -4111,12 +4183,52 @@ mDNSlocal void ResetQuestionState(mDNS *const m, DNSQuestion *q) q->RecentAnswerPkts = 0; q->ThisQInterval = MaxQuestionInterval; q->RequestUnicast = 0; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) // Reset unansweredQueries so that we don't penalize this server later when we // start sending queries when the cache expires. q->unansweredQueries = 0; +#endif debugf("ResetQuestionState: Set MaxQuestionInterval for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); } +mDNSlocal void AdjustUnansweredQueries(mDNS *const m, CacheRecord *const rr) +{ + const mDNSs32 expireTime = RRExpireTime(rr); + const mDNSu32 interval = TicksTTL(rr) / 20; // Calculate 5% of the cache record's TTL. + mDNSu32 rem; + + // If the record is expired or UnansweredQueries is already at the max, then return early. + if (((m->timenow - expireTime) >= 0) || (rr->UnansweredQueries >= MaxUnansweredQueries)) return; + + if (interval == 0) + { + LogInfo("AdjustUnansweredQueries: WARNING: unusually small TTL (%d ticks) for %s", TicksTTL(rr), CRDisplayString(m, rr)); + return; + } + + // Calculate the number of whole 5% TTL intervals between now and expiration time. + rem = ((mDNSu32)(expireTime - m->timenow)) / interval; + + // Calculate the expected number of remaining refresher queries. + // Refresher queries are sent at the start of the last MaxUnansweredQueries intervals. + if (rem > MaxUnansweredQueries) rem = MaxUnansweredQueries; + + // If the current number of remaining refresher queries is greater than expected, then at least one refresher query time + // was missed. This can happen if the cache record didn't have an active question during any of the times at which + // refresher queries would have been sent if the cache record did have an active question. The cache record's + // UnansweredQueries count needs to be adjusted to avoid a burst of refresher queries being sent in an attempt to make up + // for lost time. UnansweredQueries is set to the number of queries that would have been sent had the cache record had an + // active question from the 80% point of its lifetime up to now, with one exception: if the number of expected remaining + // refresher queries is zero (because timenow is beyond the 95% point), then UnansweredQueries is set to + // MaxUnansweredQueries - 1 so that at least one refresher query is sent before the cache record expires. + // Note: The cast is safe because rem is never greater than MaxUnansweredQueries; the comparison has to be signed. + if ((MaxUnansweredQueries - rr->UnansweredQueries) > (mDNSs32)rem) + { + if (rem == 0) rem++; + rr->UnansweredQueries = (mDNSu8)(MaxUnansweredQueries - rem); + } +} + // Note: AnswerCurrentQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list. // Any code walking either list must use the m->CurrentQuestion (and possibly m->CurrentRecord) mechanism to protect against this. // In fact, to enforce this, the routine will *only* answer the question currently pointed to by m->CurrentQuestion, @@ -4130,25 +4242,6 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco q->CurrentAnswers, AddRecord ? "Add" : "Rmv", MortalityDisplayString(rr->resrec.mortality), rr->resrec.rroriginalttl, CRDisplayString(m, rr)); - // When the response for the question was validated, the entire rrset was validated. If we deliver - // a RMV for a single record in the rrset, we invalidate the response. If we deliver another add - // in the future, we will do the revalidation again. - // - // Also, if we deliver an ADD for a negative cache record and it has no NSEC/NSEC3, the ValidationStatus needs - // to be reset. This happens normally when we deliver a "secure" negative response followed by an insecure - // negative response which can happen e.g., when disconnecting from network that leads to a negative response - // due to no DNS servers. As we don't deliver RMVs for negative responses that were delivered before, we need - // to do it on the next ADD of a negative cache record. This ADD could be the result of a timeout, no DNS servers - // etc. in which case we need to reset the state to make sure we don't deliver them as secure. If this is - // a real negative response, we would reset the state here and validate the results at the end of this function. - // or the real response again if we purge the cache. - if (q->ValidationRequired && ((AddRecord == QC_rmv) || - (rr->resrec.RecordType == kDNSRecordTypePacketNegative && (AddRecord == QC_add)))) - { - q->ValidationStatus = 0; - q->ValidationState = DNSSECValRequired; - } - // Normally we don't send out the unicast query if we have answered using our local only auth records e.g., /etc/hosts. // But if the query for "A" record has a local answer but query for "AAAA" record has no local answer, we might // send the AAAA query out which will come back with CNAME and will also answer the "A" query. To prevent that, @@ -4161,7 +4254,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco return; } - if (QuerySuppressed(q)) + if (q->Suppressed && (AddRecord != QC_suppressed)) { // If the query is suppressed, then we don't want to answer from the cache. But if this query is // supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions @@ -4174,17 +4267,33 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (AddRecord == QC_add && Question_uDNS(q) && rr->resrec.RecordType != kDNSRecordTypePacketNegative && q->allowExpired != AllowExpired_None && rr->resrec.mortality == Mortality_Mortal ) rr->resrec.mortality = Mortality_Immortal; // Update a non-expired cache record to immortal if appropriate -#if AWD_METRICS - if ((AddRecord == QC_add) && Question_uDNS(q) && !followcname) +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + if ((AddRecord == QC_add) && Question_uDNS(q) && !followcname && !q->metrics.answered) { - const domainname * queryName; - mDNSu32 responseLatencyMs; - mDNSBool isForCellular; - - queryName = q->metrics.originalQName ? q->metrics.originalQName : &q->qname; - isForCellular = (q->qDNSServer && q->qDNSServer->cellIntf); - if (!q->metrics.answered) + mDNSBool skipUpdate = mDNSfalse; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (!q->dnsservice || (mdns_dns_service_get_resolver_type(q->dnsservice) != mdns_resolver_type_normal)) + { + skipUpdate = mDNStrue; + } +#endif + if (!skipUpdate) { + const domainname * queryName; + mDNSu32 responseLatencyMs, querySendCount; + mDNSBool isForCellular; + + queryName = q->metrics.originalQName ? q->metrics.originalQName : &q->qname; + querySendCount = q->metrics.querySendCount; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (q->querier) + { + querySendCount += mdns_querier_get_send_count(q->querier); + } + isForCellular = mdns_dns_service_interface_is_cellular(q->dnsservice); +#else + isForCellular = (q->qDNSServer && q->qDNSServer->isCell); +#endif if (q->metrics.querySendCount > 0) { responseLatencyMs = ((m->timenow - q->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; @@ -4193,14 +4302,10 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco { responseLatencyMs = 0; } - - MetricsUpdateDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, q->metrics.expiredAnswerState, responseLatencyMs, isForCellular); - q->metrics.answered = mDNStrue; - } - if (q->metrics.querySendCount > 0) - { - MetricsUpdateDNSResolveStats(queryName, &rr->resrec, isForCellular); + MetricsUpdateDNSQueryStats(queryName, q->qtype, &rr->resrec, querySendCount, q->metrics.expiredAnswerState, + q->metrics.dnsOverTCPState, responseLatencyMs, isForCellular); } + q->metrics.answered = mDNStrue; } #endif // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue) @@ -4209,10 +4314,14 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q && rr->resrec.mortality != Mortality_Ghost) { - if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d", rr->CRActiveQuestion, q, CRDisplayString(m,rr), q->CurrentAnswers); - rr->CRActiveQuestion = q; // We know q is non-null + if (!rr->CRActiveQuestion) + { + m->rrcache_active++; // If not previously active, increment rrcache_active count + AdjustUnansweredQueries(m, rr); // Adjust UnansweredQueries in case the record missed out on refresher queries + } + rr->CRActiveQuestion = q; // We know q is non-null SetNextCacheCheckTimeForRecord(m, rr); } @@ -4232,7 +4341,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) // If DNS64StateMachine() returns true, then the question was restarted as a different question, so return. if (!mDNSOpaque16IsZero(q->TargetQID) && DNS64StateMachine(m, q, &rr->resrec, AddRecord)) return; #endif @@ -4272,51 +4381,43 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls if (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)) { - CacheRecord neg; - MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->qDNSServer); - q->QuestionCallback(m, q, &neg.resrec, AddRecord); + if (mDNSOpaque16IsZero(q->TargetQID)) + { + CacheRecord neg; + #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSPlatformMemZero(&neg, sizeof(neg)); + MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->dnsservice); + #else + MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->qDNSServer); + #endif + q->QuestionCallback(m, q, &neg.resrec, AddRecord); + } } else { -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) if (DNS64ShouldAnswerQuestion(q, &rr->resrec)) { - DNS64AnswerQuestion(m, q, &rr->resrec, AddRecord); + DNS64AnswerCurrentQuestion(m, &rr->resrec, AddRecord); } else #endif { +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + get_denial_records_from_negative_cache_to_dnssec_context(q->DNSSECStatus.enable_dnssec, + q->DNSSECStatus.context, rr); +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) q->QuestionCallback(m, q, &rr->resrec, AddRecord); } } mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again } - // If this is an "Add" operation and this question needs validation, validate the response. - // In the case of negative responses, extra care should be taken. Negative cache records are - // used for many purposes. For example, - // - // 1) Suppressing questions (SuppressUnusable) - // 2) Timeout questions - // 3) The name does not exist - // 4) No DNS servers are available and we need a quick response for the application - // - // (1) and (2) are handled by "QC_add" check as AddRecord would be "QC_forceresponse" or "QC_suppressed" - // in that case. For (3), it is possible that we don't get nsecs back but we still need to call - // VerifySignature so that we can deliver the appropriate DNSSEC result. There is no point in verifying - // signature for (4) and hence the explicit check for q->qDNSServer. - // - if (m->CurrentQuestion == q && (AddRecord == QC_add) && !q->ValidatingResponse && q->ValidationRequired && - q->ValidationState == DNSSECValRequired && q->qDNSServer) - { - q->ValidationState = DNSSECValInProgress; - // Treat it as callback call as that's what dnssec code expects - mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls - VerifySignature(m, mDNSNULL, q); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - return; - } + // Note: Proceed with caution after this point because client callback function + // invoked above is allowed to do anything, such as starting/stopping queries + // (including this one itself, or the next or previous query in the linked list), + // registering/deregistering records, starting/stopping NAT traversals, etc. - if ((m->CurrentQuestion == q) && !ValidatingQuestion(q)) + if (m->CurrentQuestion == q) { // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG), // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated @@ -4333,9 +4434,9 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco } } -mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) +mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *cr) { - rr->DelayDelivery = 0; + cr->DelayDelivery = 0; if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); @@ -4343,8 +4444,8 @@ mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) { DNSQuestion *q = m->CurrentQuestion; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (CacheRecordAnswersQuestion(cr, q)) + AnswerCurrentQuestionWithResourceRecord(m, cr, QC_add); if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now m->CurrentQuestion = q->next; } @@ -4381,7 +4482,7 @@ mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *c // Note: CacheRecordAdd calls AnswerCurrentQuestionWithResourceRecord which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) +mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *cr) { DNSQuestion *q; @@ -4389,7 +4490,7 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) // counters here we'll end up double-incrementing them when we do it again in AnswerNewQuestion(). for (q = m->Questions; q && q != m->NewQuestions; q=q->next) { - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + if (CacheRecordAnswersQuestion(cr, q)) { mDNSIPPort zp = zeroIPPort; // If this question is one that's actively sending queries, and it's received ten answers within one @@ -4411,28 +4512,30 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) SetNextQueryTime(m,q); } } - verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", rr, rr->resrec.name->c, - DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl, rr->resrec.rDNSServer ? - &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ? - rr->resrec.rDNSServer->port : zp), q); + verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", cr, cr->resrec.name->c, + DNSTypeName(cr->resrec.rrtype), cr->resrec.rroriginalttl, cr->resrec.rDNSServer ? + &cr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(cr->resrec.rDNSServer ? + cr->resrec.rDNSServer->port : zeroIPPort), q); q->CurrentAnswers++; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) q->unansweredQueries = 0; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; +#endif + if (cr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; if (q->CurrentAnswers > 4000) { static int msgcount = 0; if (msgcount++ < 10) LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack", q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers); - rr->resrec.rroriginalttl = 0; - rr->UnansweredQueries = MaxUnansweredQueries; + cr->resrec.rroriginalttl = 0; + cr->UnansweredQueries = MaxUnansweredQueries; } } } - if (!rr->DelayDelivery) + if (!cr->DelayDelivery) { if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); @@ -4440,15 +4543,15 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) { q = m->CurrentQuestion; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (CacheRecordAnswersQuestion(cr, q)) + AnswerCurrentQuestionWithResourceRecord(m, cr, QC_add); if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now m->CurrentQuestion = q->next; } m->CurrentQuestion = mDNSNULL; } - SetNextCacheCheckTimeForRecord(m, rr); + SetNextCacheCheckTimeForRecord(m, cr); } // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. @@ -4461,7 +4564,7 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) // Note: NoCacheAnswer calls AnswerCurrentQuestionWithResourceRecord which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) +mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *cr) { LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); if (m->CurrentQuestion) @@ -4472,8 +4575,8 @@ mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) while (m->CurrentQuestion) { DNSQuestion *q = m->CurrentQuestion; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_addnocache); // QC_addnocache means "don't expect remove events for this" + if (CacheRecordAnswersQuestion(cr, q)) + AnswerCurrentQuestionWithResourceRecord(m, cr, QC_addnocache); // QC_addnocache means "don't expect remove events for this" if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now m->CurrentQuestion = q->next; } @@ -4484,12 +4587,12 @@ mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) // Note that CacheRecordRmv is *only* called for records that are referenced by at least one active question. // If new questions are created as a result of invoking client callbacks, they will be added to // the end of the question list, and m->NewQuestions will be set to indicate the first new question. -// rr is an existing cache CacheRecord that just expired and is being deleted +// cr is an existing cache CacheRecord that just expired and is being deleted // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). // Note: CacheRecordRmv calls AnswerCurrentQuestionWithResourceRecord which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) +mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *cr) { if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", @@ -4505,24 +4608,26 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) // response. A cache may be present that answers this question e.g., cache entry generated // before the question became suppressed. We need to skip the suppressed questions here as // the RMV event has already been generated. - if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q) && - (q->allowExpired == AllowExpired_None || rr->resrec.mortality == Mortality_Mortal)) + if (!q->Suppressed && CacheRecordAnswersQuestion(cr, q) && + (q->allowExpired == AllowExpired_None || cr->resrec.mortality == Mortality_Mortal)) { - verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); + verbosedebugf("CacheRecordRmv %p %s", cr, CRDisplayString(m, cr)); q->FlappingInterface1 = mDNSNULL; q->FlappingInterface2 = mDNSNULL; - if (q->CurrentAnswers == 0) { - mDNSIPPort zp = zeroIPPort; + if (q->CurrentAnswers == 0) + { +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) LogMsg("CacheRecordRmv ERROR!!: How can CurrentAnswers already be zero for %p %##s (%s) DNSServer %#a:%d", q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, - mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zp)); - } + mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort)); +#endif + } else { q->CurrentAnswers--; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + if (cr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; } // If we have dropped below the answer threshold for this mDNS question, @@ -4535,15 +4640,15 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) LogInfo("CacheRecordRmv: (%s) %##s dropped below threshold of %d answers", DNSTypeName(q->qtype), q->qname.c, q->BrowseThreshold); } - if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results + if (cr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results { if ((q->CurrentAnswers == 0) && mDNSOpaque16IsZero(q->TargetQID)) { LogInfo("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); - ReconfirmAntecedents(m, &q->qname, q->qnamehash, rr->resrec.InterfaceID, 0); + ReconfirmAntecedents(m, &q->qname, q->qnamehash, cr->resrec.InterfaceID, 0); } - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); + AnswerCurrentQuestionWithResourceRecord(m, cr, QC_rmv); } } if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now @@ -4554,7 +4659,7 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e) { -#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 +#if MDNS_MALLOC_DEBUGGING >= 1 unsigned int i; for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; #endif @@ -4600,8 +4705,6 @@ mDNSlocal void ReleaseAdditionalCacheRecords(mDNS *const m, CacheRecord **rp) if (!rr->resrec.InterfaceID) { m->rrcache_totalused_unicast -= rr->resrec.rdlength; - if (DNSSECRecordType(rr->resrec.rrtype)) - BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, rr->resrec.rdlength); } ReleaseCacheEntity(m, (CacheEntity *)rr); } @@ -4614,6 +4717,13 @@ mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r)); if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata); r->resrec.rdata = mDNSNULL; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_forget(&r->resrec.dnsservice); +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + release_denial_records_in_cache_record(r); +#endif cg = CacheGroupForRecord(m, &r->resrec); @@ -4632,21 +4742,11 @@ mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) } r->resrec.name = mDNSNULL; - if (r->resrec.AnonInfo) - { - debugf("ReleaseCacheRecord: freeing AnonInfo for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); - FreeAnonInfo((void *)r->resrec.AnonInfo); - } - r->resrec.AnonInfo = mDNSNULL; - if (!r->resrec.InterfaceID) { m->rrcache_totalused_unicast -= r->resrec.rdlength; - if (DNSSECRecordType(r->resrec.rrtype)) - BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, r->resrec.rdlength); } - ReleaseAdditionalCacheRecords(m, &r->nsec); ReleaseAdditionalCacheRecords(m, &r->soa); ReleaseCacheEntity(m, (CacheEntity *)r); @@ -4681,7 +4781,8 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou // a normal deferred ADD case, then AnswerCurrentQuestionWithResourceRecord will reset it to // MaxQuestionInterval. If we have inactive questions referring to negative cache entries, // don't ressurect them as they will deliver duplicate "No such Record" ADD events - if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && ActiveQuestion(q)) + if (((mDNSOpaque16IsZero(q->TargetQID) && (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) || + (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived)) && ActiveQuestion(q)) { q->ThisQInterval = InitialQuestionInterval; q->LastQTime = m->timenow - q->ThisQInterval; @@ -4693,6 +4794,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou event += MAX_GHOST_TIME; // Adjust so we can check for a ghost expiration if (rr->resrec.mortality == Mortality_Mortal || // Normal expired mortal record that needs released + rr->resrec.rroriginalttl == 0 || // Non-mortal record that is set to be purged (rr->resrec.mortality == Mortality_Ghost && m->timenow - event >= 0)) // A ghost record that expired more than MAX_GHOST_TIME ago { // Release as normal *rp = rr->next; // Cut it from the list before ReleaseCacheRecord @@ -4838,45 +4940,29 @@ mDNSlocal mDNSBool AnswerQuestionWithLORecord(mDNS *const m, DNSQuestion *q, mDN // Today, we suppress questions (not send them on the wire) for several reasons e.g., // AAAA query is suppressed because no IPv6 capability or PID is not allowed to make -// DNS requests. We need to temporarily suspend the suppress status so that we can -// deliver a negative response (AnswerCurrentQuestionWithResourceRecord does not answer -// suppressed questions) and reset it back. In the future, if there are other -// reasons for suppressing the query, this function should be updated. +// DNS requests. mDNSlocal void AnswerSuppressedQuestion(mDNS *const m, DNSQuestion *q) { - mDNSBool SuppressQuery; - mDNSBool DisallowPID; - - // If the client did not set the kDNSServiceFlagsReturnIntermediates flag, then don't generate a negative response, just - // deactivate the DNSQuestion. - if (!q->ReturnIntermed) + // If the client did not set the kDNSServiceFlagsReturnIntermediates flag, then don't generate a negative response, + // just deactivate the DNSQuestion. + if (q->ReturnIntermed) + { + GenerateNegativeResponse(m, mDNSInterface_Any, QC_suppressed); + } + else { q->ThisQInterval = 0; - return; } - - SuppressQuery = q->SuppressQuery; - DisallowPID = q->DisallowPID; - - // make sure that QuerySuppressed() returns false - q->SuppressQuery = mDNSfalse; - q->DisallowPID = mDNSfalse; - - GenerateNegativeResponse(m, mDNSInterface_Any, QC_suppressed); - - q->SuppressQuery = SuppressQuery; - q->DisallowPID = DisallowPID; } mDNSlocal void AnswerNewQuestion(mDNS *const m) { mDNSBool ShouldQueryImmediately = mDNStrue; DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) if (!mDNSOpaque16IsZero(q->TargetQID)) DNS64HandleNewQuestion(m, q); #endif CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); - mDNSBool AnsweredFromCache = mDNSfalse; verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -4906,15 +4992,26 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) // If the client's question callback deletes the question, then m->CurrentQuestion will // be advanced, and we'll exit out of the loop m->lock_rrcache = 1; - if (m->CurrentQuestion) - LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", - m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + if (m->CurrentQuestion) { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d->Q%d] AnswerNewQuestion ERROR m->CurrentQuestion already set: " PRI_DM_NAME " (" PUB_S ")", + m->CurrentQuestion->request_id, mDNSVal16(m->CurrentQuestion->TargetQID), + DM_NAME_PARAM(&m->CurrentQuestion->qname), DNSTypeName(m->CurrentQuestion->qtype)); + } + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted if (q->NoAnswer == NoAnswer_Fail) { - LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d->Q%d] AnswerNewQuestion: NoAnswer_Fail " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); + +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->dnsservice); +#else MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer); +#endif q->NoAnswer = NoAnswer_Normal; // Temporarily turn off answer suppression AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache); // Don't touch the question if it has been stopped already @@ -4933,66 +5030,67 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) if (AnswerQuestionWithLORecord(m, q, mDNSfalse)) goto exit; - // If we are not supposed to answer this question, generate a negative response. - // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question - // // If it is a question trying to validate some response, it already checked the cache for a response. If it still // reissues a question it means it could not find the RRSIGs. So, we need to bypass the cache check and send // the question out. - if (QuerySuppressed(q)) + if (q->Suppressed) { AnswerSuppressedQuestion(m, q); } - else if (!q->ValidatingResponse) + else { - CacheRecord *rr; - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + CacheRecord *cr; + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + if (SameNameCacheRecordAnswersQuestion(cr, q)) { // SecsSinceRcvd is whole number of elapsed seconds, rounded down - mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; - if (rr->resrec.rroriginalttl <= SecsSinceRcvd && q->allowExpired != AllowExpired_AllowExpiredAnswers) continue; // Go to next one in loop + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - cr->TimeRcvd)) / mDNSPlatformOneSecond; + mDNSBool IsExpired = (cr->resrec.rroriginalttl <= SecsSinceRcvd); + if (IsExpired && q->allowExpired != AllowExpired_AllowExpiredAnswers) continue; // Go to next one in loop // If this record set is marked unique, then that means we can reasonably assume we have the whole set // -- we don't need to rush out on the network and query immediately to see if there are more answers out there - if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) + if ((cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) ShouldQueryImmediately = mDNSfalse; q->CurrentAnswers++; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; - AnsweredFromCache = mDNStrue; -#if AWD_METRICS - if (q->metrics.expiredAnswerState == ExpiredAnswer_Allowed) q->metrics.expiredAnswerState = ExpiredAnswer_AnsweredWithExpired; + if (cr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + if (q->metrics.expiredAnswerState == ExpiredAnswer_Allowed) q->metrics.expiredAnswerState = IsExpired ? ExpiredAnswer_AnsweredWithExpired : ExpiredAnswer_AnsweredWithCache; #endif - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + cr->LastCachedAnswerTime = m->timenow; + dnssd_analytics_update_cache_request(mDNSOpaque16IsZero(q->TargetQID) ? CacheRequestType_multicast : CacheRequestType_unicast, CacheState_hit); +#endif + AnswerCurrentQuestionWithResourceRecord(m, cr, QC_add); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } - else if (mDNSOpaque16IsZero(q->TargetQID) && RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) + else if (mDNSOpaque16IsZero(q->TargetQID) && RRTypeIsAddressType(cr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) ShouldQueryImmediately = mDNSfalse; } // We don't use LogInfo for this "Question deleted" message because it happens so routinely that // it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs. if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; } - // Neither a local record nor a cache entry could answer this question. If this question need to be retried - // with search domains, generate a negative response which will now retry after appending search domains. - // If the query was suppressed above, we already generated a negative response. When it gets unsuppressed, - // we will retry with search domains. - if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains) - { - LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); - } - - if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } - +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + dnssd_analytics_update_cache_request(mDNSOpaque16IsZero(q->TargetQID) ? CacheRequestType_multicast : CacheRequestType_unicast, CacheState_miss); +#endif + q->InitialCacheMiss = mDNStrue; // Initial cache check is done, so mark as a miss from now on if (q->allowExpired == AllowExpired_AllowExpiredAnswers) { q->allowExpired = AllowExpired_MakeAnswersImmortal; // After looking through the cache for an answer, demote to make immortal if (q->firstExpiredQname.c[0]) // If an original query name was saved on an expired answer, start it over in case it is updated { - LogMsg("AnswerNewQuestion: Restarting original question %p firstExpiredQname %##s for allowExpiredAnswers question", q, &q->firstExpiredQname.c); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d->Q%d] AnswerNewQuestion: Restarting original question %p firstExpiredQname " PRI_DM_NAME " for allowExpiredAnswers question", + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->firstExpiredQname)); mDNS_StopQuery_internal(m, q); // Stop old query +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (!SameDomainName(&q->qname, &q->firstExpiredQname)) + { + Querier_PrepareQuestionForUnwindRestart(q); + } +#endif AssignDomainName(&q->qname, &q->firstExpiredQname); // Update qname q->qnamehash = DomainNameHashValue(&q->qname); // and namehash mDNS_StartQuery_internal(m, q); // start new query @@ -5005,7 +5103,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) // Hence we don't execute the following block of code for those cases. if (ShouldQueryImmediately && ActiveQuestion(q)) { - debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + debugf("[R%d->Q%d] AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->request_id, mDNSVal16(q->TargetQID), q->qname.c, DNSTypeName(q->qtype)); q->ThisQInterval = InitialQuestionInterval; q->LastQTime = m->timenow - q->ThisQInterval; if (mDNSOpaque16IsZero(q->TargetQID)) // For mDNS, spread packets to avoid a burst of simultaneous queries @@ -5062,7 +5160,7 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) if (LocalOnlyRecordAnswersQuestion(rr, q)) { retEv = mDNStrue; - AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); + AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_add); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } } @@ -5074,12 +5172,12 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AuthRecord *ar = m->CurrentRecord; + m->CurrentRecord = ar->next; + if (AuthRecordAnswersQuestion(ar, q)) { retEv = mDNStrue; - AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); + AnswerLocalQuestionWithLocalAuthRecord(m, ar, QC_add); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } } @@ -5105,8 +5203,11 @@ mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const Pre if (!m->rrcache_free && m->MainCallback) { if (m->rrcache_totalused != m->rrcache_size) - LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", - m->rrcache_totalused, m->rrcache_size); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "GetFreeCacheRR: count mismatch: m->rrcache_totalused %u != m->rrcache_size %u", + m->rrcache_totalused, m->rrcache_size); + } // We don't want to be vulnerable to a malicious attacker flooding us with an infinite // number of bogus records so that we keep growing our cache until the machine runs out of memory. @@ -5114,8 +5215,11 @@ mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const Pre // and we're actively using less than 1/32 of that cache, then we purge all the unused records // and recycle them, instead of allocating more memory. if (m->rrcache_size > 5000 && m->rrcache_size / 32 > m->rrcache_active) - LogInfo("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", - m->rrcache_size, m->rrcache_active); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "Possible denial-of-service attack in progress: m->rrcache_size %u; m->rrcache_active %u", + m->rrcache_size, m->rrcache_active); + } else { mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback @@ -5156,8 +5260,8 @@ mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const Pre else ReleaseCacheGroup(m, cp); } } - LogInfo("GetCacheEntity recycled %d records to reduce cache from %d to %d", - oldtotalused - m->rrcache_totalused, oldtotalused, m->rrcache_totalused); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "GetCacheEntity recycled %d records to reduce cache from %d to %d", + oldtotalused - m->rrcache_totalused, oldtotalused, m->rrcache_totalused); } if (m->rrcache_free) // If there are records in the free list, take one @@ -5166,7 +5270,7 @@ mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const Pre m->rrcache_free = e->next; if (++m->rrcache_totalused >= m->rrcache_report) { - LogInfo("RR Cache now using %ld objects", m->rrcache_totalused); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "RR Cache now using %u objects", m->rrcache_totalused); if (m->rrcache_report < 100) m->rrcache_report += 10; else if (m->rrcache_report < 1000) m->rrcache_report += 100; else m->rrcache_report += 1000; @@ -5187,7 +5291,7 @@ mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDL r->resrec.rdata = (RData*)&r->smallrdatastorage; // By default, assume we're usually going to be using local storage if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage { - r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); + r->resrec.rdata = (RData*) mDNSPlatformMemAllocateClear(sizeofRDataHeader + RDLength); if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; } } @@ -5205,7 +5309,7 @@ mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const Res cg->members = mDNSNULL; cg->rrcache_tail = &cg->members; if (namelen > sizeof(cg->namestorage)) - cg->name = mDNSPlatformMemAllocate(namelen); + cg->name = (domainname *) mDNSPlatformMemAllocate(namelen); else cg->name = (domainname*)cg->namestorage; if (!cg->name) @@ -5444,7 +5548,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) mDNS_SendKeepalives(m); } -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) if (m->NextBonjourDisableTime && (m->timenow - m->NextBonjourDisableTime >= 0)) { // Schedule immediate network change processing to leave the multicast group @@ -5455,15 +5559,12 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) LogInfo("mDNS_Execute: Scheduled network changed processing to leave multicast group."); } -#endif // BONJOUR_ON_DEMAND +#endif // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) { m->AnnounceOwner = 0; - - // This is a good time to reset the delay counter used to prevent spurious conflicts - m->DelayConflictProcessing = 0; } if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) @@ -5560,6 +5661,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { m->NewLocalOnlyRecords = mDNSfalse; for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + { for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) { for (i=0; i<100 && ag->NewLocalOnlyRecords; i++) @@ -5577,6 +5679,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // We limit about 100 per AuthGroup that can be serviced at a time if (i >= 100) LogMsg("mDNS_Execute: ag->NewLocalOnlyRecords exceeded loop limit"); } + } } // 5. See what packets we need to send @@ -5698,34 +5801,15 @@ mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q) // In cases 2 and 3 we do want to cause the question to be resent immediately (ScheduleImmediately is true) mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, mDNSBool ScheduleImmediately) { - // For now this AutoTunnel stuff is specific to Mac OS X. - // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer -#if APPLE_OSX_mDNSResponder - // Even though BTMM client tunnels are only useful for AAAA queries, we need to treat v4 and v6 queries equally. - // Otherwise we can get the situation where the A query completes really fast (with an NXDOMAIN result) and the - // caller then gives up waiting for the AAAA result while we're still in the process of setting up the tunnel. - // To level the playing field, we block both A and AAAA queries while tunnel setup is in progress, and then - // returns results for both at the same time. If we are looking for the _autotunnel6 record, then skip this logic - // as this would trigger looking up _autotunnel6._autotunnel6 and end up failing the original query. - - if (RRTypeIsAddressType(question->qtype) && PrivateQuery(question) && - !SameDomainLabel(question->qname.c, (const mDNSu8 *)"\x0c_autotunnel6")&& question->QuestionCallback != AutoTunnelCallback) - { - question->NoAnswer = NoAnswer_Suspended; - AddNewClientTunnel(question); - return; - } -#endif // APPLE_OSX_mDNSResponder - if (!question->DuplicateOf) { - debugf("ActivateUnicastQuery: %##s %s%s%s", - question->qname.c, DNSTypeName(question->qtype), PrivateQuery(question) ? " (Private)" : "", ScheduleImmediately ? " ScheduleImmediately" : ""); + debugf("ActivateUnicastQuery: %##s %s%s", + question->qname.c, DNSTypeName(question->qtype), ScheduleImmediately ? " ScheduleImmediately" : ""); question->CNAMEReferrals = 0; if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } if (question->LongLived) { - question->state = LLQ_InitialRequest; + question->state = LLQ_Init; question->id = zeroOpaque64; question->servPort = zeroIPPort; if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } @@ -5813,23 +5897,13 @@ mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDoma // If the query is suppressed, the RMV events won't be delivered if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Cache Record RMV events"); continue; } - // SuppressQuery status does not affect questions that are answered using local records + // Suppressed status does not affect questions that are answered using local records if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Local Record RMV events"); continue; } - LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d, qnameOrig %p", q, - q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains, q->qnameOrig); + LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d", q, + q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains); mDNS_StopQuery_internal(m, q); - // Reset state so that it looks like it was in the beginning i.e it should look at /etc/hosts, cache - // and then search domains should be appended. At the beginning, qnameOrig was NULL. - if (q->qnameOrig) - { - LogInfo("mDNSCoreRestartAddressQueries: qnameOrig %##s", q->qnameOrig); - AssignDomainName(&q->qname, q->qnameOrig); - mDNSPlatformMemFree(q->qnameOrig); - q->qnameOrig = mDNSNULL; - q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; - } - q->SearchListIndex = 0; + if (q->ResetHandler) q->ResetHandler(q); q->next = restart; restart = q; } @@ -5863,7 +5937,13 @@ mDNSexport void mDNSCoreRestartQueries(mDNS *const m) { q = m->CurrentQuestion; m->CurrentQuestion = m->CurrentQuestion->next; - if (!mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) ActivateUnicastQuery(m, q, mDNStrue); + if (!mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) + { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_querier_forget(&q->querier); +#endif + ActivateUnicastQuery(m, q, mDNStrue); + } } #endif @@ -6130,7 +6210,7 @@ mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkIn newrdlength += 2; rdsize = newrdlength > sizeof(RDataBody) ? newrdlength : sizeof(RDataBody); - newrd = mDNSPlatformMemAllocate(sizeof(RData) - sizeof(RDataBody) + rdsize); + newrd = (RData *) mDNSPlatformMemAllocate(sizeof(RData) - sizeof(RDataBody) + rdsize); if (!newrd) { LogMsg("UpdateKeepaliveRData: ptr NULL"); return mStatus_NoMemoryErr; } newrd->MaxRDLength = (mDNSu16) rdsize; @@ -6284,7 +6364,7 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo * LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss - err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSNULL, mDNSfalse); + err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSfalse); if (err) LogSPS("SendSPSRegistration: mDNSSendDNSMessage err %d", err); if (err && intf->SPSAddr[sps].type == mDNSAddrType_IPv4 && intf->NetWakeResolve[sps].ThisQInterval == -1) { @@ -6313,15 +6393,13 @@ mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecor mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID InterfaceID, AuthRecord *const rr) { - AuthRecord *newRR = mDNSPlatformMemAllocate(sizeof(AuthRecord)); - + AuthRecord *newRR = (AuthRecord *) mDNSPlatformMemAllocateClear(sizeof(*newRR)); if (newRR == mDNSNULL) { LogSPS("%s : could not allocate memory for new resource record", __func__); return; } - mDNSPlatformMemZero(newRR, sizeof(AuthRecord)); mDNS_SetupResourceRecord(newRR, mDNSNULL, InterfaceID, rr->resrec.rrtype, rr->resrec.rroriginalttl, rr->resrec.RecordType, rr->ARType, mDNSNULL, mDNSNULL); @@ -6729,9 +6807,6 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) // which is okay because with no outstanding resolves, or updates in flight, // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed - // Setting this flag activates the SleepLimit which delays sleep by 5 seconds and - // will allow the system to deregister any BTMM records. - m->NextScheduledSPRetry = m->timenow + (5 * mDNSPlatformOneSecond); registeredIntfIDS[registeredCount] = intf->InterfaceID; registeredCount++; } @@ -6846,7 +6921,8 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { AuthRecord *rr; - LogSPS("%s (old state %d) at %ld", sleep ? "Sleeping" : "Waking", m->SleepState, m->timenow); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_INFO, + PUB_S " (old state %d) at %d", sleep ? "Sleeping" : "Waking", m->SleepState, m->timenow); if (sleep && !m->SleepState) // Going to sleep { @@ -6875,7 +6951,8 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) if (m->SystemWakeOnLANEnabled && m->DelaySleep) { // If we just woke up moments ago, allow ten seconds for networking to stabilize before going back to sleep - LogSPS("mDNSCoreMachineSleep: Re-sleeping immediately after waking; will delay for %d ticks", m->DelaySleep - m->timenow); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, + "mDNSCoreMachineSleep: Re-sleeping immediately after waking; will delay for %d ticks", m->DelaySleep - m->timenow); m->SleepLimit = NonZeroTime(m->DelaySleep + mDNSPlatformOneSecond * 10); } else @@ -6883,18 +6960,19 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) m->DelaySleep = 0; m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10); m->mDNSStats.Sleeps++; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + Querier_HandleSleep(); +#endif BeginSleepProcessing(m); } #ifndef UNICAST_DISABLED SuspendLLQs(m); #endif -#if APPLE_OSX_mDNSResponder - RemoveAutoTunnel6Record(m); -#endif - LogSPS("mDNSCoreMachineSleep: m->SleepState %d (%s) seq %d", m->SleepState, - m->SleepState == SleepState_Transferring ? "Transferring" : - m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", m->SleepSeqNum); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, "mDNSCoreMachineSleep: m->SleepState %d (" PUB_S ") seq %d", + m->SleepState, + m->SleepState == SleepState_Transferring ? "Transferring" : + m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", m->SleepSeqNum); mDNS_Unlock(m); } else if (!sleep) // Waking up @@ -6927,16 +7005,19 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags); } m->mDNSStats.Wakes++; - m->DelayConflictProcessing = MAX_CONFLICT_PROCESSING_DELAYS; // ... and the same for NextSPSAttempt for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + Querier_HandleWake(); +#endif // Restart unicast and multicast queries mDNSCoreRestartQueries(m); // and reactivtate service registrations m->NextSRVUpdate = NonZeroTime(m->timenow + mDNSPlatformOneSecond); - LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, + "mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); // 2. Re-validate our cache records currtime = mDNSPlatformUTC(); @@ -6972,21 +7053,25 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) if (diff >= remain || diff > (2 * 24 * 3600)) { - LogInfo("mDNSCoreMachineSleep: %s: Purging cache entry SleptTime %d, Remaining TTL %d", - CRDisplayString(m, cr), diff, remain); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, + "mDNSCoreMachineSleep: " PRI_S ": Purging cache entry SleptTime %d, Remaining TTL %d", + CRDisplayString(m, cr), diff, remain); mDNS_PurgeCacheResourceRecord(m, cr); continue; } cr->TimeRcvd -= (diff * mDNSPlatformOneSecond); if (m->timenow - (cr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0) { - LogInfo("mDNSCoreMachineSleep: %s: Purging after adjusting the remaining TTL %d by %d seconds", - CRDisplayString(m, cr), remain, diff); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, + "mDNSCoreMachineSleep: " PRI_S ": Purging after adjusting the remaining TTL %d by %d seconds", + CRDisplayString(m, cr), remain, diff); mDNS_PurgeCacheResourceRecord(m, cr); } else { - LogInfo("mDNSCoreMachineSleep: %s: Adjusted the remain ttl %u by %d seconds", CRDisplayString(m, cr), remain, diff); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, + "mDNSCoreMachineSleep: " PRI_S ": Adjusted the remain ttl %u by %d seconds", + CRDisplayString(m, cr), remain, diff); } } } @@ -7016,7 +7101,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) // But if we do get a network configuration change, mDNSMacOSXNetworkChanged will call uDNS_SetupDNSConfig, which // will call mDNS_SetPrimaryInterfaceInfo, which will call RecreateNATMappings to refresh them, potentially sooner // than five seconds from now. - LogInfo("mDNSCoreMachineSleep: recreating NAT mappings in 5 seconds"); + LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, "mDNSCoreMachineSleep: recreating NAT mappings in 5 seconds"); RecreateNATMappings(m, mDNSPlatformOneSecond * 5); mDNS_Unlock(m); } @@ -7086,9 +7171,6 @@ mDNSexport mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now) { if (rr->state == regState_Refresh && rr->tcp) { LogSPS("mDNSCoreReadyForSleep: waiting for Record updateIntID 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; } - #if APPLE_OSX_mDNSResponder - if (!RecordReadyForSleep(rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; } - #endif } mDNS_Unlock(m); @@ -7134,7 +7216,7 @@ notready: return mDNSfalse; } -mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now) +mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now, mDNSNextWakeReason *outReason) { AuthRecord *ar; @@ -7143,13 +7225,19 @@ mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now) // E.g. we might wake up and find no wireless network because the base station got rebooted just at that moment, // and if that happens we don't want to just give up and go back to sleep and never try again. mDNSs32 e = now + (120 * 60 * mDNSPlatformOneSecond); // Sleep for at most 120 minutes + mDNSNextWakeReason reason = mDNSNextWakeReason_UpkeepWake; NATTraversalInfo *nat; for (nat = m->NATTraversals; nat; nat=nat->next) + { if (nat->Protocol && nat->ExpiryTime && nat->ExpiryTime - now > mDNSPlatformOneSecond*4) { mDNSs32 t = nat->ExpiryTime - (nat->ExpiryTime - now) / 10; // Wake up when 90% of the way to the expiry time - if (e - t > 0) e = t; + if ((e - t) > 0) + { + e = t; + reason = mDNSNextWakeReason_NATPortMappingRenewal; + } LogSPS("ComputeWakeTime: %p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d Wake %5d", nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP", mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result, @@ -7158,21 +7246,30 @@ mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now) nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, (t - now) / mDNSPlatformOneSecond); } - + } // This loop checks both the time we need to renew wide-area registrations, // and the time we need to renew Sleep Proxy registrations for (ar = m->ResourceRecords; ar; ar = ar->next) + { if (ar->expire && ar->expire - now > mDNSPlatformOneSecond*4) { mDNSs32 t = ar->expire - (ar->expire - now) / 10; // Wake up when 90% of the way to the expiry time - if (e - t > 0) e = t; + if ((e - t) > 0) + { + e = t; + reason = mDNSNextWakeReason_RecordRegistrationRenewal; + } LogSPS("ComputeWakeTime: %p Int %7d Next %7d Expire %7d Wake %7d %s", ar, ar->ThisAPInterval / mDNSPlatformOneSecond, (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, (t - now) / mDNSPlatformOneSecond, ARDisplayString(m, ar)); } - + } + if (outReason) + { + *outReason = reason; + } return(e - now); } @@ -7191,7 +7288,7 @@ mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const m const mDNSu8 *const limit = response->data + sizeof(response->data); const mDNSu8 *ptr = query->data; AuthRecord *rr; - mDNSu32 maxttl = mDNSMaximumTTLSeconds; + mDNSu32 maxttl = (!InterfaceID) ? mDNSMaximumUnicastTTLSeconds : mDNSMaximumMulticastTTLSeconds; int i; // Initialize the response fields so we can answer the questions @@ -7262,6 +7359,12 @@ mDNSlocal int CompareRData(const AuthRecord *const our, const CacheRecord *const if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } +#if defined(__clang_analyzer__) + // Get rid of analyzer warnings about ourptr and pktptr pointing to garbage after retruning from putRData(). + // There are no clear indications from the analyzer of the cause of the supposed problem. + mDNSPlatformMemZero(ourdata, 1); + mDNSPlatformMemZero(pktdata, 1); +#endif ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } @@ -7276,6 +7379,17 @@ mDNSlocal int CompareRData(const AuthRecord *const our, const CacheRecord *const return(-1); } +mDNSlocal mDNSBool PacketRecordMatches(const AuthRecord *const rr, const CacheRecord *const pktrr, const AuthRecord *const master) +{ + if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) + { + const AuthRecord *r2 = rr; + while (r2->DependentOn) r2 = r2->DependentOn; + if (r2 == master) return(mDNStrue); + } + return(mDNSfalse); +} + // See if we have an authoritative record that's identical to this packet record, // whose canonical DependentOn record is the specified master record. // The DependentOn pointer is typically used for the TXT record of service registrations @@ -7290,21 +7404,11 @@ mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *cons const AuthRecord *r1; for (r1 = m->ResourceRecords; r1; r1=r1->next) { - if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) - { - const AuthRecord *r2 = r1; - while (r2->DependentOn) r2 = r2->DependentOn; - if (r2 == master) return(mDNStrue); - } + if (PacketRecordMatches(r1, pktrr, master)) return(mDNStrue); } for (r1 = m->DuplicateRecords; r1; r1=r1->next) { - if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) - { - const AuthRecord *r2 = r1; - while (r2->DependentOn) r2 = r2->DependentOn; - if (r2 == master) return(mDNStrue); - } + if (PacketRecordMatches(r1, pktrr, master)) return(mDNStrue); } return(mDNSfalse); } @@ -7320,8 +7424,7 @@ mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *co { if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) { - while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet; - return(rr); + return(rr->RRSet ? rr->RRSet : rr); } } return(mDNSNULL); @@ -7374,7 +7477,7 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q { ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); if (!ptr) break; - if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && CacheRecordAnswersQuestion(&m->rec.r, q)) { FoundUpdate = mDNStrue; if (PacketRRConflict(m, our, &m->rec.r)) @@ -7425,9 +7528,13 @@ mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const Res { if (!pktrr->InterfaceID) { - mDNSu16 id1 = (pktrr->rDNSServer ? pktrr->rDNSServer->resGroupID : 0); - mDNSu16 id2 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + match = (pktrr->dnsservice == rr->resrec.dnsservice) ? mDNStrue : mDNSfalse; +#else + const mDNSu32 id1 = (pktrr->rDNSServer ? pktrr->rDNSServer->resGroupID : 0); + const mDNSu32 id2 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); match = (id1 == id2); +#endif } else match = (pktrr->InterfaceID == rr->resrec.InterfaceID); @@ -7538,7 +7645,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, DNSMessage *const response) { - mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + const mDNSBool FromLocalSubnet = mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; @@ -7556,7 +7663,6 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con mDNSu8 *responseptr = mDNSNULL; AuthRecord *rr; int i; - CacheRecord *McastNSEC3Records = mDNSNULL; // *** // *** 1. Look in Additional Section for an OPT record @@ -7581,12 +7687,6 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } - // - // Look in Authority Section for NSEC3 record - // - - mDNSParseNSEC3Records(m, query, end, InterfaceID, &McastNSEC3Records); - // *** // *** 2. Parse Question Section and mark potential answers // *** @@ -7600,9 +7700,6 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... if (!ptr) goto exit; - pktq.AnonInfo = mDNSNULL; - if (McastNSEC3Records) - InitializeAnonInfoForQuestion(m, &McastNSEC3Records, &pktq); // The only queries that *need* a multicast response are: // * Queries sent via multicast // * from port 5353 @@ -7634,7 +7731,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) + if (AnyTypeRecordAnswersQuestion(rr, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) { m->mDNSStats.MatchingAnswersForQueries++; if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype)) @@ -7644,12 +7741,6 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con else if (ResourceRecordIsValidAnswer(rr)) { NumAnswersForThisQuestion++; - // As we have verified this question to be part of the same subset, - // set the anonymous data which is needed below when walk the cache - // records to see what answers we should be expecting. The cache records - // may cache only the nsec3RR and not the anonymous data itself. - if (pktq.AnonInfo && rr->resrec.AnonInfo) - SetAnonData(&pktq, &rr->resrec, mDNStrue); // Note: We should check here if this is a probe-type query, and if so, generate an immediate // unicast answer back to the source, because timeliness in answering probes is important. @@ -7711,12 +7802,16 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // Make a list indicating which of our own cache records we expect to see updated as a result of this query // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) - if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit) + { + if (SameNameCacheRecordAnswersQuestion(cr, &pktq) && cr->resrec.rdlength <= SmallRecordLimit) + { if (!cr->NextInKAList && eap != &cr->NextInKAList) { *eap = cr; eap = &cr->NextInKAList; } + } + } } #endif // POOF_ENABLED @@ -7724,25 +7819,23 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // We only do this for non-truncated queries. Right now it would be too complicated to try // to keep track of duplicate suppression state between multiple packets, especially when we // can't guarantee to receive all of the Known Answer packets that go with a particular query. - // For anonymous question, the duplicate suppressesion should happen if the - // question belongs in the same group. As the group is expected to be - // small, we don't do the optimization for now. - if (!pktq.AnonInfo) + for (q = m->Questions; q; q=q->next) { - for (q = m->Questions; q; q=q->next) - if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) - if (!q->InterfaceID || q->InterfaceID == InterfaceID) - if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) - if (q->qtype == pktq.qtype && - q->qclass == pktq.qclass && - q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) - { *dqp = q; dqp = &q->NextInDQList; } + if (ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) + { + if (!q->InterfaceID || q->InterfaceID == InterfaceID) + { + if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) + { + if (q->qtype == pktq.qtype && + q->qclass == pktq.qclass && + q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) + { *dqp = q; dqp = &q->NextInDQList; } + } + } + } } } - if (pktq.AnonInfo) - { - FreeAnonInfo(pktq.AnonInfo); - } } // *** @@ -7830,7 +7923,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con while (*dqp) { DNSQuestion *q = *dqp; - if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + if (CacheRecordAnswersQuestion(&m->rec.r, q)) { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } else dqp = &q->NextInDQList; } @@ -8025,13 +8118,6 @@ exit: debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s", q->qname.c, DNSTypeName(q->qtype), InterfaceID, srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6"); } - - if (McastNSEC3Records) - { - debugf("ProcessQuery: McastNSEC3Records not used"); - FreeNSECRecords(m, McastNSEC3Records); - } - return(responseptr); } @@ -8074,7 +8160,7 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type); - mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, mDNSNULL, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, mDNSNULL, mDNSNULL, srcaddr, srcport, mDNSNULL, mDNSfalse); } } @@ -8095,7 +8181,8 @@ struct UDPSocket_struct mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port }; -mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, const mDNSIPPort port, const mDNSOpaque16 id, const DNSQuestion *const question, mDNSBool tcp, DNSQuestion ** suspiciousQ) +mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, const mDNSIPPort port, + const mDNSOpaque16 id, const DNSQuestion *const question, mDNSBool tcp) { DNSQuestion *q; for (q = m->Questions; q; q=q->next) @@ -8110,7 +8197,6 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, if (mDNSSameOpaque16(q->TargetQID, id)) return(q); else { - if (!tcp && suspiciousQ) *suspiciousQ = q; return(mDNSNULL); } } @@ -8125,7 +8211,6 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, { DNSQuestion *q; (void)id; - (void)srcaddr; for (q = m->Questions; q; q=q->next) { @@ -8154,8 +8239,8 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking // if (TrustedSource(m, srcaddr)) return(mDNStrue); - LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s", - q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); + LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) from %#a:%d %s", + q->qname.c, DNSTypeName(q->qtype), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); return(mDNSNULL); } } @@ -8198,21 +8283,18 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C if (!rr) NoCacheAnswer(m, &m->rec.r); else { - RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer - *rr = m->rec.r; // Block copy the CacheRecord object - rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment - rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header - rr->resrec.mortality = Mortality_Mortal; + RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer + *rr = m->rec.r; // Block copy the CacheRecord object +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_retain_null_safe(rr->resrec.dnsservice); +#endif + rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment + rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + rr->resrec.mortality = Mortality_Mortal; +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + rr->resrec.dnssec_result = dnssec_indeterminate; // Set the DNSSEC validation result of a record as "indeterminate" by default. +#endif - // We need to add the anonymous info before we call CacheRecordAdd so that - // if it finds a matching question with this record, it bumps up the counters like - // CurrentAnswers etc. Otherwise, when a cache entry gets removed, CacheRecordRmv - // will complain. - if (m->rec.r.resrec.AnonInfo) - { - rr->resrec.AnonInfo = m->rec.r.resrec.AnonInfo; - m->rec.r.resrec.AnonInfo = mDNSNULL; - } rr->DelayDelivery = delay; // If this is an oversized record with external storage allocated, copy rdata to external storage @@ -8224,7 +8306,6 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C mDNSPlatformMemCopy(rr->resrec.rdata, m->rec.r.resrec.rdata, sizeofRDataHeader + RDLength); rr->next = mDNSNULL; // Clear 'next' pointer - rr->nsec = mDNSNULL; rr->soa = mDNSNULL; if (sourceAddress) @@ -8233,10 +8314,15 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C if (!rr->resrec.InterfaceID) { m->rrcache_totalused_unicast += rr->resrec.rdlength; - if (DNSSECRecordType(rr->resrec.rrtype)) - BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeMemoryUsage, rr->resrec.rdlength); } +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + if (rr != mDNSNULL) + { + rr->denial_of_existence_records = mDNSNULL; + } +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + if (Add) { *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list @@ -8247,7 +8333,7 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C { // Can't use the "cg->name" if we are not adding to the cache as the // CacheGroup may be released anytime if it is empty - domainname *name = mDNSPlatformMemAllocate(DomainNameLength(cg->name)); + domainname *name = (domainname *) mDNSPlatformMemAllocate(DomainNameLength(cg->name)); if (name) { AssignDomainName(name, cg->name); @@ -8264,6 +8350,25 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C return(rr); } +mDNSlocal void RefreshCacheRecordCacheGroupOrder(CacheGroup *cg, CacheRecord *cr) +{ // Move the cache record to the tail of the cache group to maintain a fresh ordering + if (cg->rrcache_tail != &cr->next) // If not already at the tail + { + CacheRecord **rp; + for (rp = &cg->members; *rp; rp = &(*rp)->next) + { + if (*rp == cr) // This item points to this record + { + *rp = cr->next; // Remove this record + break; + } + } + cr->next = mDNSNULL; // This record is now last + *(cg->rrcache_tail) = cr; // Append this record to tail of cache group + cg->rrcache_tail = &(cr->next); // Advance tail pointer + } +} + mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl) { rr->TimeRcvd = m->timenow; @@ -8322,13 +8427,12 @@ mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // When the response does not match the question directly, we still want to cache them sometimes. The current response is // in m->rec. -mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist, DNSQuestion *q, mDNSBool *nseclist) +mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist) { CacheRecord *const newcr = &m->rec.r; ResourceRecord *rr = &newcr->resrec; const CacheRecord *cr; - *nseclist = mDNSfalse; for (cr = crlist; cr != (CacheRecord*)1; cr = cr->NextInCFList) { domainname *target = GetRRDomainNameTarget(&cr->resrec); @@ -8344,138 +8448,18 @@ mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist return (mDNStrue); } } - - // Either the question requires validation or we are validating a response with DNSSEC in which case - // we need to accept the RRSIGs also so that we can validate the response. It is also possible that - // we receive NSECs for our query which does not match the qname and we need to cache in that case - // too. nseclist is set if they have to be cached as part of the negative cache record. - if (q && DNSSECQuestion(q)) - { - mDNSBool same = SameDomainName(&q->qname, rr->name); - if (same && (q->qtype == rr->rrtype || rr->rrtype == kDNSType_CNAME)) - { - LogInfo("IsResponseAcceptable: Accepting, same name and qtype %s, CR %s", DNSTypeName(q->qtype), - CRDisplayString(m, newcr)); - return mDNStrue; - } - // We cache RRSIGS if it covers the question type or NSEC. If it covers a NSEC, - // "nseclist" is set - if (rr->rrtype == kDNSType_RRSIG) - { - RDataBody2 *const rdb = (RDataBody2 *)newcr->smallrdatastorage.data; - rdataRRSig *rrsig = &rdb->rrsig; - mDNSu16 typeCovered = swap16(rrsig->typeCovered); - - // Note the ordering. If we are looking up the NSEC record, then the RRSIG's typeCovered - // would match the qtype and they are cached normally as they are not used to prove the - // non-existence of any name. In that case, it is like any other normal dnssec validation - // and hence nseclist should not be set. - - if (same && ((typeCovered == q->qtype) || (typeCovered == kDNSType_CNAME))) - { - LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches question type %s", CRDisplayString(m, newcr), - DNSTypeName(q->qtype)); - return mDNStrue; - } - else if (typeCovered == kDNSType_NSEC || typeCovered == kDNSType_NSEC3) - { - LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches %s type (nseclist = 1)", CRDisplayString(m, newcr), DNSTypeName(typeCovered)); - *nseclist = mDNStrue; - return mDNStrue; - } - else if (typeCovered == kDNSType_SOA) - { - LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches SOA type (nseclist = 1)", CRDisplayString(m, newcr)); - *nseclist = mDNStrue; - return mDNStrue; - } - else return mDNSfalse; - } - if (rr->rrtype == kDNSType_NSEC) - { - if (!UNICAST_NSEC(rr)) - { - LogMsg("IsResponseAcceptable: ERROR!! Not a unicast NSEC %s", CRDisplayString(m, newcr)); - return mDNSfalse; - } - LogInfo("IsResponseAcceptable: Accepting NSEC %s (nseclist = 1)", CRDisplayString(m, newcr)); - *nseclist = mDNStrue; - return mDNStrue; - } - if (rr->rrtype == kDNSType_SOA) - { - LogInfo("IsResponseAcceptable: Accepting SOA %s (nseclist = 1)", CRDisplayString(m, newcr)); - *nseclist = mDNStrue; - return mDNStrue; - } - else if (rr->rrtype == kDNSType_NSEC3) - { - LogInfo("IsResponseAcceptable: Accepting NSEC3 %s (nseclist = 1)", CRDisplayString(m, newcr)); - *nseclist = mDNStrue; - return mDNStrue; - } - } return mDNSfalse; } -mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords) -{ - CacheRecord *rp, *next; - - for (rp = NSECRecords; rp; rp = next) - { - next = rp->next; - ReleaseCacheRecord(m, rp); - } -} - -// If we received zero DNSSEC records even when the DO/EDNS0 bit was set, we need to provide this -// information to ValidatingResponse question to indicate the DNSSEC status to the application -mDNSlocal void mDNSCoreReceiveNoDNSSECAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr, - mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) -{ - int i; - const mDNSu8 *ptr = response->data; - - for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) - { - DNSQuestion pktq; - DNSQuestion *qptr = mDNSNULL; - ptr = getQuestion(response, ptr, end, InterfaceID, &pktq); - if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &pktq, !dstaddr, mDNSNULL)) && - qptr->ValidatingResponse) - { - DNSQuestion *next, *q; - - if (qptr->DuplicateOf) - LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question matching response", qptr->qname.c, DNSTypeName(qptr->qtype)); - - // Be careful to call the callback for duplicate questions first and then the original - // question. If we called the callback on the original question, it could stop and - // a duplicate question would become the original question. - mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls - for (q = qptr->next ; q && q != m->NewQuestions; q = next) - { - next = q->next; - if (q->DuplicateOf == qptr) - { - if (q->ValidatingResponse) - LogInfo("mDNSCoreReceiveNoDNSSECAnswers: qptr %##s (%s) Duplicate question found", q->qname.c, DNSTypeName(q->qtype)); - else - LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question not ValidatingResponse", q->qname.c, DNSTypeName(q->qtype)); - if (q->QuestionCallback) - q->QuestionCallback(m, q, mDNSNULL, QC_nodnssec); - } - } - if (qptr->QuestionCallback) - qptr->QuestionCallback(m, qptr, mDNSNULL, QC_nodnssec); - mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again - } - } -} - -mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr, - mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, uDNS_LLQType LLQType, mDNSu8 rcode, CacheRecord *NSECRecords) +mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, + const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + const mdns_querier_t querier, const mdns_dns_service_t uDNSService, +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + denial_of_existence_records_t **denial_of_existence_records_ptr, +#endif + const uDNS_LLQType LLQType) { int i; const mDNSu8 *ptr = response->data; @@ -8484,20 +8468,56 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) { DNSQuestion q; - DNSQuestion *qptr = mDNSNULL; ptr = getQuestion(response, ptr, end, InterfaceID, &q); - if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr, mDNSNULL))) + if (ptr) { - CacheRecord *rr, *neg = mDNSNULL; - CacheGroup *cg = CacheGroupForName(m, q.qnamehash, &q.qname); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) + DNSQuestion *qptr; + CacheRecord *cr, *neg = mDNSNULL; + CacheGroup *cg; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (querier) + { + qptr = Querier_GetDNSQuestion(querier); + } + else +#endif + { + qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr); + if (!qptr) + { + continue; + } + } + cg = CacheGroupForName(m, q.qnamehash, &q.qname); + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + { + mDNSBool isAnswer; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (querier) + { + isAnswer = (cr->resrec.dnsservice == uDNSService) && Querier_SameNameCacheRecordIsAnswer(cr, querier); + } + else +#endif + { + isAnswer = SameNameCacheRecordAnswersQuestion(cr, qptr); + } + if (isAnswer) { // 1. If we got a fresh answer to this query, then don't need to generate a negative entry - if (RRExpireTime(rr) - m->timenow > 0) break; + if (RRExpireTime(cr) - m->timenow > 0) break; // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one - if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr; + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = cr; + else if (cr->resrec.mortality == Mortality_Ghost) + { + // 3. If the existing entry is expired, mark it to be purged + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] mDNSCoreReceiveNoUnicastAnswers: Removing expired record" PRI_S, + q.request_id, mDNSVal16(q.TargetQID), CRDisplayString(m, cr)); + mDNS_PurgeCacheResourceRecord(m, cr); + } } + } // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft // Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers. // Otherwise we just fill up our cache with negative entries for just about every single multicast name we ever look up @@ -8516,26 +8536,34 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * // do the appropriate thing. This negative response is also needed for appending new search domains. if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname)) { - if (!rr) + if (!cr) { - LogInfo("mDNSCoreReceiveNoUnicastAnswers: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); - m->CurrentQuestion = qptr; - // We are not creating a cache record in this case, we need to pass back - // the error we got so that the proxy code can return the right one to - // the application - if (qptr->ProxyQuestion) - qptr->responseFlags = response->h.flags; - GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); - m->CurrentQuestion = mDNSNULL; + if (qptr) + { + const mDNSBool noData = ((response->h.flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr) ? mDNStrue : mDNSfalse; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] mDNSCoreReceiveNoUnicastAnswers: Generate negative response for " PRI_DM_NAME " (" PUB_S ")", + q.request_id, mDNSVal16(q.TargetQID), DM_NAME_PARAM(&q.qname), DNSTypeName(q.qtype)); + m->CurrentQuestion = qptr; + // We are not creating a cache record in this case, we need to pass back + // the error we got so that the proxy code can return the right one to + // the application + if (qptr->ProxyQuestion) + qptr->responseFlags = response->h.flags; + GenerateNegativeResponseEx(m, mDNSInterface_Any, QC_forceresponse, noData); + m->CurrentQuestion = mDNSNULL; + } } else { - LogInfo("mDNSCoreReceiveNoUnicastAnswers: Skipping check and not creating a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] mDNSCoreReceiveNoUnicastAnswers: Skipping check and not creating a negative cache entry for " PRI_DM_NAME " (" PUB_S ")", + q.request_id, mDNSVal16(q.TargetQID), DM_NAME_PARAM(&q.qname), DNSTypeName(q.qtype)); } } else { - if (!rr) + if (!cr) { // We start off assuming a negative caching TTL of 60 seconds // but then look to see if we can find an SOA authority record to tell us a better value we should be using @@ -8579,7 +8607,7 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * // // For ProxyQuestions, we don't do this as we need to create additional SOA records to cache them // along with the negative cache record. For simplicity, we don't create the additional records. - if (!qptr->ProxyQuestion && q.qtype == kDNSType_SOA) + if ((!qptr || !qptr->ProxyQuestion) && (q.qtype == kDNSType_SOA)) { int qcount = CountLabels(&q.qname); int scount = CountLabels(m->rec.r.resrec.name); @@ -8610,29 +8638,27 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * // If we already had a negative cache entry just update it, else make one or more new negative cache entries. if (neg) { - LogInfo("mDNSCoreReceiveNoUnicastAnswers: Renewing negative TTL from %d to %d %s", neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] mDNSCoreReceiveNoUnicastAnswers: Renewing negative TTL from %d to %d " PRI_S, + q.request_id, mDNSVal16(q.TargetQID), neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg)); RefreshCacheRecord(m, neg, negttl); +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + // replace the old records with the new ones + // If qptr is NULL, it means the question is no longer active, and we do not process the record + // for DNSSEC. + if ((qptr != mDNSNULL) && qptr->DNSSECStatus.enable_dnssec) + { + update_denial_records_in_cache_record(neg, denial_of_existence_records_ptr); + } +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) // When we created the cache for the first time and answered the question, the question's // interval was set to MaxQuestionInterval. If the cache is about to expire and we are resending // the queries, the interval should still be at MaxQuestionInterval. If the query is being // restarted (setting it to InitialQuestionInterval) for other reasons e.g., wakeup, // we should reset its question interval here to MaxQuestionInterval. - ResetQuestionState(m, qptr); - if (DNSSECQuestion(qptr)) - neg->CRDNSSECQuestion = 1; - // Update the NSEC records again. - // TBD: Need to purge and revalidate if the cached NSECS and the new set are not same. - if (NSECRecords) + if (qptr) { - if (!AddNSECSForCacheRecord(m, NSECRecords, neg, rcode)) - { - // We might just have an SOA record for zones that are not signed and hence don't log - // this as an error - LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s during refresh", CRDisplayString(m, neg)); - FreeNSECRecords(m, NSECRecords); - neg->CRDNSSECQuestion = 0; - } - NSECRecords = mDNSNULL; + ResetQuestionState(m, qptr); } if (SOARecord) { @@ -8646,7 +8672,11 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * { CacheRecord *negcr; debugf("mDNSCoreReceiveNoUnicastAnswers making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype)); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, uDNSService); +#else MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer); +#endif m->rec.r.responseFlags = response->h.flags; // We create SOA records above which might create new cache groups. Earlier // in the function we looked up the cache group for the name and it could have @@ -8654,52 +8684,29 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * // it will create additional cache groups for the same name. To avoid that, // look up the cache group again to re-initialize cg again. cg = CacheGroupForName(m, hash, name); - if (NSECRecords && DNSSECQuestion(qptr)) + // Need to add with a delay so that we can tag the SOA record + negcr = CreateNewCacheEntry(m, HashSlotFromNameHash(hash), cg, 1, mDNStrue, mDNSNULL); + + if (negcr) { - // Create the cache entry with delay and then add the NSEC records - // to it and add it immediately. - negcr = CreateNewCacheEntry(m, HashSlotFromNameHash(hash), cg, 1, mDNStrue, mDNSNULL); - if (negcr) +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + // If qptr is NULL, it means the question is no longer active, and we do not process the + // record for DNSSEC. + if (qptr != mDNSNULL && qptr->DNSSECStatus.enable_dnssec) { - negcr->CRDNSSECQuestion = 0; - if (!AddNSECSForCacheRecord(m, NSECRecords, negcr, rcode)) - { - LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s", - CRDisplayString(m, negcr)); - FreeNSECRecords(m, NSECRecords); - } - else - { - negcr->CRDNSSECQuestion = 1; - LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord added neg NSEC for %s", CRDisplayString(m, negcr)); - } - NSECRecords = mDNSNULL; - negcr->DelayDelivery = 0; - CacheRecordDeferredAdd(m, negcr); + update_denial_records_in_cache_record(negcr, denial_of_existence_records_ptr); } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - break; - } - else - { - // Need to add with a delay so that we can tag the SOA record - negcr = CreateNewCacheEntry(m, HashSlotFromNameHash(hash), cg, 1, mDNStrue, mDNSNULL); - if (negcr) - { - negcr->CRDNSSECQuestion = 0; - if (DNSSECQuestion(qptr)) - negcr->CRDNSSECQuestion = 1; - negcr->DelayDelivery = 0; +#endif + negcr->DelayDelivery = 0; - if (SOARecord) - { - if (negcr->soa) - ReleaseCacheRecord(m, negcr->soa); - negcr->soa = SOARecord; - SOARecord = mDNSNULL; - } - CacheRecordDeferredAdd(m, negcr); + if (SOARecord) + { + if (negcr->soa) + ReleaseCacheRecord(m, negcr->soa); + negcr->soa = SOARecord; + SOARecord = mDNSNULL; } + CacheRecordDeferredAdd(m, negcr); } m->rec.r.responseFlags = zeroID; m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it @@ -8712,13 +8719,17 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * } } } - if (NSECRecords) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: NSECRecords not used"); FreeNSECRecords(m, NSECRecords); } - if (SOARecord) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: SOARecord not used"); ReleaseCacheRecord(m, SOARecord); } + if (SOARecord) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "mDNSCoreReceiveNoUnicastAnswers: SOARecord not used"); + ReleaseCacheRecord(m, SOARecord); + } } mDNSlocal void mDNSCorePrintStoredProxyRecords(mDNS *const m) { AuthRecord *rrPtr = mDNSNULL; + if (!m->SPSRRSet) return; LogSPS("Stored Proxy records :"); for (rrPtr = m->SPSRRSet; rrPtr; rrPtr = rrPtr->next) { @@ -8742,222 +8753,170 @@ mDNSlocal mDNSBool mDNSCoreRegisteredProxyRecord(mDNS *const m, AuthRecord *rr) return mDNSfalse; } -mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage *const response, uDNS_LLQType LLQType, - const mDNSu32 slot, CacheGroup *cg, DNSQuestion *unicastQuestion, CacheRecord ***cfp, CacheRecord **NSECCachePtr, - mDNSInterfaceID InterfaceID) +mDNSexport CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage *const response, uDNS_LLQType LLQType, + const mDNSu32 slot, CacheGroup *cg, CacheRecord ***cfp, mDNSInterfaceID InterfaceID) { - CacheRecord *rr; + CacheRecord *cr; CacheRecord **cflocal = *cfp; - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) { mDNSBool match; // Resource record received via unicast, the resGroupID should match ? if (!InterfaceID) { - mDNSu16 id1 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); - mDNSu16 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + match = (cr->resrec.dnsservice == m->rec.r.resrec.dnsservice) ? mDNStrue : mDNSfalse; +#else + const mDNSu32 id1 = (cr->resrec.rDNSServer ? cr->resrec.rDNSServer->resGroupID : 0); + const mDNSu32 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0); match = (id1 == id2); +#endif } else - match = (rr->resrec.InterfaceID == InterfaceID); + match = (cr->resrec.InterfaceID == InterfaceID); // If we found this exact resource record, refresh its TTL - if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) + if (match) { - if (m->rec.r.resrec.rdlength > InlineCacheRDSize) - verbosedebugf("mDNSCoreReceiveCacheCheck: Found record size %5d interface %p already in cache: %s", - m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); - - if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) + if (IdenticalSameNameRecord(&m->rec.r.resrec, &cr->resrec)) { - // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list - if (rr->NextInCFList == mDNSNULL && *cfp != &rr->NextInCFList && LLQType != uDNS_LLQ_Events) + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + verbosedebugf("mDNSCoreReceiveCacheCheck: Found record size %5d interface %p already in cache: %s", + m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); + + if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) { - *cflocal = rr; - cflocal = &rr->NextInCFList; - *cflocal = (CacheRecord*)1; - *cfp = &rr->NextInCFList; + // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list + if (cr->NextInCFList == mDNSNULL && *cfp != &cr->NextInCFList && LLQType != uDNS_LLQ_Events) + { + *cflocal = cr; + cflocal = &cr->NextInCFList; + *cflocal = (CacheRecord*)1; + *cfp = &cr->NextInCFList; + } + + // If this packet record is marked unique, and our previous cached copy was not, then fix it + if (!(cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + { + if (CacheRecordAnswersQuestion(cr, q)) + q->UniqueAnswers++; + } + cr->resrec.RecordType = m->rec.r.resrec.RecordType; + } } - // If this packet record is marked unique, and our previous cached copy was not, then fix it - if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + if (!SameRDataBody(&m->rec.r.resrec, &cr->resrec.rdata->u, SameDomainNameCS)) + { + // If the rdata of the packet record differs in name capitalization from the record in our cache + // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get + // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. + // <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing + cr->resrec.rroriginalttl = 0; + cr->TimeRcvd = m->timenow; + cr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, cr); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "mDNSCoreReceiveCacheCheck: Discarding due to domainname case change old: " PRI_S, CRDisplayString(m, cr)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "mDNSCoreReceiveCacheCheck: Discarding due to domainname case change new: " PRI_S, CRDisplayString(m, &m->rec.r)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "mDNSCoreReceiveCacheCheck: Discarding due to domainname case change in %d slot %3d in %d %d", + NextCacheCheckEvent(cr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); + // DO NOT break out here -- we want to continue as if we never found it + } + else if (m->rec.r.resrec.rroriginalttl > 0) { DNSQuestion *q; - for (q = m->Questions; q; q=q->next) + + m->mDNSStats.CacheRefreshed++; + + if ((cr->resrec.mortality == Mortality_Ghost) && !cr->DelayDelivery) { - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - q->UniqueAnswers++; + cr->DelayDelivery = NonZeroTime(m->timenow); + debugf("mDNSCoreReceiveCacheCheck: Reset DelayDelivery for mortalityExpired EXP:%d RR %s", m->timenow - RRExpireTime(cr), CRDisplayString(m, cr)); } - rr->resrec.RecordType = m->rec.r.resrec.RecordType; - } - } - - if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS)) - { - // If the rdata of the packet record differs in name capitalization from the record in our cache - // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get - // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. - // <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing - rr->resrec.rroriginalttl = 0; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); - LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change old: %s", CRDisplayString(m, rr)); - LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change new: %s", CRDisplayString(m, &m->rec.r)); - LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change in %d slot %3d in %d %d", - NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); - // DO NOT break out here -- we want to continue as if we never found it - } - else if (!IdenticalAnonInfo(m->rec.r.resrec.AnonInfo, rr->resrec.AnonInfo)) - { - // If the NSEC3 record changed, a few possibilities - // - // 1) the peer reinitialized e.g., after network change and still part of the - // same set. - // 2) the peer went to a different set but we did not see the goodbyes. If we just - // update the nsec3 record, it would be incorrect. Flush the cache so that we - // can deliver a RMV followed by ADD. - // 3) if the peer is ourselves and we see the goodbye when moving to a different set - // and so we flush the cache and create a new cache record with the new set information. - // Now we move back to the original set. In this case, we can't just update the - // NSEC3 record alone. We need to flush so that we can deliver an RMV followed by ADD - // when we create the new cache entry. - // - // Note: For case (1), we could avoid flushing the cache but we can't tell the difference - // from the other cases. - rr->resrec.rroriginalttl = 0; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); - LogInfo("mDNSCoreReceiveCacheCheck: AnonInfo changed for %s", CRDisplayString(m, rr)); - // DO NOT break out here -- we want to continue as if we never found it. When we return - // from this function, we will create a new cache entry with the new NSEC3 record - } - else if (m->rec.r.resrec.rroriginalttl > 0) - { - DNSQuestion *q; - m->mDNSStats.CacheRefreshed++; - - if (rr->resrec.mortality == Mortality_Ghost && unicastQuestion && (unicastQuestion->allowExpired != AllowExpired_AllowExpiredAnswers) && !rr->DelayDelivery) - { - rr->DelayDelivery = NonZeroTime(m->timenow); - debugf("mDNSCoreReceiveCacheCheck: Reset DelayDelivery for mortalityExpired EXP:%d RR %s", m->timenow - RRExpireTime(rr), CRDisplayString(m, rr)); - } - - if (rr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, rr)); - RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); - rr->responseFlags = response->h.flags; - - // If we may have NSEC records returned with the answer (which we don't know yet as it - // has not been processed), we need to cache them along with the first cache - // record in the list that answers the question so that it can be used for validation - // later. The "type" check below is to make sure that we cache on the cache record - // that would answer the question. It is possible that we might cache additional things - // e.g., MX question might cache A records also, and we want to cache the NSEC on - // the record that answers the question. - if (response->h.numAnswers && unicastQuestion && unicastQuestion->qtype == rr->resrec.rrtype - && !(*NSECCachePtr)) - { - LogInfo("mDNSCoreReceiveCacheCheck: rescuing RR %s", CRDisplayString(m, rr)); - *NSECCachePtr = rr; - } - // We have to reset the question interval to MaxQuestionInterval so that we don't keep - // polling the network once we get a valid response back. For the first time when a new - // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. - // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server - // configuration changed, without flushing the cache, we reset the question interval here. - // Currently, we do this for for both multicast and unicast questions as long as the record - // type is unique. For unicast, resource record is always unique and for multicast it is - // true for records like A etc. but not for PTR. - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) - { - for (q = m->Questions; q; q=q->next) + if (cr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, cr)); + RefreshCacheRecord(m, cr, m->rec.r.resrec.rroriginalttl); + // RefreshCacheRecordCacheGroupOrder will modify the cache group member list that is currently being iterated over in this for-loop. + // It is safe to call because the else-if body will unconditionally break out of the for-loop now that it has found the entry to update. + RefreshCacheRecordCacheGroupOrder(cg, cr); + cr->responseFlags = response->h.flags; + + // If we may have NSEC records returned with the answer (which we don't know yet as it + // has not been processed), we need to cache them along with the first cache + // record in the list that answers the question so that it can be used for validation + // later. The "type" check below is to make sure that we cache on the cache record + // that would answer the question. It is possible that we might cache additional things + // e.g., MX question might cache A records also, and we want to cache the NSEC on + // the record that answers the question. + if (!InterfaceID) { - if (!q->DuplicateOf && !q->LongLived && - ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "mDNSCoreReceiveCacheCheck: rescuing RR " PRI_S, CRDisplayString(m, cr)); + } + // We have to reset the question interval to MaxQuestionInterval so that we don't keep + // polling the network once we get a valid response back. For the first time when a new + // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. + // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server + // configuration changed, without flushing the cache, we reset the question interval here. + // Currently, we do this for for both multicast and unicast questions as long as the record + // type is unique. For unicast, resource record is always unique and for multicast it is + // true for records like A etc. but not for PTR. + if (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + for (q = m->Questions; q; q=q->next) { - ResetQuestionState(m, q); - debugf("mDNSCoreReceiveCacheCheck: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 + if (!q->DuplicateOf && !q->LongLived && + ActiveQuestion(q) && CacheRecordAnswersQuestion(cr, q)) + { + ResetQuestionState(m, q); + debugf("mDNSCoreReceiveCacheCheck: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 + } } } + break; // Check usage of RefreshCacheRecordCacheGroupOrder before removing (See note above) + } + else + { + // If the packet TTL is zero, that means we're deleting this record. + // To give other hosts on the network a chance to protest, we push the deletion + // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. + // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent + // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. + // If record's current expiry time is more than a second from now, we set it to expire in one second. + // If the record is already going to expire in less than one second anyway, we leave it alone -- + // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. + debugf("DE for %s", CRDisplayString(m, cr)); + if (RRExpireTime(cr) - m->timenow > mDNSPlatformOneSecond) + { + cr->resrec.rroriginalttl = 1; + cr->TimeRcvd = m->timenow; + cr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, cr); + } + break; } - break; } - else + else if (cr->resrec.rroriginalttl != 0 && // Not already marked for discarding + m->rec.r.resrec.rrclass == cr->resrec.rrclass && + (m->rec.r.resrec.rrtype != cr->resrec.rrtype && + (m->rec.r.resrec.rrtype == kDNSType_CNAME || cr->resrec.rrtype == kDNSType_CNAME))) { - // If the packet TTL is zero, that means we're deleting this record. - // To give other hosts on the network a chance to protest, we push the deletion - // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. - // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent - // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. - // If record's current expiry time is more than a second from now, we set it to expire in one second. - // If the record is already going to expire in less than one second anyway, we leave it alone -- - // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. - debugf("DE for %s", CRDisplayString(m, rr)); - if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) - { - rr->resrec.rroriginalttl = 1; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); - } - break; + // If the cache record rrtype doesn't match and one is a CNAME, then flush this record + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "mDNSCoreReceiveCacheCheck: Discarding (%s) " PRI_S " rrtype change from (%s) to (%s)", + MortalityDisplayString(cr->resrec.mortality), CRDisplayString(m, cr), DNSTypeName(cr->resrec.rrtype), DNSTypeName(m->rec.r.resrec.rrtype)); + mDNS_PurgeCacheResourceRecord(m, cr); + // DO NOT break out here -- we want to continue iterating the cache entries } } } - return rr; -} - -mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, - const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records) -{ - const mDNSu8 *ptr; - CacheRecord *rr; - int i; - - if (!response->h.numAuthorities) - return; - ptr = LocateAuthorities(response, end); - if (!ptr) - { - LogInfo("mDNSParseNSEC3Records: ERROR can't locate authorities"); - return; - } - for (i = 0; i < response->h.numAuthorities && ptr && ptr < end; i++) - { - CacheGroup *cg; - - ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); - if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative || m->rec.r.resrec.rrtype != kDNSType_NSEC3) - { - debugf("mDNSParseNSEC3Records: ptr %p, Record %s, ignoring", ptr, CRDisplayString(m, &m->rec.r)); - m->rec.r.resrec.RecordType = 0; - continue; - } - cg = CacheGroupForRecord(m, &m->rec.r.resrec); - // Create the cache entry but don't add it to the cache it. We need - // to cache this along with the main cache record. - rr = CreateNewCacheEntry(m, HashSlotFromNameHash(m->rec.r.resrec.namehash), cg, 0, mDNSfalse, mDNSNULL); - if (rr) - { - debugf("mDNSParseNSEC3Records: %s", CRDisplayString(m, rr)); - *NSEC3Records = rr; - NSEC3Records = &rr->next; - } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } + return cr; } mDNSlocal void mDNSCoreResetRecord(mDNS *const m) { m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - if (m->rec.r.resrec.AnonInfo) - { - FreeAnonInfo(m->rec.r.resrec.AnonInfo); - m->rec.r.resrec.AnonInfo = mDNSNULL; - } } // Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change @@ -8966,16 +8925,17 @@ mDNSlocal void mDNSCoreResetRecord(mDNS *const m) // InterfaceID non-NULL tells us the interface this multicast response was received on // InterfaceID NULL tells us this was a unicast response // dstaddr NULL tells us we received this over an outgoing TCP connection we made -mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, - const DNSMessage *const response, const mDNSu8 *end, - const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID) +mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_querier_t querier, mdns_dns_service_t uDNSService, +#endif + const mDNSInterfaceID InterfaceID) { int i; - mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); - mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + const mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); + const mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); DNSQuestion *llqMatch = mDNSNULL; - DNSQuestion *unicastQuestion = mDNSNULL; uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); // "(CacheRecord*)1" is a special (non-zero) end-of-list marker @@ -8983,14 +8943,6 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling. CacheRecord *CacheFlushRecords = (CacheRecord*)1; CacheRecord **cfp = &CacheFlushRecords; - CacheRecord *NSECRecords = mDNSNULL; - CacheRecord *NSECCachePtr = mDNSNULL; - CacheRecord **nsecp = &NSECRecords; - CacheRecord *McastNSEC3Records = mDNSNULL; - mDNSBool nseclist; - mDNSu8 rcode = '\0'; - mDNSBool rrsigsCreated = mDNSfalse; - mDNSBool DNSSECQuestion = mDNSfalse; NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID); mDNSBool recordAcceptedInResponse = mDNSfalse; // Set if a record is accepted from a unicast mDNS response that answers an existing question. @@ -9001,7 +8953,23 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, int firstadditional = firstauthority + response->h.numAuthorities; int totalrecords = firstadditional + response->h.numAdditionals; const mDNSu8 *ptr = response->data; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) DNSServer *uDNSServer = mDNSNULL; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + denial_of_existence_records_t *denial_of_existence_records = mDNSNULL; + mDNSBool not_answer_but_required_for_dnssec = mDNSfalse; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + // Determine whether the response is mDNS, as opposed to DNS. + // Thus far, the code has assumed that responses with IDs set to zero are mDNS responses. However, this condition + // isn't sufficient because queriers, which are used exclusively for DNS queries, may set the IDs of their queries + // to zero. And consequently, their responses may have their IDs set to zero. Specifically, zero-valued IDs are used + // for DNS over HTTPs, as specified by <https://tools.ietf.org/html/rfc8484#section-4.1>. + const mDNSBool ResponseIsMDNS = mDNSOpaque16IsZero(response->h.id) && !querier; +#else + const mDNSBool ResponseIsMDNS = mDNSOpaque16IsZero(response->h.id); +#endif debugf("Received Response from %#-15a addressed to %#-15a on %p with " "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes LLQType %d", @@ -9011,7 +8979,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", response->h.numAdditionals, response->h.numAdditionals == 1 ? " " : "s", end - response->data, LLQType); -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) && !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) if (mDNSSameIPPort(srcport, UnicastDNSPort)) { MetricsUpdateDNSResponseSize((mDNSu32)(end - (mDNSu8 *)response)); @@ -9040,7 +9008,11 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // should start at the end of the response and work forward in the // datagram. Thus if there is any data for the authority section, the // answer section is guaranteed to be unique. +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC) && !querier && +#else if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC) && +#endif ((response->h.numAnswers == 0) || ((response->h.numAuthorities == 0) && (response->h.numAdditionals == 0)))) return; if (LLQType == uDNS_LLQ_Ignore) return; @@ -9055,9 +9027,23 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, else { mDNSBool failure, returnEarly; - rcode = (mDNSu8)(response->h.flags.b[1] & kDNSFlag1_RC_Mask); + const int rcode = response->h.flags.b[1] & kDNSFlag1_RC_Mask; failure = !(rcode == kDNSFlag1_RC_NoErr || rcode == kDNSFlag1_RC_NXDomain || rcode == kDNSFlag1_RC_NotAuth); returnEarly = mDNSfalse; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + // When the QUERIER functionality is enabled, DNS transport is handled exclusively by querier objects. If this + // response was provided by a querier, but the RCODE is considered a failure, then set failure to false so that + // we don't return early. The logic of returning early was so that uDNS_CheckCurrentQuestion() could handle + // resending the query and generate a negative cache record if all servers were tried. If the querier provides a + // response, then it's the best response that it could provide. If the RCODE is considered a failure, + // mDNSCoreReceiveResponse() needs to create negative cache entries for the unanwered question, so totalrecords + // is set to 0 to ignore any records that the response may contain. + if (querier && failure) + { + totalrecords = 0; + failure = mDNSfalse; + } +#endif // We could possibly combine this with the similar loop at the end of this function -- // instead of tagging cache records here and then rescuing them if we find them in the answer section, // we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in @@ -9066,110 +9052,94 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // packet number, then we deduce they are old and delete them for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) { - DNSQuestion q, *qptr = mDNSNULL, *suspiciousForQ = mDNSNULL; + DNSQuestion q; + DNSQuestion *qptr; + mDNSBool expectingResponse; ptr = getQuestion(response, ptr, end, InterfaceID, &q); - if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr, &suspiciousForQ))) + if (!ptr) + { + continue; + } +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (querier) { - if (!failure) + expectingResponse = mDNStrue; + qptr = mDNSNULL; + } + else +#endif + { + qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr); + expectingResponse = qptr ? mDNStrue : mDNSfalse; + } + if (!expectingResponse) + { + continue; + } + if (!failure) + { + CacheRecord *cr; + CacheGroup *cg = CacheGroupForName(m, q.qnamehash, &q.qname); + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) { - CacheRecord *rr; - // Remember the unicast question that we found, which we use to make caching - // decisions later on in this function - CacheGroup *cg = CacheGroupForName(m, q.qnamehash, &q.qname); - if (!mDNSOpaque16IsZero(response->h.id)) + mDNSBool isAnswer; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (querier) { - unicastQuestion = qptr; - if (qptr->qDNSServer && DNSSECQuestion(qptr)) - { - LogInfo("mDNSCoreReceiveResponse: Setting aware for %##s (%s) on %#a", qptr->qname.c, - DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr); - qptr->qDNSServer->DNSSECAware = mDNStrue; - qptr->qDNSServer->req_DO = mDNStrue; - } - if (qptr->ValidatingResponse) - DNSSECQuestion = mDNStrue; + isAnswer = (cr->resrec.dnsservice == uDNSService) && Querier_SameNameCacheRecordIsAnswer(cr, querier); } - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) - { - debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype), - rr->resrec.InterfaceID, CRDisplayString(m, rr)); - // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm - rr->TimeRcvd = m->timenow - TicksTTL(rr) - 1; - rr->UnansweredQueries = MaxUnansweredQueries; - rr->CRDNSSECQuestion = 0; - if (unicastQuestion && DNSSECQuestion(unicastQuestion)) - { - LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for record %s, question %##s (%s)", CRDisplayString(m, rr), - unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype)); - rr->CRDNSSECQuestion = 1; - } - } - } - else - { - if (qptr) + else +#endif { - // If we recv any error from the DNSServer for a DNSSEC Query and if we know that the server - // is not DNSSEC aware, stop doing DNSSEC for that DNSServer. Note that by setting the - // req_DO to false here, the next retransmission for this question will turn off validation - // and hence retransmit without the EDNS0/DOK option. - if (DNSSECOptionalQuestion(qptr) && qptr->qDNSServer && !qptr->qDNSServer->DNSSECAware) - { - LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to DNSSEC Query %##s (%s), clear DO flag", - qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); - qptr->qDNSServer->req_DO = mDNSfalse; - } - // For Unicast DNS Queries, penalize the DNSServer - else - { - LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", - qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); - PenalizeDNSServer(m, qptr, response->h.flags); - } + isAnswer = SameNameCacheRecordAnswersQuestion(cr, qptr); + } + if (isAnswer) + { + debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype), + cr->resrec.InterfaceID, CRDisplayString(m, cr)); + // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm + cr->TimeRcvd = m->timenow - TicksTTL(cr) - 1; + cr->UnansweredQueries = MaxUnansweredQueries; } - returnEarly = mDNStrue; } } - else if (!InterfaceID && suspiciousForQ) + else { - // If a response is suspicious for a question, then reissue the question via TCP - LogInfo("mDNSCoreReceiveResponse: Server %p responded suspiciously to query %##s (%s) qID %d != rID: %d", - suspiciousForQ->qDNSServer, q.qname.c, DNSTypeName(q.qtype), - mDNSVal16(suspiciousForQ->TargetQID), mDNSVal16(response->h.id)); - uDNS_RestartQuestionAsTCP(m, suspiciousForQ, srcaddr, srcport); - return; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] mDNSCoreReceiveResponse: Server %p responded with code %d to query " PRI_DM_NAME " (" PUB_S ")", + qptr->request_id, mDNSVal16(qptr->TargetQID), qptr->qDNSServer, rcode, + DM_NAME_PARAM(&q.qname), DNSTypeName(q.qtype)); + PenalizeDNSServer(m, qptr, response->h.flags); +#endif + returnEarly = mDNStrue; } } if (returnEarly) { - LogInfo("Ignoring %2d Answer%s %2d Authorit%s %2d Additional%s", - response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", - response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", - response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[Q%d] Ignoring %2d Answer" PUB_S " %2d Authorit" PUB_S " %2d Additional" PUB_S, + mDNSVal16(response->h.id), + response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", + response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", + response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); // not goto exit because we won't have any CacheFlushRecords and we do not want to // generate negative cache entries (we want to query the next server) return; } - if (unicastQuestion && DNSSECQuestion(unicastQuestion)) - { - BumpDNSSECStats(m, kStatsActionSet, kStatsTypeMsgSize, (end - response->data)); - } } - // Parse the NSEC3 records from the Authority section before we process - // the Answer section so that we can cache them along with the proper - // cache records we create. - if (mDNSOpaque16IsZero(response->h.id)) - mDNSParseNSEC3Records(m, response, end, InterfaceID, &McastNSEC3Records); - for (i = 0; i < totalrecords && ptr && ptr < end; i++) { // All responses sent via LL multicast are acceptable for caching // All responses received over our outbound TCP connections are acceptable for caching // We accept all records in a unicast response to a multicast query once we find one that // answers an active question. +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool AcceptableResponse = ResponseMCast || (!querier && !dstaddr) || LLQType || recordAcceptedInResponse; +#else mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType || recordAcceptedInResponse; +#endif // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer // to any specific question -- any code reading records from the cache needs to make that determination for itself.) @@ -9185,13 +9155,6 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, continue; } - // We have already parsed the NSEC3 records and cached them approrpriately for - // multicast responses. - if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype == kDNSType_NSEC3) - { - mDNSCoreResetRecord(m); - continue; - } // Don't want to cache OPT or TSIG pseudo-RRs if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { @@ -9224,8 +9187,10 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // When we receive uDNS LLQ responses, we assume a long cache lifetime -- // In the case of active LLQs, we'll get remove events when the records actually do go away // In the case of polling LLQs, we assume the record remains valid until the next poll - if (!mDNSOpaque16IsZero(response->h.id)) + if (!ResponseIsMDNS) + { m->rec.r.resrec.rroriginalttl = GetEffectiveTTL(LLQType, m->rec.r.resrec.rroriginalttl); + } // If response was not sent via LL multicast, // then see if it answers a recent query of ours, which would also make it acceptable for caching. @@ -9239,15 +9204,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // ExpectingUnicastResponseForRecord as the port numbers don't match. uDNS_recvLLQRespose // has already matched the question using the 64 bit Id in the packet and we use that here. +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (querier) + { + mdns_replace(&m->rec.r.resrec.dnsservice, uDNSService); + } +#else if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer; - - // If this is a DNSSEC question that is also LongLived, don't accept records from the - // Additional/Authority section blindly. We need to go through IsAcceptableResponse below - // so that NSEC/NSEC3 record are cached in the nseclist if we accept them. This can happen - // for both negative responses and wildcard expanded positive responses as both of come - // back with NSEC/NSEC3s. - if (unicastQuestion && DNSSECQuestion(unicastQuestion)) - AcceptableResponse = mDNSfalse; +#endif } else if (!AcceptableResponse || !dstaddr) { @@ -9255,42 +9219,67 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // that are not long lived e.g., AAAA lookup in a Private domain), it is indicated by !dstaddr. // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that // we create. - - DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); - - // Initialize the DNS server on the resource record which will now filter what questions we answer with - // this record. - // - // We could potentially lookup the DNS server based on the source address, but that may not work always - // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came - // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based - // on the "id" and "source port", then this response answers the question and assume the response - // came from the same DNS server that we sent the query to. - - if (q != mDNSNULL) +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (querier) { - AcceptableResponse = mDNStrue; - if (!InterfaceID) - { - debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); - m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; - if (!unicastQuestion) unicastQuestion = q; // Acceptable responses to unicast questions need to have (unicastQuestion != nil) - } - else + ResourceRecord *const rr = &m->rec.r.resrec; + if (Querier_ResourceRecordIsAnswer(rr, querier)) { - // Accept all remaining records in this unicast response to an mDNS query. - recordAcceptedInResponse = mDNStrue; - LogInfo("mDNSCoreReceiveResponse: Accepting response for query: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + const mdns_resolver_type_t resolver_type = mdns_querier_get_resolver_type(querier); + if ((resolver_type == mdns_resolver_type_normal) && + (mdns_querier_get_over_tcp_reason(querier) != mdns_query_over_tcp_reason_null)) + { + rr->protocol = mdns_resolver_type_tcp; + } + else + { + rr->protocol = resolver_type; + } + mdns_replace(&rr->dnsservice, uDNSService); + AcceptableResponse = mDNStrue; } } else +#endif { - // If we can't find a matching question, we need to see whether we have seen records earlier that matched - // the question. The code below does that. So, make this record unacceptable for now - if (!InterfaceID) + const DNSQuestion *q; + // Initialize the DNS server on the resource record which will now filter what questions we answer with + // this record. + // + // We could potentially lookup the DNS server based on the source address, but that may not work always + // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came + // from the DNS server that queried. We follow the same logic here. If we can find a matching question based + // on the "id" and "source port", then this response answers the question and assume the response + // came from the same DNS server that we sent the query to. + q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + if (q != mDNSNULL) + { + AcceptableResponse = mDNStrue; + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; +#endif + } + else + { + // Accept all remaining records in this unicast response to an mDNS query. + recordAcceptedInResponse = mDNStrue; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] mDNSCoreReceiveResponse: Accepting response for query: " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); + } + } + else { - debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); - AcceptableResponse = mDNSfalse; + // If we can't find a matching question, we need to see whether we have seen records earlier that matched + // the question. The code below does that. So, make this record unacceptable for now + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } } } } @@ -9333,7 +9322,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, } // 1. Check that this packet resource record does not conflict with any of ours - if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype != kDNSType_NSEC) + if (ResponseIsMDNS && m->rec.r.resrec.rrtype != kDNSType_NSEC) { if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); @@ -9367,7 +9356,8 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // else, the packet RR has different type or different rdata -- check to see if this is a conflict else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r)) { - LogInfo("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogInfo("mDNSCoreReceiveResponse: Pkt Record: %08lX %s (interface %d)", + m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r), IIDPrintable(InterfaceID)); LogInfo("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr->resrec.rdatahash, ARDisplayString(m, rr)); // If this record is marked DependentOn another record for conflict detection purposes, @@ -9409,30 +9399,34 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // Before we call deregister, check if this is a packet we registered with the sleep proxy. if (!mDNSCoreRegisteredProxyRecord(m, rr)) { - // This may be a conflict due to stale packets on the network. Delay probing by a second. - // If there are conflicts after 3 such attempts, then it is a true conflict. - if (m->DelayConflictProcessing) + if ((rr->ProbingConflictCount == 0) || (m->MPktNum != rr->LastConflictPktNum)) { - m->DelayConflictProcessing--; - LogMsg("Possible spurious conflict for %s. Attempt %d at suppressing probes for one second", - ARDisplayString(m, rr), (MAX_CONFLICT_PROCESSING_DELAYS - m->DelayConflictProcessing)); - rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; - rr->AnnounceCount = InitialAnnounceCount; - m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond); - InitializeLastAPTime(m, rr); - RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate - } - else - { - LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); - m->mDNSStats.NameConflicts++; -#if APPLE_OSX_mDNSResponder - // See if this record was also registered with any D2D plugins. - D2D_stop_advertising_record(rr); + const NetworkInterfaceInfo *const intf = FirstInterfaceForID(m, InterfaceID); + rr->ProbingConflictCount++; + rr->LastConflictPktNum = m->MPktNum; + if (ResponseMCast && (!intf || intf->SupportsUnicastMDNSResponse) && + (rr->ProbingConflictCount <= kMaxAllowedMCastProbingConflicts)) + { + LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; restarting probing after %d-tick pause due to possibly " + "spurious multicast conflict (%d/%d) via interface %d for %s", + rr->ProbeCount, kProbingConflictPauseDuration, rr->ProbingConflictCount, + kMaxAllowedMCastProbingConflicts, IIDPrintable(InterfaceID), ARDisplayString(m, rr)); + rr->ProbeCount = DefaultProbeCountForTypeUnique; + rr->LastAPTime = m->timenow + kProbingConflictPauseDuration - rr->ThisAPInterval; + SetNextAnnounceProbeTime(m, rr); + } + else + { + LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s due to %scast conflict via interface %d", + rr->ProbeCount, ARDisplayString(m, rr), ResponseMCast ? "multi" : "uni", IIDPrintable(InterfaceID)); + m->mDNSStats.NameConflicts++; +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + // See if this record was also registered with any D2D plugins. + D2D_stop_advertising_record(rr); #endif - mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } } - } } // We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the @@ -9444,7 +9438,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); m->mDNSStats.KnownUniqueNameConflicts++; -#if APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) D2D_stop_advertising_record(rr); #endif mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); @@ -9457,42 +9451,57 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // If the packet record has the cache-flush bit set, then we check to see if we // have any record(s) of the same type that we should re-assert to rescue them // (see note about "multi-homing and bridged networks" at the end of this function). - else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype) - if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (mDNSu32)(m->timenow - rr->LastMCTime) > (mDNSu32)mDNSPlatformOneSecond/2) - { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } + else if ((m->rec.r.resrec.rrtype == rr->resrec.rrtype) && + (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && + ((mDNSu32)(m->timenow - rr->LastMCTime) > (mDNSu32)mDNSPlatformOneSecond/2) && + ResourceRecordIsValidAnswer(rr)) + { + rr->ImmedAnswer = mDNSInterfaceMark; + m->NextScheduledResponse = m->timenow; + } } } } - nseclist = mDNSfalse; if (!AcceptableResponse) { - AcceptableResponse = IsResponseAcceptable(m, CacheFlushRecords, unicastQuestion, &nseclist); +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + not_answer_but_required_for_dnssec = adds_denial_records_in_cache_record(&m->rec.r.resrec, + querier != mDNSNULL && mdns_querier_get_dnssec_ok(querier), &denial_of_existence_records); + #else + not_answer_but_required_for_dnssec = mDNSfalse; + #endif +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + AcceptableResponse = IsResponseAcceptable(m, CacheFlushRecords); + +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (AcceptableResponse) mdns_replace(&m->rec.r.resrec.dnsservice, uDNSService); +#else if (AcceptableResponse) m->rec.r.resrec.rDNSServer = uDNSServer; +#endif } // 2. See if we want to add this packet resource record to our cache // We only try to cache answers if we have a cache to put them in // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query - if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r)); + if (!AcceptableResponse) { + const char* savedString = ""; +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + savedString = (not_answer_but_required_for_dnssec ? "Saved for DNSSEC" : ""); +#endif + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[Q%d] mDNSCoreReceiveResponse ignoring " PRI_S " %s", + mDNSVal16(response->h.id), CRDisplayString(m, &m->rec.r), savedString); + } + if (m->rrcache_size && AcceptableResponse) { const mDNSu32 slot = HashSlotFromNameHash(m->rec.r.resrec.namehash); CacheGroup *cg = CacheGroupForRecord(m, &m->rec.r.resrec); CacheRecord *rr = mDNSNULL; - if (McastNSEC3Records) - InitializeAnonInfoForCR(m, &McastNSEC3Records, &m->rec.r); - // 2a. Check if this packet resource record is already in our cache. - // - // If this record should go in the nseclist, don't look in the cache for updating it. - // They are supposed to be cached under the "nsec" field of the cache record for - // validation. Just create the cache record. - if (!nseclist) - { - rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, unicastQuestion, &cfp, &NSECCachePtr, InterfaceID); - } + rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, &cfp, InterfaceID); // If packet resource record not in our cache, add it now // (unless it is just a deletion of a record we never had, in which case we don't care) @@ -9510,36 +9519,16 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd() // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime() // to schedule an mDNS_Execute task at the appropriate time. - rr = CreateNewCacheEntry(m, slot, cg, delay, !nseclist, srcaddr); + rr = CreateNewCacheEntry(m, slot, cg, delay, mDNStrue, srcaddr); if (rr) { +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + set_denial_records_in_cache_record(rr, &denial_of_existence_records); +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + rr->responseFlags = response->h.flags; - // If we are not creating signatures, then we need to inform DNSSEC so that - // it does not wait forever. Don't do this if we got NSEC records - // as it indicates that this name does not exist. - if (rr->resrec.rrtype == kDNSType_RRSIG && !nseclist) - { - rrsigsCreated = mDNStrue; - } - // Remember whether we created a cache record in response to a DNSSEC question. - // This helps DNSSEC code not to reissue the question to fetch the DNSSEC records. - rr->CRDNSSECQuestion = 0; - if (unicastQuestion && DNSSECQuestion(unicastQuestion)) - { - LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for new record %s, question %##s (%s)", CRDisplayString(m, rr), - unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype)); - rr->CRDNSSECQuestion = 1; - } - // NSEC/NSEC3 records and its signatures are cached with the negative cache entry - // which we should be creating below. It is also needed in the wildcard - // expanded answer case and in that case it is cached along with the answer. - if (nseclist) - { - rr->TimeRcvd = m->timenow; - *nsecp = rr; - nsecp = &rr->next; - } - else if (AddToCFList) + + if (AddToCFList) { *cfp = rr; cfp = &rr->NextInCFList; @@ -9551,13 +9540,6 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, } } } - else - { - if (rr && rr->resrec.AnonInfo && m->rec.r.resrec.AnonInfo) - { - CopyAnonInfoForCR(m, rr, &m->rec.r); - } - } } mDNSCoreResetRecord(m); } @@ -9591,127 +9573,133 @@ exit: // *decrease* a record's remaining lifetime, never *increase* it. for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) { - mDNSu16 id1; - mDNSu16 id2; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool match; +#else + mDNSu32 id1; + mDNSu32 id2; +#endif if (!r1->resrec.InterfaceID) { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + match = (r1->resrec.dnsservice == r2->resrec.dnsservice) ? mDNStrue : mDNSfalse; +#else id1 = (r1->resrec.rDNSServer ? r1->resrec.rDNSServer->resGroupID : 0); id2 = (r2->resrec.rDNSServer ? r2->resrec.rDNSServer->resGroupID : 0); +#endif } else { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + match = mDNStrue; +#else id1 = id2 = 0; +#endif } - // When we receive new RRSIGs e.g., for DNSKEY record, we should not flush the old - // RRSIGS e.g., for TXT record. To do so, we need to look at the typeCovered field of - // the new RRSIG that we received. Process only if the typeCovered matches. - if ((r1->resrec.rrtype == r2->resrec.rrtype) && (r1->resrec.rrtype == kDNSType_RRSIG)) - { - rdataRRSig *rrsig1 = (rdataRRSig *)(((RDataBody2 *)(r1->resrec.rdata->u.data))->data); - rdataRRSig *rrsig2 = (rdataRRSig *)(((RDataBody2 *)(r2->resrec.rdata->u.data))->data); - if (swap16(rrsig1->typeCovered) != swap16(rrsig2->typeCovered)) - { - debugf("mDNSCoreReceiveResponse: Received RRSIG typeCovered %s, found %s, not processing", - DNSTypeName(swap16(rrsig1->typeCovered)), DNSTypeName(swap16(rrsig2->typeCovered))); - continue; - } - } - // For Unicast (null InterfaceID) the resolver IDs should also match if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) && +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + (r1->resrec.InterfaceID || match) && +#else (r1->resrec.InterfaceID || (id1 == id2)) && +#endif r1->resrec.rrtype == r2->resrec.rrtype && - r1->resrec.rrclass == r2->resrec.rrclass) + r1->resrec.rrclass == r2->resrec.rrclass +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + // 2 RRSIGs need to cover the same DNS type to be identified as one RRSET, and have the same TTL + && are_records_in_the_same_cache_set_for_dnssec(&r1->resrec, &r2->resrec) +#endif + ) { - if (r1->resrec.mortality == Mortality_Mortal && r2->resrec.mortality != Mortality_Mortal) - { - verbosedebugf("mDNSCoreReceiveResponse: R1(%p) is being immortalized by R2(%p)", r1, r2); - r1->resrec.mortality = Mortality_Immortal; // Immortalize the replacement record - } - - // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) - // else, if record is old, mark it to be flushed - if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) - { - // If we find mismatched TTLs in an RRSet, correct them. - // We only do this for records with a TTL of 2 or higher. It's possible to have a - // goodbye announcement with the cache flush bit set (or a case-change on record rdata, - // which we treat as a goodbye followed by an addition) and in that case it would be - // inappropriate to synchronize all the other records to a TTL of 0 (or 1). - - // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, - // because certain early Bonjour devices are known to have this specific mismatch, and - // there's no point filling syslog with messages about something we already know about. - // We also don't log this for uDNS responses, since a caching name server is obliged - // to give us an aged TTL to correct for how long it has held the record, - // so our received TTLs are expected to vary in that case - - // We also suppress log message in the case of SRV records that are received - // with a TTL of 4500 that are already cached with a TTL of 120 seconds, since - // this behavior was observed for a number of discoveryd based AppleTV's in iOS 8 - // GM builds. - if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) + if (r1->resrec.mortality == Mortality_Mortal && r2->resrec.mortality != Mortality_Mortal) { - if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) && - !(r2->resrec.rroriginalttl == 120 && r1->resrec.rroriginalttl == 4500 && r2->resrec.rrtype == kDNSType_SRV) && - mDNSOpaque16IsZero(response->h.id)) - LogInfo("Correcting TTL from %4d to %4d for %s", - r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); - r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; + verbosedebugf("mDNSCoreReceiveResponse: R1(%p) is being immortalized by R2(%p)", r1, r2); + r1->resrec.mortality = Mortality_Immortal; // Immortalize the replacement record } - r2->TimeRcvd = m->timenow; - SetNextCacheCheckTimeForRecord(m, r2); - } - else if (r2->resrec.InterfaceID) // else, if record is old, mark it to be flushed - { - verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1)); - verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2)); - // We set stale records to expire in one second. - // This gives the owner a chance to rescue it if necessary. - // This is important in the case of multi-homing and bridged networks: - // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be - // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit - // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet - // will promptly delete their cached copies of the (still valid) Ethernet IP address record. - // By delaying the deletion by one second, we give X a change to notice that this bridging has - // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. - - // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary - // final expiration queries for this record. - - // If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache - // flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual - // one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates. - // <rdar://problem/5636422> Updating TXT records is too slow - // We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above, - // which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0. - if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries) + + // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) + // else, if record is old, mark it to be flushed + if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) { - LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2)); - r2->resrec.rroriginalttl = 0; + // If we find mismatched TTLs in an RRSet, correct them. + // We only do this for records with a TTL of 2 or higher. It's possible to have a + // goodbye announcement with the cache flush bit set (or a case-change on record rdata, + // which we treat as a goodbye followed by an addition) and in that case it would be + // inappropriate to synchronize all the other records to a TTL of 0 (or 1). + + // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, + // because certain early Bonjour devices are known to have this specific mismatch, and + // there's no point filling syslog with messages about something we already know about. + // We also don't log this for uDNS responses, since a caching name server is obliged + // to give us an aged TTL to correct for how long it has held the record, + // so our received TTLs are expected to vary in that case + + // We also suppress log message in the case of SRV records that are received + // with a TTL of 4500 that are already cached with a TTL of 120 seconds, since + // this behavior was observed for a number of discoveryd based AppleTV's in iOS 8 + // GM builds. + if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) + { + if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) && + !(r2->resrec.rroriginalttl == 120 && r1->resrec.rroriginalttl == 4500 && r2->resrec.rrtype == kDNSType_SRV) && + ResponseIsMDNS) + LogInfo("Correcting TTL from %4d to %4d for %s", + r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); + r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; + } + r2->TimeRcvd = m->timenow; + SetNextCacheCheckTimeForRecord(m, r2); } - else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) + else if (r2->resrec.InterfaceID) // else, if record is old, mark it to be flushed { - // We only set a record to expire in one second if it currently has *more* than a second to live - // If it's already due to expire in a second or less, we just leave it alone - r2->resrec.rroriginalttl = 1; - r2->UnansweredQueries = MaxUnansweredQueries; - r2->TimeRcvd = m->timenow - 1; - // We use (m->timenow - 1) instead of m->timenow, because we use that to identify records - // that we marked for deletion via an explicit DE record + verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1)); + verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2)); + // We set stale records to expire in one second. + // This gives the owner a chance to rescue it if necessary. + // This is important in the case of multi-homing and bridged networks: + // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be + // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit + // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet + // will promptly delete their cached copies of the (still valid) Ethernet IP address record. + // By delaying the deletion by one second, we give X a change to notice that this bridging has + // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. + + // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary + // final expiration queries for this record. + + // If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache + // flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual + // one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates. + // <rdar://problem/5636422> Updating TXT records is too slow + // We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above, + // which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0. + if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries) + { + LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2)); + r2->resrec.rroriginalttl = 0; + } + else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) + { + // We only set a record to expire in one second if it currently has *more* than a second to live + // If it's already due to expire in a second or less, we just leave it alone + r2->resrec.rroriginalttl = 1; + r2->UnansweredQueries = MaxUnansweredQueries; + r2->TimeRcvd = m->timenow - 1; + // We use (m->timenow - 1) instead of m->timenow, because we use that to identify records + // that we marked for deletion via an explicit DE record + } + SetNextCacheCheckTimeForRecord(m, r2); } - SetNextCacheCheckTimeForRecord(m, r2); - } - else - { -#if AWD_METRICS + else + { +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) if (r2->resrec.mortality == Mortality_Ghost) { DNSQuestion * q; for (q = m->Questions; q; q=q->next) { if (!q->LongLived && ActiveQuestion(q) && - ResourceRecordAnswersQuestion(&r2->resrec, q) && + CacheRecordAnswersQuestion(r2, q) && q->metrics.expiredAnswerState == ExpiredAnswer_AnsweredWithExpired) { q->metrics.expiredAnswerState = ExpiredAnswer_ExpiredAnswerChanged; @@ -9720,37 +9708,14 @@ exit: } #endif // Old uDNS records are scheduled to be purged instead of given at most one second to live. - r2->resrec.mortality = Mortality_Mortal; // We want it purged, so remove any immortality mDNS_PurgeCacheResourceRecord(m, r2); purgedRecords = mDNStrue; } } - } + } if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to { - // If we had a unicast question for this response with at least one positive answer and we - // have NSECRecords, it is most likely a wildcard expanded answer. Cache the NSEC and its - // signatures along with the cache record which will be used for validation later. If - // we rescued a few records earlier in this function, then NSECCachePtr would be set. In that - // use that instead. - if (response->h.numAnswers && unicastQuestion && NSECRecords) - { - if (!NSECCachePtr) - { - LogInfo("mDNSCoreReceiveResponse: Updating NSECCachePtr to %s", CRDisplayString(m, r1)); - NSECCachePtr = r1; - } - // Note: We need to do this before we call CacheRecordDeferredAdd as this - // might start the verification process which needs these NSEC records - if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) - { - LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); - FreeNSECRecords(m, NSECRecords); - } - NSECRecords = mDNSNULL; - NSECCachePtr = mDNSNULL; - } if (r1->resrec.InterfaceID) { r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash); @@ -9767,41 +9732,19 @@ exit: } } - // If we have not consumed the NSEC records yet e.g., just refreshing the cache, - // update them now for future validations. - if (NSECRecords && NSECCachePtr) - { - LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr)); - if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) - { - LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); - FreeNSECRecords(m, NSECRecords); - } - NSECRecords = mDNSNULL; - NSECCachePtr = mDNSNULL; - } - - // If there is at least one answer and we did not create RRSIGs and there was a - // ValidatingResponse question waiting for this response, give a hint that no RRSIGs - // were created. We don't need to give a hint: - // - // - if we have no answers, the mDNSCoreReceiveNoUnicastAnswers below should - // generate a negative response - // - // - if we have NSECRecords, it means we might have a potential proof for - // non-existence of name that we are looking for - // - if (response->h.numAnswers && !rrsigsCreated && DNSSECQuestion && !NSECRecords) - mDNSCoreReceiveNoDNSSECAnswers(m, response, end, dstaddr, dstport, InterfaceID); - // See if we need to generate negative cache entries for unanswered unicast questions - mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, LLQType, rcode, NSECRecords); + mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + querier, uDNSService, +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + &denial_of_existence_records, +#endif + LLQType); - if (McastNSEC3Records) - { - debugf("mDNSCoreReceiveResponse: McastNSEC3Records not used"); - FreeNSECRecords(m, McastNSEC3Records); - } +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + destroy_denial_of_existence_records_t_if_nonnull(denial_of_existence_records); +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) } // ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing @@ -10339,7 +10282,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return; if (mDNS_PacketLoggingEnabled) - DumpPacket(mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + DumpPacket(mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end, InterfaceID); ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space); if (ptr) @@ -10404,7 +10347,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) { mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec); - AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem); + AuthRecord *ar = (AuthRecord *) mDNSPlatformMemAllocateClear(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem); if (!ar) { m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; @@ -10468,7 +10411,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, } } - if (p) mDNSSendDNSMessage(m, &m->omsg, p, InterfaceID, m->SPSSocket, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + if (p) mDNSSendDNSMessage(m, &m->omsg, p, InterfaceID, mDNSNULL, m->SPSSocket, srcaddr, srcport, mDNSNULL, mDNSfalse); mDNS_SendKeepalives(m); } @@ -10494,7 +10437,7 @@ mDNSlocal mDNSu32 mDNSGenerateOwnerOptForInterface(mDNS *const m, const mDNSInte { // Put all the integer values in IETF byte-order (MSB first, LSB second) SwapDNSHeaderBytes(msg); - length = (end - msg->data); + length = (mDNSu32)(end - msg->data); } else LogSPS("mDNSGenerateOwnerOptForInterface: Failed to generate owner OPT record"); @@ -10573,8 +10516,13 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg if (m->SleepLimit) m->NextScheduledSPRetry = m->timenow; } -mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, - const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver) +mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, const domainname *const name, + const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_dns_service_t service) +#else + DNSServer *dnsserver) +#endif { if (cr == &m->rec.r && m->rec.r.resrec.RecordType) LogFatalError("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); @@ -10582,7 +10530,11 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, // Create empty resource record cr->resrec.RecordType = kDNSRecordTypePacketNegative; cr->resrec.InterfaceID = InterfaceID; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_replace(&cr->resrec.dnsservice, service); +#else cr->resrec.rDNSServer = dnsserver; +#endif cr->resrec.name = name; // Will be updated to point to cg->name when we call CreateNewCacheEntry cr->resrec.rrtype = rrtype; cr->resrec.rrclass = rrclass; @@ -10598,18 +10550,30 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, cr->TimeRcvd = m->timenow; cr->DelayDelivery = 0; cr->NextRequiredQuery = m->timenow; +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + cr->LastCachedAnswerTime= 0; +#endif cr->CRActiveQuestion = mDNSNULL; cr->UnansweredQueries = 0; cr->LastUnansweredTime = 0; cr->NextInCFList = mDNSNULL; - cr->nsec = mDNSNULL; cr->soa = mDNSNULL; - cr->CRDNSSECQuestion = 0; // Initialize to the basic one and the caller can set it to more // specific based on the response if any cr->responseFlags = ResponseFlags; } +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +mDNSexport void mDNSCoreReceiveForQuerier(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + mdns_querier_t querier, mdns_dns_service_t dnsservice) +{ + SwapDNSHeaderBytes(msg); + mDNS_Lock(m); + mDNSCoreReceiveResponse(m, msg, end, mDNSNULL, zeroIPPort, mDNSNULL, zeroIPPort, querier, dnsservice, mDNSNULL); + mDNS_Unlock(m); +} +#endif + mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) @@ -10667,7 +10631,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up - if (srcaddr && !mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } + if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } mDNS_Lock(m); m->PktNum++; @@ -10692,13 +10656,17 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS { ifid = mDNSInterface_Any; if (mDNS_PacketLoggingEnabled) - DumpPacket(mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + DumpPacket(mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end, InterfaceID); uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport); // Note: mDNSCore also needs to get access to received unicast responses } #endif if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, mDNSNULL, mDNSNULL, ifid); +#else else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); +#endif else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, srcaddr, InterfaceID); else @@ -10735,17 +10703,6 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS #pragma mark - Searcher Functions #endif -// Targets are considered the same if both queries are untargeted, or -// if both are targeted to the same address+port -// (If Target address is zero, TargetPort is undefined) -#define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \ - (mDNSSameAddress(& (A)->Target, & (B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort))) - -// SameQuestionKind is true if *both* questions are either multicast or unicast -// TargetQID is used for this determination. -#define SameQuestionKind(A,B) ((mDNSOpaque16IsZero(A) && mDNSOpaque16IsZero(B)) || \ - ((!mDNSOpaque16IsZero(A)) && (!mDNSOpaque16IsZero(B)))) - // Note: We explicitly disallow making a public query be a duplicate of a private one. This is to avoid the // circular deadlock where a client does a query for something like "dns-sd -Q _dns-query-tls._tcp.company.com SRV" // and we have a key for company.com, so we try to locate the private query server for company.com, which necessarily entails @@ -10765,8 +10722,9 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS // (a) long-lived and // (b) being performed by a unicast DNS long-lived query (either full LLQ, or polling) // for multicast questions, we don't want to treat LongLived as anything special -#define IsLLQ(Q) ((Q)->LongLived && !mDNSOpaque16IsZero((Q)->TargetQID)) -#define IsAWDLIncluded(Q) (((Q)->flags & kDNSServiceFlagsIncludeAWDL) != 0) +#define IsLLQ(Q) ((Q)->LongLived && !mDNSOpaque16IsZero((Q)->TargetQID)) +#define AWDLIsIncluded(Q) (((Q)->flags & kDNSServiceFlagsIncludeAWDL) != 0) +#define SameQuestionKind(Q1, Q2) (mDNSOpaque16IsZero((Q1)->TargetQID) == mDNSOpaque16IsZero((Q2)->TargetQID)) mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question) { @@ -10775,24 +10733,24 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest // This prevents circular references, where two questions are each marked as a duplicate of the other. // Accordingly, we break out of the loop when we get to 'question', because there's no point searching // further in the list. - for (q = m->Questions; q && q != question; q=q->next) // Scan our list for another question - if (q->InterfaceID == question->InterfaceID && // with the same InterfaceID, - SameQTarget(q, question) && // and same unicast/multicast target settings - q->qtype == question->qtype && // type, - q->qclass == question->qclass && // class, - IsLLQ(q) == IsLLQ(question) && // and long-lived status matches - (!q->AuthInfo || question->AuthInfo) && // to avoid deadlock, don't make public query dup of a private one - (q->AnonInfo == question->AnonInfo) && // Anonymous query not a dup of normal query - (q->SuppressQuery == question->SuppressQuery) && // Questions that are suppressed/not suppressed - (q->ValidationRequired == question->ValidationRequired) && // Questions that require DNSSEC validation - (q->ValidatingResponse == question->ValidatingResponse) && // Questions that are validating responses using DNSSEC - (q->DisallowPID == question->DisallowPID) && // Disallowing a PID should not affect a PID that is allowed - (q->BrowseThreshold == question->BrowseThreshold) && // browse thresholds must match - q->qnamehash == question->qnamehash && - (IsAWDLIncluded(q) == IsAWDLIncluded(question)) && // Inclusion of AWDL interface must match - SameQuestionKind(q->TargetQID, question->TargetQID) && // mDNS or uDNS must match - SameDomainName(&q->qname, &question->qname)) // and name - return(q); + for (q = m->Questions; q && (q != question); q = q->next) + { + if (!SameQuestionKind(q, question)) continue; + if (q->qnamehash != question->qnamehash) continue; + if (q->InterfaceID != question->InterfaceID) continue; + if (q->qtype != question->qtype) continue; + if (q->qclass != question->qclass) continue; + if (IsLLQ(q) != IsLLQ(question)) continue; + if (q->AuthInfo && !question->AuthInfo) continue; + if (!q->Suppressed != !question->Suppressed) continue; + if (q->BrowseThreshold != question->BrowseThreshold) continue; + if (AWDLIsIncluded(q) != AWDLIsIncluded(question)) continue; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (q->dnsservice != question->dnsservice) continue; +#endif + if (!SameDomainName(&q->qname, &question->qname)) continue; + return(q); + } return(mDNSNULL); } @@ -10806,9 +10764,11 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi // question as a duplicate. if (question->DuplicateOf) { - LogInfo("UpdateQuestionDuplicates: question %p %##s (%s) duplicate of %p %##s (%s)", - question, question->qname.c, DNSTypeName(question->qtype), - question->DuplicateOf, question->DuplicateOf->qname.c, DNSTypeName(question->DuplicateOf->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "[R%d->DupQ%d->Q%d] UpdateQuestionDuplicates: question %p " PRI_DM_NAME " (" PUB_S ") duplicate of %p " PRI_DM_NAME " (" PUB_S ")", + question->request_id, mDNSVal16(question->TargetQID), mDNSVal16(question->DuplicateOf->TargetQID), + question, DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype), question->DuplicateOf, + DM_NAME_PARAM(&question->DuplicateOf->qname), DNSTypeName(question->DuplicateOf->qtype)); return; } @@ -10832,15 +10792,25 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi q->nta = question->nta; q->servAddr = question->servAddr; q->servPort = question->servPort; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_replace(&q->dnsservice, question->dnsservice); + mdns_forget(&question->dnsservice); + mdns_querier_forget(&q->querier); + mdns_replace(&q->querier, question->querier); + mdns_forget(&question->querier); +#else q->qDNSServer = question->qDNSServer; q->validDNSServers = question->validDNSServers; q->unansweredQueries = question->unansweredQueries; q->noServerResponse = question->noServerResponse; q->triedAllServersOnce = question->triedAllServersOnce; +#endif q->TargetQID = question->TargetQID; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) q->LocalSocket = question->LocalSocket; // No need to close old q->LocalSocket first -- duplicate questions can't have their own sockets +#endif q->state = question->state; // q->tcp = question->tcp; @@ -10849,13 +10819,16 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi q->ntries = question->ntries; q->id = question->id; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) question->LocalSocket = mDNSNULL; +#endif question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question // question->tcp = mDNSNULL; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) if (q->LocalSocket) debugf("UpdateQuestionDuplicates transferred LocalSocket pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - +#endif if (q->nta) { LogInfo("UpdateQuestionDuplicates transferred nta pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -10883,7 +10856,8 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname if (!d) d = (const domainname *)""; - LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "mDNS_AddMcastResolver: Adding " PUB_DM_NAME ", InterfaceID %p, timeout %u", DM_NAME_PARAM(d), interface, timeout); mDNS_CheckLock(m); @@ -10905,7 +10879,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname else { // allocate, add to list - *p = mDNSPlatformMemAllocate(sizeof(**p)); + *p = (McastResolver *) mDNSPlatformMemAllocateClear(sizeof(**p)); if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc"); else { @@ -10919,6 +10893,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname return(*p); } +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server) { mDNSs32 ptime = 0; @@ -10938,6 +10913,7 @@ mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server) } return ptime; } +#endif //Checks to see whether the newname is a better match for the name, given the best one we have //seen so far (given in bestcount). @@ -11046,17 +11022,18 @@ mDNSexport mDNSBool DomainEnumQuery(const domainname *qname) return mDNStrue; } +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) // Note: InterfaceID is the InterfaceID of the question mDNSlocal mDNSBool DNSServerMatch(DNSServer *d, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID) { - // 1) Unscoped questions (NULL InterfaceID) should consider *only* unscoped DNSServers ( DNSServer - // with "scoped" set to kScopeNone) + // 1) Unscoped questions (NULL InterfaceID) should consider *only* unscoped DNSServers ( DNSServer + // with scopeType set to kScopeNone) // // 2) Scoped questions (non-NULL InterfaceID) should consider *only* scoped DNSServers (DNSServer - // with "scoped" set to kScopeInterfaceId) and their InterfaceIDs should match. + // with scopeType set to kScopeInterfaceID) and their InterfaceIDs should match. // // 3) Scoped questions (non-zero ServiceID) should consider *only* scoped DNSServers (DNSServer - // with "scoped" set to kScopeServiceID) and their ServiceIDs should match. + // with scopeType set to kScopeServiceID) and their ServiceIDs should match. // // The first condition in the "if" statement checks to see if both the question and the DNSServer are // unscoped. The question is unscoped only if InterfaceID is zero and ServiceID is -1. @@ -11073,13 +11050,10 @@ mDNSlocal mDNSBool DNSServerMatch(DNSServer *d, mDNSInterfaceID InterfaceID, mDN // // - DNSServer is scoped and InterfaceID is not NULL - the InterfaceID of the question and the DNSServer // should match (Refer to (2) above). - // - // Note: mDNSInterface_Unicast is used only by .local unicast questions and are treated as unscoped. - // If a question is scoped both to InterfaceID and ServiceID, the question will be scoped to InterfaceID. - if (((d->scoped == kScopeNone) && ((!InterfaceID && ServiceID == -1) || InterfaceID == mDNSInterface_Unicast)) || - ((d->scoped == kScopeInterfaceID) && d->interface == InterfaceID) || - ((d->scoped == kScopeServiceID) && d->serviceID == ServiceID)) + if (((d->scopeType == kScopeNone) && (!InterfaceID && ServiceID == -1)) || + ((d->scopeType == kScopeInterfaceID) && d->interface == InterfaceID) || + ((d->scopeType == kScopeServiceID) && d->serviceID == ServiceID)) { return mDNStrue; } @@ -11100,11 +11074,11 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) DEQuery = DomainEnumQuery(&question->qname); for (curr = m->DNSServers; curr; curr = curr->next) { - debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped); + debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scopeType); // skip servers that will soon be deleted - if (curr->flags & DNSServer_FlagDelete) + if (curr->flags & DNSServerFlag_Delete) { - debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); + debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scopeType); continue; } @@ -11117,16 +11091,16 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout // Skip DNSServers that are InterfaceID Scoped but have no valid interfaceid set OR DNSServers that are ServiceID Scoped but have no valid serviceid set - if ((curr->scoped == kScopeInterfaceID && curr->interface == mDNSInterface_Any) || (curr->scoped == kScopeServiceID && curr->serviceID <= 0)) + if (((curr->scopeType == kScopeInterfaceID) && (curr->interface == mDNSInterface_Any)) || + ((curr->scopeType == kScopeServiceID) && (curr->serviceID <= 0))) { - LogInfo("SetValidDNSServers: ScopeType[%d] Skipping DNS server %#a (Domain %##s) Interface:[%p] Serviceid:[%d]", curr->scoped, &curr->addr, curr->domain.c, curr->interface, curr->serviceID); + LogInfo("SetValidDNSServers: ScopeType[%d] Skipping DNS server %#a (Domain %##s) Interface:[%p] Serviceid:[%d]", + (int)curr->scopeType, &curr->addr, curr->domain.c, curr->interface, curr->serviceID); continue; } currcount = CountLabels(&curr->domain); - if ((!curr->cellIntf || (!DEQuery && !(question->flags & kDNSServiceFlagsDenyCellular))) && - (!curr->isExpensive || !(question->flags & kDNSServiceFlagsDenyExpensive)) && - DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) + if ((!DEQuery || !curr->isCell) && DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) { bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); @@ -11144,11 +11118,11 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) timeout = 0; } debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d," - " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout, + " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scopeType, index, curr->timeout, curr->interface); timeout += curr->timeout; if (DEQuery) - debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf); + debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->isCell); bit_set_opaque128(question->validDNSServers, index); } } @@ -11156,11 +11130,10 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) } question->noServerResponse = 0; - debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x%x%x for question %p %##s (%s)", + debugf("SetValidDNSServers: ValidDNSServer bits 0x%08x%08x%08x%08x for question %p %##s (%s)", question->validDNSServers.l[3], question->validDNSServers.l[2], question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype)); // If there are no matching resolvers, then use the default timeout value. - // For ProxyQuestion, shorten the timeout so that dig does not timeout on us in case of no response. - return ((question->ProxyQuestion || question->ValidatingResponse) ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT); + return (timeout ? timeout : DEFAULT_UDNS_TIMEOUT); } // Get the Best server that matches a name. If you find penalized servers, look for the one @@ -11181,9 +11154,9 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac for (curr = m->DNSServers; curr; curr = curr->next) { // skip servers that will soon be deleted - if (curr->flags & DNSServer_FlagDelete) + if (curr->flags & DNSServerFlag_Delete) { - debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); + debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scopeType); continue; } @@ -11246,7 +11219,7 @@ mDNSlocal DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInter char *ifname = mDNSNULL; // for logging purposes only mDNSOpaque128 allValid; - if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + if (InterfaceID == mDNSInterface_LocalOnly) InterfaceID = mDNSNULL; if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); @@ -11275,7 +11248,7 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) const domainname *name = &question->qname; int currindex; - if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + if (InterfaceID == mDNSInterface_LocalOnly) InterfaceID = mDNSNULL; if (InterfaceID) @@ -11290,23 +11263,23 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) if (curmatch != mDNSNULL) { - LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) for %##s (%s)", - question, curmatch, &curmatch->addr, mDNSVal16(curmatch->port), - (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", - InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] GetServerForQuestion: %p DNS server (%p) " PRI_IP_ADDR ":%d (Penalty Time Left %d) (Scope " PUB_S ":%p:%d) for " PRI_DM_NAME " (" PUB_S ")", + question->request_id, mDNSVal16(question->TargetQID), question, curmatch, &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), + ifname ? ifname : "None", InterfaceID, question->ServiceID, DM_NAME_PARAM(name), DNSTypeName(question->qtype)); } else { - LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) for %##s (%s)", - question, ifname ? ifname : "None", InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] GetServerForQuestion: %p no DNS server (Scope " PUB_S ":%p:%d) for " PRI_DM_NAME " (" PUB_S ")", + question->request_id, mDNSVal16(question->TargetQID), question, ifname ? ifname : "None", InterfaceID, + question->ServiceID, DM_NAME_PARAM(name), DNSTypeName(question->qtype)); } return(curmatch); } - - -#define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \ - (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort))) +#endif // MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) // Called in normal client context (lock not held) mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) @@ -11318,221 +11291,165 @@ mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) for (q = m->Questions; q; q=q->next) if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) startLLQHandshake(m, q); // If ExternalPort is zero, will do StartLLQPolling instead -#if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); -#endif mDNS_Unlock(m); } -mDNSlocal mDNSBool IsPrivateDomain(mDNS *const m, DNSQuestion *q) -{ - DomainAuthInfo *AuthInfo; - // Skip Private domains as we have special addresses to get the hosts in the Private domain - AuthInfo = GetAuthInfoForName_internal(m, &q->qname); - if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel) - { - debugf("IsPrivateDomain: %##s true", q->qname.c); - return mDNStrue; - } - else - { - debugf("IsPrivateDomain: %##s false", q->qname.c); - return mDNSfalse; - } -} - -#define TrueFalseStr(X) ((X) ? "true" : "false") - // This function takes the DNSServer as a separate argument because sometimes the -// caller has not yet assigned the DNSServer, but wants to evaluate the SuppressQuery +// caller has not yet assigned the DNSServer, but wants to evaluate the Suppressed // status before switching to it. -mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNSServer *d) +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +mDNSexport mDNSBool ShouldSuppressUnicastQuery(const DNSQuestion *const q, const mdns_dns_service_t dnsservice) +#else +mDNSlocal mDNSBool ShouldSuppressUnicastQuery(const DNSQuestion *const q, const DNSServer *const server) +#endif { - // Some callers don't check for the qtype - if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA) - { - LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; - } + mDNSBool suppress = mDNSfalse; + const char *reason = mDNSNULL; - // Private domains are exempted irrespective of what the DNSServer says - if (IsPrivateDomain(m, q)) + if (q->BlockedByPolicy) { - LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; - } - - if (!d) - { - LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, as the DNS server is NULL", q->qname.c, DNSTypeName(q->qtype)); - return mDNStrue; + suppress = mDNStrue; + reason = " (blocked by policy)"; } - - // Check if the DNS Configuration allows A/AAAA queries to be sent - if ((q->qtype == kDNSType_A) && d->req_A) +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + else if (!dnsservice) { - // The server's configuration allows A record queries, so don't suppress this query unless - // 1. the interface associated with the server is CLAT46; and - // 2. the query has the kDNSServiceFlagsPathEvaluationDone flag, which indicates that it came from libnetcore. - // See <rdar://problem/42672030> for more info. - if (!(d->isCLAT46 && (q->flags & kDNSServiceFlagsPathEvaluationDone))) + if (!q->IsUnicastDotLocal) { - LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows A queries", q->qname.c, - DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port)); - return mDNSfalse; + suppress = mDNStrue; + reason = " (no DNS service)"; } } - if ((q->qtype == kDNSType_AAAA) && d->req_AAAA) +#else + else if (!server) { - LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows AAAA queries", q->qname.c, - DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port)); - return mDNSfalse; + if (!q->IsUnicastDotLocal) + { + suppress = mDNStrue; + reason = " (no DNS server)"; + } } -#if USE_DNS64 - if (DNS64IsQueryingARecord(q->dns64.state)) +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + else if ((q->flags & kDNSServiceFlagsDenyCellular) && mdns_dns_service_interface_is_cellular(dnsservice)) +#else + else if ((q->flags & kDNSServiceFlagsDenyCellular) && server->isCell) +#endif { - LogDebug("ShouldSuppressUnicastQuery: DNS64 query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; + suppress = mDNStrue; + reason = " (interface is cellular)"; } +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + else if ((q->flags & kDNSServiceFlagsDenyExpensive) && mdns_dns_service_interface_is_expensive(dnsservice)) +#else + else if ((q->flags & kDNSServiceFlagsDenyExpensive) && server->isExpensive) #endif - - LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, since DNS Configuration does not allow (req_A %s, req_AAAA %s, CLAT46 %s)", - q->qname.c, DNSTypeName(q->qtype), TrueFalseStr(d->req_A), TrueFalseStr(d->req_AAAA), TrueFalseStr(d->isCLAT46)); - - return mDNStrue; -} - -mDNSlocal mDNSBool ShouldSuppressDotLocalQuery(mDNS *const m, DNSQuestion *q) -{ - NetworkInterfaceInfo *intf; - AuthRecord *rr; - mDNSBool ret; - - // Check to see if there is at least one interface other than loopback and don't suppress - // .local questions if you find one. If we have at least one interface, it means that - // we can send unicast queries for the .local name and we don't want to suppress - // multicast in that case as upper layers don't know how to handle if we return a - // negative response for multicast followed by a positive response for unicast. - // - // Note: we used to check for multicast capable interfaces instead of just any interface - // present. That did not work in the case where we have a valid interface for unicast - // but not multicast capable e.g., cellular, as we ended up delivering a negative response - // first and the upper layer did not wait for the positive response that came later. - for (intf = m->HostInterfaces; intf; intf = intf->next) { - if (intf->InterfaceActive && !intf->Loopback) - { - LogInfo("ShouldSuppressDotLocalQuery: Found interface %s, not suppressing", intf->ifname); - return mDNSfalse; - } + suppress = mDNStrue; + reason = " (interface is expensive)"; } - - // 1. If we find a LocalOnly or P2P record answering this question, then don't suppress it. - // Set m->CurrentQuestion as it is required by AnswerQuestionWithLORecord. - m->CurrentQuestion = q; - ret = AnswerQuestionWithLORecord(m, q, mDNStrue); - m->CurrentQuestion = mDNSNULL; - - if (ret) +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + else if ((q->flags & kDNSServiceFlagsDenyConstrained) && mdns_dns_service_interface_is_constrained(dnsservice)) +#else + else if ((q->flags & kDNSServiceFlagsDenyConstrained) && server->isConstrained) +#endif { - LogInfo("ShouldSuppressDotLocalQuery: Found LocalOnly record for %##s (%s), not suppressing", q->qname.c, - DNSTypeName(q->qtype)); - return mDNSfalse; + suppress = mDNStrue; + reason = " (interface is constrained)"; } - - // 2. If we find a local AuthRecord answering this question, then don't suppress it. - for (rr = m->ResourceRecords; rr; rr = rr->next) +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) + else if (q->SuppressUnusable && !DNS64IsQueryingARecord(q->dns64.state)) +#else + else if (q->SuppressUnusable) +#endif { - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + if (q->qtype == kDNSType_A) + { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (!mdns_dns_service_a_queries_advised(dnsservice)) +#else + if (!server->usableA) +#endif + { + suppress = mDNStrue; + reason = " (A records are unusable)"; + } + // If the server's configuration allows A record queries, suppress this query if + // 1. the interface associated with the server is CLAT46; and + // 2. the query has the kDNSServiceFlagsPathEvaluationDone flag, indicating that it's from libnetwork. + // See <rdar://problem/42672030> for more info. +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + else if ((q->flags & kDNSServiceFlagsPathEvaluationDone) && mdns_dns_service_interface_is_clat46(dnsservice)) +#else + else if ((q->flags & kDNSServiceFlagsPathEvaluationDone) && server->isCLAT46) +#endif + { + suppress = mDNStrue; + reason = " (CLAT46 A records are unusable)"; + } + } + else if (q->qtype == kDNSType_AAAA) { - LogInfo("ShouldSuppressDotLocalQuery: Found resource record %s for %##s (%s) not suppressing", ARDisplayString(m, rr), - q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (!mdns_dns_service_aaaa_queries_advised(dnsservice)) +#else + if (!server->usableAAAA) +#endif + { + suppress = mDNStrue; + reason = " (AAAA records are unusable)"; + } } } - return mDNStrue; -} - -mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, DNSQuestion *q) -{ - if (q->InterfaceID == mDNSInterface_LocalOnly) + if (suppress) { - LogInfo("ShouldSuppressQuery: LocalOnly query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[Q%u] ShouldSuppressUnicastQuery: Query suppressed for " PRI_DM_NAME " " PUB_S PUB_S, + mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), reason ? reason : ""); } + return suppress; +} - if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA) +mDNSlocal mDNSBool ShouldSuppressQuery(DNSQuestion *q) +{ + // Multicast queries are never suppressed. + if (mDNSOpaque16IsZero(q->TargetQID)) { - LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); return mDNSfalse; } - - // We still want the ability to be able to listen to the local services and hence - // don't fail .local query if we have local records that can potentially answer - // the question. - if (q->InterfaceID != mDNSInterface_Unicast && IsLocalDomain(&q->qname)) - { - if (!ShouldSuppressDotLocalQuery(m, q)) - { - LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; - } - else - { - LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype)); - return mDNStrue; - } - } - - return (ShouldSuppressUnicastQuery(m, q, q->qDNSServer)); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + return (ShouldSuppressUnicastQuery(q, q->dnsservice)); +#else + return (ShouldSuppressUnicastQuery(q, q->qDNSServer)); +#endif } mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q) { - CacheRecord *rr; + CacheRecord *cr; CacheGroup *cg; cg = CacheGroupForName(m, q->qnamehash, &q->qname); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) { // Don't deliver RMV events for negative records - if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) { - LogInfo("CacheRecordRmvEventsForCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d", - CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] CacheRecordRmvEventsForCurrentQuestion: CacheRecord " PRI_S " Suppressing RMV events for question %p " PRI_DM_NAME " (" PUB_S "), CRActiveQuestion %p, CurrentAnswers %d", + q->request_id, mDNSVal16(q->TargetQID), CRDisplayString(m, cr), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), cr->CRActiveQuestion, q->CurrentAnswers); continue; } - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + if (SameNameCacheRecordAnswersQuestion(cr, q)) { LogInfo("CacheRecordRmvEventsForCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s LocalAnswers %d", - q->qname.c, CRDisplayString(m, rr), q->LOAddressAnswers); + q->qname.c, CRDisplayString(m, cr), q->LOAddressAnswers); q->CurrentAnswers--; - if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; - - if (rr->CRActiveQuestion == q) - { - DNSQuestion *qptr; - // If this was the active question for this cache entry, it was the one that was - // responsible for keeping the cache entry fresh when the cache entry was reaching - // its expiry. We need to handover the responsibility to someone else. Otherwise, - // when the cache entry is about to expire, we won't find an active question - // (pointed by CRActiveQuestion) to refresh the cache. - for (qptr = m->Questions; qptr; qptr=qptr->next) - if (qptr != q && ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr)) - break; - - if (qptr) - LogInfo("CacheRecordRmvEventsForCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, " - "Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d", - qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery); - - rr->CRActiveQuestion = qptr; // Question used to be active; new value may or may not be null - if (!qptr) m->rrcache_active--; // If no longer active, decrement rrcache_active count - } - AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); + if (cr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + AnswerCurrentQuestionWithResourceRecord(m, cr, QC_rmv); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } } @@ -11546,7 +11463,11 @@ mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question) return mDNSfalse; } +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +mDNSexport mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q) +#else mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q) +#endif { AuthRecord *rr; AuthGroup *ag; @@ -11611,24 +11532,28 @@ mDNSlocal void SuppressStatusChanged(mDNS *const m, DNSQuestion *q, DNSQuestion // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero // LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers) - if (q->SuppressQuery) + if (q->Suppressed) { - q->SuppressQuery = mDNSfalse; + q->Suppressed = mDNSfalse; if (!CacheRecordRmvEventsForQuestion(m, q)) { - LogInfo("SuppressStatusChanged: Question deleted while delivering RMV events from cache"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] SuppressStatusChanged: Question deleted while delivering RMV events from cache", + q->request_id, mDNSVal16(q->TargetQID)); return; } - q->SuppressQuery = mDNStrue; + q->Suppressed = mDNStrue; } // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts) - // and SuppressQuery status does not mean anything for these questions. As we are going to stop the + // and Suppressed status does not mean anything for these questions. As we are going to stop the // question below, we need to deliver the RMV events so that the ADDs that will be delivered during // the restart will not be a duplicate ADD if (!LocalRecordRmvEventsForQuestion(m, q)) { - LogInfo("SuppressStatusChanged: Question deleted while delivering RMV events from Local AuthRecords"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] SuppressStatusChanged: Question deleted while delivering RMV events from Local AuthRecords", + q->request_id, mDNSVal16(q->TargetQID)); return; } @@ -11642,9 +11567,9 @@ mDNSlocal void SuppressStatusChanged(mDNS *const m, DNSQuestion *q, DNSQuestion // // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question - // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false). + // is a duplicate of non-SuppressUnusable question if it is not suppressed (Suppressed is false). // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed - // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an + // (Suppressed is true). The reason for this is that when a question is suppressed, we want an // immediate response and not want to be blocked behind a question that is querying DNS servers. When // the question is not suppressed, we don't want two active questions sending packets on the wire. // This affects both efficiency and also the current design where there is only one active question @@ -11659,7 +11584,9 @@ mDNSlocal void SuppressStatusChanged(mDNS *const m, DNSQuestion *q, DNSQuestion // // It is much cleaner and less error prone to build a list of questions and restart at the end. - LogInfo("SuppressStatusChanged: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] SuppressStatusChanged: Stop question %p " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); mDNS_StopQuery_internal(m, q); q->next = *restart; *restart = q; @@ -11675,7 +11602,7 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) // we potentially restart questions here in this function that ends up as new questions, // which may be suppressed at this instance. Before it is handled we get another network // event that changes the status e.g., address becomes available. If we did not process - // new questions, we would never change its SuppressQuery status. + // new questions, we would never change its Suppressed status. // // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the // application callback can potentially stop the current question (detected by CurrentQuestion) or @@ -11692,9 +11619,9 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) m->RestartQuestion = q->next; if (q->SuppressUnusable) { - mDNSBool old = q->SuppressQuery; - q->SuppressQuery = ShouldSuppressQuery(m, q); - if (q->SuppressQuery != old) + const mDNSBool old = q->Suppressed; + q->Suppressed = ShouldSuppressQuery(q); + if (q->Suppressed != old) { // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before // followed by a negative cache response. Temporarily turn off suppression so that @@ -11713,10 +11640,11 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) } } +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) mDNSlocal void RestartUnicastQuestions(mDNS *const m) { DNSQuestion *q; - DNSQuestion *restart = mDNSNULL; + DNSQuestion *restartList = mDNSNULL; if (m->RestartQuestion) LogMsg("RestartUnicastQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)", @@ -11731,50 +11659,39 @@ mDNSlocal void RestartUnicastQuestions(mDNS *const m) if (mDNSOpaque16IsZero(q->TargetQID)) LogMsg("RestartUnicastQuestions: ERROR!! Restart set for multicast question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->Restart = 0; - SuppressStatusChanged(m, q, &restart); + q->Restart = mDNSfalse; + SuppressStatusChanged(m, q, &restartList); } } - while (restart) + while ((q = restartList) != mDNSNULL) { - q = restart; - restart = restart->next; + restartList = q->next; q->next = mDNSNULL; - LogInfo("RestartUnicastQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] RestartUnicastQuestions: Start question %p " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); mDNS_StartQuery_internal(m, q); } } - +#endif // ValidateParameters() is called by mDNS_StartQuery_internal() to check the client parameters of // DNS Question that are already set by the client before calling mDNS_StartQuery() mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) { - - if (question->Target.type && !ValidQuestionTarget(question)) - { - LogMsg("ValidateParameters: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)", - question->Target.type, mDNSVal16(question->TargetPort), question->qname.c); - question->Target.type = mDNSAddrType_None; - } - - // If no question->Target specified, clear TargetPort - if (!question->Target.type) - question->TargetPort = zeroIPPort; - if (!ValidateDomainName(&question->qname)) { LogMsg("ValidateParameters: Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); return(mStatus_Invalid); } - // If this question is referencing a specific interface, verify it exists - if (question->InterfaceID && !LocalOnlyOrP2PInterface(question->InterfaceID) && question->InterfaceID != mDNSInterface_Unicast) + // If this question is referencing a specific interface, verify it exists + if (question->InterfaceID && !LocalOnlyOrP2PInterface(question->InterfaceID)) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); if (!intf) LogInfo("ValidateParameters: Note: InterfaceID %d for question %##s (%s) not currently found in active interface list", - (uint32_t)question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); + IIDPrintable(question->InterfaceID), question->qname.c, DNSTypeName(question->qtype)); } return(mStatus_NoError); @@ -11785,12 +11702,16 @@ mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) { // First reset all DNS Configuration +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_forget(&question->dnsservice); +#else question->qDNSServer = mDNSNULL; question->validDNSServers = zeroOpaque128; - question->triedAllServersOnce = 0; - question->noServerResponse = 0; + question->triedAllServersOnce = mDNSfalse; + question->noServerResponse = mDNSfalse; +#endif question->StopTime = (question->TimeoutQuestion) ? question->StopTime : 0; -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) mDNSPlatformMemZero(&question->metrics, sizeof(question->metrics)); question->metrics.expiredAnswerState = (question->allowExpired != AllowExpired_None) ? ExpiredAnswer_Allowed : ExpiredAnswer_None; #endif @@ -11801,7 +11722,11 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) // Proceed to initialize DNS Configuration (some are set in SetValidDNSServers()) if (!mDNSOpaque16IsZero(question->TargetQID)) { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSu32 timeout = 30; +#else mDNSu32 timeout = SetValidDNSServers(m, question); +#endif // We set the timeout value the first time mDNS_StartQuery_internal is called for a question. // So if a question is restarted when a network change occurs, the StopTime is not reset. // Note that we set the timeout for all questions. If this turns out to be a duplicate, @@ -11809,14 +11734,21 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) if (question->TimeoutQuestion && !question->StopTime) { question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); - LogInfo("InitDNSConfig: Setting StopTime on the uDNS question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[Q%u] InitDNSConfig: Setting StopTime on the uDNS question %p " PRI_DM_NAME " (" PUB_S ")", + mDNSVal16(question->TargetQID), question, DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype)); } +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + Querier_SetDNSServiceForQuestion(question); +#else question->qDNSServer = GetServerForQuestion(m, question); - LogDebug("InitDNSConfig: question %p %##s (%s) Timeout %d, DNS Server %#a:%d", - question, question->qname.c, DNSTypeName(question->qtype), timeout, - question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, - mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "[R%u->Q%u] InitDNSConfig: question %p " PRI_DM_NAME " " PUB_S " Timeout %d, DNS Server " PRI_IP_ADDR ":%d", + question->request_id, mDNSVal16(question->TargetQID), question, DM_NAME_PARAM(&question->qname), + DNSTypeName(question->qtype), timeout, question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); +#endif } else if (question->TimeoutQuestion && !question->StopTime) { @@ -11825,7 +11757,9 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) mDNSu32 timeout = LocalOnlyOrP2PInterface(question->InterfaceID) ? DEFAULT_LO_OR_P2P_TIMEOUT : GetTimeoutForMcastQuestion(m, question); question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); - LogInfo("InitDNSConfig: Setting StopTime on question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] InitDNSConfig: Setting StopTime on the uDNS question %p " PRI_DM_NAME " (" PUB_S ")", + question->request_id, mDNSVal16(question->TargetQID), question, DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype)); } // Set StopTime here since it is a part of DNS Configuration if (question->StopTime) @@ -11841,7 +11775,6 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) mDNSlocal void InitCommonState(mDNS *const m, DNSQuestion *const question) { int i; - mDNSBool isBlocked = mDNSfalse; // Note: In the case where we already have the answer to this question in our cache, that may be all the client // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would @@ -11855,7 +11788,7 @@ mDNSlocal void InitCommonState(mDNS *const m, DNSQuestion *const question) // stopped and can't be on the list. The question is already on the list and ThisQInterval // can be negative if the caller just stopped it and starting it again. Hence, it always has to // be initialized. CheckForSoonToExpireRecords below prints the cache records when logging is - // turned ON which can allocate memory e.g., base64 encoding, in the case of DNSSEC. + // turned ON which can allocate memory e.g., base64 encoding. question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question question->qnamehash = DomainNameHashValue(&question->qname); question->DelayAnswering = mDNSOpaque16IsZero(question->TargetQID) ? CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash) : 0; @@ -11890,14 +11823,26 @@ mDNSlocal void InitCommonState(mDNS *const m, DNSQuestion *const question) question->FlappingInterface1 = mDNSNULL; question->FlappingInterface2 = mDNSNULL; + // mDNSPlatformGetDNSRoutePolicy() and InitDNSConfig() may set a DNSQuestion's BlockedByPolicy value, + // so they should be called before calling ShouldSuppressQuery(), which checks BlockedByPolicy. + question->BlockedByPolicy = mDNSfalse; + // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetDNSRoutePolicy() // since we would already have the question->ServiceID in that case. if (!(question->flags & kDNSServiceFlagsServiceIndex)) { -#if APPLE_OSX_mDNSResponder - mDNSPlatformGetDNSRoutePolicy(question, &isBlocked); -#else question->ServiceID = -1; +#if APPLE_OSX_mDNSResponder + if (!(question->flags & kDNSServiceFlagsPathEvaluationDone) || question->ForcePathEval) + { + if (question->flags & kDNSServiceFlagsPathEvaluationDone) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] Forcing another path evaluation", question->request_id, mDNSVal16(question->TargetQID)); + } + question->ForcePathEval = mDNSfalse; + mDNSPlatformGetDNSRoutePolicy(question); + } #endif } else @@ -11905,19 +11850,8 @@ mDNSlocal void InitCommonState(mDNS *const m, DNSQuestion *const question) DNSTypeName(question->qtype), question->pid, question->euid, question->ServiceID); InitDNSConfig(m, question); - question->AuthInfo = GetAuthInfoForQuestion(m, question); - question->SuppressQuery = 0; - if (question->SuppressUnusable) - question->SuppressQuery = ShouldSuppressQuery(m, question); - - // If ServiceID is 0 or the policy disallows making DNS requests, - // set DisallowPID - question->DisallowPID = (question->ServiceID == 0 || isBlocked); - if (question->DisallowPID) - LogInfo("InitCommonState: Query suppressed for %##s (%s), PID %d/ServiceID %d not allowed", question->qname.c, - DNSTypeName(question->qtype), question->pid, question->ServiceID); - + question->Suppressed = ShouldSuppressQuery(question); question->NextInDQList = mDNSNULL; question->SendQNow = mDNSNULL; question->SendOnAll = mDNSfalse; @@ -11945,7 +11879,9 @@ mDNSlocal void InitCommonState(mDNS *const m, DNSQuestion *const question) for (i=0; i<DupSuppressInfoSize; i++) question->DupSuppress[i].InterfaceID = mDNSNULL; - question->Restart = 0; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + question->Restart = mDNSfalse; +#endif debugf("InitCommonState: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow, @@ -11966,7 +11902,11 @@ mDNSlocal void InitWABState(DNSQuestion *const question) // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single // NAT mapping for receiving inbound add/remove events. question->LocalSocket = mDNSNULL; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_querier_forget(&question->querier); +#else question->unansweredQueries = 0; +#endif question->nta = mDNSNULL; question->servAddr = zeroAddr; question->servPort = zeroIPPort; @@ -11990,48 +11930,19 @@ mDNSlocal void InitLLQNATState(mDNS *const m) mDNSlocal void InitLLQState(DNSQuestion *const question) { - question->state = LLQ_InitialRequest; + question->state = LLQ_Init; question->ReqLease = 0; question->expire = 0; question->ntries = 0; question->id = zeroOpaque64; } -#ifdef DNS_PUSH_ENABLED -mDNSlocal void InitDNSPNState(DNSQuestion *const question) -{ - question->dnsPushState = DNSPUSH_INIT; -} -#endif // DNS_PUSH_ENABLED - // InitDNSSECProxyState() is called by mDNS_StartQuery_internal() to initialize // DNSSEC & DNS Proxy fields of the DNS Question. mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question) { (void) m; - - // DNS server selection affects DNSSEC. Turn off validation if req_DO is not set - // or the request is going over cellular interface. - // - // Note: This needs to be done here before we call FindDuplicateQuestion as it looks - // at ValidationRequired setting also. - if (question->qDNSServer) - { - if (question->qDNSServer->cellIntf) - { - debugf("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype)); - question->ValidationRequired = mDNSfalse; - } - if (DNSSECOptionalQuestion(question) && !(question->qDNSServer->req_DO)) - { - LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); req_DO false", - question->qname.c, DNSTypeName(question->qtype)); - question->ValidationRequired = DNSSEC_VALIDATION_NONE; - } - } - question->ValidationState = (question->ValidationRequired ? DNSSECValRequired : DNSSECValNotRequired); - question->ValidationStatus = 0; - question->responseFlags = zeroID; + question->responseFlags = zeroID; } // Once the question is completely initialized including the duplicate logic, this function @@ -12042,31 +11953,41 @@ mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question) // Ensure DNS related info of duplicate question is same as the orig question if (question->DuplicateOf) { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + const DNSQuestion *const duplicateOf = question->DuplicateOf; + mdns_replace(&question->dnsservice, duplicateOf->dnsservice); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->DupQ%u->Q%u] Duplicate question " PRI_DM_NAME " (" PUB_S ")", + question->request_id, mDNSVal16(question->TargetQID), mDNSVal16(duplicateOf->TargetQID), + DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype)); +#else question->validDNSServers = question->DuplicateOf->validDNSServers; // If current(dup) question has DNS Server assigned but the original question has no DNS Server assigned to it, // then we log a line as it could indicate an issue if (question->DuplicateOf->qDNSServer == mDNSNULL) { if (question->qDNSServer) - LogInfo("FinalizeUnicastQuestion: Current(dup) question %p has DNSServer(%#a:%d) but original question(%p) has no DNS Server! %##s (%s)", - question, question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, - mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort), - question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype)); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] FinalizeUnicastQuestion: Current(dup) question %p has DNSServer(" PRI_IP_ADDR ":%d) but original question(%p) has no DNS Server! " PRI_DM_NAME " (" PUB_S ")", + question->request_id, mDNSVal16(question->TargetQID), question, + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort), question->DuplicateOf, + DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype)); + } } question->qDNSServer = question->DuplicateOf->qDNSServer; - LogInfo("FinalizeUnicastQuestion: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d", - question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), - question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, - mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->DupQ%d->Q%d] FinalizeUnicastQuestion: Duplicate question %p (%p) " PRI_DM_NAME " (" PUB_S "), DNS Server " PRI_IP_ADDR ":%d", + question->request_id, mDNSVal16(question->TargetQID), mDNSVal16(question->DuplicateOf->TargetQID), + question, question->DuplicateOf, DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype), + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); +#endif } ActivateUnicastQuery(m, question, mDNSfalse); - if (!question->DuplicateOf && DNSSECQuestion(question)) - { - // For DNSSEC questions, we need to have the RRSIGs also for verification. - CheckForDNSSECRecords(m, question); - } if (question->LongLived) { // Unlike other initializations, InitLLQNATState should be done after @@ -12075,9 +11996,6 @@ mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question) // the LLQ NAT state only for unicast. Otherwise we will unnecessarily // start the NAT traversal that is not needed. InitLLQNATState(m); -#if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); -#endif } } @@ -12097,6 +12015,8 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu #ifdef USE_LIBIDN // If the TLD includes high-ascii bytes, assume it will need to be converted to Punycode. // (In the future the root name servers may answer UTF-8 queries directly, but for now they do not.) + // This applies to the top label (TLD) only + // -- for the second level and down we try UTF-8 first, and then fall back to Punycode only if UTF-8 fails. if (IsHighASCIILabel(LastLabel(&question->qname))) { domainname newname; @@ -12105,11 +12025,11 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu } #endif // USE_LIBIDN - question->TargetQID = #ifndef UNICAST_DISABLED - (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : -#endif // UNICAST_DISABLED - zeroID; + question->TargetQID = Question_uDNS(question) ? mDNS_NewMessageID(m) : zeroID; +#else + question->TargetQID = zeroID; +#endif debugf("mDNS_StartQuery_internal: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); // Note: It important that new questions are appended at the *end* of the list, not prepended at the start @@ -12135,9 +12055,6 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu InitCommonState(m, question); InitWABState(question); InitLLQState(question); -#ifdef DNS_PUSH_ENABLED - InitDNSPNState(question); -#endif // DNS_PUSH_ENABLED InitDNSSECProxyState(m, question); // FindDuplicateQuestion should be called last after all the intialization @@ -12169,10 +12086,11 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu } else { -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) m->NumAllInterfaceQuestions++; - LogInfo("mDNS_StartQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", - m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "mDNS_StartQuery_internal: NumAllInterfaceRecords %u NumAllInterfaceQuestions %u " PRI_DM_NAME " (" PUB_S ")", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype)); if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) { m->NextBonjourDisableTime = 0; @@ -12184,7 +12102,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu m->NetworkChanged = m->timenow; } } -#endif // BONJOUR_ON_DEMAND +#endif if (question->WakeOnResolve) { LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c); @@ -12215,7 +12133,7 @@ mDNSexport void CancelGetZoneData(mDNS *const m, ZoneData *nta) mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) { CacheGroup *cg = CacheGroupForName(m, question->qnamehash, &question->qname); - CacheRecord *rr; + CacheRecord *cr; DNSQuestion **qp = &m->Questions; //LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); @@ -12233,28 +12151,44 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que return(mStatus_BadReferenceErr); } -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) if (!LocalOnlyOrP2PInterface(question->InterfaceID) && mDNSOpaque16IsZero(question->TargetQID)) { if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) m->NextBonjourDisableTime = NonZeroTime(m->timenow + (BONJOUR_DISABLE_DELAY * mDNSPlatformOneSecond)); m->NumAllInterfaceQuestions--; - LogInfo("mDNS_StopQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", - m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "mDNS_StopQuery_internal: NumAllInterfaceRecords %u NumAllInterfaceQuestions %u " PRI_DM_NAME " (" PUB_S ")", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype)); } -#endif // BONJOUR_ON_DEMAND +#endif -#if AWD_METRICS - if (Question_uDNS(question) && !question->metrics.answered && (question->metrics.querySendCount > 0)) +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + if (Question_uDNS(question) && !question->metrics.answered && (question->metrics.firstQueryTime != 0)) { - const domainname * queryName; - mDNSBool isForCell; - mDNSu32 durationMs; + mDNSu32 querySendCount = question->metrics.querySendCount; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (question->querier) + { + querySendCount += mdns_querier_get_send_count(question->querier); + } +#endif + if (querySendCount > 0) + { + const domainname * queryName; + mDNSBool isForCell; + mDNSu32 durationMs; - queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname; - isForCell = (question->qDNSServer && question->qDNSServer->cellIntf); - durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; - MetricsUpdateDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, question->metrics.expiredAnswerState, durationMs, isForCell); + queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + isForCell = (question->dnsservice && mdns_dns_service_interface_is_cellular(question->dnsservice)); +#else + isForCell = (question->qDNSServer && question->qDNSServer->isCell); +#endif + durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; + MetricsUpdateDNSQueryStats(queryName, question->qtype, mDNSNULL, querySendCount, + question->metrics.expiredAnswerState, question->metrics.dnsOverTCPState, durationMs, isForCell); + } } #endif // Take care to cut question from list *before* calling UpdateQuestionDuplicates @@ -12264,9 +12198,9 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que // If there are any cache records referencing this as their active question, then see if there is any // other question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) { - if (rr->CRActiveQuestion == question) + if (cr->CRActiveQuestion == question) { DNSQuestion *q; DNSQuestion *replacement = mDNSNULL; @@ -12276,7 +12210,7 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que // via CacheRecordRmv() when the cache record expires. for (q = m->Questions; q && (q != m->NewQuestions); q = q->next) { - if (!q->DuplicateOf && !QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + if (!q->DuplicateOf && !q->Suppressed && CacheRecordAnswersQuestion(cr, q)) { if (q->ThisQInterval > 0) { @@ -12291,8 +12225,8 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que } if (replacement) debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question " - "CurrentAnswers %d, SuppressQuery %d", replacement, CRDisplayString(m,rr), question->CurrentAnswers, replacement->CurrentAnswers, replacement->SuppressQuery); - rr->CRActiveQuestion = replacement; // Question used to be active; new value may or may not be null + "CurrentAnswers %d, Suppressed %d", replacement, CRDisplayString(m,cr), question->CurrentAnswers, replacement->CurrentAnswers, replacement->Suppressed); + cr->CRActiveQuestion = replacement; // Question used to be active; new value may or may not be null if (!replacement) m->rrcache_active--; // If no longer active, decrement rrcache_active count } } @@ -12322,13 +12256,6 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que m->RestartQuestion = question->next; } - if (m->ValidationQuestion == question) - { - LogInfo("mDNS_StopQuery_internal: Just deleted the current Validation question: %##s (%s)", - question->qname.c, DNSTypeName(question->qtype)); - m->ValidationQuestion = question->next; - } - // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions question->next = mDNSNULL; @@ -12341,6 +12268,9 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query. if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } if (question->LocalSocket) { mDNSPlatformUDPClose(question->LocalSocket); question->LocalSocket = mDNSNULL; } +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + Querier_HandleStoppedDNSQuestion(question); +#endif if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived) { // Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal. @@ -12376,44 +12306,21 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que question->tcp = mDNSNULL; } } -#ifdef DNS_PUSH_ENABLED - else if (question->dnsPushState == DNSPUSH_ESTABLISHED) +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) + else if (question->dnsPushServer != mDNSNULL) { - if (question->tcp) - { - UnSubscribeToDNSPushNotificationServer(m, q); - question->tcp->question = mDNSNULL; - question->tcp = mDNSNULL; - } + UnSubscribeToDNSPushNotificationServer(m, question); } -#endif // DNS_PUSH_ENABLED -#if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); #endif } // wait until we send the refresh above which needs the nta if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } - if (question->ValidationRequired && question->DNSSECAuthInfo) - { - LogInfo("mDNS_StopQuery_internal: freeing DNSSECAuthInfo %##s", question->qname.c); - question->DAIFreeCallback(m, question->DNSSECAuthInfo); - question->DNSSECAuthInfo = mDNSNULL; - } - if (question->AnonInfo) - { - FreeAnonInfo(question->AnonInfo); - question->AnonInfo = mDNSNULL; - } -#if AWD_METRICS - if (question->metrics.originalQName) - { - mDNSPlatformMemFree(question->metrics.originalQName); - question->metrics.originalQName = mDNSNULL; - } +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + uDNSMetricsClear(&question->metrics); #endif -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) DNS64ResetState(question); #endif @@ -12454,16 +12361,18 @@ mDNSexport mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const q status = mDNS_StopQuery_internal(m, question); if (status == mStatus_NoError && !qq) { - const CacheRecord *rr; + const CacheRecord *cr; CacheGroup *const cg = CacheGroupForName(m, question->qnamehash, &question->qname); LogInfo("Generating terminal removes for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (rr->resrec.RecordType != kDNSRecordTypePacketNegative && SameNameRecordAnswersQuestion(&rr->resrec, question)) + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + { + if (cr->resrec.RecordType != kDNSRecordTypePacketNegative && SameNameCacheRecordAnswersQuestion(cr, question)) { // Don't use mDNS_DropLockBeforeCallback() here, since we don't allow API calls if (question->QuestionCallback) - question->QuestionCallback(m, question, &rr->resrec, QC_rmv); + question->QuestionCallback(m, question, &cr->resrec, QC_rmv); } + } } mDNS_Unlock(m); return(status); @@ -12494,13 +12403,12 @@ mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const question, const domainname *const srv, const domainname *const domain, - const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags, + const mDNSInterfaceID InterfaceID, mDNSu32 flags, mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, mDNSQuestionCallback *Callback, void *Context) { question->InterfaceID = InterfaceID; question->flags = flags; - question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; question->LongLived = mDNStrue; @@ -12508,42 +12416,29 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu question->ForceMCast = ForceMCast; question->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; question->SuppressUnusable = mDNSfalse; - question->SearchListIndex = 0; - question->AppendSearchDomains = 0; - question->RetryWithSearchDomains = mDNSfalse; + question->AppendSearchDomains = mDNSfalse; question->TimeoutQuestion = 0; question->WakeOnResolve = 0; - question->UseBackgroundTrafficClass = useBackgroundTrafficClass; - question->ValidationRequired = 0; - question->ValidatingResponse = 0; + question->UseBackgroundTraffic = useBackgroundTrafficClass; question->ProxyQuestion = 0; - question->qnameOrig = mDNSNULL; - question->AnonInfo = mDNSNULL; question->QuestionCallback = Callback; question->QuestionContext = Context; if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); - if (anondata) - { - question->AnonInfo = AllocateAnonInfo(&question->qname, anondata, mDNSPlatformStrLen(anondata), mDNSNULL); - if (!question->AnonInfo) - return(mStatus_BadParamErr); - } - return(mDNS_StartQuery_internal(m, question)); } mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, const domainname *const srv, const domainname *const domain, - const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags, + const mDNSInterfaceID InterfaceID, mDNSu32 flags, mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, mDNSQuestionCallback *Callback, void *Context) { mStatus status; mDNS_Lock(m); - status = mDNS_StartBrowse_internal(m, question, srv, domain, anondata, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context); + status = mDNS_StartBrowse_internal(m, question, srv, domain, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context); mDNS_Unlock(m); return(status); } @@ -12554,7 +12449,6 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m { question->InterfaceID = InterfaceID; question->flags = 0; - question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; question->LongLived = mDNSfalse; @@ -12562,17 +12456,11 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->ForceMCast = mDNSfalse; question->ReturnIntermed = mDNSfalse; question->SuppressUnusable = mDNSfalse; - question->SearchListIndex = 0; - question->AppendSearchDomains = 0; - question->RetryWithSearchDomains = mDNSfalse; + question->AppendSearchDomains = mDNSfalse; question->TimeoutQuestion = 0; question->WakeOnResolve = 0; - question->UseBackgroundTrafficClass = mDNSfalse; - question->ValidationRequired = 0; - question->ValidatingResponse = 0; + question->UseBackgroundTraffic = mDNSfalse; question->ProxyQuestion = 0; - question->qnameOrig = mDNSNULL; - question->AnonInfo = mDNSNULL; question->pid = mDNSPlatformGetPID(); question->euid = 0; question->QuestionCallback = Callback; @@ -12678,58 +12566,105 @@ mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) // Circular reference: AdvertiseInterface references mDNS_HostNameCallback, which calls mDNS_SetFQDN, which call AdvertiseInterface mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) +mDNSlocal void mDNS_RandomizedHostNameCallback(mDNS *m, AuthRecord *rr, mStatus result); +#endif + +mDNSlocal AuthRecord *GetInterfaceAddressRecord(NetworkInterfaceInfo *intf, mDNSBool forRandHostname) +{ +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + return(forRandHostname ? &intf->RR_AddrRand : &intf->RR_A); +#else + (void)forRandHostname; // Unused. + return(&intf->RR_A); +#endif +} -mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) +mDNSlocal AuthRecord *GetFirstAddressRecordEx(const mDNS *const m, const mDNSBool forRandHostname) { NetworkInterfaceInfo *intf; for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) break; - return(intf); + { + if (!intf->Advertise) continue; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (mDNSPlatformInterfaceIsAWDL(intf->InterfaceID)) continue; +#endif + return(GetInterfaceAddressRecord(intf, forRandHostname)); + } + return(mDNSNULL); } +#define GetFirstAddressRecord(M) GetFirstAddressRecordEx(M, mDNSfalse) // The parameter "set" here refers to the set of AuthRecords used to advertise this interface. // (It's a set of records, not a set of interfaces.) +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) +mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool useRandomizedHostname) +#else mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) +#endif { + const domainname *hostname; + mDNSRecordCallback *hostnameCallback; + AuthRecord *addrAR; + AuthRecord *ptrAR; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + const mDNSBool interfaceIsAWDL = mDNSPlatformInterfaceIsAWDL(set->InterfaceID); +#endif + mDNSu8 addrRecordType; char buffer[MAX_REVERSE_MAPPING_NAME]; - NetworkInterfaceInfo *primary; - mDNSu8 recordType; - if (m->AutoTargetServices == 0) +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (interfaceIsAWDL || useRandomizedHostname) { - LogInfo("AdvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); - return; + hostname = &m->RandomizedHostname; + hostnameCallback = mDNS_RandomizedHostNameCallback; + } + else +#endif + { + hostname = &m->MulticastHostname; + hostnameCallback = mDNS_HostNameCallback; } - primary = FindFirstAdvertisedInterface(m); - if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary - // We should never have primary be NULL, because even if there is - // no other interface yet, we should always find ourself in the list. - - // If interface is marked as a direct link, we can assume the address record is unique - // and does not need to go through the probe phase of the probe/announce packet sequence. - recordType = (set->DirectLink ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique); +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (!interfaceIsAWDL && useRandomizedHostname) + { + addrAR = &set->RR_AddrRand; + ptrAR = mDNSNULL; + } + else +#endif + { + addrAR = &set->RR_A; + ptrAR = &set->RR_PTR; + } + if (addrAR->resrec.RecordType != kDNSRecordTypeUnregistered) return; - if (set->DirectLink) - LogInfo("AdvertiseInterface: Marking address record as kDNSRecordTypeKnownUnique for %s", set->ifname); + addrRecordType = set->DirectLink ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (hostname == &m->RandomizedHostname) addrRecordType = kDNSRecordTypeKnownUnique; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "AdvertiseInterface: Advertising " PUB_S " hostname on interface " PUB_S, + (hostname == &m->RandomizedHostname) ? "randomized" : "normal", set->ifname); +#else + LogInfo("AdvertiseInterface: Advertising for ifname %s", set->ifname); +#endif // Send dynamic update for non-linklocal IPv4 Addresses - mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, recordType, AuthRecordAny, mDNS_HostNameCallback, set); - mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); - mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(addrAR, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, addrRecordType, AuthRecordAny, hostnameCallback, set); + if (ptrAR) mDNS_SetupResourceRecord(ptrAR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); #if ANSWER_REMOTE_HOSTNAME_QUERIES - set->RR_A.AllowRemoteQuery = mDNStrue; - set->RR_PTR.AllowRemoteQuery = mDNStrue; - set->RR_HINFO.AllowRemoteQuery = mDNStrue; + addrAR->AllowRemoteQuery = mDNStrue; + if (ptrAR) ptrAR->AllowRemoteQuery = mDNStrue; #endif // 1. Set up Address record to map from host name ("foo.local.") to IP address // 2. Set up reverse-lookup PTR record to map from our address back to our host name - AssignDomainName(&set->RR_A.namestorage, &m->MulticastHostname); + AssignDomainName(&addrAR->namestorage, hostname); if (set->ip.type == mDNSAddrType_IPv4) { - set->RR_A.resrec.rrtype = kDNSType_A; - set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; + addrAR->resrec.rrtype = kDNSType_A; + addrAR->resrec.rdata->u.ipv4 = set->ip.ip.v4; // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); @@ -12737,8 +12672,8 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) else if (set->ip.type == mDNSAddrType_IPv6) { int i; - set->RR_A.resrec.rrtype = kDNSType_AAAA; - set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; + addrAR->resrec.rrtype = kDNSType_AAAA; + addrAR->resrec.rdata->u.ipv6 = set->ip.ip.v6; for (i = 0; i < 16; i++) { static const char hexValues[] = "0123456789ABCDEF"; @@ -12750,102 +12685,114 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); } - MakeDomainNameFromDNSNameString(&set->RR_PTR.namestorage, buffer); - set->RR_PTR.AutoTarget = Target_AutoHost; // Tell mDNS that the target of this PTR is to be kept in sync with our host name - set->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server - - set->RR_A.RRSet = &primary->RR_A; // May refer to self + if (ptrAR) + { + MakeDomainNameFromDNSNameString(&ptrAR->namestorage, buffer); + ptrAR->AutoTarget = Target_AutoHost; // Tell mDNS that the target of this PTR is to be kept in sync with our host name + ptrAR->ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server + } - mDNS_Register_internal(m, &set->RR_A); - mDNS_Register_internal(m, &set->RR_PTR); +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + addrAR->RRSet = interfaceIsAWDL ? addrAR : GetFirstAddressRecordEx(m, useRandomizedHostname); +#else + addrAR->RRSet = GetFirstAddressRecord(m); +#endif + if (!addrAR->RRSet) addrAR->RRSet = addrAR; + mDNS_Register_internal(m, addrAR); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "Initialized RRSet for " PRI_S, ARDisplayString(m, addrAR)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "RRSet: " PRI_S, ARDisplayString(m, addrAR->RRSet)); + if (ptrAR) mDNS_Register_internal(m, ptrAR); -#if APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) // must be after the mDNS_Register_internal() calls so that records have complete rdata fields, etc D2D_start_advertising_interface(set); -#endif // APPLE_OSX_mDNSResponder +#endif +} - if (!NO_HINFO && m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) +mDNSlocal void AdvertiseInterfaceIfNeeded(mDNS *const m, NetworkInterfaceInfo *set) +{ +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (mDNSPlatformInterfaceIsAWDL(set->InterfaceID)) { - mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; - AssignDomainName(&set->RR_HINFO.namestorage, &m->MulticastHostname); - set->RR_HINFO.DependentOn = &set->RR_A; - mDNSPlatformMemCopy(p, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); - p += 1 + (int)p[0]; - mDNSPlatformMemCopy(p, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); - mDNS_Register_internal(m, &set->RR_HINFO); + if ((m->AutoTargetAWDLIncludedCount > 0) || (m->AutoTargetAWDLOnlyCount > 0)) + { + AdvertiseInterface(m, set, mDNSfalse); + } } else { - debugf("Not creating HINFO record: platform support layer provided no information"); - set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; + if (m->AutoTargetServices > 0) AdvertiseInterface(m, set, mDNSfalse); + if (m->AutoTargetAWDLIncludedCount > 0) AdvertiseInterface(m, set, mDNStrue); } +#else + if (m->AutoTargetServices > 0) AdvertiseInterface(m, set); +#endif } -mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) +mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set, DeadvertiseFlags flags) { - if (m->AutoTargetServices == 0) - { - LogInfo("DeadvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); - return; - } - -#if APPLE_OSX_mDNSResponder - D2D_stop_advertising_interface(set); -#endif // APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + const mDNSBool interfaceIsAWDL = mDNSPlatformInterfaceIsAWDL(set->InterfaceID); +#endif // Unregister these records. // When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). - if (set->RR_A .resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); - if (set->RR_PTR .resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); - if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); -} - -mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m) -{ - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if ((!interfaceIsAWDL && (flags & kDeadvertiseFlag_NormalHostname)) || + ( interfaceIsAWDL && (flags & kDeadvertiseFlag_RandHostname))) +#else + if (flags & kDeadvertiseFlag_NormalHostname) +#endif { - if (intf->Advertise) - { - LogInfo("AdvertiseInterface: Advertising for ifname %s", intf->ifname); - AdvertiseInterface(m, intf); - } + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "DeadvertiseInterface: Deadvertising " PUB_S " hostname on interface " PUB_S, + (flags & kDeadvertiseFlag_RandHostname) ? "randomized" : "normal", set->ifname); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + D2D_stop_advertising_interface(set); +#endif + if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); + if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); } -} - -mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) -{ - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (!interfaceIsAWDL && (flags & kDeadvertiseFlag_RandHostname)) { - if (intf->Advertise) - { - LogInfo("DeadvertiseInterface: Deadvertising for ifname %s", intf->ifname); - DeadvertiseInterface(m, intf); - } + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "DeadvertiseInterface: Deadvertising randomized hostname on interface " PUB_S, set->ifname); + AuthRecord *const ar = &set->RR_AddrRand; + if (ar->resrec.RecordType) mDNS_Deregister_internal(m, ar, mDNS_Dereg_normal); } +#endif } // Change target host name for record. mDNSlocal void UpdateTargetHostName(mDNS *const m, AuthRecord *const rr) { -#if APPLE_OSX_mDNSResponder - // If this record was also registered with any D2D plugins, stop advertising - // the version with the old host name. - D2D_stop_advertising_record(rr); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + // If this record was also registered with any D2D plugins, stop advertising + // the version with the old host name. + D2D_stop_advertising_record(rr); #endif SetTargetToHostName(m, rr); -#if APPLE_OSX_mDNSResponder - // Advertise the record with the updated host name with the D2D plugins if appropriate. - D2D_start_advertising_record(rr); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + // Advertise the record with the updated host name with the D2D plugins if appropriate. + D2D_start_advertising_record(rr); #endif } +mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m, DeadvertiseFlags flags) +{ + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->Advertise) DeadvertiseInterface(m, intf, flags); + } +} + mDNSexport void mDNS_SetFQDN(mDNS *const m) { domainname newmname; @@ -12861,8 +12808,8 @@ mDNSexport void mDNS_SetFQDN(mDNS *const m) else { AssignDomainName(&m->MulticastHostname, &newmname); - DeadvertiseAllInterfaceRecords(m); - AdvertiseAllInterfaceRecords(m); + DeadvertiseAllInterfaceRecords(m, kDeadvertiseFlag_NormalHostname); + AdvertiseNecessaryInterfaceRecords(m); } // 3. Make sure that any AutoTarget SRV records (and the like) get updated @@ -12920,6 +12867,44 @@ mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatu LogMsg("mDNS_HostNameCallback: Unknown error %d for registration of record %s", result, rr->resrec.name->c); } +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) +mDNSlocal void mDNS_RandomizedHostNameCallback(mDNS *const m, AuthRecord *const addrRecord, const mStatus result) +{ + (void)addrRecord; // Unused parameter + + if (result == mStatus_NameConflict) + { + AuthRecord *rr; + domainlabel newUUIDLabel; + + GetRandomUUIDLabel(&newUUIDLabel); + if (SameDomainLabel(newUUIDLabel.c, m->RandomizedHostname.c)) + { + IncrementLabelSuffix(&newUUIDLabel, mDNSfalse); + } + + mDNS_Lock(m); + + m->RandomizedHostname.c[0] = 0; + AppendDomainLabel(&m->RandomizedHostname, &newUUIDLabel); + AppendLiteralLabelString(&m->RandomizedHostname, "local"); + + DeadvertiseAllInterfaceRecords(m, kDeadvertiseFlag_RandHostname); + AdvertiseNecessaryInterfaceRecords(m); + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if (rr->AutoTarget && AuthRecordIncludesOrIsAWDL(rr)) UpdateTargetHostName(m, rr); + } + for (rr = m->DuplicateRecords; rr; rr = rr->next) + { + if (rr->AutoTarget && AuthRecordIncludesOrIsAWDL(rr)) UpdateTargetHostName(m, rr); + } + + mDNS_Unlock(m); + } +} +#endif + mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) { NetworkInterfaceInfo *intf; @@ -12976,7 +12961,7 @@ mDNSexport void mDNS_ActivateNetWake_internal(mDNS *const m, NetworkInterfaceInf if (set->InterfaceActive) { LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip); - mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, mDNSNULL, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set); + mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set); } } @@ -13020,10 +13005,19 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s NetworkInterfaceInfo **p = &m->HostInterfaces; if (!set->InterfaceID) - { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } + { + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_ERROR, + "Tried to register a NetworkInterfaceInfo with zero InterfaceID - ifaddr: " PRI_IP_ADDR, &set->ip); + return(mStatus_Invalid); + } if (!mDNSAddressIsValidNonZero(&set->mask)) - { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } + { + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_ERROR, + "Tried to register a NetworkInterfaceInfo with invalid mask - ifaddr: " PRI_IP_ADDR ", ifmask: " PUB_IP_ADDR, + &set->ip, &set->mask); + return(mStatus_Invalid); + } mDNS_Lock(m); @@ -13039,7 +13033,9 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { if (*p == set) { - LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo that's already in the list"); + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_ERROR, + "Tried to register a NetworkInterfaceInfo that's already in the list - " + "ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, set->ifname, &set->ip); mDNS_Unlock(m); return(mStatus_AlreadyRegistered); } @@ -13059,14 +13055,20 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s set->next = mDNSNULL; *p = set; - if (set->Advertise) - AdvertiseInterface(m, set); + if (set->Advertise) AdvertiseInterfaceIfNeeded(m, set); - LogInfo("mDNS_RegisterInterface: InterfaceID %d %s (%#a) %s", - (uint32_t)set->InterfaceID, set->ifname, &set->ip, - set->InterfaceActive ? - "not represented in list; marking active and retriggering queries" : - "already represented in list; marking inactive for now"); + if (set->InterfaceActive) + { + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_INFO, + "Interface not represented in list; marking active and retriggering queries - " + "ifid: %d, ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, IIDPrintable(set->InterfaceID), set->ifname, &set->ip); + } + else + { + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_INFO, + "Interface already represented in list - " + "ifid: %d, ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, IIDPrintable(set->InterfaceID), set->ifname, &set->ip); + } if (set->NetWake) mDNS_ActivateNetWake_internal(m, set); @@ -13091,15 +13093,21 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s case FastActivation: probedelay = (mDNSs32)0; numannounce = InitialAnnounceCount; - LogMsg("mDNS_RegisterInterface: Using fast activation for DirectLink interface %s (%#a)", set->ifname, &set->ip); + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_DEFAULT, + "Using fast activation for DirectLink interface - ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, + set->ifname, &set->ip); break; +#if MDNSRESPONDER_SUPPORTS(APPLE, SLOW_ACTIVATION) case SlowActivation: probedelay = mDNSPlatformOneSecond * 5; numannounce = (mDNSu8)1; - LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a), doing slow activation", set->ifname, &set->ip); + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_DEFAULT, + "Frequent transitions for interface, doing slow activation - " + "ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, set->ifname, &set->ip); m->mDNSStats.InterfaceUpFlap++; break; +#endif case NormalActivation: default: @@ -13108,7 +13116,9 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s break; } - LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_INFO, + "Interface probe will be delayed - ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR ", probe delay: %d", + set->ifname, &set->ip, probedelay); // No probe or sending suppression on DirectLink type interfaces. if (activationSpeed == FastActivation) @@ -13140,7 +13150,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s // us to reconnect to the network. If we do this as part of the wake up code, it is possible // that the network link comes UP after 60 seconds and we never set the OWNER option m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); - LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner"); + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_DEBUG, "Setting AnnounceOwner"); m->mDNSStats.InterfaceUp++; for (q = m->Questions; q; q=q->next) // Scan our list of questions @@ -13149,11 +13159,21 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, { // then reactivate this question +#if MDNSRESPONDER_SUPPORTS(APPLE, SLOW_ACTIVATION) // If flapping, delay between first and second queries is nine seconds instead of one second mDNSBool dodelay = (activationSpeed == SlowActivation) && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID); mDNSs32 initial = dodelay ? InitialQuestionInterval * QuestionIntervalStep2 : InitialQuestionInterval; mDNSs32 qdelay = dodelay ? kDefaultQueryDelayTimeForFlappingInterface : 0; - if (dodelay) LogInfo("No cache records expired for %##s (%s); delaying questions by %d seconds", q->qname.c, DNSTypeName(q->qtype), qdelay); + if (dodelay) + { + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_INFO, + "No cache records expired for the question " PRI_DM_NAME " (" PUB_S ");" + " delaying it by %d seconds", DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), qdelay); + } +#else + mDNSs32 initial = InitialQuestionInterval; + mDNSs32 qdelay = 0; +#endif if (!q->ThisQInterval || q->ThisQInterval > initial) { @@ -13162,8 +13182,6 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s } q->LastQTime = m->timenow - q->ThisQInterval + qdelay; q->RecentAnswerPkts = 0; - // Change the salt - ReInitAnonInfo(&q->AnonInfo, &q->qname); SetNextQueryTime(m,q); } } @@ -13175,14 +13193,9 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) { - // Change the salt - ReInitAnonInfo(&rr->resrec.AnonInfo, rr->resrec.name); mDNSCoreRestartRegistration(m, rr, numannounce); } } -#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE - DNSSECProbe(m); -#endif } RestartRecordGetZoneData(m); @@ -13193,22 +13206,58 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s return(mStatus_NoError); } +mDNSlocal void AdjustAddressRecordSetsEx(mDNS *const m, NetworkInterfaceInfo *removedIntf, mDNSBool forRandHostname) +{ + NetworkInterfaceInfo *intf; + const AuthRecord *oldAR; + AuthRecord *newAR; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (mDNSPlatformInterfaceIsAWDL(removedIntf->InterfaceID)) return; +#endif + oldAR = GetInterfaceAddressRecord(removedIntf, forRandHostname); + newAR = GetFirstAddressRecordEx(m, forRandHostname); + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + AuthRecord *ar; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + if (mDNSPlatformInterfaceIsAWDL(intf->InterfaceID)) continue; +#endif + ar = GetInterfaceAddressRecord(intf, forRandHostname); + if (ar->RRSet == oldAR) + { + ar->RRSet = newAR ? newAR : ar; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "Changed RRSet for " PRI_S, ARDisplayString(m, ar)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "New RRSet: " PRI_S, ARDisplayString(m, ar->RRSet)); + } + } +} +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) +#define AdjustAddressRecordSetsForRandHostname(M, REMOVED_INTF) AdjustAddressRecordSetsEx(M, REMOVED_INTF, mDNStrue) +#endif +#define AdjustAddressRecordSets(M, REMOVED_INTF) AdjustAddressRecordSetsEx(M, REMOVED_INTF, mDNSfalse) + // Note: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, InterfaceActivationSpeed activationSpeed) { +#if !MDNSRESPONDER_SUPPORTS(APPLE, SLOW_ACTIVATION) + (void)activationSpeed; // Unused parameter +#endif NetworkInterfaceInfo **p = &m->HostInterfaces; mDNSBool revalidate = mDNSfalse; - NetworkInterfaceInfo *primary; NetworkInterfaceInfo *intf; - AuthRecord *A; mDNS_Lock(m); // Find this record in our list while (*p && *p != set) p=&(*p)->next; - if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } + if (!*p) + { + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_DEBUG, "NetworkInterfaceInfo not found in list"); + mDNS_Unlock(m); + return; + } mDNS_DeactivateNetWake_internal(m, set); @@ -13228,10 +13277,15 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se intf = FirstInterfaceForID(m, set->InterfaceID); if (intf) { - LogInfo("mDNS_DeregisterInterface: Another representative of InterfaceID %d %s (%#a) exists;" - " making it active", (uint32_t)set->InterfaceID, set->ifname, &set->ip); + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_INFO, + "Another representative of InterfaceID exists - ifid: %d, ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, + IIDPrintable(set->InterfaceID), set->ifname, &set->ip); if (intf->InterfaceActive) - LogMsg("mDNS_DeregisterInterface: ERROR intf->InterfaceActive already set for %s (%#a)", set->ifname, &set->ip); + { + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_ERROR, + "intf->InterfaceActive already set for interface - ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, + set->ifname, &set->ip); + } intf->InterfaceActive = mDNStrue; UpdateInterfaceProtocols(m, intf); @@ -13250,27 +13304,41 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se CacheGroup *cg; CacheRecord *rr; DNSQuestion *q; - - LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %d %s (%#a) deregistered;" - " marking questions etc. dormant", (uint32_t)set->InterfaceID, set->ifname, &set->ip); +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + mDNSu32 cacheHitMulticastCount = 0; + mDNSu32 cacheMissMulticastCount = 0; + mDNSu32 cacheHitUnicastCount = 0; + mDNSu32 cacheMissUnicastCount = 0; +#endif + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_INFO, + "Last representative of InterfaceID deregistered; marking questions etc. dormant - " + "ifid: %d, ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, + IIDPrintable(set->InterfaceID), set->ifname, &set->ip); m->mDNSStats.InterfaceDown++; - + +#if MDNSRESPONDER_SUPPORTS(APPLE, SLOW_ACTIVATION) if (set->McastTxRx && (activationSpeed == SlowActivation)) { - LogMsg("mDNS_DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + LogRedact(MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_DEFAULT, + "Frequent transitions for interface - ifname: " PUB_S ", ifaddr: " PRI_IP_ADDR, + set->ifname, &set->ip); m->mDNSStats.InterfaceDownFlap++; } +#endif // 1. Deactivate any questions specific to this interface, and tag appropriate questions // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them for (q = m->Questions; q; q=q->next) { - if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0; - if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) + if (mDNSOpaque16IsZero(q->TargetQID)) // Only deactivate multicast quesstions. (Unicast questions are stopped when/if the associated DNS server group goes away.) { - q->FlappingInterface2 = q->FlappingInterface1; - q->FlappingInterface1 = set->InterfaceID; // Keep history of the last two interfaces to go away + if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0; + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) + { + q->FlappingInterface2 = q->FlappingInterface1; + q->FlappingInterface1 = set->InterfaceID; // Keep history of the last two interfaces to go away + } } } @@ -13280,6 +13348,7 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se { if (rr->resrec.InterfaceID == set->InterfaceID) { +#if MDNSRESPONDER_SUPPORTS(APPLE, SLOW_ACTIVATION) // If this interface is deemed flapping, // postpone deleting the cache records in case the interface comes back again if (set->McastTxRx && (activationSpeed == SlowActivation)) @@ -13292,26 +13361,48 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se rr->UnansweredQueries = MaxUnansweredQueries; } else +#endif { - rr->resrec.mortality = Mortality_Mortal; +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + if (rr->LastCachedAnswerTime) + { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (rr->resrec.dnsservice) cacheHitUnicastCount++; +#else + if (rr->resrec.rDNSServer) cacheHitUnicastCount++; +#endif + else cacheHitMulticastCount++; + } + else + { +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (rr->resrec.dnsservice) cacheMissUnicastCount++; +#else + if (rr->resrec.rDNSServer) cacheMissUnicastCount++; +#endif + else cacheMissMulticastCount++; + } +#endif mDNS_PurgeCacheResourceRecord(m, rr); } } } +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + dnssd_analytics_update_cache_usage_counts(cacheHitMulticastCount, cacheMissMulticastCount, cacheHitUnicastCount, cacheMissUnicastCount); +#endif } } // If we still have address records referring to this one, update them. // This is safe, because this NetworkInterfaceInfo has already been unlinked from the list, - // so the call to FindFirstAdvertisedInterface() won’t accidentally find it. - primary = FindFirstAdvertisedInterface(m); - A = primary ? &primary->RR_A : mDNSNULL; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->RR_A.RRSet == &set->RR_A) - intf->RR_A.RRSet = A; + // so the call to AdjustAddressRecordSets*() won’t accidentally find it. + AdjustAddressRecordSets(m, set); +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + AdjustAddressRecordSetsForRandHostname(m, set); +#endif // If we were advertising on this interface, deregister those address and reverse-lookup records now - if (set->Advertise) DeadvertiseInterface(m, set); + if (set->Advertise) DeadvertiseInterface(m, set, kDeadvertiseFlag_All); // If we have any cache records received on this interface that went away, then re-verify them. // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, @@ -13332,52 +13423,6 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se mDNS_Unlock(m); } -mDNSlocal void SetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes) -{ - int i, len; - - if (!sr->AnonData) - return; - - len = mDNSPlatformStrLen(sr->AnonData); - if (sr->RR_PTR.resrec.AnonInfo) - { - LogMsg("SetAnonInfoSRS: Freeing AnonInfo for PTR record %##s, should have been freed already", sr->RR_PTR.resrec.name->c); - FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo); - } - sr->RR_PTR.resrec.AnonInfo = AllocateAnonInfo(sr->RR_PTR.resrec.name, sr->AnonData, len, mDNSNULL); - for (i=0; i<NumSubTypes; i++) - { - if (sr->SubTypes[i].resrec.AnonInfo) - { - LogMsg("SetAnonInfoSRS: Freeing AnonInfo for subtype record %##s, should have been freed already", sr->SubTypes[i].resrec.name->c); - FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo); - } - sr->SubTypes[i].resrec.AnonInfo = AllocateAnonInfo(sr->SubTypes[i].resrec.name, sr->AnonData, len, mDNSNULL); - } -} - -mDNSlocal void ResetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes) -{ - int i; - - if (!sr->AnonData) - return; - if (sr->RR_PTR.resrec.AnonInfo) - { - FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo); - sr->RR_PTR.resrec.AnonInfo = mDNSNULL; - } - for (i=0; i<NumSubTypes; i++) - { - if (sr->SubTypes[i].resrec.AnonInfo) - { - FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo); - sr->SubTypes[i].resrec.AnonInfo = mDNSNULL; - } - } -} - mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) { ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; @@ -13423,7 +13468,6 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return; e = e->next; } - ResetAnonInfoSRS(sr, sr->NumSubTypes); // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, // then we can now report the NameConflict to the client @@ -13468,18 +13512,6 @@ mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) return artype; } -// Used to derive the original D2D specific flags specified by the client in the registration -// when we don't have access to the original flag (kDNSServiceFlags*) values. -mDNSexport mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType) -{ - mDNSu32 flags = 0; - if ((authRecType == AuthRecordAnyIncludeP2P) || (authRecType == AuthRecordAnyIncludeAWDLandP2P)) - flags |= kDNSServiceFlagsIncludeP2P; - else if ((authRecType == AuthRecordAnyIncludeAWDL) || (authRecType == AuthRecordAnyIncludeAWDLandP2P)) - flags |= kDNSServiceFlagsIncludeAWDL; - return flags; -} - // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) // Type is service type (e.g. "_ipp._tcp.") @@ -13497,7 +13529,6 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, { mStatus err; mDNSu32 i; - mDNSu32 hostTTL; AuthRecType artype; mDNSu8 recordType = (flags & kDNSServiceFlagsKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique; @@ -13522,12 +13553,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, sr->RR_PTR.AuthFlags = AuthFlagsWakeOnly; } - if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp")) - hostTTL = kHostNameSmallTTL; - else - hostTTL = kHostNameTTL; - - mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, recordType, artype, ServiceCallback, sr); mDNS_SetupResourceRecord(&sr->RR_TXT, txtrdata, InterfaceID, kDNSType_TXT, kStandardTTL, recordType, artype, ServiceCallback, sr); // If port number is zero, that means the client is really trying to do a RegisterNoSuchService @@ -13574,8 +13600,6 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, sr->SubTypes[i].Additional2 = &sr->RR_TXT; } - SetAnonInfoSRS(sr, NumSubTypes); - // 3. Set up the SRV record rdata. sr->RR_SRV.resrec.rdata->u.srv.priority = 0; sr->RR_SRV.resrec.rdata->u.srv.weight = 0; @@ -13737,6 +13761,9 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS debugf("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c); else debugf("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c); + // If there's a pending TXT record update at this point, which can happen if a DNSServiceUpdateRecord() call was made + // after the TXT record's deregistration, execute it now, otherwise it will be lost during the service re-registration. + if (sr->RR_TXT.NewRData) CompleteRDataUpdate(m, &sr->RR_TXT); err = mDNS_RegisterService(m, sr, newname, &type, &domain, host, sr->RR_SRV.resrec.rdata->u.srv.port, (sr->RR_TXT.resrec.rdata != &sr->RR_TXT.rdatastorage) ? sr->RR_TXT.resrec.rdata : mDNSNULL, @@ -13799,7 +13826,6 @@ mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *s // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); - mDNS_Deregister_internal(m, &sr->RR_ADV, drt); // We deregister all of the extra records, but we leave the sr->Extras list intact @@ -14066,10 +14092,10 @@ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, static const char msg3[] = "Creating Local NDP Cache entry "; static const char msg4[] = "Answering NDP Request from "; static const char msg5[] = "Answering NDP Probe from "; - const char *const msg = sha && mDNSSameEthAddress(sha, &rr->WakeUp.IMAC) ? msg1 : - (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : - sha && mDNSSameEthAddress(sha, &intf->MAC) ? msg3 : - spa && mDNSIPv6AddressIsZero(*spa) ? msg4 : msg5; + const char *const msg = mDNSSameEthAddress(sha, &rr->WakeUp.IMAC) ? msg1 : + (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : + mDNSSameEthAddress(sha, &intf->MAC) ? msg3 : + mDNSIPv6AddressIsZero(*spa) ? msg4 : msg5; LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s", intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); if (msg == msg1) @@ -14337,7 +14363,7 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4; const mDNSu8 * transEnd = p + 14 + mDNSVal16(pkt->v4.totlen); if (transEnd > end) transEnd = end; - debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst); + debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src.b, &pkt->v4.dst.b); src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src; dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst; if (transEnd >= trans + RequiredCapLen(pkt->v4.protocol)) @@ -14347,7 +14373,7 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6)) { const mDNSu8 *const trans = p + 54; - debugf("Got IPv6 %02X from %.16a to %.16a", pkt->v6.pro, &pkt->v6.src, &pkt->v6.dst); + debugf("Got IPv6 %02X from %.16a to %.16a", pkt->v6.pro, &pkt->v6.src.b, &pkt->v6.dst.b); src.type = mDNSAddrType_IPv6; src.ip.v6 = pkt->v6.src; dst.type = mDNSAddrType_IPv6; dst.ip.v6 = pkt->v6.dst; if (end >= trans + RequiredCapLen(pkt->v6.pro)) @@ -14484,7 +14510,6 @@ mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p, m->MainCallback = Callback; m->MainContext = Context; m->rec.r.resrec.RecordType = 0; - m->rec.r.resrec.AnonInfo = mDNSNULL; // For debugging: To catch and report locking failures m->mDNS_busy = 0; @@ -14514,12 +14539,11 @@ mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p, m->NextScheduledStopTime = timenow + FutureTime; m->NextBLEServiceTime = 0; // zero indicates inactive -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) m->NextBonjourDisableTime = 0; // Timer active when non zero. - m->BonjourEnabled = 0; // Set when Bonjour on Demand is enabled and Bonjour is currently enabled. -#endif // BONJOUR_ON_DEMAND + m->BonjourEnabled = 0; // Set when Bonjour on Demand is enabled and Bonjour is currently enabled. +#endif - m->DelayConflictProcessing = MAX_CONFLICT_PROCESSING_DELAYS; m->RandomQueryDelay = 0; m->RandomReconfirmDelay = 0; m->PktNum = 0; @@ -14545,7 +14569,6 @@ mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p, m->LocalOnlyQuestions = mDNSNULL; m->NewLocalOnlyQuestions = mDNSNULL; m->RestartQuestion = mDNSNULL; - m->ValidationQuestion = mDNSNULL; m->rrcache_size = 0; m->rrcache_totalused = 0; m->rrcache_active = 0; @@ -14568,6 +14591,9 @@ mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p, m->hostlabel.c[0] = 0; m->nicelabel.c[0] = 0; m->MulticastHostname.c[0] = 0; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + m->RandomizedHostname.c[0] = 0; +#endif m->HIHardware.c[0] = 0; m->HISoftware.c[0] = 0; m->ResourceRecords = mDNSNULL; @@ -14584,7 +14610,9 @@ mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p, m->NextuDNSEvent = timenow + FutureTime; m->NextSRVUpdate = timenow + FutureTime; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) m->DNSServers = mDNSNULL; +#endif m->Router = zeroAddr; m->AdvertisedV4 = zeroAddr; @@ -14596,14 +14624,13 @@ mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p, m->StaticHostname.c[0] = 0; m->FQDN.c[0] = 0; m->Hostnames = mDNSNULL; - m->AutoTunnelNAT.clientContext = mDNSNULL; m->WABBrowseQueriesCount = 0; m->WABLBrowseQueriesCount = 0; m->WABRegQueriesCount = 0; m->AutoTargetServices = 1; -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) m->NumAllInterfaceRecords = 0; m->NumAllInterfaceQuestions = 0; #endif @@ -14649,17 +14676,17 @@ mDNSlocal mStatus mDNS_InitStorage(mDNS *const m, mDNS_PlatformSupport *const p, m->DNSPushZones = mDNSNULL; #endif -#if APPLE_OSX_mDNSResponder - m->TunnelClients = mDNSNULL; - -#if !NO_WCF - CHECK_WCF_FUNCTION(WCFConnectionNew) +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) + if (WCFConnectionNew) { m->WCF = WCFConnectionNew(); if (!m->WCF) { LogMsg("WCFConnectionNew failed"); return -1; } } #endif - + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + result = init_and_load_trust_anchors(); + if (result != mStatus_NoError) return(result); #endif return(result); @@ -14673,6 +14700,10 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, if (result != mStatus_NoError) return(result); +#if MDNS_MALLOC_DEBUGGING + static mDNSListValidator lv; + mDNSPlatformAddListValidator(&lv, mDNS_ValidateLists, "mDNS_ValidateLists", m); +#endif result = mDNSPlatformInit(m); #ifndef UNICAST_DISABLED @@ -14717,7 +14748,8 @@ mDNSlocal void DynDNSHostNameCallback(mDNS *const m, AuthRecord *const rr, mStat mDNSPlatformDynDNSHostNameStatusChanged(rr->resrec.name, result); } -mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const DNSServer * const ptr, mDNSBool lameduck) +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr) { mDNSBool purge = cr->resrec.RecordType == kDNSRecordTypePacketNegative || cr->resrec.rrtype == kDNSType_A || @@ -14725,12 +14757,11 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const cr->resrec.rrtype == kDNSType_SRV || cr->resrec.rrtype == kDNSType_CNAME; - (void) lameduck; - (void) ptr; - debugf("PurgeOrReconfirmCacheRecord: %s cache record due to %s server %p %#a:%d (%##s): %s", + debugf("PurgeOrReconfirmCacheRecord: %s cache record due to server %#a:%d (%##s): %s", purge ? "purging" : "reconfirming", - lameduck ? "lame duck" : "new", - ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, CRDisplayString(m, cr)); + cr->resrec.rDNSServer ? &cr->resrec.rDNSServer->addr : mDNSNULL, + cr->resrec.rDNSServer ? mDNSVal16(cr->resrec.rDNSServer->port) : -1, + cr->resrec.rDNSServer ? cr->resrec.rDNSServer->domain.c : mDNSNULL, CRDisplayString(m, cr)); if (purge) { @@ -14743,86 +14774,23 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); } } +#endif mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q) { CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); CacheRecord *rp; - mDNSu8 validatingResponse = 0; - - // For DNSSEC questions, purge the corresponding RRSIGs also. - if (DNSSECQuestion(q)) - { - validatingResponse = q->ValidatingResponse; - q->ValidatingResponse = mDNStrue; - } for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) { - if (SameNameRecordAnswersQuestion(&rp->resrec, q)) + if (SameNameCacheRecordAnswersQuestion(rp, q)) { LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp)); mDNS_PurgeCacheResourceRecord(m, rp); } } - if (DNSSECQuestion(q)) - { - q->ValidatingResponse = validatingResponse; - } -} - -// For DNSSEC question, we need the DNSSEC records also. If the cache does not -// have the DNSSEC records, we need to re-issue the question with EDNS0/DO bit set. -// Just re-issuing the question for RRSIGs does not work in practice as the response -// may not contain the RRSIGs whose typeCovered field matches the question's qtype. -// -// For negative responses, we need the NSECs to prove the non-existence. If we don't -// have the cached NSECs, purge them. For positive responses, if we don't have the -// RRSIGs and if we have not already issued the question with EDNS0/DO bit set, purge -// them. -mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q) -{ - CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); - CacheRecord *rp; - - for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) - { - if (SameNameRecordAnswersQuestion(&rp->resrec, q)) - { - if (rp->resrec.RecordType != kDNSRecordTypePacketNegative || !rp->nsec) - { - if (!rp->CRDNSSECQuestion) - { - LogInfo("CheckForDNSSECRecords: Flushing %s", CRDisplayString(m, rp)); - mDNS_PurgeCacheResourceRecord(m, rp); - } - } - } - } -} - -// Check for a positive unicast response to the question but with qtype -mDNSexport mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype) -{ - DNSQuestion question; - CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); - CacheRecord *rp; - - // Create an identical question but with qtype - mDNS_SetupQuestion(&question, q->InterfaceID, &q->qname, qtype, mDNSNULL, mDNSNULL); - question.qDNSServer = q->qDNSServer; - - for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) - { - if (!rp->resrec.InterfaceID && rp->resrec.RecordType != kDNSRecordTypePacketNegative && - SameNameRecordAnswersQuestion(&rp->resrec, &question)) - { - LogInfo("mDNS_CheckForCacheRecord: Found %s", CRDisplayString(m, rp)); - return mDNStrue; - } - } - return mDNSfalse; } +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new) { DNSQuestion *qptr; @@ -14841,24 +14809,28 @@ mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSSer if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = new; } } } +#endif mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) { McastResolver *mr; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) DNSServer *ptr; +#endif if (delete) { +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) for (ptr = m->DNSServers; ptr; ptr = ptr->next) { ptr->penaltyTime = 0; - NumUnicastDNSServers--; - ptr->flags |= DNSServer_FlagDelete; -#if APPLE_OSX_mDNSResponder - if (ptr->flags & DNSServer_FlagUnreachable) + ptr->flags |= DNSServerFlag_Delete; +#if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) + if (ptr->flags & DNSServerFlag_Unreachable) NumUnreachableDNSServers--; #endif } +#endif // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at // mcast resolvers. Today we get both mcast and ucast configuration using the same // API @@ -14867,16 +14839,17 @@ mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) } else { +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) for (ptr = m->DNSServers; ptr; ptr = ptr->next) { ptr->penaltyTime = 0; - NumUnicastDNSServers++; - ptr->flags &= ~DNSServer_FlagDelete; -#if APPLE_OSX_mDNSResponder - if (ptr->flags & DNSServer_FlagUnreachable) + ptr->flags &= ~DNSServerFlag_Delete; +#if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) + if (ptr->flags & DNSServerFlag_Unreachable) NumUnreachableDNSServers++; #endif } +#endif for (mr = m->McastResolvers; mr; mr = mr->next) mr->flags &= ~McastResolver_FlagDelete; } @@ -14899,19 +14872,27 @@ mDNSlocal void SetDynDNSHostNameIfChanged(mDNS *const m, domainname *const fqdn) } } +// Even though this is called “Setup” it is not called just once at startup. +// It’s actually called multiple times, every time there’s a configuration change. mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) { +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) mDNSu32 slot; CacheGroup *cg; CacheRecord *cr; - mDNSBool Restart = mDNSfalse; +#endif mDNSAddr v4, v6, r; domainname fqdn; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) DNSServer *ptr, **p = &m->DNSServers; const DNSServer *oldServers = m->DNSServers; DNSQuestion *q; +#endif McastResolver *mr, **mres = &m->McastResolvers; - +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) && !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + DNSPushNotificationServer **psp; +#endif + debugf("uDNS_SetupDNSConfig: entry"); // Let the platform layer get the current DNS information and setup the WAB queries if needed. @@ -14956,6 +14937,9 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) } } +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + Querier_ProcessDNSServiceChanges(); +#else // Update our qDNSServer pointers before we go and free the DNSServer object memory // // All non-scoped resolvers share the same resGroupID. At no point in time a cache entry using DNSServer @@ -14983,101 +14967,118 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // cache records and as the resGroupID is different, you can't use the cache record from the scoped DNSServer to answer the // non-scoped question and vice versa. // -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) DNS64RestartQuestions(m); #endif - for (q = m->Questions; q; q=q->next) + + // First, restart questions whose suppression status will change. The suppression status of each question in a given + // question set, i.e., a non-duplicate question and all of its duplicates, if any, may or may not change. For example, + // a suppressed (or non-suppressed) question that is currently a duplicate of a suppressed (or non-suppressed) question + // may become a non-suppressed (or suppressed) question, while the question that it's a duplicate of may remain + // suppressed (or non-suppressed). + for (q = m->Questions; q; q = q->next) { - if (!mDNSOpaque16IsZero(q->TargetQID)) - { - DNSServer *s, *t; - DNSQuestion *qptr; - if (q->DuplicateOf) continue; - SetValidDNSServers(m, q); - q->triedAllServersOnce = 0; - s = GetServerForQuestion(m, q); - t = q->qDNSServer; - if (t != s) - { - mDNSBool old, new; - mDNSIPPort tport, sport; - - if (t) - tport = t->port; - else - tport = zeroIPPort; + DNSServer *s; + const DNSServer *t; + mDNSBool oldSuppressed; - if (s) - sport = s->port; - else - sport = zeroIPPort; - // If DNS Server for this question has changed, reactivate it - LogInfo("uDNS_SetupDNSConfig: Updating DNS Server from %#a:%d (%##s) to %#a:%d (%##s) for question %##s (%s) (scope:%p)", - t ? &t->addr : mDNSNULL, mDNSVal16(tport), t ? t->domain.c : (mDNSu8*)"", - s ? &s->addr : mDNSNULL, mDNSVal16(sport), s ? s->domain.c : (mDNSu8*)"", - q->qname.c, DNSTypeName(q->qtype), q->InterfaceID); - - old = q->SuppressQuery; - new = ShouldSuppressUnicastQuery(m, q, s); - if (old != new) - { - // Changing the DNS server affected the SuppressQuery status. We need to - // deliver RMVs for the previous ADDs (if any) before switching to the new - // DNSServer. To keep it simple, we walk all the questions and mark them - // to be restarted and then handle all of them at once. - q->Restart = 1; - q->SuppressQuery = new; - for (qptr = q->next ; qptr; qptr = qptr->next) - { - if (qptr->DuplicateOf == q) - qptr->Restart = 1; - } - Restart = mDNStrue; + if (mDNSOpaque16IsZero(q->TargetQID)) continue; + + SetValidDNSServers(m, q); + q->triedAllServersOnce = mDNSfalse; + s = GetServerForQuestion(m, q); + t = q->qDNSServer; + if (s != t) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_SetupDNSConfig: Updating DNS server from " PRI_IP_ADDR ":%d (" PRI_DM_NAME ") to " + PRI_IP_ADDR ":%d (" PRI_DM_NAME ") for question " PRI_DM_NAME " (" PUB_S ") (scope:%p)", + q->request_id, mDNSVal16(q->TargetQID), + t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), DM_NAME_PARAM(t ? &t->domain : mDNSNULL), + s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), DM_NAME_PARAM(s ? &s->domain : mDNSNULL), + DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), q->InterfaceID); +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) + // If this question had a DNS Push server associated with it, substitute the new server for the + // old one. If there is no new server, then we'll clean up the push server later. + if (!q->DuplicateOf && (q->dnsPushServer != mDNSNULL)) + { + if (q->dnsPushServer->qDNSServer == t) + { + q->dnsPushServer->qDNSServer = s; // which might be null } - else + // If it is null, do the accounting and drop the push server. + if (q->dnsPushServer->qDNSServer == mDNSNULL) { - DNSServerChangeForQuestion(m, q, s); - q->unansweredQueries = 0; - - // If we had sent a query out to DNSServer "t" and we are changing to "s", we - // need to ignore the responses coming back from "t" as the DNS configuration - // has changed e.g., when a new interface is coming up and that becomes the primary - // interface, we switch to the DNS servers configured for the primary interface. In - // this case, we should not accept responses associated with the previous interface as - // the "name" could resolve differently on this new primary interface. Hence, discard - // in-flight responses. - q->TargetQID = mDNS_NewMessageID(m); - - if (!QuerySuppressed(q)) - { - debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - ActivateUnicastQuery(m, q, mDNStrue); - // ActivateUnicastQuery is called for duplicate questions also as it does something - // special for AutoTunnel questions - for (qptr = q->next ; qptr; qptr = qptr->next) - { - if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue); - } - } + DNSPushReconcileConnection(m, q); } } - else +#endif + } + oldSuppressed = q->Suppressed; + q->Suppressed = ShouldSuppressUnicastQuery(q, s); + if (!q->Suppressed != !oldSuppressed) q->Restart = mDNStrue; + } + RestartUnicastQuestions(m); + + // Now, change the server for each question set, if necessary. Note that questions whose suppression status changed + // have already had their server changed by being restarted. + for (q = m->Questions; q; q = q->next) + { + DNSServer *s; + const DNSServer *t; + + if (mDNSOpaque16IsZero(q->TargetQID) || q->DuplicateOf) continue; + + SetValidDNSServers(m, q); + q->triedAllServersOnce = mDNSfalse; + s = GetServerForQuestion(m, q); + t = q->qDNSServer; + DNSServerChangeForQuestion(m, q, s); + if (s == t) continue; + + q->Suppressed = ShouldSuppressUnicastQuery(q, s); + q->unansweredQueries = 0; + q->TargetQID = mDNS_NewMessageID(m); + if (!q->Suppressed) ActivateUnicastQuery(m, q, mDNStrue); + } + +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) + // The above code may have found some DNS Push servers that are no longer valid. Now that we + // are done running through the code, we need to drop our connections to those servers. + // When we get here, any such servers should have zero questions associated with them. + for (psp = &m->DNSPushServers; *psp != mDNSNULL; ) + { + DNSPushNotificationServer *server = *psp; + + // It's possible that a push server whose DNS server has been deleted could be still connected but + // not referenced by any questions. In this case, we just delete the push server rather than trying + // to figure out with which DNS server (if any) to associate it. + if (server->qDNSServer != mDNSNULL && server->qDNSServer->flags & DNSServerFlag_Delete) + { + server->qDNSServer = mDNSNULL; + } + + if (server->qDNSServer == mDNSNULL) + { + // This would be a programming error, so should never happen. + if (server->numberOfQuestions != 0) { - mDNSIPPort zp = zeroIPPort; - debugf("uDNS_SetupDNSConfig: Not Updating DNS server question %p %##s (%s) DNS server %#a:%d %p %d", - q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zp), q->DuplicateOf, q->SuppressUnusable); - for (qptr = q->next ; qptr; qptr = qptr->next) - if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } + LogInfo("uDNS_SetupDNSConfig: deleting push server %##s that has questions.", &server->serverName); } + DNSPushServerDrop(server); + *psp = server->next; + mDNSPlatformMemFree(server); + } + else + { + psp = &(*psp)->next; } } - if (Restart) - RestartUnicastQuestions(m); +#endif FORALL_CACHERECORDS(slot, cg, cr) { - if (cr->resrec.InterfaceID) - continue; + if (cr->resrec.InterfaceID) continue; // We already walked the questions and restarted/reactivated them if the dns server // change affected the question. That should take care of updating the cache. But @@ -15090,111 +15091,84 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // the questions if they were suppressed (see above). To keep it simple, we walk // all the cache entries to make sure that there are no stale entries. We use the // active question's InterfaceID/ServiceID for looking up the right DNS server. - // Note that the unscoped value for ServiceID is -1. // // Note: If GetServerForName returns NULL, it could either mean that there are no // DNS servers or no matching DNS servers for this question. In either case, // the cache should get purged below when we process deleted DNS servers. - ptr = GetServerForName(m, cr->resrec.name, - (cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL), - (cr->CRActiveQuestion ? cr->CRActiveQuestion->ServiceID : -1)); - - // Purge or Reconfirm if this cache entry would use the new DNS server - if (ptr && (ptr != cr->resrec.rDNSServer)) + if (cr->CRActiveQuestion) { - // As the DNSServers for this cache record is not the same anymore, we don't - // want any new questions to pick this old value. If there is no active question, - // we can't possibly re-confirm, so purge in that case. If it is a DNSSEC question, - // purge the cache as the DNSSEC capabilities of the DNS server may have changed. - - if (cr->CRActiveQuestion == mDNSNULL || DNSSECQuestion(cr->CRActiveQuestion)) + // Purge or Reconfirm if this cache entry would use the new DNS server + ptr = GetServerForName(m, cr->resrec.name, cr->CRActiveQuestion->InterfaceID, cr->CRActiveQuestion->ServiceID); + if (ptr && (ptr != cr->resrec.rDNSServer)) { - LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s, New DNS server %#a , Old DNS server %#a", CRDisplayString(m, cr), - &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL)); - cr->resrec.mortality = Mortality_Mortal; - mDNS_PurgeCacheResourceRecord(m, cr); + LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s, New DNS server %#a, Old DNS server %#a", + CRDisplayString(m, cr), &ptr->addr, + cr->resrec.rDNSServer ? &cr->resrec.rDNSServer->addr : mDNSNULL); + PurgeOrReconfirmCacheRecord(m, cr); + + // If a cache record's DNSServer pointer is NULL, but its active question got a DNSServer in this DNS configuration + // update, then use its DNSServer. This way, the active question and its duplicates don't miss out on RMV events. + if (!cr->resrec.rDNSServer && cr->CRActiveQuestion->qDNSServer) + { + LogInfo("uDNS_SetupDNSConfig: Using active question's DNS server %#a for cache record %s", &cr->CRActiveQuestion->qDNSServer->addr, CRDisplayString(m, cr)); + cr->resrec.rDNSServer = cr->CRActiveQuestion->qDNSServer; + } } - else + + if (cr->resrec.rDNSServer && cr->resrec.rDNSServer->flags & DNSServerFlag_Delete) { - LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s, New DNS server %#a, Old DNS server %#a", CRDisplayString(m, cr), - &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL)); - PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); + DNSQuestion *qptr = cr->CRActiveQuestion; + if (qptr->qDNSServer == cr->resrec.rDNSServer) + { + LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) pointing to DNSServer Address %#a" + " to be freed", CRDisplayString(m, cr), + qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, + &cr->resrec.rDNSServer->addr); + qptr->validDNSServers = zeroOpaque128; + qptr->qDNSServer = mDNSNULL; + cr->resrec.rDNSServer = mDNSNULL; + } + else + { + LogInfo("uDNS_SetupDNSConfig: Cache Record %s, Active question %##s (%s) (scope:%p), pointing to DNSServer %#a (to be deleted)," + " resetting to question's DNSServer Address %#a", CRDisplayString(m, cr), + qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, + &cr->resrec.rDNSServer->addr, + qptr->qDNSServer ? &qptr->qDNSServer->addr : mDNSNULL); + cr->resrec.rDNSServer = qptr->qDNSServer; + } + PurgeOrReconfirmCacheRecord(m, cr); } } - - // If a cache record's DNSServer pointer is NULL, but its active question got a DNSServer in this DNS configuration - // update, then use its DNSServer. This way, the active question and its duplicates don't miss out on RMV events. - if (!cr->resrec.rDNSServer && cr->CRActiveQuestion && cr->CRActiveQuestion->qDNSServer) + else if (!cr->resrec.rDNSServer || cr->resrec.rDNSServer->flags & DNSServerFlag_Delete) { - cr->resrec.rDNSServer = cr->CRActiveQuestion->qDNSServer; - LogInfo("uDNS_SetupDNSConfig: Using active question's DNS server %#a for cache record %s", &cr->resrec.rDNSServer->addr, CRDisplayString(m, cr)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "uDNS_SetupDNSConfig: Purging Resourcerecord " PRI_S ", DNS server " PUB_S " " PRI_IP_ADDR " " PUB_S, + CRDisplayString(m, cr), !cr->resrec.rDNSServer ? "(to be deleted)" : "", + cr->resrec.rDNSServer ? &cr->resrec.rDNSServer->addr : mDNSNULL, + cr->resrec.rDNSServer ? DNSScopeToString(cr->resrec.rDNSServer->scopeType) : "" ); + cr->resrec.rDNSServer = mDNSNULL; + mDNS_PurgeCacheResourceRecord(m, cr); } } + // Delete all the DNS servers that are flagged for deletion while (*p) { - if (((*p)->flags & DNSServer_FlagDelete) != 0) + if (((*p)->flags & DNSServerFlag_Delete) != 0) { - // Scan our cache, looking for uDNS records that we would have queried this server for. - // We reconfirm any records that match, because in this world of split DNS, firewalls, etc. - // different DNS servers can give different answers to the same question. ptr = *p; - FORALL_CACHERECORDS(slot, cg, cr) - { - if (cr->resrec.InterfaceID) continue; - if (cr->resrec.rDNSServer == ptr) - { - // If we don't have an active question for this cache record, neither Purge can - // generate RMV events nor Reconfirm can send queries out. Just set the DNSServer - // pointer on the record NULL so that we don't point to freed memory (We might dereference - // DNSServer pointers from resource record for logging purposes). - // - // If there is an active question, point to its DNSServer as long as it does not point to the - // freed one. We already went through the questions above and made them point at either the - // new server or NULL if there is no server. - - if (cr->CRActiveQuestion) - { - DNSQuestion *qptr = cr->CRActiveQuestion; - - if (qptr->qDNSServer == ptr) - { - LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) pointing to DNSServer Address %#a" - " to be freed", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, &ptr->addr); - qptr->validDNSServers = zeroOpaque128; - qptr->qDNSServer = mDNSNULL; - cr->resrec.rDNSServer = mDNSNULL; - } - else - { - LogInfo("uDNS_SetupDNSConfig: Cache Record %s, Active question %##s (%s) (scope:%p), pointing to DNSServer %#a (to be deleted)," - " resetting to question's DNSServer Address %#a", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), - qptr->InterfaceID, &ptr->addr, (qptr->qDNSServer) ? &qptr->qDNSServer->addr : mDNSNULL); - cr->resrec.rDNSServer = qptr->qDNSServer; - } - } - else - { - LogInfo("uDNS_SetupDNSConfig: Cache Record %##s has no Active question, Record's DNSServer Address %#a, Server to be deleted %#a", - cr->resrec.name, &cr->resrec.rDNSServer->addr, &ptr->addr); - cr->resrec.rDNSServer = mDNSNULL; - } - - cr->resrec.mortality = Mortality_Mortal; - PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue); - } - } *p = (*p)->next; - LogInfo("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s) %d", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, NumUnicastDNSServers); + LogInfo("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c); mDNSPlatformMemFree(ptr); } else { - (*p)->flags &= ~DNSServer_FlagNew; p = &(*p)->next; } } + LogInfo("uDNS_SetupDNSConfig: CountOfUnicastDNSServers %d", CountOfUnicastDNSServers(m)); // If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs). // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless. @@ -15217,6 +15191,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // Force anything that needs to get zone data to get that information again RestartRecordGetZoneData(m); } +#endif // !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) SetDynDNSHostNameIfChanged(m, &fqdn); @@ -15239,7 +15214,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) if (m->FQDN.c[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); // Set status to 1 to indicate temporary failure } - debugf("uDNS_SetupDNSConfig: number of unicast DNS servers %d", NumUnicastDNSServers); + debugf("uDNS_SetupDNSConfig: number of unicast DNS servers %d", CountOfUnicastDNSServers(m)); return mStatus_NoError; } @@ -15285,19 +15260,21 @@ mDNSexport void mDNS_StartExit(mDNS *const m) mDNS_Lock(m); - LogInfo("mDNS_StartExit"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_StartExit"); m->ShutdownTime = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); mDNSCoreBeSleepProxyServer_internal(m, 0, 0, 0, 0, 0); -#if APPLE_OSX_mDNSResponder -#if !NO_WCF - CHECK_WCF_FUNCTION(WCFConnectionDealloc) +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) + if (WCFConnectionDealloc) { - if (m->WCF) WCFConnectionDealloc((WCFConnection *)m->WCF); + if (m->WCF) + { + WCFConnectionDealloc(m->WCF); + m->WCF = mDNSNULL; + } } #endif -#endif #ifndef UNICAST_DISABLED { @@ -15320,7 +15297,7 @@ mDNSexport void mDNS_StartExit(mDNS *const m) } #endif - DeadvertiseAllInterfaceRecords(m); + DeadvertiseAllInterfaceRecords(m, kDeadvertiseFlag_All); // Shut down all our active NAT Traversals while (m->NATTraversals) @@ -15344,15 +15321,18 @@ mDNSexport void mDNS_StartExit(mDNS *const m) // Make sure there are nothing but deregistering records remaining in the list if (m->CurrentRecord) - LogMsg("mDNS_StartExit: ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "mDNS_StartExit: ERROR m->CurrentRecord already set " PRI_S, ARDisplayString(m, m->CurrentRecord)); + } // We're in the process of shutting down, so queries, etc. are no longer available. // Consequently, determining certain information, e.g. the uDNS update server's IP // address, will not be possible. The records on the main list are more likely to // already contain such information, so we deregister the duplicate records first. - LogInfo("mDNS_StartExit: Deregistering duplicate resource records"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_StartExit: Deregistering duplicate resource records"); DeregLoop(m, m->DuplicateRecords); - LogInfo("mDNS_StartExit: Deregistering resource records"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_StartExit: Deregistering resource records"); DeregLoop(m, m->ResourceRecords); // If we scheduled a response to send goodbye packets, we set NextScheduledResponse to now. Normally when deregistering records, @@ -15363,18 +15343,28 @@ mDNSexport void mDNS_StartExit(mDNS *const m) m->SuppressSending = 0; } - if (m->ResourceRecords) LogInfo("mDNS_StartExit: Sending final record deregistrations"); - else LogInfo("mDNS_StartExit: No deregistering records remain"); + if (m->ResourceRecords) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_StartExit: Sending final record deregistrations"); + } + else + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_StartExit: No deregistering records remain"); + } for (rr = m->DuplicateRecords; rr; rr = rr->next) - LogMsg("mDNS_StartExit: Should not still have Duplicate Records remaining: %02X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "mDNS_StartExit: Should not still have Duplicate Records remaining: %02X " PRI_S, + rr->resrec.RecordType, ARDisplayString(m, rr)); + } // If any deregistering records remain, send their deregistration announcements before we exit if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m); mDNS_Unlock(m); - LogInfo("mDNS_StartExit: done"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_StartExit: done"); } mDNSexport void mDNS_FinalExit(mDNS *const m) @@ -15384,7 +15374,7 @@ mDNSexport void mDNS_FinalExit(mDNS *const m) mDNSu32 slot; AuthRecord *rr; - LogInfo("mDNS_FinalExit: mDNSPlatformClose"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_FinalExit: mDNSPlatformClose"); mDNSPlatformClose(m); for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) @@ -15410,7 +15400,11 @@ mDNSexport void mDNS_FinalExit(mDNS *const m) for (rr = m->ResourceRecords; rr; rr = rr->next) LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); - LogInfo("mDNS_FinalExit: done"); +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + uninit_trust_anchors(); +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "mDNS_FinalExit: done"); } #ifdef UNIT_TEST diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/mDNSDebug.h b/usr/src/contrib/mDNSResponder/mDNSCore/mDNSDebug.h index 03ed8107fb..e3e453f25a 100755 --- a/usr/src/contrib/mDNSResponder/mDNSCore/mDNSDebug.h +++ b/usr/src/contrib/mDNSResponder/mDNSCore/mDNSDebug.h @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2002-2018 Apple Inc. All rights reserved. +/* + * Copyright (c) 2002-2019 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +17,12 @@ #ifndef __mDNSDebug_h #define __mDNSDebug_h +#include "mDNSFeatures.h" + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) +#include <os/log.h> +#endif + // Set MDNS_DEBUGMSGS to 0 to optimize debugf() calls out of the compiled code // Set MDNS_DEBUGMSGS to 1 to generate normal debugging messages // Set MDNS_DEBUGMSGS to 2 to generate verbose debugging messages @@ -36,21 +41,61 @@ // warning: double format, pointer arg (arg 2) (for %.4a, %.16a, %#a -- IP address formats) #define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0 +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) +typedef os_log_t mDNSLogCategory_t; + +typedef os_log_type_t mDNSLogLevel_t; +#define MDNS_LOG_FAULT OS_LOG_TYPE_FAULT +#define MDNS_LOG_ERROR OS_LOG_TYPE_ERROR +#define MDNS_LOG_WARNING OS_LOG_TYPE_DEFAULT +#define MDNS_LOG_DEFAULT OS_LOG_TYPE_DEFAULT +#define MDNS_LOG_INFO OS_LOG_TYPE_DEFAULT +#define MDNS_LOG_DEBUG OS_LOG_TYPE_DEBUG +#else +typedef const char * mDNSLogCategory_t; typedef enum { - MDNS_LOG_MSG, - MDNS_LOG_OPERATION, - MDNS_LOG_SPS, - MDNS_LOG_INFO, - MDNS_LOG_DEBUG, + MDNS_LOG_FAULT = 1, + MDNS_LOG_ERROR = 2, + MDNS_LOG_WARNING = 3, + MDNS_LOG_DEFAULT = 4, + MDNS_LOG_INFO = 5, + MDNS_LOG_DEBUG = 6 } mDNSLogLevel_t; +#endif -// Set this symbol to 1 to answer remote queries for our Address, reverse mapping PTR, and HINFO records +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + extern os_log_t mDNSLogCategory_Default; + extern os_log_t mDNSLogCategory_mDNS; + extern os_log_t mDNSLogCategory_uDNS; + extern os_log_t mDNSLogCategory_SPS; + extern os_log_t mDNSLogCategory_XPC; + extern os_log_t mDNSLogCategory_Analytics; + extern os_log_t mDNSLogCategory_DNSSEC; + + #define MDNS_LOG_CATEGORY_DEFINITION(NAME) mDNSLogCategory_ ## NAME +#else + #define MDNS_LOG_CATEGORY_DEFINITION(NAME) # NAME +#endif + +#define MDNS_LOG_CATEGORY_DEFAULT MDNS_LOG_CATEGORY_DEFINITION(Default) +#define MDNS_LOG_CATEGORY_MDNS MDNS_LOG_CATEGORY_DEFINITION(mDNS) +#define MDNS_LOG_CATEGORY_UDNS MDNS_LOG_CATEGORY_DEFINITION(uDNS) +#define MDNS_LOG_CATEGORY_SPS MDNS_LOG_CATEGORY_DEFINITION(SPS) +#define MDNS_LOG_CATEGORY_XPC MDNS_LOG_CATEGORY_DEFINITION(XPC) +#define MDNS_LOG_CATEGORY_ANALYTICS MDNS_LOG_CATEGORY_DEFINITION(Analytics) +#define MDNS_LOG_CATEGORY_DNSSEC MDNS_LOG_CATEGORY_DEFINITION(DNSSEC) + +// Set this symbol to 1 to answer remote queries for our Address, and reverse mapping PTR #define ANSWER_REMOTE_HOSTNAME_QUERIES 0 // Set this symbol to 1 to do extra debug checks on malloc() and free() // Set this symbol to 2 to write a log message for every malloc() and free() -//#define MACOSX_MDNS_MALLOC_DEBUGGING 1 +// #define MDNS_MALLOC_DEBUGGING 1 + +#if (MDNS_MALLOC_DEBUGGING > 0) && defined(WIN32) +#error "Malloc debugging does not yet work on Windows" +#endif //#define ForceAlerts 1 //#define LogTimeStamps 1 @@ -94,21 +139,27 @@ extern "C" { #if (MDNS_HAS_VA_ARG_MACROS) #if (MDNS_C99_VA_ARGS) - #define debug_noop(... ) ((void)0) - #define LogMsg(... ) LogMsgWithLevel(MDNS_LOG_MSG, __VA_ARGS__) - #define LogOperation(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__);} while (0) - #define LogSPS(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__);} while (0) - #define LogInfo(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__);} while (0) - #define LogDebug(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_DEBUG, __VA_ARGS__);} while (0) + #define MDNS_LOG_DEFINITION(LEVEL, ...) \ + do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_CATEGORY_DEFAULT, LEVEL, __VA_ARGS__); } while (0) + + #define debug_noop(...) do {} while(0) + #define LogMsg(...) LogMsgWithLevel(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, __VA_ARGS__) + #define LogOperation(...) MDNS_LOG_DEFINITION(MDNS_LOG_INFO, __VA_ARGS__) + #define LogSPS(...) MDNS_LOG_DEFINITION(MDNS_LOG_INFO, __VA_ARGS__) + #define LogInfo(...) MDNS_LOG_DEFINITION(MDNS_LOG_INFO, __VA_ARGS__) + #define LogDebug(...) MDNS_LOG_DEFINITION(MDNS_LOG_DEBUG, __VA_ARGS__) #elif (MDNS_GNU_VA_ARGS) - #define debug_noop( ARGS... ) ((void)0) - #define LogMsg( ARGS... ) LogMsgWithLevel(MDNS_LOG_MSG, ARGS) - #define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS);} while (0) - #define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS);} while (0) - #define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS);} while (0) - #define LogDebug( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_DEBUG, ARGS);} while (0) + #define MDNS_LOG_DEFINITION(LEVEL, ARGS...) \ + do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_CATEGORY_DEFAULT, LEVEL, ARGS); } while (0) + + #define debug_noop(ARGS...) do {} while (0) + #define LogMsg(ARGS... ) LogMsgWithLevel(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, ARGS) + #define LogOperation(ARGS...) MDNS_LOG_DEFINITION(MDNS_LOG_INFO, ARGS) + #define LogSPS(ARGS...) MDNS_LOG_DEFINITION(MDNS_LOG_INFO, ARGS) + #define LogInfo(ARGS...) MDNS_LOG_DEFINITION(MDNS_LOG_INFO, ARGS) + #define LogDebug(ARGS...) MDNS_LOG_DEFINITION(MDNS_LOG_DEBUG, ARGS) #else - #error Unknown variadic macros + #error "Unknown variadic macros" #endif #else // If your platform does not support variadic macros, you need to define the following variadic functions. @@ -126,6 +177,7 @@ extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1, extern void LogDebug_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #endif + #if MDNS_DEBUGMSGS #define debugf debugf_ extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); @@ -147,7 +199,7 @@ extern int mDNS_McastTracingEnabled; extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog extern const char ProgramName[]; -extern void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3); +extern void LogMsgWithLevel(mDNSLogCategory_t category, mDNSLogLevel_t level, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); // LogMsgNoIdent needs to be fixed so that it logs without the ident prefix like it used to // (or completely overhauled to use the new "log to a separate file" facility) #define LogMsgNoIdent LogMsg @@ -158,19 +210,208 @@ extern void LogFatalError(const char *format, ...); #define LogFatalError LogMsg #endif -#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 -extern void *mallocL(char *msg, unsigned int size); -extern void freeL(char *msg, void *x); -extern void uds_validatelists(void); -extern void udns_validatelists(void *const v); +#if MDNS_MALLOC_DEBUGGING >= 1 +extern void *mallocL(const char *msg, mDNSu32 size); +extern void *callocL(const char *msg, mDNSu32 size); +extern void freeL(const char *msg, void *x); +#if APPLE_OSX_mDNSResponder extern void LogMemCorruption(const char *format, ...); #else -#define mallocL(X,Y) malloc(Y) -#define freeL(X,Y) free(Y) +#define LogMemCorruption LogMsg +#endif +#else +#define mallocL(MSG, SIZE) malloc(SIZE) +#define callocL(MSG, SIZE) calloc(1, SIZE) +#define freeL(MSG, PTR) free(PTR) #endif #ifdef __cplusplus } #endif +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) +/** @brief Write a log message to system's log storage(memory or disk). + * + * On Apple platform, os_log() will be called to log a message. + * + * @param CATEGORY A custom log object previously created by the os_log_create function, and such an object is + * used to specify "subsystem" and "category". For mDNSResponder, the subsystem should always + * be set to "com.apple.mDNSResponder"; and the category is used for categorization and + * filtering of related log messages within the subsystem’s settings. We have 4 categories that + * are pre-defined: MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_CATEGORY_MDNS, MDNS_LOG_CATEGORY_UDNS, + * MDNS_LOG_CATEGORY_SPS. If these categories are not enough, use os_log_create to create more. + * + * @param LEVEL The log level that determines the importance of the message. The levels are, in order of + * decreasing importance: + * MDNS_LOG_FAULT Fault-level messages are intended for capturing system-level errors + * that are critical to the system. They are always saved in the data store. + * MDNS_LOG_ERROR Error-level messages are intended for reporting process-level errors + * that are unexpected and incorrect during the normal operation. They + * are always saved in the data store. + * MDNS_LOG_WARNING Warning-level messages are intended for capturing unexpected and + * possible incorrect behavior that might be used later to root cause + * an error or fault. They are are initially stored in memory buffers + * and then moved to a data store. + * MDNS_LOG_DEFAULT Default-level messages are intended for reporting things that might + * result a failure. They are are initially stored in memory buffers + * and then moved to a data store. + * MDNS_LOG_INFO Info-level messages are intended for capturing information that may + * be helpful, but isn’t essential, for troubleshooting errors. They + * are initially stored in memory buffers, but will only be moved into + * data store when faults and, optionally, errors occur. + * MDNS_LOG_DEBUG Debug-level messages are intended for information that may be useful + * during development or while troubleshooting a specific problem, Debug + * logging should not be used in shipping software. They are only + * captured in memory when debug logging is enabled through a + * configuration change. + * + * @param FORMAT A constant string or format string that produces a human-readable log message. The format + * string follows the IEEE printf specification, besides the following customized format specifiers: + * %{mdnsresponder:domain_name}.*P the pointer to a DNS lable sequence + * %{mdnsresponder:ip_addr}.20P the pointer to a mDNSAddr variable + * %{network:in_addr}.4P the pointer to a mDNSv4Addr variable + * %{network:in6_addr}.16P the pointer to a mDNSv6Addr variable + * %{mdnsresponder:mac_addr}.6P the pointer to a 6-byte-length MAC address + * + * @param ... The parameter list that will be formated by the format string. Note that if the customized + * format specifiers are used and the data length is not specified in the format string, the + * size should be listed before the pointer to the data, for example: + * "%{mdnsresponder:domain_name}.*P", (name ? (int)DomainNameLength((const domainname *)name) : 0), <the pointer to a DNS label sequence> + * + */ + #define LogRedact(CATEGORY, LEVEL, FORMAT, ...) os_log_with_type(CATEGORY, LEVEL, FORMAT, ## __VA_ARGS__) +#else + #if (MDNS_HAS_VA_ARG_MACROS) + #if (MDNS_C99_VA_ARGS) + #define LogRedact(CATEGORY, LEVEL, ...) \ + do { if (mDNS_LoggingEnabled) LogMsgWithLevel(CATEGORY, LEVEL, __VA_ARGS__); } while (0) + #elif (MDNS_GNU_VA_ARGS) + #define LogRedact(CATEGORY, LEVEL, ARGS...) \ + do { if (mDNS_LoggingEnabled) LogMsgWithLevel(CATEGORY, LEVEL, ARGS); } while (0) + #else + #error "Unknown variadic macros" + #endif + #else + #define LogRedact (mDNS_LoggingEnabled == 0) ? ((void)0) : LogRedact_ + extern void LogRedact_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); + #endif +#endif // MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + +// The followings are the customized log specifier defined in os_log. For compatibility, we have to define it when it is +// not on the Apple platform, for example, the Posix platform. The keyword "public" or "private" is used to control whether +// the content would be redacted when the redaction is turned on: "public" means the content will always be printed; +// "private" means the content will be printed as <mask.hash: '<The hashed string from binary data>'> if the redaction is turned on, +// only when the redaction is turned off, the content will be printed as what it should be. Note that the hash performed +// to the data is a salted hashing transformation, and the salt is generated randomly on a per-process basis, meaning +// that hashes cannot be correlated across processes or devices. + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_S "%{public}s" + #define PRI_S "%{private, mask.hash}s" +#else + #define PUB_S "%s" + #define PRI_S PUB_S +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_DM_NAME "%{public, mdnsresponder:domain_name}.*P" + #define PRI_DM_NAME "%{private, mask.hash, mdnsresponder:domain_name}.*P" + // When DM_NAME_PARAM is used, the file where the function is defined must include DNSEmbeddedAPI.h + #define DM_NAME_PARAM(name) ((name) ? ((int)DomainNameLength((name))) : 0), (name) +#else + #define PUB_DM_NAME "%##s" + #define PRI_DM_NAME PUB_DM_NAME + #define DM_NAME_PARAM(name) (name) #endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_IP_ADDR "%{public, mdnsresponder:ip_addr}.20P" + #define PRI_IP_ADDR "%{private, mask.hash, mdnsresponder:ip_addr}.20P" + + #define PUB_IPv4_ADDR "%{public, network:in_addr}.4P" + #define PRI_IPv4_ADDR "%{private, mask.hash, network:in_addr}.4P" + + #define PUB_IPv6_ADDR "%{public, network:in6_addr}.16P" + #define PRI_IPv6_ADDR "%{private, mask.hash, network:in6_addr}.16P" +#else + #define PUB_IP_ADDR "%#a" + #define PRI_IP_ADDR PUB_IP_ADDR + + #define PUB_IPv4_ADDR "%.4a" + #define PRI_IPv4_ADDR PUB_IPv4_ADDR + + #define PUB_IPv6_ADDR "%.16a" + #define PRI_IPv6_ADDR PUB_IPv6_ADDR +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_MAC_ADDR "%{public, mdnsresponder:mac_addr}.6P" + #define PRI_MAC_ADDR "%{private, mask.hash, mdnsresponder:mac_addr}.6P" +#else + #define PUB_MAC_ADDR "%.6a" + #define PRI_MAC_ADDR PUB_MAC_ADDR +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_DNSKEY "%{public, mdns:rd.dnskey}.*P" + #define PRI_DNSKEY "%{private, mask.hash, mdns:rd.dnskey}.*P" + #define DNSKEY_PARAM(rdata, rdata_length) (rdata_length), (rdata) +#else + #define PUB_DNSKEY "%p" + #define PRI_DNSKEY PUB_DNSKEY + #define DNSKEY_PARAM(rdata, rdata_length) (rdata) +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_DS "%{public, mdns:rd.ds}.*P" + #define PRI_DS "%{private, mask.hash, mdns:rd.ds}.*P" + #define DS_PARAM(rdata, rdata_length) (rdata_length), (rdata) +#else + #define PUB_DS "%p" + #define PRI_DS PUB_DS + #define DS_PARAM(rdata, rdata_length) (rdata) +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_NSEC "%{public, mdns:rd.nsec}.*P" + #define PRI_NSEC "%{private, mask.hash, mdns:rd.nsec}.*P" + #define NSEC_PARAM(rdata, rdata_length) (rdata_length), (rdata) +#else + #define PUB_NSEC "%p" + #define PRI_NSEC PUB_NSEC + #define NSEC_PARAM(rdata, rdata_length) (rdata) +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_NSEC3 "%{public, mdns:rd.nsec3}.*P" + #define PRI_NSEC3 "%{private, mask.hash, mdns:rd.nsec3}.*P" + #define NSEC3_PARAM(rdata, rdata_length) (rdata_length), (rdata) +#else + #define PUB_NSEC3 "%p" + #define PRI_NSEC3 PUB_NSEC3 + #define NSEC3_PARAM(rdata, rdata_length) (rdata) +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_RRSIG "%{public, mdns:rd.rrsig}.*P" + #define PRI_RRSIG "%{private, mask.hash, mdns:rd.rrsig}.*P" + #define RRSIG_PARAM(rdata, rdata_length) (rdata_length), (rdata) +#else + #define PUB_RRSIG "%p" + #define PRI_RRSIG PUB_RRSIG + #define RRSIG_PARAM(rdata, rdata_length) (rdata) +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + #define PUB_SVCB "%{public, mdns:rd.svcb}.*P" + #define PRI_SVCB "%{private, mask.hash, mdns:rd.svcb}.*P" + #define SVCB_PARAM(rdata, rdata_length) (rdata_length), (rdata) +#else + #define PUB_SVCB "%p" + #define PRI_SVCB PUB_SVCB + #define SVCB_PARAM(rdata, rdata_length) (rdata) +#endif + +extern void LogToFD(int fd, const char *format, ...); + +#endif // __mDNSDebug_h diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h b/usr/src/contrib/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h index 772664fe02..321a926f20 100755 --- a/usr/src/contrib/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h +++ b/usr/src/contrib/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2002-2018 Apple Inc. All rights reserved. +/* + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,15 +66,12 @@ #include <stdarg.h> // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration #endif -#include "mDNSDebug.h" #if APPLE_OSX_mDNSResponder #include <uuid/uuid.h> -#include <TargetConditionals.h> #endif -#ifdef __cplusplus -extern "C" { -#endif +#include "mDNSFeatures.h" +#include "mDNSDebug.h" // *************************************************************************** // Feature removal compile options & limited resource targets @@ -84,13 +80,19 @@ extern "C" { // memory footprint for use in embedded systems with limited resources. // UNICAST_DISABLED - disables unicast DNS functionality, including Wide Area Bonjour -// ANONYMOUS_DISABLED - disables anonymous functionality -// DNSSEC_DISABLED - disables DNSSEC functionality // SPC_DISABLED - disables Bonjour Sleep Proxy client // IDLESLEEPCONTROL_DISABLED - disables sleep control for Bonjour Sleep Proxy clients // In order to disable the above features pass the option to your compiler, e.g. -D UNICAST_DISABLED +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) +#include <WebFilterDNS/WebFilterDNS.h> +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#include "dnssec_v2_embedded.h" +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + // Additionally, the LIMITED_RESOURCES_TARGET compile option will reduce the maximum DNS message sizes. #ifdef LIMITED_RESOURCES_TARGET @@ -101,8 +103,12 @@ extern "C" { #define MaximumRDSize 264 #endif -#if !defined(MDNSRESPONDER_BTMM_SUPPORT) -#define MDNSRESPONDER_BTMM_SUPPORT 0 +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +#include "mdns_private.h" +#endif + +#ifdef __cplusplus +extern "C" { #endif // *************************************************************************** @@ -150,6 +156,20 @@ extern "C" { #endif #endif +#ifndef fallthrough + #if __clang__ + #if __has_c_attribute(fallthrough) + #define fallthrough() [[fallthrough]] + #else + #define fallthrough() + #endif + #elif __GNUC__ + #define fallthrough() __attribute__((fallthrough)) + #else + #define fallthrough() + #endif // __GNUC__ +#endif // fallthrough + // *************************************************************************** #if 0 #pragma mark - DNS Resource Record class and type constants @@ -226,6 +246,9 @@ typedef enum // From RFC 1035 kDNSType_HIP = 55, // 55 Host Identity Protocol + kDNSType_SVCB = 64, // 64 Service Binding + kDNSType_HTTPS, // 65 HTTPS Service Binding + kDNSType_SPF = 99, // 99 Sender Policy Framework for E-Mail kDNSType_UINFO, // 100 IANA-Reserved kDNSType_UID, // 101 IANA-Reserved @@ -238,7 +261,7 @@ typedef enum // From RFC 1035 kDNSType_AXFR, // 252 Transfer zone of authority kDNSType_MAILB, // 253 Transfer mailbox records kDNSType_MAILA, // 254 Transfer mail agent records - kDNSQType_ANY // Not a DNS type, but a DNS query type, meaning "all types" + kDNSQType_ANY // Not a DNS type, but a DNS query type, meaning "all types" } DNS_TypeValues; // *************************************************************************** @@ -269,7 +292,12 @@ typedef unsigned int mDNSu32; // To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct // This way, mDNSInterfaceIDs can be assigned, and compared with each other, but not with other types // Declaring the type to be the typical generic "void *" would lack this type checking -typedef struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID; +typedef const struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID; + +// Use when printing interface IDs; the interface ID is actually a pointer, but we're only using +// the pointer as a unique identifier, and in special cases it's actually a small number. So there's +// little point in printing all 64 bits--the upper 32 bits in particular will not add information. +#define IIDPrintable(x) ((uint32_t)(uintptr_t)(x)) // These types are for opaque two- and four-byte identifiers. // The "NotAnInteger" fields of the unions allow the value to be conveniently passed around in a @@ -377,8 +405,12 @@ enum mStatus_NoRouter = -65566, mStatus_PollingMode = -65567, mStatus_Timeout = -65568, - mStatus_HostUnreachErr = -65569, - // -65570 to -65786 currently unused; available for allocation + mStatus_DefunctConnection = -65569, + mStatus_PolicyDenied = -65570, + // -65571 to -65785 currently unused; available for allocation + + // udp connection status + mStatus_HostUnreachErr = -65786, // tcp connection status mStatus_ConnPending = -65787, @@ -389,10 +421,12 @@ enum mStatus_GrowCache = -65790, mStatus_ConfigChanged = -65791, mStatus_MemFree = -65792 // Last value: 0xFFFE FF00 - // mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS + + // mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS }; typedef mDNSs32 mStatus; + #define MaxIp 5 // Needs to be consistent with MaxInputIf in dns_services.h typedef enum { q_stop = 0, q_start } q_state; @@ -440,13 +474,6 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #define kStandardTTL (3600UL * 100 / 80) #define kHostNameTTL 120UL -// Some applications want to register their SRV records with a lower ttl so that in case the server -// using a dynamic port number restarts, the clients will not have stale information for more than -// 10 seconds - -#define kHostNameSmallTTL 10UL - - // Multicast DNS uses announcements (gratuitous responses) to update peer caches. // This means it is feasible to use relatively larger TTL values than we might otherwise // use, because we have a cache coherency protocol to keep the peer caches up to date. @@ -482,6 +509,7 @@ typedef struct ResourceRecord_struct ResourceRecord; // Structure to abstract away the differences between TCP/SSL sockets, and one for UDP sockets // The actual definition of these structures appear in the appropriate platform support code +typedef struct TCPListener_struct TCPListener; typedef struct TCPSocket_struct TCPSocket; typedef struct UDPSocket_struct UDPSocket; @@ -781,133 +809,6 @@ typedef packedstruct mDNSu32 min; // Nominally the minimum record TTL for this zone, in seconds; also used for negative caching. } rdataSOA; -// http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml -// Algorithm used for RRSIG, DS and DNS KEY -#define CRYPTO_RSA_SHA1 0x05 -#define CRYPTO_DSA_NSEC3_SHA1 0x06 -#define CRYPTO_RSA_NSEC3_SHA1 0x07 -#define CRYPTO_RSA_SHA256 0x08 -#define CRYPTO_RSA_SHA512 0x0A - -#define CRYPTO_ALG_MAX 0x0B - -// alg - same as in RRSIG, DNS KEY or DS. -// RFC 4034 defines SHA1 -// RFC 4509 defines SHA256 -// Note: NSEC3 also uses 1 for SHA1 and hence we will reuse for now till a new -// value is assigned. -// -#define SHA1_DIGEST_TYPE 1 -#define SHA256_DIGEST_TYPE 2 -#define DIGEST_TYPE_MAX 3 - -// We need support for base64 and base32 encoding for displaying KEY, NSEC3 -// To make this platform agnostic, we define two types which the platform -// needs to support -#define ENC_BASE32 1 -#define ENC_BASE64 2 -#define ENC_ALG_MAX 3 - -#define DS_FIXED_SIZE 4 -typedef packedstruct -{ - mDNSu16 keyTag; - mDNSu8 alg; - mDNSu8 digestType; - mDNSu8 *digest; -} rdataDS; - -typedef struct TrustAnchor -{ - struct TrustAnchor *next; - int digestLen; - mDNSu32 validFrom; - mDNSu32 validUntil; - domainname zone; - rdataDS rds; -} TrustAnchor; - -//size of rdataRRSIG excluding signerName and signature (which are variable fields) -#define RRSIG_FIXED_SIZE 18 -typedef struct -{ - mDNSu16 typeCovered; - mDNSu8 alg; - mDNSu8 labels; - mDNSu32 origTTL; - mDNSu32 sigExpireTime; - mDNSu32 sigInceptTime; - mDNSu16 keyTag; - mDNSu8 signerName[1]; // signerName is a dynamically-sized array - // mDNSu8 *signature -} rdataRRSig; - -// RFC 4034: For DNS Key RR -// flags - the valid value for DNSSEC is 256 (Zone signing key - ZSK) and 257 (Secure Entry Point) which also -// includes the ZSK bit -// -#define DNSKEY_ZONE_SIGN_KEY 0x100 -#define DNSKEY_SECURE_ENTRY_POINT 0x101 - -// proto - the only valid value for protocol is 3 (See RFC 4034) -#define DNSKEY_VALID_PROTO_VALUE 0x003 - -// alg - The only mandatory algorithm that we support is RSA/SHA-1 -// DNSSEC_RSA_SHA1_ALG - -#define DNSKEY_FIXED_SIZE 4 -typedef packedstruct -{ - mDNSu16 flags; - mDNSu8 proto; - mDNSu8 alg; - mDNSu8 *data; -} rdataDNSKey; - -#define NSEC3_FIXED_SIZE 5 -#define NSEC3_FLAGS_OPTOUT 1 -#define NSEC3_MAX_ITERATIONS 2500 -typedef packedstruct -{ - mDNSu8 alg; - mDNSu8 flags; - mDNSu16 iterations; - mDNSu8 saltLength; - mDNSu8 *salt; - // hashLength, nxt, bitmap -} rdataNSEC3; - -// In the multicast usage of NSEC3, we know the actual size of RData -// 4 bytes : HashAlg, Flags,Iterations -// 5 bytes : Salt Length 1 byte, Salt 4 bytes -// 21 bytes : HashLength 1 byte, Hash 20 bytes -// 34 bytes : Window number, Bitmap length, Type bit map to include the first 256 types -#define MCAST_NSEC3_RDLENGTH (4 + 5 + 21 + 34) -#define SHA1_HASH_LENGTH 20 - -// Base32 encoding takes 5 bytes of the input and encodes as 8 bytes of output. -// For example, SHA-1 hash of 20 bytes will be encoded as 20/5 * 8 = 32 base32 -// bytes. For a max domain name size of 255 bytes of base32 encoding : (255/8)*5 -// is the max hash length possible. -#define NSEC3_MAX_HASH_LEN 155 -// In NSEC3, the names are hashed and stored in the first label and hence cannot exceed label -// size. -#define NSEC3_MAX_B32_LEN MAX_DOMAIN_LABEL - -// We define it here instead of dnssec.h so that these values can be used -// in files without bringing in all of dnssec.h unnecessarily. -typedef enum -{ - DNSSEC_Secure = 1, // Securely validated and has a chain up to the trust anchor - DNSSEC_Insecure, // Cannot build a chain up to the trust anchor - DNSSEC_Indeterminate, // Not used currently - DNSSEC_Bogus, // failed to validate signatures - DNSSEC_NoResponse // No DNSSEC records to start with -} DNSSECStatus; - -#define DNSSECRecordType(rrtype) (((rrtype) == kDNSType_RRSIG) || ((rrtype) == kDNSType_NSEC) || ((rrtype) == kDNSType_DNSKEY) || ((rrtype) == kDNSType_DS) || \ - ((rrtype) == kDNSType_NSEC3)) - typedef enum { platform_OSX = 1, // OSX Platform @@ -1056,9 +957,6 @@ typedef union mDNSv6Addr ipv6; // For 'AAAA' record rdataSRV srv; rdataOPT opt[2]; // For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together - rdataDS ds; - rdataDNSKey key; - rdataRRSig rrsig; } RDataBody2; typedef struct @@ -1321,15 +1219,6 @@ struct NATTraversalInfo_struct enum { - DNSServer_FlagDelete = 0x1, - DNSServer_FlagNew = 0x2, -#if APPLE_OSX_mDNSResponder - DNSServer_FlagUnreachable = 0x4, -#endif -}; - -enum -{ McastResolver_FlagDelete = 1, McastResolver_FlagNew = 2 }; @@ -1350,58 +1239,51 @@ enum { }; typedef mDNSu8 MortalityState; -// scoped values for DNSServer matching -enum +// ScopeType values for DNSServer matching +typedef enum { kScopeNone = 0, // DNS server used by unscoped questions kScopeInterfaceID = 1, // Scoped DNS server used only by scoped questions - kScopeServiceID = 2, // Service specific DNS server used only by questions + kScopeServiceID = 2 // Service specific DNS server used only by questions // have a matching serviceID - kScopesMaxCount = 3 // Max count for scopes enum -}; +} ScopeType; + +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +typedef mDNSu32 DNSServerFlags; +#define DNSServerFlag_Delete (1U << 0) +#if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) +#define DNSServerFlag_Unreachable (1U << 1) +#endif -// Note: DNSSECAware is set if we are able to get a valid response to -// a DNSSEC question. In some cases it is possible that the proxy -// strips the EDNS0 option and we just get a plain response with no -// signatures. But we still mark DNSSECAware in that case. As DNSSECAware -// is only used to determine whether DNSSEC_VALIDATION_SECURE_OPTIONAL -// should be turned off or not, it is sufficient that we are getting -// responses back. typedef struct DNSServer { struct DNSServer *next; mDNSInterfaceID interface; // DNS requests should be sent on this interface - mDNSs32 serviceID; - mDNSAddr addr; - mDNSIPPort port; - mDNSu32 flags; // Set when we're planning to delete this from the list - domainname domain; // name->server matching for "split dns" + mDNSs32 serviceID; // ServiceID from DNS configuration. + mDNSAddr addr; // DNS server's IP address. + DNSServerFlags flags; // Set when we're planning to delete this from the list. mDNSs32 penaltyTime; // amount of time this server is penalized - mDNSu32 scoped; // See the scoped enum above + ScopeType scopeType; // See the ScopeType enum above mDNSu32 timeout; // timeout value for questions - mDNSu16 resGroupID; // ID of the resolver group that contains this DNSServer - mDNSu8 retransDO; // Total Retransmissions for queries sent with DO option - mDNSBool cellIntf; // Resolver from Cellular Interface? - mDNSBool req_A; // If set, send v4 query (DNSConfig allows A queries) - mDNSBool req_AAAA; // If set, send v6 query (DNSConfig allows AAAA queries) - mDNSBool req_DO; // If set, okay to send DNSSEC queries (EDNS DO bit is supported) - mDNSBool DNSSECAware; // Set if we are able to receive a response to a request sent with DO option. + mDNSu32 resGroupID; // ID of the resolver group that contains this DNSServer + mDNSIPPort port; // DNS server's port number. + mDNSBool usableA; // True if A query results are usable over the interface, i.e., interface has IPv4. + mDNSBool usableAAAA; // True if AAAA query results are usable over the interface, i.e., interface has IPv6. + mDNSBool isCell; // True if the interface to this server is cellular. mDNSBool isExpensive; // True if the interface to this server is expensive. - mDNSBool isCLAT46; // True if the interface to this server is CLAT46. + mDNSBool isConstrained; // True if the interface to this server is constrained. + mDNSBool isCLAT46; // True if the interface to this server supports CLAT46. + domainname domain; // name->server matching for "split dns" } DNSServer; +#endif -typedef struct -{ - mDNSu8 *AnonData; - int AnonDataLen; - mDNSu32 salt; - ResourceRecord *nsec3RR; - mDNSInterfaceID SendNow; // The interface ID that this record should be sent on -} AnonymousInfo; +#define kNegativeRecordType_Unspecified 0 // Initializer of ResourceRecord didn't specify why the record is negative. +#define kNegativeRecordType_NoData 1 // The record's name exists, but there are no records of this type. struct ResourceRecord_struct { mDNSu8 RecordType; // See kDNSRecordTypes enum. + mDNSu8 negativeRecordType; // If RecordType is kDNSRecordTypePacketNegative, specifies type of negative record. MortalityState mortality; // Mortality of this resource record (See MortalityState enum) mDNSu16 rrtype; // See DNS_TypeValues enum. mDNSu16 rrclass; // See DNS_ClassValues enum. @@ -1423,8 +1305,21 @@ struct ResourceRecord_struct // that are interface-specific (e.g. address records, especially linklocal addresses) const domainname *name; RData *rdata; // Pointer to storage for this rdata +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_dns_service_t dnsservice; + mdns_resolver_type_t protocol; +#else DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry; null for multicast - AnonymousInfo *AnonInfo; // Anonymous Information +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + dnssec_result_t dnssec_result; // DNSSEC validation result of the current resource record. + // For all DNSSEC-disabled queries, the result would always be dnssec_indeterminate. + // For DNSSEC-enabled queries, the result would be dnssec_indeterminate, + // dnssec_secure, dnssec_insecure, or dnssec_bogus, see + // <https://tools.ietf.org/html/rfc4033#section-5> for the detailed meaning of + // each state. +#endif }; @@ -1495,9 +1390,12 @@ typedef enum AuthRecordAnyIncludeAWDL, // registered for *Any, including AWDL interface AuthRecordAnyIncludeAWDLandP2P, // registered for *Any, including AWDL and P2P interfaces AuthRecordLocalOnly, - AuthRecordP2P // discovered over D2D/P2P framework + AuthRecordP2P, // discovered over D2D/P2P framework } AuthRecType; +#define AuthRecordIncludesAWDL(AR) \ + (((AR)->ARType == AuthRecordAnyIncludeAWDL) || ((AR)->ARType == AuthRecordAnyIncludeAWDLandP2P)) + typedef enum { AuthFlagsWakeOnly = 0x1 // WakeOnly service @@ -1534,6 +1432,8 @@ struct AuthRecord_struct mDNSs32 KATimeExpire; // In platform time units: time to send keepalive packet for the proxy record // Field Group 3: Transient state for Authoritative Records + mDNSs32 ProbingConflictCount; // Number of conflicting records observed during probing. + mDNSs32 LastConflictPktNum; // Number of the last received packet that caused a probing conflict. mDNSu8 Acknowledged; // Set if we've given the success callback to the client mDNSu8 ProbeRestartCount; // Number of times we have restarted probing mDNSu8 ProbeCount; // Number of probes remaining before this record is valid (kDNSRecordTypeUnique) @@ -1615,7 +1515,7 @@ struct AuthRecord_struct // Note: Question_uDNS(Q) is used in *only* one place -- on entry to mDNS_StartQuery_internal, to decide whether to set TargetQID. // Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero. #define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name)) -#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || (Q)->ProxyQuestion || \ +#define Question_uDNS(Q) ((Q)->IsUnicastDotLocal || (Q)->ProxyQuestion || \ ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && (Q)->InterfaceID != mDNSInterface_BLE && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) // AuthRecordLocalOnly records are registered using mDNSInterface_LocalOnly and @@ -1625,13 +1525,6 @@ struct AuthRecord_struct // All other auth records, not including those defined as RRLocalOnly(). #define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P || (rr)->ARType == AuthRecordAnyIncludeAWDL || (rr)->ARType == AuthRecordAnyIncludeAWDLandP2P) -// Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address -// is not available locally for A or AAAA question respectively. Also, if the -// query is disallowed for the "pid" that we are sending on behalf of, suppress it. -#define QuerySuppressed(Q) (((Q)->SuppressUnusable && (Q)->SuppressQuery) || ((Q)->DisallowPID)) - -#define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel) - // Normally we always lookup the cache and /etc/hosts before sending the query on the wire. For single label // queries (A and AAAA) that are unqualified (indicated by AppendSearchDomains), we want to append search // domains before we try them as such @@ -1654,15 +1547,21 @@ struct CacheRecord_struct mDNSs32 TimeRcvd; // In platform time units mDNSs32 DelayDelivery; // Set if we want to defer delivery of this answer to local clients mDNSs32 NextRequiredQuery; // In platform time units +#if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS) + mDNSs32 LastCachedAnswerTime; // Last time this record was used as an answer from the cache (before a query) + // In platform time units +#else // Extra four bytes here (on 64bit) +#endif DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer. Can never point to a NewQuestion. mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries mDNSu8 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer - mDNSu8 CRDNSSECQuestion; // Set to 1 if this was created in response to a DNSSEC question mDNSOpaque16 responseFlags; // Second 16 bit in the DNS response CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set - CacheRecord *nsec; // NSEC records needed for non-existence proofs CacheRecord *soa; // SOA record to return for proxy questions +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + void *denial_of_existence_records; // denial_of_existence_records_t +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) mDNSAddr sourceAddress; // node from which we received this record // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit (now 160 bytes for 64-bit) @@ -1749,8 +1648,7 @@ struct ServiceRecordSet_struct ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration mDNSu32 NumSubTypes; AuthRecord *SubTypes; - const mDNSu8 *AnonData; - mDNSu32 flags; // saved for subsequent calls to mDNS_RegisterService() if records + mDNSu32 flags; // saved for subsequent calls to mDNS_RegisterService() if records // need to be re-registered. AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. @@ -1781,10 +1679,21 @@ typedef struct typedef enum { - LLQ_InitialRequest = 1, - LLQ_SecondaryRequest = 2, - LLQ_Established = 3, - LLQ_Poll = 4 + // This is the initial state. + LLQ_Init = 1, + + // All of these states indicate that we are doing DNS Push, and haven't given up yet. + LLQ_DNSPush_ServerDiscovery = 100, + LLQ_DNSPush_Connecting = 101, + LLQ_DNSPush_Established = 102, + + // All of these states indicate that we are doing LLQ and haven't given up yet. + LLQ_InitialRequest = 200, + LLQ_SecondaryRequest = 201, + LLQ_Established = 202, + + // If we get here, it means DNS Push isn't available, so we're polling. + LLQ_Poll = 300 } LLQ_State; // LLQ constants @@ -1811,23 +1720,15 @@ enum enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; -// DNS Push Notification -typedef enum -{ - DNSPUSH_NOERROR = 0, - DNSPUSH_FORMERR = 1, - DNSPUSH_SERVFAIL = 2, - DNSPUSH_NOTIMP = 4, - DNSPUSH_REFUSED = 5 -} DNSPUSH_ErrorCode; - typedef enum { - DNSPUSH_INIT = 1, - DNSPUSH_NOSERVER = 2, - DNSPUSH_SERVERFOUND = 3, - DNSPUSH_ESTABLISHED = 4 -} DNSPush_State; - + DNSPushServerDisconnected, + DNSPushServerConnectFailed, + DNSPushServerConnectionInProgress, + DNSPushServerConnected, + DNSPushServerSessionEstablished, + DNSPushServerNoDNSPush +} DNSPushServer_ConnectState; + enum { AllowExpired_None = 0, // Don't allow expired answers or mark answers immortal (behave normally) AllowExpired_MakeAnswersImmortal = 1, // Any answers to this question get marked as immortal @@ -1840,26 +1741,11 @@ typedef mDNSu8 AllowExpiredState; #define HMAC_OPAD 0x5c #define MD5_LEN 16 -#define AutoTunnelUnregistered(X) ( \ - (X)->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered ) - // Internal data structure to maintain authentication information typedef struct DomainAuthInfo { struct DomainAuthInfo *next; mDNSs32 deltime; // If we're planning to delete this DomainAuthInfo, the time we want it deleted - mDNSBool AutoTunnel; // Whether this is AutoTunnel - AuthRecord AutoTunnelHostRecord; // User-visible hostname; used as SRV target for AutoTunnel services - AuthRecord AutoTunnelTarget; // Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record - AuthRecord AutoTunnelDeviceInfo; // Device info of tunnel endpoint - AuthRecord AutoTunnelService; // Service record (possibly NAT-Mapped) of IKE daemon implementing tunnel endpoint - AuthRecord AutoTunnel6Record; // AutoTunnel AAAA Record obtained from awacsd - mDNSBool AutoTunnelServiceStarted; // Whether a service has been registered in this domain - mDNSv6Addr AutoTunnelInnerAddress; domainname domain; domainname keyname; domainname hostname; @@ -1874,64 +1760,55 @@ typedef struct DomainAuthInfo // layer. These values are used within mDNSResponder and not sent across to the application. QC_addnocache is for // delivering a response without adding to the cache. QC_forceresponse is superset of QC_addnocache where in // addition to not entering in the cache, it also forces the negative response through. -typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_dnssec , QC_nodnssec, QC_suppressed } QC_result; +typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_suppressed } QC_result; typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); +typedef void (*mDNSQuestionResetHandler)(DNSQuestion *question); typedef void AsyncDispatchFunc(mDNS *const m, void *context); -typedef void DNSSECAuthInfoFreeCallback(mDNS *const m, void *context); extern void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func); #define NextQSendTime(Q) ((Q)->LastQTime + (Q)->ThisQInterval) #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - NextQSendTime(Q) >= 0) -// q->ValidationStatus is either DNSSECValNotRequired or DNSSECValRequired and then moves onto DNSSECValInProgress. -// When Validation is done, we mark all "DNSSECValInProgress" questions "DNSSECValDone". If we are answering -// questions from /etc/hosts, then we go straight to DNSSECValDone from the initial state. -typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, DNSSECValDone } DNSSECValState; - -// ValidationRequired can be set to the following values: -// -// SECURE validation is set to determine whether something is secure or bogus -// INSECURE validation is set internally by dnssec code to indicate that it is currently proving something -// is insecure -#define DNSSEC_VALIDATION_NONE 0x00 -#define DNSSEC_VALIDATION_SECURE 0x01 -#define DNSSEC_VALIDATION_SECURE_OPTIONAL 0x02 -#define DNSSEC_VALIDATION_INSECURE 0x03 - -// For both ValidationRequired and ValidatingResponse question, we validate DNSSEC responses. -// For ProxyQuestion with DNSSECOK, we just receive the DNSSEC records to pass them along without -// validation and if the CD bit is not set, we also validate. -#define DNSSECQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse || ((q)->ProxyQuestion && (q)->ProxyDNSSECOK)) - -// ValidatingQuestion is used when we need to know whether we are validating the DNSSEC responses for a question -#define ValidatingQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse) - -#define DNSSECOptionalQuestion(q) ((q)->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL) +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#define FollowCNAMEOptionDNSSEC(Q) !(Q)->DNSSECStatus.enable_dnssec +#else // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#define FollowCNAMEOptionDNSSEC(Q) mDNStrue +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) // Given the resource record and the question, should we follow the CNAME ? #define FollowCNAME(q, rr, AddRecord) (AddRecord && (q)->qtype != kDNSType_CNAME && \ (rr)->RecordType != kDNSRecordTypePacketNegative && \ - (rr)->rrtype == kDNSType_CNAME) + (rr)->rrtype == kDNSType_CNAME \ + && FollowCNAMEOptionDNSSEC(q)) // RFC 4122 defines it to be 16 bytes #define UUID_SIZE 16 -#define AWD_METRICS (USE_AWD && TARGET_OS_IOS) - -#if AWD_METRICS - +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) enum { ExpiredAnswer_None = 0, // No expired answers used ExpiredAnswer_Allowed = 1, // An expired answer is allowed by this request - ExpiredAnswer_AnsweredWithExpired = 2, // Question was answered with an expired answer - ExpiredAnswer_ExpiredAnswerChanged = 3, // Expired answer changed on refresh + ExpiredAnswer_AnsweredWithCache = 2, // Question was answered with a cached answer + ExpiredAnswer_AnsweredWithExpired = 3, // Question was answered with an expired answer + ExpiredAnswer_ExpiredAnswerChanged = 4, // Expired answer changed on refresh ExpiredAnswer_EnumCount }; typedef mDNSu8 ExpiredAnswerMetric; +enum +{ + DNSOverTCP_None = 0, // DNS Over TCP not used + DNSOverTCP_Truncated = 1, // DNS Over TCP used because UDP reply was truncated + DNSOverTCP_Suspicious = 2, // DNS Over TCP used because we received a suspicious reply + DNSOverTCP_SuspiciousDefense = 3, // DNS Over TCP used because we were within the timeframe of a previous suspicious response + + DNSOverTCP_EnumCount +}; +typedef mDNSu8 DNSOverTCPMetric; + typedef struct { domainname * originalQName; // Name of original A/AAAA record if this question is for a CNAME record. @@ -1939,21 +1816,22 @@ typedef struct mDNSs32 firstQueryTime; // The time when the first query was sent to a DNS server. mDNSBool answered; // Has this question been answered? ExpiredAnswerMetric expiredAnswerState; // Expired answer state (see ExpiredAnswerMetric above) - + DNSOverTCPMetric dnsOverTCPState; // DNS Over TCP state (see DNSOverTCPMetric above) + } uDNSMetrics; #endif -// DNS64 code is only for iOS, which is currently the only Apple OS that supports DNS proxy network extensions. -#define USE_DNS64 (HAVE_DNS64 && TARGET_OS_IOS) +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) +extern mDNSu32 curr_num_regservices; // tracks the current number of services registered +extern mDNSu32 max_num_regservices; // tracks the max number of simultaneous services registered by the device +#endif -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) #include "DNS64State.h" #endif -#if TARGET_OS_EMBEDDED -extern mDNSu32 curr_num_regservices; // tracks the current number of services registered -extern mDNSu32 max_num_regservices; // tracks the max number of simultaneous services registered by the device -#endif +typedef struct mDNS_DNSPushNotificationServer DNSPushNotificationServer; +typedef struct mDNS_DNSPushNotificationZone DNSPushNotificationZone; struct DNSQuestion_struct { @@ -1979,7 +1857,6 @@ struct DNSQuestion_struct DomainAuthInfo *AuthInfo; // Non-NULL if query is currently being done using Private DNS DNSQuestion *DuplicateOf; DNSQuestion *NextInDQList; - AnonymousInfo *AnonInfo; // Anonymous Information DupSuppressInfo DupSuppress[DupSuppressInfoSize]; mDNSInterfaceID SendQNow; // The interface this query is being sent on right now mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces @@ -1988,29 +1865,28 @@ struct DNSQuestion_struct mDNSu32 RequestUnicast; // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces mDNSu32 CNAMEReferrals; // Count of how many CNAME redirections we've done - mDNSBool SuppressQuery; // This query should be suppressed and not sent on the wire + mDNSBool Suppressed; // This query should be suppressed, i.e., not sent on the wire. mDNSu8 LOAddressAnswers; // Number of answers from the local only auth records that are // answering A, AAAA, CNAME, or PTR (/etc/hosts) mDNSu8 WakeOnResolveCount; // Number of wakes that should be sent on resolve + mDNSBool InitialCacheMiss; // True after the question cannot be answered from the cache mDNSs32 StopTime; // Time this question should be stopped by giving them a negative answer - // DNSSEC fields - DNSSECValState ValidationState; // Current state of the Validation process - DNSSECStatus ValidationStatus; // Validation status for "ValidationRequired" questions (dnssec) - mDNSu8 ValidatingResponse; // Question trying to validate a response (dnssec) on behalf of - // ValidationRequired question - void *DNSSECAuthInfo; - DNSSECAuthInfoFreeCallback *DAIFreeCallback; - // Wide Area fields. These are used internally by the uDNS core (Unicast) UDPSocket *LocalSocket; // |-> DNS Configuration related fields used in uDNS (Subset of Wide Area/Unicast fields) +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_dns_service_t dnsservice; // The current DNS service. + mdns_dns_service_id_t lastDNSServiceID; // The ID of the previous DNS service before a CNAME restart. + mdns_querier_t querier; // The current querier. +#else DNSServer *qDNSServer; // Caching server for this query (in the absence of an SRV saying otherwise) mDNSOpaque128 validDNSServers; // Valid DNSServers for this question mDNSu16 noServerResponse; // At least one server did not respond. - mDNSu16 triedAllServersOnce; // Tried all DNS servers once + mDNSBool triedAllServersOnce; // True if all DNS servers have been tried once. mDNSu8 unansweredQueries; // The number of unanswered queries to this server +#endif AllowExpiredState allowExpired; // Allow expired answers state (see enum AllowExpired_None, etc. above) ZoneData *nta; // Used for getting zone data for private or LLQ query @@ -2020,7 +1896,9 @@ struct DNSQuestion_struct mDNSIPPort tcpSrcPort; // Local Port TCP packet received on;need this as tcp struct is disposed // by tcpCallback before calling into mDNSCoreReceive mDNSu8 NoAnswer; // Set if we want to suppress answers until tunnel setup has completed - mDNSu8 Restart; // This question should be restarted soon +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool Restart; // This question should be restarted soon. +#endif // LLQ-specific fields. These fields are only meaningful when LongLived flag is set LLQ_State state; @@ -2032,24 +1910,24 @@ struct DNSQuestion_struct // the number of packets sent for this TCP/TLS connection // DNS Push Notification fields. These fields are only meaningful when LongLived flag is set - DNSPush_State dnsPushState; // The state of the DNS push notification negotiation - mDNSAddr dnsPushServerAddr; // Address of the system acting as the DNS Push Server - mDNSIPPort dnsPushServerPort; // Port on which the DNS Push Server is being advertised. - + DNSPushNotificationServer *dnsPushServer; + mDNSOpaque64 id; // DNS Proxy fields mDNSOpaque16 responseFlags; // Temporary place holder for the error we get back from the DNS server // till we populate in the cache - mDNSBool DisallowPID; // Is the query allowed for the "PID" that we are sending on behalf of ? + mDNSBool BlockedByPolicy; // True if the question is blocked by policy rule evaluation. mDNSs32 ServiceID; // Service identifier to match against the DNS server +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSu8 ResolverUUID[UUID_SIZE]; // Resolver UUID to match against the DNS server + mdns_dns_service_id_t CustomID; +#endif // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() mDNSInterfaceID InterfaceID; // Non-zero if you want to issue queries only on a single specific IP interface mDNSu32 flags; // flags from original DNSService*() API request. - mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address - mDNSIPPort TargetPort; // Must be set if Target is set - mDNSOpaque16 TargetQID; // Must be set if Target is set + mDNSOpaque16 TargetQID; // DNS or mDNS message ID. domainname qname; domainname firstExpiredQname; // first expired qname in request chain mDNSu16 qtype; @@ -2059,28 +1937,37 @@ struct DNSQuestion_struct mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire - mDNSu8 RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords - mDNSu8 TimeoutQuestion; // Timeout this question if there is no reply in configured time - mDNSu8 WakeOnResolve; // Send wakeup on resolve - mDNSu8 UseBackgroundTrafficClass; // Set by client to use background traffic class for request - mDNSs8 SearchListIndex; // Index into SearchList; Used by the client layer but not touched by core - mDNSs8 AppendSearchDomains; // Search domains can be appended for this query - mDNSs8 AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query - mDNSu8 ValidationRequired; // Requires DNSSEC validation. + mDNSBool TimeoutQuestion; // Timeout this question if there is no reply in configured time + mDNSBool IsUnicastDotLocal; // True if this is a dot-local query that should be answered via unicast DNS. + mDNSBool WakeOnResolve; // Send wakeup on resolve + mDNSBool UseBackgroundTraffic; // Set by client to use background traffic class for request + mDNSBool AppendSearchDomains; // Search domains can be appended for this query + mDNSBool ForcePathEval; // Perform a path evaluation even if kDNSServiceFlagsPathEvaluationDone is set. +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool RequireEncryption; // Set by client to require encrypted queries +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + mDNSBool inAppBrowserRequest; // Is request associated with an in-app-browser + audit_token_t peerAuditToken; // audit token of the peer requesting the question + audit_token_t delegateAuditToken; // audit token of the delegated client the question is for +#endif mDNSu8 ProxyQuestion; // Proxy Question - mDNSu8 ProxyDNSSECOK; // Proxy Question with EDNS0 DNSSEC OK bit set mDNSs32 pid; // Process ID of the client that is requesting the question mDNSu8 uuid[UUID_SIZE]; // Unique ID of the client that is requesting the question (valid only if pid is zero) mDNSu32 euid; // Effective User Id of the client that is requesting the question - domainname *qnameOrig; // Copy of the original question name if it is not fully qualified + mDNSu32 request_id; // The ID of request that generates the current question mDNSQuestionCallback *QuestionCallback; + mDNSQuestionResetHandler ResetHandler; void *QuestionContext; -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) uDNSMetrics metrics; // Data used for collecting unicast DNS query metrics. #endif -#if USE_DNS64 +#if MDNSRESPONDER_SUPPORTS(APPLE, DNS64) DNS64 dns64; // DNS64 state for performing IPv6 address synthesis on networks with NAT64. #endif +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + dnssec_status_t DNSSECStatus; // DNSSEC state for fectching DNSSEC records and doing validation +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) }; typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ, ZoneServiceDNSPush } ZoneService; @@ -2174,7 +2061,9 @@ struct NetworkInterfaceInfo_struct // Standard AuthRecords that every Responder host should have (one per active IP address) AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name AuthRecord RR_PTR; // PTR (reverse lookup) record - AuthRecord RR_HINFO; +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + AuthRecord RR_AddrRand; // For non-AWDL interfaces, this is the A or AAAA record of the randomized hostname. +#endif // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 @@ -2240,48 +2129,6 @@ enum SleepState_Sleeping = 2 }; -typedef enum -{ - kStatsActionIncrement, - kStatsActionDecrement, - kStatsActionClear, - kStatsActionSet -} DNSSECStatsAction; - -typedef enum -{ - kStatsTypeMemoryUsage, - kStatsTypeLatency, - kStatsTypeExtraPackets, - kStatsTypeStatus, - kStatsTypeProbe, - kStatsTypeMsgSize -} DNSSECStatsType; - -typedef struct -{ - mDNSu32 TotalMemUsed; - mDNSu32 Latency0; // 0 to 4 ms - mDNSu32 Latency5; // 5 to 9 ms - mDNSu32 Latency10; // 10 to 19 ms - mDNSu32 Latency20; // 20 to 49 ms - mDNSu32 Latency50; // 50 to 99 ms - mDNSu32 Latency100; // >= 100 ms - mDNSu32 ExtraPackets0; // 0 to 2 packets - mDNSu32 ExtraPackets3; // 3 to 6 packets - mDNSu32 ExtraPackets7; // 7 to 9 packets - mDNSu32 ExtraPackets10; // >= 10 packets - mDNSu32 SecureStatus; - mDNSu32 InsecureStatus; - mDNSu32 IndeterminateStatus; - mDNSu32 BogusStatus; - mDNSu32 NoResponseStatus; - mDNSu32 NumProbesSent; // Number of probes sent - mDNSu32 MsgSize0; // DNSSEC message size <= 1024 - mDNSu32 MsgSize1; // DNSSEC message size <= 2048 - mDNSu32 MsgSize2; // DNSSEC message size > 2048 -} DNSSECStatistics; - typedef struct { mDNSu32 NameConflicts; // Normal Name conflicts @@ -2307,27 +2154,7 @@ typedef struct mDNSu32 WakeOnResolves; // Number of times we did a wake on resolve } mDNSStatistics; -extern void LogMDNSStatistics(mDNS *const m); - -typedef struct mDNS_DNSPushNotificationServer DNSPushNotificationServer; -typedef struct mDNS_DNSPushNotificationZone DNSPushNotificationZone; - -struct mDNS_DNSPushNotificationServer -{ - mDNSAddr serverAddr; // Server Address - tcpInfo_t *connection; // TCP Connection pointer - mDNSu32 numberOfQuestions; // Number of questions for this server - DNSPushNotificationServer *next; -} ; - -struct mDNS_DNSPushNotificationZone -{ - domainname zoneName; - DNSPushNotificationServer *servers; // DNS Push Notification Servers for this zone - mDNSu32 numberOfQuestions; // Number of questions for this zone - DNSPushNotificationZone *next; -} ; - +extern void LogMDNSStatisticsToFD(int fd, mDNS *const m); // Time constant (~= 260 hours ~= 10 days and 21 hours) used to set // various time values to a point well into the future. @@ -2358,10 +2185,6 @@ struct mDNS_struct mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified mDNSu8 lock_Questions; mDNSu8 lock_Records; -#ifndef MaxMsg - #define MaxMsg 512 -#endif - char MsgBuffer[MaxMsg]; // Temp storage used while building error log messages // Task Scheduling variables mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards @@ -2377,11 +2200,10 @@ struct mDNS_struct mDNSs32 NextScheduledNATOp; // Next time to send NAT-traversal packets mDNSs32 NextScheduledSPS; // Next time to purge expiring Sleep Proxy records mDNSs32 NextScheduledKA; // Next time to send Keepalive packets (SPS) -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) mDNSs32 NextBonjourDisableTime; // Next time to leave multicast group if Bonjour on Demand is enabled mDNSu8 BonjourEnabled; // Non zero if Bonjour is currently enabled by the Bonjour on Demand logic -#endif // BONJOUR_ON_DEMAND - mDNSs32 DelayConflictProcessing; // To prevent spurious confilcts due to stale packets on the wire/air. +#endif mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire mDNSu32 RandomReconfirmDelay; // For de-synchronization of reconfirmation queries on the wire mDNSs32 PktNum; // Unique sequence number assigned to each received packet @@ -2416,7 +2238,6 @@ struct mDNS_struct DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only or P2P questions not yet answered DNSQuestion *RestartQuestion; // Questions that are being restarted (stop followed by start) - DNSQuestion *ValidationQuestion; // Questions that are being validated (dnssec) mDNSu32 rrcache_size; // Total number of available cache entries mDNSu32 rrcache_totalused; // Number of cache entries currently occupied mDNSu32 rrcache_totalused_unicast; // Number of cache entries currently occupied by unicast @@ -2432,6 +2253,14 @@ struct mDNS_struct domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules domainname MulticastHostname; // Fully Qualified "dot-local" Host Name, e.g. "Foo.local." +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + domainname RandomizedHostname; // Randomized hostname to use for services involving AWDL interfaces. This is to + // avoid using a hostname derived from the device's name, which may contain the + // owner's real name, (e.g., "Steve's iPhone" -> "Steves-iPhone.local"), which is a + // privacy concern. + mDNSu32 AutoTargetAWDLIncludedCount;// Number of registered AWDL-included auto-target records. + mDNSu32 AutoTargetAWDLOnlyCount; // Number of registered AWDL-only auto-target records. +#endif UTF8str255 HIHardware; UTF8str255 HISoftware; AuthRecord DeviceInfo; @@ -2450,7 +2279,9 @@ struct mDNS_struct mDNSs32 NextuDNSEvent; // uDNS next event mDNSs32 NextSRVUpdate; // Time to perform delayed update +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) DNSServer *DNSServers; // list of DNS servers +#endif McastResolver *McastResolvers; // list of Mcast Resolvers mDNSAddr Router; @@ -2464,8 +2295,6 @@ struct mDNS_struct domainname StaticHostname; // Current answer to reverse-map query domainname FQDN; HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata - NATTraversalInfo AutoTunnelNAT; // Shared between all AutoTunnel DomainAuthInfo structs - mDNSv6Addr AutoTunnelRelayAddr; mDNSu32 WABBrowseQueriesCount; // Number of WAB Browse domain enumeration queries (b, db) callers mDNSu32 WABLBrowseQueriesCount; // Number of legacy WAB Browse domain enumeration queries (lb) callers @@ -2523,26 +2352,23 @@ struct mDNS_struct int ProxyRecords; // Total number of records we're holding as proxy #define MAX_PROXY_RECORDS 10000 /* DOS protection: 400 machines at 25 records each */ -#if APPLE_OSX_mDNSResponder - ClientTunnel *TunnelClients; - void *WCF; +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) + WCFConnection *WCF; #endif // DNS Proxy fields mDNSu32 dp_ipintf[MaxIp]; // input interface index list from the DNS Proxy Client mDNSu32 dp_opintf; // output interface index from the DNS Proxy Client - TrustAnchor *TrustAnchors; int notifyToken; int uds_listener_skt; // Listening socket for incoming UDS clients. This should not be here -- it's private to uds_daemon.c and nothing to do with mDNSCore -- SC mDNSu32 AutoTargetServices; // # of services that have AutoTarget set -#if BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) // Counters used in Bonjour on Demand logic. mDNSu32 NumAllInterfaceRecords; // Right now we count *all* multicast records here. Later we may want to change to count interface-specific records separately. (This count includes records on the DuplicateRecords list too.) mDNSu32 NumAllInterfaceQuestions; // Right now we count *all* multicast questions here. Later we may want to change to count interface-specific questions separately. -#endif // BONJOUR_ON_DEMAND +#endif - DNSSECStatistics DNSSECStats; mDNSStatistics mDNSStats; // Fixed storage, to avoid creating large objects on the stack @@ -2550,6 +2376,11 @@ struct mDNS_struct union { DNSMessage m; void *p; } imsg; // Incoming message received from wire DNSMessage omsg; // Outgoing message we're building LargeCacheRecord rec; // Resource Record extracted from received message + +#ifndef MaxMsg + #define MaxMsg 512 +#endif + char MsgBuffer[MaxMsg]; // Temp storage used while building error log messages (keep at end of struct) }; #define FORALL_CACHERECORDS(SLOT,CG,CR) \ @@ -2565,13 +2396,12 @@ struct mDNS_struct extern const mDNSInterfaceID mDNSInterface_Any; // Zero extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value -extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value extern const mDNSInterfaceID mDNSInterfaceMark; // Special value extern const mDNSInterfaceID mDNSInterface_P2P; // Special value extern const mDNSInterfaceID uDNSInterfaceMark; // Special value extern const mDNSInterfaceID mDNSInterface_BLE; // Special value -#define LocalOnlyOrP2PInterface(INTERFACE) ((INTERFACE == mDNSInterface_LocalOnly) || (INTERFACE == mDNSInterface_P2P) || (INTERFACE == mDNSInterface_BLE)) +#define LocalOnlyOrP2PInterface(INTERFACE) (((INTERFACE) == mDNSInterface_LocalOnly) || ((INTERFACE) == mDNSInterface_P2P) || ((INTERFACE) == mDNSInterface_BLE)) extern const mDNSIPPort DiscardPort; extern const mDNSIPPort SSHPort; @@ -2609,21 +2439,17 @@ extern const mDNSOpaque16 zeroID; extern const mDNSOpaque16 onesID; extern const mDNSOpaque16 QueryFlags; extern const mDNSOpaque16 uQueryFlags; -extern const mDNSOpaque16 DNSSecQFlags; extern const mDNSOpaque16 ResponseFlags; extern const mDNSOpaque16 UpdateReqFlags; extern const mDNSOpaque16 UpdateRespFlags; extern const mDNSOpaque16 SubscribeFlags; extern const mDNSOpaque16 UnSubscribeFlags; +extern const mDNSOpaque16 uDNSSecQueryFlags; extern const mDNSOpaque64 zeroOpaque64; extern const mDNSOpaque128 zeroOpaque128; extern mDNSBool StrictUnicastOrdering; -extern mDNSu8 NumUnicastDNSServers; -#if APPLE_OSX_mDNSResponder -extern mDNSu8 NumUnreachableDNSServers; -#endif #define localdomain (*(const domainname *)"\x5" "local") #define DeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp") @@ -2646,6 +2472,9 @@ extern mDNSu8 NumUnreachableDNSServers; // If we're not doing inline functions, then this header needs to have the extern declarations #if !defined(mDNSinline) +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +extern int CountOfUnicastDNSServers(mDNS *const m); +#endif extern mDNSs32 NonZeroTime(mDNSs32 t); extern mDNSu16 mDNSVal16(mDNSOpaque16 x); extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v); @@ -2659,6 +2488,16 @@ extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v); #ifdef mDNSinline +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +mDNSinline int CountOfUnicastDNSServers(mDNS *const m) +{ + int count = 0; + DNSServer *ptr = m->DNSServers; + while(ptr) { if(!(ptr->flags & DNSServerFlag_Delete)) count++; ptr = ptr->next; } + return (count); +} +#endif + mDNSinline mDNSs32 NonZeroTime(mDNSs32 t) { if (t) return(t);else return(1);} mDNSinline mDNSu16 mDNSVal16(mDNSOpaque16 x) { return((mDNSu16)((mDNSu16)x.b[0] << 8 | (mDNSu16)x.b[1])); } @@ -2672,7 +2511,7 @@ mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) } #endif - + // *************************************************************************** #if 0 #pragma mark - @@ -2813,7 +2652,6 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_De extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context); -extern mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType); extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, const domainlabel *const name, const domainname *const type, const domainname *const domain, const domainname *const host, mDNSIPPort port, RData *txtrdata, const mDNSu8 txtinfo[], mDNSu16 txtlen, @@ -2835,7 +2673,7 @@ extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID Inter const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context); extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, const mDNSu8 *anondata, + const domainname *const srv, const domainname *const domain, const mDNSInterfaceID InterfaceID, mDNSu32 flags, mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, mDNSQuestionCallback *Callback, void *Context); @@ -2864,8 +2702,14 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m); extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr); +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question); +#endif extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +extern mDNSBool ShouldSuppressUnicastQuery(const DNSQuestion *q, mdns_dns_service_t dnsservice); +extern mDNSBool LocalRecordRmvEventsForQuestion(mDNS *m, DNSQuestion *q); +#endif // *************************************************************************** #if 0 @@ -2884,6 +2728,10 @@ extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); // This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. #define AssignDomainName(DST, SRC) do { mDNSu16 len__ = DomainNameLength((SRC)); \ if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__); else (DST)->c[0] = 0; } while(0) +#define AssignConstStringDomainName(DST, SRC) do { \ + mDNSu16 len__ = DomainNameLengthLimit((domainname *)(SRC), (mDNSu8 *)(SRC) + sizeof (SRC)); \ + if (len__ <= MAX_DOMAIN_NAME) \ + mDNSPlatformMemCopy((DST)->c, (SRC), len__); else (DST)->c[0] = 0; } while(0) // Comparison functions #define SameDomainLabelCS(A,B) ((A)[0] == (B)[0] && mDNSPlatformMemSame((A)+1, (B)+1, (A)[0])) @@ -2971,8 +2819,10 @@ extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel // not the number of characters that *would* have been printed were buflen unlimited. extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) IS_A_PRINTF_STYLE_FUNCTION(3,0); extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); +extern void mDNS_snprintf_add(char **dst, const char *lim, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); extern mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id); extern char *DNSTypeName(mDNSu16 rrtype); +extern const char *mStatusDescription(mStatus error); extern char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer); #define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer) #define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) @@ -2982,6 +2832,7 @@ extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); extern mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr); // returns true for RFC1918 private addresses #define mDNSAddrIsRFC1918(X) ((X)->type == mDNSAddrType_IPv4 && mDNSv4AddrIsRFC1918(&(X)->ip.v4)) +extern const char *DNSScopeToString(mDNSu32 scope); // For PCP extern void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out); @@ -3053,7 +2904,7 @@ extern mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr *out); // and the value is prepended to the IPSec identifier (used for key lookup) extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, - const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel); + const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port); extern void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks); @@ -3077,10 +2928,12 @@ extern void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks); extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router); +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSs32 serviceID, const mDNSAddr *addr, - const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSBool isExpensive, mDNSBool isCLAT46, - mDNSu16 resGroupID, mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO); + const mDNSIPPort port, ScopeType scopeType, mDNSu32 timeout, mDNSBool cellIntf, mDNSBool isExpensive, mDNSBool isConstrained, mDNSBool isCLAT46, + mDNSu32 resGroupID, mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO); extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags); +#endif extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID); extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout); @@ -3107,9 +2960,6 @@ extern void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo (M)->h.numAdditionals = (mDNSu16)((mDNSu8 *)&(M)->h.numAdditionals)[0] << 8 | ((mDNSu8 *)&(M)->h.numAdditionals)[1]; \ } while (0) -#define DNSDigest_SignMessageHostByteOrder(M,E,INFO) \ - do { SwapDNSHeaderBytes(M); DNSDigest_SignMessage((M), (E), (INFO), 0); SwapDNSHeaderBytes(M); } while (0) - // verify a DNS message. The message must be complete, with all values in network byte order. end points to the // end of the record. tsig is a pointer to the resource record that contains the TSIG OPT record. info is // the matching key to use for verifying the message. This function expects that the additionals member @@ -3151,6 +3001,17 @@ extern mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCache // // mDNSPlatformUTC returns the time, in seconds, since Jan 1st 1970 UTC and is required for generating TSIG records +#ifdef MDNS_MALLOC_DEBUGGING +typedef void mDNSListValidationFunction(void *); +typedef struct listValidator mDNSListValidator; +struct listValidator { + struct listValidator *next; + const char *validationFunctionName; + mDNSListValidationFunction *validator; + void *context; +}; +#endif // MDNS_MALLOC_DEBUGGING + extern mStatus mDNSPlatformInit (mDNS *const m); extern void mDNSPlatformClose (mDNS *const m); extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, @@ -3160,7 +3021,6 @@ extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, extern void mDNSPlatformLock (const mDNS *const m); extern void mDNSPlatformUnlock (const mDNS *const m); -extern void mDNSPlatformStrCopy ( void *dst, const void *src); extern mDNSu32 mDNSPlatformStrLCopy ( void *dst, const void *src, mDNSu32 len); extern mDNSu32 mDNSPlatformStrLen ( const void *src); extern void mDNSPlatformMemCopy ( void *dst, const void *src, mDNSu32 len); @@ -3168,12 +3028,18 @@ extern mDNSBool mDNSPlatformMemSame (const void *dst, const void *src, mDNSu extern int mDNSPlatformMemCmp (const void *dst, const void *src, mDNSu32 len); extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); extern void mDNSPlatformQsort (void *base, int nel, int width, int (*compar)(const void *, const void *)); -#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING -#define mDNSPlatformMemAllocate(X) mallocL(# X, X) +#if MDNS_MALLOC_DEBUGGING +#define mDNSPlatformMemAllocate(X) mallocL(# X, X) +#define mDNSPlatformMemAllocateClear(X) callocL(# X, X) +#define mDNSPlatformMemFree(X) freeL(# X, X) +extern void mDNSPlatformValidateLists (void); +extern void mDNSPlatformAddListValidator(mDNSListValidator *validator, + mDNSListValidationFunction *vf, const char *vfName, void *context); #else -extern void * mDNSPlatformMemAllocate (mDNSu32 len); -#endif -extern void mDNSPlatformMemFree (void *mem); +extern void * mDNSPlatformMemAllocate(mDNSu32 len); +extern void * mDNSPlatformMemAllocateClear(mDNSu32 len); +extern void mDNSPlatformMemFree(void *mem); +#endif // MDNS_MALLOC_DEBUGGING // If the platform doesn't have a strong PRNG, we define a naive multiply-and-add based on a seed // from the platform layer. Long-term, we should embed an arc4 implementation, but the strength @@ -3227,10 +3093,17 @@ typedef enum } TCPSocketFlags; typedef void (*TCPConnectionCallback)(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err); -extern TCPSocket *mDNSPlatformTCPSocket(TCPSocketFlags flags, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass); // creates a TCP socket +typedef void (*TCPAcceptedCallback)(TCPSocket *sock, mDNSAddr *addr, mDNSIPPort *port, + const char *remoteName, void *context); +extern TCPSocket *mDNSPlatformTCPSocket(TCPSocketFlags flags, mDNSAddr_Type addrtype, mDNSIPPort *port, domainname *hostname, mDNSBool useBackgroundTrafficClass); // creates a TCP socket +extern TCPListener *mDNSPlatformTCPListen(mDNSAddr_Type addrtype, mDNSIPPort *port, mDNSAddr *addr, + TCPSocketFlags socketFlags, mDNSBool reuseAddr, int queueLength, + TCPAcceptedCallback callback, void *context); // Listen on a port +extern mStatus mDNSPlatformTCPSocketSetCallback(TCPSocket *sock, TCPConnectionCallback callback, void *context); extern TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd); extern int mDNSPlatformTCPGetFD(TCPSocket *sock); -extern mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, +extern mDNSBool mDNSPlatformTCPWritable(TCPSocket *sock); +extern mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context); extern void mDNSPlatformTCPCloseConnection(TCPSocket *sock); extern long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed); @@ -3259,7 +3132,7 @@ extern void mDNSPlatformTLSTearDownCerts(void); // in browse/registration calls must implement these routines to get the "default" browse/registration list. extern mDNSBool mDNSPlatformSetDNSConfig(mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, - DNameListElem **BrowseDomains, mDNSBool ackConfig); + DNameListElem **BrowseDomains, mDNSBool ackConfig); extern mStatus mDNSPlatformGetPrimaryInterface(mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router); extern void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status); @@ -3268,13 +3141,17 @@ extern void mDNSPlatformPreventSleep(mDNSu32 timeout, const char *reason); extern void mDNSPlatformSendWakeupPacket(mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration); extern mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID); -extern mDNSBool mDNSPlatformInterfaceIsAWDL(const NetworkInterfaceInfo *intf); +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) +extern mDNSBool mDNSPlatformInterfaceIsAWDL(mDNSInterfaceID interfaceID); +#endif extern mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); extern mDNSBool mDNSPlatformValidRecordForInterface(const AuthRecord *rr, mDNSInterfaceID InterfaceID); extern mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf); extern void mDNSPlatformFormatTime(unsigned long t, mDNSu8 *buf, int bufsize); +// Platform event API + #ifdef _LEGACY_NAT_TRAVERSAL_ // Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core. extern void LNT_SendDiscoveryMsg(mDNS *m); @@ -3338,6 +3215,12 @@ extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); extern void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +extern void mDNSCoreReceiveForQuerier(mDNS *m, DNSMessage *msg, const mDNSu8 *end, mdns_querier_t querier, mdns_dns_service_t service); +#endif +extern CacheRecord *mDNSCheckCacheFlushRecords(mDNS *m, CacheRecord *CacheFlushRecords, mDNSBool id_is_zero, int numAnswers, + DNSQuestion *unicastQuestion, CacheRecord *NSECCachePtr, CacheRecord *NSECRecords, + mDNSu8 rcode); extern void mDNSCoreRestartQueries(mDNS *const m); extern void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q); extern void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount); @@ -3348,7 +3231,18 @@ extern void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDoma extern mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m); extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now); -extern mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now); + +typedef enum +{ + mDNSNextWakeReason_Null = 0, + mDNSNextWakeReason_NATPortMappingRenewal = 1, + mDNSNextWakeReason_RecordRegistrationRenewal = 2, + mDNSNextWakeReason_UpkeepWake = 3, + mDNSNextWakeReason_DHCPLeaseRenewal = 4, + mDNSNextWakeReason_SleepProxyRegistrationRetry = 5 +} mDNSNextWakeReason; + +extern mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now, mDNSNextWakeReason *outReason); extern void mDNSCoreReceiveRawPacket (mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID); @@ -3360,14 +3254,20 @@ extern void ReleaseCacheRecord(mDNS *const m, CacheRecord *r); extern void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event); extern void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr); extern void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease); -extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, - const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, - mDNSInterfaceID InterfaceID, DNSServer *dnsserver); +extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, const domainname *const name, + const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_dns_service_t service); +#else + DNSServer *dnsserver); +#endif extern void CompleteDeregistration(mDNS *const m, AuthRecord *rr); extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord); extern void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr); extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID); +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *newServer); +#endif extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr); extern void CheckSuppressUnusableQuestions(mDNS *const m); extern void RetrySearchDomainQuestions(mDNS *const m); @@ -3383,26 +3283,12 @@ extern AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 namehash, const do extern AuthGroup *AuthGroupForRecord(AuthHash *r, const ResourceRecord *const rr); extern AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr); extern AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr); -extern mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype); -// For now this AutoTunnel stuff is specific to Mac OS X. -// In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer #if APPLE_OSX_mDNSResponder -extern void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); -extern void AddNewClientTunnel(DNSQuestion *const q); -extern void StartServerTunnel(DomainAuthInfo *const info); -extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m); -extern void RemoveAutoTunnel6Record(mDNS *const m); -extern mDNSBool RecordReadyForSleep(AuthRecord *rr); // For now this LocalSleepProxy stuff is specific to Mac OS X. // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer extern mStatus ActivateLocalProxy(NetworkInterfaceInfo *const intf, mDNSBool offloadKeepAlivesOnly, mDNSBool *keepaliveOnly); -extern void mDNSPlatformUpdateDNSStatus(DNSQuestion *q); -extern void mDNSPlatformTriggerDNSRetry(DNSQuestion *v4q, DNSQuestion *v6q); -extern void mDNSPlatformLogToFile(int log_level, const char *buffer); extern mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf); -extern mStatus SymptomReporterDNSServerReachable(mDNS *const m, const mDNSAddr *addr); -extern mStatus SymptomReporterDNSServerUnreachable(DNSServer *s); #endif typedef void ProxyCallback (void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, @@ -3413,12 +3299,19 @@ extern void mDNSPlatformDisposeProxyContext(void *context); extern mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *start, mDNSu8 *limit); #if APPLE_OSX_mDNSResponder -extern void mDNSPlatformGetDNSRoutePolicy(DNSQuestion *q, mDNSBool *isBlocked); +extern void mDNSPlatformGetDNSRoutePolicy(DNSQuestion *q); #endif extern void mDNSPlatformSetSocktOpt(void *sock, mDNSTransport_Type transType, mDNSAddr_Type addrType, const DNSQuestion *q); extern mDNSs32 mDNSPlatformGetPID(void); extern mDNSBool mDNSValidKeepAliveRecord(AuthRecord *rr); extern mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) +extern void GetRandomUUIDLabel(domainlabel *label); +extern void GetRandomUUIDLocalHostname(domainname *hostname); +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) +extern void uDNSMetricsClear(uDNSMetrics *metrics); +#endif // *************************************************************************** #if 0 @@ -3580,7 +3473,7 @@ typedef struct MD5state_st mDNSu32 A,B,C,D; mDNSu32 Nl,Nh; mDNSu32 data[MD5_BLOCK_LONG]; - int num; + mDNSu32 num; } MD5_CTX; extern int MD5_Init(MD5_CTX *c); @@ -3627,7 +3520,6 @@ struct CompileTimeAssertionChecks_mDNS char assertL[(sizeof(IKEHeader ) == 28 ) ? 1 : -1]; char assertM[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; char assertN[(sizeof(rdataOPT) == 24 ) ? 1 : -1]; - char assertO[(sizeof(rdataRRSig) == 20 ) ? 1 : -1]; char assertP[(sizeof(PCPMapRequest) == 60 ) ? 1 : -1]; char assertQ[(sizeof(PCPMapReply) == 60 ) ? 1 : -1]; @@ -3637,38 +3529,33 @@ struct CompileTimeAssertionChecks_mDNS // cause structure sizes (and therefore memory usage) to balloon unreasonably. char sizecheck_RDataBody [(sizeof(RDataBody) == 264) ? 1 : -1]; char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 72) ? 1 : -1]; - char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1]; + char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1176) ? 1 : -1]; char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 232) ? 1 : -1]; char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 232) ? 1 : -1]; - char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 1168) ? 1 : -1]; - - char sizecheck_ZoneData [(sizeof(ZoneData) <= 2000) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 1216) ? 1 : -1]; + char sizecheck_ZoneData [(sizeof(ZoneData) <= 2048) ? 1 : -1]; char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 200) ? 1 : -1]; char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; - char sizecheck_DNSServer [(sizeof(DNSServer) <= 330) ? 1 : -1]; - char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 8400) ? 1 : -1]; - char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5540) ? 1 : -1]; - char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1]; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + char sizecheck_DNSServer [(sizeof(DNSServer) <= 328) ? 1 : -1]; +#endif + char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 9000) ? 1 : -1]; + char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 4760) ? 1 : -1]; + char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 944) ? 1 : -1]; #if APPLE_OSX_mDNSResponder - char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1512) ? 1 : -1]; + char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1560) ? 1 : -1]; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + // structure size is assumed by LogRedact routine. + char sizecheck_mDNSAddr [(sizeof(mDNSAddr) == 20) ? 1 : -1]; + char sizecheck_mDNSv4Addr [(sizeof(mDNSv4Addr) == 4) ? 1 : -1]; + char sizecheck_mDNSv6Addr [(sizeof(mDNSv6Addr) == 16) ? 1 : -1]; #endif }; // Routine to initialize device-info TXT record contents mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr); -#if APPLE_OSX_mDNSResponder -extern void D2D_start_advertising_interface(NetworkInterfaceInfo *interface); -extern void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface); -extern void D2D_start_advertising_record(AuthRecord *ar); -extern void D2D_stop_advertising_record(AuthRecord *ar); -#else -#define D2D_start_advertising_interface(X) -#define D2D_stop_advertising_interface(X) -#define D2D_start_advertising_record(X) -#define D2D_stop_advertising_record(X) -#endif - // *************************************************************************** #ifdef __cplusplus diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/nsec.h b/usr/src/contrib/mDNSResponder/mDNSCore/nsec.h deleted file mode 100644 index 198f57db92..0000000000 --- a/usr/src/contrib/mDNSResponder/mDNSCore/nsec.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2011-2012 Apple Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __NSEC_H -#define __NSEC_H - -#include "dnssec.h" - -extern mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode); -extern void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv); -extern void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *rr); -extern mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q); -extern int CountLabelsMatch(const domainname *const d1, const domainname *const d2); -extern void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); -extern void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, - DNSSECVerifierCallback callback); -extern CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype); -extern void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); - -#endif // __NSEC_H diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.c b/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.c index 84913404d4..3892582a73 100755 --- a/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.c +++ b/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.c @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2002-2017 Apple Inc. All rights reserved. +/* + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +19,24 @@ * Any dynamic run-time requirements should be handled by the platform layer below or client layer above */ -#if APPLE_OSX_mDNSResponder -#include <TargetConditionals.h> -#endif #include "uDNS.h" -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) #include "Metrics.h" #endif +#if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) +#include "SymptomReporter.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +#include "QuerierSupport.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#include "dnssec_v2.h" +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + #if (defined(_MSC_VER)) // Disable "assignment within conditional expression". // Other compilers understand the convention that if you place the assignment expression within an extra pair @@ -48,16 +56,14 @@ mDNSexport SearchListElem *SearchList = mDNSNULL; // The value can be set to true by the Platform code e.g., MacOSX uses the plist mechanism mDNSBool StrictUnicastOrdering = mDNSfalse; +extern mDNS mDNSStorage; + // We keep track of the number of unicast DNS servers and log a message when we exceed 64. // Currently the unicast queries maintain a 128 bit map to track the valid DNS servers for that // question. Bit position is the index into the DNS server list. This is done so to try all // the servers exactly once before giving up. If we could allocate memory in the core, then // arbitrary limitation of 128 DNSServers can be removed. -mDNSu8 NumUnicastDNSServers = 0; #define MAX_UNICAST_DNS_SERVERS 128 -#if APPLE_OSX_mDNSResponder -mDNSu8 NumUnreachableDNSServers = 0; -#endif #define SetNextuDNSEvent(m, rr) { \ if ((m)->NextuDNSEvent - ((rr)->LastAPTime + (rr)->ThisAPInterval) >= 0) \ @@ -115,111 +121,115 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random) #pragma mark - Name Server List Management #endif -#define TrueFalseStr(X) ((X) ? "true" : "false") - -mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr, - const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSBool isExpensive, mDNSBool isCLAT46, - mDNSu16 resGroupID, mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO) +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *domain, const mDNSInterfaceID interface, + const mDNSs32 serviceID, const mDNSAddr *addr, const mDNSIPPort port, ScopeType scopeType, mDNSu32 timeout, + mDNSBool isCell, mDNSBool isExpensive, mDNSBool isConstrained, mDNSBool isCLAT46, mDNSu32 resGroupID, + mDNSBool usableA, mDNSBool usableAAAA, mDNSBool reqDO) { - DNSServer **p = &m->DNSServers; - DNSServer *tmp = mDNSNULL; - - if ((NumUnicastDNSServers + 1) > MAX_UNICAST_DNS_SERVERS) + DNSServer **p; + DNSServer *server; + int dnsCount = CountOfUnicastDNSServers(m); + if (dnsCount >= MAX_UNICAST_DNS_SERVERS) { - LogMsg("mDNS_AddDNSServer: DNS server limit of %d reached, not adding this server", MAX_UNICAST_DNS_SERVERS); + LogMsg("mDNS_AddDNSServer: DNS server count of %d reached, not adding this server", dnsCount); return mDNSNULL; } - if (!d) - d = (const domainname *)""; - - LogInfo("mDNS_AddDNSServer(%d): Adding %#a for %##s, InterfaceID %p, serviceID %u, scoped %d, resGroupID %d req_A %s, req_AAAA %s, cell %s, expensive %s, CLAT46 %s, req_DO %s", - NumUnicastDNSServers, addr, d->c, interface, serviceID, scoped, resGroupID, - TrueFalseStr(reqA), TrueFalseStr(reqAAAA), TrueFalseStr(cellIntf), TrueFalseStr(isExpensive), TrueFalseStr(isCLAT46), TrueFalseStr(reqDO)); + if (!domain) domain = (const domainname *)""; + + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "mDNS_AddDNSServer(%d): Adding " PRI_IP_ADDR " for " PRI_DM_NAME " interface " PUB_S " (%p), serviceID %u, " + "scopeType %d, resGroupID %u" PUB_S PUB_S PUB_S PUB_S PUB_S PUB_S PUB_S, + dnsCount + 1, addr, DM_NAME_PARAM(domain), InterfaceNameForID(&mDNSStorage, interface), interface, serviceID, + (int)scopeType, resGroupID, + usableA ? ", usableA" : "", + usableAAAA ? ", usableAAAA" : "", + isCell ? ", cell" : "", + isExpensive ? ", expensive" : "", + isConstrained ? ", constrained" : "", + isCLAT46 ? ", CLAT46" : "", + reqDO ? ", reqDO" : ""); + + // Scan our existing list to see if we already have a matching record for this DNS resolver + for (p = &m->DNSServers; (server = *p) != mDNSNULL; p = &server->next) + { + if (server->interface != interface) continue; + if (server->serviceID != serviceID) continue; + if (!mDNSSameAddress(&server->addr, addr)) continue; + if (!mDNSSameIPPort(server->port, port)) continue; + if (!SameDomainName(&server->domain, domain)) continue; + if (server->scopeType != scopeType) continue; + if (server->timeout != timeout) continue; + if (!server->usableA != !usableA) continue; + if (!server->usableAAAA != !usableAAAA) continue; + if (!server->isCell != !isCell) continue; + if (!(server->flags & DNSServerFlag_Delete)) + { + debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", + addr, mDNSVal16(port), domain->c, interface); + } + // If we found a matching record, cut it from the list + // (and if we’re *not* resurrecting a record that was marked for deletion, it’s a duplicate, + // and the debugf message signifies that we’re collapsing duplicate entries into one) + *p = server->next; + server->next = mDNSNULL; + break; + } - while (*p) // Check if we already have this {interface,address,port,domain} tuple registered + reqA/reqAAAA bits + // If we broke out because we found an existing matching record, advance our pointer to the end of the list + while (*p) { - if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->serviceID == serviceID && - mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d) && - (*p)->req_A == reqA && (*p)->req_AAAA == reqAAAA) - { - if (!((*p)->flags & DNSServer_FlagDelete)) - debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); - tmp = *p; - *p = tmp->next; - tmp->next = mDNSNULL; - } - else - { - p=&(*p)->next; - } + p = &(*p)->next; } - // NumUnicastDNSServers is the count of active DNS servers i.e., ones that are not marked - // with DNSServer_FlagDelete. We should increment it: - // - // 1) When we add a new DNS server - // 2) When we resurrect a old DNS server that is marked with DNSServer_FlagDelete - // - // Don't increment when we resurrect a DNS server that is not marked with DNSServer_FlagDelete. - // We have already accounted for it when it was added for the first time. This case happens when - // we add DNS servers with the same address multiple times (mis-configuration). - - if (!tmp || (tmp->flags & DNSServer_FlagDelete)) - NumUnicastDNSServers++; - - - if (tmp) + if (server) { -#if APPLE_OSX_mDNSResponder - if (tmp->flags & DNSServer_FlagDelete) + if (server->flags & DNSServerFlag_Delete) { - tmp->flags &= ~DNSServer_FlagUnreachable; - } +#if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) + server->flags &= ~DNSServerFlag_Unreachable; #endif - tmp->flags &= ~DNSServer_FlagDelete; - *p = tmp; // move to end of list, to ensure ordering from platform layer + server->flags &= ~DNSServerFlag_Delete; + } + server->isExpensive = isExpensive; + server->isConstrained = isConstrained; + server->isCLAT46 = isCLAT46; + *p = server; // Append resurrected record at end of list } else { - // allocate, add to list - *p = mDNSPlatformMemAllocate(sizeof(**p)); - if (!*p) + server = (DNSServer *) mDNSPlatformMemAllocateClear(sizeof(*server)); + if (!server) { LogMsg("Error: mDNS_AddDNSServer - malloc"); } else { - (*p)->scoped = scoped; - (*p)->interface = interface; - (*p)->serviceID = serviceID; - (*p)->addr = *addr; - (*p)->port = port; - (*p)->flags = DNSServer_FlagNew; - (*p)->timeout = timeout; - (*p)->cellIntf = cellIntf; - (*p)->isExpensive = isExpensive; - (*p)->isCLAT46 = isCLAT46; - (*p)->req_A = reqA; - (*p)->req_AAAA = reqAAAA; - (*p)->req_DO = reqDO; - // We start off assuming that the DNS server is not DNSSEC aware and - // when we receive the first response to a DNSSEC question, we set - // it to true. - (*p)->DNSSECAware = mDNSfalse; - (*p)->retransDO = 0; - AssignDomainName(&(*p)->domain, d); - (*p)->next = mDNSNULL; - } - } - if (*p) { - (*p)->penaltyTime = 0; - // We always update the ID (not just when we allocate a new instance) because we could - // be adding a new non-scoped resolver with a new ID and we want all the non-scoped - // resolvers belong to the same group. - (*p)->resGroupID = resGroupID; - } - return(*p); + server->interface = interface; + server->serviceID = serviceID; + server->addr = *addr; + server->port = port; + server->scopeType = scopeType; + server->timeout = timeout; + server->usableA = usableA; + server->usableAAAA = usableAAAA; + server->isCell = isCell; + server->isExpensive = isExpensive; + server->isConstrained = isConstrained; + server->isCLAT46 = isCLAT46; + AssignDomainName(&server->domain, domain); + *p = server; // Append new record at end of list + } + } + if (server) + { + server->penaltyTime = 0; + // We always update the ID (not just when we allocate a new instance) because we want + // all the resGroupIDs for a particular domain to match. + server->resGroupID = resGroupID; + } + return(server); } // PenalizeDNSServer is called when the number of queries to the unicast @@ -233,8 +243,9 @@ mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 re mDNS_CheckLock(m); - LogInfo("PenalizeDNSServer: Penalizing DNS server %#a question for question %p %##s (%s) SuppressUnusable %d", - (q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL), q, q->qname.c, DNSTypeName(q->qtype), q->SuppressUnusable); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "PenalizeDNSServer: Penalizing DNS server " PRI_IP_ADDR " question for question %p " PRI_DM_NAME " (" PUB_S ") SuppressUnusable %d", + (q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), q->SuppressUnusable); // If we get error from any DNS server, remember the error. If all of the servers, // return the error, then return the first error. @@ -257,27 +268,34 @@ mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 re if (!StrictUnicastOrdering) { - LogInfo("PenalizeDNSServer: Strict Unicast Ordering is FALSE"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "PenalizeDNSServer: Strict Unicast Ordering is FALSE"); // We penalize the server so that new queries don't pick this server for DNSSERVER_PENALTY_TIME // XXX Include other logic here to see if this server should really be penalized // if (q->qtype == kDNSType_PTR) { - LogInfo("PenalizeDNSServer: Not Penalizing PTR question"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "PenalizeDNSServer: Not Penalizing PTR question"); } - else if ((rcode == kDNSFlag1_RC_FormErr) || (rcode == kDNSFlag1_RC_ServFail) || (rcode == kDNSFlag1_RC_NotImpl) || (rcode == kDNSFlag1_RC_Refused)) + else if ((rcode == kDNSFlag1_RC_FormErr) || (rcode == kDNSFlag1_RC_ServFail) || (rcode == kDNSFlag1_RC_NotImpl)) { - LogInfo("PenalizeDNSServer: Not Penalizing DNS Server since it at least responded with rcode %d", rcode); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "PenalizeDNSServer: Not Penalizing DNS Server since it at least responded with rcode %d", rcode); } else { - LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype); + const char *reason = ""; + if (rcode == kDNSFlag1_RC_Refused) + { + reason = " because server refused to answer"; + } + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "PenalizeDNSServer: Penalizing question type %d" PUB_S, + q->qtype, reason); q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME); } } else { - LogInfo("PenalizeDNSServer: Strict Unicast Ordering is TRUE"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "PenalizeDNSServer: Strict Unicast Ordering is TRUE"); } end: @@ -287,8 +305,9 @@ end: { if (new) { - LogMsg("PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server %#a:%d", &new->addr, - mDNSVal16(new->port)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server " PRI_IP_ADDR ":%d", + &new->addr, mDNSVal16(new->port)); q->ThisQInterval = 0; // Inactivate this question so that we dont bombard the network } else @@ -298,7 +317,7 @@ end: // is slow in responding and we have sent three queries. When we repeatedly call, it is // okay to receive the same NULL DNS server. Next time we try to send the query, we will // realize and re-initialize the DNS servers. - LogInfo("PenalizeDNSServer: GetServerForQuestion returned the same server NULL"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "PenalizeDNSServer: GetServerForQuestion returned the same server NULL"); } } else @@ -308,8 +327,9 @@ end: if (new) { - LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s)", - q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "PenalizeDNSServer: Server for " PRI_DM_NAME " (" PUB_S ") changed to " PRI_IP_ADDR ":%d (" PRI_DM_NAME ")", + DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), DM_NAME_PARAM(&q->qDNSServer->domain)); // We want to try the next server immediately. As the question may already have backed off, reset // the interval. We do this only the first time when we try all the DNS servers. Once we reached the end of // list and retrying all the servers again e.g., at least one server failed to respond in the previous try, we @@ -337,12 +357,15 @@ end: // the next query will not happen until cache expiry. If it is a long lived question, // AnswerCurrentQuestionWithResourceRecord will not set it to MaxQuestionInterval. In that case, // we want the normal backoff to work. - LogInfo("PenalizeDNSServer: Server for %p, %##s (%s) changed to NULL, Interval %d", q, q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "PenalizeDNSServer: Server for %p, " PRI_DM_NAME " (" PUB_S ") changed to NULL, Interval %d", + q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), q->ThisQInterval); } q->unansweredQueries = 0; } } +#endif // !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -377,7 +400,7 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname // First purge any dead keys from the list while (*p) { - if ((*p)->deltime && m->timenow - (*p)->deltime >= 0 && AutoTunnelUnregistered(*p)) + if ((*p)->deltime && m->timenow - (*p)->deltime >= 0) { DNSQuestion *q; DomainAuthInfo *info = *p; @@ -415,15 +438,14 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const n // MUST be called with the lock held mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, - const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel) + const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port) { DNSQuestion *q; DomainAuthInfo **p = &m->AuthInfoList; if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); } - LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s", domain->c, keyname->c, autoTunnel ? " AutoTunnel" : ""); + LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s", domain->c, keyname->c); - info->AutoTunnel = autoTunnel; AssignDomainName(&info->domain, domain); AssignDomainName(&info->keyname, keyname); if (hostname) @@ -448,16 +470,6 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, while (*p && (*p) != info) p=&(*p)->next; if (*p) {LogInfo("mDNS_SetSecretForDomain: Domain %##s Already in list", (*p)->domain.c); return(mStatus_AlreadyRegistered);} - // Caution: Only zero AutoTunnelHostRecord.namestorage AFTER we've determined that this is a NEW DomainAuthInfo - // being added to the list. Otherwise we risk smashing our AutoTunnel host records that are already active and in use. - info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelHostRecord.namestorage.c[0] = 0; - info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelServiceStarted = mDNSfalse; - info->AutoTunnelInnerAddress = zerov6Addr; info->next = mDNSNULL; *p = info; @@ -1003,9 +1015,6 @@ mDNSlocal void StartLLQPolling(mDNS *const m, DNSQuestion *q) // we risk causing spurious "SendQueries didn't send all its queries" log messages q->LastQTime = m->timenow - q->ThisQInterval + 1; SetNextQueryTime(m, q); -#if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); -#endif } mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *const question, const LLQOptData *const data) @@ -1061,8 +1070,6 @@ mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const if (q->tcp) { LogMsg("sendChallengeResponse: ERROR!!: question %##s (%s) tcp non-NULL", q->qname.c, DNSTypeName(q->qtype)); return; } - if (PrivateQuery(q)) { LogMsg("sendChallengeResponse: ERROR!!: Private Query %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - if (q->ntries++ == kLLQ_MAX_TRIES) { LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s", kLLQ_MAX_TRIES, q->qname.c); @@ -1091,7 +1098,7 @@ mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const responsePtr = putLLQ(&m->omsg, responsePtr, q, llq); if (responsePtr) { - mStatus err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL, mDNSfalse); + mStatus err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, mDNSNULL, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSfalse); if (err) { LogMsg("sendChallengeResponse: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); } } else StartLLQPolling(m,q); @@ -1140,27 +1147,12 @@ mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const } else if (q->state == LLQ_SecondaryRequest) { - //LogInfo("Got LLQ_SecondaryRequest"); - - // Fix this immediately if not sooner. Copy the id from the LLQOptData into our DNSQuestion struct. This is only - // an issue for private LLQs, because we skip parts 2 and 3 of the handshake. This is related to a bigger - // problem of the current implementation of TCP LLQ setup: we're not handling state transitions correctly - // if the server sends back SERVFULL or STATIC. - if (PrivateQuery(q)) - { - LogInfo("Private LLQ_SecondaryRequest; copying id %08X%08X", llq->id.l[0], llq->id.l[1]); - q->id = llq->id; - } - if (llq->err) { LogMsg("ERROR: recvSetupResponse %##s (%s) code %d from server", q->qname.c, DNSTypeName(q->qtype), llq->err); StartLLQPolling(m,q); return; } if (!mDNSSameOpaque64(&q->id, &llq->id)) { LogMsg("recvSetupResponse - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering) q->state = LLQ_Established; q->ntries = 0; SetLLQTimer(m, q, llq); -#if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); -#endif } } @@ -1216,7 +1208,7 @@ mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *co //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags); ackEnd = putLLQ(&m->omsg, m->omsg.data, q, &opt->u.llq); - if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, q->LocalSocket, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, mDNSNULL, q->LocalSocket, srcaddr, srcport, mDNSNULL, mDNSfalse); m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it debugf("uDNS_LLQ_Events: q->state == LLQ_Established msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID)); *matchQuestion = q; @@ -1266,7 +1258,7 @@ mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *co } // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.) -struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; +struct TCPSocket_struct { mDNSIPPort port; TCPSocketFlags flags; /* ... */ }; // tcpCallback is called to handle events (e.g. connection opening and data reception) on TCP connections for // Private DNS operations -- private queries, private LLQs, private record updates and private service updates @@ -1326,23 +1318,18 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs } else if (q) { + mDNSOpaque16 HeaderFlags = uQueryFlags; + // LLQ Polling mode or non-LLQ uDNS over TCP - InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); + InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, HeaderFlags); end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - if (DNSSECQuestion(q) && q->qDNSServer && !q->qDNSServer->cellIntf) - { - if (q->ProxyQuestion) - end = DNSProxySetAttributes(q, &tcpInfo->request.h, &tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); - else - end = putDNSSECOption(&tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); - } AuthInfo = q->AuthInfo; // Need to add TSIG to this message } - err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, mDNSNULL, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo, mDNSfalse); + err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, sock, mDNSNULL, &tcpInfo->Addr, tcpInfo->Port, AuthInfo, mDNSfalse); if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %d", err); err = mStatus_UnknownErr; goto exit; } -#if AWD_METRICS +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) if (mDNSSameIPPort(tcpInfo->Port, UnicastDNSPort)) { MetricsUpdateDNSQuerySize((mDNSu32)(end - (mDNSu8 *)&tcpInfo->request)); @@ -1404,7 +1391,7 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs if (tcpInfo->replylen < sizeof(DNSMessageHeader)) { LogMsg("ERROR: tcpCallback - length too short (%d bytes)", tcpInfo->replylen); err = mStatus_UnknownErr; goto exit; } - tcpInfo->reply = mDNSPlatformMemAllocate(tcpInfo->replylen); + tcpInfo->reply = (DNSMessage *) mDNSPlatformMemAllocate(tcpInfo->replylen); if (!tcpInfo->reply) { LogMsg("ERROR: tcpCallback - malloc failed"); err = mStatus_NoMemoryErr; goto exit; } } @@ -1564,18 +1551,30 @@ mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, con tcpInfo_t *info; mDNSBool useBackgroundTrafficClass; - useBackgroundTrafficClass = question ? question->UseBackgroundTrafficClass : mDNSfalse; + useBackgroundTrafficClass = question ? question->UseBackgroundTraffic : mDNSfalse; if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0])) { LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; } - info = (tcpInfo_t *)mDNSPlatformMemAllocate(sizeof(tcpInfo_t)); + info = (tcpInfo_t *) mDNSPlatformMemAllocateClear(sizeof(*info)); if (!info) { LogMsg("ERROR: MakeTCP - memallocate failed"); return(mDNSNULL); } - mDNSPlatformMemZero(info, sizeof(tcpInfo_t)); + + if (msg) + { + const mDNSu8 *const start = (const mDNSu8 *)msg; + if ((end < start) || ((end - start) > (int)sizeof(info->request))) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "MakeTCPConn: invalid DNS message pointers -- msg: %p, end: %p", msg, end); + mDNSPlatformMemFree(info); + return mDNSNULL; + } + info->requestLen = (int)(end - start); + mDNSPlatformMemCopy(&info->request, msg, info->requestLen); + } info->m = m; - info->sock = mDNSPlatformTCPSocket(flags, &srcport, useBackgroundTrafficClass); - info->requestLen = 0; + info->sock = mDNSPlatformTCPSocket(flags, Addr->type, &srcport, hostname, useBackgroundTrafficClass); info->question = question; info->rr = rr; info->Addr = *Addr; @@ -1586,15 +1585,9 @@ mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, con info->numReplies = 0; info->SrcPort = srcport; - if (msg) - { - info->requestLen = (int) (end - ((mDNSu8*)msg)); - mDNSPlatformMemCopy(&info->request, msg, info->requestLen); - } - if (!info->sock) { LogMsg("MakeTCPConn: unable to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); } mDNSPlatformSetSocktOpt(info->sock, mDNSTransport_TCP, Addr->type, question); - err = mDNSPlatformTCPConnect(info->sock, Addr, Port, hostname, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info); + err = mDNSPlatformTCPConnect(info->sock, Addr, Port, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info); // Probably suboptimal here. // Instead of returning mDNSNULL here on failure, we should probably invoke the callback with an error code. @@ -1618,6 +1611,16 @@ mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) // Lock must be held mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) { + // States prior to LLQ_InitialRequest should not react to NAT Mapping changes. + // startLLQHandshake is never called with q->state < LLQ_InitialRequest except + // from LLQNATCallback. When we are actually trying to do LLQ, then q->state will + // be equal to or greater than LLQ_InitialRequest when LLQNATCallback calls + // startLLQHandshake. + if (q->state < LLQ_InitialRequest) + { + return; + } + if (m->LLQNAT.clientContext != mDNSNULL) // LLQNAT just started, give it some time { LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -1650,77 +1653,40 @@ mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) return; } - if (PrivateQuery(q)) + debugf("startLLQHandshake: m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)", + &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "", + &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "", + q->qname.c, DNSTypeName(q->qtype)); + + if (q->ntries++ >= kLLQ_MAX_TRIES) { - if (q->tcp) LogInfo("startLLQHandshake: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } - if (!q->nta) - { - // Normally we lookup the zone data and then call this function. And we never free the zone data - // for "PrivateQuery". But sometimes this can happen due to some race conditions. When we - // switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT. - // When we poll, we free the zone information as we send the query to the server (See - // PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we - // are still behind Double NAT, we would have returned early in this function. But we could - // have switched to a network with no NATs and we should get the zone data again. - LogInfo("startLLQHandshake: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); - return; - } - else if (!q->nta->Host.c[0]) - { - // This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname - LogMsg("startLLQHandshake: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived); - } - q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); - if (!q->tcp) - q->ThisQInterval = mDNSPlatformOneSecond * 5; // If TCP failed (transient networking glitch) try again in five seconds - else - { - q->state = LLQ_SecondaryRequest; // Right now, for private DNS, we skip the four-way LLQ handshake - q->ReqLease = kLLQ_DefLease; - q->ThisQInterval = 0; - } - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); + LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); + StartLLQPolling(m, q); } else { - debugf("startLLQHandshake: m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)", - &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "", - &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "", - q->qname.c, DNSTypeName(q->qtype)); + mDNSu8 *end; + LLQOptData llqData; - if (q->ntries++ >= kLLQ_MAX_TRIES) - { - LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); - StartLLQPolling(m, q); - } - else - { - mDNSu8 *end; - LLQOptData llqData; + // set llq rdata + llqData.vers = kLLQ_Vers; + llqData.llqOp = kLLQOp_Setup; + llqData.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP + llqData.id = zeroOpaque64; + llqData.llqlease = kLLQ_DefLease; - // set llq rdata - llqData.vers = kLLQ_Vers; - llqData.llqOp = kLLQOp_Setup; - llqData.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP - llqData.id = zeroOpaque64; - llqData.llqlease = kLLQ_DefLease; - - InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); - end = putLLQ(&m->omsg, m->omsg.data, q, &llqData); - if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; } + InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); + end = putLLQ(&m->omsg, m->omsg.data, q, &llqData); + if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; } - mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL, mDNSfalse); + mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, mDNSNULL, q->LocalSocket, &q->servAddr, q->servPort , mDNSNULL, mDNSfalse); - // update question state - q->state = LLQ_InitialRequest; - q->ReqLease = kLLQ_DefLease; - q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - } + // update question state + q->state = LLQ_InitialRequest; + q->ReqLease = kLLQ_DefLease; + q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); } } @@ -1736,17 +1702,6 @@ mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) return(&rr->resrec.rdata->u.srv.target); else { -#if APPLE_OSX_mDNSResponder - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - if (AuthInfo && AuthInfo->AutoTunnel) - { - StartServerTunnel(AuthInfo); - if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL); - debugf("GetServiceTarget: Returning %##s", AuthInfo->AutoTunnelHostRecord.namestorage.c); - return(&AuthInfo->AutoTunnelHostRecord.namestorage); - } - else -#endif // APPLE_OSX_mDNSResponder { const int srvcount = CountLabels(rr->resrec.name); HostnameInfo *besthi = mDNSNULL, *hi; @@ -1770,13 +1725,13 @@ mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) } } -mDNSlocal const domainname *PUBLIC_UPDATE_SERVICE_TYPE = (const domainname*)"\x0B_dns-update" "\x04_udp"; -mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE = (const domainname*)"\x08_dns-llq" "\x04_udp"; +mDNSlocal const domainname *PUBLIC_UPDATE_SERVICE_TYPE = (const domainname*)"\x0B_dns-update" "\x04_udp"; +mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE = (const domainname*)"\x08_dns-llq" "\x04_udp"; -mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x0F_dns-update-tls" "\x04_tcp"; -mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE = (const domainname*)"\x0E_dns-query-tls" "\x04_tcp"; -mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE = (const domainname*)"\x0C_dns-llq-tls" "\x04_tcp"; -mDNSlocal const domainname *DNS_PUSH_NOTIFICATION_SERVICE_TYPE = (const domainname*)"\x0C_dns-push-tls" "\x04_tcp"; +mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x0F_dns-update-tls" "\x04_tcp"; +mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE = (const domainname*)"\x0E_dns-query-tls" "\x04_tcp"; +mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE = (const domainname*)"\x0C_dns-llq-tls" "\x04_tcp"; +mDNSlocal const domainname *DNS_PUSH_NOTIFICATION_SERVICE_TYPE = (const domainname*)"\x0D_dns-push-tls" "\x04_tcp"; #define ZoneDataSRV(X) ( \ (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ @@ -1809,25 +1764,13 @@ mDNSlocal void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question { AssignDomainName(&zd->ZoneName, answer->name); zd->ZoneClass = answer->rrclass; - AssignDomainName(&zd->question.qname, &zd->ZoneName); GetZoneData_StartQuery(m, zd, kDNSType_SRV); } else if (zd->CurrentSOA->c[0]) { - DomainAuthInfo *AuthInfo = GetAuthInfoForName(m, zd->CurrentSOA); - if (AuthInfo && AuthInfo->AutoTunnel) - { - // To keep the load on the server down, we don't chop down on - // SOA lookups for AutoTunnels - LogInfo("GetZoneData_QuestionCallback: not chopping labels for %##s", zd->CurrentSOA->c); - zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd); - } - else - { - zd->CurrentSOA = (domainname *)(zd->CurrentSOA->c + zd->CurrentSOA->c[0]+1); - AssignDomainName(&zd->question.qname, zd->CurrentSOA); - GetZoneData_StartQuery(m, zd, kDNSType_SOA); - } + zd->CurrentSOA = (domainname *)(zd->CurrentSOA->c + zd->CurrentSOA->c[0]+1); + AssignDomainName(&zd->question.qname, zd->CurrentSOA); + GetZoneData_StartQuery(m, zd, kDNSType_SOA); } else { @@ -1857,8 +1800,37 @@ mDNSlocal void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question { AssignDomainName(&zd->Host, &answer->rdata->u.srv.target); zd->Port = answer->rdata->u.srv.port; - AssignDomainName(&zd->question.qname, &zd->Host); - GetZoneData_StartQuery(m, zd, kDNSType_A); + // The MakeTCPConn path, which is used by everything but DNS Push, won't work at all for + // IPv6. This should be fixed for all cases we care about, but for now we make an exception + // for Push notifications: we do not look up the a record here, but rather rely on the DSO + // infrastructure to do a GetAddrInfo call on the name and try each IP address in sequence + // until one connects. We can't do this for the other use cases because this is in the DSO + // code, not in MakeTCPConn. Ultimately the fix for this is to use Network Framework to do + // the connection establishment for all of these use cases. + // + // One implication of this is that if two different zones have DNS push server SRV records + // pointing to the same server using a different domain name, we will not see these as being + // the same server, and will not share the connection. This isn't something we can easily + // fix, and so the advice if someone runs into this and considers it a problem should be to + // use the same name. + // + // Another issue with this code is that at present, we do not wait for more than one SRV + // record--we cancel the query as soon as the first one comes in. This isn't ideal: it + // would be better to wait until we've gotten all our answers and then pick the one with + // the highest priority. Of course, this is unlikely to cause an operational problem in + // practice, and as with the previous point, the fix is easy: figure out which server you + // want people to use and don't list any other servers. Fully switching to Network + // Framework for this would (I think!) address this problem, or at least make it someone + // else's problem. + if (zd->ZoneService != ZoneServiceDNSPush) + { + AssignDomainName(&zd->question.qname, &zd->Host); + GetZoneData_StartQuery(m, zd, kDNSType_A); + } + else + { + zd->ZoneDataCallback(m, mStatus_NoError, zd); + } } else { @@ -1912,7 +1884,6 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.ThisQInterval = -1; zd->question.InterfaceID = mDNSInterface_Any; zd->question.flags = 0; - zd->question.Target = zeroAddr; //zd->question.qname.c[0] = 0; // Already set zd->question.qtype = qtype; zd->question.qclass = kDNSClass_IN; @@ -1921,17 +1892,11 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.ForceMCast = mDNSfalse; zd->question.ReturnIntermed = mDNStrue; zd->question.SuppressUnusable = mDNSfalse; - zd->question.SearchListIndex = 0; zd->question.AppendSearchDomains = 0; - zd->question.RetryWithSearchDomains = mDNSfalse; zd->question.TimeoutQuestion = 0; zd->question.WakeOnResolve = 0; - zd->question.UseBackgroundTrafficClass = mDNSfalse; - zd->question.ValidationRequired = 0; - zd->question.ValidatingResponse = 0; + zd->question.UseBackgroundTraffic = mDNSfalse; zd->question.ProxyQuestion = 0; - zd->question.qnameOrig = mDNSNULL; - zd->question.AnonInfo = mDNSNULL; zd->question.pid = mDNSPlatformGetPID(); zd->question.euid = 0; zd->question.QuestionCallback = GetZoneData_QuestionCallback; @@ -1944,51 +1909,25 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt // StartGetZoneData is an internal routine (i.e. must be called with the lock already held) mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext) { - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, name); - int initialskip = (AuthInfo && AuthInfo->AutoTunnel) ? DomainNameLength(name) - DomainNameLength(&AuthInfo->domain) : 0; - ZoneData *zd = (ZoneData*)mDNSPlatformMemAllocate(sizeof(ZoneData)); - if (!zd) { LogMsg("ERROR: StartGetZoneData - mDNSPlatformMemAllocate failed"); return mDNSNULL; } - mDNSPlatformMemZero(zd, sizeof(ZoneData)); + ZoneData *zd = (ZoneData*) mDNSPlatformMemAllocateClear(sizeof(*zd)); + if (!zd) { LogMsg("ERROR: StartGetZoneData - mDNSPlatformMemAllocateClear failed"); return mDNSNULL; } AssignDomainName(&zd->ChildName, name); zd->ZoneService = target; - zd->CurrentSOA = (domainname *)(&zd->ChildName.c[initialskip]); + zd->CurrentSOA = &zd->ChildName; zd->ZoneName.c[0] = 0; zd->ZoneClass = 0; zd->Host.c[0] = 0; zd->Port = zeroIPPort; zd->Addr = zeroAddr; - zd->ZonePrivate = AuthInfo && AuthInfo->AutoTunnel ? mDNStrue : mDNSfalse; + zd->ZonePrivate = mDNSfalse; zd->ZoneDataCallback = callback; zd->ZoneDataContext = ZoneDataContext; zd->question.QuestionContext = zd; mDNS_DropLockBeforeCallback(); // GetZoneData_StartQuery expects to be called from a normal callback, so we emulate that here - if (AuthInfo && AuthInfo->AutoTunnel && !mDNSIPPortIsZero(AuthInfo->port)) - { - LogInfo("StartGetZoneData: Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); - // We bypass SOA and SRV queries if we know the hostname and port already from the configuration. - // Today this is only true for AutoTunnel. As we bypass, we need to infer a few things: - // - // 1. Zone name is the same as the AuthInfo domain - // 2. ZoneClass is kDNSClass_IN which should be a safe assumption - // - // If we want to make this bypass mechanism work for non-AutoTunnels also, (1) has to hold - // good. Otherwise, it has to be configured also. - - AssignDomainName(&zd->ZoneName, &AuthInfo->domain); - zd->ZoneClass = kDNSClass_IN; - AssignDomainName(&zd->Host, &AuthInfo->hostname); - zd->Port = AuthInfo->port; - AssignDomainName(&zd->question.qname, &zd->Host); - GetZoneData_StartQuery(m, zd, kDNSType_A); - } - else - { - if (AuthInfo && AuthInfo->AutoTunnel) LogInfo("StartGetZoneData: Not Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); - AssignDomainName(&zd->question.qname, zd->CurrentSOA); - GetZoneData_StartQuery(m, zd, kDNSType_SOA); - } + AssignDomainName(&zd->question.qname, zd->CurrentSOA); + GetZoneData_StartQuery(m, zd, kDNSType_SOA); mDNS_ReclaimLockAfterCallback(); return zd; @@ -2331,7 +2270,7 @@ mDNSlocal void UpdateOneSRVRecord(mDNS *m, AuthRecord *rr) case regState_NATError: if (!NATChanged) return; - // if nat changed, register if we have a target (below) + // if nat changed, register if we have a target (below) /* FALLTHROUGH */ case regState_NoTarget: @@ -2602,7 +2541,6 @@ mDNSlocal void GetStaticHostname(mDNS *m) q->InterfaceID = mDNSInterface_Any; q->flags = 0; - q->Target = zeroAddr; q->qtype = kDNSType_PTR; q->qclass = kDNSClass_IN; q->LongLived = mDNSfalse; @@ -2610,17 +2548,11 @@ mDNSlocal void GetStaticHostname(mDNS *m) q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNStrue; q->SuppressUnusable = mDNSfalse; - q->SearchListIndex = 0; q->AppendSearchDomains = 0; - q->RetryWithSearchDomains = mDNSfalse; q->TimeoutQuestion = 0; q->WakeOnResolve = 0; - q->UseBackgroundTrafficClass = mDNSfalse; - q->ValidationRequired = 0; - q->ValidatingResponse = 0; + q->UseBackgroundTraffic = mDNSfalse; q->ProxyQuestion = 0; - q->qnameOrig = mDNSNULL; - q->AnonInfo = mDNSNULL; q->pid = mDNSPlatformGetPID(); q->euid = 0; q->QuestionCallback = FoundStaticHostname; @@ -2641,10 +2573,9 @@ mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSReco if (*ptr) { LogMsg("DynDNSHostName %##s already in list", fqdn->c); return; } // allocate and format new address record - *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); + *ptr = (HostnameInfo *) mDNSPlatformMemAllocateClear(sizeof(**ptr)); if (!*ptr) { LogMsg("ERROR: mDNS_AddDynDNSHostName - malloc"); return; } - mDNSPlatformMemZero(*ptr, sizeof(**ptr)); AssignDomainName(&(*ptr)->fqdn, fqdn); (*ptr)->arv4.state = regState_Unregistered; (*ptr)->arv6.state = regState_Unregistered; @@ -2786,10 +2717,6 @@ mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, co m->StaticHostname.c[0] = 0; m->NextSRVUpdate = NonZeroTime(m->timenow); - -#if APPLE_OSX_mDNSResponder - UpdateAutoTunnelDomainStatuses(m); -#endif } mDNS_Unlock(m); @@ -2896,23 +2823,14 @@ mDNSlocal mStatus checkUpdateResult(mDNS *const m, const domainname *const displ } } -// We add three Additional Records for unicast resource record registrations -// which is a function of AuthInfo and AutoTunnel properties -mDNSlocal mDNSu32 RRAdditionalSize(mDNS *const m, DomainAuthInfo *AuthInfo) +mDNSlocal mDNSu32 RRAdditionalSize(DomainAuthInfo *AuthInfo) { - mDNSu32 leaseSize, hinfoSize, tsigSize; + mDNSu32 leaseSize, tsigSize; mDNSu32 rr_base_size = 10; // type (2) class (2) TTL (4) rdlength (2) // OPT RR : Emptyname(.) + base size + rdataOPT leaseSize = 1 + rr_base_size + sizeof(rdataOPT); - // HINFO: Resource Record Name + base size + RDATA - // HINFO is added only for autotunnels - hinfoSize = 0; - if (AuthInfo && AuthInfo->AutoTunnel) - hinfoSize = (m->hostlabel.c[0] + 1) + DomainNameLength(&AuthInfo->domain) + - rr_base_size + (2 + m->HIHardware.c[0] + m->HISoftware.c[0]); - //TSIG: Resource Record Name + base size + RDATA // RDATA: // Algorithm name: hmac-md5.sig-alg.reg.int (8+7+3+3 + 5 bytes for length = 26 bytes) @@ -2927,7 +2845,7 @@ mDNSlocal mDNSu32 RRAdditionalSize(mDNS *const m, DomainAuthInfo *AuthInfo) tsigSize = 0; if (AuthInfo) tsigSize = DomainNameLength(&AuthInfo->keyname) + rr_base_size + 58; - return (leaseSize + hinfoSize + tsigSize); + return (leaseSize + tsigSize); } //Note: Make sure that RREstimatedSize is updated accordingly if anything that is done here @@ -3009,7 +2927,7 @@ mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr) limit = ptr + AbsoluteMaxDNSMessageData; AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - limit -= RRAdditionalSize(m, AuthInfo); + limit -= RRAdditionalSize(AuthInfo); mDNS_CheckLock(m); @@ -3046,7 +2964,7 @@ mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr) { LogInfo("SendRecordRegistration UDP %s", ARDisplayString(m, rr)); if (!rr->nta) { LogMsg("SendRecordRegistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } - err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); + err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, mDNSNULL, &rr->nta->Addr, rr->nta->Port, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %d", err); } @@ -3139,8 +3057,7 @@ mDNSlocal void SendGroupRRMessage(mDNS *const m, AuthRecord *anchorRR, mDNSu8 *p mDNSu8 *limit; if (!anchorRR) {debugf("SendGroupRRMessage: Could not merge records"); return;} - if (info && info->AutoTunnel) limit = m->omsg.data + AbsoluteMaxDNSMessageData; - else limit = m->omsg.data + NormalMaxDNSMessageData; + limit = m->omsg.data + NormalMaxDNSMessageData; // This has to go in the additional section and hence need to be done last ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit); @@ -3162,7 +3079,7 @@ mDNSlocal void SendGroupRRMessage(mDNS *const m, AuthRecord *anchorRR, mDNSu8 *p } else { - mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &anchorRR->nta->Addr, anchorRR->nta->Port, mDNSNULL, info, mDNSfalse); + mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, mDNSNULL, &anchorRR->nta->Addr, anchorRR->nta->Port, info, mDNSfalse); if (err) LogInfo("SendGroupRRMessage: Cannot send UDP message for %s", ARDisplayString(m, anchorRR)); else LogInfo("SendGroupRRMessage: Sent a group UDP update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit); } @@ -3318,11 +3235,10 @@ mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m) // Though we allow single record registrations for UDP to be AbsoluteMaxDNSMessageData (See // SendRecordRegistration) to handle large TXT records, to avoid fragmentation we limit UDP // message to NormalMaxDNSMessageData - if (AuthInfo && AuthInfo->AutoTunnel) spaceleft = AbsoluteMaxDNSMessageData; - else spaceleft = NormalMaxDNSMessageData; + spaceleft = NormalMaxDNSMessageData; next = m->omsg.data; - spaceleft -= RRAdditionalSize(m, AuthInfo); + spaceleft -= RRAdditionalSize(AuthInfo); if (spaceleft <= 0) { LogMsg("SendGroupUpdates: ERROR!!: spaceleft is zero at the beginning"); @@ -3525,9 +3441,6 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr); rr->updateError = err; -#if APPLE_OSX_mDNSResponder - if (err == mStatus_BadSig || err == mStatus_BadKey || err == mStatus_BadTime) UpdateAutoTunnelDomainStatuses(m); -#endif SetRecordRetry(m, rr, random); @@ -3920,7 +3833,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s", end - msg->data); -#if APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) && !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) if (NumUnreachableDNSServers > 0) SymptomReporterDNSServerReachable(m, srcaddr); #endif @@ -3932,7 +3845,13 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW) { if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring"); - else uDNS_RestartQuestionAsTCP(m, qptr, srcaddr, srcport); + else + { + uDNS_RestartQuestionAsTCP(m, qptr, srcaddr, srcport); +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + qptr->metrics.dnsOverTCPState = DNSOverTCP_Truncated; +#endif + } } } @@ -3982,7 +3901,6 @@ mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) { mDNSu8 *end; LLQOptData llq; - mDNSu8 *limit = m->omsg.data + AbsoluteMaxDNSMessageData; if (q->ReqLease) if ((q->state == LLQ_Established && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0) @@ -4002,45 +3920,12 @@ mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) end = putLLQ(&m->omsg, m->omsg.data, q, &llq); if (!end) { LogMsg("sendLLQRefresh: putLLQ failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - // Note that we (conditionally) add HINFO and TSIG here, since the question might be going away, - // so we may not be able to reference it (most importantly it's AuthInfo) when we actually send the message - end = putHINFO(m, &m->omsg, end, q->AuthInfo, limit); - if (!end) { LogMsg("sendLLQRefresh: putHINFO failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - - if (PrivateQuery(q)) - { - DNSDigest_SignMessageHostByteOrder(&m->omsg, &end, q->AuthInfo); - if (!end) { LogMsg("sendLLQRefresh: DNSDigest_SignMessage failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - } - - if (PrivateQuery(q) && !q->tcp) - { - LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (!q->nta) - { - // Note: If a question is in LLQ_Established state, we never free the zone data for the - // question (PrivateQuery). If we free, we reset the state to something other than LLQ_Established. - // This function is called only if the query is in LLQ_Established state and hence nta should - // never be NULL. In spite of that, we have seen q->nta being NULL in the field. Just refetch the - // zone data in that case. - q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); - return; - // ThisQInterval is not adjusted when we return from here which means that we will get called back - // again immediately. As q->servAddr and q->servPort are still valid and the nta->Host is initialized - // without any additional discovery for PrivateQuery, things work. - } - q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); - } - else { mStatus err; - // if AuthInfo and AuthInfo->AutoTunnel is set, we use the TCP socket but don't need to pass the AuthInfo as - // we already protected the message above. - LogInfo("sendLLQRefresh: using existing %s session %##s (%s)", PrivateQuery(q) ? "TLS" : "UDP", - q->qname.c, DNSTypeName(q->qtype)); + LogInfo("sendLLQRefresh: using existing UDP session %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, mDNSNULL, mDNSfalse); + err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->tcp ? q->tcp->sock : mDNSNULL, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSfalse); if (err) { LogMsg("sendLLQRefresh: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); @@ -4072,16 +3957,13 @@ mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneI { q->servAddr = zoneInfo->Addr; q->servPort = zoneInfo->Port; - if (!PrivateQuery(q)) + // We don't need the zone data as we use it only for the Host information which we + // don't need if we are not going to use TLS connections. + if (q->nta) { - // We don't need the zone data as we use it only for the Host information which we - // don't need if we are not going to use TLS connections. - if (q->nta) - { - if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - } + if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; } q->ntries = 0; debugf("LLQGotZoneData %#a:%d", &q->servAddr, mDNSVal16(q->servPort)); @@ -4107,90 +3989,36 @@ mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneI mDNS_Unlock(m); } -#ifdef DNS_PUSH_ENABLED +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) mDNSexport void DNSPushNotificationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) { DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext; mDNS_Lock(m); // If we get here it means that the GetZoneData operation has completed. - // We hold on to the zone data if it is AutoTunnel as we use the hostname - // in zoneInfo during the TLS connection setup. q->servAddr = zeroAddr; q->servPort = zeroIPPort; - if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) - { - q->dnsPushState = DNSPUSH_SERVERFOUND; - q->dnsPushServerAddr = zoneInfo->Addr; - q->dnsPushServerPort = zoneInfo->Port; - q->ntries = 0; - LogInfo("DNSPushNotificationGotZoneData %#a:%d", &q->dnsPushServerAddr, mDNSVal16(q->dnsPushServerPort)); - SubscribeToDNSPushNotificationServer(m,q); - } - else + if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && zoneInfo->Host.c[0]) { - q->dnsPushState = DNSPUSH_NOSERVER; - StartLLQPolling(m,q); - if (err == mStatus_NoSuchNameErr) + q->state = LLQ_DNSPush_Connecting; + LogInfo("DNSPushNotificationGotZoneData %##s%%%d", &zoneInfo->Host, ntohs(zoneInfo->Port.NotAnInteger)); + q->dnsPushServer = SubscribeToDNSPushNotificationServer(m, q); + if (q->dnsPushServer == mDNSNULL || (q->dnsPushServer->connectState != DNSPushServerConnectionInProgress && + q->dnsPushServer->connectState != DNSPushServerConnected && + q->dnsPushServer->connectState != DNSPushServerSessionEstablished)) { - // this actually failed, so mark it by setting address to all ones - q->servAddr.type = mDNSAddrType_IPv4; - q->servAddr.ip.v4 = onesIPv4Addr; + goto noServer; } } - mDNS_Unlock(m); -} -#endif // DNS_PUSH_ENABLED - -// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) -mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) -{ - DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext; - - LogInfo("PrivateQueryGotZoneData %##s (%s) err %d Zone %##s Private %d", q->qname.c, DNSTypeName(q->qtype), err, zoneInfo->ZoneName.c, zoneInfo->ZonePrivate); - - if (q->nta != zoneInfo) LogMsg("PrivateQueryGotZoneData:ERROR!!: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); - - if (err || !zoneInfo || mDNSAddressIsZero(&zoneInfo->Addr) || mDNSIPPortIsZero(zoneInfo->Port) || !zoneInfo->Host.c[0]) - { - LogInfo("PrivateQueryGotZoneData: ERROR!! %##s (%s) invoked with error code %d %p %#a:%d", - q->qname.c, DNSTypeName(q->qtype), err, zoneInfo, - zoneInfo ? &zoneInfo->Addr : mDNSNULL, - zoneInfo ? mDNSVal16(zoneInfo->Port) : 0); - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - return; - } - - if (!zoneInfo->ZonePrivate) - { - debugf("Private port lookup failed -- retrying without TLS -- %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->AuthInfo = mDNSNULL; // Clear AuthInfo so we try again non-private - q->ThisQInterval = InitialQuestionInterval; - q->LastQTime = m->timenow - q->ThisQInterval; - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - mDNS_Lock(m); - SetNextQueryTime(m, q); - mDNS_Unlock(m); - return; - // Next call to uDNS_CheckCurrentQuestion() will do this as a non-private query - } - - if (!PrivateQuery(q)) + else { - LogMsg("PrivateQueryGotZoneData: ERROR!! Not a private query %##s (%s) AuthInfo %p", q->qname.c, DNSTypeName(q->qtype), q->AuthInfo); - CancelGetZoneData(m, q->nta); - q->nta = mDNSNULL; - return; + noServer: + q->state = LLQ_InitialRequest; + startLLQHandshake(m,q); } - - q->TargetQID = mDNS_NewMessageID(m); - if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } - if (!q->nta) { LogMsg("PrivateQueryGotZoneData:ERROR!! nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } - q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, &q->nta->Host, q, mDNSNULL); - if (q->nta) { CancelGetZoneData(m, q->nta); q->nta = mDNSNULL; } + mDNS_Unlock(m); } +#endif // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -4320,19 +4148,6 @@ mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && newRR->nta && !mDNSAddrIsRFC1918(&newRR->nta->Addr) && newRR->AutoTarget == Target_AutoHostAndNATMAP) { - DomainAuthInfo *AuthInfo; - AuthInfo = GetAuthInfoForName(m, newRR->resrec.name); - if (AuthInfo && AuthInfo->AutoTunnel) - { - domainname *t = GetRRDomainNameTarget(&newRR->resrec); - LogMsg("RecordRegistrationGotZoneData: ERROR!! AutoTunnel has Target_AutoHostAndNATMAP for %s", ARDisplayString(m, newRR)); - if (t) t->c[0] = 0; - newRR->resrec.rdlength = newRR->resrec.rdestimate = 0; - newRR->state = regState_NoTarget; - CancelGetZoneData(m, newRR->nta); - newRR->nta = mDNSNULL; - return; - } // During network transitions, we are called multiple times in different states. Setup NAT // state just once for this record. if (!newRR->NATinfo.clientContext) @@ -4381,7 +4196,7 @@ mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) limit = ptr + AbsoluteMaxDNSMessageData; AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); - limit -= RRAdditionalSize(m, AuthInfo); + limit -= RRAdditionalSize(AuthInfo); rr->updateid = mDNS_NewMessageID(m); InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags); @@ -4407,7 +4222,7 @@ mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) mStatus err; LogInfo("SendRecordDeregistration UDP %s", ARDisplayString(m, rr)); if (!rr->nta) { LogMsg("SendRecordDeregistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } - err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); + err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, mDNSNULL, &rr->nta->Addr, rr->nta->Port, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %d", err); //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this } @@ -4602,70 +4417,76 @@ unreg_error: #pragma mark - Periodic Execution Routines #endif -mDNSlocal void handle_unanswered_query(mDNS *const m) +mDNSlocal void uDNS_HandleLLQState(mDNS *const m, DNSQuestion *q) { - DNSQuestion *q = m->CurrentQuestion; - - if (q->unansweredQueries >= MAX_DNSSEC_UNANSWERED_QUERIES && DNSSECOptionalQuestion(q)) + LogMsg("->uDNS_HandleLLQState: %##s %d", &q->qname, q->state); + switch(q->state) { - // If we are not receiving any responses for DNSSEC question, it could be due to - // a broken middlebox or a DNS server that does not understand the EDNS0/DOK option that - // silently drops the packets. Also as per RFC 5625 there are certain buggy DNS Proxies - // that are known to drop these pkts. To handle this, we turn off sending the EDNS0/DOK - // option if we have not received any responses indicating that the server or - // the middlebox is DNSSEC aware. If we receive at least one response to a DNSSEC - // question, we don't turn off validation. Also, we wait for MAX_DNSSEC_RETRANSMISSIONS - // before turning off validation to accomodate packet loss. - // - // Note: req_DO affects only DNSSEC_VALIDATION_SECURE_OPTIONAL questions; - // DNSSEC_VALIDATION_SECURE questions ignores req_DO. + case LLQ_Init: + // If DNS Push isn't supported, LLQ_Init falls through to LLQ_InitialRequest. +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) + // First attempt to use DNS Push Notification. + DiscoverDNSPushNotificationServer(m, q); + break; - if (!q->qDNSServer->DNSSECAware && q->qDNSServer->req_DO) + case LLQ_DNSPush_ServerDiscovery: + case LLQ_DNSPush_Connecting: + case LLQ_DNSPush_Established: + // Sanity check the server state to see if it matches. If we find that we aren't connected, when + // we think we should be, change our state. + if (q->dnsPushServer == NULL) { - q->qDNSServer->retransDO++; - if (q->qDNSServer->retransDO == MAX_DNSSEC_RETRANSMISSIONS) - { - LogInfo("handle_unanswered_query: setting req_DO false for %#a", &q->qDNSServer->addr); - q->qDNSServer->req_DO = mDNSfalse; - } + q->state = LLQ_Init; + q->ThisQInterval = 0; + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); } - - if (!q->qDNSServer->req_DO) + else { - q->ValidationState = DNSSECValNotRequired; - q->ValidationRequired = DNSSEC_VALIDATION_NONE; - - if (q->ProxyQuestion) - q->ProxyDNSSECOK = mDNSfalse; - LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a", - q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr); + switch(q->dnsPushServer->connectState) + { + case DNSPushServerDisconnected: + case DNSPushServerConnectFailed: + case DNSPushServerNoDNSPush: + LogMsg("uDNS_HandleLLQState: %##s, server state %d doesn't match question state %d", + &q->dnsPushServer->serverName, q->state, q->dnsPushServer->connectState); + q->state = LLQ_Poll; + q->ThisQInterval = (mDNSPlatformOneSecond * 5); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + break; + case DNSPushServerSessionEstablished: + LogMsg("uDNS_HandleLLQState: %##s, server connection established but question state is %d", + &q->dnsPushServer->serverName, q->state); + q->state = LLQ_DNSPush_Established; + q->ThisQInterval = 0; + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + break; + + case DNSPushServerConnectionInProgress: + case DNSPushServerConnected: + break; + } } - } -} - -mDNSlocal void uDNS_HandleLLQState(mDNS *const m, DNSQuestion *q) -{ -#ifdef DNS_PUSH_ENABLED - // First attempt to use DNS Push Notification. - if (q->dnsPushState == DNSPUSH_INIT) - DiscoverDNSPushNotificationServer(m, q); -#endif // DNS_PUSH_ENABLED - switch (q->state) - { + break; +#else + // Silence warnings; these are never reached without DNS Push + case LLQ_DNSPush_ServerDiscovery: + case LLQ_DNSPush_Connecting: + case LLQ_DNSPush_Established: +#endif // MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) case LLQ_InitialRequest: startLLQHandshake(m, q); break; - case LLQ_SecondaryRequest: - // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step - if (PrivateQuery(q)) startLLQHandshake(m, q); - else sendChallengeResponse(m, q, mDNSNULL); - break; + case LLQ_SecondaryRequest: sendChallengeResponse(m, q, mDNSNULL); break; case LLQ_Established: sendLLQRefresh(m, q); break; case LLQ_Poll: break; // Do nothing (handled below) } + LogMsg("<-uDNS_HandleLLQState: %##s %d %d", &q->qname, q->state); } // The question to be checked is not passed in as an explicit parameter; // instead it is implicit that the question to be checked is m->CurrentQuestion. -mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) +mDNSlocal void uDNS_CheckCurrentQuestion(mDNS *const m) { DNSQuestion *q = m->CurrentQuestion; if (m->timenow - NextQSendTime(q) < 0) return; @@ -4675,7 +4496,9 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) uDNS_HandleLLQState(m,q); } - handle_unanswered_query(m); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + Querier_HandleUnicastQuestion(q); +#else // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll if (!(q->LongLived && q->state != LLQ_Poll)) { @@ -4683,10 +4506,13 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { DNSServer *orig = q->qDNSServer; if (orig) - LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", - q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_CheckCurrentQuestion: Sent %d unanswered queries for " PRI_DM_NAME " (" PUB_S ") to " PRI_IP_ADDR ":%d (" PRI_DM_NAME ")", + q->request_id, mDNSVal16(q->TargetQID), q->unansweredQueries, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), DM_NAME_PARAM(&orig->domain)); + } -#if APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) SymptomReporterDNSServerUnreachable(orig); #endif PenalizeDNSServer(m, q, zeroID); @@ -4709,16 +4535,16 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { DNSServer *new; DNSQuestion *qptr; - q->triedAllServersOnce = 1; + q->triedAllServersOnce = mDNStrue; // Re-initialize all DNS servers for this question. If we have a DNSServer, DNSServerChangeForQuestion will // handle all the work including setting the new DNS server. SetValidDNSServers(m, q); new = GetServerForQuestion(m, q); if (new) { - mDNSIPPort zp = zeroIPPort; - LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%d ThisQInterval %d", - q, q->qname.c, DNSTypeName(q->qtype), new ? &new->addr : mDNSNULL, mDNSVal16(new? new->port : zp), q->ThisQInterval); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_checkCurrentQuestion: Retrying question %p " PRI_DM_NAME " (" PUB_S ") DNS Server " PRI_IP_ADDR ":%d ThisQInterval %d", + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), new ? &new->addr : mDNSNULL, mDNSVal16(new ? new->port : zeroIPPort), q->ThisQInterval); DNSServerChangeForQuestion(m, q, new); } for (qptr = q->next ; qptr; qptr = qptr->next) @@ -4728,84 +4554,66 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { mDNSu8 *end; mStatus err = mStatus_NoError; - mDNSBool private = mDNSfalse; - - InitializeDNSMessage(&m->omsg.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); + mDNSOpaque16 HeaderFlags = uQueryFlags; + InitializeDNSMessage(&m->omsg.h, q->TargetQID, HeaderFlags); end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) - { - if (q->ProxyQuestion) - end = DNSProxySetAttributes(q, &m->omsg.h, &m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); - else - end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); - } - private = PrivateQuery(q); if (end > m->omsg.data) { - //LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype)); - if (private) + debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d", + q, q->qname.c, DNSTypeName(q->qtype), + q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries); +#if APPLE_OSX_mDNSResponder + // When a DNS proxy network extension initiates the close of a UDP flow (this usually happens when a DNS + // proxy gets disabled or crashes), mDNSResponder's corresponding UDP socket will be marked with the + // SS_CANTRCVMORE state flag. Reading from such a socket is no longer possible, so close the current + // socket pair so that we can create a new pair. + if (q->LocalSocket && mDNSPlatformUDPSocketEncounteredEOF(q->LocalSocket)) { - if (q->nta) CancelGetZoneData(m, q->nta); - q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q); - if (q->state == LLQ_Poll) q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep; + mDNSPlatformUDPClose(q->LocalSocket); + q->LocalSocket = mDNSNULL; } - else +#endif + if (!q->LocalSocket) { - mDNSIPPort zp = zeroIPPort; - debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d", - q, q->qname.c, DNSTypeName(q->qtype), - q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries); -#if APPLE_OSX_mDNSResponder - // When a DNS proxy network extension initiates the close of a UDP flow (this usually happens when a DNS - // proxy gets disabled or crashes), mDNSResponder's corresponding UDP socket will be marked with the - // SS_CANTRCVMORE state flag. Reading from such a socket is no longer possible, so close the current - // socket pair so that we can create a new pair. - if (q->LocalSocket && mDNSPlatformUDPSocketEncounteredEOF(q->LocalSocket)) + q->LocalSocket = mDNSPlatformUDPSocket(zeroIPPort); + if (q->LocalSocket) { - mDNSPlatformUDPClose(q->LocalSocket); - q->LocalSocket = mDNSNULL; + mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, mDNSAddrType_IPv4, q); + mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, mDNSAddrType_IPv6, q); } -#endif - if (!q->LocalSocket) + } + if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time + else + { + err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, mDNSNULL, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, q->UseBackgroundTraffic); + +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + if (!err) { - q->LocalSocket = mDNSPlatformUDPSocket(zeroIPPort); - if (q->LocalSocket) + MetricsUpdateDNSQuerySize((mDNSu32)(end - (mDNSu8 *)&m->omsg)); + if (q->metrics.answered) { - mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, mDNSAddrType_IPv4, q); - mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, mDNSAddrType_IPv6, q); + q->metrics.querySendCount = 0; + q->metrics.answered = mDNSfalse; } - } - if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time - else - { - err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); -#if AWD_METRICS - if (!err) + if (q->metrics.querySendCount++ == 0) { - MetricsUpdateDNSQuerySize((mDNSu32)(end - (mDNSu8 *)&m->omsg)); - if (q->metrics.answered) - { - q->metrics.querySendCount = 0; - q->metrics.answered = mDNSfalse; - } - if (q->metrics.querySendCount++ == 0) - { - q->metrics.firstQueryTime = m->timenow; - } + q->metrics.firstQueryTime = NonZeroTime(m->timenow); } -#endif } - } +#endif + } } if (err == mStatus_HostUnreachErr) { DNSServer *newServer; - LogInfo("uDNS_CheckCurrentQuestion: host unreachable error for DNS server %#a for question [%p] %##s (%s)", - &q->qDNSServer->addr, q, q->qname.c, DNSTypeName(q->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_CheckCurrentQuestion: host unreachable error for DNS server " PRI_IP_ADDR " for question [%p] " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), &q->qDNSServer->addr, q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); if (!StrictUnicastOrdering) { @@ -4815,14 +4623,16 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) newServer = GetServerForQuestion(m, q); if (!newServer) { - q->triedAllServersOnce = 1; + q->triedAllServersOnce = mDNStrue; SetValidDNSServers(m, q); newServer = GetServerForQuestion(m, q); } if (newServer) { - LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%u ThisQInterval %d", - q, q->qname.c, DNSTypeName(q->qtype), newServer ? &newServer->addr : mDNSNULL, mDNSVal16(newServer ? newServer->port : zeroIPPort), q->ThisQInterval); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_checkCurrentQuestion: Retrying question %p " PRI_DM_NAME " (" PUB_S ") DNS Server " PRI_IP_ADDR ":%u ThisQInterval %d", + q->request_id, mDNSVal16(q->TargetQID), q, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), + newServer ? &newServer->addr : mDNSNULL, mDNSVal16(newServer ? newServer->port : zeroIPPort), q->ThisQInterval); DNSServerChangeForQuestion(m, q, newServer); } if (q->triedAllServersOnce) @@ -4850,24 +4660,14 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) q->unansweredQueries++; if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; - if (private && q->state != LLQ_Poll) - { - // We don't want to retransmit too soon. Hence, we always schedule our first - // retransmisson at 3 seconds rather than one second - if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; - if (q->ThisQInterval > LLQ_POLL_INTERVAL) - q->ThisQInterval = LLQ_POLL_INTERVAL; - LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); - } - if (q->qDNSServer->cellIntf) + if (q->qDNSServer->isCell) { // We don't want to retransmit too soon. Schedule our first retransmisson at // MIN_UCAST_RETRANS_TIMEOUT seconds. if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; } - debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); + debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->isCell); } q->LastQTime = m->timenow; } @@ -4883,15 +4683,16 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) // (When we have a group of identical questions, only the active representative of the group gets // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire -- // but we want *all* of the questions to get answer callbacks.) - CacheRecord *rr; + CacheRecord *cr; const mDNSu32 slot = HashSlotFromNameHash(q->qnamehash); CacheGroup *const cg = CacheGroupForName(m, q->qnamehash, &q->qname); if (!q->qDNSServer) { if (!mDNSOpaque128IsZero(&q->validDNSServers)) - LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x 0x%x 0x%x for question %##s (%s)", - q->validDNSServers.l[3], q->validDNSServers.l[2], q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u->Q%u] uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x 0x%x 0x%x for question " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), q->validDNSServers.l[3], q->validDNSServers.l[2], q->validDNSServers.l[1], q->validDNSServers.l[0], DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); // If we reached the end of list while picking DNS servers, then we don't want to deactivate the // question. Try after 60 seconds. We find this by looking for valid DNSServers for this question, // if we find any, then we must have tried them before we came here. This avoids maintaining @@ -4899,7 +4700,9 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) SetValidDNSServers(m, q); if (mDNSOpaque128IsZero(&q->validDNSServers)) { - LogInfo("uDNS_CheckCurrentQuestion: no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_CheckCurrentQuestion: no DNS server for " PRI_DM_NAME " (" PUB_S ")", + q->request_id, mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype)); q->ThisQInterval = 0; } else @@ -4916,28 +4719,30 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) q->qDNSServer = GetServerForQuestion(m, q); for (qptr = q->next ; qptr; qptr = qptr->next) if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } - { - mDNSIPPort zp = zeroIPPort; - LogInfo("uDNS_checkCurrentQuestion: Tried all DNS servers, retry question %p SuppressUnusable %d %##s (%s) with DNS Server %#a:%d after 60 seconds, ThisQInterval %d", - q, q->SuppressUnusable, q->qname.c, DNSTypeName(q->qtype), - q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zp), q->ThisQInterval); - } + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_checkCurrentQuestion: Tried all DNS servers, retry question %p SuppressUnusable %d " PRI_DM_NAME " (" PUB_S ") with DNS Server " PRI_IP_ADDR ":%d after 60 seconds, ThisQInterval %d", + q->request_id, mDNSVal16(q->TargetQID), q, q->SuppressUnusable, DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), + q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->ThisQInterval); } } else { q->ThisQInterval = 0; - LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_CheckCurrentQuestion DNS server " PRI_IP_ADDR ":%d for " PRI_DM_NAME " is disabled", + q->request_id, mDNSVal16(q->TargetQID), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), DM_NAME_PARAM(&q->qname)); } if (cg) { - for (rr = cg->members; rr; rr=rr->next) + for (cr = cg->members; cr; cr=cr->next) { - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + if (SameNameCacheRecordAnswersQuestion(cr, q)) { - LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); - mDNS_PurgeCacheResourceRecord(m, rr); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] uDNS_CheckCurrentQuestion: Purged resourcerecord " PRI_S, + q->request_id, mDNSVal16(q->TargetQID), CRDisplayString(m, cr)); + mDNS_PurgeCacheResourceRecord(m, cr); } } } @@ -4949,7 +4754,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) if (!mDNSOpaque16IsZero(q->responseFlags)) m->rec.r.responseFlags = q->responseFlags; // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list. - // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we + // To solve this problem we set cr->DelayDelivery to a nonzero value (which happens to be 'now') so that we // momentarily defer generating answer callbacks until mDNS_Execute time. CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow), mDNStrue, mDNSNULL); ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow)); @@ -4958,6 +4763,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it } } +#endif // MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) } mDNSexport void CheckNATMappings(mDNS *m) @@ -5151,7 +4957,9 @@ mDNSlocal mDNSs32 CheckRecordUpdates(mDNS *m) mDNSexport void uDNS_Tasks(mDNS *const m) { mDNSs32 nexte; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) DNSServer *d; +#endif m->NextuDNSEvent = m->timenow + FutureTime; @@ -5159,21 +4967,28 @@ mDNSexport void uDNS_Tasks(mDNS *const m) if (m->NextuDNSEvent - nexte > 0) m->NextuDNSEvent = nexte; +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) for (d = m->DNSServers; d; d=d->next) if (d->penaltyTime) { if (m->timenow - d->penaltyTime >= 0) { - LogInfo("DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "DNS server " PRI_IP_ADDR ":%d out of penalty box", &d->addr, mDNSVal16(d->port)); d->penaltyTime = 0; } else if (m->NextuDNSEvent - d->penaltyTime > 0) m->NextuDNSEvent = d->penaltyTime; } +#endif if (m->CurrentQuestion) - LogMsg("uDNS_Tasks ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "uDNS_Tasks ERROR m->CurrentQuestion already set: " PRI_DM_NAME " (" PRI_S ")", + DM_NAME_PARAM(&m->CurrentQuestion->qname), DNSTypeName(m->CurrentQuestion->qtype)); + } m->CurrentQuestion = m->Questions; while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) { @@ -5265,9 +5080,8 @@ mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfa else { // if domain not in list, add to list, mark as add (1) - *p = mDNSPlatformMemAllocate(sizeof(SearchListElem)); + *p = (SearchListElem *) mDNSPlatformMemAllocateClear(sizeof(**p)); if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; } - mDNSPlatformMemZero(*p, sizeof(SearchListElem)); AssignDomainName(&(*p)->domain, domain); (*p)->next = mDNSNULL; (*p)->InterfaceID = InterfaceID; @@ -5302,7 +5116,7 @@ mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceR if (AddRecord) { - ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem)); + ARListElem *arElem = (ARListElem *) mDNSPlatformMemAllocateClear(sizeof(*arElem)); if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; } mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem); MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name); @@ -5696,7 +5510,7 @@ mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType) uDNS_SetupWABQueries(m); } -mDNSexport domainname *uDNS_GetNextSearchDomain(mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal) +mDNSexport domainname *uDNS_GetNextSearchDomain(mDNSInterfaceID InterfaceID, int *searchIndex, mDNSBool ignoreDotLocal) { SearchListElem *p = SearchList; int count = *searchIndex; @@ -5736,10 +5550,7 @@ mDNSexport domainname *uDNS_GetNextSearchDomain(mDNSInterfaceID InterfaceID, mD } // Point to the next one in the list which we will look at next time. (*searchIndex)++; - // When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID - // set to mDNSInterface_Unicast. Match the unscoped entries in that case. - if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) || - p->InterfaceID == InterfaceID) + if (p->InterfaceID == InterfaceID) { LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); return &p->domain; @@ -5824,240 +5635,666 @@ struct CompileTimeAssertionChecks_uDNS // other overly-large structures instead of having a pointer to them, can inadvertently // cause structure sizes (and therefore memory usage) to balloon unreasonably. char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1]; - char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 6136) ? 1 : -1]; + char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 6381) ? 1 : -1]; }; #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - DNS Push Notification functions #endif -#ifdef DNS_PUSH_ENABLED -mDNSlocal tcpInfo_t * GetTCPConnectionToPushServer(mDNS *m, DNSQuestion *q) +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) +mDNSlocal void DNSPushProcessResponse(mDNS *const m, const DNSMessage *const msg, + DNSPushNotificationServer *server, ResourceRecord *mrr) { - DNSPushNotificationZone *zone; - DNSPushNotificationServer *server; - DNSPushNotificationZone *newZone; - DNSPushNotificationServer *newServer; + // "(CacheRecord*)1" is a special (non-zero) end-of-list marker + // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList + // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling. + CacheRecord *CacheFlushRecords = (CacheRecord*)1; + CacheRecord **cfp = &CacheFlushRecords; + enum { removeName, removeClass, removeRRset, removeRR, addRR } action; - // If we already have a question for this zone and if the server is the same, reuse it - for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next) + // Ignore records we don't want to cache. + + // Don't want to cache OPT or TSIG pseudo-RRs + if (mrr->rrtype == kDNSType_TSIG) { - if (SameDomainName(&q->nta->ChildName, &zone->zoneName)) + return; + } + if (mrr->rrtype == kDNSType_OPT) + { + return; + } + + if ((mrr->rrtype == kDNSType_CNAME) && SameDomainName(mrr->name, &mrr->rdata->u.name)) + { + LogInfo("DNSPushProcessResponse: CNAME loop domain name %##s", mrr->name->c); + return; + } + + // TTL == -1: delete individual record + // TTL == -2: wildcard delete + // CLASS != ANY, TYPE != ANY: delete all records of specified type and class + // CLASS != ANY, TYPE == ANY: delete all RRs of specified class + // CLASS == ANY: delete all RRs on the name, regardless of type or class (TYPE is ignored). + // If TTL is zero, this is a delete, not an add. + if ((mDNSs32)mrr->rroriginalttl == -1) + { + LogMsg("DNSPushProcessResponse: Got remove on %##s with type %s", + mrr->name, DNSTypeName(mrr->rrtype)); + action = removeRR; + } + else if ((mDNSs32)mrr->rroriginalttl == -2) + { + if (mrr->rrclass == kDNSQClass_ANY) { - DNSPushNotificationServer *zoneServer = mDNSNULL; - for (zoneServer = zone->servers; zoneServer != mDNSNULL; zoneServer = zoneServer->next) + LogMsg("DNSPushProcessResponse: Got Remove Name on %##s", mrr->name); + action = removeName; + } + else if (mrr->rrtype == kDNSQType_ANY) + { + LogMsg("DNSPushProcessResponse: Got Remove Name on %##s", mrr->name); + action = removeClass; + } + else + { + LogMsg("DNSPushProcessResponse: Got Remove RRset on %##s, type %s, rdlength %d", + mrr->name, DNSTypeName(mrr->rrtype), mrr->rdlength); + action = removeRRset; + } + } + else + { + action = addRR; + } + + if (action != addRR) + { + if (m->rrcache_size) + { + CacheRecord *rr; + // Remember the unicast question that we found, which we use to make caching + // decisions later on in this function + CacheGroup *cg = CacheGroupForName(m, mrr->namehash, mrr->name); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) { - if (mDNSSameAddress(&q->dnsPushServerAddr, &zoneServer->serverAddr)) + if ( action == removeName || + (action == removeClass && rr->resrec.rrclass == mrr->rrclass) || + (rr->resrec.rrclass == mrr->rrclass && + ((action == removeRRset && rr->resrec.rrtype == mrr->rrtype) || + (action == removeRR && rr->resrec.rrtype == mrr->rrtype && + SameRDataBody(mrr, &rr->resrec.rdata->u, SameDomainName))))) { - zone->numberOfQuestions++; - zoneServer->numberOfQuestions++; - return zoneServer->connection; + LogInfo("DNSPushProcessResponse purging %##s (%s) %s", + rr->resrec.name, DNSTypeName(mrr->rrtype), CRDisplayString(m, rr)); + // We've found a cache entry to delete. Now what? + mDNS_PurgeCacheResourceRecord(m, rr); } } } } - - // If we have a connection to this server but it is for a differnt zone, create a new zone entry and reuse the connection - for (server = m->DNSPushServers; server != mDNSNULL; server = server->next) + else { - if (mDNSSameAddress(&q->dnsPushServerAddr, &server->serverAddr)) + // It's an add. + LogMsg("DNSPushProcessResponse: Got add RR on %##s, type %s, length %d", + mrr->name, DNSTypeName(mrr->rrtype), mrr->rdlength); + + // When we receive DNS Push responses, we assume a long cache lifetime -- + // This path is only reached for DNS Push responses; as long as the connection to the server is + // live, the RR should stay updated. + mrr->rroriginalttl = kLLQ_DefLease /* XXX */; + + // Use the DNS Server we remember from the question that created this DNS Push server structure. +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_replace(&mrr->dnsservice, server->dnsservice); +#else + mrr->rDNSServer = server->qDNSServer; +#endif + + // 2. See if we want to add this packet resource record to our cache + // We only try to cache answers if we have a cache to put them in + if (m->rrcache_size) { - newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone)); - newZone->numberOfQuestions = 1; - newZone->zoneName = q->nta->ChildName; - newZone->servers = server; + const mDNSu32 slot = HashSlotFromNameHash(mrr->namehash); + CacheGroup *cg = CacheGroupForName(m, mrr->namehash, mrr->name); + CacheRecord *rr = mDNSNULL; - // Add the new zone to the begining of the list - newZone->next = m->DNSPushZones; - m->DNSPushZones = newZone; + // 2a. Check if this packet resource record is already in our cache. + rr = mDNSCoreReceiveCacheCheck(m, msg, uDNS_LLQ_Events, slot, cg, &cfp, mDNSNULL); - server->numberOfQuestions++; - return server->connection; + // If packet resource record not in our cache, add it now + // (unless it is just a deletion of a record we never had, in which case we don't care) + if (!rr && mrr->rroriginalttl > 0) + { + rr = CreateNewCacheEntry(m, slot, cg, 0, + mDNStrue, &server->connection->transport->remote_addr); + if (rr) + { + // Not clear that this is ever used, but for verisimilitude, set this to look like + // an authoritative response to a regular query. + rr->responseFlags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA; + rr->responseFlags.b[1] = kDNSFlag1_RC_NoErr | kDNSFlag0_AA; + } + } } } +} - // If we do not have any existing connections, create a new connection - newServer = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationServer)); - newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone)); - - newServer->numberOfQuestions = 1; - newServer->serverAddr = q->dnsPushServerAddr; - newServer->connection = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->dnsPushServerAddr, q->dnsPushServerPort, &q->nta->Host, q, mDNSNULL); - - newZone->numberOfQuestions = 1; - newZone->zoneName = q->nta->ChildName; - newZone->servers = newServer; - - // Add the new zone to the begining of the list - newZone->next = m->DNSPushZones; - m->DNSPushZones = newZone; - - newServer->next = m->DNSPushServers; - m->DNSPushServers = newServer; - return newServer->connection; +mDNSlocal void DNSPushProcessResponses(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *firstAnswer, + const mDNSu8 *const end, DNSPushNotificationServer *server) +{ + DNSQuestion *q; + const mDNSu8 *ptr = firstAnswer; + mDNSIPPort port; + port.NotAnInteger = 0; + ResourceRecord *mrr = &m->rec.r.resrec; + + // Validate the contents of the message + // XXX Right now this code will happily parse all the valid data and then hit invalid data + // and give up. I don't think there's a risk here, but we should discuss it. + // XXX what about source validation? Like, if we have a VPN, are we safe? I think yes, but let's think about it. + while ((ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSNULL, kDNSRecordTypePacketAns, &m->rec))) + { + int gotOne = 0; + for (q = m->Questions; q; q = q->next) + { + if (q->LongLived && + (q->qtype == mrr->rrtype || q->qtype == kDNSServiceType_ANY) + && q->qnamehash == mrr->namehash && SameDomainName(&q->qname, mrr->name)) + { + LogMsg("DNSPushProcessResponses found %##s (%s) %d %s %s", + q->qname.c, DNSTypeName(q->qtype), q->state, + q->dnsPushServer ? (q->dnsPushServer->connection + ? q->dnsPushServer->connection->remote_name + : "<no push server>") : "<no push server>", + server->connection->remote_name); + if (q->dnsPushServer == server) + { + gotOne++; + DNSPushProcessResponse(m, msg, server, mrr); + break; // question list may have changed + } + } + } + if (!gotOne) { + LogMsg("DNSPushProcessResponses: no match for %##s %d %d", mrr->name, mrr->rrtype, mrr->rrclass); + } + mrr->RecordType = 0; // Clear RecordType to show we're not still using it + } +} + +static void +DNSPushStartConnecting(DNSPushNotificationServer *server) +{ + if (dso_connect(server->connectInfo)) + { + server->connectState = DNSPushServerConnectionInProgress; + } + else + { + server->connectState = DNSPushServerConnectFailed; + } } -mDNSexport void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +mDNSexport void DNSPushReconcileConnection(mDNS *m, DNSQuestion *q) { - /* Use the same NAT setup as in the LLQ case */ - if (m->LLQNAT.clientContext != mDNSNULL) // LLQNAT just started, give it some time + DNSPushNotificationZone *zone; + DNSPushNotificationZone *nextZone; + + if (q->dnsPushServer == mDNSNULL) { - LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); return; } + + // Update the counts + for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next) + { + if (zone->server == q->dnsPushServer) + { + zone->numberOfQuestions--; + } + } + q->dnsPushServer->numberOfQuestions--; - // Either we don't have {PCP, NAT-PMP, UPnP/IGD} support (ExternalPort is zero) or behind a Double NAT that may or - // may not have {PCP, NAT-PMP, UPnP/IGD} support (NATResult is non-zero) - if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) + nextZone = mDNSNULL; + for (zone = m->DNSPushZones; zone != mDNSNULL; zone = nextZone) { - LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d", - q->qname.c, DNSTypeName(q->qtype), mDNSVal16(m->LLQNAT.ExternalPort), m->LLQNAT.Result); - StartLLQPolling(m, q); // Actually sets up the NAT Auto Tunnel + nextZone = zone->next; + if (zone->numberOfQuestions == 0) + { + if (zone == m->DNSPushZones) + m->DNSPushZones = nextZone; + LogInfo("DNSPushReconcileConnection: zone %##s is being freed", &zone->zoneName); + mDNSPlatformMemFree(zone); + } + } + + q->dnsPushServer = mDNSNULL; +} + +static const char kDNSPushActivity_Subscription[] = "dns-push-subscription"; + +static void DNSPushSendKeepalive(DNSPushNotificationServer *server, mDNSu32 inactivity_timeout, mDNSu32 keepalive_interval) +{ + dso_message_t state; + dso_transport_t *transport = server->connection->transport; + if (transport == NULL || transport->outbuf == NULL) { + // Should be impossible, don't crash. + LogInfo("DNSPushNotificationSendSubscribe: no transport!"); return; } + dso_make_message(&state, transport->outbuf, transport->outbuf_size, server->connection, false, 0); + dso_start_tlv(&state, kDSOType_Keepalive); + dso_add_tlv_u32(&state, inactivity_timeout); + dso_add_tlv_u32(&state, keepalive_interval); + dso_finish_tlv(&state); + dso_message_write(server->connection, &state, mDNSfalse); +} - if (mDNSIPPortIsZero(q->dnsPushServerPort) && q->dnsPushState == DNSPUSH_INIT) - { - LogInfo("SubscribeToDNSPushNotificationServer: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); - q->dnsPushServerAddr = zeroAddr; - // We know q->dnsPushServerPort is zero because of check above - if (q->nta) CancelGetZoneData(m, q->nta); - q->nta = StartGetZoneData(m, &q->qname, ZoneServiceDNSPush, DNSPushNotificationGotZoneData, q); +static void DNSPushNotificationSendSubscriptionChange(mDNSBool subscribe, dso_state_t *dso, DNSQuestion *q) +{ + dso_message_t state; + dso_transport_t *transport = dso->transport; + mDNSu16 len; + if (transport == NULL || transport->outbuf == NULL) { + // Should be impossible, don't crash. + LogInfo("DNSPushNotificationSendSubscribe: no transport!"); return; } + dso_make_message(&state, transport->outbuf, transport->outbuf_size, dso, subscribe ? false : true, q); + dso_start_tlv(&state, subscribe ? kDSOType_DNSPushSubscribe : kDSOType_DNSPushUnsubscribe); + len = DomainNameLengthLimit(&q->qname, q->qname.c + (sizeof q->qname)); + dso_add_tlv_bytes(&state, q->qname.c, len); + dso_add_tlv_u16(&state, q->qtype); + dso_add_tlv_u16(&state, q->qclass); + dso_finish_tlv(&state); + dso_message_write(dso, &state, mDNSfalse); +} - if (q->tcp) +static void DNSPushStop(mDNS *m, DNSPushNotificationServer *server) +{ + mDNSBool found = mDNStrue; + DNSQuestion *q; + while (found) { - LogInfo("SubscribeToDNSPushNotificationServer: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - DisposeTCPConn(q->tcp); - q->tcp = mDNSNULL; + found = mDNSfalse; + server->connectState = DNSPushServerNoDNSPush; + + for (q = m->Questions; q; q = q->next) + { + if (q->dnsPushServer == server) + { + DNSPushReconcileConnection(m, q); + q->dnsPushServer = NULL; + q->state = LLQ_Poll; + q->ThisQInterval = 0; + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + break; + } + } } +} - if (!q->nta) +mDNSexport void DNSPushServerDrop(DNSPushNotificationServer *server) +{ + if (server->connection) { - // Normally we lookup the zone data and then call this function. And we never free the zone data - // for "PrivateQuery". But sometimes this can happen due to some race conditions. When we - // switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT. - // When we poll, we free the zone information as we send the query to the server (See - // PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we - // are still behind Double NAT, we would have returned early in this function. But we could - // have switched to a network with no NATs and we should get the zone data again. - LogInfo("SubscribeToDNSPushNotificationServer: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->nta = StartGetZoneData(m, &q->qname, ZoneServiceDNSPush, DNSPushNotificationGotZoneData, q); - return; + dso_drop(server->connection); + server->connection = NULL; } - else if (!q->nta->Host.c[0]) + if (server->connectInfo) { - // This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname - LogMsg("SubscribeToDNSPushNotificationServer: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived); + dso_connect_state_drop(server->connectInfo); } - q->tcp = GetTCPConnectionToPushServer(m,q); - // If TCP failed (transient networking glitch) try again in five seconds - q->ThisQInterval = (q->tcp != mDNSNULL) ? q->ThisQInterval = 0 : (mDNSPlatformOneSecond * 5); - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); } - -mDNSexport void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +static void DNSPushServerFree(mDNS *m, DNSPushNotificationServer *server) { - mDNSu8 *end = mDNSNULL; - InitializeDNSMessage(&m->omsg.h, zeroID, SubscribeFlags); - end = putQuestion(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - if (!end) + DNSPushNotificationServer **sp; + DNSPushServerDrop(server); + + sp = &m->DNSPushServers; + while (*sp) { - LogMsg("ERROR: SubscribeToDNSPushNotificationServer putQuestion failed"); - return; + if (*sp == server) + { + *sp = server->next; + break; + } + else + { + sp = &server->next; + } } + mDNSPlatformMemFree(server); +} - mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->dnsPushServerAddr, q->dnsPushServerPort, q->tcp->sock, mDNSNULL, mDNSfalse); +static void DNSPushDSOCallback(void *context, const void *event_context, + dso_state_t *dso, dso_event_type_t eventType) +{ + const DNSMessage *message; + DNSPushNotificationServer *server = context; + dso_activity_t *activity; + const dso_query_receive_context_t *receive_context; + const dso_disconnect_context_t *disconnect_context; + const dso_keepalive_context_t *keepalive_context; + DNSQuestion *q; + uint16_t rcode; + mDNSs32 reconnect_when = 0; + mDNS *m = server->m; - // update question state - q->dnsPushState = DNSPUSH_ESTABLISHED; - q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); - q->LastQTime = m->timenow; - SetNextQueryTime(m, q); + mDNS_CheckLock(m); + + switch(eventType) + { + case kDSOEventType_DNSMessage: + // We shouldn't get here because we won't use this connection for DNS messages. + message = event_context; + LogMsg("DNSPushDSOCallback: DNS Message (opcode=%d) received from %##s", + (message->h.flags.b[0] & kDNSFlag0_OP_Mask) >> 3, &server->serverName); + break; + + case kDSOEventType_DNSResponse: + // We shouldn't get here because we already handled any DNS messages + message = event_context; + LogMsg("DNSPushDSOCallback: DNS Response (opcode=%d) received from %##s", + (message->h.flags.b[0] & kDNSFlag0_OP_Mask) >> 3, &server->serverName); + break; + + case kDSOEventType_DSOMessage: + message = event_context; + if (dso->primary.opcode == kDSOType_DNSPushUpdate) { + DNSPushProcessResponses(server->m, message, dso->primary.payload, + dso->primary.payload + dso->primary.length, server); + } else { + dso_send_not_implemented(dso, &message->h); + LogMsg("DNSPushDSOCallback: Unknown DSO Message (Primary TLV=%d) received from %##s", + dso->primary.opcode, &server->serverName); + } + break; + + case kDSOEventType_DSOResponse: + receive_context = event_context; + q = receive_context->query_context; + rcode = receive_context->rcode; + if (q) { + // If we got an error on a subscribe, we need to evaluate what went wrong + if (rcode == kDNSFlag1_RC_NoErr) { + LogMsg("DNSPushDSOCallback: Subscription for %##s/%d/%d succeeded.", q->qname.c, q->qtype, q->qclass); + q->state = LLQ_DNSPush_Established; + server->connectState = DNSPushServerSessionEstablished; + } else { + // Don't use this server. + q->dnsPushServer->connectState = DNSPushServerNoDNSPush; + q->state = LLQ_Poll; + q->ThisQInterval = 0; + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + LogMsg("DNSPushDSOCallback: Subscription for %##s/%d/%d failed.", q->qname.c, q->qtype, q->qclass); + } + } else { + LogMsg("DNSPushDSOCallback: DSO Response (Primary TLV=%d) (RCODE=%d) (no query) received from %##s", + dso->primary.opcode, receive_context->rcode, &server->serverName); + server->connectState = DNSPushServerSessionEstablished; + } + break; + + case kDSOEventType_Finalize: + LogMsg("DNSPushDSOCallback: Finalize"); + break; + + case kDSOEventType_Connected: + LogMsg("DNSPushDSOCallback: Connected to %##s", &server->serverName); + server->connectState = DNSPushServerConnected; + for (activity = dso->activities; activity; activity = activity->next) { + DNSPushNotificationSendSubscriptionChange(mDNStrue, dso, activity->context); + } + break; + + case kDSOEventType_ConnectFailed: + DNSPushStop(m, server); + LogMsg("DNSPushDSOCallback: Connection to %##s failed", &server->serverName); + break; + + case kDSOEventType_Disconnected: + disconnect_context = event_context; + + // If a network glitch broke the connection, try to reconnect immediately. But if this happens + // twice, don't just blindly reconnect. + if (disconnect_context->reconnect_delay == 0) { + if ((server->lastDisconnect + 90 * mDNSPlatformOneSecond) - m->timenow > 0) { + reconnect_when = 3600000; // If we get two disconnects in quick succession, wait an hour before trying again. + } else { + DNSPushStartConnecting(server); + LogMsg("DNSPushDSOCallback: Connection to %##s disconnected, trying immediate reconnect", + &server->serverName); + } + } else { + reconnect_when = disconnect_context->reconnect_delay; + } + if (reconnect_when != 0) { + LogMsg("DNSPushDSOCallback: Holding server %##s out as not reconnectable for %lf seconds", + &server->serverName, 1000.0 * (reconnect_when - m->timenow) / (double)mDNSPlatformOneSecond); + dso_schedule_reconnect(m, server->connectInfo, reconnect_when); + } + server->lastDisconnect = m->timenow; + server->connection = mDNSNULL; + break; + + // We don't reconnect unless there is demand. The reason we have this event is so that we can + // leave the DNSPushNotificationServer data structure around to _prevent_ attempts to reconnect + // before the reconnect delay interval has expired. When we get this call, we just free up the + // server. + case kDSOEventType_ShouldReconnect: + // This should be unnecessary, but it would be bad to accidentally have a question pointing at + // a server that had been freed, so make sure we don't. + LogMsg("DNSPushDSOCallback: ShouldReconnect timer for %##s fired, disposing of it.", &server->serverName); + DNSPushStop(m, server); + DNSPushServerFree(m, server); + break; + + case kDSOEventType_Keepalive: + LogMsg("DNSPushDSOCallback: Keepalive timer for %##s fired.", &server->serverName); + keepalive_context = event_context; + DNSPushSendKeepalive(server, keepalive_context->inactivity_timeout, keepalive_context->keepalive_interval); + break; + + case kDSOEventType_KeepaliveRcvd: + LogMsg("DNSPushDSOCallback: Keepalive message received from %##s.", &server->serverName); + break; + + case kDSOEventType_Inactive: + // The set of activities went to zero, and we set the idle timeout. And it expired without any + // new activities starting. So we can disconnect. + LogMsg("DNSPushDSOCallback: Inactivity timer for %##s fired, disposing of it.", &server->serverName); + DNSPushStop(m, server); + DNSPushServerFree(m, server); + break; + case kDSOEventType_RetryDelay: + disconnect_context = event_context; + DNSPushStop(m, server); + dso_schedule_reconnect(m, server->connectInfo, disconnect_context->reconnect_delay); + break; + } } -mDNSlocal void reconcileDNSPushConnection(mDNS *m, DNSQuestion *q) +DNSPushNotificationServer *GetConnectionToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) { DNSPushNotificationZone *zone; DNSPushNotificationServer *server; - DNSPushNotificationServer *nextServer; - DNSPushNotificationZone *nextZone; + DNSPushNotificationZone *newZone; + DNSPushNotificationServer *newServer; + char name[MAX_ESCAPED_DOMAIN_NAME]; - // Update the counts + // If we already have a question for this zone and if the server is the same, reuse it for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next) { - if (SameDomainName(&zone->zoneName, &q->nta->ChildName)) + LogMsg("GetConnectionToDNSPushNotificationServer: zone compare zone %##s question %##s", &zone->zoneName, &q->nta->ChildName); + if (SameDomainName(&q->nta->ChildName, &zone->zoneName)) { - zone->numberOfQuestions--; - for (server = zone->servers; server != mDNSNULL; server = server->next) - { - if (mDNSSameAddress(&server->serverAddr, &q->dnsPushServerAddr)) - server->numberOfQuestions--; + DNSPushNotificationServer *zoneServer = mDNSNULL; + zoneServer = zone->server; + if (zoneServer != mDNSNULL) { + LogMsg("GetConnectionToDNSPushNotificationServer: server compare server %##s question %##s", + &zoneServer->serverName, &q->nta->Host); + if (SameDomainName(&q->nta->Host, &zoneServer->serverName)) + { + LogMsg("GetConnectionToDNSPushNotificationServer: server and zone already present."); + zone->numberOfQuestions++; + zoneServer->numberOfQuestions++; + return zoneServer; + } } } } - // Now prune the lists - server = m->DNSPushServers; - nextServer = mDNSNULL; - while(server != mDNSNULL) + // If we have a connection to this server but it is for a differnt zone, create a new zone entry and reuse the connection + for (server = m->DNSPushServers; server != mDNSNULL; server = server->next) { - nextServer = server->next; - if (server->numberOfQuestions <= 0) + LogMsg("GetConnectionToDNSPushNotificationServer: server compare server %##s question %##s", + &server->serverName, &q->nta->Host); + if (SameDomainName(&q->nta->Host, &server->serverName)) { - DisposeTCPConn(server->connection); - if (server == m->DNSPushServers) - m->DNSPushServers = nextServer; - mDNSPlatformMemFree(server); - server = nextServer; + newZone = (DNSPushNotificationZone *) mDNSPlatformMemAllocateClear(sizeof(*newZone)); + if (newZone == NULL) + { + return NULL; + } + newZone->numberOfQuestions = 1; + newZone->zoneName = q->nta->ChildName; + newZone->server = server; + + // Add the new zone to the begining of the list + newZone->next = m->DNSPushZones; + m->DNSPushZones = newZone; + + server->numberOfQuestions++; + LogMsg("GetConnectionToDNSPushNotificationServer: server already present."); + return server; } - else server = server->next; } - zone = m->DNSPushZones; - nextZone = mDNSNULL; - while(zone != mDNSNULL) + // If we do not have any existing connections, create a new connection + newServer = (DNSPushNotificationServer *) mDNSPlatformMemAllocateClear(sizeof(*newServer)); + if (newServer == NULL) { - nextZone = zone->next; - if (zone->numberOfQuestions <= 0) - { - if (zone == m->DNSPushZones) - m->DNSPushZones = nextZone; - mDNSPlatformMemFree(zone); - zone = nextZone; - } - else zone = zone->next; + return NULL; + } + newZone = (DNSPushNotificationZone *) mDNSPlatformMemAllocateClear(sizeof(*newZone)); + if (newZone == NULL) + { + mDNSPlatformMemFree(newServer); + return NULL; } + newServer->m = m; + newServer->numberOfQuestions = 1; + AssignDomainName(&newServer->serverName, &q->nta->Host); + newServer->port = q->nta->Port; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_replace(&newServer->dnsservice, q->dnsservice); +#else + newServer->qDNSServer = q->qDNSServer; +#endif + ConvertDomainNameToCString(&newServer->serverName, name); + newServer->connection = dso_create(mDNSfalse, 10, name, DNSPushDSOCallback, newServer, NULL); + if (newServer->connection == NULL) + { + mDNSPlatformMemFree(newServer); + mDNSPlatformMemFree(newZone); + return NULL; + } + newServer->connectInfo = dso_connect_state_create(name, mDNSNULL, newServer->port, 10, + AbsoluteMaxDNSMessageData, AbsoluteMaxDNSMessageData, + DNSPushDSOCallback, newServer->connection, newServer, "GetDSOConnectionToPushServer"); + if (newServer->connectInfo) + { + dso_connect_state_use_tls(newServer->connectInfo); + DNSPushStartConnecting(newServer); + } + else + { + newServer->connectState = DNSPushServerConnectFailed; + } + newZone->numberOfQuestions = 1; + newZone->zoneName = q->nta->ChildName; + newZone->server = newServer; + + // Add the new zone to the begining of the list + newZone->next = m->DNSPushZones; + m->DNSPushZones = newZone; + + newServer->next = m->DNSPushServers; + m->DNSPushServers = newServer; + LogMsg("GetConnectionToDNSPushNotificationServer: allocated new server."); + + return newServer; } -mDNSexport void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +DNSPushNotificationServer *SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) { - mDNSu8 *end = mDNSNULL; - InitializeDNSMessage(&m->omsg.h, q->TargetQID, UnSubscribeFlags); - end = putQuestion(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - if (!end) + DNSPushNotificationServer *server = GetConnectionToDNSPushNotificationServer(m, q); + char name[MAX_ESCAPED_DOMAIN_NAME + 9]; // type(hex)+class(hex)+name + dso_activity_t *activity; + if (server == mDNSNULL) return server; + + // Now we have a connection to a push notification server. It may be pending, or it may be active, + // but either way we can add a DNS Push subscription to the server object. + mDNS_snprintf(name, sizeof name, "%04x%04x", q->qtype, q->qclass); + ConvertDomainNameToCString(&q->qname, &name[8]); + activity = dso_add_activity(server->connection, name, kDNSPushActivity_Subscription, q, mDNSNULL); + if (activity == mDNSNULL) { - LogMsg("ERROR: UnSubscribeToDNSPushNotificationServer - putQuestion failed"); - return; + LogInfo("SubscribeToDNSPushNotificationServer: failed to add question %##s", &q->qname); + return mDNSNULL; } + // If we're already connected, send the subscribe request immediately. + if (server->connectState == DNSPushServerConnected || server->connectState == DNSPushServerSessionEstablished) + { + DNSPushNotificationSendSubscriptionChange(mDNStrue, server->connection, q); + } + return server; +} - mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->dnsPushServerAddr, q->dnsPushServerPort, q->tcp->sock, mDNSNULL, mDNSfalse); +mDNSexport void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + LogInfo("DiscoverDNSPushNotificationServer: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + if (q->nta) CancelGetZoneData(m, q->nta); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceDNSPush, DNSPushNotificationGotZoneData, q); + q->state = LLQ_DNSPush_ServerDiscovery; +} - reconcileDNSPushConnection(m, q); +mDNSexport void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + dso_activity_t *activity; + + if (q->dnsPushServer != mDNSNULL) + { + if (q->dnsPushServer->connection != mDNSNULL) + { + if (q->dnsPushServer->connectState == DNSPushServerSessionEstablished || + q->dnsPushServer->connectState == DNSPushServerConnected) + { + // Ignore any response we get to a pending subscribe. + dso_ignore_response(q->dnsPushServer->connection, q); + DNSPushNotificationSendSubscriptionChange(mDNSfalse, q->dnsPushServer->connection, q); + } + // activities linger even if we are not connected. + activity = dso_find_activity(q->dnsPushServer->connection, mDNSNULL, kDNSPushActivity_Subscription, q); + if (activity != mDNSNULL) { + dso_drop_activity(q->dnsPushServer->connection, activity); + } + } + DNSPushReconcileConnection(m, q); + } + // We let the DSO Idle mechanism clean up the connection to the server. } +#endif // MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) -#endif // DNS_PUSH_ENABLED #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #endif @@ -6169,7 +6406,7 @@ mDNSexport void RetrySearchDomainQuestions(mDNS *const m) (void) m; } -mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel) +mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port) { (void) m; (void) info; @@ -6178,7 +6415,6 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const (void) b64keydata; (void) hostname; (void) port; - (void) autoTunnel; return mStatus_UnsupportedErr; } @@ -6217,8 +6453,8 @@ mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traver } mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr, - const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSBool isExpensive, mDNSu16 resGroupID, - mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO) + const mDNSIPPort port, ScopeType scopeType, mDNSu32 timeout, mDNSBool isCell, mDNSBool isExpensive, mDNSBool isConstrained, mDNSBool isCLAT46, + mDNSu32 resGroupID, mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO) { (void) m; (void) d; @@ -6226,10 +6462,12 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (void) serviceID; (void) addr; (void) port; - (void) scoped; + (void) scopeType; (void) timeout; - (void) cellIntf; + (void) isCell; (void) isExpensive; + (void) isCLAT46; + (void) isConstrained; (void) resGroupID; (void) reqA; (void) reqAAAA; @@ -6308,3 +6546,13 @@ mDNSexport void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q) } #endif // !UNICAST_DISABLED + + +// Local Variables: +// mode: C +// tab-width: 4 +// c-file-style: "bsd" +// c-basic-offset: 4 +// fill-column: 108 +// indent-tabs-mode: nil +// End: diff --git a/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.h b/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.h index 0a0622784d..bb5f0c07c9 100755 --- a/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.h +++ b/usr/src/contrib/mDNSResponder/mDNSCore/uDNS.h @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. +/* + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +19,13 @@ #include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" +#include <sys/types.h> +#include "dns_sd.h" + +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) +#include "dso.h" +#include "dso-transport.h" +#endif #ifdef __cplusplus extern "C" { @@ -32,7 +38,6 @@ extern "C" { //#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond) #define LLQ_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc. #define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request -#define MAX_DNSSEC_UNANSWERED_QUERIES 1 // number of unanswered queries from any one uDNS server before turning off DNSSEC Validation #define MAX_UCAST_UNANSWERED_QUERIES 2 // number of unanswered queries from any one uDNS server before trying another server #define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server @@ -66,14 +71,34 @@ extern "C" { // then use the default value of 30 seconds #define DEFAULT_UDNS_TIMEOUT 30 // in seconds -// For questions that are validating responses (q->ValidatingResponse == 1), use 10 seconds -// which accomodates two DNS servers and two queries per DNS server. -#define DEFAULT_UDNSSEC_TIMEOUT 10 // in seconds +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) +// Push notification structures +struct mDNS_DNSPushNotificationServer +{ + dso_connect_state_t *connectInfo; // DSO Connection state information + dso_state_t *connection; // DNS Stateful Operations/TCP Connection pointer, might be null. + mDNSu32 numberOfQuestions; // Number of questions for this server + DNSPushServer_ConnectState connectState; // Current status of connection attempt to this server + mDNSs32 lastDisconnect; // Last time we got a disconnect, used to avoid constant reconnects + domainname serverName; // The hostname returned by the _dns-push-tls._tcp.<zone> SRV lookup + mDNSIPPort port; // The port from the SRV lookup +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_dns_service_t dnsservice; +#else + DNSServer *qDNSServer; // DNS server stolen from the question that created this server structure. +#endif + mDNS *m; + DNSPushNotificationServer *next; +} ; -// If we are sending queries with EDNS0/DO option and we have no indications that the server -// is DNSSEC aware and we have already reached MAX_DNSSEC_RETRANSMISSIONS, we disable -// validation (for optional case only) for any questions that uses this server -#define MAX_DNSSEC_RETRANSMISSIONS 3 +struct mDNS_DNSPushNotificationZone +{ + domainname zoneName; + DNSPushNotificationServer *server; // DNS Push Notification Servers for this zone + mDNSu32 numberOfQuestions; // Number of questions for this zone + DNSPushNotificationZone *next; +} ; +#endif // Entry points into unicast-specific routines @@ -81,10 +106,15 @@ extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) extern void startLLQHandshake(mDNS *m, DNSQuestion *q); extern void sendLLQRefresh(mDNS *m, DNSQuestion *q); +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) extern void DNSPushNotificationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo); extern void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q); -extern void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); +extern DNSPushNotificationServer *GetConnectionToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); +extern DNSPushNotificationServer *SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); extern void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); +extern void DNSPushReconcileConnection(mDNS *m, DNSQuestion *q); +extern void DNSPushServerDrop(DNSPushNotificationServer *server); +#endif extern void SleepRecordRegistrations(mDNS *m); @@ -106,7 +136,6 @@ extern mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo * extern void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData); extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr); extern const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr); -extern void uDNS_CheckCurrentQuestion(mDNS *const m); // integer fields of msg header must be in HOST byte order before calling this routine extern void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, @@ -128,7 +157,7 @@ extern mStatus uDNS_SetupDNSConfig(mDNS *const m); extern void uDNS_SetupWABQueries(mDNS *const m); extern void uDNS_StartWABQueries(mDNS *const m, int queryType); extern void uDNS_StopWABQueries(mDNS *const m, int queryType); -extern domainname *uDNS_GetNextSearchDomain(mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal); +extern domainname *uDNS_GetNextSearchDomain(mDNSInterfaceID InterfaceID, int *searchIndex, mDNSBool ignoreDotLocal); extern void uDNS_RestartQuestionAsTCP(mDNS *m, DNSQuestion *const q, const mDNSAddr *const srcaddr, const mDNSIPPort srcport); @@ -150,10 +179,14 @@ extern void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mD extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr); extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol); +#if MDNSRESPONDER_SUPPORTS(COMMON, DNS_PUSH) // DNS Push Notification extern void SubscribeToDNSPushNotification(mDNS *m, DNSQuestion *q); +#endif - +extern CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage *const response, uDNS_LLQType LLQType, + const mDNSu32 slot, CacheGroup *cg, + CacheRecord ***cfp, mDNSInterfaceID InterfaceID); #ifdef __cplusplus } #endif diff --git a/usr/src/contrib/mDNSResponder/mDNSPosix/PosixDaemon.c b/usr/src/contrib/mDNSResponder/mDNSPosix/PosixDaemon.c index dd932949e7..60a6727884 100644 --- a/usr/src/contrib/mDNSResponder/mDNSPosix/PosixDaemon.c +++ b/usr/src/contrib/mDNSResponder/mDNSPosix/PosixDaemon.c @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. +/* + * Copyright (c) 2003-2019 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +36,7 @@ #include <fcntl.h> #include <pwd.h> #include <sys/types.h> +#include <sys/socket.h> #if __APPLE__ #undef daemon @@ -48,6 +48,7 @@ extern int daemon(int, int); #include "mDNSUNP.h" // For daemon() #include "uds_daemon.h" #include "PlatformCommon.h" +#include "posix_utilities.h" // For getLocalTimestamp() #ifndef MDNSD_USER #define MDNSD_USER "nobody" @@ -135,9 +136,19 @@ mDNSlocal void ToggleLogPacket(void) // Dump a little log of what we've been up to. mDNSlocal void DumpStateLog() { - LogMsg("---- BEGIN STATE LOG ----"); - udsserver_info(); - LogMsg("---- END STATE LOG ----"); + char timestamp[64]; // 64 is enough to store the UTC timestmp + + mDNSu32 major_version = _DNS_SD_H / 10000; + mDNSu32 minor_version1 = (_DNS_SD_H - major_version * 10000) / 100; + mDNSu32 minor_version2 = _DNS_SD_H % 100; + + getLocalTimestamp(timestamp, sizeof(timestamp)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "---- BEGIN STATE LOG ---- (%s mDNSResponder Build %d.%02d.%02d)", timestamp, major_version, minor_version1, minor_version2); + + udsserver_info_dump_to_fd(STDERR_FILENO); + + getLocalTimestamp(timestamp, sizeof(timestamp)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "---- END STATE LOG ---- (%s mDNSResponder Build %d.%02d.%02d)", timestamp, major_version, minor_version1, minor_version2); } mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit. @@ -207,16 +218,21 @@ int main(int argc, char **argv) { const struct passwd *pw = getpwnam(MDNSD_USER); if (pw != NULL) - setuid(pw->pw_uid); + { + if (setgid(pw->pw_gid) < 0) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "WARNING: mdnsd continuing as group root because setgid to \""MDNSD_USER"\" failed with " PUB_S, strerror(errno)); + } + if (setuid(pw->pw_uid) < 0) + { + LogMsg("WARNING: mdnsd continuing as root because setuid to \""MDNSD_USER"\" failed with %s", strerror(errno)); + } + } else -#ifdef MDNSD_NOROOT - { - LogMsg("WARNING: mdnsd exiting because user \""MDNSD_USER"\" does not exist"); - err = mStatus_Invalid; - } -#else + { LogMsg("WARNING: mdnsd continuing as root because user \""MDNSD_USER"\" does not exist"); -#endif + } } if (mStatus_NoError == err) @@ -230,7 +246,7 @@ int main(int argc, char **argv) LogMsg("ExitCallback: udsserver_exit failed"); #if MDNS_DEBUGMSGS > 0 - printf("mDNSResponder exiting normally with %ld\n", err); + printf("mDNSResponder exiting normally with %d\n", err); #endif return err; diff --git a/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.c b/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.c index 50acd2bd4c..45e59a5728 100755 --- a/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.c +++ b/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.c @@ -1,6 +1,6 @@ -/* -*- Mode: C; tab-width: 4 -*- +/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*- * - * Copyright (c) 2002-2015 Apple Inc. All rights reserved. + * Copyright (c) 2002-2019 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +19,8 @@ #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "PlatformCommon.h" #include "dns_sd.h" -#include "dnssec.h" -#include "nsec.h" #include <assert.h> #include <stdio.h> @@ -40,6 +39,7 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <time.h> // platform support for UTC time +#include <ifaddrs.h> #if USES_NETLINK #include <asm/types.h> @@ -57,16 +57,6 @@ // *************************************************************************** // Structures -// We keep a list of client-supplied event sources in PosixEventSource records -struct PosixEventSource -{ - mDNSPosixEventCallback Callback; - void *Context; - int fd; - struct PosixEventSource *Next; -}; -typedef struct PosixEventSource PosixEventSource; - // Context record for interface change callback struct IfChangeRec { @@ -76,9 +66,7 @@ struct IfChangeRec typedef struct IfChangeRec IfChangeRec; // Note that static data is initialized to zero in (modern) C. -static fd_set gEventFDs; -static int gMaxFD; // largest fd in gEventFDs -static GenLinkedList gEventSources; // linked list of PosixEventSource's +static PosixEventSource *gEventSources; // linked list of PosixEventSource's static sigset_t gEventSignalSet; // Signals which event loop listens for static sigset_t gEventSignals; // Signals which were received while inside loop @@ -92,8 +80,22 @@ static int num_pkts_accepted = 0; static int num_pkts_rejected = 0; // *************************************************************************** +// Locals +mDNSlocal void requestReadEvents(PosixEventSource *eventSource, + const char *taskName, mDNSPosixEventCallback callback, void *context); +mDNSlocal mStatus stopReadOrWriteEvents(int fd, mDNSBool freeSource, mDNSBool removeSource, int flags); +mDNSlocal void requestWriteEvents(PosixEventSource *eventSource, + const char *taskName, mDNSPosixEventCallback callback, void *context); +// *************************************************************************** // Functions +#if MDNS_MALLOC_DEBUGGING +mDNSexport void mDNSPlatformValidateLists(void) +{ + // This should validate gEventSources and any other Posix-specific stuff that gets allocated. +} +#endif + int gMDNSPlatformPosixVerboseLevel = 0; #define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr) @@ -215,6 +217,70 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms return PosixErrorToStatus(err); } +mDNSlocal void TCPReadCallback(int fd, void *context) +{ + TCPSocket *sock = context; + (void)fd; + + if (sock->flags & kTCPSocketFlags_UseTLS) + { + // implement + } + else + { + sock->callback(sock, sock->context, mDNSfalse, sock->err); + } +} + +mDNSlocal void tcpConnectCallback(int fd, void *context) +{ + TCPSocket *sock = context; + mDNSBool c = !sock->connected; + int result; + socklen_t len = sizeof result; + + sock->connected = mDNStrue; + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &len) < 0) + { + LogInfo("ERROR: TCPConnectCallback - unable to get connect error: socket %d: Error %d (%s)", + sock->events.fd, result, strerror(result)); + sock->err = mStatus_ConnFailed; + } + else + { + if (result != 0) + { + sock->err = mStatus_ConnFailed; + if (result == EHOSTUNREACH || result == EADDRNOTAVAIL || result == ENETDOWN) + { + LogInfo("ERROR: TCPConnectCallback - connect failed: socket %d: Error %d (%s)", + sock->events.fd, result, strerror(result)); + } + else + { + LogMsg("ERROR: TCPConnectCallback - connect failed: socket %d: Error %d (%s)", + sock->events.fd, result, strerror(result)); + } + } + else + { + // The connection succeeded. + sock->connected = mDNStrue; + // Select for read events. + sock->events.fd = fd; + requestReadEvents(&sock->events, "mDNSPosix::tcpConnectCallback", TCPReadCallback, sock); + } + } + + if (sock->callback) + { + sock->callback(sock, sock->context, c, sock->err); + // Here sock must be assumed to be invalid, in case the callback freed it. + return; + } +} + // This routine is called when the main loop detects that data is available on a socket. mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) { @@ -315,60 +381,314 @@ mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int s &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); } -mDNSexport TCPSocket *mDNSPlatformTCPSocket(TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass) +mDNSexport TCPSocket *mDNSPlatformTCPSocket(TCPSocketFlags flags, mDNSAddr_Type addrType, mDNSIPPort * port, + domainname *hostname, mDNSBool useBackgroundTrafficClass) { - (void)flags; // Unused - (void)port; // Unused - (void)useBackgroundTrafficClass; // Unused - return NULL; + TCPSocket *sock; + int len = sizeof (TCPSocket); + + (void)useBackgroundTrafficClass; + + if (hostname) + { + len += sizeof (domainname); + } + sock = malloc(len); + + if (sock == NULL) + { + LogMsg("mDNSPlatformTCPSocket: no memory for socket"); + return NULL; + } + memset(sock, 0, sizeof *sock); + + if (hostname) + { + sock->hostname = (domainname *)(sock + 1); + LogMsg("mDNSPlatformTCPSocket: hostname %##s", hostname->c); + AssignDomainName(sock->hostname, hostname); + } + + sock->events.fd = -1; + if (!mDNSPosixTCPSocketSetup(&sock->events.fd, addrType, port, &sock->port)) + { + if (sock->events.fd != -1) close(sock->events.fd); + free(sock); + return mDNSNULL; + } + + // Set up the other fields in the structure. + sock->flags = flags; + sock->err = mStatus_NoError; + sock->setup = mDNSfalse; + sock->connected = mDNSfalse; + return sock; } -mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd) +mDNSexport mStatus mDNSPlatformTCPSocketSetCallback(TCPSocket *sock, TCPConnectionCallback callback, void *context) { - (void)flags; // Unused - (void)sd; // Unused - return NULL; + sock->callback = callback; + sock->context = context; + return mStatus_NoError; +} + +mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd) +{ + TCPSocket *sock; + + // XXX Add! + if (flags & kTCPSocketFlags_UseTLS) + { + return mDNSNULL; // not supported yet. + } + + sock = (TCPSocket *) mDNSPlatformMemAllocateClear(sizeof *sock); + if (!sock) + { + return mDNSNULL; + } + + sock->events.fd = fd; + sock->flags = flags; + sock->connected = mDNStrue; + return sock; +} + + +mDNSlocal void tcpListenCallback(int fd, void *context) +{ + TCPListener *listener = context; + TCPSocket *sock; + + sock = mDNSPosixDoTCPListenCallback(fd, listener->addressType, listener->socketFlags, + listener->callback, listener->context); + if (sock != NULL) + { + requestReadEvents(&sock->events, "mDNSPosix::tcpListenCallback", TCPReadCallback, sock); + } +} + +mDNSexport TCPListener *mDNSPlatformTCPListen(mDNSAddr_Type addrType, mDNSIPPort *port, mDNSAddr *addr, + TCPSocketFlags socketFlags, mDNSBool reuseAddr, int queueLength, + TCPAcceptedCallback callback, void *context) +{ + TCPListener *ret; + int fd = -1; + + if (!mDNSPosixTCPListen(&fd, addrType, port, addr, reuseAddr, queueLength)) + { + if (fd != -1) + { + close(fd); + } + return mDNSNULL; + } + + // Allocate a listener structure + ret = (TCPListener *) mDNSPlatformMemAllocateClear(sizeof *ret); + if (ret == NULL) + { + LogMsg("mDNSPlatformTCPListen: no memory for TCPListener struct."); + close(fd); + return mDNSNULL; + } + ret->events.fd = fd; + ret->callback = callback; + ret->context = context; + ret->addressType = addrType; + ret->socketFlags = socketFlags; + + // When we get a connection, mDNSPosixListenCallback will be called, and it will invoke the + // callback we were passed. + requestReadEvents(&ret->events, "tcpListenCallback", tcpListenCallback, ret); + return ret; } mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) { - (void)sock; // Unused - return -1; + return sock->events.fd; } -mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, void *context) +mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, + mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context) { - (void)sock; // Unused - (void)dst; // Unused - (void)dstport; // Unused - (void)hostname; // Unused - (void)InterfaceID; // Unused - (void)callback; // Unused - (void)context; // Unused - return(mStatus_UnsupportedErr); + int result; + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } addr; + socklen_t len; + + sock->callback = callback; + sock->context = context; + sock->setup = mDNSfalse; + sock->connected = mDNSfalse; + sock->err = mStatus_NoError; + + result = fcntl(sock->events.fd, F_GETFL, 0); + if (result < 0) + { + LogMsg("mDNSPlatformTCPConnect: F_GETFL failed: %s", strerror(errno)); + return mStatus_UnknownErr; + } + + result = fcntl(sock->events.fd, F_SETFL, result | O_NONBLOCK); + if (result < 0) + { + LogMsg("mDNSPlatformTCPConnect: F_SETFL failed: %s", strerror(errno)); + return mStatus_UnknownErr; + } + + // If we've been asked to bind to a single interface, do it. See comment in mDNSMacOSX.c for more info. + if (InterfaceID) + { + PosixNetworkInterface *iface = (PosixNetworkInterface *)InterfaceID; +#if defined(SO_BINDTODEVICE) + result = setsockopt(sock->events.fd, + SOL_SOCKET, SO_BINDTODEVICE, iface->intfName, strlen(iface->intfName)); + if (result < 0) + { + LogMsg("mDNSPlatformTCPConnect: SO_BINDTODEVICE failed on %s: %s", iface->intfName, strerror(errno)); + return mStatus_BadParamErr; + } +#else + if (dst->type == mDNSAddrType_IPv4) + { +#if defined(IP_BOUND_IF) + result = setsockopt(sock->events.fd, IPPROTO_IP, IP_BOUND_IF, &iface->index, sizeof iface->index); + if (result < 0) + { + LogMsg("mDNSPlatformTCPConnect: IP_BOUND_IF failed on %s (%d): %s", + iface->intfName, iface->index, strerror(errno)); + return mStatus_BadParamErr; + } +#else + (void)iface; +#endif // IP_BOUND_IF + } + else + { // IPv6 +#if defined(IPV6_BOUND_IF) + result = setsockopt(sock->events.fd, IPPROTO_IPV6, IPV6_BOUND_IF, &iface->index, sizeof iface->index); + if (result < 0) + { + LogMsg("mDNSPlatformTCPConnect: IP_BOUND_IF failed on %s (%d): %s", + iface->intfName, iface->index, strerror(errno)); + return mStatus_BadParamErr; + } +#else + (void)iface; +#endif // IPV6_BOUND_IF + } +#endif // SO_BINDTODEVICE + } + + memset(&addr, 0, sizeof addr); + if (dst->type == mDNSAddrType_IPv4) + { + addr.sa.sa_family = AF_INET; + addr.sin.sin_port = dstport.NotAnInteger; + len = sizeof (struct sockaddr_in); + addr.sin.sin_addr.s_addr = dst->ip.v4.NotAnInteger; + } + else + { + addr.sa.sa_family = AF_INET6; + len = sizeof (struct sockaddr_in6); + addr.sin6.sin6_port = dstport.NotAnInteger; + memcpy(&addr.sin6.sin6_addr.s6_addr, &dst->ip.v6, sizeof addr.sin6.sin6_addr.s6_addr); + } +#ifndef NOT_HAVE_SA_LEN + addr.sa.sa_len = len; +#endif + + result = connect(sock->events.fd, (struct sockaddr *)&addr, len); + if (result < 0) + { + if (errno == EINPROGRESS) + { + requestWriteEvents(&sock->events, "mDNSPlatformConnect", tcpConnectCallback, sock); + return mStatus_ConnPending; + } + if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) + { + LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", + sock->events.fd, errno, strerror(errno)); + } + else + { + LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", + sock->events.fd, errno, strerror(errno), len); + } + return mStatus_ConnFailed; + } + + LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously"); + return mStatus_NoError; } mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) { - (void)sock; // Unused + if (sock) + { // can sock really be NULL when this is called? + shutdown(sock->events.fd, SHUT_RDWR); + stopReadOrWriteEvents(sock->events.fd, mDNSfalse, mDNStrue, + PosixEventFlag_Read | PosixEventFlag_Write); + close(sock->events.fd); + free(sock); + } } mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool * closed) { - (void)sock; // Unused - (void)buf; // Unused - (void)buflen; // Unused - (void)closed; // Unused - return 0; + ssize_t nread; + + *closed = mDNSfalse; + if (sock->flags & kTCPSocketFlags_UseTLS) + { + // Implement... + nread = -1; + *closed = mDNStrue; + } else { + nread = mDNSPosixReadTCP(sock->events.fd, buf, buflen, closed); + } + return nread; +} + +mDNSexport mDNSBool mDNSPlatformTCPWritable(TCPSocket *sock) +{ + fd_set w = { 0 }; + int nfds = sock->events.fd + 1; + int count; + struct timeval tv; + + if (nfds > FD_SETSIZE) + { + LogMsg("ERROR: mDNSPlatformTCPWritable called on an fd that won't fit in an fd_set."); + return mDNStrue; // hope for the best? + } + FD_SET(sock->events.fd, &w); + tv.tv_sec = tv.tv_usec = 0; + count = select(nfds, NULL, &w, NULL, &tv); + if (count > 0) + { + return mDNStrue; + } + return mDNSfalse; } mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) { - (void)sock; // Unused - (void)msg; // Unused - (void)len; // Unused - return 0; + if (sock->flags & kTCPSocketFlags_UseTLS) + { + // implement + return -1; + } + else + { + return mDNSPosixWriteTCP(sock->events.fd, msg, len); + } } mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNSIPPort port) @@ -437,12 +757,13 @@ mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNSBool setservers, mDNSBool setse DNameListElem **BrowseDomains, mDNSBool ackConfig) { (void) setservers; - if (fqdn) fqdn->c[0] = 0; (void) setsearch; - if (RegDomains) *RegDomains = NULL; - if (BrowseDomains) *BrowseDomains = NULL; (void) ackConfig; + if (fqdn ) fqdn->c[0] = 0; + if (RegDomains ) *RegDomains = NULL; + if (BrowseDomains) *BrowseDomains = NULL; + return mDNStrue; } @@ -502,11 +823,11 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) mDNSAddr DNSAddr; DNSAddr.type = mDNSAddrType_IPv4; DNSAddr.ip.v4.NotAnInteger = ina.s_addr; - mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, 0, &DNSAddr, UnicastDNSPort, kScopeNone, 0, mDNSfalse, mDNSfalse, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse); + mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, 0, &DNSAddr, UnicastDNSPort, kScopeNone, 0, mDNSfalse, mDNSfalse, mDNSfalse, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse); numOfServers++; } } - fclose(fp); + fclose(fp); return (numOfServers > 0) ? 0 : -1; } @@ -639,7 +960,7 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf // ... with a shared UDP port, if it's for multicast receiving if (err == 0 && port.NotAnInteger) { - // <rdar://problem/20946253> + // <rdar://problem/20946253> Suggestions from Jonny Törnbom at Axis Communications // We test for SO_REUSEADDR first, as suggested by Jonny Törnbom from Axis Communications // Linux kernel versions 3.9 introduces support for socket option // SO_REUSEPORT, however this is not implemented the same as on *BSD @@ -660,12 +981,15 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf #endif if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } +#if TARGET_OS_MAC // Enable inbound packets on IFEF_AWDL interface. // Only done for multicast sockets, since we don't expect unicast socket operations // on the IFEF_AWDL interface. Operation is a no-op for other interface types. - #ifdef SO_RECV_ANYIF + #ifndef SO_RECV_ANYIF + #define SO_RECV_ANYIF 0x1104 /* unrestricted inbound processing */ + #endif if (setsockopt(*sktPtr, SOL_SOCKET, SO_RECV_ANYIF, &kOn, sizeof(kOn)) < 0) perror("setsockopt - SO_RECV_ANYIF"); - #endif +#endif } // We want to receive destination addresses and interface identifiers. @@ -890,8 +1214,26 @@ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct // And make a copy of the intfName. if (err == 0) { +#ifdef LINUX + char *s; + int len; + s = strchr(intfName, ':'); + if (s != NULL) + { + len = (s - intfName) + 1; + } + else + { + len = strlen(intfName) + 1; + } + intf->intfName = malloc(len); + if (intf->intfName == NULL) { assert(0); err = ENOMEM; } + memcpy(intf->intfName, intfName, len - 1); + intfName[len - 1] = 0; +#else intf->intfName = strdup(intfName); if (intf->intfName == NULL) { assert(0); err = ENOMEM; } +#endif } if (err == 0) @@ -903,6 +1245,7 @@ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct //LogMsg("SetupOneInterface: %#a %#a", &intf->coreIntf.ip, &intf->coreIntf.mask); strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname)); intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0; + intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; intf->coreIntf.McastTxRx = mDNStrue; @@ -970,47 +1313,56 @@ mDNSlocal int SetupInterfaceList(mDNS *const m) { mDNSBool foundav4 = mDNSfalse; int err = 0; - struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue); - struct ifi_info *firstLoopback = NULL; + struct ifaddrs *intfList; + struct ifaddrs *firstLoopback = NULL; + int firstLoopbackIndex = 0; assert(m != NULL); debugf("SetupInterfaceList"); - if (intfList == NULL) err = ENOENT; - -#if HAVE_IPV6 - if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ + if (getifaddrs(&intfList) < 0) { - struct ifi_info **p = &intfList; - while (*p) p = &(*p)->ifi_next; - *p = get_ifi_info(AF_INET6, mDNStrue); + err = errno; } -#endif + if (intfList == NULL) err = ENOENT; if (err == 0) { - struct ifi_info *i = intfList; + struct ifaddrs *i = intfList; while (i) { - if ( ((i->ifi_addr->sa_family == AF_INET) + if ( i->ifa_addr != NULL && + ((i->ifa_addr->sa_family == AF_INET) #if HAVE_IPV6 - || (i->ifi_addr->sa_family == AF_INET6) + || (i->ifa_addr->sa_family == AF_INET6) #endif - ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT)) + ) && (i->ifa_flags & IFF_UP) && !(i->ifa_flags & IFF_POINTOPOINT)) { - if (i->ifi_flags & IFF_LOOPBACK) + int ifIndex = if_nametoindex(i->ifa_name); + if (ifIndex == 0) + { + continue; + } + if (i->ifa_flags & IFF_LOOPBACK) { if (firstLoopback == NULL) + { firstLoopback = i; + firstLoopbackIndex = ifIndex; + } } else { - if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0) - if (i->ifi_addr->sa_family == AF_INET) + if (SetupOneInterface(m, i->ifa_addr, i->ifa_netmask, i->ifa_name, ifIndex) == 0) + { + if (i->ifa_addr->sa_family == AF_INET) + { foundav4 = mDNStrue; + } + } } } - i = i->ifi_next; + i = i->ifa_next; } // If we found no normal interfaces but we did find a loopback interface, register the @@ -1019,11 +1371,14 @@ mDNSlocal int SetupInterfaceList(mDNS *const m) // In the interim, we skip loopback interface only if we found at least one v4 interface to use // if ((m->HostInterfaces == NULL) && (firstLoopback != NULL)) if (!foundav4 && firstLoopback) - (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index); + { + (void)SetupOneInterface(m, firstLoopback->ifa_addr, firstLoopback->ifa_netmask, firstLoopback->ifa_name, + firstLoopbackIndex); + } } // Clean up. - if (intfList != NULL) free_ifi_info(intfList); + if (intfList != NULL) freeifaddrs(intfList); // Clean up any interfaces that have been hanging around on the RecentInterfaces list for more than a minute PosixNetworkInterface **ri = &gRecentInterfaces; @@ -1229,7 +1584,7 @@ mDNSlocal mDNSu32 ProcessRoutingNotification(int sd) #endif // USES_NETLINK // Called when data appears on interface change notification socket -mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context) +mDNSlocal void InterfaceChangeCallback(int fd, void *context) { IfChangeRec *pChgRec = (IfChangeRec*) context; fd_set readFDs; @@ -1237,7 +1592,6 @@ mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context) struct timeval zeroTimeout = { 0, 0 }; (void)fd; // Unused - (void)filter; // Unused FD_ZERO(&readFDs); FD_SET(pChgRec->NotifySD, &readFDs); @@ -1261,7 +1615,7 @@ mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m) mStatus err; IfChangeRec *pChgRec; - pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate(sizeof *pChgRec); + pChgRec = (IfChangeRec*) mDNSPlatformMemAllocateClear(sizeof *pChgRec); if (pChgRec == NULL) return mStatus_NoMemoryErr; @@ -1269,6 +1623,8 @@ mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m) err = OpenIfNotifySocket(&pChgRec->NotifySD); if (err == 0) err = mDNSPosixAddFDToEventLoop(pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); + if (err) + mDNSPlatformMemFree(pChgRec); return err; } @@ -1412,13 +1768,6 @@ mDNSexport void mDNSPlatformUnlock (const mDNS *const m) #pragma mark ***** Strings #endif -// mDNS core calls this routine to copy C strings. -// On the Posix platform this maps directly to the ANSI C strcpy. -mDNSexport void mDNSPlatformStrCopy(void *dst, const void *src) -{ - strcpy((char *)dst, (const char *)src); -} - mDNSexport mDNSu32 mDNSPlatformStrLCopy(void *dst, const void *src, mDNSu32 len) { #if HAVE_STRLCPY @@ -1474,31 +1823,6 @@ mDNSexport void mDNSPlatformQsort(void *base, int nel, int width, int (*compar)( (void)qsort(base, nel, width, compar); } -// DNSSEC stub functions -mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) -{ - (void)m; - (void)dv; - (void)q; -} - -mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode) -{ - (void)m; - (void)crlist; - (void)negcr; - (void)rcode; - return mDNSfalse; -} - -mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) -{ - (void)m; - (void)action; - (void)type; - (void)value; -} - // Proxy stub functions mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) { @@ -1523,18 +1847,21 @@ mDNSexport void DNSProxyTerminate(void) // mDNS core calls this routine to clear blocks of memory. // On the Posix platform this is a simple wrapper around ANSI C memset. -mDNSexport void mDNSPlatformMemZero(void *dst, mDNSu32 len) +mDNSexport void mDNSPlatformMemZero(void *dst, mDNSu32 len) { memset(dst, 0, len); } -mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); } -mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); } +#if !MDNS_MALLOC_DEBUGGING +mDNSexport void *mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } +mDNSexport void *mDNSPlatformMemAllocateClear(mDNSu32 len) { return(callocL(name, len)); } +mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } +#endif #if _PLATFORM_HAS_STRONG_PRNG_ mDNSexport mDNSu32 mDNSPlatformRandomNumber(void) { - return(arc4random()); + return(arc4random()); } #else mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) @@ -1557,15 +1884,18 @@ mDNSexport mStatus mDNSPlatformTimeInit(void) mDNSexport mDNSs32 mDNSPlatformRawTime() { - struct timeval tv; - gettimeofday(&tv, NULL); - // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) - // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) - // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result - // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. + struct timespec tm; + int ret = clock_gettime(CLOCK_MONOTONIC, &tm); + assert(ret == 0); // This call will only fail if the number of seconds does not fit in an object of type time_t. + + // tm.tv_sec is seconds since some unspecified starting point (it is usually the system start up time) + // tm.tv_nsec is nanoseconds since the start of this second (i.e. values 0 to 999999999) + // We use the lower 22 bits of tm.tv_sec for the top 22 bits of our result + // and we multiply tm.tv_nsec by 2 / 1953125 to get a value in the range 0-1023 to go in the bottom 10 bits. // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). - return((tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625)); + + return ((tm.tv_sec << 10) | (tm.tv_nsec * 2 / 1953125)); } mDNSexport mDNSs32 mDNSPlatformUTC(void) @@ -1687,29 +2017,48 @@ mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) FD_SET(s, readfds); } -mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout) +mDNSexport void mDNSPosixGetFDSetForSelect(mDNS *m, int *nfds, fd_set *readfds, fd_set *writefds) { - mDNSs32 ticks; - struct timeval interval; - - // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do - mDNSs32 nextevent = mDNS_Execute(m); + int numFDs = *nfds; + PosixEventSource *iSource; // 2. Build our list of active file descriptors PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); - if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4); + if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(&numFDs, readfds, m->p->unicastSocket4); #if HAVE_IPV6 - if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); + if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(&numFDs, readfds, m->p->unicastSocket6); #endif while (info) { - if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); + if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(&numFDs, readfds, info->multicastSocket4); #if HAVE_IPV6 - if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); + if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(&numFDs, readfds, info->multicastSocket6); #endif info = (PosixNetworkInterface *)(info->coreIntf.next); } + // Copy over the event fds. We have to do it this way because client-provided event loops expect + // to initialize their FD sets first and then call mDNSPosixGetFDSet() + for (iSource = gEventSources; iSource; iSource = iSource->next) + { + if (iSource->readCallback != NULL) + FD_SET(iSource->fd, readfds); + if (iSource->writeCallback != NULL) + FD_SET(iSource->fd, writefds); + if (numFDs <= iSource->fd) + numFDs = iSource->fd + 1; + } + *nfds = numFDs; +} + +mDNSexport void mDNSPosixGetNextDNSEventTime(mDNS *m, struct timeval *timeout) +{ + mDNSs32 ticks; + struct timeval interval; + + // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do + mDNSs32 nextevent = mDNS_Execute(m); + // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) ticks = nextevent - mDNS_TimeNow(m); if (ticks < 1) ticks = 1; @@ -1722,9 +2071,16 @@ mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct ti *timeout = interval; } -mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) +mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, fd_set *writefds, struct timeval *timeout) +{ + mDNSPosixGetNextDNSEventTime(m, timeout); + mDNSPosixGetFDSetForSelect(m, nfds, readfds, writefds); +} + +mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds, fd_set *writefds) { PosixNetworkInterface *info; + PosixEventSource *iSource; assert(m != NULL); assert(readfds != NULL); info = (PosixNetworkInterface *)(m->HostInterfaces); @@ -1758,67 +2114,151 @@ mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) #endif info = (PosixNetworkInterface *)(info->coreIntf.next); } -} - -// update gMaxFD -mDNSlocal void DetermineMaxEventFD(void) -{ - PosixEventSource *iSource; - gMaxFD = 0; - for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) - if (gMaxFD < iSource->fd) - gMaxFD = iSource->fd; + // Now process routing socket events, discovery relay events and anything else of that ilk. + for (iSource = gEventSources; iSource; iSource = iSource->next) + { + if (iSource->readCallback != NULL && FD_ISSET(iSource->fd, readfds)) + { + iSource->readCallback(iSource->fd, iSource->readContext); + break; // in case callback removed elements from gEventSources + } + else if (iSource->writeCallback != NULL && FD_ISSET(iSource->fd, writefds)) + { + mDNSPosixEventCallback writeCallback = iSource->writeCallback; + // Write events are one-shot: to get another event, the consumer has to put in a new request. + // We reset this before calling the callback just in case the callback requests another write + // callback, or deletes the event context from the list. + iSource->writeCallback = NULL; + writeCallback(iSource->fd, iSource->writeContext); + break; // in case callback removed elements from gEventSources + } + } } -// Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to. -mStatus mDNSPosixAddFDToEventLoop(int fd, mDNSPosixEventCallback callback, void *context) -{ - PosixEventSource *newSource; +mDNSu32 mDNSPlatformEventContextSize = sizeof (PosixEventSource); - if (gEventSources.LinkOffset == 0) - InitLinkedList(&gEventSources, offsetof(PosixEventSource, Next)); +mDNSlocal void requestIOEvents(PosixEventSource *newSource, const char *taskName, + mDNSPosixEventCallback callback, void *context, int flag) +{ + PosixEventSource **epp = &gEventSources; - if (fd >= (int) FD_SETSIZE || fd < 0) - return mStatus_UnsupportedErr; + if (newSource->fd >= (int) FD_SETSIZE || newSource->fd < 0) + { + LogMsg("requestIOEvents called with fd %d > FD_SETSIZE %d.", newSource->fd, FD_SETSIZE); + assert(0); + } if (callback == NULL) - return mStatus_BadParamErr; - - newSource = (PosixEventSource*) malloc(sizeof *newSource); - if (NULL == newSource) - return mStatus_NoMemoryErr; + { + LogMsg("requestIOEvents called no callback.", newSource->fd, FD_SETSIZE); + assert(0); + } - newSource->Callback = callback; - newSource->Context = context; - newSource->fd = fd; + // See if this event context is already on the list; if it is, no need to scan the list. + if (!(newSource->flags & PosixEventFlag_OnList)) + { + while (*epp) + { + // This should never happen. + if (newSource == *epp) + { + LogMsg("Event context marked not on list but is on list."); + assert(0); + } + epp = &(*epp)->next; + } + if (*epp == NULL) + { + *epp = newSource; + newSource->next = NULL; + newSource->flags = PosixEventFlag_OnList; + } + } - AddToTail(&gEventSources, newSource); - FD_SET(fd, &gEventFDs); + if (flag & PosixEventFlag_Read) + { + newSource->readCallback = callback; + newSource->readContext = context; + newSource->flags |= PosixEventFlag_Read; + newSource->readTaskName = taskName; + } + if (flag & PosixEventFlag_Write) + { + newSource->writeCallback = callback; + newSource->writeContext = context; + newSource->flags |= PosixEventFlag_Write; + newSource->writeTaskName = taskName; + } +} - DetermineMaxEventFD(); +mDNSlocal void requestReadEvents(PosixEventSource *eventSource, + const char *taskName, mDNSPosixEventCallback callback, void *context) +{ + requestIOEvents(eventSource, taskName, callback, context, PosixEventFlag_Read); +} - return mStatus_NoError; +mDNSlocal void requestWriteEvents(PosixEventSource *eventSource, + const char *taskName, mDNSPosixEventCallback callback, void *context) +{ + requestIOEvents(eventSource, taskName, callback, context, PosixEventFlag_Write); } // Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to. -mStatus mDNSPosixRemoveFDFromEventLoop(int fd) +mDNSlocal mStatus stopReadOrWriteEvents(int fd, mDNSBool freeContext, mDNSBool removeContext, int flags) { - PosixEventSource *iSource; + PosixEventSource *iSource, **epp = &gEventSources; - for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + while (*epp) { + iSource = *epp; if (fd == iSource->fd) { - FD_CLR(fd, &gEventFDs); - RemoveFromList(&gEventSources, iSource); - free(iSource); - DetermineMaxEventFD(); + if (flags & PosixEventFlag_Read) + { + iSource->readCallback = NULL; + iSource->readContext = NULL; + } + if (flags & PosixEventFlag_Write) + { + iSource->writeCallback = NULL; + iSource->writeContext = NULL; + } + if (iSource->writeCallback == NULL && iSource->readCallback == NULL) + { + if (removeContext || freeContext) + *epp = iSource->next; + if (freeContext) + free(iSource); + } return mStatus_NoError; } + epp = &(*epp)->next; } return mStatus_NoSuchNameErr; } +// Some of the mDNSPosix client code relies on being able to add FDs to the event loop without +// providing storage for the event-related info. mDNSPosixAddFDToEventLoop and +// mDNSPosixRemoveFDFromEventLoop handle the event structure storage automatically. +mStatus mDNSPosixAddFDToEventLoop(int fd, mDNSPosixEventCallback callback, void *context) +{ + PosixEventSource *newSource; + + newSource = (PosixEventSource*) malloc(sizeof *newSource); + if (NULL == newSource) + return mStatus_NoMemoryErr; + memset(newSource, 0, sizeof *newSource); + newSource->fd = fd; + + requestReadEvents(newSource, "mDNSPosixAddFDToEventLoop", callback, context); + return mStatus_NoError; +} + +mStatus mDNSPosixRemoveFDFromEventLoop(int fd) +{ + return stopReadOrWriteEvents(fd, mDNStrue, mDNStrue, PosixEventFlag_Read | PosixEventFlag_Write); +} + // Simply note the received signal in gEventSignals. mDNSlocal void NoteSignal(int signum) { @@ -1860,34 +2300,39 @@ mStatus mDNSPosixIgnoreSignalInEventLoop(int signum) mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) { - fd_set listenFDs = gEventFDs; - int fdMax = 0, numReady; + fd_set listenFDs; + fd_set writeFDs; + int numFDs = 0, numReady; struct timeval timeout = *pTimeout; - // Include the sockets that are listening to the wire in our select() set - mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout); // timeout may get modified - if (fdMax < gMaxFD) - fdMax = gMaxFD; + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&listenFDs); + FD_ZERO(&writeFDs); + + // 2. Set up the timeout. + mDNSPosixGetNextDNSEventTime(m, &timeout); - numReady = select(fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); + // Include the sockets that are listening to the wire in our select() set + mDNSPosixGetFDSetForSelect(m, &numFDs, &listenFDs, &writeFDs); + numReady = select(numFDs, &listenFDs, &writeFDs, (fd_set*) NULL, &timeout); - // If any data appeared, invoke its callback if (numReady > 0) { - PosixEventSource *iSource; - - (void) mDNSPosixProcessFDSet(m, &listenFDs); // call this first to process wire data for clients - - for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) - { - if (FD_ISSET(iSource->fd, &listenFDs)) - { - iSource->Callback(iSource->fd, 0, iSource->Context); - break; // in case callback removed elements from gEventSources - } - } + mDNSPosixProcessFDSet(m, &listenFDs, &writeFDs); *pDataDispatched = mDNStrue; } + else if (numReady < 0) + { + if (errno != EINTR) { + // This should never happen, represents a coding error, and is not recoverable, since + // we'll just sit here spinning and never receive another event. The usual reason for + // it to happen is that an FD was closed but not removed from the event list. + LogMsg("select failed: %s", strerror(errno)); + abort(); + } + } else *pDataDispatched = mDNSfalse; diff --git a/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.h b/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.h index ca60d806ab..01d7e96805 100755 --- a/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.h +++ b/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSPosix.h @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 4 -*- +/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*- * * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * @@ -35,7 +35,7 @@ typedef struct PosixNetworkInterface PosixNetworkInterface; struct PosixNetworkInterface { - NetworkInterfaceInfo coreIntf; // MUST be the first element in this structure + NetworkInterfaceInfo coreIntf; // MUST be the first element in this structure mDNSs32 LastSeen; const char * intfName; PosixNetworkInterface * aliasIntf; @@ -57,21 +57,76 @@ struct mDNS_PlatformSupport_struct #endif }; +// We keep a list of client-supplied event sources in PosixEventSource records +// Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to. +#define PosixEventFlag_OnList 1 +#define PosixEventFlag_Read 2 +#define PosixEventFlag_Write 4 + +typedef void (*mDNSPosixEventCallback)(int fd, void *context); +struct PosixEventSource +{ + struct PosixEventSource *next; + mDNSPosixEventCallback readCallback; + mDNSPosixEventCallback writeCallback; + const char *readTaskName; + const char *writeTaskName; + void *readContext; + void *writeContext; + int fd; + unsigned flags; +}; +typedef struct PosixEventSource PosixEventSource; + +struct TCPSocket_struct +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with mDNSIPPort + TCPSocketFlags flags; // MUST BE SECOND FIELD -- mDNSCore expects every TCPSocket_struct have TCPSocketFlags flags after mDNSIPPort + TCPConnectionCallback callback; + PosixEventSource events; + // SSL context goes here. + domainname *hostname; + mDNSAddr remoteAddress; + mDNSIPPort remotePort; + void *context; + mDNSBool setup; + mDNSBool connected; + mStatus err; +}; + +struct TCPListener_struct +{ + TCPAcceptedCallback callback; + PosixEventSource events; + void *context; + mDNSAddr_Type addressType; + TCPSocketFlags socketFlags; +}; + #define uDNS_SERVERS_FILE "/etc/resolv.conf" extern int ParseDNSServers(mDNS *m, const char *filePath); extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); // See comment in implementation. +// Get the next upcoming mDNS (or DNS) event time as a posix timeval that can be passed to select. +// This will only update timeout if the next mDNS event is sooner than the value that was passed. +// Therefore, use { FutureTime, 0 } as an initializer if no other timer events are being managed. +extern void mDNSPosixGetNextDNSEventTime(mDNS *m, struct timeval *timeout); + +// Returns all the FDs that the posix I/O event system expects to be passed to select. +extern void mDNSPosixGetFDSetForSelect(mDNS *m, int *nfds, fd_set *readfds, fd_set *writefds); + // Call mDNSPosixGetFDSet before calling select(), to update the parameters // as may be necessary to meet the needs of the mDNSCore code. // The timeout pointer MUST NOT be NULL. // Set timeout->tv_sec to FutureTime if you want to have effectively no timeout // After calling mDNSPosixGetFDSet(), call select(nfds, &readfds, NULL, NULL, &timeout); as usual // After select() returns, call mDNSPosixProcessFDSet() to let mDNSCore do its work -extern void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout); -extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds); +// mDNSPosixGetFDSet simply calls mDNSPosixGetNextDNSEventTime and then mDNSPosixGetFDSetForSelect. +extern void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, fd_set *writefds, struct timeval *timeout); -typedef void (*mDNSPosixEventCallback)(int fd, short filter, void *context); + +extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds, fd_set *writefds); extern mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context); extern mStatus mDNSPosixRemoveFDFromEventLoop( int fd); @@ -79,6 +134,10 @@ extern mStatus mDNSPosixListenForSignalInEventLoop( int signum); extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum); extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched); +extern mStatus mDNSPosixListenForSignalInEventLoop( int signum); +extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum); +extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched); + #ifdef __cplusplus } #endif diff --git a/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSUNP.h b/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSUNP.h index 2b36ceb042..1cead0975c 100755 --- a/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSUNP.h +++ b/usr/src/contrib/mDNSResponder/mDNSPosix/mDNSUNP.h @@ -44,14 +44,6 @@ extern "C" { typedef unsigned int socklen_t; #endif -#if !defined(_SS_MAXSIZE) -#if HAVE_IPV6 -#define sockaddr_storage sockaddr_in6 -#else -#define sockaddr_storage sockaddr -#endif // HAVE_IPV6 -#endif // !defined(_SS_MAXSIZE) - #ifndef NOT_HAVE_SA_LEN #define GET_SA_LEN(X) (sizeof(struct sockaddr) > ((struct sockaddr*)&(X))->sa_len ? \ sizeof(struct sockaddr) : ((struct sockaddr*)&(X))->sa_len ) @@ -96,17 +88,6 @@ struct ifi_info { struct ifi_info *ifi_next; /* next of these structures */ }; -#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX -#define PROC_IFINET6_PATH "/proc/net/if_inet6" -extern struct ifi_info *get_ifi_info_linuxv6(int doaliases); -#endif - -#if defined(AF_INET6) && HAVE_IPV6 -#define INET6_ADDRSTRLEN 46 /*Maximum length of IPv6 address */ -#endif - - - #define IFI_ALIAS 1 /* ifi_addr is an alias */ /* From the text (Stevens, section 16.6): */ @@ -117,7 +98,11 @@ extern struct ifi_info *get_ifi_info(int family, int doaliases); /* 'The free_ifi_info function, which takes a pointer that was */ /* returned by get_ifi_info and frees all the dynamic memory.' */ -extern void free_ifi_info(struct ifi_info *); +extern void free_ifi_info(struct ifi_info *); + +#if defined(AF_INET6) && HAVE_IPV6 +#define INET6_ADDRSTRLEN 46 /*Maximum length of IPv6 address */ +#endif #ifdef NOT_HAVE_DAEMON extern int daemon(int nochdir, int noclose); diff --git a/usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.c b/usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.c new file mode 100644 index 0000000000..89eca5b098 --- /dev/null +++ b/usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.c @@ -0,0 +1,28 @@ +// +// posix_utilities.c +// mDNSResponder +// +// Copyright (c) 2019 Apple Inc. All rights reserved. +// + +#include "posix_utilities.h" +#include "mDNSEmbeddedAPI.h" +#include <stdlib.h> // for NULL +#include <stdio.h> // for snprintf +#include <time.h> +#include <sys/time.h> // for gettimeofday + +mDNSexport void getLocalTimestamp(char * const buffer, mDNSu32 buffer_len) +{ + struct timeval now; + struct tm local_time; + char date_time_str[32]; + char time_zone_str[32]; + + gettimeofday(&now, NULL); + localtime_r(&now.tv_sec, &local_time); + + strftime(date_time_str, sizeof(date_time_str), "%F %T", &local_time); + strftime(time_zone_str, sizeof(time_zone_str), "%z", &local_time); + snprintf(buffer, buffer_len, "%s.%06lu%s", date_time_str, (unsigned long)now.tv_usec, time_zone_str); +} diff --git a/usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.h b/usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.h new file mode 100644 index 0000000000..2eff6f384b --- /dev/null +++ b/usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.h @@ -0,0 +1,16 @@ +// +// posix_utilities.h +// mDNSResponder +// +// Copyright (c) 2019 Apple Inc. All rights reserved. +// + +#ifndef posix_utilities_h +#define posix_utilities_h + +#include "mDNSEmbeddedAPI.h" + +// timestamp format: "2008-08-08 20:00:00.000000+0800", a 64-byte buffer is enough to store the result +extern void getLocalTimestamp(char * const buffer, mDNSu32 buffer_len); + +#endif /* posix_utilities_h */ diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.c b/usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.c new file mode 100644 index 0000000000..4ea8101b64 --- /dev/null +++ b/usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.c @@ -0,0 +1,1068 @@ +/* + * Copyright (c) 2018-2020 Apple Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ClientRequests.h" + +#include "DNSCommon.h" +#include "uDNS.h" + +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +#include "QuerierSupport.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) +#include "D2D.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) +#include "mDNSMacOSX.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES) +#include <dispatch/dispatch.h> +#include <net/if.h> +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) +#include <WebFilterDNS/WebFilterDNS.h> + +int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import)); +int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import)); +int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import)); +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#include "dnssec_v2.h" +#endif + +#define RecordTypeIsAddress(TYPE) (((TYPE) == kDNSType_A) || ((TYPE) == kDNSType_AAAA)) + +extern mDNS mDNSStorage; +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) +extern domainname ActiveDirectoryPrimaryDomain; +#endif + +// Normally we append search domains only for queries with a single label that are not fully qualified. This can be +// overridden to apply search domains for queries (that are not fully qualified) with any number of labels e.g., moon, +// moon.cs, moon.cs.be, etc. - Mohan +mDNSBool AlwaysAppendSearchDomains = mDNSfalse; + +// Control enabling optimistic DNS - Phil +mDNSBool EnableAllowExpired = mDNStrue; + + +typedef struct +{ + mDNSu32 requestID; + const domainname * qname; + mDNSu16 qtype; + mDNSu16 qclass; + mDNSInterfaceID interfaceID; + mDNSs32 serviceID; + mDNSu32 flags; + mDNSBool appendSearchDomains; + mDNSs32 effectivePID; + const mDNSu8 * effectiveUUID; + mDNSu32 peerUID; + mDNSBool isInAppBrowserRequest; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + const mDNSu8 * resolverUUID; + mdns_dns_service_id_t customID; + mDNSBool needEncryption; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + const audit_token_t * peerAuditToken; + const audit_token_t * delegatorAuditToken; +#endif + +} QueryRecordOpParams; + +mDNSlocal void QueryRecordOpParamsInit(QueryRecordOpParams *inParams) +{ + mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams)); + inParams->serviceID = -1; +} + +mDNSlocal mStatus QueryRecordOpCreate(QueryRecordOp **outOp); +mDNSlocal void QueryRecordOpFree(QueryRecordOp *operation); +mDNSlocal mStatus QueryRecordOpStart(QueryRecordOp *inOp, const QueryRecordOpParams *inParams, + QueryRecordResultHandler inResultHandler, void *inResultContext); +mDNSlocal void QueryRecordOpStop(QueryRecordOp *op); +mDNSlocal mDNSBool QueryRecordOpIsMulticast(const QueryRecordOp *op); +mDNSlocal void QueryRecordOpCallback(mDNS *m, DNSQuestion *inQuestion, const ResourceRecord *inAnswer, + QC_result inAddRecord); +mDNSlocal void QueryRecordOpResetHandler(DNSQuestion *inQuestion); +mDNSlocal mStatus QueryRecordOpStartQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion); +mDNSlocal mStatus QueryRecordOpStopQuestion(DNSQuestion *inQuestion); +mDNSlocal mStatus QueryRecordOpRestartUnicastQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion, + const domainname *inSearchDomain); +mDNSlocal mStatus InterfaceIndexToInterfaceID(mDNSu32 inInterfaceIndex, mDNSInterfaceID *outInterfaceID); +mDNSlocal mDNSBool DomainNameIsSingleLabel(const domainname *inName); +mDNSlocal mDNSBool StringEndsWithDot(const char *inString); +mDNSlocal const domainname * NextSearchDomain(QueryRecordOp *inOp); +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) +mDNSlocal mDNSBool DomainNameIsInSearchList(const domainname *domain, mDNSBool inExcludeLocal); +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) +mDNSlocal void NotifyWebContentFilter(const ResourceRecord *inAnswer, uid_t inUID); +#endif + +mDNSexport void GetAddrInfoClientRequestParamsInit(GetAddrInfoClientRequestParams *inParams) +{ + mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams)); +} + +mDNSexport mStatus GetAddrInfoClientRequestStart(GetAddrInfoClientRequest *inRequest, + const GetAddrInfoClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext) +{ + mStatus err; + domainname hostname; + mDNSBool appendSearchDomains; + mDNSInterfaceID interfaceID; + DNSServiceFlags flags; + mDNSs32 serviceID; + QueryRecordOpParams opParams; + + if (!MakeDomainNameFromDNSNameString(&hostname, inParams->hostnameStr)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] ERROR: bad hostname '" PRI_S "'", inParams->requestID, inParams->hostnameStr); + err = mStatus_BadParamErr; + goto exit; + } + + if (inParams->protocols & ~(kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) + { + err = mStatus_BadParamErr; + goto exit; + } + + flags = inParams->flags; + if (inParams->protocols == 0) + { + flags |= kDNSServiceFlagsSuppressUnusable; + inRequest->protocols = kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6; + } + else + { + inRequest->protocols = inParams->protocols; + } + + if (flags & kDNSServiceFlagsServiceIndex) + { + // NOTE: kDNSServiceFlagsServiceIndex flag can only be set for DNSServiceGetAddrInfo() + LogInfo("GetAddrInfoClientRequestStart: kDNSServiceFlagsServiceIndex is SET by the client"); + + // If kDNSServiceFlagsServiceIndex is SET, interpret the interfaceID as the serviceId and set the interfaceID to 0. + serviceID = (mDNSs32)inParams->interfaceIndex; + interfaceID = mDNSNULL; + } + else + { + serviceID = -1; + err = InterfaceIndexToInterfaceID(inParams->interfaceIndex, &interfaceID); + if (err) goto exit; + } + inRequest->interfaceID = interfaceID; + + if (!StringEndsWithDot(inParams->hostnameStr) && (AlwaysAppendSearchDomains || DomainNameIsSingleLabel(&hostname))) + { + appendSearchDomains = mDNStrue; + } + else + { + appendSearchDomains = mDNSfalse; + } + QueryRecordOpParamsInit(&opParams); + opParams.requestID = inParams->requestID; + opParams.qname = &hostname; + opParams.qclass = kDNSClass_IN; + opParams.interfaceID = inRequest->interfaceID; + opParams.serviceID = serviceID; + opParams.flags = flags; + opParams.appendSearchDomains = appendSearchDomains; + opParams.effectivePID = inParams->effectivePID; + opParams.effectiveUUID = inParams->effectiveUUID; + opParams.peerUID = inParams->peerUID; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + opParams.resolverUUID = inParams->resolverUUID; + opParams.customID = inParams->customID; + opParams.needEncryption = inParams->needEncryption; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + opParams.peerAuditToken = inParams->peerAuditToken; + opParams.delegatorAuditToken = inParams->delegatorAuditToken; + opParams.isInAppBrowserRequest = inParams->isInAppBrowserRequest; +#endif + if (inRequest->protocols & kDNSServiceProtocol_IPv6) + { + err = QueryRecordOpCreate(&inRequest->op6); + if (err) goto exit; + + opParams.qtype = kDNSType_AAAA; + err = QueryRecordOpStart(inRequest->op6, &opParams, inResultHandler, inResultContext); + if (err) goto exit; + } + if (inRequest->protocols & kDNSServiceProtocol_IPv4) + { + err = QueryRecordOpCreate(&inRequest->op4); + if (err) goto exit; + + opParams.qtype = kDNSType_A; + err = QueryRecordOpStart(inRequest->op4, &opParams, inResultHandler, inResultContext); + if (err) goto exit; + } + err = mStatus_NoError; + +exit: + if (err) GetAddrInfoClientRequestStop(inRequest); + return err; +} + +mDNSexport void GetAddrInfoClientRequestStop(GetAddrInfoClientRequest *inRequest) +{ + if (inRequest->op4) QueryRecordOpStop(inRequest->op4); + if (inRequest->op6) QueryRecordOpStop(inRequest->op6); + +#if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) + { + const QueryRecordOp * const op4 = inRequest->op4; + const QueryRecordOp * const op6 = inRequest->op6; + const DNSQuestion * q4 = mDNSNULL; + const DNSQuestion * q6 = mDNSNULL; + + if (op4) + { + if (op4->answered) + { + // If we have a v4 answer and if we timed out prematurely before, provide a trigger to the upper layer so + // that it can retry questions if needed. - Mohan + q4 = &op4->q; + } + else if (op4->q.TimeoutQuestion) + { + // If we are not delivering answers, we may be timing out prematurely. Note down the current state so that + // we know to retry when we see a valid response again. - Mohan + mDNSPlatformUpdateDNSStatus(&op4->q); + } + } + if (op6) + { + if (op6->answered) + { + q6 = &op6->q; + } + else if (op6->q.TimeoutQuestion) + { + mDNSPlatformUpdateDNSStatus(&op6->q); + } + } + mDNSPlatformTriggerDNSRetry(q4, q6); + } +#endif + + if (inRequest->op4) + { + QueryRecordOpFree(inRequest->op4); + inRequest->op4 = mDNSNULL; + } + if (inRequest->op6) + { + QueryRecordOpFree(inRequest->op6); + inRequest->op6 = mDNSNULL; + } +} + +mDNSexport const domainname * GetAddrInfoClientRequestGetQName(const GetAddrInfoClientRequest *inRequest) +{ + if (inRequest->op4) return &inRequest->op4->q.qname; + if (inRequest->op6) return &inRequest->op6->q.qname; + return (const domainname *)""; +} + +mDNSexport mDNSBool GetAddrInfoClientRequestIsMulticast(const GetAddrInfoClientRequest *inRequest) +{ + if ((inRequest->op4 && QueryRecordOpIsMulticast(inRequest->op4)) || + (inRequest->op6 && QueryRecordOpIsMulticast(inRequest->op6))) + { + return mDNStrue; + } + return mDNSfalse; +} + +mDNSexport void QueryRecordClientRequestParamsInit(QueryRecordClientRequestParams *inParams) +{ + mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams)); +} + +mDNSexport mStatus QueryRecordClientRequestStart(QueryRecordClientRequest *inRequest, + const QueryRecordClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext) +{ + mStatus err; + domainname qname; + mDNSInterfaceID interfaceID; + mDNSBool appendSearchDomains; + QueryRecordOpParams opParams; +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + dnssec_context_t * dnssecContext = mDNSNULL; +#endif + + err = InterfaceIndexToInterfaceID(inParams->interfaceIndex, &interfaceID); + if (err) goto exit; + + if (!MakeDomainNameFromDNSNameString(&qname, inParams->qnameStr)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] ERROR: bad domain name '" PRI_S "'", inParams->requestID, inParams->qnameStr); + err = mStatus_BadParamErr; + goto exit; + } + + if (RecordTypeIsAddress(inParams->qtype) && !StringEndsWithDot(inParams->qnameStr) && + (AlwaysAppendSearchDomains || DomainNameIsSingleLabel(&qname))) + { + appendSearchDomains = mDNStrue; + } + else + { + appendSearchDomains = mDNSfalse; + } + QueryRecordOpParamsInit(&opParams); + opParams.requestID = inParams->requestID; + opParams.qname = &qname; + opParams.qtype = inParams->qtype; + opParams.qclass = inParams->qclass; + opParams.interfaceID = interfaceID; + opParams.appendSearchDomains = appendSearchDomains; + opParams.effectivePID = inParams->effectivePID; + opParams.effectiveUUID = inParams->effectiveUUID; + opParams.peerUID = inParams->peerUID; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + opParams.resolverUUID = inParams->resolverUUID; + opParams.customID = inParams->customID; + opParams.needEncryption = inParams->needEncryption; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + opParams.peerAuditToken = inParams->peerAuditToken; + opParams.delegatorAuditToken = inParams->delegatorAuditToken; + opParams.isInAppBrowserRequest = inParams->isInAppBrowserRequest; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + // Query ends with ".local." and query for RRSIG or ANY type cannot be validated by DNSSEC even if the user sets the + // kDNSServiceFlagsEnableDNSSEC flag. + if (FLAGS_CONTAIN_DNSOK_BIT(inParams->flags) && is_eligible_for_dnssec(&qname, inParams->qtype)) + { + opParams.flags = inParams->flags | kDNSServiceFlagsReturnIntermediates; // to handle CNAME reference + err = create_dnssec_context_t(inRequest, inParams->requestID, &qname, inParams->qtype, inParams->qclass, + interfaceID, -1, inParams->flags, appendSearchDomains, inParams->effectivePID, inParams->effectiveUUID, + inParams->peerUID, +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + inParams->peerAuditToken, inParams->delegatorAuditToken, +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSNULL, inParams->needEncryption, inParams->customID, +#endif + inResultHandler, inResultContext, mDNSNULL, &dnssecContext); + require_action(err == mStatus_NoError, exit, log_debug("create_dnssec_context_t failed; error_description='%s'", + mStatusDescription(err))); + + err = QueryRecordOpStart(&inRequest->op, &opParams, query_record_result_reply_with_dnssec, dnssecContext); + } else +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + { + opParams.flags = inParams->flags; + err = QueryRecordOpStart(&inRequest->op, &opParams, inResultHandler, inResultContext); + } + +exit: + if (err) QueryRecordClientRequestStop(inRequest); + return err; +} + +mDNSexport void QueryRecordClientRequestStop(QueryRecordClientRequest *inRequest) +{ + QueryRecordOpStop(&inRequest->op); + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + stop_dnssec_if_enable_dnssec(inRequest); +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) + if (inRequest->op.answered) + { + DNSQuestion *v4q, *v6q; + // If we are receiving positive answers, provide the hint to the upper layer. - Mohan + v4q = (inRequest->op.q.qtype == kDNSType_A) ? &inRequest->op.q : mDNSNULL; + v6q = (inRequest->op.q.qtype == kDNSType_AAAA) ? &inRequest->op.q : mDNSNULL; + mDNSPlatformTriggerDNSRetry(v4q, v6q); + } +#endif +} + +mDNSexport const domainname * QueryRecordClientRequestGetQName(const QueryRecordClientRequest *inRequest) +{ + return &inRequest->op.q.qname; +} + +mDNSexport mDNSu16 QueryRecordClientRequestGetType(const QueryRecordClientRequest *inRequest) +{ + return inRequest->op.q.qtype; +} + +mDNSexport mDNSBool QueryRecordClientRequestIsMulticast(QueryRecordClientRequest *inRequest) +{ + return (QueryRecordOpIsMulticast(&inRequest->op) ? mDNStrue : mDNSfalse); +} +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +mDNSexport mStatus QueryRecordOpStartForClientRequest( + QueryRecordOp * inOp, + mDNSu32 inReqID, + const domainname * inQName, + mDNSu16 inQType, + mDNSu16 inQClass, + mDNSInterfaceID inInterfaceID, + mDNSs32 inServiceID, + mDNSu32 inFlags, + mDNSBool inAppendSearchDomains, + mDNSs32 inPID, + const mDNSu8 inUUID[UUID_SIZE], + mDNSu32 inUID, +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + const audit_token_t * inPeerAuditTokenPtr, + const audit_token_t * inDelegateAuditTokenPtr, +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + const mDNSu8 inResolverUUID[UUID_SIZE], + mDNSBool inNeedEncryption, + const mdns_dns_service_id_t inCustomID, +#endif + QueryRecordResultHandler inResultHandler, + void * inResultContext) { + QueryRecordOpParams opParams; + QueryRecordOpParamsInit(&opParams); + opParams.requestID = inReqID; + opParams.qname = inQName; + opParams.qtype = inQType; + opParams.qclass = inQClass; + opParams.interfaceID = inInterfaceID; + opParams.serviceID = inServiceID; + opParams.flags = inFlags; + opParams.appendSearchDomains = inAppendSearchDomains; + opParams.effectivePID = inPID; + opParams.effectiveUUID = inUUID; + opParams.peerUID = inUID; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + opParams.resolverUUID = inResolverUUID; + opParams.customID = inCustomID; + opParams.needEncryption = inNeedEncryption; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + opParams.peerAuditToken = inPeerAuditTokenPtr; + opParams.delegatorAuditToken = inDelegateAuditTokenPtr; +#endif + return QueryRecordOpStart(inOp, &opParams, inResultHandler, inResultContext); +} + +mDNSexport void QueryRecordOpStopForClientRequest(QueryRecordOp *op) { + QueryRecordOpStop(op); +} + +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + +mDNSlocal mStatus QueryRecordOpCreate(QueryRecordOp **outOp) +{ + mStatus err; + QueryRecordOp *op; + + op = (QueryRecordOp *) mDNSPlatformMemAllocateClear(sizeof(*op)); + if (!op) + { + err = mStatus_NoMemoryErr; + goto exit; + } + *outOp = op; + err = mStatus_NoError; + +exit: + return err; +} + +mDNSlocal void QueryRecordOpFree(QueryRecordOp *operation) +{ + mDNSPlatformMemFree(operation); +} + +#define VALID_MSAD_SRV_TRANSPORT(T) \ + (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp")) +#define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname))) + +mDNSlocal mStatus QueryRecordOpStart(QueryRecordOp *inOp, const QueryRecordOpParams *inParams, + QueryRecordResultHandler inResultHandler, void *inResultContext) +{ + mStatus err; + DNSQuestion * const q = &inOp->q; + mDNSu32 len; + + // Save the original qname. + + len = DomainNameLength(inParams->qname); + inOp->qname = (domainname *) mDNSPlatformMemAllocate(len); + if (!inOp->qname) + { + err = mStatus_NoMemoryErr; + goto exit; + } + mDNSPlatformMemCopy(inOp->qname, inParams->qname, len); + + inOp->interfaceID = inParams->interfaceID; + inOp->reqID = inParams->requestID; + inOp->resultHandler = inResultHandler; + inOp->resultContext = inResultContext; + + // Set up DNSQuestion. + + if (EnableAllowExpired && (inParams->flags & kDNSServiceFlagsAllowExpiredAnswers)) + { + q->allowExpired = AllowExpired_AllowExpiredAnswers; + } + else + { + q->allowExpired = AllowExpired_None; + } + q->ServiceID = inParams->serviceID; +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + q->inAppBrowserRequest = inParams->isInAppBrowserRequest; + if (inParams->peerAuditToken) + { + q->peerAuditToken = *inParams->peerAuditToken; + } + if (inParams->delegatorAuditToken) + { + q->delegateAuditToken = *inParams->delegatorAuditToken; + } +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (inParams->resolverUUID) + { + mDNSPlatformMemCopy(q->ResolverUUID, inParams->resolverUUID, UUID_SIZE); + } +#endif + q->InterfaceID = inParams->interfaceID; + q->flags = inParams->flags; + AssignDomainName(&q->qname, inParams->qname); + q->qtype = inParams->qtype; + q->qclass = inParams->qclass; + q->LongLived = (inParams->flags & kDNSServiceFlagsLongLivedQuery) ? mDNStrue : mDNSfalse; + q->ForceMCast = (inParams->flags & kDNSServiceFlagsForceMulticast) ? mDNStrue : mDNSfalse; + q->ReturnIntermed = (inParams->flags & kDNSServiceFlagsReturnIntermediates) ? mDNStrue : mDNSfalse; + q->SuppressUnusable = (inParams->flags & kDNSServiceFlagsSuppressUnusable) ? mDNStrue : mDNSfalse; + q->TimeoutQuestion = (inParams->flags & kDNSServiceFlagsTimeout) ? mDNStrue : mDNSfalse; + q->UseBackgroundTraffic = (inParams->flags & kDNSServiceFlagsBackgroundTrafficClass) ? mDNStrue : mDNSfalse; + q->AppendSearchDomains = inParams->appendSearchDomains; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + q->RequireEncryption = inParams->needEncryption; + q->CustomID = inParams->customID; +#endif + q->InitialCacheMiss = mDNSfalse; + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + err = initialize_dnssec_status_t(&q->DNSSECStatus, inParams->qname, inParams->qtype, inParams->flags, inResultContext); + require_action(err == mStatus_NoError, exit, log_debug("initialize_dnssec_status failed; error_description='%s'", mStatusDescription(err))); +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + + q->pid = inParams->effectivePID; + if (inParams->effectiveUUID) + { + mDNSPlatformMemCopy(q->uuid, inParams->effectiveUUID, UUID_SIZE); + } + q->euid = inParams->peerUID; + q->request_id = inParams->requestID; + q->QuestionCallback = QueryRecordOpCallback; + q->ResetHandler = QueryRecordOpResetHandler; + + // For single label queries that are not fully qualified, look at /etc/hosts, cache and try search domains before trying + // them on the wire as a single label query. - Mohan + + if (q->AppendSearchDomains && DomainNameIsSingleLabel(inOp->qname)) q->InterfaceID = mDNSInterface_LocalOnly; + err = QueryRecordOpStartQuestion(inOp, q); + if (err) goto exit; + +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + if (callExternalHelpers(q->InterfaceID, &q->qname, q->flags)) + { + external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, q->flags, q->pid); + } +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) + if ((RecordTypeIsAddress(q->qtype) || VALID_MSAD_SRV(&inOp->q)) && !q->ForceMCast && + SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain)) + { + DNSQuestion * q2; + + q2 = (DNSQuestion *) mDNSPlatformMemAllocate((mDNSu32)sizeof(*inOp->q2)); + if (!q2) + { + err = mStatus_NoMemoryErr; + goto exit; + } + inOp->q2 = q2; + + *q2 = *q; + q2->IsUnicastDotLocal = mDNStrue; + + if ((CountLabels(&q2->qname) == 2) && !SameDomainName(&q2->qname, &ActiveDirectoryPrimaryDomain) + && !DomainNameIsInSearchList(&q2->qname, mDNSfalse)) + { + inOp->q2Type = q2->qtype; + inOp->q2LongLived = q2->LongLived; + inOp->q2ReturnIntermed = q2->ReturnIntermed; + inOp->q2TimeoutQuestion = q2->TimeoutQuestion; + inOp->q2AppendSearchDomains = q2->AppendSearchDomains; + + AssignDomainName(&q2->qname, &localdomain); + q2->qtype = kDNSType_SOA; + q2->LongLived = mDNSfalse; + q2->ReturnIntermed = mDNStrue; + q2->TimeoutQuestion = mDNSfalse; + q2->AppendSearchDomains = mDNSfalse; + } + + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] QueryRecordOpStart: starting parallel unicast query for " PRI_DM_NAME " " PUB_S, + inOp->reqID, DM_NAME_PARAM(&q2->qname), DNSTypeName(q2->qtype)); + + err = QueryRecordOpStartQuestion(inOp, q2); + if (err) goto exit; + } +#endif + err = mStatus_NoError; + +exit: + if (err) QueryRecordOpStop(inOp); + return err; +} + +mDNSlocal void QueryRecordOpStop(QueryRecordOp *op) +{ + if (op->q.QuestionContext) + { + QueryRecordOpStopQuestion(&op->q); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + if (callExternalHelpers(op->q.InterfaceID, op->qname, op->q.flags)) + { + external_stop_browsing_for_service(op->q.InterfaceID, &op->q.qname, op->q.qtype, op->q.flags, op->q.pid); + } +#endif + } + if (op->qname) + { + mDNSPlatformMemFree(op->qname); + op->qname = mDNSNULL; + } +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) + if (op->q2) + { + if (op->q2->QuestionContext) QueryRecordOpStopQuestion(op->q2); + mDNSPlatformMemFree(op->q2); + op->q2 = mDNSNULL; + } +#endif +} + +mDNSlocal mDNSBool QueryRecordOpIsMulticast(const QueryRecordOp *op) +{ + return ((mDNSOpaque16IsZero(op->q.TargetQID) && (op->q.ThisQInterval > 0)) ? mDNStrue : mDNSfalse); +} + +// GetTimeNow is a callback-safe alternative to mDNS_TimeNow(), which expects to be called with m->mDNS_busy == 0. +mDNSlocal mDNSs32 GetTimeNow(mDNS *m) +{ + mDNSs32 time; + mDNS_Lock(m); + time = m->timenow; + mDNS_Unlock(m); + return time; +} + +mDNSlocal void QueryRecordOpCallback(mDNS *m, DNSQuestion *inQuestion, const ResourceRecord *inAnswer, QC_result inAddRecord) +{ + mStatus resultErr; + QueryRecordOp *const op = (QueryRecordOp *)inQuestion->QuestionContext; + const domainname * domain; + +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) + if ((inQuestion == op->q2) && (inQuestion->qtype == kDNSType_SOA)) + { + DNSQuestion * const q2 = op->q2; + + if (inAnswer->rrtype != kDNSType_SOA) goto exit; + QueryRecordOpStopQuestion(q2); + + // Restore DNSQuestion variables that were modified for the SOA query. + + q2->qtype = op->q2Type; + q2->LongLived = op->q2LongLived; + q2->ReturnIntermed = op->q2ReturnIntermed; + q2->TimeoutQuestion = op->q2TimeoutQuestion; + q2->AppendSearchDomains = op->q2AppendSearchDomains; + + if (inAnswer->RecordType != kDNSRecordTypePacketNegative) + { + QueryRecordOpRestartUnicastQuestion(op, q2, mDNSNULL); + } + else if (q2->AppendSearchDomains) + { + domain = NextSearchDomain(op); + if (domain) QueryRecordOpRestartUnicastQuestion(op, q2, domain); + } + goto exit; + } +#endif + + if (inAddRecord == QC_suppressed) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "[R%u] QueryRecordOpCallback: Suppressed question " PRI_DM_NAME " (" PUB_S ")", + op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype)); + + resultErr = kDNSServiceErr_NoSuchRecord; + } + else if (inAnswer->RecordType == kDNSRecordTypePacketNegative) + { + if (inQuestion->TimeoutQuestion && ((GetTimeNow(m) - inQuestion->StopTime) >= 0)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] QueryRecordOpCallback: Question " PRI_DM_NAME " (" PUB_S ") timing out, InterfaceID %p", + op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype), + inQuestion->InterfaceID); + resultErr = kDNSServiceErr_Timeout; + } + else + { + if (inQuestion->AppendSearchDomains && (op->searchListIndex >= 0) && inAddRecord) + { + domain = NextSearchDomain(op); + if (domain || DomainNameIsSingleLabel(op->qname)) + { + QueryRecordOpStopQuestion(inQuestion); + QueryRecordOpRestartUnicastQuestion(op, inQuestion, domain); + goto exit; + } + } +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) + if (!inAnswer->InterfaceID && IsLocalDomain(inAnswer->name)) + { + if ((RecordTypeIsAddress(inQuestion->qtype) && + (inAnswer->negativeRecordType == kNegativeRecordType_NoData)) || + DomainNameIsInSearchList(&inQuestion->qname, mDNStrue)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] QueryRecordOpCallback: Question " PRI_DM_NAME " (" PUB_S ") answering local with negative unicast response", + op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype)); + } + else + { + goto exit; + } + } +#endif + resultErr = kDNSServiceErr_NoSuchRecord; + } + } + else + { + resultErr = kDNSServiceErr_NoError; + } + +#if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) + if ((resultErr != kDNSServiceErr_Timeout) && (inAddRecord == QC_add)) + { + op->answered = mDNStrue; + } +#endif + + if (op->resultHandler) op->resultHandler(m, inQuestion, inAnswer, inAddRecord, resultErr, op->resultContext); + if (resultErr == kDNSServiceErr_Timeout) QueryRecordOpStopQuestion(inQuestion); + +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) + NotifyWebContentFilter(inAnswer, inQuestion->euid); +#endif + +exit: + return; +} + +mDNSlocal void QueryRecordOpResetHandler(DNSQuestion *inQuestion) +{ + QueryRecordOp *const op = (QueryRecordOp *)inQuestion->QuestionContext; + + AssignDomainName(&inQuestion->qname, op->qname); + if (inQuestion->AppendSearchDomains && DomainNameIsSingleLabel(op->qname)) + { + inQuestion->InterfaceID = mDNSInterface_LocalOnly; + } + else + { + inQuestion->InterfaceID = op->interfaceID; + } + op->searchListIndex = 0; +} + +mDNSlocal mStatus QueryRecordOpStartQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion) +{ + mStatus err; + + inQuestion->QuestionContext = inOp; + err = mDNS_StartQuery(&mDNSStorage, inQuestion); + if (err) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] ERROR: QueryRecordOpStartQuestion mDNS_StartQuery for " PRI_DM_NAME " " PUB_S " failed with error %d", + inOp->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype), err); + inQuestion->QuestionContext = mDNSNULL; + } + return err; +} + +mDNSlocal mStatus QueryRecordOpStopQuestion(DNSQuestion *inQuestion) +{ + mStatus err; + + err = mDNS_StopQuery(&mDNSStorage, inQuestion); + inQuestion->QuestionContext = mDNSNULL; + return err; +} + +mDNSlocal mStatus QueryRecordOpRestartUnicastQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion, + const domainname *inSearchDomain) +{ + mStatus err; + + inQuestion->InterfaceID = inOp->interfaceID; + AssignDomainName(&inQuestion->qname, inOp->qname); + if (inSearchDomain) AppendDomainName(&inQuestion->qname, inSearchDomain); + if (SameDomainLabel(LastLabel(&inQuestion->qname), (const mDNSu8 *)&localdomain)) + { + inQuestion->IsUnicastDotLocal = mDNStrue; + } + else + { + inQuestion->IsUnicastDotLocal = mDNSfalse; + } + err = QueryRecordOpStartQuestion(inOp, inQuestion); + return err; +} + +mDNSlocal mStatus InterfaceIndexToInterfaceID(mDNSu32 inInterfaceIndex, mDNSInterfaceID *outInterfaceID) +{ + mStatus err; + mDNSInterfaceID interfaceID; + + interfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, inInterfaceIndex); + +#if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES) + // The request is scoped to a specific interface index, but the interface is not currently in our list. + if ((inInterfaceIndex != kDNSServiceInterfaceIndexAny) && (interfaceID == mDNSInterface_Any)) + { + static dispatch_once_t getLoopbackIndexOnce = 0; + static mDNSu32 loopbackIndex = 0; + + dispatch_once(&getLoopbackIndexOnce, + ^{ + loopbackIndex = if_nametoindex("lo0"); + }); + + // If it's one of the specially defined inteface index values, just return an error. Also, caller should return an + // error immediately if lo0 is not configured into the current active interfaces. See <rdar://problem/21967160>. + if ((inInterfaceIndex == kDNSServiceInterfaceIndexLocalOnly) || + (inInterfaceIndex == kDNSServiceInterfaceIndexUnicast) || + (inInterfaceIndex == kDNSServiceInterfaceIndexP2P) || + (inInterfaceIndex == kDNSServiceInterfaceIndexBLE) || + (inInterfaceIndex == loopbackIndex)) + { + LogInfo("ERROR: bad interfaceIndex %d", inInterfaceIndex); + err = mStatus_BadParamErr; + goto exit; + } + + // Otherwise, use the specified interface index value and the request will be applied to that interface when it + // comes up. + interfaceID = (mDNSInterfaceID)(uintptr_t)inInterfaceIndex; + LogInfo("Query pending for interface index %d", inInterfaceIndex); + } +#endif + + *outInterfaceID = interfaceID; + err = mStatus_NoError; + +#if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES) +exit: +#endif + return err; +} + +mDNSlocal mDNSBool DomainNameIsSingleLabel(const domainname *inName) +{ + const mDNSu8 *const label = inName->c; + return (((label[0] != 0) && (label[1 + label[0]] == 0)) ? mDNStrue : mDNSfalse); +} + +mDNSlocal mDNSBool StringEndsWithDot(const char *inString) +{ + const char * ptr; + mDNSu32 escapeCount; + mDNSBool result; + + // Loop invariant: escapeCount is the number of consecutive escape characters that immediately precede *ptr. + // - If escapeCount is even, then *ptr is immediately preceded by escapeCount / 2 consecutive literal backslash + // characters, so *ptr is not escaped. + // - If escapeCount is odd, then *ptr is immediately preceded by (escapeCount - 1) / 2 consecutive literal backslash + // characters followed by an escape character, so *ptr is escaped. + escapeCount = 0; + result = mDNSfalse; + for (ptr = inString; *ptr != '\0'; ptr++) + { + if (*ptr == '\\') + { + escapeCount++; + } + else + { + if ((*ptr == '.') && (ptr[1] == '\0')) + { + if ((escapeCount % 2) == 0) result = mDNStrue; + break; + } + escapeCount = 0; + } + } + return result; +} + +mDNSlocal const domainname * NextSearchDomain(QueryRecordOp *inOp) +{ + const domainname * domain; + + while ((domain = uDNS_GetNextSearchDomain(inOp->interfaceID, &inOp->searchListIndex, mDNSfalse)) != mDNSNULL) + { + if ((DomainNameLength(inOp->qname) - 1 + DomainNameLength(domain)) <= MAX_DOMAIN_NAME) break; + } + if (!domain) inOp->searchListIndex = -1; + return domain; +} + +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) +mDNSlocal mDNSBool DomainNameIsInSearchList(const domainname *inName, mDNSBool inExcludeLocal) +{ + const SearchListElem * item; + int labelCount, domainLabelCount; + + labelCount = CountLabels(inName); + for (item = SearchList; item; item = item->next) + { + if (inExcludeLocal && SameDomainName(&item->domain, &localdomain)) continue; + domainLabelCount = CountLabels(&item->domain); + if (labelCount >= domainLabelCount) + { + if (SameDomainName(&item->domain, SkipLeadingLabels(inName, (labelCount - domainLabelCount)))) + { + return mDNStrue; + } + } + } + return mDNSfalse; +} +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) +mDNSlocal void NotifyWebContentFilter(const ResourceRecord *inAnswer, uid_t inUID) +{ + if (WCFIsServerRunning) + { + const mDNS *const m = &mDNSStorage; + + if (WCFIsServerRunning(m->WCF) && inAnswer->rdlength != 0) + { + struct sockaddr_storage addr; + addr.ss_len = 0; + if (inAnswer->rrtype == kDNSType_A || inAnswer->rrtype == kDNSType_AAAA) + { + if (inAnswer->rrtype == kDNSType_A) + { + struct sockaddr_in *const sin = (struct sockaddr_in *)&addr; + sin->sin_port = 0; + // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: + // sin->sin_addr.s_addr = inAnswer->rdata->u.ipv4.NotAnInteger; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(mDNSv4Addr)), inAnswer)) + LogMsg("NotifyWebContentFilter: WCF AF_INET putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in); + addr.ss_family = AF_INET; + } + } + else if (inAnswer->rrtype == kDNSType_AAAA) + { + struct sockaddr_in6 *const sin6 = (struct sockaddr_in6 *)&addr; + sin6->sin6_port = 0; + // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: + // sin6->sin6_addr.__u6_addr.__u6_addr32[0] = inAnswer->rdata->u.ipv6.l[0]; + // sin6->sin6_addr.__u6_addr.__u6_addr32[1] = inAnswer->rdata->u.ipv6.l[1]; + // sin6->sin6_addr.__u6_addr.__u6_addr32[2] = inAnswer->rdata->u.ipv6.l[2]; + // sin6->sin6_addr.__u6_addr.__u6_addr32[3] = inAnswer->rdata->u.ipv6.l[3]; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(mDNSv6Addr)), inAnswer)) + LogMsg("NotifyWebContentFilter: WCF AF_INET6 putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in6); + addr.ss_family = AF_INET6; + } + } + if (addr.ss_len) + { + char name[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(inAnswer->name, name); + + debugf("NotifyWebContentFilter: Name %s, uid %u, addr length %d", name, inUID, addr.ss_len); + if (WCFNameResolvesToAddr) + { + WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, inUID); + } + } + } + else if (inAnswer->rrtype == kDNSType_CNAME) + { + domainname cname; + char name[MAX_ESCAPED_DOMAIN_NAME]; + char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; + + if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), inAnswer)) + LogMsg("NotifyWebContentFilter: WCF CNAME putRData failed"); + else + { + ConvertDomainNameToCString(inAnswer->name, name); + ConvertDomainNameToCString(&cname, cname_cstr); + if (WCFNameResolvesToAddr) + { + WCFNameResolvesToName(m->WCF, name, cname_cstr, inUID); + } + } + } + } + } +} +#endif diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.h b/usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.h new file mode 100644 index 0000000000..c3a42f4337 --- /dev/null +++ b/usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018-2019 Apple Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ClientRequests_h +#define __ClientRequests_h + +#include "mDNSEmbeddedAPI.h" +#include "dns_sd_internal.h" + +typedef void (*QueryRecordResultHandler)(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord, + DNSServiceErrorType error, void *context); + +typedef struct +{ + DNSQuestion q; // DNSQuestion for record query. + domainname * qname; // Name of the original record. + mDNSInterfaceID interfaceID; // Interface over which to perform query. + QueryRecordResultHandler resultHandler; // Handler for query record operation results. + void * resultContext; // Context to pass to result handler. + mDNSu32 reqID; // + int searchListIndex; // Index that indicates the next search domain to try. +#if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) + DNSQuestion * q2; // DNSQuestion for unicast version of a record with a dot-local name. + mDNSu16 q2Type; // q2's original qtype value. + mDNSBool q2LongLived; // q2's original LongLived value. + mDNSBool q2ReturnIntermed; // q2's original ReturnIntermed value. + mDNSBool q2TimeoutQuestion; // q2's original TimeoutQuestion value. + mDNSBool q2AppendSearchDomains; // q2's original AppendSearchDomains value. +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) + mDNSBool answered; // True if the query was answered. +#endif + +} QueryRecordOp; + +typedef struct +{ + mDNSInterfaceID interfaceID; // InterfaceID being used for query record operations. + mDNSu32 protocols; // Protocols (IPv4, IPv6) specified by client. + QueryRecordOp * op4; // Query record operation object for A record. + QueryRecordOp * op6; // Query record operation object for AAAA record. + +} GetAddrInfoClientRequest; + +typedef struct +{ + QueryRecordOp op; // Query record operation object. + +} QueryRecordClientRequest; + +typedef struct +{ + mDNSu32 requestID; + const char * hostnameStr; + mDNSu32 interfaceIndex; + DNSServiceFlags flags; + mDNSu32 protocols; + mDNSs32 effectivePID; + const mDNSu8 * effectiveUUID; + mDNSu32 peerUID; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool needEncryption; + const mDNSu8 * resolverUUID; + mdns_dns_service_id_t customID; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + const audit_token_t * peerAuditToken; + const audit_token_t * delegatorAuditToken; + mDNSBool isInAppBrowserRequest; +#endif + +} GetAddrInfoClientRequestParams; + +typedef struct +{ + mDNSu32 requestID; + const char * qnameStr; + mDNSu32 interfaceIndex; + DNSServiceFlags flags; + mDNSu16 qtype; + mDNSu16 qclass; + mDNSs32 effectivePID; + const mDNSu8 * effectiveUUID; + mDNSu32 peerUID; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool needEncryption; + const mDNSu8 * resolverUUID; + mdns_dns_service_id_t customID; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + const audit_token_t * peerAuditToken; + const audit_token_t * delegatorAuditToken; + mDNSBool isInAppBrowserRequest; +#endif + +} QueryRecordClientRequestParams; + +#ifdef __cplusplus +extern "C" { +#endif + +mDNSexport void GetAddrInfoClientRequestParamsInit(GetAddrInfoClientRequestParams *inParams); +mDNSexport mStatus GetAddrInfoClientRequestStart(GetAddrInfoClientRequest *inRequest, + const GetAddrInfoClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext); +mDNSexport void GetAddrInfoClientRequestStop(GetAddrInfoClientRequest *inRequest); +mDNSexport const domainname * GetAddrInfoClientRequestGetQName(const GetAddrInfoClientRequest *inRequest); +mDNSexport mDNSBool GetAddrInfoClientRequestIsMulticast(const GetAddrInfoClientRequest *inRequest); + +mDNSexport void QueryRecordClientRequestParamsInit(QueryRecordClientRequestParams *inParams); +mDNSexport mStatus QueryRecordClientRequestStart(QueryRecordClientRequest *inRequest, + const QueryRecordClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext); +mDNSexport void QueryRecordClientRequestStop(QueryRecordClientRequest *inRequest); +mDNSexport const domainname * QueryRecordClientRequestGetQName(const QueryRecordClientRequest *inRequest); +mDNSexport mDNSu16 QueryRecordClientRequestGetType(const QueryRecordClientRequest *inRequest); +mDNSexport mDNSBool QueryRecordClientRequestIsMulticast(QueryRecordClientRequest *inRequest); + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +// This is a "mDNSexport" wrapper around the "static" QueryRecordOpStart that cannot be called by outside, which can be +// called by the outside(dnssec related function). +mDNSexport mStatus QueryRecordOpStartForClientRequest( + QueryRecordOp * inOp, + mDNSu32 inReqID, + const domainname * inQName, + mDNSu16 inQType, + mDNSu16 inQClass, + mDNSInterfaceID inInterfaceID, + mDNSs32 inServiceID, + mDNSu32 inFlags, + mDNSBool inAppendSearchDomains, + mDNSs32 inPID, + const mDNSu8 inUUID[UUID_SIZE], + mDNSu32 inUID, +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + const audit_token_t * inPeerAuditTokenPtr, + const audit_token_t * inDelegateAuditTokenPtr, +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + const mDNSu8 inResolverUUID[UUID_SIZE], + mDNSBool inNeedEncryption, + const mdns_dns_service_id_t inCustomID, +#endif + QueryRecordResultHandler inResultHandler, + void * inResultContext); + +mDNSexport void QueryRecordOpStopForClientRequest(QueryRecordOp *op); +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + +#ifdef __cplusplus +} +#endif + +#endif // __ClientRequests_h diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/GenLinkedList.c b/usr/src/contrib/mDNSResponder/mDNSShared/GenLinkedList.c index 1c6cb5be7c..2135c6ae88 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/GenLinkedList.c +++ b/usr/src/contrib/mDNSResponder/mDNSShared/GenLinkedList.c @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2003-2011 Apple Inc. All rights reserved. +/* + * Copyright (c) 2003-2019 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -255,6 +254,9 @@ int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem) { void *iElem, *lastElem; + if (elem == NULL) { + return 0; + } for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; iElem = GetOffsetLink( pList, iElem)) { diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.c b/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.c index 3a13d5ce53..5f785e8080 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.c +++ b/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.c @@ -1,6 +1,6 @@ -/* -*- Mode: C; tab-width: 4 -*- +/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*- * - * Copyright (c) 2004-2015 Apple Inc. All rights reserved. + * Copyright (c) 2004-2019 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,23 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * + * This file defines functions that are common to platforms with Posix APIs. + * Current examples are mDNSMacOSX and mDNSPosix. */ #include <stdio.h> // Needed for fopen() etc. #include <unistd.h> // Needed for close() +#include <stdlib.h> // Needed for malloc() #include <string.h> // Needed for strlen() etc. #include <errno.h> // Needed for errno etc. #include <sys/socket.h> // Needed for socket() etc. #include <netinet/in.h> // Needed for sockaddr_in #include <syslog.h> +#include <sys/fcntl.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <assert.h> #if APPLE_OSX_mDNSResponder #include <os/log.h> @@ -35,6 +43,126 @@ typedef unsigned int socklen_t; #endif +#if MDNS_MALLOC_DEBUGGING +// We ONLY want this for malloc debugging--on a running production system we want to deal with +// malloc failures, not just die. There is a small performance penalty for enabling these options +// as well, so they are all only appropriate for debugging. The flags mean: +// +// A = warnings are errors +// X = abort on failure +// Z = sets J & R +// J = allocated memory is initialized to a pattern +// R causes realloc to always reallocate even if not needed + +char _malloc_options[] = "AXZ"; + +mDNSlocal mDNSListValidator *listValidators; + +mDNSexport void mDNSPlatformAddListValidator(mDNSListValidator *lv, mDNSListValidationFunction *lvf, + const char *lvfName, void *context) +{ + mDNSPlatformMemZero(lv, sizeof *lv); + lv->validator = lvf; + lv->validationFunctionName = lvfName; + lv->context = context; + lv->next = listValidators; + listValidators = lv; +} + +mDNSlocal void validateLists(void) +{ + mDNSListValidator *vfp; + // Check Unix Domain Socket client lists (uds_daemon.c) + for (vfp = listValidators; vfp; vfp = vfp->next) + { + vfp->validator(vfp->context); + } + + mDNSPlatformValidateLists(); +} + +#define kAllocMagic 0xDEAD1234 +#define kGuardMagic 0xDEAD1234 +#define kFreeMagic 0xDEADDEAD +#define kAllocLargeSize 32768 + +mDNSexport void *mallocL(const char *msg, mDNSu32 size) +{ + // Allocate space for two words of sanity checking data before the requested block and two words after. + // Adjust the length for alignment. + mDNSu32 *mem = malloc(sizeof(mDNSu32) * 4 + size); + mDNSu32 guard[2]; + if (!mem) + { LogMsg("malloc( %s : %u ) failed", msg, size); return(NULL); } + else + { + mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)(mem + 2) + size); + if (size > kAllocLargeSize) LogMsg("malloc( %s : %lu ) @ %p suspiciously large", msg, size, &mem[2]); + else if (MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) @ %p", msg, size, &mem[2]); + mem[ 0] = kAllocMagic; + guard[0] = kGuardMagic; + mem[ 1] = size; + guard[1] = size; + memcpy(after, &guard, sizeof guard); + memset(&mem[2], 0xFF, size); + validateLists(); + return(&mem[2]); + } +} + +mDNSexport void *callocL(const char *msg, mDNSu32 size) +{ + mDNSu32 guard[2]; + const mDNSu32 headerSize = 4 * sizeof(mDNSu32); + + // Allocate space for two words of sanity checking data before the requested block and two words after. + // Adjust the length for alignment. + mDNSu32 *mem = (mDNSu32 *)calloc(1, headerSize + size); + if (!mem) + { LogMsg("calloc( %s : %u ) failed", msg, size); return(NULL); } + else + { + mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)(mem + 2) + size); + if (size > kAllocLargeSize) LogMsg("calloc( %s : %lu ) @ %p suspiciously large", msg, size, &mem[2]); + else if (MDNS_MALLOC_DEBUGGING >= 2) LogMsg("calloc( %s : %lu ) @ %p", msg, size, &mem[2]); + mem[ 0] = kAllocMagic; + guard[0] = kGuardMagic; + mem[ 1] = size; + guard[1] = size; + memcpy(after, guard, sizeof guard); + validateLists(); + return(&mem[2]); + } +} + +mDNSexport void freeL(const char *msg, void *x) +{ + if (!x) + LogMsg("free( %s @ NULL )!", msg); + else + { + mDNSu32 *mem = ((mDNSu32 *)x) - 2; + if (mem[0] == kFreeMagic) { LogMemCorruption("free( %s : %lu @ %p ) !!!! ALREADY DISPOSED !!!!", msg, mem[1], &mem[2]); return; } + if (mem[0] != kAllocMagic) { LogMemCorruption("free( %s : %lu @ %p ) !!!! NEVER ALLOCATED !!!!", msg, mem[1], &mem[2]); return; } + if (mem[1] > kAllocLargeSize) LogMsg("free( %s : %lu @ %p) suspiciously large", msg, mem[1], &mem[2]); + else if (MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)", msg, mem[1], &mem[2]); + mDNSu32 *after = (mDNSu32 *)((mDNSu8 *)x + mem[1]); + mDNSu32 guard[2]; + + memcpy(guard, after, sizeof guard); + if (guard[0] != kGuardMagic) { LogMemCorruption("free( %s : %lu @ %p ) !!!! END GUARD OVERWRITE !!!!", + msg, mem[1], &mem[2]); return; } + if (guard[1] != mem[1]) { LogMemCorruption("free( %s : %lu @ %p ) !!!! LENGTH MISMATCH !!!!", + msg, mem[1], &mem[2]); return; } + mem[0] = kFreeMagic; + memset(mem + 2, 0xFF, mem[1] + 2 * sizeof(mDNSu32)); + validateLists(); + free(mem); + } +} + +#endif + // Bind a UDP socket to find the source address to a destination mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst) { @@ -89,7 +217,7 @@ exit: mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f) { char buf[32+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value - unsigned int len = strlen(option); + size_t len = strlen(option); if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; } fseek(f, 0, SEEK_SET); // set position to beginning of stream while (fgets(buf, sizeof(buf), f)) // Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator @@ -135,9 +263,9 @@ mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const fi if (domain && domain->c[0] && buf[0]) { - DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info)); + DomainAuthInfo *info = (DomainAuthInfo*) mDNSPlatformMemAllocateClear(sizeof(*info)); // for now we assume keyname = service reg domain and we use same key for service and hostname registration - err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, mDNSfalse); + err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0); if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c); } @@ -156,6 +284,7 @@ mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg) } #endif +#if !MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel) { #if APPLE_OSX_mDNSResponder && LogTimeStamps @@ -179,26 +308,16 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m { static int log_inited = 0; - int syslog_level = LOG_ERR; + int syslog_level; switch (loglevel) { -#if APPLE_OSX_mDNSResponder - case MDNS_LOG_MSG: syslog_level = OS_LOG_TYPE_DEFAULT; break; - case MDNS_LOG_OPERATION: syslog_level = OS_LOG_TYPE_INFO; break; - case MDNS_LOG_SPS: syslog_level = OS_LOG_TYPE_INFO; break; - case MDNS_LOG_INFO: syslog_level = OS_LOG_TYPE_INFO; break; - case MDNS_LOG_DEBUG: syslog_level = OS_LOG_TYPE_DEBUG; break; - default: syslog_level = OS_LOG_TYPE_DEFAULT; break; -#else - case MDNS_LOG_MSG: syslog_level = LOG_ERR; break; - case MDNS_LOG_OPERATION: syslog_level = LOG_WARNING; break; - case MDNS_LOG_SPS: syslog_level = LOG_NOTICE; break; - case MDNS_LOG_INFO: syslog_level = LOG_INFO; break; - case MDNS_LOG_DEBUG: syslog_level = LOG_DEBUG; break; - default: - fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel); - fflush(stderr); -#endif + case MDNS_LOG_FAULT: syslog_level = LOG_ERR; break; + case MDNS_LOG_ERROR: syslog_level = LOG_ERR; break; + case MDNS_LOG_WARNING: syslog_level = LOG_WARNING; break; + case MDNS_LOG_DEFAULT: syslog_level = LOG_NOTICE; break; + case MDNS_LOG_INFO: syslog_level = LOG_INFO; break; + case MDNS_LOG_DEBUG: syslog_level = LOG_DEBUG; break; + default: syslog_level = LOG_NOTICE; break; } if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; } @@ -209,11 +328,412 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m else #endif { -#if APPLE_OSX_mDNSResponder - mDNSPlatformLogToFile(syslog_level, buffer); -#else syslog(syslog_level, "%s", buffer); + } + } +} +#endif // !MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) + +mDNSexport mDNSBool mDNSPosixTCPSocketSetup(int *fd, mDNSAddr_Type addrType, mDNSIPPort *port, mDNSIPPort *outTcpPort) +{ + int sa_family = (addrType == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + int err; + int sock; + mDNSu32 lowWater = 15384; + + sock = socket(sa_family, SOCK_STREAM, IPPROTO_TCP); + if (sock < 3) + { + if (errno != EAFNOSUPPORT) + { + LogMsg("mDNSPosixTCPSocketSetup: socket error %d errno %d (%s)", sock, errno, strerror(errno)); + } + return mDNStrue; + } + *fd = sock; + + union + { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } addr; + // If port is not NULL, bind to it. + if (port != NULL) + { + socklen_t len = (sa_family == AF_INET) ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6); + mDNSPlatformMemZero(&addr, sizeof addr); + + addr.sa.sa_family = sa_family; +#ifndef NOT_HAVE_SA_LEN + addr.sa.sa_len = len; #endif + if (sa_family == AF_INET6) + { + addr.sin6.sin6_port = port->NotAnInteger; } + else + { + addr.sin.sin_port = port->NotAnInteger; + } + err = bind(sock, &addr.sa, len); + if (err < 0) + { + LogMsg("mDNSPosixTCPSocketSetup getsockname: %s", strerror(errno)); + return mDNSfalse; + } + } + + socklen_t addrlen = sizeof addr; + err = getsockname(sock, (struct sockaddr *)&addr, &addrlen); + if (err < 0) + { + LogMsg("mDNSPosixTCPSocketSetup getsockname: %s", strerror(errno)); + return mDNSfalse; + } + if (sa_family == AF_INET6) + { + outTcpPort->NotAnInteger = addr.sin6.sin6_port; + + } else + { + outTcpPort->NotAnInteger = addr.sin.sin_port; + } + if (port) + port->NotAnInteger = outTcpPort->NotAnInteger; + +#ifdef TCP_NOTSENT_LOWAT + err = setsockopt(sock, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &lowWater, sizeof lowWater); + if (err < 0) + { + LogMsg("mDNSPosixTCPSocketSetup: TCP_NOTSENT_LOWAT failed: %s", strerror(errno)); + return mDNSfalse; + } +#endif + + return mDNStrue; +} + +mDNSexport TCPSocket *mDNSPosixDoTCPListenCallback(int fd, mDNSAddr_Type addressType, TCPSocketFlags socketFlags, + TCPAcceptedCallback callback, void *context) +{ + union + { + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct sockaddr sa; + } address; + + socklen_t slen = sizeof address; + int remoteSock; + mDNSAddr addr; + mDNSIPPort port; + TCPSocket *sock = mDNSNULL; + int failed; + char *nbp; + int i; + mDNSu32 lowWater = 16384; + // When we remember our connection, we remember a name that we can print for logging. But + // since we are the listener in this case, we don't /have/ a name for it. This buffer + // is used to print the IP address into a human readable string which will serve that purpose + // for this case. + char namebuf[INET6_ADDRSTRLEN + 1 + 5 + 1]; + + remoteSock = accept(fd, &address.sa, &slen); + if (remoteSock < 0) + { + LogMsg("mDNSPosixDoTCPListenCallback: accept returned %d", remoteSock); + goto out; + } + + failed = fcntl(remoteSock, F_SETFL, O_NONBLOCK); + if (failed < 0) + { + close(remoteSock); + LogMsg("mDNSPosixDoTCPListenCallback: fcntl returned %d", errno); + goto out; + } + +#ifdef TCP_NOTSENT_LOWAT + failed = setsockopt(remoteSock, IPPROTO_TCP, TCP_NOTSENT_LOWAT, + &lowWater, sizeof lowWater); + if (failed < 0) + { + close(remoteSock); + LogMsg("mDNSPosixDoTCPListenCallback: TCP_NOTSENT_LOWAT returned %d", errno); + goto out; + } +#endif + + if (address.sa.sa_family == AF_INET6) + { + // If we are listening on an IPv4/IPv6 socket, the incoming address might be an IPv4-in-IPv6 address + for (i = 0; i < 10; i++) + { + if (address.sin6.sin6_addr.s6_addr[i] != 0) + { + addr.type = mDNSAddrType_IPv6; + goto nope; + } + } + + // a legit IPv4 address would be ::ffff:a.b.c.d; if there's no ::ffff bit, then it's an IPv6 + // address with a really weird prefix. + if (address.sin6.sin6_addr.s6_addr[10] != 0xFF || address.sin6.sin6_addr.s6_addr[11] != 0xFF) + { + addr.type = mDNSAddrType_IPv6; + } else if (addressType != mDNSAddrType_None) + { + if (inet_ntop(AF_INET, &address.sin6.sin6_addr.s6_addr[12], namebuf, INET6_ADDRSTRLEN + 1) == NULL) + { + strcpy(namebuf, ":unknown:"); + } + LogMsg("mDNSPosixDoTCPListenCallback received an IPv4 connection from %s on an IPv6-only socket.", + namebuf); + close(remoteSock); + goto out; + } + else + { + addr.type = mDNSAddrType_IPv4; + } + nope: + if (addr.type == mDNSAddrType_IPv6) + { + if (inet_ntop(address.sin6.sin6_family, &address.sin6.sin6_addr, namebuf, INET6_ADDRSTRLEN + 1) == NULL) + { + strcpy(namebuf, ":unknown:"); + } + memcpy(&addr.ip.v6, &address.sin6.sin6_addr, sizeof addr.ip.v6); + } + else + { + if (inet_ntop(AF_INET, &address.sin6.sin6_addr.s6_addr[12], namebuf, INET6_ADDRSTRLEN + 1) == NULL) + { + strcpy(namebuf, ":unknown:"); + } + memcpy(&addr.ip.v4, &address.sin6.sin6_addr.s6_addr[12], sizeof addr.ip.v4); + } + port.NotAnInteger = address.sin6.sin6_port; + } + else if (address.sa.sa_family == AF_INET) + { + addr.type = mDNSAddrType_IPv4; + memcpy(&addr.ip.v4, &address.sin.sin_addr, sizeof addr.ip.v4); + port.NotAnInteger = address.sin.sin_port; + if (inet_ntop(AF_INET, &address.sin.sin_addr, namebuf, INET6_ADDRSTRLEN + 1) == NULL) + { + strcpy(namebuf, ":unknown:"); + } + } else { + LogMsg("mDNSPosixDoTCPListenCallback: connection from unknown address family %d", address.sa.sa_family); + close(remoteSock); + goto out; + } + nbp = namebuf + strlen(namebuf); + *nbp++ = '%'; + snprintf(nbp, 6, "%u", ntohs(port.NotAnInteger)); + + sock = mDNSPlatformTCPAccept(socketFlags, remoteSock); + if (sock == NULL) + { + LogMsg("mDNSPosixDoTCPListenCallback: mDNSPlatformTCPAccept returned NULL; dropping connection from %s", + namebuf); + close(remoteSock); + goto out; + } + callback(sock, &addr, &port, namebuf, context); +out: + return sock; +} + +mDNSexport mDNSBool mDNSPosixTCPListen(int *fd, mDNSAddr_Type addrtype, mDNSIPPort *port, mDNSAddr *addr, + mDNSBool reuseAddr, int queueLength) + +{ + union + { + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct sockaddr sa; + } address; + + int failed; + int sock; + int one = 1; + socklen_t sock_len; + + // We require an addrtype parameter because addr is allowed to be null, but they have to agree. + if (addr != mDNSNULL && addr->type != addrtype) + { + LogMsg("mDNSPlatformTCPListen: address type conflict: %d:%d", addr->type, addrtype); + return mDNSfalse; + } + if (port == mDNSNULL) + { + LogMsg("mDNSPlatformTCPListen: port must not be NULL"); + return mDNSfalse; + } + + mDNSPlatformMemZero(&address, sizeof address); + if (addrtype == mDNSAddrType_None || addrtype == mDNSAddrType_IPv6) + { + // Set up DNS listener socket + if (addr != mDNSNULL) + { + memcpy(&address.sin6.sin6_addr.s6_addr, &addr->ip, sizeof address.sin6.sin6_addr.s6_addr); + } + address.sin6.sin6_port = port->NotAnInteger; + + sock_len = sizeof address.sin6; + address.sin6.sin6_family = AF_INET6; + } + else if (addrtype == mDNSAddrType_IPv4) + { + if (addr != mDNSNULL) + { + memcpy(&address.sin.sin_addr.s_addr, &addr->ip, sizeof address.sin.sin_addr.s_addr); + } + address.sin.sin_port = port->NotAnInteger; + sock_len = sizeof address.sin; + address.sin.sin_family = AF_INET; + } + else + { + LogMsg("mDNSPlatformTCPListen: invalid address type: %d", addrtype); + return mDNSfalse; + } +#ifndef NOT_HAVE_SA_LEN + address.sa.sa_len = sock_len; +#endif + sock = socket(address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP); + + if (sock < 0) + { + LogMsg("mDNSPlatformTCPListen: socket call failed: %s", strerror(errno)); + return mDNSfalse; + } + *fd = sock; + + // The reuseAddr flag is used to indicate that we want to listen on this port even if + // there are still lingering sockets. We will still fail if there is another listener. + // Note that this requires SO_REUSEADDR, not SO_REUSEPORT, which does not have special + // handling for lingering sockets. + if (reuseAddr) + { + failed = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); + if (failed < 0) + { + LogMsg("mDNSPlatformTCPListen: SO_REUSEADDR failed %s", strerror(errno)); + return mDNSfalse; + } + } + + // Bind to the port and (if provided) address + failed = bind(sock, &address.sa, sock_len); + if (failed < 0) + { + LogMsg("mDNSPlatformTCPListen: bind failed %s", strerror(errno)); + return mDNSfalse; + } + + // If there was no specified listen port, we need to know what port we got. + if (port->NotAnInteger == 0) + { + mDNSPlatformMemZero(&address, sizeof address); + failed = getsockname(sock, &address.sa, &sock_len); + if (failed < 0) + { + LogMsg("mDNSRelay: getsockname failed: %s", strerror(errno)); + return mDNSfalse; + } + if (address.sa.sa_family == AF_INET) + { + port->NotAnInteger = address.sin.sin_port; + } + else + { + port->NotAnInteger = address.sin6.sin6_port; + } + } + + failed = listen(sock, queueLength); + if (failed < 0) + { + LogMsg("mDNSPlatformTCPListen: listen failed: %s", strerror(errno)); + return mDNSfalse; + } + return mDNStrue; +} + +mDNSexport long mDNSPosixReadTCP(int fd, void *buf, unsigned long buflen, mDNSBool *closed) +{ + static int CLOSEDcount = 0; + static int EAGAINcount = 0; + ssize_t nread = recv(fd, buf, buflen, 0); + + if (nread > 0) + { + CLOSEDcount = 0; + EAGAINcount = 0; + } // On success, clear our error counters + else if (nread == 0) + { + *closed = mDNStrue; + if ((++CLOSEDcount % 20) == 0) + { + LogMsg("ERROR: mDNSPosixReadFromSocket - recv %d got CLOSED %d times", fd, CLOSEDcount); + assert(CLOSEDcount < 1000); + // Recovery Mechanism to bail mDNSResponder out of trouble: Instead of logging the same error + // msg multiple times, crash mDNSResponder using assert() and restart fresh. See advantages + // below: + // 1.Better User Experience + // 2.CrashLogs frequency can be monitored + // 3.StackTrace can be used for more info + } + } + // else nread is negative -- see what kind of error we got + else if (errno == ECONNRESET) + { + nread = 0; *closed = mDNStrue; + } + else if (errno != EAGAIN) + { + LogMsg("ERROR: mDNSPosixReadFromSocket - recv: %d (%s)", errno, strerror(errno)); + nread = -1; + } + else + { // errno is EAGAIN (EWOULDBLOCK) -- no data available + nread = 0; + if ((++EAGAINcount % 1000) == 0) + { + LogMsg("ERROR: mDNSPosixReadFromSocket - recv %d got EAGAIN %d times", fd, EAGAINcount); + sleep(1); + } + } + return nread; +} + +mDNSexport long mDNSPosixWriteTCP(int fd, const char *msg, unsigned long len) +{ + ssize_t result; + long nsent; + + result = write(fd, msg, len); + if (result < 0) + { + if (errno == EAGAIN) + { + nsent = 0; + } + else + { + LogMsg("ERROR: mDNSPosixWriteTCP - send %s", strerror(errno)); nsent = -1; + } + } + else + { + nsent = (long)result; } + return nsent; } diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.h b/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.h index 2a068711e0..fae414ae4c 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.h +++ b/usr/src/contrib/mDNSResponder/mDNSShared/PlatformCommon.h @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 4 -*- +/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*- * * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * @@ -15,4 +15,16 @@ * limitations under the License. */ -extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled); +#ifndef __PLATFORM_COMMON_H +#define __PLATFORM_COMMON_H +extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, + domainname *const hostname, domainname *const domain, + mDNSBool *DomainDiscoveryDisabled); +extern mDNSBool mDNSPosixTCPSocketSetup(int *fd, mDNSAddr_Type addrType, mDNSIPPort *port, mDNSIPPort *outTcpPort); +extern TCPSocket *mDNSPosixDoTCPListenCallback(int fd, mDNSAddr_Type addressType, TCPSocketFlags socketFlags, + TCPAcceptedCallback callback, void *context); +extern mDNSBool mDNSPosixTCPListen(int *fd, mDNSAddr_Type addrtype, mDNSIPPort *port, mDNSAddr *addr, + mDNSBool reuseAddr, int queueLength); +extern long mDNSPosixReadTCP(int fd, void *buf, unsigned long buflen, mDNSBool *closed); +extern long mDNSPosixWriteTCP(int fd, const char *msg, unsigned long len); +#endif diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd.h b/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd.h index 0a4597fae0..690178ac7e 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd.h +++ b/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd.h @@ -66,7 +66,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 8806001 +#define _DNS_SD_H 13108001 #ifdef __cplusplus extern "C" { @@ -197,7 +197,7 @@ enum kDNSServiceFlagsAutoTrigger = 0x1, /* Valid for browses using kDNSServiceInterfaceIndexAny. - * Will auto trigger the browse over AWDL as well once the service is discoveryed + * Will auto trigger the browse over AWDL as well once the service is discovered * over BLE. * This flag is an input value to DNSServiceBrowse(), which is why we can * use the same value as kDNSServiceFlagsMoreComing, which is an output flag @@ -256,7 +256,6 @@ enum /* * Client guarantees that record names are unique, so we can skip sending out initial * probe messages. Standard name conflict resolution is still done if a conflict is discovered. - * Currently only valid for a DNSServiceRegister call. */ kDNSServiceFlagsReturnIntermediates = 0x1000, @@ -273,38 +272,28 @@ enum * (In earlier builds this flag was briefly calledkDNSServiceFlagsReturnCNAME) */ - kDNSServiceFlagsNonBrowsable = 0x2000, - /* A service registered with the NonBrowsable flag set can be resolved using - * DNSServiceResolve(), but will not be discoverable using DNSServiceBrowse(). - * This is for cases where the name is actually a GUID; it is found by other means; - * there is no end-user benefit to browsing to find a long list of opaque GUIDs. - * Using the NonBrowsable flag creates SRV+TXT without the cost of also advertising - * an associated PTR record. - */ - kDNSServiceFlagsShareConnection = 0x4000, /* For efficiency, clients that perform many concurrent operations may want to use a * single Unix Domain Socket connection with the background daemon, instead of having a * separate connection for each independent operation. To use this mode, clients first - * call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef. + * call DNSServiceCreateConnection(&SharedRef) to initialize the main DNSServiceRef. * For each subsequent operation that is to share that same connection, the client copies - * the MainRef, and then passes the address of that copy, setting the ShareConnection flag + * the SharedRef, and then passes the address of that copy, setting the ShareConnection flag * to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef; * it's a copy of an existing DNSServiceRef whose connection information should be reused. * * For example: * * DNSServiceErrorType error; - * DNSServiceRef MainRef; - * error = DNSServiceCreateConnection(&MainRef); + * DNSServiceRef SharedRef; + * error = DNSServiceCreateConnection(&SharedRef); * if (error) ... - * DNSServiceRef BrowseRef = MainRef; // Important: COPY the primary DNSServiceRef first... + * DNSServiceRef BrowseRef = SharedRef; // Important: COPY the primary DNSServiceRef first... * error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, ...); // then use the copy * if (error) ... * ... * DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation - * DNSServiceRefDeallocate(MainRef); // Terminate the shared connection - * Also see Point 4.(Don't Double-Deallocate if the MainRef has been Deallocated) in Notes below: + * DNSServiceRefDeallocate(SharedRef); // Terminate the shared connection * * Notes: * @@ -342,15 +331,18 @@ enum * DNSServiceRef's created by other calls like DNSServiceBrowse() or DNSServiceResolve() * cannot be shared by copying them and using kDNSServiceFlagsShareConnection. * - * 4. Don't Double-Deallocate if the MainRef has been Deallocated - * Calling DNSServiceRefDeallocate(ref) for a particular operation's DNSServiceRef terminates - * just that operation. Calling DNSServiceRefDeallocate(ref) for the main shared DNSServiceRef - * (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&ref)) - * automatically terminates the shared connection and all operations that were still using it. + * 4. Don't Double-Deallocate + * Calling DNSServiceRefDeallocate(OpRef) for a particular operation's DNSServiceRef terminates + * just that operation. Calling DNSServiceRefDeallocate(SharedRef) for the main shared DNSServiceRef + * (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&SharedRef)) + * automatically terminates the shared connection *and* all operations that were still using it. * After doing this, DO NOT then attempt to deallocate any remaining subordinate DNSServiceRef's. * The memory used by those subordinate DNSServiceRef's has already been freed, so any attempt * to do a DNSServiceRefDeallocate (or any other operation) on them will result in accesses * to freed memory, leading to crashes or other equally undesirable results. + * You can deallocate individual operations first and then deallocate the parent DNSServiceRef last, + * but if you deallocate the parent DNSServiceRef first, then all of the subordinate DNSServiceRef's + * are implicitly deallocated, and explicitly deallocating them a second time will lead to crashes. * * 5. Thread Safety * The dns_sd.h API does not presuppose any particular threading model, and consequently @@ -358,15 +350,15 @@ enum * If the client concurrently, from multiple threads (or contexts), calls API routines using * the same DNSServiceRef, it is the client's responsibility to provide mutual exclusion for * that DNSServiceRef. - + * * For example, use of DNSServiceRefDeallocate requires caution. A common mistake is as follows: * Thread B calls DNSServiceRefDeallocate to deallocate sdRef while Thread A is processing events * using sdRef. Doing this will lead to intermittent crashes on thread A if the sdRef is used after * it was deallocated. - + * * A telltale sign of this crash type is to see DNSServiceProcessResult on the stack preceding the * actual crash location. - + * * To state this more explicitly, mDNSResponder does not queue DNSServiceRefDeallocate so * that it occurs discretely before or after an event is handled. */ @@ -414,6 +406,12 @@ enum * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified. */ + kDNSServiceFlagsEnableDNSSEC = 0x200000, + /* + * Perform DNSSEC validation on the client request when kDNSServiceFlagsEnableDNSSEC is specified + * Since the client API has not been finalized, we will use it as a temporary flag to turn on the DNSSEC validation. + */ + kDNSServiceFlagsValidate = 0x200000, /* * This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid @@ -434,8 +432,8 @@ enum * kDNSServiceFlagsAdd and kDNSServiceFlagsValidate. * * The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback. - * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the - * other applicable output flags should be masked. See kDNSServiceOutputFlags below. + * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the + * other applicable output flags should be masked. */ kDNSServiceFlagsSecure = 0x200010, @@ -519,26 +517,38 @@ enum * is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on * input to a DNSServiceBrowse call. */ - kDNSServiceFlagsPrivateOne = 0x8000000, + kDNSServiceFlagsPrivateOne = 0x2000, + /* + * This flag is private and should not be used. + */ + + kDNSServiceFlagsPrivateTwo = 0x8000000, /* * This flag is private and should not be used. */ - kDNSServiceFlagsPrivateTwo = 0x10000000, + kDNSServiceFlagsPrivateThree = 0x10000000, /* * This flag is private and should not be used. */ - kDNSServiceFlagsPrivateThree = 0x20000000, + kDNSServiceFlagsPrivateFour = 0x20000000, /* * This flag is private and should not be used. */ - kDNSServiceFlagsPrivateFour = 0x40000000, + kDNSServiceFlagsPrivateFive = 0x40000000, /* * This flag is private and should not be used. */ + + kDNSServiceFlagAnsweredFromCache = 0x40000000, + /* + * When kDNSServiceFlagAnsweredFromCache is passed back in the flags parameter of DNSServiceQueryRecordReply or DNSServiceGetAddrInfoReply, + * an answer will have this flag set if it was answered from the cache. + */ + kDNSServiceFlagsAllowExpiredAnswers = 0x80000000, /* * When kDNSServiceFlagsAllowExpiredAnswers is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, @@ -555,9 +565,6 @@ enum }; -#define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault) - /* All the output flags excluding the DNSSEC Status flags. Typically used to check DNSSEC Status */ - /* Possible protocol values */ enum { @@ -647,6 +654,9 @@ enum kDNSServiceType_HIP = 55, /* Host Identity Protocol */ + kDNSServiceType_SVCB = 64, /* Service Binding. */ + kDNSServiceType_HTTPS = 65, /* HTTPS Service Binding. */ + kDNSServiceType_SPF = 99, /* Sender Policy Framework for E-Mail */ kDNSServiceType_UINFO = 100, /* IANA-Reserved */ kDNSServiceType_UID = 101, /* IANA-Reserved */ @@ -659,7 +669,7 @@ enum kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ - kDNSServiceType_ANY = 255 /* Wildcard match. */ + kDNSServiceType_ANY = 255 /* Wildcard match. */ }; /* possible error code values */ @@ -696,7 +706,9 @@ enum kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */ kDNSServiceErr_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */ kDNSServiceErr_PollingMode = -65567, - kDNSServiceErr_Timeout = -65568 + kDNSServiceErr_Timeout = -65568, + kDNSServiceErr_DefunctConnection = -65569, /* Connection to daemon returned a SO_ISDEFUNCT error result */ + kDNSServiceErr_PolicyDenied = -65570 /* mDNS Error codes are in the range * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ @@ -722,8 +734,10 @@ enum * conventional DNS escaping rules, as used by the traditional DNS res_query() API, as described below: * * Generally all UTF-8 characters (which includes all US ASCII characters) represent themselves, - * with two exceptions, the dot ('.') character, which is the label separator, - * and the backslash ('\') character, which is the escape character. + * with three exceptions: + * the dot ('.') character, which is the DNS label separator, + * the backslash ('\') character, which is the DNS escape character, and + * the ASCII NUL (0) byte value, which is the C-string terminator character. * The escape character ('\') is interpreted as described below: * * '\ddd', where ddd is a three-digit decimal value from 000 to 255, @@ -732,11 +746,11 @@ enum * For example, the ASCII code for 'w' is 119, and therefore '\119' is equivalent to 'w'. * Thus the command "ping '\119\119\119.apple.com'" is the equivalent to the command "ping 'www.apple.com'". * Nonprinting ASCII characters in the range 0-31 are often represented this way. - * In particular, the ASCII NUL character (0) cannot appear in a C string because C uses it as the - * string terminator character, so ASCII NUL in a domain name has to be represented in a C string as '\000'. + * In particular, the ASCII NUL character (0) cannot appear in a C-string because C uses it as the + * string terminator character, so ASCII NUL in a domain name has to be represented in a C-string as '\000'. * Other characters like space (ASCII code 32) are sometimes represented as '\032' - * in contexts where having an actual space character in a C string would be inconvenient. - * + * in contexts where having an actual space character in a C-string would be inconvenient. + * * Otherwise, for all cases where a '\' is followed by anything other than a three-digit decimal value * from 000 to 255, the character sequence '\x' represents a single literal occurrence of character 'x'. * This is legal for any character, so, for example, '\w' is equivalent to 'w'. @@ -751,6 +765,21 @@ enum * followed by neither a three-digit decimal value from 000 to 255 nor a single character. * If a lone escape character ('\') does appear as the last character of a string, it is silently ignored. * + * The worse-case length for an escaped domain name is calculated as follows: + * The longest legal domain name is 256 bytes in wire format (see RFC 6762, Appendix C, DNS Name Length). + * For our calculation of the longest *escaped* domain name, we use + * the longest legal domain name, with the most characters escaped. + * + * We consider a domain name of the form: "label63.label63.label63.label62." + * where "label63" is a 63-byte label and "label62" is a 62-byte label. + * Counting four label-length bytes, 251 bytes of label data, and the terminating zero, + * this makes a total of 256 bytes in wire format, the longest legal domain name. + * + * If each one of the 251 bytes of label data is represented using '\ddd', + * then it takes 251 * 4 = 1004 bytes to represent these in a C-string. + * Adding four '.' characters as shown above, plus the C-string terminating + * zero at the end, results in a maximum storage requirement of 1009 bytes. + * * The exceptions, that do not use escaping, are the routines where the full * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. * In these routines, the "servicename" is NOT escaped. It does not need to be, since @@ -997,6 +1026,9 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent * functions. * + * If the reference was passed to DNSServiceSetDispatchQueue(), DNSServiceRefDeallocate() must + * be called on the same queue originally passed as an argument to DNSServiceSetDispatchQueue(). + * * Note: This call is to be used only with the DNSServiceRef defined by this API. * * sdRef: A DNSServiceRef initialized by any of the DNSService calls. @@ -1061,12 +1093,17 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) /* DNSServiceEnumerateDomains() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the enumeration operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef + * (or, if the kDNSServiceFlagsShareConnection flag is used, + * a copy of the shared connection reference that is to be used). + * If the call succeeds then it initializes (or updates) the DNSServiceRef, + * returns kDNSServiceErr_NoError, and the enumeration operation + * will remain active indefinitely until the client terminates it + * by passing this DNSServiceRef to DNSServiceRefDeallocate() + * (or by closing the underlying shared connection, if used). * * flags: Possible values are: + * kDNSServiceFlagsShareConnection to use a shared connection. * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. * kDNSServiceFlagsRegistrationDomains to enumerate domains recommended * for registration. @@ -1154,13 +1191,20 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) /* DNSServiceRegister() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the registration will remain active indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef + * (or, if the kDNSServiceFlagsShareConnection flag is used, + * a copy of the shared connection reference that is to be used). + * If the call succeeds then it initializes (or updates) the DNSServiceRef, + * returns kDNSServiceErr_NoError, and the service registration + * will remain active indefinitely until the client terminates it + * by passing this DNSServiceRef to DNSServiceRefDeallocate() + * (or by closing the underlying shared connection, if used). * - * flags: Indicates the renaming behavior on name conflict (most applications - * will pass 0). See flag definitions above for details. + * flags: Possible values are: + * kDNSServiceFlagsShareConnection to use a shared connection. + * Other flags indicate the renaming behavior on name conflict + * (not required for most applications). + * See flag definitions above for details. * * interfaceIndex: If non-zero, specifies the interface on which to register the service * (the index for a given interface is determined via the if_nametoindex() @@ -1210,31 +1254,6 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123 * - * When a service is registered, all the clients browsing for the registered - * type ("regtype") will discover it. If the discovery should be - * restricted to a smaller set of well known peers, the service can be - * registered with additional data (group identifier) that is known - * only to a smaller set of peers. The group identifier should follow primary - * service type using a colon (":") as a delimeter. If subtypes are also present, - * it should be given before the subtype as shown below. - * - * % dns-sd -R _test1 _http._tcp:mygroup1 local 1001 - * % dns-sd -R _test2 _http._tcp:mygroup2 local 1001 - * % dns-sd -R _test3 _http._tcp:mygroup3,HasFeatureA local 1001 - * - * Now: - * % dns-sd -B _http._tcp:"mygroup1" # will discover only test1 - * % dns-sd -B _http._tcp:"mygroup2" # will discover only test2 - * % dns-sd -B _http._tcp:"mygroup3",HasFeatureA # will discover only test3 - * - * By specifying the group information, only the members of that group are - * discovered. - * - * The group identifier itself is not sent in clear. Only a hash of the group - * identifier is sent and the clients discover them anonymously. The group identifier - * may be up to 256 bytes long and may contain any eight bit values except comma which - * should be escaped. - * * domain: If non-NULL, specifies the domain on which to advertise the service. * Most applications will not specify a domain, instead automatically * registering in the default domain(s). @@ -1478,12 +1497,17 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) /* DNSServiceBrowse() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the browse operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef + * (or, if the kDNSServiceFlagsShareConnection flag is used, + * a copy of the shared connection reference that is to be used). + * If the call succeeds then it initializes (or updates) the DNSServiceRef, + * returns kDNSServiceErr_NoError, and the browse operation + * will remain active indefinitely until the client terminates it + * by passing this DNSServiceRef to DNSServiceRefDeallocate() + * (or by closing the underlying shared connection, if used). * - * flags: Currently ignored, reserved for future use. + * flags: Possible values are: + * kDNSServiceFlagsShareConnection to use a shared connection. * * interfaceIndex: If non-zero, specifies the interface on which to browse for services * (the index for a given interface is determined via the if_nametoindex() @@ -1495,10 +1519,7 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) * A client may optionally specify a single subtype to perform filtered browsing: * e.g. browsing for "_primarytype._tcp,_subtype" will discover only those * instances of "_primarytype._tcp" that were registered specifying "_subtype" - * in their list of registered subtypes. Additionally, a group identifier may - * also be specified before the subtype e.g., _primarytype._tcp:GroupID, which - * will discover only the members that register the service with GroupID. See - * DNSServiceRegister for more details. + * in their list of registered subtypes. * * domain: If non-NULL, specifies the domain on which to browse for services. * Most applications will not specify a domain, instead browsing on the @@ -1607,12 +1628,18 @@ typedef void (DNSSD_API *DNSServiceResolveReply) /* DNSServiceResolve() Parameters * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the resolve operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef + * (or, if the kDNSServiceFlagsShareConnection flag is used, + * a copy of the shared connection reference that is to be used). + * If the call succeeds then it initializes (or updates) the DNSServiceRef, + * returns kDNSServiceErr_NoError, and the resolve operation + * will remain active indefinitely until the client terminates it + * by passing this DNSServiceRef to DNSServiceRefDeallocate() + * (or by closing the underlying shared connection, if used). * - * flags: Specifying kDNSServiceFlagsForceMulticast will cause query to be + * flags: Possible values are: + * kDNSServiceFlagsShareConnection to use a shared connection. + * Specifying kDNSServiceFlagsForceMulticast will cause query to be * performed with a link-local mDNS query, even if the name is an * apparently non-local name (i.e. a name not ending in ".local.") * @@ -1729,12 +1756,18 @@ typedef void (DNSSD_API *DNSServiceQueryRecordReply) /* DNSServiceQueryRecord() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the query operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef + * (or, if the kDNSServiceFlagsShareConnection flag is used, + * a copy of the shared connection reference that is to be used). + * If the call succeeds then it initializes (or updates) the DNSServiceRef, + * returns kDNSServiceErr_NoError, and the query operation + * will remain active indefinitely until the client terminates it + * by passing this DNSServiceRef to DNSServiceRefDeallocate() + * (or by closing the underlying shared connection, if used). * - * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery. + * flags: Possible values are: + * kDNSServiceFlagsShareConnection to use a shared connection. + * kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery. * Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast * query to a unicast DNS server that implements the protocol. This flag * has no effect on link-local multicast queries. @@ -1835,12 +1868,18 @@ typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) /* DNSServiceGetAddrInfo() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it - * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the query - * begins and will last indefinitely until the client terminates the query - * by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef + * (or, if the kDNSServiceFlagsShareConnection flag is used, + * a copy of the shared connection reference that is to be used). + * If the call succeeds then it initializes (or updates) the DNSServiceRef, + * returns kDNSServiceErr_NoError, and the address query operation + * will remain active indefinitely until the client terminates it + * by passing this DNSServiceRef to DNSServiceRefDeallocate() + * (or by closing the underlying shared connection, if used). * - * flags: kDNSServiceFlagsForceMulticast + * flags: Possible values are: + * kDNSServiceFlagsShareConnection to use a shared connection. + * kDNSServiceFlagsForceMulticast * * interfaceIndex: The interface on which to issue the query. Passing 0 causes the query to be * sent on all active interfaces via Multicast or the primary interface via Unicast. @@ -1896,13 +1935,14 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo * * Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating - * the reference (via DNSServiceRefDeallocate()) severs the - * connection and deregisters all records registered on this connection. + * sdRef: A pointer to an uninitialized DNSServiceRef. + * Deallocating the reference (via DNSServiceRefDeallocate()) + * severs the connection and cancels all operations and + * deregisters all records registered on this connection. * * return value: Returns kDNSServiceErr_NoError on success, otherwise returns - * an error code indicating the specific failure that occurred (in which - * case the DNSServiceRef is not initialized). + * an error code indicating the specific failure that occurred + * (in which case the DNSServiceRef is not initialized). */ DNSSD_EXPORT @@ -1954,8 +1994,7 @@ typedef void (DNSSD_API *DNSServiceRegisterRecordReply) * and deallocate each of their corresponding DNSServiceRecordRefs, call * DNSServiceRefDeallocate()). * - * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique - * (see flag type definitions for details). + * flags: One of either kDNSServiceFlagsShared, kDNSServiceFlagsUnique or kDNSServiceFlagsKnownUnique must be set. * * interfaceIndex: If non-zero, specifies the interface on which to register the record * (the index for a given interface is determined via the if_nametoindex() @@ -2175,15 +2214,20 @@ typedef void (DNSSD_API *DNSServiceNATPortMappingReply) /* DNSServiceNATPortMappingCreate() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it - * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the nat - * port mapping will last indefinitely until the client terminates the port - * mapping request by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef + * (or, if the kDNSServiceFlagsShareConnection flag is used, + * a copy of the shared connection reference that is to be used). + * If the call succeeds then it initializes (or updates) the DNSServiceRef, + * returns kDNSServiceErr_NoError, and the NAT port mapping + * will remain active indefinitely until the client terminates it + * by passing this DNSServiceRef to DNSServiceRefDeallocate() + * (or by closing the underlying shared connection, if used). * - * flags: Currently ignored, reserved for future use. + * flags: Possible values are: + * kDNSServiceFlagsShareConnection to use a shared connection. * - * interfaceIndex: The interface on which to create port mappings in a NAT gateway. Passing 0 causes - * the port mapping request to be sent on the primary interface. + * interfaceIndex: The interface on which to create port mappings in a NAT gateway. + * Passing 0 causes the port mapping request to be sent on the primary interface. * * protocol: To request a port mapping, pass in kDNSServiceProtocol_UDP, or kDNSServiceProtocol_TCP, * or (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP) to map both. @@ -2315,7 +2359,11 @@ typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignmen * * If the buffer parameter is NULL, or the specified storage size is not * large enough to hold a key subsequently added using TXTRecordSetValue(), - * then additional memory will be added as needed using malloc(). + * then additional memory will be added as needed using malloc(). Note that + * an existing TXT record buffer should not be passed to TXTRecordCreate + * to create a copy of another TXT Record. The correct way to copy TXTRecordRef + * is creating an empty TXTRecordRef with TXTRecordCreate() first, and using + * TXTRecordSetValue to set the same value. * * On some platforms, when memory is low, malloc() may fail. In this * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this @@ -2613,7 +2661,7 @@ uint16_t DNSSD_API TXTRecordGetCount * keyBufLen: The size of the string buffer being supplied. * * key: A string buffer used to store the key name. - * On return, the buffer contains a null-terminated C string + * On return, the buffer contains a null-terminated C-string * giving the key name. DNS-SD TXT keys are usually * 9 characters or fewer. To hold the maximum possible * key name, the buffer should be 256 bytes long. @@ -2670,6 +2718,8 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex * DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch * queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until * the application no longer requires that operation and terminates it using DNSServiceRefDeallocate. + * Note that the call to DNSServiceRefDeallocate() must be done on the same queue originally passed + * as an argument to DNSServiceSetDispatchQueue(). * * service: DNSServiceRef that was allocated and returned to the application, when the * application calls one of the DNSService API. diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_internal.h b/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_internal.h index 4be93e943b..a3755a1f61 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_internal.h +++ b/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_internal.h @@ -1,15 +1,28 @@ -/* -*- Mode: C; tab-width: 4 -*- +/* + * Copyright (c) 2016-2020 Apple Inc. All rights reserved. * - * Copyright (c) 2016 Apple Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #ifndef _DNS_SD_INTERNAL_H #define _DNS_SD_INTERNAL_H -#if !APPLE_OSX_mDNSResponder -#define DNSSD_NO_CREATE_DELEGATE_CONNECTION 1 +// The mDNSResponder daemon doesn't call the private DNS-SD API. + +#if !defined(DNS_SD_EXCLUDE_PRIVATE_API) + #define DNS_SD_EXCLUDE_PRIVATE_API 1 #endif #include "dns_sd_private.h" -#endif +#endif // _DNS_SD_INTERNAL_H diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_private.h b/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_private.h index 2d42973361..1c4baabfb2 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_private.h +++ b/usr/src/contrib/mDNSResponder/mDNSShared/dns_sd_private.h @@ -1,6 +1,17 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2015-2018 Apple Inc. All rights reserved. +/* + * Copyright (c) 2015-2020 Apple Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #ifndef _DNS_SD_PRIVATE_H @@ -8,9 +19,23 @@ #include <dns_sd.h> -// Private flags (kDNSServiceFlagsPrivateOne, kDNSServiceFlagsPrivateTwo, kDNSServiceFlagsPrivateThree, kDNSServiceFlagsPrivateFour) from dns_sd.h +#if !defined(DNS_SD_EXCLUDE_PRIVATE_API) + #if defined(__APPLE__) + #define DNS_SD_EXCLUDE_PRIVATE_API 0 + #else + #define DNS_SD_EXCLUDE_PRIVATE_API 1 + #endif +#endif + +// Private flags (kDNSServiceFlagsPrivateOne, kDNSServiceFlagsPrivateTwo, kDNSServiceFlagsPrivateThree, kDNSServiceFlagsPrivateFour, kDNSServiceFlagsPrivateFive) from dns_sd.h enum { + kDNSServiceFlagsDenyConstrained = 0x2000, + /* + * This flag is meaningful only for Unicast DNS queries. When set, the daemon will restrict + * DNS resolutions on interfaces defined as constrained for that request. + */ + kDNSServiceFlagsDenyCellular = 0x8000000, /* * This flag is meaningful only for Unicast DNS queries. When set, the daemon will restrict @@ -36,8 +61,7 @@ enum */ }; - -#if !DNSSD_NO_CREATE_DELEGATE_CONNECTION +#if !DNS_SD_EXCLUDE_PRIVATE_API /* DNSServiceCreateDelegateConnection() * * Parameters: @@ -61,7 +85,6 @@ enum */ DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid); -#endif // Map the source port of the local UDP socket that was opened for sending the DNS query // to the process ID of the application that triggered the DNS resolution. @@ -89,7 +112,50 @@ DNSServiceErrorType DNSSD_API DNSServiceGetPID DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain); +SPI_AVAILABLE(macos(10.15.4), ios(13.2.2), watchos(6.2), tvos(13.2)) +DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive_sockaddr +( + DNSServiceRef * sdRef, + DNSServiceFlags flags, + const struct sockaddr * localAddr, + const struct sockaddr * remoteAddr, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void * context +); + +/*! + * @brief + * Sets the default DNS resolver settings for the caller's process. + * + * @param plist_data_ptr + * Pointer to an nw_resolver_config's binary property list data. + * + * @param plist_data_len + * Byte-length of the binary property list data. Ignored if plist_data_ptr is NULL. + * + * @param require_encryption + * Pass true if the process requires that DNS queries use an encrypted DNS service, such as DNS over HTTPS. + * + * @result + * This function returns kDNSServiceErr_NoError on success, kDNSServiceErr_Invalid if plist_data_len + * exceeds 32,768, and kDNSServiceErr_NoMemory if it fails to allocate memory. + * + * @discussion + * These settings only apply to the calling process's DNSServiceGetAddrInfo and DNSServiceQueryRecord + * requests. This function exists for code that may still use the legacy DNS-SD API for resolving + * hostnames, i.e., it implements the functionality of dnssd_getaddrinfo_set_need_encrypted_query(), but at + * a process-wide level of granularity. + * + * Due to underlying IPC limitations, there's currently a 32 KB limit on the size of the binary property + * list data. + */ +SPI_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)) +DNSServiceErrorType DNSSD_API DNSServiceSetResolverDefaults(const void *plist_data_ptr, size_t plist_data_len, + bool require_encryption); +#endif // !DNS_SD_EXCLUDE_PRIVATE_API + #define kDNSServiceCompPrivateDNS "PrivateDNS" #define kDNSServiceCompMulticastDNS "MulticastDNS" -#endif +#endif // _DNS_SD_PRIVATE_H diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_clientstub.c b/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_clientstub.c index 189c9361fa..58b5a460e2 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_clientstub.c +++ b/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_clientstub.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2015 Apple Inc. All rights reserved. + * Copyright (c) 2003-2020 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,7 +36,14 @@ #include <mach-o/dyld.h> #include <uuid/uuid.h> #include <TargetConditionals.h> -#include "dns_sd_internal.h" +#include "dns_sd_private.h" +#include "dnssd_clientstub_apple.h" +#include <CoreUtils/CommonServices.h> +#if !defined(__i386__) +#define CHECK_BUNDLE_VERSION 1 +#else +#define CHECK_BUNDLE_VERSION 0 +#endif #endif #if defined(_WIN32) @@ -81,7 +88,7 @@ static void syslog( int priority, const char * message, ...) } #else - #include <sys/fcntl.h> // For O_RDWR etc. + #include <fcntl.h> // For O_RDWR etc. #include <sys/time.h> #include <sys/socket.h> #include <syslog.h> @@ -91,9 +98,16 @@ static void syslog( int priority, const char * message, ...) #endif +#if CHECK_BUNDLE_VERSION +#include "bundle_utilities.h" +#include <os/feature_private.h> +#endif + +#if defined(_WIN32) // <rdar://problem/4096913> Specifies how many times we'll try and connect to the server. #define DNSSD_CLIENT_MAXTRIES 4 +#endif // _WIN32 // Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp) //#define USE_NAMED_ERROR_RETURN_SOCKET 1 @@ -173,6 +187,19 @@ struct _DNSRecordRef_t DNSServiceOp *sdr; }; +#if CHECK_BUNDLE_VERSION +static bool _should_return_noauth_error(void) +{ + static dispatch_once_t s_once = 0; + static bool s_should = false; + dispatch_once(&s_once, + ^{ + s_should = bundle_sdk_is_ios14_or_later(); + }); + return s_should; +} +#endif + #if !defined(USE_TCP_LOOPBACK) static void SetUDSPath(struct sockaddr_un *saddr, const char *path) { @@ -186,11 +213,13 @@ static void SetUDSPath(struct sockaddr_un *saddr, const char *path) } #endif +enum { write_all_success = 0, write_all_fail = -1, write_all_defunct = -2 }; + // Write len bytes. Return 0 on success, -1 on error static int write_all(dnssd_sock_t sd, char *buf, size_t len) { // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. - //if (send(sd, buf, len, MSG_WAITALL) != len) return -1; + //if (send(sd, buf, len, MSG_WAITALL) != len) return write_all_fail; while (len) { ssize_t num_written = send(sd, buf, (long)len, 0); @@ -211,21 +240,22 @@ static int write_all(dnssd_sock_t sd, char *buf, size_t len) (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); else syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd); + return defunct ? write_all_defunct : write_all_fail; #else syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, (long)num_written, (long)len, (num_written < 0) ? dnssd_errno : 0, (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); + return write_all_fail; #endif - return -1; } buf += num_written; len -= num_written; } - return 0; + return write_all_success; } -enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2 }; +enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2, read_all_defunct = -3 }; // Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for static int read_all(dnssd_sock_t sd, char *buf, int len) @@ -273,7 +303,7 @@ static int read_all(dnssd_sock_t sd, char *buf, int len) (num_read < 0) ? dnssd_strerror(dnssd_errno) : ""); else if (defunct) syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd); - return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : read_all_fail; + return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : (defunct ? read_all_defunct : read_all_fail); } buf += num_read; len -= num_read; @@ -295,6 +325,7 @@ static int more_bytes(dnssd_sock_t sd) FD_SET(sd, fs); ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); #else + // This whole thing would probably be better done using kevent() instead of select() if (sd < FD_SETSIZE) { fs = &readfds; @@ -333,6 +364,10 @@ static int set_waitlimit(dnssd_sock_t sock, int timeout) { int gDaemonErr = kDNSServiceErr_NoError; + // The comment below is wrong. The select() routine does not cause stack corruption. + // The use of FD_SET out of range for the bitmap is what causes stack corruption. + // For how to do this correctly, see the example using calloc() in more_bytes() above. + // Even better, both should be changed to use kevent() instead of select(). // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024) if (!gDaemonErr && sock < FD_SETSIZE) { @@ -431,9 +466,6 @@ static void FreeDNSServiceOp(DNSServiceOp *x) // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket) if ((x->sockfd ^ x->validator) != ValidatorBits) { - static DNSServiceOp *op_were_not_going_to_free_but_we_need_to_fool_the_analyzer; - syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator); - op_were_not_going_to_free_but_we_need_to_fool_the_analyzer = x; } else { @@ -469,7 +501,9 @@ static void FreeDNSServiceOp(DNSServiceOp *x) // Return a connected service ref (deallocate with DNSServiceRefDeallocate) static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext) { + #if defined(_WIN32) int NumTries = 0; + #endif // _WIN32 dnssd_sockaddr_t saddr; DNSServiceOp *sdr; @@ -534,7 +568,7 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f sdr->disp_queue = NULL; #endif sdr->kacontext = NULL; - + if (flags & kDNSServiceFlagsShareConnection) { DNSServiceOp **p = &(*ref)->next; // Append ourselves to end of primary's list @@ -574,6 +608,22 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f FreeDNSServiceOp(sdr); return kDNSServiceErr_NoMemory; } +#if !defined(_WIN32) + int fcntl_flags = fcntl(sdr->sockfd, F_GETFD); + if (fcntl_flags != -1) + { + fcntl_flags |= FD_CLOEXEC; + int ret = fcntl(sdr->sockfd, F_SETFD, fcntl_flags); + if (ret == -1) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: Failed to set FD_CLOEXEC on socket %d %s", + dnssd_errno, dnssd_strerror(dnssd_errno)); + } + else + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: Failed to get the file descriptor flags of socket %d %s", + dnssd_errno, dnssd_strerror(dnssd_errno)); + } +#endif // !defined(_WIN32) #ifdef SO_NOSIGPIPE // Some environments (e.g. OS X) support turning off SIGPIPE for a socket if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) @@ -595,11 +645,13 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f #endif #endif + #if defined(_WIN32) while (1) { int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); if (!err) break; // If we succeeded, return sdr + // If we failed, then it may be because the daemon is still launching. // This can happen for processes that launch early in the boot process, while the // daemon is still coming up. Rather than fail here, we wait 1 sec and try again. @@ -621,7 +673,19 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f return kDNSServiceErr_ServiceNotRunning; } } - //printf("ConnectToServer opened socket %d\n", sdr->sockfd); + #else + int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); + if (err) + { + #if !defined(USE_TCP_LOOPBACK) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed path:%s Socket:%d Err:%d Errno:%d %s", + uds_serverpath, sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno)); + #endif + dnssd_close(sdr->sockfd); + FreeDNSServiceOp(sdr); + return kDNSServiceErr_ServiceNotRunning; + } + #endif } *ref = sdr; @@ -637,6 +701,7 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; DNSServiceErrorType err = kDNSServiceErr_Unknown; // Default for the "goto cleanup" cases int MakeSeparateReturnSocket; + int ioresult; #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) char *data; #endif @@ -736,7 +801,7 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) { int defunct = 1; if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0) - syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + syslog(LOG_WARNING, "dnssd_clientstub deliver_request: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); } #endif } @@ -764,18 +829,25 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) for (i=0; i<datalen + sizeof(ipc_msg_hdr); i++) { syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %d", i); - if (write_all(sdr->sockfd, ((char *)hdr)+i, 1) < 0) - { syslog(LOG_WARNING, "write_all (byte %u) failed", i); goto cleanup; } + ioresult = write_all(sdr->sockfd, ((char *)hdr)+i, 1); + if (ioresult < write_all_success) + { + syslog(LOG_WARNING, "dnssd_clientstub deliver_request write_all (byte %u) failed", i); + err = (ioresult == write_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; + goto cleanup; + } usleep(10000); } #else - if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0) + ioresult = write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)); + if (ioresult < write_all_success) { // write_all already prints an error message if there is an error writing to // the socket except for DEFUNCT. Logging here is unnecessary and also wrong // in the case of DEFUNCT sockets syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed", sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr))); + err = (ioresult == write_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; goto cleanup; } #endif @@ -818,9 +890,9 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) { snprintf(p, sizeof(p), "/dev/bpf%d", i); listenfd = open(p, O_RDWR, 0); - //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p); + //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "dnssd_clientstub deliver_request Sending fd %d for %s", listenfd, p); if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY) - syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno)); + syslog(LOG_WARNING, "dnssd_clientstub deliver_request Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno)); if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break; } } @@ -839,7 +911,7 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) #endif #if DEBUG_64BIT_SCM_RIGHTS - syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld", + syslog(LOG_WARNING, "dnssd_clientstub deliver_request sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld", errsd, listenfd, sizeof(dnssd_sock_t), sizeof(void*), sizeof(struct cmsghdr) + sizeof(dnssd_sock_t), CMSG_LEN(sizeof(dnssd_sock_t)), (long)CMSG_SPACE(sizeof(dnssd_sock_t)), @@ -855,7 +927,7 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) } #if DEBUG_64BIT_SCM_RIGHTS - syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d okay", errsd, listenfd); + syslog(LOG_WARNING, "dnssd_clientstub deliver_request sendmsg read sd=%d write sd=%d okay", errsd, listenfd); #endif // DEBUG_64BIT_SCM_RIGHTS #endif @@ -875,8 +947,9 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) err = kDNSServiceErr_NoError; else if ((err = set_waitlimit(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError) { - if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0) - err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us + ioresult = read_all(errsd, (char*)&err, (int)sizeof(err)); + if (ioresult < read_all_success) + err = (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us else err = ntohl(err); } @@ -964,7 +1037,7 @@ static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error) if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext); // The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records. // Detect that and return early - if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:Record: CallbackwithError morebytes zero"); return;} + if (!morebytes) { syslog(LOG_WARNING, "dnssd_clientstub:Record: CallbackwithError morebytes zero"); return; } rec = recnext; } break; @@ -982,7 +1055,7 @@ static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error) // // If DNSServiceRefDeallocate was not called in the callback, then set moreptr to NULL so that // we don't access the stack variable after we return from this function. - if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:sdRef: CallbackwithError morebytes zero sdr %p", sdr); return;} + if (!morebytes) { syslog(LOG_WARNING, "dnssd_clientstub:sdRef: CallbackwithError morebytes zero sdr %p", sdr); return; } else {sdr->moreptr = NULL;} sdr = sdrNext; } @@ -994,6 +1067,8 @@ static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error) DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) { int morebytes = 0; + int ioresult; + DNSServiceErrorType error; if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } @@ -1026,9 +1101,11 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) // where a non-blocking socket is told there is data, but it was a false positive. // On error, read_all will write a message to syslog for us, so don't need to duplicate that here // Note: If we want to properly support using non-blocking sockets in the future - int result = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr)); - if (result == read_all_fail) + ioresult = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr)); + if (ioresult == read_all_fail || ioresult == read_all_defunct) { + error = (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated // in the callback. sdRef->ProcessReply = NULL; @@ -1043,13 +1120,13 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) dispatch_source_cancel(sdRef->disp_source); dispatch_release(sdRef->disp_source); sdRef->disp_source = NULL; - CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + CallbackWithError(sdRef, error); } #endif // Don't touch sdRef anymore as it might have been deallocated - return kDNSServiceErr_ServiceNotRunning; + return error; } - else if (result == read_all_wouldblock) + else if (ioresult == read_all_wouldblock) { if (morebytes && sdRef->logcounter < 100) { @@ -1069,8 +1146,11 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) data = malloc(cbh.ipc_hdr.datalen); if (!data) return kDNSServiceErr_NoMemory; - if (read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen) < 0) // On error, read_all will write a message to syslog for us + ioresult = read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen); + if (ioresult < read_all_success) // On error, read_all will write a message to syslog for us { + error = (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated // in the callback. sdRef->ProcessReply = NULL; @@ -1083,12 +1163,12 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) dispatch_source_cancel(sdRef->disp_source); dispatch_release(sdRef->disp_source); sdRef->disp_source = NULL; - CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + CallbackWithError(sdRef, error); } #endif // Don't touch sdRef anymore as it might have been deallocated free(data); - return kDNSServiceErr_ServiceNotRunning; + return error; } else { @@ -1207,6 +1287,7 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void * ipc_msg_hdr *hdr; DNSServiceOp *tmp; uint32_t actualsize; + int ioresult; if (!property || !result || !size) return kDNSServiceErr_BadParam; @@ -1222,12 +1303,14 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void * err = deliver_request(hdr, tmp); // Will free hdr for us if (err) { DNSServiceRefDeallocate(tmp); return err; } - if (read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)) < 0) - { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + ioresult = read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)); + if (ioresult < read_all_success) + { DNSServiceRefDeallocate(tmp); return (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; } actualsize = ntohl(actualsize); - if (read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size) < 0) - { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + ioresult = read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size); + if (ioresult < read_all_success) + { DNSServiceRefDeallocate(tmp); return (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; } DNSServiceRefDeallocate(tmp); // Swap version result back to local process byte order @@ -1244,6 +1327,7 @@ DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t * ipc_msg_hdr *hdr; DNSServiceOp *tmp = NULL; size_t len = sizeof(int32_t); + int ioresult; DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL); if (err) return err; @@ -1255,8 +1339,9 @@ DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t * err = deliver_request(hdr, tmp); // Will free hdr for us if (err) { DNSServiceRefDeallocate(tmp); return err; } - if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0) - { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + ioresult = read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)); + if (ioresult < read_all_success) + { DNSServiceRefDeallocate(tmp); return (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; } DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoError; @@ -1287,7 +1372,7 @@ fail: syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon"); } -#if TARGET_OS_EMBEDDED +#if TARGET_OS_IPHONE static int32_t libSystemVersion = 0; @@ -1305,7 +1390,7 @@ static int includeP2PWithIndexAny() return 0; } -#else // TARGET_OS_EMBEDDED +#else // TARGET_OS_IPHONE // always return false for non iOS platforms static int includeP2PWithIndexAny() @@ -1313,7 +1398,7 @@ static int includeP2PWithIndexAny() return 0; } -#endif // TARGET_OS_EMBEDDED +#endif // TARGET_OS_IPHONE DNSServiceErrorType DNSSD_API DNSServiceResolve ( @@ -1368,12 +1453,24 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve put_string(domain, &ptr); err = deliver_request(hdr, *sdRef); // Will free hdr for us +#if CHECK_BUNDLE_VERSION + if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error()) + { + err = kDNSServiceErr_NoError; + } +#endif if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } return err; } static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) { +#if CHECK_BUNDLE_VERSION + if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error()) + { + return; + } +#endif uint32_t ttl; char name[kDNSServiceMaxDomainName]; uint16_t rrtype, rrclass, rdlen; @@ -1391,6 +1488,37 @@ static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function } +#if APPLE_OSX_mDNSResponder +static size_t get_required_length_for_defaults(const xpc_object_t defaults) +{ + size_t required_len = 0; + size_t plist_data_len = 0; + // Add length for IPC_TLV_TYPE_RESOLVER_CONFIG_PLIST_DATA. + if (xpc_dictionary_get_data(defaults, kDNSServiceDefaultsKey_ResolverConfigPListData, &plist_data_len)) + { + required_len += get_required_tlv16_length(plist_data_len); + } + // Add length for IPC_TLV_TYPE_REQUIRE_PRIVACY. + required_len += get_required_tlv16_length(sizeof(uint8_t)); + return required_len; +} + +static void put_tlvs_for_defaults(const xpc_object_t defaults, ipc_msg_hdr *const hdr, char **ptr) +{ + uint8_t require_privacy; + size_t plist_data_len = 0; + const uint8_t *const plist_data_ptr = xpc_dictionary_get_data(defaults, + kDNSServiceDefaultsKey_ResolverConfigPListData, &plist_data_len); + if (plist_data_ptr) + { + put_tlv16(IPC_TLV_TYPE_RESOLVER_CONFIG_PLIST_DATA, (uint16_t)plist_data_len, plist_data_ptr, ptr); + } + require_privacy = xpc_dictionary_get_bool(defaults, kDNSServiceDefaultsKey_RequirePrivacy) ? 1 : 0; + put_tlv16(IPC_TLV_TYPE_REQUIRE_PRIVACY, sizeof(require_privacy), &require_privacy, ptr); + hdr->ipc_flags |= IPC_FLAGS_TRAILING_TLVS; +} +#endif + DNSServiceErrorType DNSSD_API DNSServiceQueryRecord ( DNSServiceRef *sdRef, @@ -1407,7 +1535,9 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord size_t len; ipc_msg_hdr *hdr; DNSServiceErrorType err; - +#if APPLE_OSX_mDNSResponder + xpc_object_t defaults; +#endif // NULL name handled below. if (!sdRef || !callBack) return kDNSServiceErr_BadParam; @@ -1424,23 +1554,54 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord len += sizeof(uint32_t); // interfaceIndex len += strlen(name) + 1; len += 2 * sizeof(uint16_t); // rrtype, rrclass - +#if APPLE_OSX_mDNSResponder + defaults = DNSServiceGetRetainedResolverDefaults(); + if (defaults) + { + len += get_required_length_for_defaults(defaults); + } +#endif hdr = create_hdr(query_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); - if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } - + if (!hdr) + { + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; +#if APPLE_OSX_mDNSResponder + xpc_forget(&defaults); +#endif + return kDNSServiceErr_NoMemory; + } put_flags(flags, &ptr); put_uint32(interfaceIndex, &ptr); put_string(name, &ptr); put_uint16(rrtype, &ptr); put_uint16(rrclass, &ptr); - +#if APPLE_OSX_mDNSResponder + if (defaults) + { + put_tlvs_for_defaults(defaults, hdr, &ptr); + xpc_forget(&defaults); + } +#endif err = deliver_request(hdr, *sdRef); // Will free hdr for us +#if CHECK_BUNDLE_VERSION + if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error()) + { + err = kDNSServiceErr_NoError; + } +#endif if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } return err; } static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) { +#if CHECK_BUNDLE_VERSION + if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error()) + { + return; + } +#endif char hostname[kDNSServiceMaxDomainName]; uint16_t rrtype, rrclass, rdlen; const char *rdata; @@ -1490,17 +1651,12 @@ static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHead if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface; } } - // Validation results are always delivered separately from the actual results of the - // DNSServiceGetAddrInfo. Set the "addr" to NULL as per the documentation. - // - // Note: If we deliver validation results along with the "addr" in the future, we need - // a way to differentiate the negative response from validation-only response as both - // has zero address. - if (!(cbh->cb_flags & kDNSServiceFlagsValidate)) - ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext); - else - ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, 0, sdr->AppContext); - // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext); + } + else if (cbh->cb_err == kDNSServiceErr_PolicyDenied) + { + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, ttl, sdr->AppContext); } } @@ -1519,6 +1675,9 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo size_t len; ipc_msg_hdr *hdr; DNSServiceErrorType err; +#if APPLE_OSX_mDNSResponder + xpc_object_t defaults; +#endif if (!sdRef || !hostname || !callBack) return kDNSServiceErr_BadParam; @@ -1533,22 +1692,53 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo len += sizeof(uint32_t); // interfaceIndex len += sizeof(uint32_t); // protocol len += strlen(hostname) + 1; - +#if APPLE_OSX_mDNSResponder + defaults = DNSServiceGetRetainedResolverDefaults(); + if (defaults) + { + len += get_required_length_for_defaults(defaults); + } +#endif hdr = create_hdr(addrinfo_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); - if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } - + if (!hdr) + { + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; +#if APPLE_OSX_mDNSResponder + xpc_forget(&defaults); +#endif + return kDNSServiceErr_NoMemory; + } put_flags(flags, &ptr); put_uint32(interfaceIndex, &ptr); put_uint32(protocol, &ptr); put_string(hostname, &ptr); - +#if APPLE_OSX_mDNSResponder + if (defaults) + { + put_tlvs_for_defaults(defaults, hdr, &ptr); + xpc_forget(&defaults); + } +#endif err = deliver_request(hdr, *sdRef); // Will free hdr for us +#if CHECK_BUNDLE_VERSION + if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error()) + { + err = kDNSServiceErr_NoError; + } +#endif if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } return err; } static void handle_browse_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) { +#if CHECK_BUNDLE_VERSION + if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error()) + { + return; + } +#endif char replyName[256], replyType[kDNSServiceMaxDomainName], replyDomain[kDNSServiceMaxDomainName]; get_string(&data, end, replyName, 256); get_string(&data, end, replyType, kDNSServiceMaxDomainName); @@ -1598,6 +1788,12 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse put_string(domain, &ptr); err = deliver_request(hdr, *sdRef); // Will free hdr for us +#if CHECK_BUNDLE_VERSION + if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error()) + { + err = kDNSServiceErr_NoError; + } +#endif if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } return err; } @@ -1628,6 +1824,12 @@ DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags static void handle_regservice_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) { +#if CHECK_BUNDLE_VERSION + if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error()) + { + return; + } +#endif char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName]; get_string(&data, end, name, 256); get_string(&data, end, regtype, kDNSServiceMaxDomainName); @@ -1696,6 +1898,12 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister put_rdata(txtLen, txtRecord, &ptr); err = deliver_request(hdr, *sdRef); // Will free hdr for us +#if CHECK_BUNDLE_VERSION + if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error()) + { + err = kDNSServiceErr_NoError; + } +#endif if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } return err; } @@ -1769,6 +1977,12 @@ static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *co } else { +#if CHECK_BUNDLE_VERSION + if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error()) + { + return; + } +#endif DNSRecordRef rec; for (rec = sdr->rec; rec; rec = rec->recnext) { @@ -1779,12 +1993,12 @@ static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *co // error if the record is not found. if (!rec) { - syslog(LOG_INFO, "ConnectionResponse: Record not found"); + syslog(LOG_INFO, "dnssd_clientstub ConnectionResponse: Record not found"); return; } if (rec->sdr != sdr) { - syslog(LOG_WARNING, "ConnectionResponse: Record sdr mismatch: rec %p sdr %p", rec->sdr, sdr); + syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: Record sdr mismatch: rec %p sdr %p", rec->sdr, sdr); return; } @@ -1820,7 +2034,7 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) return err; } -#if APPLE_OSX_mDNSResponder && !TARGET_IPHONE_SIMULATOR +#if APPLE_OSX_mDNSResponder && !TARGET_OS_SIMULATOR DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) { char *ptr; @@ -1849,9 +2063,9 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef * } if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1) - { - syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for PID[%d], no entitlements or process(pid) invalid errno:%d (%s)", pid, errno, strerror(errno)); - // Free the hdr in case we return before calling deliver_request() + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceCreateDelegateConnection: Could not setsockopt() for PID[%d], no entitlements or process(pid) invalid errno:%d (%s)", pid, errno, strerror(errno)); + // Free the hdr in case we return before calling deliver_request() if (hdr) free(hdr); DNSServiceRefDeallocate(*sdRef); @@ -1861,7 +2075,7 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef * if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1) { - syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for UUID, no entitlements or process(uuid) invalid errno:%d (%s) ", errno, strerror(errno)); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceCreateDelegateConnection: Could not setsockopt() for UUID, no entitlements or process(uuid) invalid errno:%d (%s) ", errno, strerror(errno)); // Free the hdr in case we return before calling deliver_request() if (hdr) free(hdr); @@ -1880,7 +2094,7 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef * } return err; } -#elif TARGET_IPHONE_SIMULATOR // This hack is for Simulator platform only +#elif TARGET_OS_SIMULATOR // This hack is for Simulator platform only DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) { (void) pid; @@ -1905,14 +2119,17 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord void *context ) { + DNSServiceErrorType err; char *ptr; size_t len; ipc_msg_hdr *hdr = NULL; DNSRecordRef rref = NULL; DNSRecord **p; + // Verify that only one of the following flags is set. int f1 = (flags & kDNSServiceFlagsShared) != 0; int f2 = (flags & kDNSServiceFlagsUnique) != 0; - if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + int f3 = (flags & kDNSServiceFlagsKnownUnique) != 0; + if (f1 + f2 + f3 != 1) return kDNSServiceErr_BadParam; if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) flags |= kDNSServiceFlagsIncludeP2P; @@ -1981,7 +2198,14 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord while (*p) p = &(*p)->recnext; *p = rref; - return deliver_request(hdr, sdRef); // Will free hdr for us + err = deliver_request(hdr, sdRef); // Will free hdr for us +#if CHECK_BUNDLE_VERSION + if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error()) + { + err = kDNSServiceErr_NoError; + } +#endif + return err; } // sdRef returned by DNSServiceRegister() @@ -2276,13 +2500,13 @@ DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue } if (service->disp_source) { - syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch source set already"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch source set already"); return kDNSServiceErr_BadParam; } service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue); if (!service->disp_source) { - syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch_source_create failed"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch_source_create failed"); return kDNSServiceErr_NoMemory; } service->disp_queue = queue; @@ -2303,12 +2527,23 @@ static void DNSSD_API SleepKeepaliveCallback(DNSServiceRef sdRef, DNSRecordRef r (void)flags; // Unused if (sdRef->kacontext != context) - syslog(LOG_WARNING, "SleepKeepaliveCallback context mismatch"); + syslog(LOG_WARNING, "dnssd_clientstub SleepKeepaliveCallback context mismatch"); if (ka->AppCallback) ((DNSServiceSleepKeepaliveReply)ka->AppCallback)(sdRef, errorCode, ka->AppContext); } +static DNSServiceErrorType _DNSServiceSleepKeepalive_sockaddr +( + DNSServiceRef * sdRef, + DNSServiceFlags flags, + const struct sockaddr * localAddr, + const struct sockaddr * remoteAddr, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void * context +); + DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive ( DNSServiceRef *sdRef, @@ -2319,60 +2554,87 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive void *context ) { - char source_str[INET6_ADDRSTRLEN]; - char target_str[INET6_ADDRSTRLEN]; struct sockaddr_storage lss; struct sockaddr_storage rss; socklen_t len1, len2; - unsigned int len, proxyreclen; - char buf[256]; - DNSServiceErrorType err; - DNSRecordRef record = NULL; - char name[10]; - char recname[128]; - SleepKAContext *ka; - unsigned int i, unique; - - - (void) flags; //unused - if (!timeout) return kDNSServiceErr_BadParam; - len1 = sizeof(lss); if (getsockname(fd, (struct sockaddr *)&lss, &len1) < 0) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getsockname %d\n", errno); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive: getsockname %d\n", errno); return kDNSServiceErr_BadParam; } len2 = sizeof(rss); if (getpeername(fd, (struct sockaddr *)&rss, &len2) < 0) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getpeername %d\n", errno); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive: getpeername %d\n", errno); return kDNSServiceErr_BadParam; } if (len1 != len2) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive local/remote info not same"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive local/remote info not same"); return kDNSServiceErr_Unknown; } + return _DNSServiceSleepKeepalive_sockaddr(sdRef, flags, (const struct sockaddr *)&lss, (const struct sockaddr *)&rss, + timeout, callBack, context); +} + +DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive_sockaddr +( + DNSServiceRef * sdRef, + DNSServiceFlags flags, + const struct sockaddr * localAddr, + const struct sockaddr * remoteAddr, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void * context +) +{ + return _DNSServiceSleepKeepalive_sockaddr(sdRef, flags, localAddr, remoteAddr, timeout, callBack, context ); +} + +static DNSServiceErrorType _DNSServiceSleepKeepalive_sockaddr +( + DNSServiceRef * sdRef, + DNSServiceFlags flags, + const struct sockaddr * localAddr, + const struct sockaddr * remoteAddr, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void * context +) +{ + char source_str[INET6_ADDRSTRLEN]; + char target_str[INET6_ADDRSTRLEN]; + unsigned int len, proxyreclen; + char buf[256]; + DNSServiceErrorType err; + DNSRecordRef record = NULL; + char name[10]; + char recname[128]; + SleepKAContext *ka; + unsigned int i, unique; + + (void) flags; //unused + if (!timeout) return kDNSServiceErr_BadParam; unique = 0; - if (lss.ss_family == AF_INET) + if ((localAddr->sa_family == AF_INET) && (remoteAddr->sa_family == AF_INET)) { - struct sockaddr_in *sl = (struct sockaddr_in *)&lss; - struct sockaddr_in *sr = (struct sockaddr_in *)&rss; + const struct sockaddr_in *sl = (const struct sockaddr_in *)localAddr; + const struct sockaddr_in *sr = (const struct sockaddr_in *)remoteAddr; unsigned char *ptr = (unsigned char *)&sl->sin_addr; if (!inet_ntop(AF_INET, (const void *)&sr->sin_addr, target_str, sizeof (target_str))) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote info failed %d", errno); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive remote info failed %d", errno); return kDNSServiceErr_Unknown; } if (!inet_ntop(AF_INET, (const void *)&sl->sin_addr, source_str, sizeof (source_str))) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive local info failed %d", errno); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive local info failed %d", errno); return kDNSServiceErr_Unknown; } // Sum of all bytes in the local address and port should result in a unique @@ -2382,20 +2644,20 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive unique += sl->sin_port; len = snprintf(buf+1, sizeof(buf) - 1, "t=%u h=%s d=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl->sin_port), ntohs(sr->sin_port)); } - else + else if ((localAddr->sa_family == AF_INET6) && (remoteAddr->sa_family == AF_INET6)) { - struct sockaddr_in6 *sl6 = (struct sockaddr_in6 *)&lss; - struct sockaddr_in6 *sr6 = (struct sockaddr_in6 *)&rss; + const struct sockaddr_in6 *sl6 = (const struct sockaddr_in6 *)localAddr; + const struct sockaddr_in6 *sr6 = (const struct sockaddr_in6 *)remoteAddr; unsigned char *ptr = (unsigned char *)&sl6->sin6_addr; if (!inet_ntop(AF_INET6, (const void *)&sr6->sin6_addr, target_str, sizeof (target_str))) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote6 info failed %d", errno); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive remote6 info failed %d", errno); return kDNSServiceErr_Unknown; } if (!inet_ntop(AF_INET6, (const void *)&sl6->sin6_addr, source_str, sizeof (source_str))) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive local6 info failed %d", errno); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive local6 info failed %d", errno); return kDNSServiceErr_Unknown; } for (i = 0; i < sizeof(struct in6_addr); i++) @@ -2403,10 +2665,14 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive unique += sl6->sin6_port; len = snprintf(buf+1, sizeof(buf) - 1, "t=%u H=%s D=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl6->sin6_port), ntohs(sr6->sin6_port)); } + else + { + return kDNSServiceErr_BadParam; + } if (len >= (sizeof(buf) - 1)) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit local/remote info"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive could not fit local/remote info"); return kDNSServiceErr_Unknown; } // Include the NULL byte also in the first byte. The total length of the record includes the @@ -2417,14 +2683,14 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive len = snprintf(name, sizeof(name), "%u", unique); if (len >= sizeof(name)) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit unique"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive could not fit unique"); return kDNSServiceErr_Unknown; } len = snprintf(recname, sizeof(recname), "%s.%s", name, "_keepalive._dns-sd._udp.local"); if (len >= sizeof(recname)) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit name"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive could not fit name"); return kDNSServiceErr_Unknown; } @@ -2436,7 +2702,7 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive err = DNSServiceCreateConnection(sdRef); if (err) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive cannot create connection"); free(ka); return err; } @@ -2446,7 +2712,7 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive kDNSServiceType_NULL, kDNSServiceClass_IN, proxyreclen, buf, kDNSServiceInterfaceIndexAny, SleepKeepaliveCallback, ka); if (err) { - syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection"); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive cannot create connection"); free(ka); return err; } diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.c b/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.c index 0fd75824f5..a149678ef6 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.c +++ b/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.c @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2003-2011 Apple Inc. All rights reserved. +/* + * Copyright (c) 2003-2020 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -27,6 +26,9 @@ */ #include "dnssd_ipc.h" +#if APPLE_OSX_mDNSResponder +#include "mdns_tlv.h" +#endif #if defined(_WIN32) @@ -102,9 +104,11 @@ uint16_t get_uint16(const char **ptr, const char *end) int put_string(const char *str, char **ptr) { + size_t len; if (!str) str = ""; - strcpy(*ptr, str); - *ptr += strlen(str) + 1; + len = strlen(str) + 1; + memcpy(*ptr, str, len); + *ptr += len; return 0; } @@ -151,6 +155,20 @@ const char *get_rdata(const char **ptr, const char *end, int rdlen) } } +#if APPLE_OSX_mDNSResponder +size_t get_required_tlv16_length(const uint16_t valuelen) +{ + return mdns_tlv16_get_required_length(valuelen); +} + +void put_tlv16(const uint16_t type, const uint16_t length, const uint8_t *value, char **ptr) +{ + uint8_t *dst = (uint8_t *)*ptr; + mdns_tlv16_set(dst, NULL, type, length, value, &dst); + *ptr = (char *)dst; +} +#endif + void ConvertHeaderBytes(ipc_msg_hdr *hdr) { hdr->version = htonl(hdr->version); diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.h b/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.h index c054188453..4cf83700df 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.h +++ b/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_ipc.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2015 Apple Inc. All rights reserved. + * Copyright (c) 2003-2020 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -100,7 +100,11 @@ extern char *win32_strerror(int inErrorCode); // IPC data encoding constants and types #define VERSION 1 -#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client +#define IPC_FLAGS_NOREPLY (1U << 0) // Set flag if no asynchronous replies are to be sent to client. +#define IPC_FLAGS_TRAILING_TLVS (1U << 1) // Set flag if TLVs follow the standard request data. + +#define IPC_TLV_TYPE_RESOLVER_CONFIG_PLIST_DATA 1 // An nw_resolver_config as a binary property list. +#define IPC_TLV_TYPE_REQUIRE_PRIVACY 2 // A uint8. Non-zero means privacy is required, zero means not required. // Structure packing macro. If we're not using GNUC, it's not fatal. Most compilers naturally pack the on-the-wire // structures correctly anyway, so a plain "struct" is usually fine. In the event that structures are not packed @@ -211,6 +215,11 @@ void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr); const char *get_rdata(const char **ptr, const char *end, int rdlen); // return value is rdata pointed to by *ptr - // rdata is not copied from buffer. +#if APPLE_OSX_mDNSResponder +size_t get_required_tlv16_length(const uint16_t valuelen); +void put_tlv16(const uint16_t type, const uint16_t length, const uint8_t *value, char **ptr); +#endif + void ConvertHeaderBytes(ipc_msg_hdr *hdr); struct CompileTimeAssertionChecks_dnssd_ipc diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/mDNSDebug.c b/usr/src/contrib/mDNSResponder/mDNSShared/mDNSDebug.c index e76ae419bb..f7b2644a01 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/mDNSDebug.c +++ b/usr/src/contrib/mDNSResponder/mDNSShared/mDNSDebug.c @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2003-2018 Apple Inc. All rights reserved. +/* + * Copyright (c) 2003-2019 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +14,6 @@ * limitations under the License. */ -#include "mDNSDebug.h" - #include <stdio.h> #if defined(WIN32) || defined(EFI32) || defined(EFI64) || defined(EFIX64) @@ -47,37 +44,49 @@ mDNSexport int mDNS_DebugMode = mDNSfalse; mDNSexport void verbosedebugf_(const char *format, ...) { char buffer[512]; - va_list ptr; - va_start(ptr,format); - buffer[mDNS_vsnprintf(buffer, sizeof(buffer), format, ptr)] = 0; - va_end(ptr); + va_list args; + va_start(args, format); + buffer[mDNS_vsnprintf(buffer, sizeof(buffer), format, args)] = 0; + va_end(args); mDNSPlatformWriteDebugMsg(buffer); } #endif // Log message with default "mDNSResponder" ident string at the start -mDNSlocal void LogMsgWithLevelv(mDNSLogLevel_t logLevel, const char *format, va_list ptr) +#if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG) +mDNSlocal void LogMsgWithLevelv(os_log_t category, os_log_type_t level, const char *format, va_list args) +{ + char buffer[512]; + mDNS_vsnprintf(buffer, (mDNSu32)sizeof(buffer), format, args); + os_log_with_type(category ? category : mDNSLogCategory_Default, level, "%{private}s", buffer); +} +#else +mDNSlocal void LogMsgWithLevelv(const char *category, mDNSLogLevel_t level, const char *format, va_list args) { char buffer[512]; - buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; - mDNSPlatformWriteLogMsg(ProgramName, buffer, logLevel); + char *dst = buffer; + const char *const lim = &buffer[512]; + if (category) mDNS_snprintf_add(&dst, lim, "%s: ", category); + mDNS_vsnprintf(dst, (mDNSu32)(lim - dst), format, args); + mDNSPlatformWriteLogMsg(ProgramName, buffer, level); } +#endif -#define LOG_HELPER_BODY(L) \ +#define LOG_HELPER_BODY(CATEGORY, LEVEL) \ { \ - va_list ptr; \ - va_start(ptr,format); \ - LogMsgWithLevelv(L, format, ptr); \ - va_end(ptr); \ + va_list args; \ + va_start(args,format); \ + LogMsgWithLevelv(CATEGORY, LEVEL, format, args); \ + va_end(args); \ } // see mDNSDebug.h #if !MDNS_HAS_VA_ARG_MACROS -void LogMsg_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_MSG) -void LogOperation_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_OPERATION) -void LogSPS_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_SPS) -void LogInfo_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_INFO) -void LogDebug_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_DEBUG) +void LogMsg_(const char *format, ...) LOG_HELPER_BODY(NULL, MDNS_LOG_INFO) +void LogOperation_(const char *format, ...) LOG_HELPER_BODY(NULL, MDNS_LOG_INFO) +void LogSPS_(const char *format, ...) LOG_HELPER_BODY(NULL, MDNS_LOG_INFO) +void LogInfo_(const char *format, ...) LOG_HELPER_BODY(NULL, MDNS_LOG_INFO) +void LogDebug_(const char *format, ...) LOG_HELPER_BODY(NULL, MDNS_LOG_DEBUG) #endif #if MDNS_DEBUGMSGS @@ -85,5 +94,20 @@ void debugf_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_DEBUG) #endif // Log message with default "mDNSResponder" ident string at the start -mDNSexport void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...) -LOG_HELPER_BODY(logLevel) +mDNSexport void LogMsgWithLevel(mDNSLogCategory_t category, mDNSLogLevel_t level, const char *format, ...) +LOG_HELPER_BODY(category, level) + +mDNSexport void LogToFD(int fd, const char *format, ...) +{ + va_list args; + va_start(args, format); +#if APPLE_OSX_mDNSResponder + char buffer[1024]; + buffer[mDNS_vsnprintf(buffer, (mDNSu32)sizeof(buffer), format, args)] = '\0'; + dprintf(fd, "%s\n", buffer); +#else + (void)fd; + LogMsgWithLevelv(NULL, MDNS_LOG_INFO, format, args); +#endif + va_end(args); +} diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/mDNSFeatures.h b/usr/src/contrib/mDNSResponder/mDNSShared/mDNSFeatures.h new file mode 100644 index 0000000000..3f2f79e925 --- /dev/null +++ b/usr/src/contrib/mDNSResponder/mDNSShared/mDNSFeatures.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 Apple Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __mDNSFeatures_h +#define __mDNSFeatures_h + +#if MDNSRESPONDER_PLATFORM_APPLE +#include "ApplePlatformFeatures.h" +#endif + +// Common Features + +#undef MDNSRESPONDER_PLATFORM_COMMON +#define MDNSRESPONDER_PLATFORM_COMMON 1 + +// Feature: DNS Push +// Radar: <rdar://problem/23226275> +// Enabled: Yes, for Apple. + +#if !defined(MDNSRESPONDER_SUPPORTS_COMMON_DNS_PUSH) + #if defined(MDNSRESPONDER_PLATFORM_APPLE) && MDNSRESPONDER_PLATFORM_APPLE + #define MDNSRESPONDER_SUPPORTS_COMMON_DNS_PUSH 1 + #else + #define MDNSRESPONDER_SUPPORTS_COMMON_DNS_PUSH 0 + #endif +#endif + +#define HAS_FEATURE_CAT(A, B) A ## B +#define HAS_FEATURE_CHECK_0 1 +#define HAS_FEATURE_CHECK_1 1 +#define HAS_FEATURE(X) ((X) / HAS_FEATURE_CAT(HAS_FEATURE_CHECK_, X)) + +#define MDNSRESPONDER_SUPPORTS(PLATFORM, FEATURE) \ + (defined(MDNSRESPONDER_PLATFORM_ ## PLATFORM) && MDNSRESPONDER_PLATFORM_ ## PLATFORM && \ + HAS_FEATURE(MDNSRESPONDER_SUPPORTS_ ## PLATFORM ## _ ## FEATURE)) + +#endif // __mDNSFeatures_h diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.c b/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.c index 35b65f6608..fba7721356 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.c +++ b/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.c @@ -1,6 +1,5 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2003-2018 Apple Inc. All rights reserved. +/* + * Copyright (c) 2003-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,19 +35,12 @@ #include "uds_daemon.h" #include "dns_sd_internal.h" -// Normally we append search domains only for queries with a single label that are not -// fully qualified. This can be overridden to apply search domains for queries (that are -// not fully qualified) with any number of labels e.g., moon, moon.cs, moon.cs.be, etc. -mDNSBool AlwaysAppendSearchDomains = mDNSfalse; - -// Control enabling ioptimistic DNS -mDNSBool EnableAllowExpired = mDNStrue; - // Apple-specific functionality, not required for other platforms #if APPLE_OSX_mDNSResponder +#include <os/log.h> #include <sys/ucred.h> #ifndef PID_FILE -#define PID_FILE "" +#define NO_PID_FILE // We need to signal that this platform has no PID file, and not just that we are taking the default #endif #endif @@ -59,34 +51,43 @@ mDNSBool EnableAllowExpired = mDNStrue; #include <libproc.h> // for proc_pidinfo() #endif //LOCAL_PEEREPID -#ifdef UNIT_TEST -#include "unittest.h" +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) +#include "D2D.h" #endif #if APPLE_OSX_mDNSResponder -#include <WebFilterDNS/WebFilterDNS.h> #include "BLE.h" +#endif -#if !NO_WCF +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) +#include "mDNSMacOSX.h" +#include <os/feature_private.h> +#endif -int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import)); -int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import)); -int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import)); +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) +#include <bsm/libbsm.h> +#endif -// Do we really need to define a macro for "if"? -#define CHECK_WCF_FUNCTION(X) if (X) -#endif // ! NO_WCF +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +#include "QuerierSupport.h" +#endif -#else -#define NO_WCF 1 -#endif // APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) && MDNSRESPONDER_SUPPORTS(APPLE, IPC_TLV) +#include "mdns_tlv.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) +#include "dnssec_v2.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSD_XPC_SERVICE) +#include "dnssd_server.h" +#endif // User IDs 0-500 are system-wide processes, not actual users in the usual sense // User IDs for real user accounts start at 501 and count up from there #define SystemUID(X) ((X) <= 500) -#define MAX_ANONYMOUS_DATA 256 - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -119,12 +120,11 @@ static mDNSu32 n_mrecords; // tracks the current active mcast records for McastL static mDNSu32 n_mquests; // tracks the current active mcast questions for McastLogging -#if TARGET_OS_EMBEDDED +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) mDNSu32 curr_num_regservices = 0; mDNSu32 max_num_regservices = 0; #endif - // Note asymmetry here between registration and browsing. // For service registrations we only automatically register in domains that explicitly appear in local configuration data // (so AutoRegistrationDomains could equally well be called SCPrefRegDomains) @@ -149,14 +149,22 @@ mDNSexport DNameListElem *AutoBrowseDomains; // List created from those l #define PID_FILE "/var/run/mDNSResponder.pid" #endif -mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen); - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - General Utility Functions #endif +mDNSlocal mDNSu32 GetNewRequestID(void) +{ +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSD_XPC_SERVICE) + return dnssd_server_get_new_request_id(); +#else + static mDNSu32 s_last_id = 0; + return ++s_last_id; +#endif +} + mDNSlocal void FatalError(char *errmsg) { LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno)); @@ -315,21 +323,44 @@ mDNSexport void LogMcastStateInfo(mDNSBool mflag, mDNSBool start, mDNSBool mstat mDNSlocal void abort_request(request_state *req) { if (req->terminate == (req_termination_fn) ~0) - { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req->request_id, req, req->terminate); + return; + } // First stop whatever mDNSCore operation we were doing // If this is actually a shared connection operation, then its req->terminate function will scan // the all_requests list and terminate any subbordinate operations sharing this file descriptor if (req->terminate) req->terminate(req); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (req->custom_service_id != 0) + { + Querier_DeregisterCustomDNSService(req->custom_service_id); + req->custom_service_id = 0; + } +#endif if (!dnssd_SocketValid(req->sd)) - { LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req, req->sd); return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req->request_id, req, req->sd); + return; + } // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies if (!req->primary) { - if (req->errsd != req->sd) LogDebug("%3d: Removing FD and closing errsd %d", req->sd, req->errsd); - else LogDebug("%3d: Removing FD", req->sd); + if (req->errsd != req->sd) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "[R%d] Removing FD %d and closing errsd %d", req->request_id, req->sd, req->errsd); + } + else + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "[R%d] Removing FD %d", req->request_id, req->sd); + } udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; } @@ -342,9 +373,12 @@ mDNSlocal void abort_request(request_state *req) } // Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure -#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING - // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses +#if MDNS_MALLOC_DEBUGGING + // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MDNS_MALLOC_DEBUGGING uses // for detecting when the memory for an object is inadvertently freed while the object is still on some list +#ifdef WIN32 +#error This will not work on Windows, look at IsValidSocket in mDNSShared/CommonServices.h to see why +#endif req->sd = req->errsd = -2; #else req->sd = req->errsd = dnssd_InvalidSocket; @@ -376,7 +410,20 @@ mDNSlocal void AbortUnlinkAndFree(request_state *req) request_state **p = &all_requests; abort_request(req); while (*p && *p != req) p=&(*p)->next; - if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); } + if (*p) + { + *p = req->next; +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (req->trust) + { + void * context = mdns_trust_get_context(req->trust); + mdns_trust_set_context(req->trust, NULL); + if (context) freeL("context/AbortUnlinkAndFree", context); + mdns_trust_forget(&req->trust); + } +#endif + freeL("request_state/AbortUnlinkAndFree", req); + } else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req); } @@ -390,8 +437,8 @@ mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, r return NULL; } - reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr)); - if (!reply) FatalError("ERROR: malloc"); + reply = (reply_state *) callocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr)); + if (!reply) FatalError("ERROR: calloc"); reply->next = mDNSNULL; reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr); @@ -437,7 +484,7 @@ mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const domainlabel name; domainname type, dom; *rep = NULL; - if (!DeconstructServiceName(servicename, &name, &type, &dom)) + if (servicename && !DeconstructServiceName(servicename, &name, &type, &dom)) return kDNSServiceErr_Invalid; else { @@ -447,9 +494,18 @@ mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const int len; char *data; - ConvertDomainLabelToCString_unescaped(&name, namestr); - ConvertDomainNameToCString(&type, typestr); - ConvertDomainNameToCString(&dom, domstr); + if (servicename) + { + ConvertDomainLabelToCString_unescaped(&name, namestr); + ConvertDomainNameToCString(&type, typestr); + ConvertDomainNameToCString(&dom, domstr); + } + else + { + namestr[0] = 0; + typestr[0] = 0; + domstr[0] = 0; + } // Calculate reply data length len = sizeof(DNSServiceFlags); @@ -486,11 +542,19 @@ mDNSlocal void GenerateBrowseReply(const domainname *const servicename, const mD *rep = NULL; - // 1. Put first label in namestr - ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr); + if (servicename) + { + // 1. Put first label in namestr + ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr); - // 2. Put second label and "local" into typestr - mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename)); + // 2. Put second label and "local" into typestr + mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename)); + } + else + { + namestr[0] = 0; + typestr[0] = 0; + } // Calculate reply data length len = sizeof(DNSServiceFlags); @@ -520,17 +584,18 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i { DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - char name[256]; + char name[MAX_ESCAPED_DOMAIN_NAME]; int str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name)); mDNSu16 type = get_uint16(&request->msgptr, request->msgend); mDNSu16 class = get_uint16(&request->msgptr, request->msgend); mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); - const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + const mDNSu8 *const rdata = (const mDNSu8 *)get_rdata (&request->msgptr, request->msgend, rdlen); mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0; - size_t storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + size_t rdcapacity; AuthRecord *rr; mDNSInterfaceID InterfaceID; AuthRecType artype; + mDNSu8 recordType; request->flags = flags; request->interfaceIndex = interfaceIndex; @@ -541,16 +606,32 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i if (validate_flags && !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) && - !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)) + !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique) && + !((flags & kDNSServiceFlagsKnownUnique) == kDNSServiceFlagsKnownUnique)) { - LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)"); + LogMsg("ERROR: Bad resource record flags (must be one of either kDNSServiceFlagsShared, kDNSServiceFlagsUnique or kDNSServiceFlagsKnownUnique)"); return NULL; } + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size); - if (!rr) FatalError("ERROR: malloc"); + // The registration is scoped to a specific interface index, but the interface is not currently on our list. + if ((InterfaceID == mDNSInterface_Any) && (interfaceIndex != kDNSServiceInterfaceIndexAny)) + { + // On Apple platforms, an interface's mDNSInterfaceID is equal to its index. Using an interface index that isn't + // currently valid will cause the registration to take place as soon as it becomes valid. On other platforms, + // mDNSInterfaceID is actually a pointer to a platform-specific interface object, but we don't know what the pointer + // for the interface index will be ahead of time. For now, just return NULL to indicate an error condition since the + // interface index is invalid. Otherwise, the registration would be performed on all interfaces. +#if APPLE_OSX_mDNSResponder + InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; +#else + return NULL; +#endif + } + rdcapacity = (rdlen > sizeof(RDataBody2)) ? rdlen : sizeof(RDataBody2); + rr = (AuthRecord *) callocL("AuthRecord/read_rr_from_ipc_msg", sizeof(*rr) - sizeof(RDataBody) + rdcapacity); + if (!rr) FatalError("ERROR: calloc"); - InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); if (InterfaceID == mDNSInterface_LocalOnly) artype = AuthRecordLocalOnly; else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE) @@ -565,8 +646,14 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i else artype = AuthRecordAny; - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0, - (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL); + if (flags & kDNSServiceFlagsShared) + recordType = (mDNSu8) kDNSRecordTypeShared; + else if (flags & kDNSServiceFlagsKnownUnique) + recordType = (mDNSu8) kDNSRecordTypeKnownUnique; + else + recordType = (mDNSu8) kDNSRecordTypeUnique; + + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0, recordType, artype, mDNSNULL, mDNSNULL); if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name)) { @@ -578,8 +665,15 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue; rr->resrec.rrclass = class; rr->resrec.rdlength = rdlen; - rr->resrec.rdata->MaxRDLength = rdlen; - mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen); + rr->resrec.rdata->MaxRDLength = (mDNSu16)rdcapacity; + if (!SetRData(mDNSNULL, rdata, rdata + rdlen, &rr->resrec, rdlen)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] read_rr_from_ipc_msg: SetRData failed for " PRI_DM_NAME " (" PUB_S ")", + request->request_id, DM_NAME_PARAM(rr->resrec.name), DNSTypeName(type)); + freeL("AuthRecord/read_rr_from_ipc_msg", rr); + return NULL; + } if (GetTTL) rr->resrec.rroriginalttl = ttl; rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us @@ -600,13 +694,15 @@ mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *r mDNSlocal void send_all(dnssd_sock_t s, const char *ptr, int len) { - int n = send(s, ptr, len, 0); + const ssize_t n = send(s, ptr, len, 0); // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us // (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)). // If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong. if (n < len) - LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)", - s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno)); + { + LogMsg("ERROR: send_all(%d) wrote %ld of %d errno %d (%s)", + s, (long)n, len, dnssd_errno, dnssd_strerror(dnssd_errno)); + } } #if 0 @@ -638,40 +734,41 @@ mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const d } #endif -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - external helpers -#endif - -mDNSexport mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags) +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) +mDNSlocal void SetupAuditTokenForRequest(request_state *request) { -#if APPLE_OSX_mDNSResponder - // Only call D2D layer routines if request applies to a D2D interface and the domain is "local". - if ( (((InterfaceID == mDNSInterface_Any) && (flags & (kDNSServiceFlagsIncludeP2P | kDNSServiceFlagsIncludeAWDL | kDNSServiceFlagsAutoTrigger))) - || mDNSPlatformInterfaceIsD2D(InterfaceID) || (InterfaceID == mDNSInterface_BLE)) - && IsLocalDomain(domain)) + pid_t audit_pid = audit_token_to_pid(request->audit_token); + if (audit_pid == 0) { - return mDNStrue; +#if !defined(LOCAL_PEERTOKEN) +#define LOCAL_PEERTOKEN 0x006 /* retrieve peer audit token */ +#endif + socklen_t len = sizeof(audit_token_t); + int ret = getsockopt(request->sd, SOL_LOCAL, LOCAL_PEERTOKEN, &request->audit_token, &len); + if (ret != 0) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "SetupAuditTokenForRequest: No audit_token using LOCAL_PEERTOKEN (%s PID %d) for op %d ret(%d)", + request->pid_name, request->process_id, request->hdr.op, ret); + } } - else - return mDNSfalse; - -#else - (void) InterfaceID; - (void) domain; - (void) flags; - - return mDNSfalse; -#endif // APPLE_OSX_mDNSResponder } +#endif +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - external helpers +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) mDNSlocal void external_start_advertising_helper(service_instance *const instance) { AuthRecord *st = instance->subtypes; ExtraResourceRecord *e; int i; + const pid_t requestPID = instance->request->process_id; if (mDNSIPPortIsZero(instance->request->u.servicereg.port)) { @@ -682,15 +779,14 @@ mDNSlocal void external_start_advertising_helper(service_instance *const instanc if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!"); for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) - external_start_advertising_service(&st[i].resrec, instance->request->flags); - - external_start_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags); - external_start_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags); + external_start_advertising_service(&st[i].resrec, instance->request->flags, requestPID); - external_start_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags); + external_start_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags, requestPID); + external_start_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags, requestPID); + external_start_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags, requestPID); for (e = instance->srs.Extras; e; e = e->next) - external_start_advertising_service(&e->r.resrec, instance->request->flags); + external_start_advertising_service(&e->r.resrec, instance->request->flags, requestPID); instance->external_advertise = mDNStrue; } @@ -705,18 +801,41 @@ mDNSlocal void external_stop_advertising_helper(service_instance *const instance LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service"); - for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) - external_stop_advertising_service(&st[i].resrec, instance->request->flags); + if (instance->request) + { + const pid_t requestPID = instance->request->process_id; + for (i = 0; i < instance->request->u.servicereg.num_subtypes; i++) + { + external_stop_advertising_service(&st[i].resrec, instance->request->flags, requestPID); + } - external_stop_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags); - external_stop_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags); - external_stop_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags); + external_stop_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags, requestPID); + external_stop_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags, requestPID); + external_stop_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags, requestPID); - for (e = instance->srs.Extras; e; e = e->next) - external_stop_advertising_service(&e->r.resrec, instance->request->flags); + for (e = instance->srs.Extras; e; e = e->next) + { + external_stop_advertising_service(&e->r.resrec, instance->request->flags, requestPID); + } + } instance->external_advertise = mDNSfalse; } +#endif // MDNSRESPONDER_SUPPORTS(APPLE, D2D) + +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) +mDNSlocal dispatch_queue_t _get_trust_results_dispatch_queue(void) +{ + static dispatch_once_t once = 0; + static dispatch_queue_t queue = NULL; + + dispatch_once(&once, ^{ + dispatch_queue_attr_t const attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0); + queue = dispatch_queue_create("com.apple.mDNSResponder.trust_results-queue", attr); + }); + return queue; +} +#endif // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -742,7 +861,9 @@ mDNSlocal void unlink_and_free_service_instance(service_instance *srv) { ExtraResourceRecord *e = srv->srs.Extras, *tmp; +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) external_stop_advertising_helper(srv); +#endif // clear pointers from parent struct if (srv->request) @@ -771,11 +892,6 @@ mDNSlocal void unlink_and_free_service_instance(service_instance *srv) freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; } - if (srv->srs.AnonData) - { - freeL("Anonymous", (void *)srv->srs.AnonData); - srv->srs.AnonData = NULL; - } freeL("service_instance", srv); } @@ -827,10 +943,18 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m reply_state *rep; (void)m; // Unused - if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; } + if (!srs) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "regservice_callback: srs is NULL %d", result); + return; + } instance = srs->ServiceContext; - if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; } + if (!instance) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "regservice_callback: srs->ServiceContext is NULL %d", result); + return; + } // don't send errors up to client for wide-area, empty-string registrations if (instance->request && @@ -840,18 +964,33 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m if (mDNS_LoggingEnabled) { - const char *const fmt = - (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" : - (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" : - (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" : - "%s DNSServiceRegister(%##s, %u) %s %d"; - char prefix[16] = "---:"; - if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd); - LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), - SuppressError ? "suppressed error" : "CALLBACK", result); + const char *result_description; + char description[32]; // 32-byte is enough for holding "suppressed error -2147483648\0" + mDNSu32 request_id = instance->request ? instance->request->request_id : 0; + switch (result) { + case mStatus_NoError: + result_description = "REGISTERED"; + break; + case mStatus_MemFree: + result_description = "DEREGISTERED"; + break; + case mStatus_NameConflict: + result_description = "NAME CONFLICT"; + break; + default: + mDNS_snprintf(description, sizeof(description), "%s %d", SuppressError ? "suppressed error" : "CALLBACK", result); + result_description = description; + break; + } + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%u] DNSServiceRegister(" PRI_DM_NAME ", %u) %s", + request_id, DM_NAME_PARAM(srs->RR_SRV.resrec.name), mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result_description); } - if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; } + if (!instance->request && result != mStatus_MemFree) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "regservice_callback: instance->request is NULL %d", result); + return; + } if (result == mStatus_NoError) { @@ -866,28 +1005,33 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m } if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) - LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[R%u] regservice_callback: " PRI_DM_NAME " is not valid DNS-SD SRV name", instance->request->request_id, DM_NAME_PARAM(srs->RR_SRV.resrec.name)); else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) if (callExternalHelpers(instance->request->u.servicereg.InterfaceID, &instance->domain, instance->request->flags)) { - LogInfo("regservice_callback: calling external_start_advertising_helper()"); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%u] regservice_callback: calling external_start_advertising_helper()", instance->request->request_id); external_start_advertising_helper(instance); } +#endif if (instance->request->u.servicereg.autoname && CountPeerRegistrations(srs) == 0) RecordUpdatedNiceLabel(0); // Successfully got new name, tell user immediately } else if (result == mStatus_MemFree) { -#if TARGET_OS_EMBEDDED +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) curr_num_regservices--; #endif if (instance->request && instance->renameonmemfree) { +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) external_stop_advertising_helper(instance); +#endif instance->renameonmemfree = 0; err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name); - if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err); + if (err) + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[R%u] ERROR: regservice_callback - RenameAndReregisterService returned %d", instance->request->request_id, err); // error should never happen - safest to log and continue } else @@ -897,7 +1041,9 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m { if (instance->request->u.servicereg.autorename) { +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) external_stop_advertising_helper(instance); +#endif if (instance->request->u.servicereg.autoname && CountPeerRegistrations(srs) == 0) { // On conflict for an autoname service, rename and reregister *all* autoname services @@ -915,7 +1061,7 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m if (!SuppressError) { if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) - LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[R%u] regservice_callback: " PRI_DM_NAME " is not valid DNS-SD SRV name", instance->request->request_id, DM_NAME_PARAM(srs->RR_SRV.resrec.name)); else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } } unlink_and_free_service_instance(instance); @@ -926,7 +1072,7 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m if (!SuppressError) { if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) - LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[R%u] regservice_callback: " PRI_DM_NAME " is not valid DNS-SD SRV name", instance->request->request_id, DM_NAME_PARAM(srs->RR_SRV.resrec.name)); else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } } } @@ -938,10 +1084,11 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) if (!rr->RecordContext) // parent struct already freed by termination callback { if (result == mStatus_NoError) - LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Error: regrecord_callback: successful registration of orphaned record " PRI_S, ARDisplayString(m, rr)); else { - if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); + if (result != mStatus_MemFree) + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "regrecord_callback: error %d received after parent termination", result); // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination. // If the record has been updated, we need to free the rdata. Every time we call mDNS_Update, it calls update_callback @@ -958,11 +1105,26 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) if (mDNS_LoggingEnabled) { - char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" : - (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" : - (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" : - "%3d: DNSServiceRegisterRecord(%u %s) %d"; - LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result); + const char *result_description; + char description[16]; // 16-byte is enough for holding -2147483648\0 + switch (result) { + case mStatus_NoError: + result_description = "REGISTERED"; + break; + case mStatus_MemFree: + result_description = "DEREGISTERED"; + break; + case mStatus_NameConflict: + result_description = "NAME CONFLICT"; + break; + default: + mDNS_snprintf(description, sizeof(description), "%d", result); + result_description = description; + break; + } + + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%u] DNSServiceRegisterRecord(%u " PRI_S ")" PUB_S, + request->request_id, re->key, RRDisplayString(m, &rr->resrec), result_description); } if (result != mStatus_MemFree) @@ -981,14 +1143,20 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) // If this is a callback to a keepalive record, do not free it. if (result == mStatus_BadStateErr) { - LogInfo("regrecord_callback: Callback with error code mStatus_BadStateErr - not freeing the record."); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] regrecord_callback: Callback with error code mStatus_BadStateErr - not freeing the record.", request->request_id); } else { // unlink from list, free memory registered_record_entry **ptr = &request->u.reg_recs; while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; - if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } + if (!*ptr) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] regrecord_callback - record not in list!", request->request_id); + return; + } *ptr = (*ptr)->next; freeL("registered_record_entry AuthRecord regrecord_callback", re->rr); freeL("registered_record_entry regrecord_callback", re); @@ -996,14 +1164,21 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) } else { - if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!"); + if (re->external_advertise) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] regrecord_callback: external_advertise already set!", request->request_id); + } +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) if (callExternalHelpers(re->origInterfaceID, &rr->namestorage, request->flags)) { - LogInfo("regrecord_callback: calling external_start_advertising_service"); - external_start_advertising_service(&rr->resrec, request->flags); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] regrecord_callback: calling external_start_advertising_service", request->request_id); + external_start_advertising_service(&rr->resrec, request->flags, request->process_id); re->external_advertise = mDNStrue; } +#endif } } } @@ -1012,14 +1187,11 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) // This accounts for 2 places (connect_callback, request_callback) mDNSlocal void set_peer_pid(request_state *request) { -#ifdef LOCAL_PEEREPID - pid_t p = (pid_t) -1; - socklen_t len = sizeof(p); -#endif - request->pid_name[0] = '\0'; request->process_id = -1; #ifdef LOCAL_PEEREPID + pid_t p = (pid_t) -1; + socklen_t len = sizeof(p); if (request->sd < 0) return; // to extract the effective pid value @@ -1044,7 +1216,9 @@ mDNSlocal void connection_termination(request_state *request) // and terminate any subbordinate operations sharing this file descriptor request_state **req = &all_requests; - LogOperation("%3d: DNSServiceCreateConnection STOP PID[%d](%s)", request->sd, request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceCreateConnection STOP PID[%d](" PUB_S ")", + request->request_id, request->process_id, request->pid_name); while (*req) { @@ -1056,6 +1230,15 @@ mDNSlocal void connection_termination(request_state *request) if (tmp->replies) LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd); abort_request(tmp); *req = tmp->next; +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (tmp->trust) + { + void * context = mdns_trust_get_context(tmp->trust); + mdns_trust_set_context(tmp->trust, NULL); + if (context) freeL("context/connection_termination", context); + mdns_trust_forget(&tmp->trust); + } +#endif freeL("request_state/connection_termination", tmp); } else @@ -1065,13 +1248,18 @@ mDNSlocal void connection_termination(request_state *request) while (request->u.reg_recs) { registered_record_entry *ptr = request->u.reg_recs; - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceRegisterRecord(0x%X, %d, " PRI_S ") STOP PID[%d](" PUB_S ")", + request->request_id, request->flags, request->interfaceIndex, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), request->process_id, + request->pid_name); request->u.reg_recs = request->u.reg_recs->next; ptr->rr->RecordContext = NULL; if (ptr->external_advertise) { ptr->external_advertise = mDNSfalse; - external_stop_advertising_service(&ptr->rr->resrec, request->flags); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + external_stop_advertising_service(&ptr->rr->resrec, request->flags, request->process_id); +#endif } LogMcastS(ptr->rr, request, reg_stop); mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us @@ -1082,7 +1270,8 @@ mDNSlocal void connection_termination(request_state *request) mDNSlocal void handle_cancel_request(request_state *request) { request_state **req = &all_requests; - LogDebug("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "[R%d] Cancel %08X %08X", + request->request_id, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]); while (*req) { if ((*req)->primary == request && @@ -1093,6 +1282,15 @@ mDNSlocal void handle_cancel_request(request_state *request) request_state *tmp = *req; abort_request(tmp); *req = tmp->next; +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (tmp->trust) + { + void * context = mdns_trust_get_context(tmp->trust); + mdns_trust_set_context(tmp->trust, NULL); + if (context) freeL("context/handle_cancel_request", context); + mdns_trust_forget(&tmp->trust); + } +#endif freeL("request_state/handle_cancel_request", tmp); } else @@ -1100,6 +1298,161 @@ mDNSlocal void handle_cancel_request(request_state *request) } } +mDNSlocal mStatus _handle_regrecord_request_start(request_state *request, AuthRecord * rr) +{ + mStatus err; + registered_record_entry *re; + // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit + // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari. + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) && + rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA || + rr->resrec.rrtype == kDNSType_CNAME)) + { + freeL("AuthRecord/handle_regrecord_request", rr); + return (mStatus_BadParamErr); + } + // allocate registration entry, link into list + re = (registered_record_entry *) callocL("registered_record_entry", sizeof(*re)); + if (!re) FatalError("ERROR: calloc"); + re->key = request->hdr.reg_index; + re->rr = rr; + re->regrec_client_context = request->hdr.client_context; + re->request = request; + re->external_advertise = mDNSfalse; + rr->RecordContext = re; + rr->RecordCallback = regrecord_callback; + + re->origInterfaceID = rr->resrec.InterfaceID; + if (rr->resrec.InterfaceID == mDNSInterface_P2P) + rr->resrec.InterfaceID = mDNSInterface_Any; +#if 0 + if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); +#endif + if (rr->resrec.rroriginalttl == 0) + rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); + + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceRegisterRecord(0x%X, %d, " PRI_S ") START PID[%d](" PUB_S ")", + request->request_id, request->flags, request->interfaceIndex, RRDisplayString(&mDNSStorage, &rr->resrec), request->process_id, + request->pid_name); + + err = mDNS_Register(&mDNSStorage, rr); + if (err) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceRegisterRecord(0x%X, %d," PRI_S ") ERROR (%d)", + request->request_id, request->flags, request->interfaceIndex, RRDisplayString(&mDNSStorage, &rr->resrec), err); + freeL("registered_record_entry", re); + freeL("registered_record_entry/AuthRecord", rr); + } + else + { + LogMcastS(rr, request, reg_start); + re->next = request->u.reg_recs; + request->u.reg_recs = re; + } + return err; +} + +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + +mDNSlocal void _return_regrecord_request_error(request_state *request, mStatus error) +{ + reply_state *rep; + if (GenerateNTDResponse(NULL, 0, request, &rep, reg_record_reply_op, 0, error) != mStatus_NoError) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[R%u] DNSServiceRegisterRecord _return_regrecord_request_error: error(%d)", request->request_id, error); + } + else + { + append_reply(request, rep); + } +} + +mDNSlocal mStatus _handle_regrecord_request_with_trust(request_state *request, AuthRecord * rr) +{ + mStatus err; + if (audit_token_to_pid(request->audit_token) == 0) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING, "[R%u] _handle_regrecord_request_with_trust: no audit token for pid(%s %d)", request->request_id, request->pid_name, request->process_id); + err = _handle_regrecord_request_start(request, rr); + } + else + { + const char *service_ptr = NULL; + char type_str[MAX_ESCAPED_DOMAIN_NAME] = ""; + domainlabel name; + domainname type, domain; + bool good = DeconstructServiceName(rr->resrec.name, &name, &type, &domain); + if (good) + { + ConvertDomainNameToCString(&type, type_str); + service_ptr = type_str; + } + + mdns_trust_flags_t flags = mdns_trust_flags_none; + mdns_trust_status_t status = mdns_trust_check_bonjour(request->audit_token, service_ptr, &flags); + switch (status) + { + case mdns_trust_status_denied: + case mdns_trust_status_pending: + { + mdns_trust_t trust = mdns_trust_create(request->audit_token, service_ptr, flags); + if (!trust) + { + freeL("AuthRecord/_handle_regrecord_request_with_trust", rr); + err = mStatus_NoMemoryErr; + goto exit; + } + mdns_trust_set_context(trust, rr); + mdns_trust_set_queue(trust, _get_trust_results_dispatch_queue()); + mdns_trust_set_event_handler(trust, ^(mdns_trust_event_t event, mdns_trust_status_t update) + { + if (event == mdns_trust_event_result) + { + mStatus error = (update != mdns_trust_status_granted) ? mStatus_PolicyDenied : mStatus_NoError; + KQueueLock(); + AuthRecord * _rr = mdns_trust_get_context(trust); + if (_rr) + { + if (!error) + { + mdns_trust_set_context(trust, NULL); // _handle_regrecord_request_start handles free + error = _handle_regrecord_request_start(request, _rr); + // No context means the request was canceled before we got here + } + if (error) // (not else if) Always check for error result + { + _return_regrecord_request_error(request, error); + } + } + KQueueUnlock("_handle_regrecord_request_with_trust"); + } + }); + request->trust = trust; + mdns_trust_activate(trust); + err = mStatus_NoError; + break; + } + + case mdns_trust_status_no_entitlement: + err = mStatus_NoAuth; + break; + + case mdns_trust_status_granted: + err = _handle_regrecord_request_start(request, rr); + break; + + default: + err = mStatus_UnknownErr; + break; + } + } +exit: + return err; +} +#endif // TRUST_ENFORCEMENT + mDNSlocal mStatus handle_regrecord_request(request_state *request) { mStatus err = mStatus_BadParamErr; @@ -1111,53 +1464,19 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) rr = read_rr_from_ipc_msg(request, 1, 1); if (rr) { - registered_record_entry *re; - // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit - // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari. - if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) && - rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA || - rr->resrec.rrtype == kDNSType_CNAME)) +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (os_feature_enabled(mDNSResponder, bonjour_privacy) && + IsLocalDomain(rr->resrec.name)) { - freeL("AuthRecord/handle_regrecord_request", rr); - return (mStatus_BadParamErr); - } - // allocate registration entry, link into list - re = mallocL("registered_record_entry", sizeof(registered_record_entry)); - if (!re) - FatalError("ERROR: malloc"); - re->key = request->hdr.reg_index; - re->rr = rr; - re->regrec_client_context = request->hdr.client_context; - re->request = request; - re->external_advertise = mDNSfalse; - rr->RecordContext = re; - rr->RecordCallback = regrecord_callback; - - re->origInterfaceID = rr->resrec.InterfaceID; - if (rr->resrec.InterfaceID == mDNSInterface_P2P) - rr->resrec.InterfaceID = mDNSInterface_Any; -#if 0 - if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); -#endif - if (rr->resrec.rroriginalttl == 0) - rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); - - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), - request->process_id, request->pid_name); - - err = mDNS_Register(&mDNSStorage, rr); - if (err) - { - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err); - freeL("registered_record_entry", re); - freeL("registered_record_entry/AuthRecord", rr); + err = _handle_regrecord_request_with_trust(request, rr); } else { - LogMcastS(rr, request, reg_start); - re->next = request->u.reg_recs; - request->u.reg_recs = re; + err = _handle_regrecord_request_start(request, rr); } +#else + err = _handle_regrecord_request_start(request, rr); +#endif } return(err); } @@ -1176,10 +1495,13 @@ mDNSlocal void regservice_termination_callback(request_state *request) service_instance *p = request->u.servicereg.instances; request->u.servicereg.instances = request->u.servicereg.instances->next; // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) - LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", request->sd, p->srs.RR_SRV.resrec.name->c, - mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%d] DNSServiceRegister(" PRI_DM_NAME ", %u) STOP PID[%d](" PUB_S ")", + request->request_id, DM_NAME_PARAM(p->srs.RR_SRV.resrec.name), + mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), request->process_id, request->pid_name); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) external_stop_advertising_helper(p); +#endif // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing @@ -1217,19 +1539,29 @@ mDNSlocal request_state *LocateSubordinateRequest(request_state *request) return(request); } -mDNSlocal mStatus add_record_to_service(request_state *request, service_instance *instance, mDNSu16 rrtype, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl) +mDNSlocal mStatus add_record_to_service(request_state *request, service_instance *instance, mDNSu16 rrtype, mDNSu16 rdlen, + const mDNSu8 *const rdata, mDNSu32 ttl) { ServiceRecordSet *srs = &instance->srs; mStatus result; - size_t size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); - ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); - if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } + const size_t rdcapacity = (rdlen > sizeof(RDataBody2)) ? rdlen : sizeof(RDataBody2); + ExtraResourceRecord *extra = (ExtraResourceRecord *)callocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + rdcapacity); + if (!extra) { my_perror("ERROR: calloc"); return mStatus_NoMemoryErr; } - mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd extra->r.resrec.rrtype = rrtype; - extra->r.rdatastorage.MaxRDLength = (mDNSu16) size; + extra->r.resrec.rdata = &extra->r.rdatastorage; + extra->r.resrec.rdata->MaxRDLength = (mDNSu16)rdcapacity; extra->r.resrec.rdlength = rdlen; - mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen); + if (!SetRData(mDNSNULL, rdata, rdata + rdlen, &extra->r.resrec, rdlen)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] read_rr_from_ipc_msg: SetRData failed for " PRI_DM_NAME " (" PUB_S ")", + request->request_id, DM_NAME_PARAM(request->u.servicereg.instances ? + request->u.servicereg.instances->srs.RR_SRV.resrec.name : mDNSNULL), DNSTypeName(rrtype)); + freeL("ExtraResourceRecord/add_record_to_service", extra); + return mStatus_BadParamErr; + } + SetNewRData(&extra->r.resrec, mDNSNULL, 0); // Sets rr->rdatahash for us // use InterfaceID value from DNSServiceRegister() call that created the original service extra->r.resrec.InterfaceID = request->u.servicereg.InterfaceID; @@ -1242,12 +1574,14 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance LogMcastS(&srs->RR_PTR, request, reg_start); extra->ClientID = request->hdr.reg_index; +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) if ( instance->external_advertise && callExternalHelpers(request->u.servicereg.InterfaceID, &instance->domain, request->flags)) { LogInfo("add_record_to_service: calling external_start_advertising_service"); - external_start_advertising_service(&extra->r.resrec, request->flags); + external_start_advertising_service(&extra->r.resrec, request->flags, request->process_id); } +#endif return result; } @@ -1258,27 +1592,41 @@ mDNSlocal mStatus handle_add_request(request_state *request) DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); mDNSu16 rrtype = get_uint16(&request->msgptr, request->msgend); mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); - const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + const mDNSu8 *const rdata = (const mDNSu8 *)get_rdata(&request->msgptr, request->msgend, rdlen); mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); if (!ttl) ttl = DefaultTTLforRRType(rrtype); (void)flags; // Unused - if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceAddRecord(unreadable parameters)", request->request_id); + return(mStatus_BadParamErr); + } // If this is a shared connection, check if the operation actually applies to a subordinate request_state object if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); if (request->terminate != regservice_termination_callback) - { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceAddRecord(not a registered service ref)", request->request_id); + return(mStatus_BadParamErr); + } // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug // in the application. See radar://9165807. if (mDNSIPPortIsZero(request->u.servicereg.port)) - { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); } - - LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d) PID[%d](%s)", request->sd, flags, - (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen, - request->process_id, request->pid_name); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceAddRecord: adding record to a service registered with zero port", request->request_id); + return(mStatus_BadParamErr); + } + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceAddRecord(%X, " PRI_DM_NAME ", " PUB_S ", %d) PID[%d](" PUB_S ")", + request->request_id, flags, + DM_NAME_PARAM((request->u.servicereg.instances) ? (request->u.servicereg.instances->srs.RR_SRV.resrec.name) : mDNSNULL), + DNSTypeName(rrtype), rdlen, request->process_id, request->pid_name); for (i = request->u.servicereg.instances; i; i = i->next) { @@ -1307,36 +1655,55 @@ mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd if (external_advertise) { ResourceRecord ext = rr->resrec; +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) DNSServiceFlags flags = deriveD2DFlagsFromAuthRecType(rr->ARType); +#endif if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit; SetNewRData(&ext, oldrd, oldrdlen); - external_stop_advertising_service(&ext, flags); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + external_stop_advertising_service(&ext, flags, 0); LogInfo("update_callback: calling external_start_advertising_service"); - external_start_advertising_service(&rr->resrec, flags); + external_start_advertising_service(&rr->resrec, flags, 0); +#endif } exit: if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd); } -mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise) +mDNSlocal mStatus update_record(AuthRecord *ar, mDNSu16 rdlen, const mDNSu8 *const rdata, mDNSu32 ttl, + const mDNSBool *const external_advertise, const mDNSu32 request_id) { + ResourceRecord rr; mStatus result; - const size_t rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); - RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize); - if (!newrd) FatalError("ERROR: malloc"); - newrd->MaxRDLength = (mDNSu16) rdsize; - mDNSPlatformMemCopy(&newrd->u, rdata, rdlen); - + const size_t rdcapacity = (rdlen > sizeof(RDataBody2)) ? rdlen : sizeof(RDataBody2); + RData *newrd = (RData *) callocL("RData/update_record", sizeof(*newrd) - sizeof(RDataBody) + rdcapacity); + if (!newrd) FatalError("ERROR: calloc"); + mDNSPlatformMemZero(&rr, (mDNSu32)sizeof(rr)); + rr.name = ar->resrec.name; + rr.rrtype = ar->resrec.rrtype; + rr.rrclass = ar->resrec.rrclass; + rr.rdata = newrd; + rr.rdata->MaxRDLength = (mDNSu16)rdcapacity; + rr.rdlength = rdlen; + if (!SetRData(mDNSNULL, rdata, rdata + rdlen, &rr, rdlen)) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] update_record: SetRData failed for " PRI_DM_NAME " (" PUB_S ")", + request_id, DM_NAME_PARAM(rr.name), DNSTypeName(rr.rrtype)); + freeL("RData/update_record", newrd); + return mStatus_BadParamErr; + } + rdlen = GetRDLength(&rr, mDNSfalse); // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. - if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; } + if (ar->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; } - if (external_advertise) rr->UpdateContext = (void *)external_advertise; + if (external_advertise) ar->UpdateContext = (void *)external_advertise; - result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback); - if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); } + result = mDNS_Update(&mDNSStorage, ar, ttl, rdlen, newrd, update_callback); + if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, ar)); freeL("RData/update_record", newrd); } return result; } @@ -1350,11 +1717,16 @@ mDNSlocal mStatus handle_update_request(request_state *request) // get the message data DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); // flags unused mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); - const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + const mDNSu8 *const rdata = (const mDNSu8 *)get_rdata(&request->msgptr, request->msgend, rdlen); mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); (void)flags; // Unused - if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceUpdateRecord(unreadable parameters)", request->request_id); + return(mStatus_BadParamErr); + } // If this is a shared connection, check if the operation actually applies to a subordinate request_state object if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); @@ -1367,10 +1739,12 @@ mDNSlocal mStatus handle_update_request(request_state *request) { if (reptr->key == hdr->reg_index) { - result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise); - LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s) PID[%d](%s)", - request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>", - request->process_id, request->pid_name); + result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise, request->request_id); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceUpdateRecord(" PRI_DM_NAME ", " PUB_S ") PID[%d](" PUB_S ")", + request->request_id, DM_NAME_PARAM(reptr->rr->resrec.name), + reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>", + request->process_id, request->pid_name); goto end; } } @@ -1379,11 +1753,19 @@ mDNSlocal mStatus handle_update_request(request_state *request) } if (request->terminate != regservice_termination_callback) - { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceUpdateRecord(not a registered service ref)", request->request_id); + return(mStatus_BadParamErr); + } // For a service registered with zero port, only SRV record is initialized. Don't allow any updates. if (mDNSIPPortIsZero(request->u.servicereg.port)) - { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->request_id); + return(mStatus_BadParamErr); + } // update the saved off TXT data for the service if (hdr->reg_index == TXT_RECORD_INDEX) @@ -1411,7 +1793,7 @@ mDNSlocal mStatus handle_update_request(request_state *request) } if (!rr) { result = mStatus_BadReferenceErr; goto end; } - result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise); + result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise, request->request_id); if (result && i->default_local) goto end; else result = mStatus_NoError; // suppress non-local default errors } @@ -1442,7 +1824,9 @@ mDNSlocal mStatus remove_record(request_state *request) e->rr->RecordContext = NULL; if (e->external_advertise) { - external_stop_advertising_service(&e->rr->resrec, request->flags); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + external_stop_advertising_service(&e->rr->resrec, request->flags, request->process_id); +#endif e->external_advertise = mDNSfalse; } LogMcastS(e->rr, request, reg_stop); @@ -1466,7 +1850,12 @@ mDNSlocal mStatus remove_extra(const request_state *const request, service_insta if (ptr->ClientID == request->hdr.reg_index) // found match { *rrtype = ptr->r.resrec.rrtype; - if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec, request->flags); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + if (serv->external_advertise) + { + external_stop_advertising_service(&ptr->r.resrec, request->flags, request->process_id); + } +#endif err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr); break; } @@ -1479,7 +1868,12 @@ mDNSlocal mStatus handle_removerecord_request(request_state *request) mStatus err = mStatus_BadReferenceErr; get_flags(&request->msgptr, request->msgend); // flags unused - if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (!request->msgptr) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceRemoveRecord(unreadable parameters)", request->request_id); + return(mStatus_BadParamErr); + } // If this is a shared connection, check if the operation actually applies to a subordinate request_state object if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); @@ -1487,14 +1881,19 @@ mDNSlocal mStatus handle_removerecord_request(request_state *request) if (request->terminate == connection_termination) err = remove_record(request); // remove individually registered record else if (request->terminate != regservice_termination_callback) - { LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceRemoveRecord(not a registered service ref)", request->request_id); + return(mStatus_BadParamErr); + } else { service_instance *i; mDNSu16 rrtype = 0; - LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s) PID[%d](%s)", request->sd, - (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, - rrtype ? DNSTypeName(rrtype) : "<NONE>", request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%d] DNSServiceRemoveRecord(" PRI_DM_NAME ", " PUB_S ") PID[%d](" PUB_S ")", + request->request_id, + DM_NAME_PARAM((request->u.servicereg.instances) ? (request->u.servicereg.instances->srs.RR_SRV.resrec.name) : mDNSNULL), + rrtype ? DNSTypeName(rrtype) : "<NONE>", request->process_id, request->pid_name); for (i = request->u.servicereg.instances; i; i = i->next) { err = remove_extra(request, i, &rrtype); @@ -1509,7 +1908,7 @@ mDNSlocal mStatus handle_removerecord_request(request_state *request) // If there's a comma followed by another character, // FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character. // Otherwise, it returns a pointer to the final nul at the end of the string -mDNSlocal char *FindFirstSubType(char *p, char **AnonData) +mDNSlocal char *FindFirstSubType(char *p) { while (*p) { @@ -1522,11 +1921,6 @@ mDNSlocal char *FindFirstSubType(char *p, char **AnonData) *p++ = 0; return(p); } - else if (p[0] == ':' && p[1]) - { - *p++ = 0; - *AnonData = p; - } else { p++; @@ -1558,10 +1952,10 @@ mDNSlocal char *FindNextSubType(char *p) } // Returns -1 if illegal subtype found -mDNSexport mDNSs32 ChopSubTypes(char *regtype, char **AnonData) +mDNSlocal mDNSs32 ChopSubTypes(char *regtype) { mDNSs32 NumSubTypes = 0; - char *stp = FindFirstSubType(regtype, AnonData); + char *stp = FindFirstSubType(regtype); while (stp && *stp) // If we found a comma... { if (*stp == ',') return(-1); @@ -1572,65 +1966,26 @@ mDNSexport mDNSs32 ChopSubTypes(char *regtype, char **AnonData) return(NumSubTypes); } -mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData) +mDNSlocal AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) { AuthRecord *st = mDNSNULL; - // - // "p" is pointing at the regtype e.g., _http._tcp followed by ":<AnonData>" indicated - // by AnonData being non-NULL which is in turn follwed by ",<SubTypes>" indicated by - // NumSubTypes being non-zero. We need to skip the initial regtype to get to the actual - // data that we want. When we come here, ChopSubTypes has null terminated like this e.g., - // - // _http._tcp<NULL><AnonData><NULL><SubType1><NULL><SubType2><NULL> etc. - // - // 1. If we have Anonymous data and subtypes, skip the regtype (e.g., "_http._tcp") - // to get the AnonData and then skip the AnonData to get to the SubType. - // - // 2. If we have only SubTypes, skip the regtype to get to the SubType data. - // - // 3. If we have only AnonData, skip the regtype to get to the AnonData. - // - // 4. If we don't have AnonData or NumStypes, it is a noop. - // - if (AnonData) - { - int len; - - // Skip the regtype - while (*p) p++; - p++; - - len = strlen(p) + 1; - *AnonData = mallocL("Anonymous", len); - if (!(*AnonData)) - { - return (mDNSNULL); - } - mDNSPlatformMemCopy(*AnonData, p, len); - } if (NumSubTypes) { mDNSs32 i; - st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); + st = (AuthRecord *) callocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); if (!st) return(mDNSNULL); for (i = 0; i < NumSubTypes; i++) { mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); - // First time through we skip the regtype or AnonData. Subsequently, the - // previous subtype. while (*p) p++; p++; if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p)) { freeL("ServiceSubTypes", st); - if (AnonData && *AnonData) - freeL("AnonymousData", *AnonData); return(mDNSNULL); } } } - // If NumSubTypes is zero and AnonData is non-NULL, we still return NULL but AnonData has been - // initialized. The caller knows how to handle this. return(st); } @@ -1659,8 +2014,8 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain } } - instance = mallocL("service_instance", sizeof(*instance) + extra_size); - if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } + instance = (service_instance *) callocL("service_instance", sizeof(*instance) + extra_size); + if (!instance) { my_perror("ERROR: calloc"); return mStatus_NoMemoryErr; } instance->next = mDNSNULL; instance->request = request; @@ -1670,18 +2025,7 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain instance->external_advertise = mDNSfalse; AssignDomainName(&instance->domain, domain); - instance->srs.AnonData = mDNSNULL; - if (!request->u.servicereg.AnonData) - { - instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, mDNSNULL); - } - else - { - char *AnonData = mDNSNULL; - instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, &AnonData); - if (AnonData) - instance->srs.AnonData = (const mDNSu8 *)AnonData; - } + instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string); if (request->u.servicereg.num_subtypes && !instance->subtypes) { @@ -1774,80 +2118,163 @@ mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d } } -// Don't allow normal and anonymous registration to coexist. -mDNSlocal mDNSBool CheckForMixedRegistrations(domainname *regtype, domainname *domain, mDNSBool AnonData) +// Returns true if the interfaceIndex value matches one of the pre-defined +// special values listed in the switch statement below. +mDNSlocal mDNSBool PreDefinedInterfaceIndex(mDNSu32 interfaceIndex) { - request_state *request; - - // We only care about local domains where the anonymous extension is - // implemented. - if (!SameDomainName(domain, (const domainname *) "\x5" "local")) + switch(interfaceIndex) { - return mDNStrue; + case kDNSServiceInterfaceIndexAny: + case kDNSServiceInterfaceIndexLocalOnly: + case kDNSServiceInterfaceIndexUnicast: + case kDNSServiceInterfaceIndexP2P: + case kDNSServiceInterfaceIndexBLE: + return mDNStrue; + default: + return mDNSfalse; } +} - for (request = all_requests; request; request = request->next) +mDNSlocal mStatus _handle_regservice_request_start(request_state *request, const domainname * const d) +{ + mStatus err; + + request->terminate = regservice_termination_callback; + err = register_service_instance(request, d); + +#if MDNSRESPONDER_SUPPORTS(APPLE, METRICS) + ++curr_num_regservices; + if (curr_num_regservices > max_num_regservices) + max_num_regservices = curr_num_regservices; +#endif + +#if 0 + err = AuthorizedDomain(request, d, AutoRegistrationDomains) ? register_service_instance(request, d) : mStatus_NoError; +#endif + if (!err) { - service_instance *ptr; + if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage); - if (request->terminate != regservice_termination_callback) continue; - for (ptr = request->u.servicereg.instances; ptr ; ptr = ptr->next) + if (request->u.servicereg.default_domain) { - if (!SameDomainName(&ptr->domain, (const domainname *)"\x5" "local") || - !SameDomainName(&request->u.servicereg.type, regtype)) - { - continue; - } + DNameListElem *ptr; + // Note that we don't report errors for non-local, non-explicit domains + for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next) + if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid) + register_service_instance(request, &ptr->name); + } + } + return err; +} + +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + +mDNSlocal void _return_regservice_request_error(request_state *request, mStatus error) +{ + if (request->u.servicereg.txtdata) + { + freeL("service_info txtdata", request->u.servicereg.txtdata); + request->u.servicereg.txtdata = NULL; + } + + reply_state *rep; + if (GenerateNTDResponse(NULL, 0, request, &rep, reg_service_reply_op, 0, error) != mStatus_NoError) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[R%u] DNSServiceRegister _return_regservice_request_error: error(%d)", request->request_id, error); + } + else + { + append_reply(request, rep); + } +} - // If we are about to register a anonymous registraion, we dont't want to - // allow the regular ones and vice versa. - if (AnonData) +mDNSlocal mStatus _handle_regservice_request_with_trust(request_state *request, const domainname * const d) +{ + mStatus err; + if (audit_token_to_pid(request->audit_token) == 0) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING, "[R%u] _handle_regservice_request_with_trust: no audit token for pid(%s %d)", request->request_id, request->pid_name, request->process_id); + err = _handle_regservice_request_start(request, d); + } + else + { + mdns_trust_flags_t flags = mdns_trust_flags_none; + mdns_trust_status_t status = mdns_trust_check_register_service(request->audit_token, request->u.servicereg.type_as_string, &flags); + switch (status) { + case mdns_trust_status_denied: + case mdns_trust_status_pending: { - if (!ptr->srs.AnonData) + mdns_trust_t trust = mdns_trust_create(request->audit_token, request->u.servicereg.type_as_string, flags); + if (!trust) { - LogMsg("CheckForMixedRegistrations: Normal registration already exists for %##s", regtype->c); - return mDNSfalse; + err = mStatus_NoMemoryErr; + goto exit; } - } - else - { - // Allow multiple regular registrations - if (ptr->srs.AnonData) + void * context = mallocL("context/_handle_regservice_request_with_trust", sizeof(domainname)); + if (!context) { - LogMsg("CheckForMixedRegistrations: Anonymous registration already exists for %##s", regtype->c); - return mDNSfalse; + my_perror("ERROR: mallocL context/_handle_regservice_request_with_trust"); + mdns_release(trust); + err = mStatus_NoMemoryErr; + goto exit; } + memcpy(context, d, sizeof(domainname)); + mdns_trust_set_context(trust, context); + + mdns_trust_set_queue(trust, _get_trust_results_dispatch_queue()); + mdns_trust_set_event_handler(trust, ^(mdns_trust_event_t event, mdns_trust_status_t update) + { + if (event == mdns_trust_event_result) + { + mStatus error = (update != mdns_trust_status_granted) ? mStatus_PolicyDenied : mStatus_NoError; + KQueueLock(); + const domainname * _d = mdns_trust_get_context(trust); + if (_d) + { + if (!error) + { + error = _handle_regservice_request_start(request, _d); + // No context means the request was canceled before we got here + } + if (error) // (not else if) Always check for error result + { + _return_regservice_request_error(request, error); + } + } + KQueueUnlock("_register_service_instance_with_trust"); + } + }); + request->trust = trust; + mdns_trust_activate(trust); + err = mStatus_NoError; + break; } - } - } - return mDNStrue; -} -// Returns true if the interfaceIndex value matches one of the pre-defined -// special values listed in the switch statement below. -mDNSlocal mDNSBool PreDefinedInterfaceIndex(mDNSu32 interfaceIndex) -{ - switch(interfaceIndex) - { - case kDNSServiceInterfaceIndexAny: - case kDNSServiceInterfaceIndexLocalOnly: - case kDNSServiceInterfaceIndexUnicast: - case kDNSServiceInterfaceIndexP2P: - case kDNSServiceInterfaceIndexBLE: - return mDNStrue; - default: - return mDNSfalse; + case mdns_trust_status_no_entitlement: + err = mStatus_NoAuth; + break; + + case mdns_trust_status_granted: + err = _handle_regservice_request_start(request, d); + break; + + default: + err = mStatus_UnknownErr; + break; + } } +exit: + return err; } +#endif // TRUST_ENFORCEMENT mDNSlocal mStatus handle_regservice_request(request_state *request) { char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME]; - char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; + char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; // Note that this service type may include a trailing list of subtypes domainname d, srv; mStatus err; - char *AnonData = mDNSNULL; const char *msgTXTData; DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); @@ -1882,10 +2309,10 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) LogInfo("handle_regservice_request: registration pending for interface index %d", interfaceIndex); } - if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 || - get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0) + if (get_string(&request->msgptr, request->msgend, name, sizeof(name )) < 0 || + get_string(&request->msgptr, request->msgend, type_as_string, sizeof(type_as_string)) < 0 || + get_string(&request->msgptr, request->msgend, domain, sizeof(domain )) < 0 || + get_string(&request->msgptr, request->msgend, host, sizeof(host )) < 0) { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } request->flags = flags; @@ -1916,26 +2343,12 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) } // Check for sub-types after the service type - request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string, &AnonData); // Note: Modifies regtype string to remove trailing subtypes + request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string); // Note: Modifies regtype string to remove trailing subtypes if (request->u.servicereg.num_subtypes < 0) { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); goto bad_param; } - if (AnonData) - { - int AnonDataLen = strlen(AnonData); - if (AnonDataLen > MAX_ANONYMOUS_DATA) - { - LogMsg("ERROR: handle_regservice_request: AnonDataLen %d", AnonDataLen); - goto bad_param; - } - request->u.servicereg.AnonData = mDNStrue; - } - else - { - request->u.servicereg.AnonData = mDNSfalse; - } // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string)) @@ -1971,9 +2384,6 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) MakeDomainNameFromDNSNameString(&d, "local."); } - // We don't allow the anonymous and the regular ones to coexist - if (!CheckForMixedRegistrations(&request->u.servicereg.type, &d, request->u.servicereg.AnonData)) { goto bad_param; } - if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d)) { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", @@ -2005,42 +2415,37 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) } #endif // APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR - LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START PID[%d](%s)", - request->sd, request->flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, - mDNSVal16(request->u.servicereg.port), request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceRegister(%X, %d, \"" PRI_S "\", \"" PRI_S "\", \"" PRI_S "\", \"" PRI_S "\", %u) START PID[%d](" PUB_S ")", + request->request_id, request->flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, + mDNSVal16(request->u.servicereg.port), request->process_id, request->pid_name); // We need to unconditionally set request->terminate, because even if we didn't successfully // start any registrations right now, subsequent configuration changes may cause successful // registrations to be added, and we'll need to cancel them before freeing this memory. // We also need to set request->terminate first, before adding additional service instances, - // because the uds_validatelists uses the request->terminate function pointer to determine + // because the udsserver_validatelists uses the request->terminate function pointer to determine // what kind of request this is, and therefore what kind of list validation is required. - request->terminate = regservice_termination_callback; - - err = register_service_instance(request, &d); - -#if TARGET_OS_EMBEDDED - ++curr_num_regservices; - if (curr_num_regservices > max_num_regservices) - max_num_regservices = curr_num_regservices; -#endif + request->terminate = NULL; -#if 0 - err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError; -#endif - if (!err) +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (os_feature_enabled(mDNSResponder, bonjour_privacy) && + (request->u.servicereg.default_domain || IsLocalDomain(&d))) { - if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage); - - if (!*domain) + err = _handle_regservice_request_with_trust(request, &d); + if (err == mStatus_NoAuth && request->u.servicereg.txtdata) { - DNameListElem *ptr; - // Note that we don't report errors for non-local, non-explicit domains - for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next) - if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid) - register_service_instance(request, &ptr->name); + freeL("service_info txtdata", request->u.servicereg.txtdata); + request->u.servicereg.txtdata = NULL; } } + else + { + err = _handle_regservice_request_start(request, &d); + } +#else + err = _handle_regservice_request_start(request, &d); +#endif return(err); @@ -2095,9 +2500,11 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc validReply: - LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s interface %d: %s", - req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", - mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] DNSServiceBrowse(" PRI_DM_NAME ", " PUB_S ") RESULT " PUB_S " interface %d: " PRI_S, + req->request_id, mDNSVal16(question->TargetQID), DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype), + AddRecord ? "ADD" : "RMV", mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), + RRDisplayString(m, answer)); append_reply(req, rep); } @@ -2139,12 +2546,11 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d { debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; } } - b = mallocL("browser_t", sizeof(*b)); + b = (browser_t *) callocL("browser_t", sizeof(*b)); if (!b) return mStatus_NoMemoryErr; - mDNSPlatformMemZero(b, sizeof(*b)); AssignDomainName(&b->domain, d); SetQuestionPolicy(&b->q, info); - err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.AnonData, info->u.browser.interface_id, info->flags, + err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.interface_id, info->flags, info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info); if (err) { @@ -2167,13 +2573,15 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d #endif // APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR LogMcastQ(&b->q, info, q_start); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) if (callExternalHelpers(info->u.browser.interface_id, &b->domain, info->flags)) { domainname tmp; ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain); LogDebug("add_domain_to_browser: calling external_start_browsing_for_service()"); - external_start_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags); + external_start_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags, info->process_id); } +#endif } return err; } @@ -2186,22 +2594,23 @@ mDNSlocal void browse_termination_callback(request_state *info) LogInfo("%3d: DNSServiceBrowse Cancel WAB PID[%d](%s)", info->sd, info->process_id, info->pid_name); uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY); } - if (info->u.browser.AnonData) - freeL("Anonymous", (void *)info->u.browser.AnonData); while (info->u.browser.browsers) { browser_t *ptr = info->u.browser.browsers; +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) if (callExternalHelpers(ptr->q.InterfaceID, &ptr->domain, ptr->q.flags)) { domainname tmp; ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain); LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()"); - external_stop_browsing_for_service(ptr->q.InterfaceID, &tmp, kDNSType_PTR, ptr->q.flags); + external_stop_browsing_for_service(ptr->q.InterfaceID, &tmp, kDNSType_PTR, ptr->q.flags, info->process_id); } - - LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\") STOP PID[%d](%s)", - info->sd, info->flags, info->interfaceIndex, ptr->q.qname.c, info->process_id, info->pid_name); +#endif + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceBrowse(%X, %d, \"" PRI_DM_NAME "\") STOP PID[%d](" PUB_S ")", + info->request_id, info->flags, info->interfaceIndex, DM_NAME_PARAM(&ptr->q.qname), + info->process_id, info->pid_name); info->u.browser.browsers = ptr->next; mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result @@ -2278,7 +2687,7 @@ mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int { // allocate/register legacy and non-legacy _browse PTR record mStatus err; - ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr)); + ARListElem *ptr = (ARListElem *) mDNSPlatformMemAllocateClear(sizeof(*ptr)); debugf("Incrementing %s refcount for %##s", (type == mDNS_DomainTypeBrowse ) ? "browse domain " : @@ -2330,7 +2739,7 @@ mDNSlocal void DeregisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, in mDNSlocal void AddAutoBrowseDomain(const mDNSu32 uid, const domainname *const name) { - DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem)); + DNameListElem *new = (DNameListElem *) mDNSPlatformMemAllocateClear(sizeof(*new)); if (!new) { LogMsg("ERROR: malloc"); return; } AssignDomainName(&new->name, name); new->uid = uid; @@ -2508,14 +2917,143 @@ mDNSlocal void AutomaticBrowseDomainChange(mDNS *const m, DNSQuestion *q, const else RmvAutoBrowseDomain(0, &answer->rdata->u.name); } +mDNSlocal mStatus _handle_browse_request_start(request_state *request, const char * domain) +{ + domainname d; + mStatus err = mStatus_NoError; + + request->terminate = browse_termination_callback; + + if (domain[0]) + { + if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr); + err = add_domain_to_browser(request, &d); + } + else + { + DNameListElem *sdom; + for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next) + if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid) + { + err = add_domain_to_browser(request, &sdom->name); + if (err) + { + if (SameDomainName(&sdom->name, &localdomain)) break; + else err = mStatus_NoError; // suppress errors for non-local "default" domains + } + } + } + + return(err); +} + +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + +mDNSlocal void _return_browse_request_error(request_state *request, mStatus error) +{ + reply_state *rep; + + GenerateBrowseReply(NULL, 0, request, &rep, browse_reply_op, 0, error); + + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceBrowse _return_browse_request_error: error (%d)", request->request_id, error); + + append_reply(request, rep); +} + +mDNSlocal mStatus _handle_browse_request_with_trust(request_state *request, const char * domain) +{ + mStatus err; + if (audit_token_to_pid(request->audit_token) == 0) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING, "[R%u] _handle_browse_request_with_trust: no audit token for pid(%s %d)", request->request_id, request->pid_name, request->process_id); + err = _handle_browse_request_start(request, domain); + } + else + { + char typestr[MAX_ESCAPED_DOMAIN_NAME]; + typestr[0] = 0; + (void)ConvertDomainNameToCString(&request->u.browser.regtype, typestr); + mdns_trust_flags_t flags = mdns_trust_flags_none; + mdns_trust_status_t status = mdns_trust_check_bonjour(request->audit_token, typestr, &flags); + switch (status) + { + case mdns_trust_status_denied: + case mdns_trust_status_pending: + { + mdns_trust_t trust = mdns_trust_create(request->audit_token, typestr, flags); + if (!trust ) + { + err = mStatus_NoMemoryErr; + goto exit; + } + + size_t len = strlen(domain) + 1; + void * context = mallocL("context/_handle_browse_request_with_trust", len); + if (!context) + { + my_perror("ERROR: mallocL context/_handle_browse_request_with_trust"); + mdns_release(trust); + err = mStatus_NoMemoryErr; + goto exit; + } + memcpy(context, domain, len); + mdns_trust_set_context(trust, context); + + mdns_trust_set_queue(trust, _get_trust_results_dispatch_queue()); + mdns_trust_set_event_handler(trust, ^(mdns_trust_event_t event, mdns_trust_status_t update) + { + if (event == mdns_trust_event_result) + { + mStatus error = (update != mdns_trust_status_granted) ? mStatus_PolicyDenied : mStatus_NoError; + KQueueLock(); + const char * _domain = mdns_trust_get_context(trust); + if (_domain) + { + if (!error) + { + error = _handle_browse_request_start(request, _domain); + // No context means the request was canceled before we got here + } + if (error) // (not else if) Always check for error result + { + _return_browse_request_error(request, error); + } + } + KQueueUnlock("_handle_browse_request_with_trust"); + } + }); + request->trust = trust; + mdns_trust_activate(trust); + err = mStatus_NoError; + break; + } + + case mdns_trust_status_no_entitlement: + err = mStatus_NoAuth; + break; + + case mdns_trust_status_granted: + err = _handle_browse_request_start(request, domain); + break; + + default: + err = mStatus_UnknownErr; + break; + } + } +exit: + return err; +} +#endif // TRUST_ENFORCEMENT + mDNSlocal mStatus handle_browse_request(request_state *request) { + // Note that regtype may include a trailing subtype char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - domainname typedn, d, temp; + domainname typedn, temp; mDNSs32 NumSubTypes; - char *AnonData = mDNSNULL; mStatus err = mStatus_NoError; - int AnonDataLen; DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); @@ -2538,32 +3076,20 @@ mDNSlocal mStatus handle_browse_request(request_state *request) LogInfo("handle_browse_request: browse pending for interface index %d", interfaceIndex); } - if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr); + if (get_string(&request->msgptr, request->msgend, regtype, sizeof(regtype)) < 0 || + get_string(&request->msgptr, request->msgend, domain, sizeof(domain )) < 0) return(mStatus_BadParamErr); if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } request->flags = flags; request->interfaceIndex = interfaceIndex; typedn.c[0] = 0; - NumSubTypes = ChopSubTypes(regtype, &AnonData); // Note: Modifies regtype string to remove trailing subtypes + NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr); - AnonDataLen = 0; - if (AnonData) - { - AnonDataLen = strlen(AnonData); - if (AnonDataLen > MAX_ANONYMOUS_DATA) - { - LogMsg("handle_browse_request: AnonDataLen %d", AnonDataLen); - return(mStatus_BadParamErr); - } - // Account for the null byte - AnonDataLen += 1; - } if (NumSubTypes == 1) { - if (!AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1 + AnonDataLen)) + if (!AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr); } @@ -2580,49 +3106,39 @@ mDNSlocal mStatus handle_browse_request(request_state *request) request->u.browser.default_domain = !domain[0]; request->u.browser.browsers = NULL; - LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)", - request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%d] DNSServiceBrowse(%X, %d, \"" PRI_DM_NAME "\", \"" PRI_S "\") START PID[%d](" PUB_S ")", + request->request_id, request->flags, interfaceIndex, DM_NAME_PARAM(&request->u.browser.regtype), domain, + request->process_id, request->pid_name); if (request->u.browser.default_domain) { // Start the domain enumeration queries to discover the WAB browse domains - LogInfo("%3d: DNSServiceBrowse Start WAB PID[%d](%s)", request->sd, request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceBrowse Start WAB PID[%d](" PUB_S ")", + request->request_id, request->process_id, request->pid_name); uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY); } - request->u.browser.AnonData = mDNSNULL; - if (AnonData) - { - int len = strlen(AnonData) + 1; - request->u.browser.AnonData = mallocL("Anonymous", len); - if (!request->u.browser.AnonData) - return mStatus_NoMemoryErr; - else - mDNSPlatformMemCopy((void *)request->u.browser.AnonData, AnonData, len); - } // We need to unconditionally set request->terminate, because even if we didn't successfully // start any browses right now, subsequent configuration changes may cause successful // browses to be added, and we'll need to cancel them before freeing this memory. - request->terminate = browse_termination_callback; + request->terminate = NULL; - if (domain[0]) +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + domainname d; + if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr); + + if (os_feature_enabled(mDNSResponder, bonjour_privacy) && + (request->u.browser.default_domain || IsLocalDomain(&d) || request->u.browser.ForceMCast)) { - if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr); - err = add_domain_to_browser(request, &d); + err = _handle_browse_request_with_trust(request, domain); } else { - DNameListElem *sdom; - for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next) - if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid) - { - err = add_domain_to_browser(request, &sdom->name); - if (err) - { - if (SameDomainName(&sdom->name, &localdomain)) break; - else err = mStatus_NoError; // suppress errors for non-local "default" domains - } - } + err = _handle_browse_request_start(request, domain); } +#else + err = _handle_browse_request_start(request, domain); +#endif return(err); } @@ -2633,6 +3149,61 @@ mDNSlocal mStatus handle_browse_request(request_state *request) #pragma mark - DNSServiceResolve #endif +mDNSlocal void resolve_termination_callback(request_state *request) +{ + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceResolve(%X, %d, \"" PRI_DM_NAME "\") STOP PID[%d](" PUB_S ")", + request->request_id, request->flags, request->interfaceIndex, DM_NAME_PARAM(&request->u.resolve.qtxt.qname), + request->process_id, request->pid_name); + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + LogMcastQ(&request->u.resolve.qsrv, request, q_stop); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + if (request->u.resolve.external_advertise) + { + external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags, request->process_id); + } +#endif +} + +typedef struct { + char regtype[MAX_ESCAPED_DOMAIN_NAME]; + domainname fqdn; + mDNSInterfaceID InterfaceID; +} _resolve_start_params_t; + +mDNSlocal mStatus _handle_resolve_request_start(request_state *request, const _resolve_start_params_t * const params) +{ + mStatus err; + + err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv); + + if (!err) + { + err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); + if (err) + { + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + } + else + { + request->terminate = resolve_termination_callback; + LogMcastQ(&request->u.resolve.qsrv, request, q_start); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) + if (callExternalHelpers(params->InterfaceID, ¶ms->fqdn, request->flags)) + { + request->u.resolve.external_advertise = mDNStrue; + LogInfo("handle_resolve_request: calling external_start_resolving_service()"); + external_start_resolving_service(params->InterfaceID, ¶ms->fqdn, request->flags, request->process_id); + } +#else + (void)params; +#endif + } + } + return err; +} + mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) { size_t len = 0; @@ -2690,31 +3261,139 @@ mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, con put_uint16(req->u.resolve.txt->rdlength, &data); put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data); - LogOperation("%3d: DNSServiceResolve(%s) RESULT %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%d->Q%d] DNSServiceResolve(" PRI_S ") RESULT " PRI_S ":%d", + req->request_id, mDNSVal16(question->TargetQID), fullname, target, + mDNSVal16(req->u.resolve.srv->rdata->u.srv.port)); append_reply(req, rep); } -mDNSlocal void resolve_termination_callback(request_state *request) +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + +mDNSlocal void _return_resolve_request_error(request_state * request, mStatus error) { - LogOperation("%3d: DNSServiceResolve(%X, %d, \"%##s\") STOP PID[%d](%s)", - request->sd, request->flags, request->interfaceIndex, request->u.resolve.qtxt.qname.c, request->process_id, request->pid_name); - mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); - mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); - LogMcastQ(&request->u.resolve.qsrv, request, q_stop); - if (request->u.resolve.external_advertise) - external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags); + size_t len; + char * emptystr = "\0"; + char * data; + reply_state *rep; + + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] DNSServiceResolve _return_resolve_request_error: error(%d)", request->request_id, error); + + // calculate reply length + len = sizeof(DNSServiceFlags); + len += sizeof(mDNSu32); // interface index + len += sizeof(DNSServiceErrorType); + len += 2; // name, target + len += 2 * sizeof(mDNSu16); // port, txtLen + len += 0; //req->u.resolve.txt->rdlength; + + rep = create_reply(resolve_reply_op, len, request); + + rep->rhdr->flags = 0; + rep->rhdr->ifi = 0; + rep->rhdr->error = dnssd_htonl(error); + + data = (char *)&rep->rhdr[1]; + + // write reply data to message + put_string(emptystr, &data); // name + put_string(emptystr, &data); // target + put_uint16(0, &data); // port + put_uint16(0, &data); // txtLen + + append_reply(request, rep); +} + +mDNSlocal mStatus _handle_resolve_request_with_trust(request_state *request, const _resolve_start_params_t * const params) +{ + mStatus err; + if (audit_token_to_pid(request->audit_token) == 0) + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING, "[R%u] _handle_resolve_request_with_trust: no audit token for pid(%s %d)", request->request_id, request->pid_name, request->process_id); + err = _handle_resolve_request_start(request, params); + } + else + { + mdns_trust_flags_t flags = mdns_trust_flags_none; + mdns_trust_status_t status = mdns_trust_check_bonjour(request->audit_token, params->regtype, &flags); + switch (status) + { + case mdns_trust_status_denied: + case mdns_trust_status_pending: + { + mdns_trust_t trust = mdns_trust_create(request->audit_token, params->regtype, flags); + if (!trust ) + { + err = mStatus_NoMemoryErr; + goto exit; + } + + void * context = mallocL("context/_handle_resolve_request_with_trust", sizeof(_resolve_start_params_t)); + if (!context) + { + my_perror("ERROR: mallocL context/_handle_resolve_request_with_trust"); + mdns_release(trust); + err = mStatus_NoMemoryErr; + goto exit; + } + memcpy(context, params, sizeof(_resolve_start_params_t)); + mdns_trust_set_context(trust, context); + mdns_trust_set_queue(trust, _get_trust_results_dispatch_queue()); + mdns_trust_set_event_handler(trust, ^(mdns_trust_event_t event, mdns_trust_status_t update) + { + if (event == mdns_trust_event_result) + { + mStatus error = (update != mdns_trust_status_granted) ? mStatus_PolicyDenied : mStatus_NoError; + KQueueLock(); + _resolve_start_params_t * _params = mdns_trust_get_context(trust); + if (_params) + { + if (!error) + { + error = _handle_resolve_request_start(request, _params); + // No context means the request was canceled before we got here + } + if (error) // (not else if) Always check for error result + { + _return_resolve_request_error(request, error); + } + } + KQueueUnlock("_handle_resolve_request_with_trust"); + } + }); + request->trust = trust; + mdns_trust_activate(trust); + err = mStatus_NoError; + break; + } + + case mdns_trust_status_no_entitlement: + err = mStatus_NoAuth; + break; + + case mdns_trust_status_granted: + err = _handle_resolve_request_start(request, params); + break; + + default: + err = mStatus_UnknownErr; + break; + } + } +exit: + return err; } +#endif // TRUST_ENFORCEMENT mDNSlocal mStatus handle_resolve_request(request_state *request) { - char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - domainname fqdn; + char name[256], domain[MAX_ESCAPED_DOMAIN_NAME]; + _resolve_start_params_t params; mStatus err; // extract the data from the message DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID; // Map kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P // flag set so that the resolve will run over P2P interfaces that are not yet created. @@ -2725,11 +3404,11 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) interfaceIndex = kDNSServiceInterfaceIndexAny; } - InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + params.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); // The operation is scoped to a specific interface index, but the // interface is not currently in our list. - if (interfaceIndex && !InterfaceID) + if (interfaceIndex && !params.InterfaceID) { // If it's one of the specially defined inteface index values, just return an error. if (PreDefinedInterfaceIndex(interfaceIndex)) @@ -2740,19 +3419,19 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) // Otherwise, use the specified interface index value and the operation will // be applied to that interface when it comes up. - InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; + params.InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; LogInfo("handle_resolve_request: resolve pending for interface index %d", interfaceIndex); } - if (get_string(&request->msgptr, request->msgend, name, 256) < 0 || - get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) + if (get_string(&request->msgptr, request->msgend, name, sizeof(name )) < 0 || + get_string(&request->msgptr, request->msgend, params.regtype, sizeof(params.regtype)) < 0 || + get_string(&request->msgptr, request->msgend, domain, sizeof(domain )) < 0) { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0) - { LogMsg("ERROR: handle_resolve_request bad “%s” “%s” “%s”", name, regtype, domain); return(mStatus_BadParamErr); } + if (build_domainname_from_strings(¶ms.fqdn, name, params.regtype, domain) < 0) + { LogMsg("ERROR: handle_resolve_request bad “%s” “%s” “%s”", name, params.regtype, domain); return(mStatus_BadParamErr); } mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve)); @@ -2769,10 +3448,9 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->interfaceIndex = interfaceIndex; // format questions - request->u.resolve.qsrv.InterfaceID = InterfaceID; + request->u.resolve.qsrv.InterfaceID = params.InterfaceID; request->u.resolve.qsrv.flags = flags; - request->u.resolve.qsrv.Target = zeroAddr; - AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn); + AssignDomainName(&request->u.resolve.qsrv.qname, ¶ms.fqdn); request->u.resolve.qsrv.qtype = kDNSType_SRV; request->u.resolve.qsrv.qclass = kDNSClass_IN; request->u.resolve.qsrv.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; @@ -2780,26 +3458,19 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; request->u.resolve.qsrv.SuppressUnusable = mDNSfalse; - request->u.resolve.qsrv.SearchListIndex = 0; request->u.resolve.qsrv.AppendSearchDomains = 0; - request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse; request->u.resolve.qsrv.TimeoutQuestion = 0; request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0; - request->u.resolve.qsrv.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - request->u.resolve.qsrv.ValidationRequired = 0; - request->u.resolve.qsrv.ValidatingResponse = 0; + request->u.resolve.qsrv.UseBackgroundTraffic = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; request->u.resolve.qsrv.ProxyQuestion = 0; - request->u.resolve.qsrv.qnameOrig = mDNSNULL; - request->u.resolve.qsrv.AnonInfo = mDNSNULL; request->u.resolve.qsrv.pid = request->process_id; request->u.resolve.qsrv.euid = request->uid; request->u.resolve.qsrv.QuestionCallback = resolve_result_callback; request->u.resolve.qsrv.QuestionContext = request; - request->u.resolve.qtxt.InterfaceID = InterfaceID; + request->u.resolve.qtxt.InterfaceID = params.InterfaceID; request->u.resolve.qtxt.flags = flags; - request->u.resolve.qtxt.Target = zeroAddr; - AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn); + AssignDomainName(&request->u.resolve.qtxt.qname, ¶ms.fqdn); request->u.resolve.qtxt.qtype = kDNSType_TXT; request->u.resolve.qtxt.qclass = kDNSClass_IN; request->u.resolve.qtxt.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; @@ -2807,17 +3478,11 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; request->u.resolve.qtxt.SuppressUnusable = mDNSfalse; - request->u.resolve.qtxt.SearchListIndex = 0; request->u.resolve.qtxt.AppendSearchDomains = 0; - request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse; request->u.resolve.qtxt.TimeoutQuestion = 0; request->u.resolve.qtxt.WakeOnResolve = 0; - request->u.resolve.qtxt.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - request->u.resolve.qtxt.ValidationRequired = 0; - request->u.resolve.qtxt.ValidatingResponse = 0; + request->u.resolve.qtxt.UseBackgroundTraffic = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; request->u.resolve.qtxt.ProxyQuestion = 0; - request->u.resolve.qtxt.qnameOrig = mDNSNULL; - request->u.resolve.qtxt.AnonInfo = mDNSNULL; request->u.resolve.qtxt.pid = request->process_id; request->u.resolve.qtxt.euid = request->uid; request->u.resolve.qtxt.QuestionCallback = resolve_result_callback; @@ -2832,30 +3497,28 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) #endif // ask the questions - LogOperation("%3d: DNSServiceResolve(%X, %d, \"%##s\") START PID[%d](%s)", request->sd, flags, interfaceIndex, - request->u.resolve.qsrv.qname.c, request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceResolve(%X, %d, \"" PRI_DM_NAME "\") START PID[%d](" PUB_S ")", + request->request_id, flags, interfaceIndex, DM_NAME_PARAM(&request->u.resolve.qsrv.qname), + request->process_id, request->pid_name); - err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv); + request->terminate = NULL; +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + domainname d; + if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr); - if (!err) + if (os_feature_enabled(mDNSResponder, bonjour_privacy) && + (IsLocalDomain(&d) || request->u.resolve.qsrv.ForceMCast)) { - err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); - if (err) - { - mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); - } - else - { - request->terminate = resolve_termination_callback; - LogMcastQ(&request->u.resolve.qsrv, request, q_start); - if (callExternalHelpers(InterfaceID, &fqdn, flags)) - { - request->u.resolve.external_advertise = mDNStrue; - LogInfo("handle_resolve_request: calling external_start_resolving_service()"); - external_start_resolving_service(InterfaceID, &fqdn, flags); - } - } + err = _handle_resolve_request_with_trust(request, ¶ms); + } + else + { + err = _handle_resolve_request_start(request, ¶ms); } +#else + err = _handle_resolve_request_start(request, ¶ms); +#endif return(err); } @@ -2866,328 +3529,55 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) #pragma mark - DNSServiceQueryRecord #endif -// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses -// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback -// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts -// the mDNSCore operation if the client dies or closes its socket. - -// Returns -1 to tell the caller that it should not try to reissue the query anymore -// Returns 1 on successfully appending a search domain and the caller should reissue the new query -// Returns 0 when there are no more search domains and the caller should reissue the query -mDNSlocal int AppendNewSearchDomain(DNSQuestion *question) +mDNSlocal void queryrecord_result_reply(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord, DNSServiceErrorType error, void *context) { - domainname *sd; - mStatus err; - - // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all - // the domains and should try the single label query directly on the wire. - if (question->SearchListIndex == -1) - { - LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype)); - return -1; - } - - if (!question->AppendSearchDomains) - { - LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype)); - return -1; - } - - // Save the original name, before we modify them below. - if (!question->qnameOrig) - { - question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname)); - if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; } - question->qnameOrig->c[0] = 0; - AssignDomainName(question->qnameOrig, &question->qname); - LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c); - } - - sd = uDNS_GetNextSearchDomain(question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains); - // We use -1 to indicate that we have searched all the domains and should try the single label - // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value - if (question->SearchListIndex == -1) - { - LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1"); - return -1; - } - - // Not a common case. Perhaps, we should try the next search domain if it exceeds ? - if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME) - { - LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd)); - return -1; - } - - // if there are no more search domains and we have already tried this question - // without appending search domains, then we are done. - if (!sd && !ApplySearchDomainsFirst(question)) - { - LogInfo("AppendNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype)); - return -1; - } - - // Stop the question before changing the name as negative cache entries could be pointing at this question. - // Even if we don't change the question in the case of returning 0, the caller is going to restart the - // question. - err = mDNS_StopQuery(&mDNSStorage, question); - if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); } - - AssignDomainName(&question->qname, question->qnameOrig); - if (sd) - { - AppendDomainName(&question->qname, sd); - LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex); - return 1; - } - - // Try the question as single label - LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype)); - return 0; -} - -#if APPLE_OSX_mDNSResponder + char name[MAX_ESCAPED_DOMAIN_NAME]; + size_t len; + DNSServiceFlags flags = 0; + reply_state *rep; + char *data; + request_state *req = (request_state *)context; + const char *dnssec_result_description = ""; -mDNSlocal mDNSBool DomainInSearchList(const domainname *domain, mDNSBool excludeLocal) -{ - const SearchListElem *s; - int qcount, scount; + ConvertDomainNameToCString(answer->name, name); - qcount = CountLabels(domain); - for (s=SearchList; s; s=s->next) - { - if (excludeLocal && SameDomainName(&s->domain, &localdomain)) - continue; - scount = CountLabels(&s->domain); - if (qcount >= scount) +#if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) + if (question->DNSSECStatus.enable_dnssec) { + if (answer->dnssec_result == dnssec_secure) { - // Note: When qcount == scount, we do a complete match of the domain - // which is expected by the callers. - const domainname *d = SkipLeadingLabels(domain, (qcount - scount)); - if (SameDomainName(&s->domain, d)) - { - return mDNStrue; - } + flags |= kDNSServiceFlagsSecure; + dnssec_result_description = ", DNSSEC_Secure"; } - } - return mDNSfalse; -} - -// The caller already checks that this is a dotlocal question. -mDNSlocal mDNSBool ShouldDeliverNegativeResponse(DNSQuestion *question) -{ - mDNSu16 qtype; - - // If the question matches the search domain exactly or the search domain is a - // subdomain of the question, it is most likely a valid unicast domain and hence - // don't suppress negative responses. - // - // If the user has configured ".local" as a search domain, we don't want - // to deliver a negative response for names ending in ".local" as that would - // prevent bonjour discovery. Passing mDNStrue for the last argument excludes - // ".local" search domains. - if (DomainInSearchList(&question->qname, mDNStrue)) - { - LogOperation("ShouldDeliverNegativeResponse: Question %##s (%s) in SearchList", question->qname.c, DNSTypeName(question->qtype)); - return mDNStrue; - } - - // Deliver negative response for A/AAAA if there was a positive response for AAAA/A respectively. - if (question->qtype != kDNSType_A && question->qtype != kDNSType_AAAA) - { - LogOperation("ShouldDeliverNegativeResponse: Question %##s (%s) not answering local question with negative unicast response", - question->qname.c, DNSTypeName(question->qtype)); - return mDNSfalse; - } - qtype = (question->qtype == kDNSType_A ? kDNSType_AAAA : kDNSType_A); - if (!mDNS_CheckForCacheRecord(&mDNSStorage, question, qtype)) - { - LogOperation("ShouldDeliverNegativeResponse:Question %##s (%s) not answering local question with negative unicast response" - " (can't find positive record)", question->qname.c, DNSTypeName(question->qtype)); - return mDNSfalse; - } - LogOperation("ShouldDeliverNegativeResponse:Question %##s (%s) answering local with negative unicast response (found positive record)", - question->qname.c, DNSTypeName(question->qtype)); - return mDNStrue; -} - -// Workaround for networks using Microsoft Active Directory using "local" as a private internal -// top-level domain -mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err) -{ -#ifndef UNICAST_DISABLED - extern domainname ActiveDirectoryPrimaryDomain; - DNSQuestion **question2; - #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp")) - #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname))) - - question2 = mDNSNULL; - if (request->hdr.op == query_request) - question2 = &request->u.queryrecord.q2; - else if (request->hdr.op == addrinfo_request) - { - if (q->qtype == kDNSType_A) - question2 = &request->u.addrinfo.q42; - else if (q->qtype == kDNSType_AAAA) - question2 = &request->u.addrinfo.q62; - } - if (!question2) - { - LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - return mStatus_BadParamErr; - } - - // Sanity check: If we already sent an additonal query, we don't need to send one more. - // - // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function - // is called to see whether a unicast query should be sent or not. - // - // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it - // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to - // send the additional query. - // - // Thus, it should not be called more than once. - if (*question2) - { - LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype)); - return err; - } - - if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain)) - if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q)) + else if (answer->dnssec_result == dnssec_insecure) { - DNSQuestion *q2; - int labels = CountLabels(&q->qname); - q2 = mallocL("DNSQuestion", sizeof(DNSQuestion)); - if (!q2) FatalError("ERROR: SendAdditionalQuery malloc"); - *question2 = q2; - *q2 = *q; - q2->InterfaceID = mDNSInterface_Unicast; - q2->ExpectUnique = mDNStrue; - // Always set the QuestionContext to indicate that this question should be stopped - // before freeing. Don't rely on "q". - q2->QuestionContext = request; - // If the query starts as a single label e.g., somehost, and we have search domains with .local, - // queryrecord_result_callback calls this function when .local is appended to "somehost". - // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at - // "somehost". We need to copy that information so that when we retry with a different search - // domain e.g., mycompany.local, we get "somehost.mycompany.local". - if (q->qnameOrig) - { - (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig)); - if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; } - (*question2)->qnameOrig->c[0] = 0; - AssignDomainName((*question2)->qnameOrig, q->qnameOrig); - LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c); - } - // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel. - // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query. - // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP - // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser) - // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the - // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries - // for names in the "local" domain will be safely answered privately before they hit the root name servers. - // Note that in the "my-small-company.local" example above there will typically be an SOA record for - // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case. - // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either - // of those, we don't want do the SOA check for the local - if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname, mDNSfalse)) - { - AssignDomainName(&q2->qname, &localdomain); - q2->qtype = kDNSType_SOA; - q2->LongLived = mDNSfalse; - q2->ForceMCast = mDNSfalse; - q2->ReturnIntermed = mDNStrue; - // Don't append search domains for the .local SOA query - q2->AppendSearchDomains = 0; - q2->AppendLocalSearchDomains = 0; - q2->RetryWithSearchDomains = mDNSfalse; - q2->SearchListIndex = 0; - q2->TimeoutQuestion = 0; - q2->AnonInfo = mDNSNULL; - q2->pid = request->process_id; - q2->euid = request->uid; - } - LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); - err = mDNS_StartQuery(&mDNSStorage, q2); - if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err); + flags |= kDNSServiceFlagsInsecure; + dnssec_result_description = ", DNSSEC_Insecure"; } - return(err); -#else // !UNICAST_DISABLED - (void) q; - (void) request; - (void) err; - - return mStatus_NoError; -#endif // !UNICAST_DISABLED -} -#endif // APPLE_OSX_mDNSResponder - -// This function tries to append a search domain if valid and possible. If so, returns true. -mDNSlocal mDNSBool RetryQuestionWithSearchDomains(DNSQuestion *question, request_state *req, QC_result AddRecord) -{ - int result; - // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no - // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so - // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch - // RetryWithSearchDomains which may or may not be set. - // - // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and - // is a valid question for appending search domains, retry by appending domains - - if ((AddRecord != QC_suppressed) && question->SearchListIndex != -1 && question->AppendSearchDomains) - { - question->RetryWithSearchDomains = 0; - result = AppendNewSearchDomain(question); - // As long as the result is either zero or 1, we retry the question. If we exahaust the search - // domains (result is zero) we try the original query (as it was before appending the search - // domains) as such on the wire as a last resort if we have not tried them before. For queries - // with more than one label, we have already tried them before appending search domains and - // hence don't retry again - if (result != -1) + else if (answer->dnssec_result == dnssec_bogus) { - mStatus err; - err = mDNS_StartQuery(&mDNSStorage, question); - if (!err) - { - LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype)); - // If the result was zero, it meant that there are no search domains and we just retried the question - // as a single label and we should not retry with search domains anymore. - if (!result) question->SearchListIndex = -1; - return mDNStrue; - } - else - { - LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); - // We have already stopped the query and could not restart. Reset the appropriate pointers - // so that we don't call stop again when the question terminates - question->QuestionContext = mDNSNULL; - } + flags |= kDNSServiceFlagsBogus; + dnssec_result_description = ", DNSSEC_Bogus"; } + else if (answer->dnssec_result == dnssec_indeterminate) + { + flags |= kDNSServiceFlagsIndeterminate; + dnssec_result_description = ", DNSSEC_Indeterminate"; + } + } else if (question->DNSSECStatus.tried_dnssec_but_unsigned) { + // handle the case where we restart the question without the DNSSEC while the user requires DNSSEC result, for + // some reason we failed to get DNSSEC records. In which case, even if we go back to normal query, we should pass + // the DNSSEC result + flags |= kDNSServiceFlagsInsecure; + dnssec_result_description = ", DNSSEC_Insecure"; } - else - { - LogDebug("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, AddRecord, question->SearchListIndex, question->AppendSearchDomains); - } - return mDNSfalse; -} +#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) -mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord, - DNSServiceErrorType error) -{ - char name[MAX_ESCAPED_DOMAIN_NAME]; - size_t len; - DNSServiceFlags flags = 0; - reply_state *rep; - char *data; - - ConvertDomainNameToCString(answer->name, name); - - LogOperation("%3d: %s(%##s, %s) RESULT %s interface %d: (%s)%s", req->sd, - req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo", - question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", - mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), - MortalityDisplayString(answer->mortality), RRDisplayString(m, answer)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u->Q%u] DNSService" PUB_S "(" PRI_DM_NAME ", " PUB_S ") RESULT " PUB_S " interface %d: (" PUB_S PUB_S ")" PRI_S, + req->request_id, mDNSVal16(question->TargetQID), req->hdr.op == query_request ? "QueryRecord" : "GetAddrInfo", + DM_NAME_PARAM(&question->qname), DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", + mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), + MortalityDisplayString(answer->mortality), dnssec_result_description, RRDisplayString(m, answer)); len = sizeof(DNSServiceFlags); // calculate reply data length len += sizeof(mDNSu32); // interface index @@ -3203,30 +3593,8 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu flags |= kDNSServiceFlagsAdd; if (answer->mortality == Mortality_Ghost) flags |= kDNSServiceFlagsExpiredAnswer; - if (question->ValidationStatus != 0) - { - error = kDNSServiceErr_NoError; - if (question->ValidationRequired && question->ValidationState == DNSSECValDone) - { - switch (question->ValidationStatus) //Set the dnssec flags to be passed on to the Apps here - { - case DNSSEC_Secure: - flags |= kDNSServiceFlagsSecure; - break; - case DNSSEC_Insecure: - flags |= kDNSServiceFlagsInsecure; - break; - case DNSSEC_Indeterminate: - flags |= kDNSServiceFlagsIndeterminate; - break; - case DNSSEC_Bogus: - flags |= kDNSServiceFlagsBogus; - break; - default: - LogMsg("queryrecord_result_reply unknown status %d for %##s", question->ValidationStatus, question->qname.c); - } - } - } + if (!question->InitialCacheMiss) + flags |= kDNSServiceFlagAnsweredFromCache; rep->rhdr->flags = dnssd_htonl(flags); // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the @@ -3254,507 +3622,280 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu put_uint32(AddRecord ? answer->rroriginalttl : 0, &data); append_reply(req, rep); - // Stop the question, if we just timed out - if (error == kDNSServiceErr_Timeout) - { - mDNS_StopQuery(m, question); - // Reset the pointers so that we don't call stop on termination - question->QuestionContext = mDNSNULL; - } - else if ((AddRecord == QC_add) && req->hdr.op == addrinfo_request) - { - // Note: We count all answers including LocalOnly e.g., /etc/hosts. If we - // exclude that, v4ans/v6ans will be zero and we would wrongly think that - // we did not answer questions and setup the status to deliver triggers. - if (question->qtype == kDNSType_A) - req->u.addrinfo.v4ans = 1; - if (question->qtype == kDNSType_AAAA) - req->u.addrinfo.v6ans = 1; - } - else if ((AddRecord == QC_add) && req->hdr.op == query_request) +} + +mDNSlocal void queryrecord_termination_callback(request_state *request) +{ + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] DNSServiceQueryRecord(%X, %d, " PRI_DM_NAME ", " PUB_S ") STOP PID[%d](" PUB_S ")", + request->request_id, request->flags, request->interfaceIndex, + DM_NAME_PARAM(QueryRecordClientRequestGetQName(&request->u.queryrecord)), + DNSTypeName(QueryRecordClientRequestGetType(&request->u.queryrecord)), request->process_id, request->pid_name); + + QueryRecordClientRequestStop(&request->u.queryrecord); +} + +typedef struct { + char qname[MAX_ESCAPED_DOMAIN_NAME]; + mDNSu32 interfaceIndex; + DNSServiceFlags flags; + mDNSu16 qtype; + mDNSu16 qclass; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool require_privacy; +#endif +} _queryrecord_start_params_t; + +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) && MDNSRESPONDER_SUPPORTS(APPLE, IPC_TLV) +mDNSlocal const mDNSu8 * ipc_tlv_get_resolver_config_plist_data(const mDNSu8 *const start, const mDNSu8 *const end, + size_t *outLen) +{ + size_t len = 0; + const mDNSu8 *value = NULL; + mdns_tlv16_get_value(start, end, IPC_TLV_TYPE_RESOLVER_CONFIG_PLIST_DATA, &len, &value, NULL); + if (outLen) { - if (question->qtype == kDNSType_A || question->qtype == kDNSType_AAAA) - req->u.queryrecord.ans = 1; + *outLen = len; } + return value; +} -#if APPLE_OSX_mDNSResponder -#if !NO_WCF - CHECK_WCF_FUNCTION(WCFIsServerRunning) - { - struct xucred x; - socklen_t xucredlen = sizeof(x); +mDNSlocal mDNSBool ipc_tlv_get_require_privacy(const mDNSu8 *const start, const mDNSu8 *const end) +{ + size_t len = 0; + const mDNSu8 *value = NULL; + mdns_tlv16_get_value(start, end, IPC_TLV_TYPE_REQUIRE_PRIVACY, &len, &value, NULL); + return ((len == 1) && (*value != 0)) ? mDNStrue : mDNSfalse; +} +#endif - if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0) - { - if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && - (x.cr_version == XUCRED_VERSION)) - { - struct sockaddr_storage addr; - addr.ss_len = 0; - if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) - { - if (answer->rrtype == kDNSType_A) - { - struct sockaddr_in *const sin = (struct sockaddr_in *)&addr; - sin->sin_port = 0; - // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: - // sin->sin_addr.s_addr = answer->rdata->u.ipv4.NotAnInteger; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(mDNSv4Addr)), answer)) - LogMsg("queryrecord_result_reply: WCF AF_INET putRData failed"); - else - { - addr.ss_len = sizeof (struct sockaddr_in); - addr.ss_family = AF_INET; - } - } - else if (answer->rrtype == kDNSType_AAAA) - { - struct sockaddr_in6 *const sin6 = (struct sockaddr_in6 *)&addr; - sin6->sin6_port = 0; - // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: - // sin6->sin6_addr.__u6_addr.__u6_addr32[0] = answer->rdata->u.ipv6.l[0]; - // sin6->sin6_addr.__u6_addr.__u6_addr32[1] = answer->rdata->u.ipv6.l[1]; - // sin6->sin6_addr.__u6_addr.__u6_addr32[2] = answer->rdata->u.ipv6.l[2]; - // sin6->sin6_addr.__u6_addr.__u6_addr32[3] = answer->rdata->u.ipv6.l[3]; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(mDNSv6Addr)), answer)) - LogMsg("queryrecord_result_reply: WCF AF_INET6 putRData failed"); - else - { - addr.ss_len = sizeof (struct sockaddr_in6); - addr.ss_family = AF_INET6; - } - } - if (addr.ss_len) - { - debugf("queryrecord_result_reply: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len); - CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) - { - WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid); - } - } - } - else if (answer->rrtype == kDNSType_CNAME) - { - domainname cname; - char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; - if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer)) - LogMsg("queryrecord_result_reply: WCF CNAME putRData failed"); - else - { - ConvertDomainNameToCString(&cname, cname_cstr); - CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) - { - WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid); - } - } - } - } - else my_perror("queryrecord_result_reply: ERROR: getsockopt LOCAL_PEERCRED"); - } - } +mDNSlocal mStatus _handle_queryrecord_request_start(request_state *request, const _queryrecord_start_params_t * const params) +{ + mStatus err; + + request->terminate = queryrecord_termination_callback; + + QueryRecordClientRequestParams queryParams; + QueryRecordClientRequestParamsInit(&queryParams); + queryParams.requestID = request->request_id; + queryParams.qnameStr = params->qname; + queryParams.interfaceIndex = params->interfaceIndex; + queryParams.flags = params->flags; + queryParams.qtype = params->qtype; + queryParams.qclass = params->qclass; + queryParams.effectivePID = request->validUUID ? 0 : request->process_id; + queryParams.effectiveUUID = request->validUUID ? request->uuid : mDNSNULL; + queryParams.peerUID = request->uid; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + queryParams.needEncryption = params->require_privacy ? mDNStrue : mDNSfalse; + queryParams.customID = request->custom_service_id; #endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + queryParams.peerAuditToken = &request->audit_token; #endif + err = QueryRecordClientRequestStart(&request->u.queryrecord, &queryParams, queryrecord_result_reply, request); + return err; } -mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + +mDNSlocal void _return_queryrecord_request_error(request_state * request, mStatus error) { - request_state *req = question->QuestionContext; - DNSServiceErrorType error = kDNSServiceErr_NoError; - DNSQuestion *q = mDNSNULL; + size_t len; + char * emptystr = "\0"; + char * data; + reply_state *rep; -#if APPLE_OSX_mDNSResponder - { - // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not - // get any callbacks from the core after this. - if (!req) - { - LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - return; - } - if (req->hdr.op == query_request && question == req->u.queryrecord.q2) - q = &req->u.queryrecord.q; - else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42) - q = &req->u.addrinfo.q4; - else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62) - q = &req->u.addrinfo.q6; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] DNSService" PUB_S " _return_queryrecord_request_error: error(%d)", + request->request_id, request->hdr.op == query_request ? "QueryRecord" : "GetAddrInfo", error); - if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname)) - { - mStatus err; - domainname *orig = question->qnameOrig; - - LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c); - mDNS_StopQuery(m, question); - question->QuestionContext = mDNSNULL; - - // We got a negative response for the SOA record indicating that .local does not exist. - // But we might have other search domains (that does not end in .local) that can be - // appended to this question. In that case, we want to retry the question. Otherwise, - // we don't want to try this question as unicast. - if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains) - { - LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c); - return; - } + len = sizeof(DNSServiceFlags); // calculate reply data length + len += sizeof(mDNSu32); // interface index + len += sizeof(DNSServiceErrorType); + len += strlen(emptystr) + 1; + len += 3 * sizeof(mDNSu16); // type, class, rdlen + len += 0;//answer->rdlength; + len += sizeof(mDNSu32); // TTL - // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query - // - // Note: When we copy the original question, we copy everything including the AppendSearchDomains, - // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is - // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in - // SendAdditionalQuery as to how qnameOrig gets initialized. - *question = *q; - question->InterfaceID = mDNSInterface_Unicast; - question->ExpectUnique = mDNStrue; - question->qnameOrig = orig; - - LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext); - - // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above. - // Hence, we need to set it explicitly here. - question->QuestionContext = req; - err = mDNS_StartQuery(m, question); - if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); - - // If we got a positive response to local SOA, then try the .local question as unicast - if (answer->RecordType != kDNSRecordTypePacketNegative) return; - - // Fall through and get the next search domain. The question is pointing at .local - // and we don't want to try that. Try the next search domain. Don't try with local - // search domains for the unicast question anymore. - // - // Note: we started the question above which will be stopped immediately (never sent on the wire) - // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the - // question has already started. - question->AppendLocalSearchDomains = 0; - } - - if (q && AddRecord && AddRecord != QC_dnssec && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength) - { - // If we get a negative response to the unicast query that we sent above, retry after appending search domains - // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here. - // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended. - // To keep things simple, we handle unicast ".local" separately here. - LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype)); - if (RetryQuestionWithSearchDomains(question, req, AddRecord)) - return; - if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname)) - { - // If "local" is the last search domain, we need to stop the question so that we don't send the "local" - // question on the wire as we got a negative response for the local SOA. But, we can't stop the question - // yet as we may have to timeout the question (done by the "core") for which we need to leave the question - // in the list. We leave it disabled so that it does not hit the wire. - LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - question->ThisQInterval = 0; - } - } - // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search - // domains to append for "q2". In all cases, fall through and deliver the response - } -#endif // APPLE_OSX_mDNSResponder + rep = create_reply(request->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, request); - // If a query is being suppressed for some reason, we don't have to do any other - // processing. - // - // Note: We don't check for "SuppressQuery" and instead use QC_suppressed because - // the "core" needs to temporarily turn off SuppressQuery to answer this query. - if (AddRecord == QC_suppressed) + rep->rhdr->flags = 0; + rep->rhdr->ifi = 0; + rep->rhdr->error = dnssd_htonl(error); + + data = (char *)&rep->rhdr[1]; + + put_string(emptystr, &data); + put_uint16(0, &data); + put_uint16(0, &data); + put_uint16(0, &data); + data += 0; + put_uint32(0, &data); + + append_reply(request, rep); +} + +mDNSlocal mStatus _handle_queryrecord_request_with_trust(request_state *request, const _queryrecord_start_params_t * const params) +{ + mStatus err; + if (audit_token_to_pid(request->audit_token) == 0) { - LogDebug("queryrecord_result_callback: Suppressed question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - queryrecord_result_reply(m, req, question, answer, AddRecord, kDNSServiceErr_NoSuchRecord); - return; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING, "[R%u] _handle_queryrecord_request_with_trust: no audit token for pid(%s %d)", request->request_id, request->pid_name, request->process_id); + err = _handle_queryrecord_request_start(request, params); } - - if (answer->RecordType == kDNSRecordTypePacketNegative) + else { - // If this question needs to be timed out and we have reached the stop time, mark - // the error as timeout. It is possible that we might get a negative response from an - // external DNS server at the same time when this question reaches its stop time. We - // can't tell the difference as there is no indication in the callback. This should - // be okay as we will be timing out this query anyway. - mDNS_Lock(m); - if (question->TimeoutQuestion) + const char *service_ptr = NULL; + char type_str[MAX_ESCAPED_DOMAIN_NAME] = ""; + domainname query_name; + if (MakeDomainNameFromDNSNameString(&query_name, params->qname)) { - if ((m->timenow - question->StopTime) >= 0) + domainlabel name; + domainname type, domain; + bool good = DeconstructServiceName(&query_name, &name, &type, &domain); + if (good) { - LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); - error = kDNSServiceErr_Timeout; + ConvertDomainNameToCString(&type, type_str); + service_ptr = type_str; } } - mDNS_Unlock(m); - // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft - // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative - // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory - // server is going to assert that pretty much every single multicast name doesn't exist. - // - // If we are timing out this query, we need to deliver the negative answer to the application - if (error != kDNSServiceErr_Timeout) + + mdns_trust_flags_t flags = mdns_trust_flags_none; + mdns_trust_status_t status = mdns_trust_check_query(request->audit_token, params->qname, service_ptr, params->qtype, (params->flags & kDNSServiceFlagsForceMulticast) != 0, &flags); + switch (status) { - if (!answer->InterfaceID && IsLocalDomain(answer->name)) + case mdns_trust_status_denied: + case mdns_trust_status_pending: { - // Sanity check: "q" will be set only if "question" is the .local unicast query. - if (!q) + mdns_trust_t trust = mdns_trust_create(request->audit_token, service_ptr, flags); + if (!trust ) { - LogMsg("queryrecord_result_callback: ERROR!! answering multicast question %s with unicast cache record", - RRDisplayString(m, answer)); - return; + err = mStatus_NoMemoryErr; + goto exit; } -#if APPLE_OSX_mDNSResponder - if (!ShouldDeliverNegativeResponse(question)) + + void * context = mallocL("context/_handle_queryrecord_request_with_trust", sizeof(_queryrecord_start_params_t)); + if (!context) { - return; + my_perror("ERROR: mallocL context/_handle_queryrecord_request_with_trust"); + mdns_release(trust); + err = mStatus_NoMemoryErr; + goto exit; } -#endif // APPLE_OSX_mDNSResponder - LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response", question->qname.c, - DNSTypeName(question->qtype)); - } - error = kDNSServiceErr_NoSuchRecord; - } - } - // If we get a negative answer, try appending search domains. Don't append search domains - // - if we are timing out this question - // - if the negative response was received as a result of a multicast query - // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below) - // - if this response is forced e.g., dnssec validation result - if (error != kDNSServiceErr_Timeout) - { - if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord && AddRecord != QC_dnssec) - { - // If the original question did not end in .local, we did not send an SOA query - // to figure out whether we should send an additional unicast query or not. If we just - // appended .local, we need to see if we need to send an additional query. This should - // normally happen just once because after we append .local, we ignore all negative - // responses for .local above. - LogDebug("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype)); - if (RetryQuestionWithSearchDomains(question, req, AddRecord)) - { - // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could - // be anywhere in the search domain list. -#if APPLE_OSX_mDNSResponder - mStatus err = mStatus_NoError; - err = SendAdditionalQuery(question, req, err); - if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains"); -#endif // APPLE_OSX_mDNSResponder - return; + memcpy(context, params, sizeof(_queryrecord_start_params_t)); + mdns_trust_set_context(trust, context); + mdns_trust_set_queue(trust, _get_trust_results_dispatch_queue()); + mdns_trust_set_event_handler(trust, ^(mdns_trust_event_t event, mdns_trust_status_t update) + { + if (event == mdns_trust_event_result) + { + mStatus error = (update != mdns_trust_status_granted) ? mStatus_PolicyDenied : mStatus_NoError; + KQueueLock(); + _queryrecord_start_params_t * _params = mdns_trust_get_context(trust); + if (_params) + { + if (!error) + { + error = _handle_queryrecord_request_start(request, _params); + // No context means the request was canceled before we got here + } + if (error) // (not else if) Always check for error result + { + _return_queryrecord_request_error(request, error); + } + } + KQueueUnlock("_handle_queryrecord_request_with_trust"); + } + }); + request->trust = trust; + mdns_trust_activate(trust); + err = mStatus_NoError; + break; } - } - } - queryrecord_result_reply(m, req, question, answer, AddRecord, error); -} -mDNSlocal void queryrecord_termination_callback(request_state *request) -{ - LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) STOP PID[%d](%s)", - request->sd, request->flags, request->interfaceIndex, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), request->process_id, request->pid_name); - if (request->u.queryrecord.q.QuestionContext) - { - mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check - LogMcastQ(&request->u.queryrecord.q, request, q_stop); - request->u.queryrecord.q.QuestionContext = mDNSNULL; - } - else - { - DNSQuestion *question = &request->u.queryrecord.q; - LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); - } + case mdns_trust_status_no_entitlement: + err = mStatus_NoAuth; + break; - if (request->u.queryrecord.q.qnameOrig) - { - freeL("QueryTermination", request->u.queryrecord.q.qnameOrig); - request->u.queryrecord.q.qnameOrig = mDNSNULL; - } + case mdns_trust_status_granted: + err = _handle_queryrecord_request_start(request, params); + break; - if (callExternalHelpers(request->u.queryrecord.q.InterfaceID, &request->u.queryrecord.q.qname, request->u.queryrecord.q.flags)) - { - LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()"); - external_stop_browsing_for_service(request->u.queryrecord.q.InterfaceID, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype, request->u.queryrecord.q.flags); - } - if (request->u.queryrecord.q2) - { - if (request->u.queryrecord.q2->QuestionContext) - { - LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c); - mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2); - LogMcastQ(request->u.queryrecord.q2, request, q_stop); - } - else - { - DNSQuestion *question = request->u.queryrecord.q2; - LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); - } - if (request->u.queryrecord.q2->qnameOrig) - { - LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c); - freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig); - request->u.queryrecord.q2->qnameOrig = mDNSNULL; - } - freeL("queryrecord Q2", request->u.queryrecord.q2); - request->u.queryrecord.q2 = mDNSNULL; - } -#if APPLE_OSX_mDNSResponder - { - if (request->u.queryrecord.ans) - { - DNSQuestion *v4q, *v6q; - // If we are receiving poisitive answers, provide the hint to the - // upper layer. - v4q = v6q = mDNSNULL; - if (request->u.queryrecord.q.qtype == kDNSType_A) - v4q = &request->u.queryrecord.q; - else if (request->u.queryrecord.q.qtype == kDNSType_AAAA) - v6q = &request->u.queryrecord.q; - mDNSPlatformTriggerDNSRetry(v4q, v6q); + default: + err = mStatus_UnknownErr; + break; } } -#endif // APPLE_OSX_mDNSResponder +exit: + return err; } +#endif // TRUST_ENFORCEMENT mDNSlocal mStatus handle_queryrecord_request(request_state *request) { - DNSQuestion *const q = &request->u.queryrecord.q; - char name[256]; - size_t nameLen; - mDNSu16 rrtype, rrclass; mStatus err; + _queryrecord_start_params_t params; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - - // The request is scoped to a specific interface index, but the - // interface is not currently in our list. - if (interfaceIndex && !InterfaceID) + params.flags = get_flags(&request->msgptr, request->msgend); + params.interfaceIndex = get_uint32(&request->msgptr, request->msgend); + if (get_string(&request->msgptr, request->msgend, params.qname, sizeof(params.qname)) < 0) { - if (interfaceIndex > 1) - LogMsg("handle_queryrecord_request: interfaceIndex %d is currently inactive requested by client[%d][%s]", - interfaceIndex, request->process_id, request->pid_name); - // If it's one of the specially defined inteface index values, just return an error. - // Also, caller should return an error immediately if lo0 (index 1) is not configured - // into the current active interfaces. See background in Radar 21967160. - if (PreDefinedInterfaceIndex(interfaceIndex) || interfaceIndex == 1) - { - LogInfo("handle_queryrecord_request: bad interfaceIndex %d", interfaceIndex); - return(mStatus_BadParamErr); - } - - // Otherwise, use the specified interface index value and the request will - // be applied to that interface when it comes up. - InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; - LogInfo("handle_queryrecord_request: query pending for interface index %d", interfaceIndex); + err = mStatus_BadParamErr; + goto exit; } - - if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr); - rrtype = get_uint16(&request->msgptr, request->msgend); - rrclass = get_uint16(&request->msgptr, request->msgend); + params.qtype = get_uint16(&request->msgptr, request->msgend); + params.qclass = get_uint16(&request->msgptr, request->msgend); if (!request->msgptr) - { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - - request->flags = flags; - request->interfaceIndex = interfaceIndex; - mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord)); - - q->InterfaceID = InterfaceID; - q->flags = flags; - q->Target = zeroAddr; - if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr); -#if 0 - if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError); -#endif - q->qtype = rrtype; - q->qclass = rrclass; - q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - q->ExpectUnique = mDNSfalse; - q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; - q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; - q->allowExpired = (EnableAllowExpired && (flags & kDNSServiceFlagsAllowExpiredAnswers) != 0) ? AllowExpired_AllowExpiredAnswers : AllowExpired_None; - q->WakeOnResolve = 0; - q->UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - if ((flags & kDNSServiceFlagsValidate) != 0) - q->ValidationRequired = DNSSEC_VALIDATION_SECURE; - else if ((flags & kDNSServiceFlagsValidateOptional) != 0) - q->ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL; - q->ValidatingResponse = 0; - q->ProxyQuestion = 0; - q->AnonInfo = mDNSNULL; - q->QuestionCallback = queryrecord_result_callback; - q->QuestionContext = request; - q->SearchListIndex = 0; - q->StopTime = 0; - - q->DNSSECAuthInfo = mDNSNULL; - q->DAIFreeCallback = mDNSNULL; - - //Turn off dnssec validation for local domains and Question Types: RRSIG/ANY(ANY Type is not supported yet) - if ((IsLocalDomain(&q->qname)) || (q->qtype == kDNSServiceType_RRSIG) || (q->qtype == kDNSServiceType_ANY)) - q->ValidationRequired = 0; - - // Don't append search domains for fully qualified domain names including queries - // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally - // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should - // append search domains or not. So, we record that information in AppendSearchDomains. - // - // We append search domains only for queries that are a single label. If overriden using command line - // argument "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. - // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. - - nameLen = strlen(name); - if ((!(q->ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(q->ValidationRequired == DNSSEC_VALIDATION_INSECURE)) - && (rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && ((nameLen == 0) || (name[nameLen - 1] != '.')) && - (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) { - q->AppendSearchDomains = 1; - q->AppendLocalSearchDomains = 1; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceQueryRecord(unreadable parameters)", request->request_id); + err = mStatus_BadParamErr; + goto exit; } - else +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + params.require_privacy = mDNSfalse; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) && MDNSRESPONDER_SUPPORTS(APPLE, IPC_TLV) + if (request->msgptr && (request->hdr.ipc_flags & IPC_FLAGS_TRAILING_TLVS)) { - q->AppendSearchDomains = 0; - q->AppendLocalSearchDomains = 0; + size_t len; + const mDNSu8 *const start = (const mDNSu8 *)request->msgptr; + const mDNSu8 *const end = (const mDNSu8 *)request->msgend; + const mDNSu8 *const data = ipc_tlv_get_resolver_config_plist_data(start, end, &len); + if (data) + { + request->custom_service_id = Querier_RegisterCustomDNSServiceWithPListData(data, len); + } + params.require_privacy = ipc_tlv_get_require_privacy(start, end); } +#endif + request->flags = params.flags; + request->interfaceIndex = params.interfaceIndex; - // For single label queries that are not fully qualified, look at /etc/hosts, cache and try - // search domains before trying them on the wire as a single label query. RetryWithSearchDomains - // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or - // the cache - q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; - q->qnameOrig = mDNSNULL; - SetQuestionPolicy(q, request); - -#if APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR - // Determine if this request should be promoted to use BLE triggered discovery. - if (shouldUseBLE(InterfaceID, rrtype, (domainname *)SkipLeadingLabels(&q->qname, 1), &q->qname)) - { - q->flags |= (kDNSServiceFlagsAutoTrigger | kDNSServiceFlagsIncludeAWDL); - request->flags |= (kDNSServiceFlagsAutoTrigger | kDNSServiceFlagsIncludeAWDL); - LogInfo("handle_queryrecord_request: request promoted to use kDNSServiceFlagsAutoTrigger"); - } -#endif // APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceQueryRecord(%X, %d, " PRI_S ", " PUB_S ") START PID[%d](" PUB_S ")", + request->request_id, request->flags, request->interfaceIndex, params.qname, DNSTypeName(params.qtype), request->process_id, + request->pid_name); - LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)", - request->sd, request->flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), request->process_id, request->pid_name); - err = mDNS_StartQuery(&mDNSStorage, q); + mDNSPlatformMemZero(&request->u.queryrecord, (mDNSu32)sizeof(request->u.queryrecord)); + request->terminate = NULL; - if (err) +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (os_feature_enabled(mDNSResponder, bonjour_privacy)) { - LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); + err = _handle_queryrecord_request_with_trust(request, ¶ms); } else { - request->terminate = queryrecord_termination_callback; - LogMcastQ(q, request, q_start); - if (callExternalHelpers(q->InterfaceID, &q->qname, q->flags)) - { - LogDebug("handle_queryrecord_request: calling external_start_browsing_for_service()"); - external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, q->flags); - } + err = _handle_queryrecord_request_start(request, ¶ms); } +#else + err = _handle_queryrecord_request_start(request, ¶ms); +#endif -#if APPLE_OSX_mDNSResponder - err = SendAdditionalQuery(q, request, err); -#endif // APPLE_OSX_mDNSResponder - +exit: return(err); } @@ -3835,7 +3976,10 @@ mDNSlocal void enum_result_callback(mDNS *const m, reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError); if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; } - LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "ADD" : "RMV", domain); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d->Q%d] DNSServiceEnumerateDomains(%2.*s) RESULT " PUB_S ": " PRI_S, + request->request_id, mDNSVal16(question->TargetQID), question->qname.c[0], &question->qname.c[1], + AddRecord ? "ADD" : "RMV", domain); append_reply(request, reply); } @@ -3943,9 +4087,9 @@ mDNSlocal mStatus handle_release_request(request_state *request) // extract the data from the message DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - if (get_string(&request->msgptr, request->msgend, name, 256) < 0 || - get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) + if (get_string(&request->msgptr, request->msgend, name, sizeof(name )) < 0 || + get_string(&request->msgptr, request->msgend, regtype, sizeof(regtype)) < 0 || + get_string(&request->msgptr, request->msgend, domain, sizeof(domain )) < 0) { LogMsg("ERROR: handle_release_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); @@ -3963,10 +4107,13 @@ mDNSlocal mStatus handle_release_request(request_state *request) return(mStatus_BadParamErr); } - LogOperation("%3d: PeerConnectionRelease(%X %##s) START PID[%d](%s)", - request->sd, flags, instance.c, request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] PeerConnectionRelease(%X " PRI_DM_NAME ") START PID[%d](" PUB_S ")", + request->request_id, flags, DM_NAME_PARAM(&instance), request->process_id, request->pid_name); +#if MDNSRESPONDER_SUPPORTS(APPLE, D2D) external_connection_release(&instance); +#endif return(err); } @@ -3986,7 +4133,7 @@ mDNSlocal mStatus handle_setdomain_request(request_state *request) domainname domain; DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); (void)flags; // Unused - if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 || + if (get_string(&request->msgptr, request->msgend, domainstr, sizeof(domainstr)) < 0 || !MakeDomainNameFromDNSNameString(&domain, domainstr)) { LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } @@ -4007,7 +4154,8 @@ mDNSlocal void handle_getproperty_request(request_state *request) char prop[256]; if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0) { - LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceGetProperty(" PUB_S ")", request->request_id, prop); if (!strcmp(prop, kDNSServiceProperty_DaemonVersion)) { DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) }; @@ -4029,8 +4177,9 @@ mDNSlocal void handle_connection_delegate_request(request_state *request) mDNSs32 pid; socklen_t len; - LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)", - request->sd, request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceCreateDelegateConnection START PID[%d](" PUB_S ")", + request->request_id, request->process_id, request->pid_name); request->terminate = connection_termination; len = 0; @@ -4077,63 +4226,6 @@ typedef packedstruct mDNSs32 pid; } PIDInfo; -mDNSlocal void handle_getpid_request(request_state *request) -{ - const request_state *req; - mDNSs32 pid = -1; - mDNSu16 srcport = get_uint16(&request->msgptr, request->msgend); - const DNSQuestion *q = NULL; - PIDInfo pi; - - LogMsg("%3d: DNSServiceGetPID START", request->sd); - - for (req = all_requests; req; req=req->next) - { - if (req->hdr.op == query_request) - q = &req->u.queryrecord.q; - else if (req->hdr.op == addrinfo_request) - q = &req->u.addrinfo.q4; - else if (req->hdr.op == addrinfo_request) - q = &req->u.addrinfo.q6; - - if (q && q->LocalSocket != NULL) - { - mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket); - if (port == srcport) - { - pid = req->process_id; - LogMsg("DNSServiceGetPID: srcport %d, pid %d [%s] question %##s", htons(srcport), pid, req->pid_name, q->qname.c); - break; - } - } - } - // If we cannot find in the client requests, look to see if this was - // started by mDNSResponder. - if (pid == -1) - { - for (q = mDNSStorage.Questions; q; q = q->next) - { - if (q && q->LocalSocket != NULL) - { - mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket); - if (port == srcport) - { -#if APPLE_OSX_mDNSResponder - pid = getpid(); -#endif // APPLE_OSX_mDNSResponder - LogMsg("DNSServiceGetPID: srcport %d, pid %d [%s], question %##s", htons(srcport), pid, "_mDNSResponder", q->qname.c); - break; - } - } - } - } - - pi.err = 0; - pi.pid = pid; - send_all(request->sd, (const char *)&pi, sizeof(PIDInfo)); - LogMsg("%3d: DNSServiceGetPID STOP", request->sd); -} - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -4144,10 +4236,11 @@ mDNSlocal void handle_getpid_request(request_state *request) mDNSlocal void port_mapping_termination_callback(request_state *request) { - LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](%s)", request->sd, - DNSServiceProtocol(request->u.pm.NATinfo.Protocol), - mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, "[R%d] DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](" PUB_S ")", + request->request_id, DNSServiceProtocol(request->u.pm.NATinfo.Protocol), + mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + request->process_id, request->pid_name); + mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo); } @@ -4187,10 +4280,12 @@ mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n *data++ = request->u.pm.NATinfo.ExternalPort.b[1]; put_uint32(request->u.pm.NATinfo.Lifetime, &data); - LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd, - DNSServiceProtocol(request->u.pm.NATinfo.Protocol), - mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT " PRI_IPv4_ADDR ":%u TTL %u", + request->request_id, DNSServiceProtocol(request->u.pm.NATinfo.Protocol), + mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), + request->u.pm.NATinfo.Lifetime); append_reply(request, rep); } @@ -4217,7 +4312,11 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) } if (!request->msgptr) - { LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%d] DNSServiceNATPortMappingCreate(unreadable parameters)", request->request_id); + return(mStatus_BadParamErr); + } if (protocol == 0) // If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too { @@ -4238,9 +4337,10 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback; request->u.pm.NATinfo.clientContext = request; - LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](%s)", request->sd, - protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](" PUB_S ")", + request->request_id, protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), + request->u.pm.NATinfo.NATLease, request->process_id, request->pid_name); err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo); if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err); else request->terminate = port_mapping_termination_callback; @@ -4256,319 +4356,201 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) mDNSlocal void addrinfo_termination_callback(request_state *request) { - LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP PID[%d](%s)", request->sd, request->u.addrinfo.q4.qname.c, - request->process_id, request->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] DNSServiceGetAddrInfo(" PRI_DM_NAME ") STOP PID[%d](" PUB_S ")", + request->request_id, DM_NAME_PARAM(GetAddrInfoClientRequestGetQName(&request->u.addrinfo)), + request->process_id, request->pid_name); - if (request->u.addrinfo.q4.QuestionContext) - { - mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); - LogMcastQ(&request->u.addrinfo.q4, request, q_stop); - request->u.addrinfo.q4.QuestionContext = mDNSNULL; + GetAddrInfoClientRequestStop(&request->u.addrinfo); +} - if (callExternalHelpers(request->u.addrinfo.interface_id, &request->u.addrinfo.q4.qname, request->flags)) - { - LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for A record"); - external_stop_browsing_for_service(request->u.addrinfo.interface_id, &request->u.addrinfo.q4.qname, kDNSServiceType_A, request->flags); - } - } - if (request->u.addrinfo.q4.qnameOrig) - { - freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig); - request->u.addrinfo.q4.qnameOrig = mDNSNULL; - } - if (request->u.addrinfo.q42) - { - if (request->u.addrinfo.q42->QuestionContext) - { - LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c); - mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42); - LogMcastQ(request->u.addrinfo.q42, request, q_stop); - } - if (request->u.addrinfo.q42->qnameOrig) - { - LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c); - freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig); - request->u.addrinfo.q42->qnameOrig = mDNSNULL; - } - freeL("addrinfo Q42", request->u.addrinfo.q42); - request->u.addrinfo.q42 = mDNSNULL; - } +typedef struct { + mDNSu32 protocols; + char hostname[MAX_ESCAPED_DOMAIN_NAME]; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mDNSBool require_privacy; +#endif +} _addrinfo_start_params_t; - if (request->u.addrinfo.q6.QuestionContext) - { - mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); - LogMcastQ(&request->u.addrinfo.q6, request, q_stop); - request->u.addrinfo.q6.QuestionContext = mDNSNULL; +mDNSlocal mStatus _handle_addrinfo_request_start(request_state *request, const _addrinfo_start_params_t * const params) +{ + mStatus err; - if (callExternalHelpers(request->u.addrinfo.interface_id, &request->u.addrinfo.q6.qname, request->flags)) - { - LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for AAAA record"); - external_stop_browsing_for_service(request->u.addrinfo.interface_id, &request->u.addrinfo.q6.qname, kDNSServiceType_AAAA, request->flags); - } - } - if (request->u.addrinfo.q6.qnameOrig) - { - freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig); - request->u.addrinfo.q6.qnameOrig = mDNSNULL; - } - if (request->u.addrinfo.q62) + request->terminate = addrinfo_termination_callback; + + GetAddrInfoClientRequestParams gaiParams; + GetAddrInfoClientRequestParamsInit(&gaiParams); + gaiParams.requestID = request->request_id; + gaiParams.hostnameStr = params->hostname; + gaiParams.interfaceIndex = request->interfaceIndex; + gaiParams.flags = request->flags; + gaiParams.protocols = params->protocols; + gaiParams.effectivePID = request->validUUID ? 0 : request->process_id; + gaiParams.effectiveUUID = request->validUUID ? request->uuid : mDNSNULL; + gaiParams.peerUID = request->uid; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + gaiParams.needEncryption = params->require_privacy ? mDNStrue : mDNSfalse; + gaiParams.customID = request->custom_service_id; +#endif +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + gaiParams.peerAuditToken = &request->audit_token; +#endif + err = GetAddrInfoClientRequestStart(&request->u.addrinfo, &gaiParams, queryrecord_result_reply, request); + + return err; +} + +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + +mDNSlocal void _return_addrinfo_request_error(request_state * request, mStatus error) +{ + _return_queryrecord_request_error(request, error); +} + +mDNSlocal mStatus _handle_addrinfo_request_with_trust(request_state *request, const _addrinfo_start_params_t * const params) +{ + mStatus err; + if (audit_token_to_pid(request->audit_token) == 0) { - if (request->u.addrinfo.q62->QuestionContext) - { - LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c); - mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62); - LogMcastQ(request->u.addrinfo.q62, request, q_stop); - } - if (request->u.addrinfo.q62->qnameOrig) - { - LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c); - freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig); - request->u.addrinfo.q62->qnameOrig = mDNSNULL; - } - freeL("addrinfo Q62", request->u.addrinfo.q62); - request->u.addrinfo.q62 = mDNSNULL; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING, "[R%u] _handle_addrinfo_request_with_trust: no audit token for pid(%s %d)", request->request_id, request->pid_name, request->process_id); + err = _handle_addrinfo_request_start(request, params); } -#if APPLE_OSX_mDNSResponder + else { - DNSQuestion *v4q, *v6q; - v4q = v6q = mDNSNULL; - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) - { - // If we are not delivering answers, we may be timing out prematurely. - // Note down the current state so that we know to retry when we see a - // valid response again. - if (request->u.addrinfo.q4.TimeoutQuestion && !request->u.addrinfo.v4ans) - { - mDNSPlatformUpdateDNSStatus(&request->u.addrinfo.q4); - } - // If we have a v4 answer and if we timed out prematurely before, provide - // a trigger to the upper layer so that it can retry questions if needed. - if (request->u.addrinfo.v4ans) - v4q = &request->u.addrinfo.q4; - } - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) + mdns_trust_flags_t flags = mdns_trust_flags_none; + mdns_trust_status_t status = mdns_trust_check_getaddrinfo(request->audit_token, params->hostname, &flags); + switch (status) { - if (request->u.addrinfo.q6.TimeoutQuestion && !request->u.addrinfo.v6ans) + case mdns_trust_status_denied: + case mdns_trust_status_pending: { - mDNSPlatformUpdateDNSStatus(&request->u.addrinfo.q6); + mdns_trust_t trust = mdns_trust_create(request->audit_token, NULL, flags); + if (!trust ) + { + err = mStatus_NoMemoryErr; + goto exit; + } + + void * context = mallocL("context/_handle_addrinfo_request_with_trust", sizeof(_addrinfo_start_params_t)); + if (!context) + { + my_perror("ERROR: mallocL context/_handle_addrinfo_request_with_trust"); + mdns_release(trust); + err = mStatus_NoMemoryErr; + goto exit; + } + memcpy(context, params, sizeof(_addrinfo_start_params_t)); + mdns_trust_set_context(trust, context); + mdns_trust_set_queue(trust, _get_trust_results_dispatch_queue()); + mdns_trust_set_event_handler(trust, ^(mdns_trust_event_t event, mdns_trust_status_t update) + { + if (event == mdns_trust_event_result) + { + mStatus error = (update != mdns_trust_status_granted) ? mStatus_PolicyDenied : mStatus_NoError; + KQueueLock(); + _addrinfo_start_params_t * _params = mdns_trust_get_context(trust); + if (_params) + { + if (!error) + { + error = _handle_addrinfo_request_start(request, _params); + // No context means the request was canceled before we got here + } + if (error) // (not else if) Always check for error result + { + _return_addrinfo_request_error(request, error); + } + } + KQueueUnlock("_handle_addrinfo_request_with_trust"); + } + }); + request->trust = trust; + mdns_trust_activate(trust); + err = mStatus_NoError; + break; } - if (request->u.addrinfo.v6ans) - v6q = &request->u.addrinfo.q6; + + case mdns_trust_status_no_entitlement: + err = mStatus_NoAuth; + break; + + case mdns_trust_status_granted: + err = _handle_addrinfo_request_start(request, params); + break; + + default: + err = mStatus_UnknownErr; + break; } - mDNSPlatformTriggerDNSRetry(v4q, v6q); } -#endif // APPLE_OSX_mDNSResponder +exit: + return err; } +#endif // TRUST_ENFORCEMENT mDNSlocal mStatus handle_addrinfo_request(request_state *request) { - char hostname[256]; - size_t hostnameLen; - domainname d; - mStatus err = 0; - mDNSs32 serviceIndex = -1; // default unscoped value for ServiceID is -1 - mDNSInterfaceID InterfaceID; - - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - - mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mStatus err; + DNSServiceFlags flags; + mDNSu32 interfaceIndex; + _addrinfo_start_params_t params; - if (flags & kDNSServiceFlagsServiceIndex) + flags = get_flags(&request->msgptr, request->msgend); + interfaceIndex = get_uint32(&request->msgptr, request->msgend); + params.protocols = get_uint32(&request->msgptr, request->msgend); + if (get_string(&request->msgptr, request->msgend, params.hostname, sizeof(params.hostname)) < 0) { - // NOTE: kDNSServiceFlagsServiceIndex flag can only be set for DNSServiceGetAddrInfo() - LogInfo("DNSServiceGetAddrInfo: kDNSServiceFlagsServiceIndex is SET by the client"); - // if kDNSServiceFlagsServiceIndex is SET, - // interpret the interfaceID as the serviceId and set the interfaceID to 0. - serviceIndex = interfaceIndex; - interfaceIndex = 0; + err = mStatus_BadParamErr; + goto exit; } - - mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo)); - - InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - - // The request is scoped to a specific interface index, but the - // interface is not currently in our list. - if (interfaceIndex && !InterfaceID) + if (!request->msgptr) { - if (interfaceIndex > 1) - LogMsg("handle_addrinfo_request: interfaceIndex %d is currently inactive requested by client[%d][%s]", - interfaceIndex, request->process_id, request->pid_name); - // If it's one of the specially defined inteface index values, just return an error. - if (PreDefinedInterfaceIndex(interfaceIndex)) - { - LogInfo("handle_addrinfo_request: bad interfaceIndex %d", interfaceIndex); - return(mStatus_BadParamErr); - } - - // Otherwise, use the specified interface index value and the request will - // be applied to that interface when it comes up. - InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; - LogInfo("handle_addrinfo_request: query pending for interface index %d", interfaceIndex); + LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); + err = mStatus_BadParamErr; + goto exit; } - - request->flags = flags; - request->interfaceIndex = interfaceIndex; - request->u.addrinfo.interface_id = InterfaceID; - request->u.addrinfo.flags = flags; - request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend); - - if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr); - - if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr); - - if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - - if (!MakeDomainNameFromDNSNameString(&d, hostname)) - { LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); } - -#if 0 - if (!AuthorizedDomain(request, &d, AutoBrowseDomains)) return (mStatus_NoError); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + params.require_privacy = mDNSfalse; #endif - - if (!request->u.addrinfo.protocol) - { - flags |= kDNSServiceFlagsSuppressUnusable; - request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); - } - - request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; - request->u.addrinfo.q4.ServiceID = request->u.addrinfo.q6.ServiceID = serviceIndex; - request->u.addrinfo.q4.flags = request->u.addrinfo.q6.flags = flags; - request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr; - request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d; - request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; - request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse; - request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; - request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; - request->u.addrinfo.q4.allowExpired = request->u.addrinfo.q6.allowExpired = (EnableAllowExpired && (flags & kDNSServiceFlagsAllowExpiredAnswers) != 0) ? AllowExpired_AllowExpiredAnswers : AllowExpired_None; - request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0; - request->u.addrinfo.q4.UseBackgroundTrafficClass = request->u.addrinfo.q6.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - if ((flags & kDNSServiceFlagsValidate) != 0) - request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE; - else if ((flags & kDNSServiceFlagsValidateOptional) != 0) - request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL; - request->u.addrinfo.q4.ValidatingResponse = request->u.addrinfo.q6.ValidatingResponse = 0; - request->u.addrinfo.q4.ProxyQuestion = request->u.addrinfo.q6.ProxyQuestion = 0; - request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL; - request->u.addrinfo.q4.AnonInfo = request->u.addrinfo.q6.AnonInfo = mDNSNULL; - - SetQuestionPolicy(&request->u.addrinfo.q4, request); - SetQuestionPolicy(&request->u.addrinfo.q6, request); - - request->u.addrinfo.q4.StopTime = request->u.addrinfo.q6.StopTime = 0; - - request->u.addrinfo.q4.DNSSECAuthInfo = request->u.addrinfo.q6.DNSSECAuthInfo = mDNSNULL; - request->u.addrinfo.q4.DAIFreeCallback = request->u.addrinfo.q6.DAIFreeCallback = mDNSNULL; - - //Turn off dnssec validation for local domains - if (IsLocalDomain(&d)) - request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 0; - - hostnameLen = strlen(hostname); - - LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", - request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c, request->process_id, request->pid_name); - - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) - { - request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA; - request->u.addrinfo.q6.SearchListIndex = 0; - // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set - if ((!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) - && ((hostnameLen == 0) || (hostname[hostnameLen - 1] != '.')) && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) - { - request->u.addrinfo.q6.AppendSearchDomains = 1; - request->u.addrinfo.q6.AppendLocalSearchDomains = 1; - } - else - { - request->u.addrinfo.q6.AppendSearchDomains = 0; - request->u.addrinfo.q6.AppendLocalSearchDomains = 0; - } - request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0); - request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback; - request->u.addrinfo.q6.QuestionContext = request; - err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6); - if (err != mStatus_NoError) - { - LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); - request->u.addrinfo.q6.QuestionContext = mDNSNULL; - } - #if APPLE_OSX_mDNSResponder - err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err); - #endif // APPLE_OSX_mDNSResponder - if (!err) +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) && MDNSRESPONDER_SUPPORTS(APPLE, IPC_TLV) + if (request->msgptr && (request->hdr.ipc_flags & IPC_FLAGS_TRAILING_TLVS)) + { + size_t len; + const mDNSu8 *const start = (const mDNSu8 *)request->msgptr; + const mDNSu8 *const end = (const mDNSu8 *)request->msgend; + const mDNSu8 *const data = ipc_tlv_get_resolver_config_plist_data(start, end, &len); + if (data) { - request->terminate = addrinfo_termination_callback; - LogMcastQ(&request->u.addrinfo.q6, request, q_start); - if (callExternalHelpers(InterfaceID, &d, flags)) - { - LogDebug("handle_addrinfo_request: calling external_start_browsing_for_service() for AAAA record"); - external_start_browsing_for_service(InterfaceID, &d, kDNSServiceType_AAAA, flags); - } + request->custom_service_id = Querier_RegisterCustomDNSServiceWithPListData(data, len); } + params.require_privacy = ipc_tlv_get_require_privacy(start, end); } +#endif + request->flags = flags; + request->interfaceIndex = interfaceIndex; - if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)) - { - request->u.addrinfo.q4.qtype = kDNSServiceType_A; - request->u.addrinfo.q4.SearchListIndex = 0; - - // We append search domains only for queries that are a single label. If overriden using cmd line arg - // "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. - // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] DNSServiceGetAddrInfo(%X, %d, %u, " PRI_S ") START PID[%d](" PUB_S ")", + request->request_id, request->flags, request->interfaceIndex, params.protocols, params.hostname, request->process_id, + request->pid_name); - if ((!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) - && ((hostnameLen == 0) || (hostname[hostnameLen - 1] != '.')) && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) - { - request->u.addrinfo.q4.AppendSearchDomains = 1; - request->u.addrinfo.q4.AppendLocalSearchDomains = 1; - } - else - { - request->u.addrinfo.q4.AppendSearchDomains = 0; - request->u.addrinfo.q4.AppendLocalSearchDomains = 0; - } - request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0); - request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback; - request->u.addrinfo.q4.QuestionContext = request; - err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4); - if (err != mStatus_NoError) - { - LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); - request->u.addrinfo.q4.QuestionContext = mDNSNULL; - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) - { - // If we started a query for IPv6, we need to cancel it - mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); - request->u.addrinfo.q6.QuestionContext = mDNSNULL; + mDNSPlatformMemZero(&request->u.addrinfo, (mDNSu32)sizeof(request->u.addrinfo)); + request->terminate = NULL; - if (callExternalHelpers(InterfaceID, &d, flags)) - { - LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for AAAA record"); - external_stop_browsing_for_service(InterfaceID, &d, kDNSServiceType_AAAA, flags); - } - } - } - #if APPLE_OSX_mDNSResponder - err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err); - #endif // APPLE_OSX_mDNSResponder - if (!err) - { - request->terminate = addrinfo_termination_callback; - LogMcastQ(&request->u.addrinfo.q4, request, q_start); - if (callExternalHelpers(InterfaceID, &d, flags)) - { - LogDebug("handle_addrinfo_request: calling external_start_browsing_for_service() for A record"); - external_start_browsing_for_service(InterfaceID, &d, kDNSServiceType_A, flags); - } - } +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (os_feature_enabled(mDNSResponder, bonjour_privacy)) + { + err = _handle_addrinfo_request_with_trust(request, ¶ms); + } + else + { + err = _handle_addrinfo_request_start(request, ¶ms); } +#else + err = _handle_addrinfo_request_start(request, ¶ms); +#endif +exit: return(err); } @@ -4580,14 +4562,13 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) mDNSlocal request_state *NewRequest(void) { + request_state *request; request_state **p = &all_requests; - while (*p) - p=&(*p)->next; - *p = mallocL("request_state", sizeof(request_state)); - if (!*p) - FatalError("ERROR: malloc"); - mDNSPlatformMemZero(*p, sizeof(request_state)); - return(*p); + request = (request_state *) callocL("request_state", sizeof(*request)); + if (!request) FatalError("ERROR: calloc"); + while (*p) p = &(*p)->next; + *p = request; + return(request); } // read_msg may be called any time when the transfer state (req->ts) is t_morecoming. @@ -4595,7 +4576,12 @@ mDNSlocal request_state *NewRequest(void) mDNSlocal void read_msg(request_state *req) { if (req->ts == t_terminated || req->ts == t_error) - { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: read_msg called with transfer state terminated or error", req->request_id); + req->ts = t_error; + return; + } if (req->ts == t_complete) // this must be death or something is wrong { @@ -4603,13 +4589,19 @@ mDNSlocal void read_msg(request_state *req) int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data); if (!nread) { req->ts = t_terminated; return; } if (nread < 0) goto rerror; - LogMsg("%3d: ERROR: read data from a completed request", req->sd); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: read data from a completed request", req->request_id); req->ts = t_error; return; } if (req->ts != t_morecoming) - { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: read_msg called with invalid transfer state (%d)", req->request_id, req->ts); + req->ts = t_error; + return; + } if (req->hdr_bytes < sizeof(ipc_msg_hdr)) { @@ -4619,25 +4611,39 @@ mDNSlocal void read_msg(request_state *req) if (nread < 0) goto rerror; req->hdr_bytes += nread; if (req->hdr_bytes > sizeof(ipc_msg_hdr)) - { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: read_msg - read too many header bytes", req->request_id); + req->ts = t_error; + return; + } // only read data if header is complete if (req->hdr_bytes == sizeof(ipc_msg_hdr)) { ConvertHeaderBytes(&req->hdr); if (req->hdr.version != VERSION) - { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: client version 0x%08X daemon version 0x%08X", req->request_id, req->hdr.version, VERSION); + req->ts = t_error; + return; + } // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord() // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin // for other overhead, this means any message above 70kB is definitely bogus. if (req->hdr.datalen > 70000) - { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; } - req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES); - if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->request_id, req->hdr.datalen, req->hdr.datalen); + req->ts = t_error; + return; + } + req->msgbuf = (char *) callocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES); + if (!req->msgbuf) { my_perror("ERROR: calloc"); req->ts = t_error; return; } req->msgptr = req->msgbuf; req->msgend = req->msgbuf + req->hdr.datalen; - mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES); } } @@ -4648,7 +4654,7 @@ mDNSlocal void read_msg(request_state *req) if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen) { mDNSu32 nleft = req->hdr.datalen - req->data_bytes; - int nread; + ssize_t nread; #if !defined(_WIN32) struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put struct msghdr msg; @@ -4669,12 +4675,19 @@ mDNSlocal void read_msg(request_state *req) if (nread < 0) goto rerror; req->data_bytes += nread; if (req->data_bytes > req->hdr.datalen) - { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: read_msg - read too many data bytes", req->request_id); + req->ts = t_error; + return; + } #if !defined(_WIN32) cmsg = CMSG_FIRSTHDR(&msg); #if DEBUG_64BIT_SCM_RIGHTS - LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); - LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg ? cmsg->cmsg_len : -1, cmsg ? cmsg->cmsg_level : -1, cmsg ? cmsg->cmsg_type : -1); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] Expecting %d %d %d %d", req->request_id, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] Got %d %d %d %d", req->request_id, msg.msg_controllen, cmsg ? cmsg->cmsg_len : -1, cmsg ? cmsg->cmsg_level : -1, cmsg ? cmsg->cmsg_type : -1); #endif // DEBUG_64BIT_SCM_RIGHTS if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { @@ -4685,19 +4698,22 @@ mDNSlocal void read_msg(request_state *req) if (req->hdr.op == send_bpf) { dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg); - LogOperation("%3d: Got len %d, BPF %d", req->sd, cmsg->cmsg_len, x); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] Got len %d, BPF %d", req->request_id, cmsg->cmsg_len, x); mDNSPlatformReceiveBPF_fd(x); } else #endif // APPLE_OSX_mDNSResponder req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg); #if DEBUG_64BIT_SCM_RIGHTS - LogMsg("%3d: read req->errsd %d", req->sd, req->errsd); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, + "[R%u] read req->errsd %d", req->request_id, req->errsd); #endif // DEBUG_64BIT_SCM_RIGHTS if (req->data_bytes < req->hdr.datalen) { - LogMsg("%3d: Client(PID [%d](%s)) sent result code socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", - req->sd, req->process_id, req->pid_name, req->errsd, req->data_bytes, req->hdr.datalen); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, + "[R%u] Client(PID [%d](" PUB_S ")) sent result code socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", + req->request_id, req->process_id, req->pid_name, req->errsd, req->data_bytes, req->hdr.datalen); req->ts = t_error; return; } @@ -4732,7 +4748,12 @@ mDNSlocal void read_msg(request_state *req) if (ctrl_path[0] == 0) { if (req->errsd == req->sd) - { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; } + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->request_id); + req->ts = t_error; + return; + } goto got_errfd; } #endif @@ -4749,12 +4770,21 @@ mDNSlocal void read_msg(request_state *req) { #if !defined(USE_TCP_LOOPBACK) struct stat sb; - LogMsg("%3d: read_msg: Couldn't connect to error return path socket “%s” errno %d (%s)", - req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] read_msg: Couldn't connect to error return path socket " PUB_S " errno %d (" PUB_S ")", + req->request_id, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); if (stat(cliaddr.sun_path, &sb) < 0) - LogMsg("%3d: read_msg: stat failed “%s” errno %d (%s)", req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] read_msg: stat failed " PUB_S " errno %d (" PUB_S ")", + req->request_id, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); + } else - LogMsg("%3d: read_msg: file “%s” mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid); + { + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] read_msg: file " PUB_S " mode %o (octal) uid %d gid %d", + req->request_id, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid); + } #endif req->ts = t_error; return; @@ -4763,15 +4793,16 @@ mDNSlocal void read_msg(request_state *req) #if !defined(USE_TCP_LOOPBACK) got_errfd: #endif - LogDebug("%3d: Result code socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]); + #if defined(_WIN32) if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0) #else if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0) #endif { - LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)", - req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: could not set control socket to non-blocking mode errno %d (" PUB_S ")", + req->request_id, dnssd_errno, dnssd_strerror(dnssd_errno)); req->ts = t_error; return; } @@ -4784,24 +4815,30 @@ got_errfd: rerror: if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return; - LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, + "[R%u] ERROR: read_msg errno %d (" PUB_S ")", req->request_id, dnssd_errno, dnssd_strerror(dnssd_errno)); req->ts = t_error; } mDNSlocal mStatus handle_client_request(request_state *req) { mStatus err = mStatus_NoError; +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + SetupAuditTokenForRequest(req); +#endif switch(req->hdr.op) { // These are all operations that have their own first-class request_state object case connection_request: - LogOperation("%3d: DNSServiceCreateConnection START PID[%d](%s)", - req->sd, req->process_id, req->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceCreateConnection START PID[%d](" PUB_S ")", + req->request_id, req->process_id, req->pid_name); req->terminate = connection_termination; break; case connection_delegate_request: - LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)", - req->sd, req->process_id, req->pid_name); + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%d] DNSServiceCreateDelegateConnection START PID[%d](" PRI_S ")", + req->request_id, req->process_id, req->pid_name); req->terminate = connection_termination; handle_connection_delegate_request(req); break; @@ -4813,7 +4850,6 @@ mDNSlocal mStatus handle_client_request(request_state *req) case reconfirm_record_request: err = handle_reconfirm_request (req); break; case setdomain_request: err = handle_setdomain_request (req); break; case getproperty_request: handle_getproperty_request (req); break; - case getpid_request: handle_getpid_request (req); break; case port_mapping_request: err = handle_port_mapping_request(req); break; case addrinfo_request: err = handle_addrinfo_request (req); break; case send_bpf: /* Do nothing for send_bpf */ break; @@ -4840,13 +4876,12 @@ mDNSlocal mStatus handle_client_request(request_state *req) // The lightweight operations are the ones that don't need a dedicated request_state structure allocated for them #define LightweightOp(X) (RecordOrientedOp(X) || (X) == cancel_request) -mDNSlocal void request_callback(int fd, short filter, void *info) +mDNSlocal void request_callback(int fd, void *info) { mStatus err = 0; request_state *req = info; mDNSs32 min_size = sizeof(DNSServiceFlags); (void)fd; // Unused - (void)filter; // Unused for (;;) { @@ -4881,7 +4916,6 @@ mDNSlocal void request_callback(int fd, short filter, void *info) case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break; case setdomain_request: min_size += 1 /* domain */; break; case getproperty_request: min_size = 2; break; - case getpid_request: min_size = 2; break; case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break; case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break; case send_bpf: // Same as cancel_request below @@ -4919,6 +4953,10 @@ mDNSlocal void request_callback(int fd, short filter, void *info) newreq->msgbuf = req->msgbuf; newreq->msgptr = req->msgptr; newreq->msgend = req->msgend; + newreq->request_id = GetNewRequestID(); +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + newreq->audit_token = req->audit_token; +#endif // if the parent request is a delegate connection, copy the // relevant bits if (req->validUUID) @@ -4962,8 +5000,6 @@ mDNSlocal void request_callback(int fd, short filter, void *info) send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder)); if (req->errsd != req->sd) { - LogDebug("%3d: Result code socket %d closed %08X %08X (%d)", - req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err); dnssd_close(req->errsd); req->errsd = req->sd; // Also need to reset the parent's errsd, if this is a subordinate operation @@ -4982,7 +5018,7 @@ mDNSlocal void request_callback(int fd, short filter, void *info) } } -mDNSlocal void connect_callback(int fd, short filter, void *info) +mDNSlocal void connect_callback(int fd, void *info) { dnssd_sockaddr_t cliaddr; dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr); @@ -4991,7 +5027,6 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) unsigned long optval = 1; #endif - (void)filter; // Unused (void)info; // Unused if (!dnssd_SocketValid(sd)) @@ -5023,6 +5058,7 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) request->ts = t_morecoming; request->sd = sd; request->errsd = sd; + request->request_id = GetNewRequestID(); set_peer_pid(request); #if APPLE_OSX_mDNSResponder struct xucred x; @@ -5031,7 +5067,6 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) request->uid = x.cr_uid; // save the effective userid of the client else my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); - debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups); #endif // APPLE_OSX_mDNSResponder LogDebug("%3d: connect_callback: Adding FD for uid %u", request->sd, request->uid); @@ -5081,27 +5116,32 @@ mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt) return mDNStrue; } -mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) +#if MDNS_MALLOC_DEBUGGING +mDNSlocal void udsserver_validatelists(void *context); +#endif + +mDNSexport int udsserver_init(dnssd_sock_t skts[], const size_t count) { dnssd_sockaddr_t laddr; int ret; - mDNSu32 i = 0; - LogInfo("udsserver_init: %d %d", _DNS_SD_H, mDNSStorage.mDNS_plat); - - // If a particular platform wants to opt out of having a PID file, define PID_FILE to be "" - if (PID_FILE[0]) +#ifndef NO_PID_FILE + FILE *fp = fopen(PID_FILE, "w"); + if (fp != NULL) { - FILE *fp = fopen(PID_FILE, "w"); - if (fp != NULL) - { - fprintf(fp, "%d\n", (int)getpid()); - fclose(fp); - } + fprintf(fp, "%d\n", getpid()); + fclose(fp); } +#endif + +#if MDNS_MALLOC_DEBUGGING + static mDNSListValidator validator; + mDNSPlatformAddListValidator(&validator, udsserver_validatelists, "udsserver_validatelists", NULL); +#endif if (skts) { + size_t i; for (i = 0; i < count; i++) if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i])) goto error; @@ -5229,21 +5269,100 @@ mDNSexport int udsserver_exit(void) #endif } - if (PID_FILE[0]) unlink(PID_FILE); +#ifndef NO_PID_FILE + unlink(PID_FILE); +#endif return 0; } -mDNSlocal void LogClientInfo(request_state *req) +mDNSlocal void LogClientInfoToFD(int fd, request_state *req) { - char prefix[16]; - if (req->primary) - mDNS_snprintf(prefix, sizeof(prefix), " -> "); + char reqIDStr[14]; + char prefix[18]; + + mDNS_snprintf(reqIDStr, sizeof(reqIDStr), "[R%u]", req->request_id); + + mDNS_snprintf(prefix, sizeof(prefix), "%-6s %2s", reqIDStr, req->primary ? "->" : ""); + + if (!req->terminate) + LogToFD(fd, "%s No operation yet on this socket", prefix); + else if (req->terminate == connection_termination) + { + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + request_state *r; + for (p = req->u.reg_recs; p; p=p->next) num_records++; + for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; + LogToFD(fd, "%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", + prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", + req->process_id, req->pid_name); + for (p = req->u.reg_recs; p; p=p->next) + LogToFD(fd, " -> DNSServiceRegisterRecord 0x%08X %2d %3d %s PID[%d](%s)", + req->flags, req->interfaceIndex, p->key, ARDisplayString(&mDNSStorage, p->rr), req->process_id, req->pid_name); + for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfoToFD(fd, r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + LogToFD(fd, "%-9s DNSServiceRegister 0x%08X %2d %##s %u/%u PID[%d](%s)", + (ptr == req->u.servicereg.instances) ? prefix : "", req->flags, req->interfaceIndex, ptr->srs.RR_SRV.resrec.name->c, + mDNSVal16(req->u.servicereg.port), + SRS_PORT(&ptr->srs), req->process_id, req->pid_name); + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->u.browser.browsers; blist; blist = blist->next) + LogToFD(fd, "%-9s DNSServiceBrowse 0x%08X %2d %##s PID[%d](%s)", + (blist == req->u.browser.browsers) ? prefix : "", req->flags, req->interfaceIndex, blist->q.qname.c, + req->process_id, req->pid_name); + } + else if (req->terminate == resolve_termination_callback) + LogToFD(fd, "%s DNSServiceResolve 0x%08X %2d %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name); + else if (req->terminate == queryrecord_termination_callback) + LogToFD(fd, "%s DNSServiceQueryRecord 0x%08X %2d %##s (%s) PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, QueryRecordClientRequestGetQName(&req->u.queryrecord), DNSTypeName(QueryRecordClientRequestGetType(&req->u.queryrecord)), req->process_id, req->pid_name); + else if (req->terminate == enum_termination_callback) + LogToFD(fd, "%s DNSServiceEnumerateDomains 0x%08X %2d %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.enumeration.q_all.qname.c, req->process_id, req->pid_name); + else if (req->terminate == port_mapping_termination_callback) + LogToFD(fd, "%s DNSServiceNATPortMapping 0x%08X %2d %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)", + prefix, + req->flags, + req->interfaceIndex, + req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", + req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", + mDNSVal16(req->u.pm.NATinfo.IntPort), + mDNSVal16(req->u.pm.ReqExt), + &req->u.pm.NATinfo.ExternalAddress, + mDNSVal16(req->u.pm.NATinfo.ExternalPort), + req->u.pm.NATinfo.NATLease, + req->u.pm.NATinfo.Lifetime, + req->process_id, req->pid_name); + else if (req->terminate == addrinfo_termination_callback) + LogToFD(fd, "%s DNSServiceGetAddrInfo 0x%08X %2d %s%s %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, + req->u.addrinfo.protocols & kDNSServiceProtocol_IPv4 ? "v4" : " ", + req->u.addrinfo.protocols & kDNSServiceProtocol_IPv6 ? "v6" : " ", + GetAddrInfoClientRequestGetQName(&req->u.addrinfo), req->process_id, req->pid_name); else - mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); + LogToFD(fd, "%s Unrecognized operation %p", prefix, req->terminate); +} + +mDNSlocal void LogClientInfo(request_state *req) +{ + char reqIDStr[14]; + char prefix[18]; + + mDNS_snprintf(reqIDStr, sizeof(reqIDStr), "[R%u]", req->request_id); + + mDNS_snprintf(prefix, sizeof(prefix), "%-6s %2s", reqIDStr, req->primary ? "->" : ""); if (!req->terminate) - LogMsgNoIdent("%s No operation yet on this socket", prefix); + LogMsgNoIdent("%s No operation yet on this socket", prefix); else if (req->terminate == connection_termination) { int num_records = 0, num_ops = 0; @@ -5252,63 +5371,61 @@ mDNSlocal void LogClientInfo(request_state *req) for (p = req->u.reg_recs; p; p=p->next) num_records++; for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", - prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", - req->process_id, req->pid_name); + prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", + req->process_id, req->pid_name); for (p = req->u.reg_recs; p; p=p->next) - LogMsgNoIdent(" -> DNSServiceRegisterRecord 0x%08X %2d %3d %s PID[%d](%s)", - req->flags, req->interfaceIndex, p->key, ARDisplayString(&mDNSStorage, p->rr), req->process_id, req->pid_name); + LogMsgNoIdent(" -> DNSServiceRegisterRecord 0x%08X %2d %3d %s PID[%d](%s)", + req->flags, req->interfaceIndex, p->key, ARDisplayString(&mDNSStorage, p->rr), req->process_id, req->pid_name); for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(r); } else if (req->terminate == regservice_termination_callback) { service_instance *ptr; - char anonstr[256]; for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) - LogMsgNoIdent("%s DNSServiceRegister 0x%08X %2d %##s%s %u/%u PID[%d](%s)", - (ptr == req->u.servicereg.instances) ? prefix : " ", req->flags, req->interfaceIndex, ptr->srs.RR_SRV.resrec.name->c, - AnonDataToString(ptr->srs.AnonData, 0, anonstr, sizeof(anonstr)), mDNSVal16(req->u.servicereg.port), - SRS_PORT(&ptr->srs), req->process_id, req->pid_name); + LogMsgNoIdent("%-9s DNSServiceRegister 0x%08X %2d %##s %u/%u PID[%d](%s)", + (ptr == req->u.servicereg.instances) ? prefix : "", req->flags, req->interfaceIndex, ptr->srs.RR_SRV.resrec.name->c, + mDNSVal16(req->u.servicereg.port), + SRS_PORT(&ptr->srs), req->process_id, req->pid_name); } else if (req->terminate == browse_termination_callback) { browser_t *blist; - char anonstr[256]; for (blist = req->u.browser.browsers; blist; blist = blist->next) - LogMsgNoIdent("%s DNSServiceBrowse 0x%08X %2d %##s%s PID[%d](%s)", - (blist == req->u.browser.browsers) ? prefix : " ", req->flags, req->interfaceIndex, blist->q.qname.c, - AnonDataToString(req->u.browser.AnonData, 0, anonstr, sizeof(anonstr)), req->process_id, req->pid_name); + LogMsgNoIdent("%-9s DNSServiceBrowse 0x%08X %2d %##s PID[%d](%s)", + (blist == req->u.browser.browsers) ? prefix : "", req->flags, req->interfaceIndex, blist->q.qname.c, + req->process_id, req->pid_name); } else if (req->terminate == resolve_termination_callback) - LogMsgNoIdent("%s DNSServiceResolve 0x%08X %2d %##s PID[%d](%s)", - prefix, req->flags, req->interfaceIndex, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceResolve 0x%08X %2d %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name); else if (req->terminate == queryrecord_termination_callback) - LogMsgNoIdent("%s DNSServiceQueryRecord 0x%08X %2d %##s (%s) PID[%d](%s)", - prefix, req->flags, req->interfaceIndex, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceQueryRecord 0x%08X %2d %##s (%s) PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, QueryRecordClientRequestGetQName(&req->u.queryrecord), DNSTypeName(QueryRecordClientRequestGetType(&req->u.queryrecord)), req->process_id, req->pid_name); else if (req->terminate == enum_termination_callback) - LogMsgNoIdent("%s DNSServiceEnumerateDomains 0x%08X %2d %##s PID[%d](%s)", - prefix, req->flags, req->interfaceIndex, req->u.enumeration.q_all.qname.c, req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceEnumerateDomains 0x%08X %2d %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.enumeration.q_all.qname.c, req->process_id, req->pid_name); else if (req->terminate == port_mapping_termination_callback) - LogMsgNoIdent("%s DNSServiceNATPortMapping 0x%08X %2d %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)", - prefix, - req->flags, - req->interfaceIndex, - req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", - req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", - mDNSVal16(req->u.pm.NATinfo.IntPort), - mDNSVal16(req->u.pm.ReqExt), - &req->u.pm.NATinfo.ExternalAddress, - mDNSVal16(req->u.pm.NATinfo.ExternalPort), - req->u.pm.NATinfo.NATLease, - req->u.pm.NATinfo.Lifetime, - req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceNATPortMapping 0x%08X %2d %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)", + prefix, + req->flags, + req->interfaceIndex, + req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", + req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", + mDNSVal16(req->u.pm.NATinfo.IntPort), + mDNSVal16(req->u.pm.ReqExt), + &req->u.pm.NATinfo.ExternalAddress, + mDNSVal16(req->u.pm.NATinfo.ExternalPort), + req->u.pm.NATinfo.NATLease, + req->u.pm.NATinfo.Lifetime, + req->process_id, req->pid_name); else if (req->terminate == addrinfo_termination_callback) - LogMsgNoIdent("%s DNSServiceGetAddrInfo 0x%08X %2d %s%s %##s PID[%d](%s)", - prefix, req->flags, req->interfaceIndex, - req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", - req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", - req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceGetAddrInfo 0x%08X %2d %s%s %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, + req->u.addrinfo.protocols & kDNSServiceProtocol_IPv4 ? "v4" : " ", + req->u.addrinfo.protocols & kDNSServiceProtocol_IPv6 ? "v6" : " ", + GetAddrInfoClientRequestGetQName(&req->u.addrinfo), req->process_id, req->pid_name); else - LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate); + LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate); } mDNSlocal void GetMcastClients(request_state *req) @@ -5357,12 +5474,12 @@ mDNSlocal void GetMcastClients(request_state *req) } else if (req->terminate == queryrecord_termination_callback) { - if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0)) + if (QueryRecordClientRequestIsMulticast(&req->u.queryrecord)) n_mquests++; } else if (req->terminate == addrinfo_termination_callback) { - if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0)) + if (GetAddrInfoClientRequestIsMulticast(&req->u.addrinfo)) n_mquests++; } else @@ -5424,23 +5541,24 @@ mDNSlocal void LogMcastClientInfo(request_state *req) } else if (req->terminate == queryrecord_termination_callback) { - if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0)) - LogMcastNoIdent("Q: DNSServiceQueryRecord %##s %s PID[%d](%s)", req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), + if (QueryRecordClientRequestIsMulticast(&req->u.queryrecord)) + { + LogMcastNoIdent("Q: DNSServiceQueryRecord %##s %s PID[%d](%s)", + QueryRecordClientRequestGetQName(&req->u.queryrecord), + DNSTypeName(QueryRecordClientRequestGetType(&req->u.queryrecord)), req->process_id, req->pid_name, i_mcount++); + } } else if (req->terminate == addrinfo_termination_callback) { - if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0)) + if (GetAddrInfoClientRequestIsMulticast(&req->u.addrinfo)) + { LogMcastNoIdent("Q: DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)", - req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", - req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", - req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name, i_mcount++); - } - else - { - return; + req->u.addrinfo.protocols & kDNSServiceProtocol_IPv4 ? "v4" : " ", + req->u.addrinfo.protocols & kDNSServiceProtocol_IPv6 ? "v6" : " ", + GetAddrInfoClientRequestGetQName(&req->u.addrinfo), req->process_id, req->pid_name, i_mcount++); + } } - } mDNSlocal char *RecordTypeName(mDNSu8 rtype) @@ -5458,7 +5576,7 @@ mDNSlocal char *RecordTypeName(mDNSu8 rtype) } } -mDNSlocal int LogEtcHosts(mDNS *const m) +mDNSlocal int LogEtcHostsToFD(int fd, mDNS *const m) { mDNSBool showheader = mDNStrue; const AuthRecord *ar; @@ -5475,29 +5593,29 @@ mDNSlocal int LogEtcHosts(mDNS *const m) for (ar = ag->members; ar; ar = ar->next) { if (ar->RecordCallback != FreeEtcHosts) continue; - if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } + if (showheader) { showheader = mDNSfalse; LogToFD(fd, " State Interface"); } // Print a maximum of 50 records if (count++ >= 50) { truncated = mDNStrue; continue; } if (ar->ARType == AuthRecordLocalOnly) { if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly) - LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + LogToFD(fd, " %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); else { mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID; - LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar)); + LogToFD(fd, " %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar)); } } } } - if (showheader) LogMsgNoIdent("<None>"); - else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot); + if (showheader) LogToFD(fd, "<None>"); + else if (truncated) LogToFD(fd, "<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot); return count; } -mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m) +mDNSlocal void LogLocalOnlyAuthRecordsToFD(int fd, mDNS *const m) { mDNSBool showheader = mDNStrue; const AuthRecord *ar; @@ -5510,62 +5628,52 @@ mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m) for (ar = ag->members; ar; ar = ar->next) { if (ar->RecordCallback == FreeEtcHosts) continue; - if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } + if (showheader) { showheader = mDNSfalse; LogToFD(fd, " State Interface"); } // Print a maximum of 400 records if (ar->ARType == AuthRecordLocalOnly) - LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + LogToFD(fd, " %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); else if (ar->ARType == AuthRecordP2P) { if (ar->resrec.InterfaceID == mDNSInterface_BLE) - LogMsgNoIdent(" %s BLE %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + LogToFD(fd, " %s BLE %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); else - LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + LogToFD(fd, " %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); } } } - if (showheader) LogMsgNoIdent("<None>"); + if (showheader) LogToFD(fd, "<None>"); } -mDNSlocal char *AnonInfoToString(AnonymousInfo *ai, char *anonstr, int anstrlen) +mDNSlocal void LogOneAuthRecordToFD(int fd, const AuthRecord *ar, mDNSs32 now, const char *ifname) { - anonstr[0] = 0; - if (ai && ai->AnonData) - { - return (AnonDataToString(ai->AnonData, ai->AnonDataLen, anonstr, anstrlen)); - } - return anonstr; -} - -mDNSlocal void LogOneAuthRecord(const AuthRecord *ar, mDNSs32 now, const char *const ifname) -{ - char anstr[256]; if (AuthRecord_uDNS(ar)) { - LogMsgNoIdent("%7d %7d %7d %-7s %4d %s %s", - ar->ThisAPInterval / mDNSPlatformOneSecond, - (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, - ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, - "-U-", - ar->state, - ar->AllowRemoteQuery ? "☠" : " ", - ARDisplayString(&mDNSStorage, ar)); + LogToFD(fd, "%7d %7d %7d %-7s %4d %s %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, + ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, + "-U-", + ar->state, + ar->AllowRemoteQuery ? "☠" : " ", + ARDisplayString(&mDNSStorage, ar)); } else { - LogMsgNoIdent("%7d %7d %7d %-7s 0x%02X %s %s%s", - ar->ThisAPInterval / mDNSPlatformOneSecond, - ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, - ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, - ifname ? ifname : "ALL", - ar->resrec.RecordType, - ar->AllowRemoteQuery ? "☠" : " ", - ARDisplayString(&mDNSStorage, ar), AnonInfoToString(ar->resrec.AnonInfo, anstr, sizeof(anstr))); + LogToFD(fd, "%7d %7d %7d %-7s 0x%02X %s %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, + ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, + ifname ? ifname : "ALL", + ar->resrec.RecordType, + ar->AllowRemoteQuery ? "☠" : " ", + ARDisplayString(&mDNSStorage, ar)); } } -mDNSlocal void LogAuthRecords(const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy) +mDNSlocal void LogAuthRecordsToFD(int fd, + const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy) { mDNSBool showheader = mDNStrue; const AuthRecord *ar; @@ -5575,169 +5683,102 @@ mDNSlocal void LogAuthRecords(const mDNSs32 now, AuthRecord *ResourceRecords, in const char *const ifname = InterfaceNameForID(&mDNSStorage, ar->resrec.InterfaceID); if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL)) { - if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire if State"); } + if (showheader) { showheader = mDNSfalse; LogToFD(fd, " Int Next Expire if State"); } if (proxy) (*proxy)++; if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner))) { owner = ar->WakeUp; if (owner.password.l[0]) - LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); + LogToFD(fd, "Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC)) - LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq); + LogToFD(fd, "Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq); else - LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq); + LogToFD(fd, "Proxying for %.6a seq %d", &owner.HMAC, owner.seq); } if (AuthRecord_uDNS(ar)) { - LogOneAuthRecord(ar, now, ifname); + LogOneAuthRecordToFD(fd, ar, now, ifname); } else if (ar->ARType == AuthRecordLocalOnly) { - LogMsgNoIdent(" LO %s", ARDisplayString(&mDNSStorage, ar)); + LogToFD(fd, " LO %s", ARDisplayString(&mDNSStorage, ar)); } else if (ar->ARType == AuthRecordP2P) { if (ar->resrec.InterfaceID == mDNSInterface_BLE) - LogMsgNoIdent(" BLE %s", ARDisplayString(&mDNSStorage, ar)); + LogToFD(fd, " BLE %s", ARDisplayString(&mDNSStorage, ar)); else - LogMsgNoIdent(" PP %s", ARDisplayString(&mDNSStorage, ar)); + LogToFD(fd, " PP %s", ARDisplayString(&mDNSStorage, ar)); } else { - LogOneAuthRecord(ar, now, ifname); - if (ar->resrec.AnonInfo) - { - ResourceRecord *nsec3 = ar->resrec.AnonInfo->nsec3RR; - // We just print the values from the AuthRecord to keep it nicely aligned though - // all we want here is the nsec3 information. - LogMsgNoIdent("%7d %7d %7d %7s %s", - ar->ThisAPInterval / mDNSPlatformOneSecond, - ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, - ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, - ifname ? ifname : "ALL", - RRDisplayString(&mDNSStorage, nsec3)); - } + LogOneAuthRecordToFD(fd, ar, now, ifname); } } } - if (showheader) LogMsgNoIdent("<None>"); + if (showheader) LogToFD(fd, "<None>"); } -mDNSlocal void PrintOneCacheRecord(const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) +mDNSlocal void PrintOneCacheRecordToFD(int fd, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) { - LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", - slot, - cr->CRActiveQuestion ? "*" : " ", - remain, - ifname ? ifname : "-U-", - (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : - (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", - DNSTypeName(cr->resrec.rrtype), - CRDisplayString(&mDNSStorage, cr)); + LogToFD(fd, "%3d %s%8d %-7s%s %-6s%s", + slot, + cr->CRActiveQuestion ? "*" : " ", + remain, + ifname ? ifname : "-U-", + (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : + (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", + DNSTypeName(cr->resrec.rrtype), + CRDisplayString(&mDNSStorage, cr)); (*CacheUsed)++; } -mDNSlocal void PrintCachedRecords(const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) +mDNSlocal void PrintCachedRecordsToFD(int fd, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) { - CacheRecord *nsec; CacheRecord *soa; - nsec = cr->nsec; - // The records that are cached under the main cache record like nsec, soa don't have - // their own lifetime. If the main cache record expires, they also expire. - while (nsec) - { - PrintOneCacheRecord(nsec, slot, remain, ifname, CacheUsed); - nsec = nsec->next; - } soa = cr->soa; if (soa) { - PrintOneCacheRecord(soa, slot, remain, ifname, CacheUsed); - } - if (cr->resrec.AnonInfo) - { - ResourceRecord *nsec3 = cr->resrec.AnonInfo->nsec3RR; - // Even though it is a resource record, we print the sameway - // as a cache record so that it aligns properly. - if (nsec3) - { - LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", - slot, - " ", - remain, - ifname ? ifname : "-U-", - (nsec3->RecordType == kDNSRecordTypePacketNegative) ? "-" : - (nsec3->RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", - DNSTypeName(nsec3->rrtype), - RRDisplayString(&mDNSStorage, nsec3)); - } + PrintOneCacheRecordToFD(fd, soa, slot, remain, ifname, CacheUsed); } } -mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen) +mDNSexport void LogMDNSStatisticsToFD(int fd, mDNS *const m) { - adstr[0] = 0; - if (ad) - { - int len; - char *orig = adstr; + LogToFD(fd, "--- MDNS Statistics ---"); + + LogToFD(fd, "Name Conflicts %u", m->mDNSStats.NameConflicts); + LogToFD(fd, "KnownUnique Name Conflicts %u", m->mDNSStats.KnownUniqueNameConflicts); + LogToFD(fd, "Duplicate Query Suppressions %u", m->mDNSStats.DupQuerySuppressions); + LogToFD(fd, "KA Suppressions %u", m->mDNSStats.KnownAnswerSuppressions); + LogToFD(fd, "KA Multiple Packets %u", m->mDNSStats.KnownAnswerMultiplePkts); + LogToFD(fd, "Poof Cache Deletions %u", m->mDNSStats.PoofCacheDeletions); + LogToFD(fd, "--------------------------------"); + + LogToFD(fd, "Multicast packets Sent %u", m->MulticastPacketsSent); + LogToFD(fd, "Multicast packets Received %u", m->MPktNum); + LogToFD(fd, "Remote Subnet packets %u", m->RemoteSubnet); + LogToFD(fd, "QU questions received %u", m->mDNSStats.UnicastBitInQueries); + LogToFD(fd, "Normal multicast questions %u", m->mDNSStats.NormalQueries); + LogToFD(fd, "Answers for questions %u", m->mDNSStats.MatchingAnswersForQueries); + LogToFD(fd, "Unicast responses %u", m->mDNSStats.UnicastResponses); + LogToFD(fd, "Multicast responses %u", m->mDNSStats.MulticastResponses); + LogToFD(fd, "Unicast response Demotions %u", m->mDNSStats.UnicastDemotedToMulticast); + LogToFD(fd, "--------------------------------"); + + LogToFD(fd, "Sleeps %u", m->mDNSStats.Sleeps); + LogToFD(fd, "Wakeups %u", m->mDNSStats.Wakes); + LogToFD(fd, "Interface UP events %u", m->mDNSStats.InterfaceUp); + LogToFD(fd, "Interface UP Flap events %u", m->mDNSStats.InterfaceUpFlap); + LogToFD(fd, "Interface Down events %u", m->mDNSStats.InterfaceDown); + LogToFD(fd, "Interface DownFlap events %u", m->mDNSStats.InterfaceDownFlap); + LogToFD(fd, "Cache refresh queries %u", m->mDNSStats.CacheRefreshQueries); + LogToFD(fd, "Cache refreshed %u", m->mDNSStats.CacheRefreshed); + LogToFD(fd, "Wakeup on Resolves %u", m->mDNSStats.WakeOnResolves); +} - // If the caller is lazy to compute the length, we do it for them. - if (!adlen) - len = strlen((const char *)ad); - else - len = adlen; - - // Print the anondata within brackets. Hence, we need space for two - // brackets and a NULL byte. - if (len > (adstrlen - 3)) - len = adstrlen - 3; - - *adstr++ = '('; - mDNSPlatformMemCopy(adstr, ad, len); - adstr[len] = ')'; - adstr[len+1] = 0; - return orig; - } - return adstr; -} - -mDNSexport void LogMDNSStatistics(mDNS *const m) -{ - LogMsgNoIdent("--- MDNS Statistics ---"); - - LogMsgNoIdent("Name Conflicts %u", m->mDNSStats.NameConflicts); - LogMsgNoIdent("KnownUnique Name Conflicts %u", m->mDNSStats.KnownUniqueNameConflicts); - LogMsgNoIdent("Duplicate Query Suppressions %u", m->mDNSStats.DupQuerySuppressions); - LogMsgNoIdent("KA Suppressions %u", m->mDNSStats.KnownAnswerSuppressions); - LogMsgNoIdent("KA Multiple Packets %u", m->mDNSStats.KnownAnswerMultiplePkts); - LogMsgNoIdent("Poof Cache Deletions %u", m->mDNSStats.PoofCacheDeletions); - LogMsgNoIdent("--------------------------------"); - - LogMsgNoIdent("Multicast packets Sent %u", m->MulticastPacketsSent); - LogMsgNoIdent("Multicast packets Received %u", m->MPktNum); - LogMsgNoIdent("Remote Subnet packets %u", m->RemoteSubnet); - LogMsgNoIdent("QU questions received %u", m->mDNSStats.UnicastBitInQueries); - LogMsgNoIdent("Normal multicast questions %u", m->mDNSStats.NormalQueries); - LogMsgNoIdent("Answers for questions %u", m->mDNSStats.MatchingAnswersForQueries); - LogMsgNoIdent("Unicast responses %u", m->mDNSStats.UnicastResponses); - LogMsgNoIdent("Multicast responses %u", m->mDNSStats.MulticastResponses); - LogMsgNoIdent("Unicast response Demotions %u", m->mDNSStats.UnicastDemotedToMulticast); - LogMsgNoIdent("--------------------------------"); - - LogMsgNoIdent("Sleeps %u", m->mDNSStats.Sleeps); - LogMsgNoIdent("Wakeups %u", m->mDNSStats.Wakes); - LogMsgNoIdent("Interface UP events %u", m->mDNSStats.InterfaceUp); - LogMsgNoIdent("Interface UP Flap events %u", m->mDNSStats.InterfaceUpFlap); - LogMsgNoIdent("Interface Down events %u", m->mDNSStats.InterfaceDown); - LogMsgNoIdent("Interface DownFlap events %u", m->mDNSStats.InterfaceDownFlap); - LogMsgNoIdent("Cache refresh queries %u", m->mDNSStats.CacheRefreshQueries); - LogMsgNoIdent("Cache refreshed %u", m->mDNSStats.CacheRefreshed); - LogMsgNoIdent("Wakeup on Resolves %u", m->mDNSStats.WakeOnResolves); -} - -mDNSexport void udsserver_info() +mDNSexport void udsserver_info_dump_to_fd(int fd) { mDNS *const m = &mDNSStorage; const mDNSs32 now = mDNS_TimeNow(m); @@ -5752,10 +5793,8 @@ mDNSexport void udsserver_info() const DNameListElem *d; const SearchListElem *s; - LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); - - LogMsgNoIdent("------------ Cache -------------"); - LogMsgNoIdent("Slt Q TTL if U Type rdlen"); + LogToFD(fd, "------------ Cache -------------"); + LogToFD(fd, "Slt Q TTL if U Type rdlen"); for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) { for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) @@ -5767,50 +5806,57 @@ mDNSexport void udsserver_info() const char *ifname; mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID; mDNSu32 *const countPtr = InterfaceID ? &mcastRecordCount : &ucastRecordCount; - if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scoped) +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + if (!InterfaceID && cr->resrec.dnsservice && + (mdns_dns_service_get_scope(cr->resrec.dnsservice) == mdns_dns_service_scope_interface)) + { + InterfaceID = (mDNSInterfaceID)(uintptr_t)mdns_dns_service_get_interface_index(cr->resrec.dnsservice); + } +#else + if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scopeType) InterfaceID = cr->resrec.rDNSServer->interface; +#endif ifname = InterfaceNameForID(m, InterfaceID); if (cr->CRActiveQuestion) CacheActive++; - PrintOneCacheRecord(cr, slot, remain, ifname, countPtr); - PrintCachedRecords(cr, slot, remain, ifname, countPtr); + PrintOneCacheRecordToFD(fd, cr, slot, remain, ifname, countPtr); + PrintCachedRecordsToFD(fd, cr, slot, remain, ifname, countPtr); } } } CacheUsed = groupCount + mcastRecordCount + ucastRecordCount; if (m->rrcache_totalused != CacheUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); + LogToFD(fd, "Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); if (m->rrcache_active != CacheActive) - LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); - LogMsgNoIdent("Cache size %u entities; %u in use (%u group, %u multicast, %u unicast); %u referenced by active questions", - m->rrcache_size, CacheUsed, groupCount, mcastRecordCount, ucastRecordCount, CacheActive); + LogToFD(fd, "Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); + LogToFD(fd, "Cache size %u entities; %u in use (%u group, %u multicast, %u unicast); %u referenced by active questions", + m->rrcache_size, CacheUsed, groupCount, mcastRecordCount, ucastRecordCount, CacheActive); - LogMsgNoIdent("--------- Auth Records ---------"); - LogAuthRecords(now, m->ResourceRecords, mDNSNULL); + LogToFD(fd, "--------- Auth Records ---------"); + LogAuthRecordsToFD(fd, now, m->ResourceRecords, mDNSNULL); - LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------"); - LogLocalOnlyAuthRecords(m); + LogToFD(fd, "--------- LocalOnly, P2P Auth Records ---------"); + LogLocalOnlyAuthRecordsToFD(fd, m); - LogMsgNoIdent("--------- /etc/hosts ---------"); - LogEtcHosts(m); + LogToFD(fd, "--------- /etc/hosts ---------"); + LogEtcHostsToFD(fd, m); - LogMsgNoIdent("------ Duplicate Records -------"); - LogAuthRecords(now, m->DuplicateRecords, mDNSNULL); + LogToFD(fd, "------ Duplicate Records -------"); + LogAuthRecordsToFD(fd, now, m->DuplicateRecords, mDNSNULL); - LogMsgNoIdent("----- Auth Records Proxied -----"); - LogAuthRecords(now, m->ResourceRecords, &ProxyA); + LogToFD(fd, "----- Auth Records Proxied -----"); + LogAuthRecordsToFD(fd, now, m->ResourceRecords, &ProxyA); - LogMsgNoIdent("-- Duplicate Records Proxied ---"); - LogAuthRecords(now, m->DuplicateRecords, &ProxyD); + LogToFD(fd, "-- Duplicate Records Proxied ---"); + LogAuthRecordsToFD(fd, now, m->DuplicateRecords, &ProxyD); - LogMsgNoIdent("---------- Questions -----------"); - if (!m->Questions) LogMsgNoIdent("<None>"); + LogToFD(fd, "---------- Questions -----------"); + if (!m->Questions) LogToFD(fd, "<None>"); else { - char anonstr[256]; CacheUsed = 0; CacheActive = 0; - LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name"); + LogToFD(fd, " Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name"); for (q = m->Questions; q; q=q->next) { mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond; @@ -5818,29 +5864,34 @@ mDNSexport void udsserver_info() char *ifname = InterfaceNameForID(m, q->InterfaceID); CacheUsed++; if (q->ThisQInterval) CacheActive++; - LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s%s", - i, n, - ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", - mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), - PrivateQuery(q) ? "P" : q->ValidationRequired ? "V" : q->ValidatingResponse ? "R" : " ", - q->CurrentAnswers, q->validDNSServers.l[3], q->validDNSServers.l[2], q->validDNSServers.l[1], - q->validDNSServers.l[0], q, q->DuplicateOf, - q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, - AnonInfoToString(q->AnonInfo, anonstr, sizeof(anonstr)), - q->DuplicateOf ? " (dup)" : ""); - } - LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); - } - - LogMsgNoIdent("----- LocalOnly, P2P Questions -----"); - if (!m->LocalOnlyQuestions) LogMsgNoIdent("<None>"); +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + LogToFD(fd, "%6d%6d %-7s%s %5d 0x%p 0x%p %1d %2d %-5s%##s%s", +#else + LogToFD(fd, "%6d%6d %-7s%s %5d 0x%08x%08x%08x%08x 0x%p 0x%p %1d %2d %-5s%##s%s", +#endif + i, n, + ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", + mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), + q->CurrentAnswers, +#if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + q->validDNSServers.l[3], q->validDNSServers.l[2], q->validDNSServers.l[1], q->validDNSServers.l[0], +#endif + q, q->DuplicateOf, + q->SuppressUnusable, q->Suppressed, DNSTypeName(q->qtype), q->qname.c, + q->DuplicateOf ? " (dup)" : ""); + } + LogToFD(fd, "%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); + } + + LogToFD(fd, "----- LocalOnly, P2P Questions -----"); + if (!m->LocalOnlyQuestions) LogToFD(fd, "<None>"); else for (q = m->LocalOnlyQuestions; q; q=q->next) - LogMsgNoIdent(" %3s %5d %-6s%##s%s", - q->InterfaceID == mDNSInterface_LocalOnly ? "LO ": q->InterfaceID == mDNSInterface_BLE ? "BLE": "P2P", - q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); + LogToFD(fd, " %3s %5d %-6s%##s%s", + q->InterfaceID == mDNSInterface_LocalOnly ? "LO ": q->InterfaceID == mDNSInterface_BLE ? "BLE": "P2P", + q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); - LogMsgNoIdent("---- Active UDS Client Requests ----"); - if (!all_requests) LogMsgNoIdent("<None>"); + LogToFD(fd, "---- Active UDS Client Requests ----"); + if (!all_requests) LogToFD(fd, "<None>"); else { request_state *req, *r; @@ -5849,231 +5900,174 @@ mDNSexport void udsserver_info() if (req->primary) // If this is a subbordinate operation, check that the parent is in the list { for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent; - LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd); + LogToFD(fd, "%3d: Orhpan operation %p; parent %p not found in request list", req->sd); } // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info - LogClientInfo(req); -foundparent:; + LogClientInfoToFD(fd, req); + foundparent:; } } - LogMsgNoIdent("-------- NAT Traversals --------"); - LogMsgNoIdent("ExtAddress %.4a Retry %d Interval %d", - &m->ExtAddress, - m->retryGetAddr ? (m->retryGetAddr - now) / mDNSPlatformOneSecond : 0, - m->retryIntervalGetAddr / mDNSPlatformOneSecond); + LogToFD(fd, "-------- NAT Traversals --------"); + LogToFD(fd, "ExtAddress %.4a Retry %d Interval %d", + &m->ExtAddress, + m->retryGetAddr ? (m->retryGetAddr - now) / mDNSPlatformOneSecond : 0, + m->retryIntervalGetAddr / mDNSPlatformOneSecond); if (m->NATTraversals) { const NATTraversalInfo *nat; for (nat = m->NATTraversals; nat; nat=nat->next) { - LogMsgNoIdent("%p %s Int %5d %s Err %d Retry %5d Interval %5d Expire %5d Req %.4a:%d Ext %.4a:%d", - nat, - nat->Protocol ? (nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP") : "ADD", - mDNSVal16(nat->IntPort), - (nat->lastSuccessfulProtocol == NATTProtocolNone ? "None " : - nat->lastSuccessfulProtocol == NATTProtocolNATPMP ? "NAT-PMP " : - nat->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" : - nat->lastSuccessfulProtocol == NATTProtocolPCP ? "PCP " : - /* else */ "Unknown " ), - nat->Result, - nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, - nat->retryInterval / mDNSPlatformOneSecond, - nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, - &nat->NewAddress, mDNSVal16(nat->RequestedPort), - &nat->ExternalAddress, mDNSVal16(nat->ExternalPort)); - } - } - - LogMsgNoIdent("--------- AuthInfoList ---------"); - if (!m->AuthInfoList) LogMsgNoIdent("<None>"); + LogToFD(fd, "%p %s Int %5d %s Err %d Retry %5d Interval %5d Expire %5d Req %.4a:%d Ext %.4a:%d", + nat, + nat->Protocol ? (nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP") : "ADD", + mDNSVal16(nat->IntPort), + (nat->lastSuccessfulProtocol == NATTProtocolNone ? "None " : + nat->lastSuccessfulProtocol == NATTProtocolNATPMP ? "NAT-PMP " : + nat->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" : + nat->lastSuccessfulProtocol == NATTProtocolPCP ? "PCP " : + /* else */ "Unknown " ), + nat->Result, + nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, + nat->retryInterval / mDNSPlatformOneSecond, + nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, + &nat->NewAddress, mDNSVal16(nat->RequestedPort), + &nat->ExternalAddress, mDNSVal16(nat->ExternalPort)); + } + } + + LogToFD(fd, "--------- AuthInfoList ---------"); + if (!m->AuthInfoList) LogToFD(fd, "<None>"); else { const DomainAuthInfo *a; for (a = m->AuthInfoList; a; a = a->next) { - LogMsgNoIdent("%##s %##s %##s %d %d %.16a%s", - a->domain.c, a->keyname.c, - a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), - (a->deltime ? (a->deltime - now) : 0), - &a->AutoTunnelInnerAddress, a->AutoTunnel ? " AutoTunnel" : ""); + LogToFD(fd, "%##s %##s %##s %d %d", + a->domain.c, a->keyname.c, + a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), + (a->deltime ? (a->deltime - now) : 0)); } } - #if APPLE_OSX_mDNSResponder - LogMsgNoIdent("--------- TunnelClients --------"); - if (!m->TunnelClients) LogMsgNoIdent("<None>"); - else - { - const ClientTunnel *c; - for (c = m->TunnelClients; c; c = c->next) - LogMsgNoIdent("%##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d", - c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval); - } - #endif // APPLE_OSX_mDNSResponder - - LogMsgNoIdent("---------- Misc State ----------"); + LogToFD(fd, "---------- Misc State ----------"); - LogMsgNoIdent("PrimaryMAC: %.6a", &m->PrimaryMAC); + LogToFD(fd, "PrimaryMAC: %.6a", &m->PrimaryMAC); - LogMsgNoIdent("m->SleepState %d (%s) seq %d", - m->SleepState, - m->SleepState == SleepState_Awake ? "Awake" : - m->SleepState == SleepState_Transferring ? "Transferring" : - m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", - m->SleepSeqNum); + LogToFD(fd, "m->SleepState %d (%s) seq %d", + m->SleepState, + m->SleepState == SleepState_Awake ? "Awake" : + m->SleepState == SleepState_Transferring ? "Transferring" : + m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", + m->SleepSeqNum); - if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service"); + if (!m->SPSSocket) LogToFD(fd, "Not offering Sleep Proxy Service"); #ifndef SPC_DISABLED - else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c); + else LogToFD(fd, "Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c); #endif - if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD); - else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords); + if (m->ProxyRecords == ProxyA + ProxyD) LogToFD(fd, "ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD); + else LogToFD(fd, "ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords); - LogMsgNoIdent("------ Auto Browse Domains -----"); - if (!AutoBrowseDomains) LogMsgNoIdent("<None>"); - else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); + LogToFD(fd, "------ Auto Browse Domains -----"); + if (!AutoBrowseDomains) LogToFD(fd, "<None>"); + else for (d=AutoBrowseDomains; d; d=d->next) LogToFD(fd, "%##s", d->name.c); - LogMsgNoIdent("--- Auto Registration Domains --"); - if (!AutoRegistrationDomains) LogMsgNoIdent("<None>"); - else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); + LogToFD(fd, "--- Auto Registration Domains --"); + if (!AutoRegistrationDomains) LogToFD(fd, "<None>"); + else for (d=AutoRegistrationDomains; d; d=d->next) LogToFD(fd, "%##s", d->name.c); - LogMsgNoIdent("--- Search Domains --"); - if (!SearchList) LogMsgNoIdent("<None>"); + LogToFD(fd, "--- Search Domains --"); + if (!SearchList) LogToFD(fd, "<None>"); else { for (s=SearchList; s; s=s->next) { char *ifname = InterfaceNameForID(m, s->InterfaceID); - LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : ""); - } - } - LogInfo("--- Trust Anchors ---"); - if (!m->TrustAnchors) - { - LogInfo("<None>"); - } - else - { - TrustAnchor *ta; - mDNSu8 fromTimeBuf[64]; - mDNSu8 untilTimeBuf[64]; - - for (ta=m->TrustAnchors; ta; ta=ta->next) - { - mDNSPlatformFormatTime((unsigned long)ta->validFrom, fromTimeBuf, sizeof(fromTimeBuf)); - mDNSPlatformFormatTime((unsigned long)ta->validUntil, untilTimeBuf, sizeof(untilTimeBuf)); - LogInfo("%##s %d %d %d %d %s %s", ta->zone.c, ta->rds.keyTag, - ta->rds.alg, ta->rds.digestType, ta->digestLen, fromTimeBuf, untilTimeBuf); + LogToFD(fd, "%##s %s", s->domain.c, ifname ? ifname : ""); } } + LogMDNSStatisticsToFD(fd, m); - LogInfo("--- DNSSEC Statistics ---"); + LogToFD(fd, "---- Task Scheduling Timers ----"); - LogMsgNoIdent("Unicast Cache size %u", m->rrcache_totalused_unicast); - LogInfo("DNSSEC Cache size %u", m->DNSSECStats.TotalMemUsed); - if (m->rrcache_totalused_unicast) - LogInfo("DNSSEC usage percentage %u", ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast); - LogInfo("DNSSEC Extra Packets (0 to 2) %u", m->DNSSECStats.ExtraPackets0); - LogInfo("DNSSEC Extra Packets (3 to 6) %u", m->DNSSECStats.ExtraPackets3); - LogInfo("DNSSEC Extra Packets (7 to 9) %u", m->DNSSECStats.ExtraPackets7); - LogInfo("DNSSEC Extra Packets ( >= 10) %u", m->DNSSECStats.ExtraPackets10); - - LogInfo("DNSSEC Latency (0 to 4ms) %u", m->DNSSECStats.Latency0); - LogInfo("DNSSEC Latency (4 to 9ms) %u", m->DNSSECStats.Latency5); - LogInfo("DNSSEC Latency (10 to 19ms) %u", m->DNSSECStats.Latency10); - LogInfo("DNSSEC Latency (20 to 49ms) %u", m->DNSSECStats.Latency20); - LogInfo("DNSSEC Latency (50 to 99ms) %u", m->DNSSECStats.Latency50); - LogInfo("DNSSEC Latency ( >=100ms) %u", m->DNSSECStats.Latency100); - - LogInfo("DNSSEC Secure Status %u", m->DNSSECStats.SecureStatus); - LogInfo("DNSSEC Insecure Status %u", m->DNSSECStats.InsecureStatus); - LogInfo("DNSSEC Indeterminate Status %u", m->DNSSECStats.IndeterminateStatus); - LogInfo("DNSSEC Bogus Status %u", m->DNSSECStats.BogusStatus); - LogInfo("DNSSEC NoResponse Status %u", m->DNSSECStats.NoResponseStatus); - LogInfo("DNSSEC Probes sent %u", m->DNSSECStats.NumProbesSent); - LogInfo("DNSSEC Msg Size (<=1024) %u", m->DNSSECStats.MsgSize0); - LogInfo("DNSSEC Msg Size (<=2048) %u", m->DNSSECStats.MsgSize1); - LogInfo("DNSSEC Msg Size (> 2048) %u", m->DNSSECStats.MsgSize2); - - LogMDNSStatistics(m); - - LogMsgNoIdent("---- Task Scheduling Timers ----"); - -#if BONJOUR_ON_DEMAND - LogMsgNoIdent("BonjourEnabled %d", m->BonjourEnabled); -#endif // BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) + LogToFD(fd, "BonjourEnabled %d", m->BonjourEnabled); +#endif #if APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR - LogMsgNoIdent("EnableBLEBasedDiscovery %d", EnableBLEBasedDiscovery); - LogMsgNoIdent("DefaultToBLETriggered %d", DefaultToBLETriggered); + LogToFD(fd, "EnableBLEBasedDiscovery %d", EnableBLEBasedDiscovery); + LogToFD(fd, "DefaultToBLETriggered %d", DefaultToBLETriggered); #endif // APPLE_OSX_mDNSResponder && ENABLE_BLE_TRIGGERED_BONJOUR if (!m->NewQuestions) - LogMsgNoIdent("NewQuestion <NONE>"); + LogToFD(fd, "NewQuestion <NONE>"); else - LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)", - m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now, - m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); + LogToFD(fd, "NewQuestion DelayAnswering %d %d %##s (%s)", + m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now, + m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); if (!m->NewLocalOnlyQuestions) - LogMsgNoIdent("NewLocalOnlyQuestions <NONE>"); + LogToFD(fd, "NewLocalOnlyQuestions <NONE>"); else - LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)", - m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); + LogToFD(fd, "NewLocalOnlyQuestions %##s (%s)", + m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); if (!m->NewLocalRecords) - LogMsgNoIdent("NewLocalRecords <NONE>"); + LogToFD(fd, "NewLocalRecords <NONE>"); else - LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords)); - - LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>"); - LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>"); - LogMsgNoIdent("m->AutoTunnelRelayAddr %.16a", &m->AutoTunnelRelayAddr); - LogMsgNoIdent("m->WABBrowseQueriesCount %d", m->WABBrowseQueriesCount); - LogMsgNoIdent("m->WABLBrowseQueriesCount %d", m->WABLBrowseQueriesCount); - LogMsgNoIdent("m->WABRegQueriesCount %d", m->WABRegQueriesCount); - LogMsgNoIdent("m->AutoTargetServices %d", m->AutoTargetServices); + LogToFD(fd, "NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords)); + + LogToFD(fd, "SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>"); + LogToFD(fd, "LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>"); + LogToFD(fd, "m->WABBrowseQueriesCount %d", m->WABBrowseQueriesCount); + LogToFD(fd, "m->WABLBrowseQueriesCount %d", m->WABLBrowseQueriesCount); + LogToFD(fd, "m->WABRegQueriesCount %d", m->WABRegQueriesCount); + LogToFD(fd, "m->AutoTargetServices %u", m->AutoTargetServices); +#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME) + LogToFD(fd, "m->AutoTargetAWDLIncludedCount %u", m->AutoTargetAWDLIncludedCount); + LogToFD(fd, "m->AutoTargetAWDLOnlyCount %u", m->AutoTargetAWDLOnlyCount); +#endif - LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)"); - LogMsgNoIdent("m->timenow %08X %11d", now, now); - LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust); - LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent); + LogToFD(fd, " ABS (hex) ABS (dec) REL (hex) REL (dec)"); + LogToFD(fd, "m->timenow %08X %11d", now, now); + LogToFD(fd, "m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust); + LogTimerToFD(fd, "m->NextScheduledEvent ", m->NextScheduledEvent); #ifndef UNICAST_DISABLED - LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent); - LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate); - LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp); - LogTimer("m->retryGetAddr ", m->retryGetAddr); + LogTimerToFD(fd, "m->NextuDNSEvent ", m->NextuDNSEvent); + LogTimerToFD(fd, "m->NextSRVUpdate ", m->NextSRVUpdate); + LogTimerToFD(fd, "m->NextScheduledNATOp ", m->NextScheduledNATOp); + LogTimerToFD(fd, "m->retryGetAddr ", m->retryGetAddr); #endif - LogTimer("m->NextCacheCheck ", m->NextCacheCheck); - LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS); - LogTimer("m->NextScheduledKA ", m->NextScheduledKA); + LogTimerToFD(fd, "m->NextCacheCheck ", m->NextCacheCheck); + LogTimerToFD(fd, "m->NextScheduledSPS ", m->NextScheduledSPS); + LogTimerToFD(fd, "m->NextScheduledKA ", m->NextScheduledKA); -#if BONJOUR_ON_DEMAND - LogTimer("m->NextBonjourDisableTime ", m->NextBonjourDisableTime); -#endif // BONJOUR_ON_DEMAND +#if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND) + LogTimerToFD(fd, "m->NextBonjourDisableTime ", m->NextBonjourDisableTime); +#endif - LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry); - LogTimer("m->DelaySleep ", m->DelaySleep); + LogTimerToFD(fd, "m->NextScheduledSPRetry ", m->NextScheduledSPRetry); + LogTimerToFD(fd, "m->DelaySleep ", m->DelaySleep); - LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery); - LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe); - LogTimer("m->NextScheduledResponse", m->NextScheduledResponse); + LogTimerToFD(fd, "m->NextScheduledQuery ", m->NextScheduledQuery); + LogTimerToFD(fd, "m->NextScheduledProbe ", m->NextScheduledProbe); + LogTimerToFD(fd, "m->NextScheduledResponse", m->NextScheduledResponse); - LogTimer("m->SuppressSending ", m->SuppressSending); - LogTimer("m->SuppressProbes ", m->SuppressProbes); - LogTimer("m->ProbeFailTime ", m->ProbeFailTime); - LogTimer("m->DelaySleep ", m->DelaySleep); - LogTimer("m->SleepLimit ", m->SleepLimit); - LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime); + LogTimerToFD(fd, "m->SuppressSending ", m->SuppressSending); + LogTimerToFD(fd, "m->SuppressProbes ", m->SuppressProbes); + LogTimerToFD(fd, "m->ProbeFailTime ", m->ProbeFailTime); + LogTimerToFD(fd, "m->DelaySleep ", m->DelaySleep); + LogTimerToFD(fd, "m->SleepLimit ", m->SleepLimit); + LogTimerToFD(fd, "m->NextScheduledStopTime ", m->NextScheduledStopTime); } -#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING -mDNSexport void uds_validatelists(void) +#if MDNS_MALLOC_DEBUGGING +mDNSlocal void udsserver_validatelists(void *context) { const request_state *req, *p; + (void)context; // unused for (req = all_requests; req; req=req->next) { if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2)) @@ -6138,7 +6132,7 @@ mDNSexport void uds_validatelists(void) if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]); } -#endif // APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING +#endif // MDNS_MALLOC_DEBUGGING mDNSlocal int send_msg(request_state *const req) { @@ -6223,6 +6217,8 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; + LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, + "[R%u] Could not send all replies. Will try again in %d ticks.", r->request_id, nextevent - now); if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0; else if (!r->time_blocked) @@ -6264,10 +6260,10 @@ struct CompileTimeAssertionChecks_uds_daemon // Check our structures are reasonable sizes. Including overly-large buffers, or embedding // other overly-large structures instead of having a pointer to them, can inadvertently // cause structure sizes (and therefore memory usage) to balloon unreasonably. - char sizecheck_request_state [(sizeof(request_state) <= 3696) ? 1 : -1]; + char sizecheck_request_state [(sizeof(request_state) <= 3880) ? 1 : -1]; char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1]; char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1]; - char sizecheck_browser_t [(sizeof(browser_t) <= 1432) ? 1 : -1]; + char sizecheck_browser_t [(sizeof(browser_t) <= 1480) ? 1 : -1]; char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1]; char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1]; }; diff --git a/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.h b/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.h index dc7d9ac26b..d9210579fd 100644 --- a/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.h +++ b/usr/src/contrib/mDNSResponder/mDNSShared/uds_daemon.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Inc. All rights reserved. + * Copyright (c) 2002-2020 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,10 @@ #include "mDNSEmbeddedAPI.h" #include "dnssd_ipc.h" +#include "ClientRequests.h" +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) +#include "mdns_private.h" +#endif /* Client request: */ @@ -97,9 +101,15 @@ struct request_state mDNSu8 uuid[UUID_SIZE]; mDNSBool validUUID; dnssd_sock_t errsd; +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) + audit_token_t audit_token; +#endif mDNSu32 uid; + mDNSu32 request_id; void * platform_data; - +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + mdns_trust_t trust; +#endif // Note: On a shared connection these fields in the primary structure, including hdr, are re-used // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the // operation is, we don't know if we're going to need to allocate a new request_state or not. @@ -119,6 +129,9 @@ struct request_state req_termination_fn terminate; DNSServiceFlags flags; mDNSu32 interfaceIndex; +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) + mdns_dns_service_id_t custom_service_id; +#endif union { @@ -130,7 +143,6 @@ struct request_state mDNSBool ForceMCast; domainname regtype; browser_t *browsers; - const mDNSu8 *AnonData; } browser; struct { @@ -147,23 +159,10 @@ struct request_state mDNSBool autorename; // Set if this client wants us to automatically rename on conflict mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? int num_subtypes; - mDNSBool AnonData; service_instance *instances; } servicereg; struct { - mDNSInterfaceID interface_id; - mDNSu32 flags; - mDNSu32 protocol; - DNSQuestion q4; - DNSQuestion *q42; - DNSQuestion q6; - DNSQuestion *q62; - mDNSu8 v4ans; - mDNSu8 v6ans; - } addrinfo; - struct - { mDNSIPPort ReqExt; // External port we originally requested, for logging purposes NATTraversalInfo NATinfo; } pm; @@ -176,12 +175,6 @@ struct request_state } enumeration; struct { - DNSQuestion q; - DNSQuestion *q2; - mDNSu8 ans; - } queryrecord; - struct - { DNSQuestion qtxt; DNSQuestion qsrv; const ResourceRecord *txt; @@ -189,6 +182,8 @@ struct request_state mDNSs32 ReportTime; mDNSBool external_advertise; } resolve; + GetAddrInfoClientRequest addrinfo; + QueryRecordClientRequest queryrecord; } u; }; @@ -213,11 +208,11 @@ typedef struct reply_state #define SRS_PORT(S) mDNSVal16((S)->RR_SRV.resrec.rdata->u.srv.port) -#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) +#define LogTimerToFD(FILE_DESCRIPTOR, MSG, T) LogToFD((FILE_DESCRIPTOR), MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) -extern int udsserver_init(dnssd_sock_t skts[], mDNSu32 count); +extern int udsserver_init(dnssd_sock_t skts[], size_t count); extern mDNSs32 udsserver_idle(mDNSs32 nextevent); -extern void udsserver_info(void); // print out info about current state +extern void udsserver_info_dump_to_fd(int fd); extern void udsserver_handle_configchange(mDNS *const m); extern int udsserver_exit(void); // should be called prior to app exit extern void LogMcastStateInfo(mDNSBool mflag, mDNSBool start, mDNSBool mstatelog); @@ -228,7 +223,7 @@ extern void LogMcastStateInfo(mDNSBool mflag, mDNSBool start, mDNSBool mstatelog /* Routines that uds_daemon expects to link against: */ -typedef void (*udsEventCallback)(int fd, short filter, void *context); +typedef void (*udsEventCallback)(int fd, void *context); extern mStatus udsSupportAddFDToEventLoop(dnssd_sock_t fd, udsEventCallback callback, void *context, void **platform_data); extern int udsSupportReadFD(dnssd_sock_t fd, char* buf, int len, int flags, void *platform_data); extern mStatus udsSupportRemoveFDFromEventLoop(dnssd_sock_t fd, void *platform_data); // Note: This also CLOSES the file descriptor as well @@ -241,36 +236,10 @@ extern mDNS mDNSStorage; extern DNameListElem *AutoRegistrationDomains; extern DNameListElem *AutoBrowseDomains; -extern mDNSs32 ChopSubTypes(char *regtype, char **AnonData); -extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData); extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port); -extern mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags); extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result); extern int CountPeerRegistrations(ServiceRecordSet *const srs); -#if APPLE_OSX_mDNSResponder - -// D2D interface support -extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); -extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); -extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); -extern void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); -extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags); -extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags); -extern void external_connection_release(const domainname *instance); - -#else // APPLE_OSX_mDNSResponder - -#define external_start_browsing_for_service(A,B,C,D) (void)(A) -#define external_stop_browsing_for_service(A,B,C,D) (void)(A) -#define external_start_advertising_service(A,B) (void)(A) -#define external_stop_advertising_service(A,B) do { (void)(A); (void)(B); } while (0) -#define external_start_resolving_service(A,B,C) (void)(A) -#define external_stop_resolving_service(A,B,C) (void)(A) -#define external_connection_release(A) (void)(A) - -#endif // APPLE_OSX_mDNSResponder - extern const char mDNSResponderVersionString_SCCS[]; #define mDNSResponderVersionString (mDNSResponderVersionString_SCCS+5) diff --git a/usr/src/lib/brand/lx/lx_init/lxinit.c b/usr/src/lib/brand/lx/lx_init/lxinit.c index 983318c7eb..5f98d4598e 100644 --- a/usr/src/lib/brand/lx/lx_init/lxinit.c +++ b/usr/src/lib/brand/lx/lx_init/lxinit.c @@ -490,11 +490,34 @@ lxi_iface_ipv6_link_local(const char *iface) return (0); } +static int lxi_route_send_msg(struct rt_msghdr *rtm) +{ + int sockfd, len; + + if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno)); + return (-1); + } + + if ((len = write(sockfd, (const void *)rtm, rtm->rtm_msglen)) < 0) { + lxi_warn("could not write rtmsg: %s", strerror(errno)); + close(sockfd); + return (-1); + } else if (len < rtm->rtm_msglen) { + lxi_warn("write() rtmsg incomplete"); + close(sockfd); + return (-1); + } + + close(sockfd); + return (0); +} + static int lxi_iface_gateway(const char *iface, const char *dst, int dstpfx, - const char *gwaddr) + const char *gwaddr, boolean_t llroute) { - int idx, len, sockfd; + int idx; char rtbuf[RTMBUFSZ]; struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf; struct sockaddr_in *dst_sin = (struct sockaddr_in *) @@ -504,7 +527,10 @@ lxi_iface_gateway(const char *iface, const char *dst, int dstpfx, (void) bzero(rtm, RTMBUFSZ); rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; - rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY; + + if (!llroute) + rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY; + rtm->rtm_msglen = sizeof (rtbuf); rtm->rtm_pid = getpid(); rtm->rtm_type = RTM_ADD; @@ -516,6 +542,15 @@ lxi_iface_gateway(const char *iface, const char *dst, int dstpfx, * which represents the default gateway. If we were passed a more * specific destination network, use that instead. */ + + if (dstpfx == -1) { + /* + * no prefix was specified; assume a prefix length of 32, + * which seems in line with the behavior of vmadm. + */ + dstpfx = 32; + } + dst_sin->sin_family = AF_INET; netmask_sin->sin_family = AF_INET; if (dst != NULL) { @@ -543,23 +578,7 @@ lxi_iface_gateway(const char *iface, const char *dst, int dstpfx, rtm->rtm_index = idx; } - if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { - lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno)); - return (-1); - } - - if ((len = write(sockfd, rtbuf, rtm->rtm_msglen)) < 0) { - lxi_warn("could not write rtmsg: %s", strerror(errno)); - close(sockfd); - return (-1); - } else if (len < rtm->rtm_msglen) { - lxi_warn("write() rtmsg incomplete"); - close(sockfd); - return (-1); - } - - close(sockfd); - return (0); + return (lxi_route_send_msg(rtm)); } static void @@ -634,7 +653,7 @@ lxi_net_setup(zone_dochandle_t handle) while (zonecfg_getnwifent(handle, &lookup) == Z_OK) { const char *iface = lookup.zone_nwif_physical; struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp; - const char *ipaddrs, *primary, *gateway; + const char *ipaddrs; char ipaddrs_copy[MAXNAMELEN], cidraddr[BUFSIZ], *ipaddr, *tmp, *lasts; boolean_t first_ipv4_configured = B_FALSE; @@ -684,12 +703,6 @@ lxi_net_setup(zone_dochandle_t handle) "to interface %s", ipaddr, iface); } } - - if (zone_find_attr(attrs, "primary", &primary) == 0 && - strncmp(primary, "true", MAXNAMELEN) == 0 && - zone_find_attr(attrs, "gateway", &gateway) == 0) { - lxi_iface_gateway(iface, NULL, 0, gateway); - } } if (do_addrconf) { @@ -700,31 +713,52 @@ lxi_net_setup(zone_dochandle_t handle) } static void -lxi_net_static_route(const char *line) +lxi_net_setup_gateways(zone_dochandle_t handle) { - /* - * Each static route line is a string of the form: - * - * "10.77.77.2|10.1.1.0/24|false" - * - * i.e. gateway address, destination network, and whether this is - * a "link local" route or a next hop route. - */ - custr_t *cu = NULL; - char *gw = NULL, *dst = NULL; - int pfx = -1; - int i; + struct zone_nwiftab lookup; - if (custr_alloc(&cu) != 0) { - lxi_err("custr_alloc failure"); + if (zonecfg_setnwifent(handle) != Z_OK) + return; + + while (zonecfg_getnwifent(handle, &lookup) == Z_OK) { + const char *iface = lookup.zone_nwif_physical; + struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp; + const char *primary, *gateway; + + if (zone_find_attr(attrs, "primary", &primary) == 0 && + strcmp(primary, "true") == 0 && + zone_find_attr(attrs, "gateway", &gateway) == 0) { + lxi_iface_gateway(iface, NULL, 0, gateway, B_FALSE); + } } + (void) zonecfg_endnwifent(handle); +} + +/* + * Parse a static route line string of the form: + * + * "10.77.77.2|10.1.1.0/24|false" + * + * i.e. gateway address, destination network, and whether this is + * a "link local" route or a next hop route. + */ +static void +lxi_parse_route_line(const char *line, custr_t *cu, char *gw, char *dst, + int *pfx, size_t gwlen, size_t dstlen) +{ + int i; + boolean_t havegw = B_FALSE, havedst = B_FALSE, nopfx = B_FALSE; + for (i = 0; line[i] != '\0'; i++) { - if (gw == NULL) { + if (!havegw) { if (line[i] == '|') { - if ((gw = strdup(custr_cstr(cu))) == NULL) { - lxi_err("strdup failure"); - } + if (strlcpy(gw, custr_cstr(cu), gwlen) + >= gwlen) { + lxi_err("strlcpy failure"); + } else { + havegw = B_TRUE; + } custr_reset(cu); } else { if (custr_appendc(cu, line[i]) != 0) { @@ -734,10 +768,16 @@ lxi_net_static_route(const char *line) continue; } - if (dst == NULL) { - if (line[i] == '/') { - if ((dst = strdup(custr_cstr(cu))) == NULL) { - lxi_err("strdup failure"); + if (!havedst) { + if (line[i] == '/' || line[i] == '|') { + if ((strlcpy(dst, custr_cstr(cu), dstlen)) + >= dstlen) { + lxi_err("strlcpy failure"); + } else { + havedst = B_TRUE; + if (line[i] == '|') { + nopfx = B_TRUE; + } } custr_reset(cu); } else { @@ -748,9 +788,9 @@ lxi_net_static_route(const char *line) continue; } - if (pfx == -1) { + if (*pfx == -1 && !nopfx) { if (line[i] == '|') { - pfx = atoi(custr_cstr(cu)); + *pfx = atoi(custr_cstr(cu)); custr_reset(cu); } else { if (custr_appendc(cu, line[i]) != 0) { @@ -764,26 +804,59 @@ lxi_net_static_route(const char *line) lxi_err("custr_appendc failure"); } } +} - /* - * We currently only support "next hop" routes, so ensure that - * "linklocal" is false: - */ - if (strcmp(custr_cstr(cu), "false") != 0) { - lxi_warn("invalid static route: %s", line); +static void +lxi_net_process_route_line(const char *line, boolean_t llonly) +{ + custr_t *cu = NULL; + int pfx = -1; + char gw[INET6_ADDRSTRLEN]; + char dst[INET6_ADDRSTRLEN]; + + if (custr_alloc(&cu) != 0) { + lxi_err("custr_alloc failure"); } - if (lxi_iface_gateway(NULL, dst, pfx, gw) != 0) { - lxi_err("failed to add route: %s/%d -> %s", dst, pfx, gw); + lxi_parse_route_line(line, cu, gw, dst, &pfx, sizeof (gw), + sizeof (dst)); + + if (llonly && strcmp(custr_cstr(cu), "true") == 0) { + if (lxi_iface_gateway(NULL, dst, pfx, gw, B_TRUE) != 0) { + lxi_err("failed to add link-local route: %s/%d -> %s", + dst, pfx, gw); + } + } else if (!llonly && strcmp(custr_cstr(cu), "false") == 0) { + if (lxi_iface_gateway(NULL, dst, pfx, gw, B_FALSE) != 0) { + lxi_err("failed to add next-hop route: %s/%d -> %s", + dst, pfx, gw); + } + } else if (strcmp(custr_cstr(cu), "true") != 0 && + strcmp(custr_cstr(cu), "false") != 0) { + /* + * try to be helpful when we run into something we don't expect. + */ + lxi_warn("skipping unknown static route defined in line %s, " + " parsed link-local flag=%s", line, custr_cstr(cu)); } custr_free(cu); - free(gw); - free(dst); } static void -lxi_net_static_routes(void) +lxi_net_static_route(const char *line) +{ + lxi_net_process_route_line(line, B_FALSE); +} + +static void +lxi_net_linklocal_route(const char *line) +{ + lxi_net_process_route_line(line, B_TRUE); +} + +static void +lxi_run_routeinfo(void * callback) { const char *cmd = "/native/usr/lib/brand/lx/routeinfo"; char *const argv[] = { "routeinfo", NULL }; @@ -796,7 +869,8 @@ lxi_net_static_routes(void) /* * This binary is (potentially) shipped from another * consolidation. If it does not exist, then the platform does - * not currently support static routes for LX-branded zones. + * not currently support link-local or static routes for + * LX-branded zones. */ return; } @@ -807,12 +881,25 @@ lxi_net_static_routes(void) * is complete. */ if (run_command(cmd, argv, envp, errbuf, sizeof (errbuf), - lxi_net_static_route, &code) != 0 || code != 0) { + callback, &code) != 0 || code != 0) { lxi_err("failed to run \"%s\": %s", cmd, errbuf); } } static void +lxi_net_linklocal_routes(void) +{ + lxi_run_routeinfo(lxi_net_linklocal_route); +} + + +static void +lxi_net_static_routes(void) +{ + lxi_run_routeinfo(lxi_net_static_route); +} + +static void lxi_config_close(zone_dochandle_t handle) { zonecfg_fini_handle(handle); @@ -917,6 +1004,10 @@ main(int argc, char *argv[]) handle = lxi_config_open(); lxi_net_loopback(); lxi_net_setup(handle); + + lxi_net_linklocal_routes(); + lxi_net_setup_gateways(handle); + lxi_config_close(handle); lxi_net_static_routes(); diff --git a/usr/src/lib/libc/Makefile.targ b/usr/src/lib/libc/Makefile.targ index 9b2d6d35bd..fceb9b27b4 100644 --- a/usr/src/lib/libc/Makefile.targ +++ b/usr/src/lib/libc/Makefile.targ @@ -320,7 +320,7 @@ pics/%.o: $(LIBCBASE)/../port/threads/%.d $(THREADSOBJS:%=pics/%) # assym rules -LDFLAGS.native = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) +LDFLAGS.native = $(LDASSERTS) $(BDIRECT) # # genassym is a funny fish: it's run on the build machine, so should use the diff --git a/usr/src/lib/libc/inc/libc.h b/usr/src/lib/libc/inc/libc.h index 90a2859b33..98872ac161 100644 --- a/usr/src/lib/libc/inc/libc.h +++ b/usr/src/lib/libc/inc/libc.h @@ -222,6 +222,11 @@ extern int _so_getsockopt(int, int, int, char *, int *); extern int lsign(dl_t); /* + * defined in getctxt.c + */ +extern int _getcontext(ucontext_t *) __RETURNS_TWICE; + +/* * defined in ucontext.s */ extern int __getcontext(ucontext_t *); diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com index 217cd58dc8..2b20606aa6 100644 --- a/usr/src/lib/libc/sparc/Makefile.com +++ b/usr/src/lib/libc/sparc/Makefile.com @@ -1381,7 +1381,9 @@ $(ASSYMDEP_OBJS:%=pics/%): assym.h assym.h := CFLAGS += $(CCGDEBUG) GENASSYM_C = $(LIBCDIR)/$(MACH)/genassym.c -LDFLAGS.native = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) +LDFLAGS.native = $(LDASSERTS) $(BDIRECT) + +genassym := NATIVE_LIBS += libc.so genassym: $(GENASSYM_C) $(NATIVECC) $(NATIVE_CFLAGS) -I$(LIBCBASE)/inc -I$(LIBCDIR)/inc \ diff --git a/usr/src/lib/libc/sparc/fp/_D_cplx_mul.c b/usr/src/lib/libc/sparc/fp/_D_cplx_mul.c index 6e32418f72..bfb92e1789 100644 --- a/usr/src/lib/libc/sparc/fp/_D_cplx_mul.c +++ b/usr/src/lib/libc/sparc/fp/_D_cplx_mul.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * _D_cplx_mul(z, w) returns z * w with infinities handled according * to C99. @@ -82,7 +80,7 @@ testinf(double x) double _Complex _D_cplx_mul(double _Complex z, double _Complex w) { - double _Complex v; + double _Complex v = 0; double a, b, c, d, x, y; int recalc, i, j; diff --git a/usr/src/lib/libc/sparc/fp/_F_cplx_mul.c b/usr/src/lib/libc/sparc/fp/_F_cplx_mul.c index f4073e9327..026a60c7a4 100644 --- a/usr/src/lib/libc/sparc/fp/_F_cplx_mul.c +++ b/usr/src/lib/libc/sparc/fp/_F_cplx_mul.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * _F_cplx_mul(z, w) returns z * w with infinities handled according * to C99. @@ -79,7 +77,7 @@ testinff(float x) float _Complex _F_cplx_mul(float _Complex z, float _Complex w) { - float _Complex v; + float _Complex v = 0; float a, b, c, d; double x, y; int recalc, i, j; diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_div.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_div.c index b529c5eecf..8740c42f54 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_div.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_div.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_div(v, z, w) sets *v = *z / *w with infin- * ities handling according to C99. @@ -85,7 +83,7 @@ testinfl(long double x) long double _Complex _Q_cplx_div(const long double _Complex *z, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_div(long double _Complex *v, const long double _Complex *z, diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_div_ix.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_div_ix.c index 2d9c18ba02..fa2b595406 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_div_ix.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_div_ix.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_div_ix(v, b, w) sets *v = (I * *b / *w) with * infinities handling according to C99. @@ -80,7 +78,7 @@ testinfl(long double x) long double _Complex _Q_cplx_div_ix(const long double *pb, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_div_ix(long double _Complex *v, const long double *pb, diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_div_rx.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_div_rx.c index 80f050bd94..a500db400f 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_div_rx.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_div_rx.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_div_rx(v, a, w) sets *v = *a / *w with in- * finities handling according to C99. @@ -80,7 +78,7 @@ testinfl(long double x) long double _Complex _Q_cplx_div_rx(const long double *pa, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_div_rx(long double _Complex *v, const long double *pa, diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div.c index 92473041e3..120b94f38b 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_lr_div(v, z, w) sets *v = *z / *w computed * by the textbook formula without regard to exceptions or special @@ -45,7 +43,7 @@ long double _Complex _Q_cplx_lr_div(const long double _Complex *z, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_lr_div(long double _Complex *v, const long double _Complex *z, diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_ix.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_ix.c index ca968d809a..916d239159 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_ix.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_ix.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_lr_div_ix(v, b, w) sets *v = (I * *b / *w) * compute by the textbook formula without regard to exceptions or @@ -45,7 +43,7 @@ long double _Complex _Q_cplx_lr_div_ix(const long double *pb, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_lr_div_ix(long double _Complex *v, const long double *pb, diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_rx.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_rx.c index b6b91ddf48..8159e10ab0 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_rx.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_rx.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_lr_div_rx(v, a, w) sets *v = *a / *w computed * by the textbook formula without regard to exceptions or special @@ -45,7 +43,7 @@ long double _Complex _Q_cplx_lr_div_rx(const long double *pa, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_lr_div_rx(long double _Complex *v, const long double *pa, diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_mul.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_mul.c index f2be5888df..b52ead1ba2 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_mul.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_lr_mul.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_lr_mul(v, z, w) sets *v = *z * *w computed by * the textbook formula without regard to exceptions or special cases. @@ -44,7 +42,7 @@ long double _Complex _Q_cplx_lr_mul(const long double _Complex *z, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_lr_mul(long double _Complex *v, const long double _Complex *z, diff --git a/usr/src/lib/libc/sparc/fp/_Q_cplx_mul.c b/usr/src/lib/libc/sparc/fp/_Q_cplx_mul.c index 9e4d41b2f7..6afbbe6a19 100644 --- a/usr/src/lib/libc/sparc/fp/_Q_cplx_mul.c +++ b/usr/src/lib/libc/sparc/fp/_Q_cplx_mul.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * On SPARC V8, _Q_cplx_mul(v, z, w) sets *v = *z * *w with infinities * handled according to C99. @@ -86,7 +84,7 @@ testinfl(long double x) long double _Complex _Q_cplx_mul(const long double _Complex *z, const long double _Complex *w) { - long double _Complex v; + long double _Complex v = 0; #else void _Q_cplx_mul(long double _Complex *v, const long double _Complex *z, diff --git a/usr/src/lib/libc/sparc/gen/getctxt.c b/usr/src/lib/libc/sparc/gen/getctxt.c index 3213955108..317a3f92ec 100644 --- a/usr/src/lib/libc/sparc/gen/getctxt.c +++ b/usr/src/lib/libc/sparc/gen/getctxt.c @@ -25,16 +25,15 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#pragma weak _getcontext = getcontext +/* All Rights Reserved */ #include "lint.h" #include "thr_uberdata.h" #include <ucontext.h> #include <sys/types.h> +#include "libc.h" + +#pragma weak _getcontext = getcontext int getcontext(ucontext_t *ucp) diff --git a/usr/src/lib/libc/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com index 7689a5b66e..42a6048ff8 100644 --- a/usr/src/lib/libc/sparcv9/Makefile.com +++ b/usr/src/lib/libc/sparcv9/Makefile.com @@ -1299,7 +1299,9 @@ $(ASSYMDEP_OBJS:%=pics/%): assym.h assym.h := CFLAGS64 += $(CCGDEBUG) GENASSYM_C = $(LIBCDIR)/$(MACH)/genassym.c -LDFLAGS.native = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) +LDFLAGS.native = $(LDASSERTS) $(BDIRECT) + +genassym := NATIVE_LIBS += libc.so genassym: $(GENASSYM_C) $(NATIVECC) $(NATIVE_CFLAGS) -I$(LIBCBASE)/inc -I$(LIBCDIR)/inc \ diff --git a/usr/src/lib/libc/sparcv9/gen/getctxt.c b/usr/src/lib/libc/sparcv9/gen/getctxt.c index 3213955108..317a3f92ec 100644 --- a/usr/src/lib/libc/sparcv9/gen/getctxt.c +++ b/usr/src/lib/libc/sparcv9/gen/getctxt.c @@ -25,16 +25,15 @@ */ /* Copyright (c) 1988 AT&T */ -/* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#pragma weak _getcontext = getcontext +/* All Rights Reserved */ #include "lint.h" #include "thr_uberdata.h" #include <ucontext.h> #include <sys/types.h> +#include "libc.h" + +#pragma weak _getcontext = getcontext int getcontext(ucontext_t *ucp) diff --git a/usr/src/lib/libdns_sd/Makefile.com b/usr/src/lib/libdns_sd/Makefile.com index 44b7b6c104..6bd7b77b21 100644 --- a/usr/src/lib/libdns_sd/Makefile.com +++ b/usr/src/lib/libdns_sd/Makefile.com @@ -38,7 +38,7 @@ LDLIBS += -lsocket -lnsl -lc CSTD = $(CSTD_GNU99) CPPFLAGS += -I$(SRCDIR) -DNOT_HAVE_SA_LEN -D_XPG4_2 -D__EXTENSIONS__ -CPPFLAGS += -DMDNS_VERSIONSTR_NODTS -DmDNSResponderVersion=878.1.1 +CPPFLAGS += -DMDNS_VERSIONSTR_NODTS -DmDNSResponderVersion=1310.80.1 pics/dnssd_clientstub.o := CERRWARN += -_gcc=-Wno-unused-but-set-variable diff --git a/usr/src/lib/libfru/libfruraw/fruraw.c b/usr/src/lib/libfru/libfruraw/fruraw.c index 39d341486b..968a9bd053 100644 --- a/usr/src/lib/libfru/libfruraw/fruraw.c +++ b/usr/src/lib/libfru/libfruraw/fruraw.c @@ -549,10 +549,17 @@ frt_get_segment_name(fru_seghdl_t node, char **name) for (each_seg = 0; each_seg < num_segment; each_seg++) { if (segs[each_seg].handle == node) { - segs[each_seg].name[FRU_SEGNAMELEN] = '\0'; - *name = strdup(segs[each_seg].name); + *name = malloc(SEG_NAME_LEN + 1); + if (*name != NULL) { + (void) memcpy(*name, + segs[each_seg].name, + SEG_NAME_LEN); + *name[SEG_NAME_LEN] = '\0'; + } free(sects); free(segs); + if (*name == NULL) + return (FRU_FAILURE); return (FRU_SUCCESS); } } diff --git a/usr/src/lib/libfru/libfruraw/raw_access.c b/usr/src/lib/libfru/libfruraw/raw_access.c index 7c1d2077e1..3ffca614da 100644 --- a/usr/src/lib/libfru/libfruraw/raw_access.c +++ b/usr/src/lib/libfru/libfruraw/raw_access.c @@ -205,7 +205,7 @@ create_packet_hash_object(void) static hash_obj_t * get_container_hash_object(int object_type, handle_t handle) { - hash_obj_t *hash_obj; + hash_obj_t *hash_obj = NULL; switch (object_type) { case CONTAINER_TYPE: diff --git a/usr/src/lib/libsldap/common/ns_confmgr.c b/usr/src/lib/libsldap/common/ns_confmgr.c index 862e20d035..7fa980d412 100644 --- a/usr/src/lib/libsldap/common/ns_confmgr.c +++ b/usr/src/lib/libsldap/common/ns_confmgr.c @@ -124,7 +124,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) gettext("Unable to open filename '%s' " "for reading (errno=%d)."), file, errno); MKERROR(LOG_ERR, *error, NS_CONFIG_FILE, strdup(errstr), - NS_LDAP_MEMORY); + NS_PARSE_ERR); return (NS_NOTFOUND); } @@ -150,7 +150,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) gettext("Missing Name or Value on line %d."), lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, - strdup(errstr), NS_LDAP_MEMORY); + strdup(errstr), NS_PARSE_ERR); (void) fclose(fp); return (NS_PARSE_ERR); } @@ -159,7 +159,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) gettext("Illegal profile type on line %d."), lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, - strdup(errstr), NS_LDAP_MEMORY); + strdup(errstr), NS_PARSE_ERR); (void) fclose(fp); return (NS_PARSE_ERR); } @@ -168,7 +168,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) gettext("Illegal NS_LDAP_FILE_VERSION " "on line %d."), lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, - strdup(errstr), NS_LDAP_MEMORY); + strdup(errstr), NS_PARSE_ERR); (void) fclose(fp); return (NS_PARSE_ERR); } @@ -188,7 +188,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) gettext("Illegal entry in '%s' on " "line %d"), file, lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, - strdup(errstr), NS_LDAP_MEMORY); + strdup(errstr), NS_PARSE_ERR); (void) fclose(fp); return (NS_PARSE_ERR); } @@ -208,7 +208,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) gettext("Illegal entry in '%s' on " "line %d"), file, lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, - strdup(errstr), NS_LDAP_MEMORY); + strdup(errstr), NS_PARSE_ERR); (void) fclose(fp); return (NS_PARSE_ERR); } @@ -220,7 +220,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) (void) snprintf(errstr, sizeof (errstr), gettext("Empty config file: '%s'"), file); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), - NS_LDAP_MEMORY); + NS_PARSE_ERR); return (NS_PARSE_ERR); } if (linelen == -2) { @@ -228,7 +228,7 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) (void) snprintf(errstr, sizeof (errstr), gettext("Line too long in '%s'"), file); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), - NS_LDAP_MEMORY); + NS_PARSE_ERR); return (NS_PARSE_ERR); } return (NS_SUCCESS); @@ -237,10 +237,8 @@ read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) static ns_ldap_return_code -set_attr(ns_config_t *config_struct, - char *attr_name, - char *attr_val, - ns_ldap_error_t **errorp) +set_attr(ns_config_t *config_struct, char *attr_name, char *attr_val, + ns_ldap_error_t **errorp) { ParamIndexType idx; char errmsg[MAXERROR]; @@ -471,7 +469,7 @@ __print2buf(LineBuf *line, const char *toprint, char *sep) ns_ldap_error_t * __ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname, - ns_config_t *new, int cred_only) + ns_config_t *new, int cred_only) { ns_config_t *ptr; char errstr[MAXERROR]; @@ -824,7 +822,7 @@ __ns_ldap_make_config(ns_ldap_result_t *result) { int l, m; char val[BUFSIZE]; - char *attrname; + char *attrname; ns_ldap_entry_t *entry; ns_ldap_attr_t *attr; char **attrval; @@ -997,7 +995,7 @@ makeconfigerror: */ int __ns_ldap_download(const char *profile, char *addr, char *baseDN, - ns_ldap_error_t **errorp) + ns_ldap_error_t **errorp) { char filter[BUFSIZE]; int rc; diff --git a/usr/src/lib/libsldap/common/ns_reads.c b/usr/src/lib/libsldap/common/ns_reads.c index da987fd81a..414ddffaa1 100644 --- a/usr/src/lib/libsldap/common/ns_reads.c +++ b/usr/src/lib/libsldap/common/ns_reads.c @@ -2052,7 +2052,7 @@ multi_result(ns_ldap_cookie_t *cookie) gettext(ldap_err2string(cookie->err_rc))); err = strdup(errstr); MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, - NS_LDAP_MEMORY); + LDAP_ERROR); cookie->err_rc = NS_LDAP_INTERNAL; cookie->errorp = *errorp; return (LDAP_ERROR); @@ -2122,7 +2122,7 @@ multi_result(ns_ldap_cookie_t *cookie) gettext(ldap_err2string(cookie->err_rc))); err = strdup(errstr); MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, - NS_LDAP_MEMORY); + LDAP_ERROR); cookie->err_rc = NS_LDAP_INTERNAL; cookie->errorp = *errorp; return (LDAP_ERROR); @@ -2380,7 +2380,7 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle) state); err = strdup(errstr); MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, - NS_LDAP_MEMORY); + LDAP_ERROR); cookie->err_rc = NS_LDAP_INTERNAL; cookie->errorp = *errorp; cookie->new_state = EXIT; @@ -2922,15 +2922,15 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle) if (cookie->err_rc == LDAP_SERVER_DOWN) { MKERROR(LOG_INFO, *errorp, cookie->err_rc, err, - NS_LDAP_MEMORY); + LDAP_ERROR); } else { MKERROR(LOG_WARNING, *errorp, cookie->err_rc, err, - NS_LDAP_MEMORY); + LDAP_ERROR); } } else { MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, - err, NS_LDAP_MEMORY); + err, LDAP_ERROR); } cookie->err_rc = NS_LDAP_INTERNAL; cookie->errorp = *errorp; @@ -2954,7 +2954,7 @@ search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle) cookie->state); err = strdup(errstr); MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, - NS_LDAP_MEMORY); + LDAP_ERROR); cookie->err_rc = NS_LDAP_INTERNAL; cookie->errorp = *errorp; return (ERROR); diff --git a/usr/src/lib/nsswitch/ldap/common/getexecattr.c b/usr/src/lib/nsswitch/ldap/common/getexecattr.c index abd22908e0..fc44698267 100644 --- a/usr/src/lib/nsswitch/ldap/common/getexecattr.c +++ b/usr/src/lib/nsswitch/ldap/common/getexecattr.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2021 Joyent, Inc. */ #include <secdb.h> @@ -355,7 +356,7 @@ result_exec2str: static nss_status_t _exec_process_val(ldap_backend_ptr be, nss_XbyY_args_t *argp) { - int status; + int status; nss_status_t nss_stat = NSS_UNAVAIL; ns_ldap_attr_t *attrptr; ns_ldap_entry_t *entry; @@ -420,7 +421,7 @@ get_wild(ldap_backend_ptr be, nss_XbyY_args_t *argp, int getby_flag) const char *policy = _priv_exec->policy; const char *type = _priv_exec->type; - if (strpbrk(policy, "*()\\") != NULL || + if ((policy != NULL && strpbrk(policy, "*()\\") != NULL) || type != NULL && strpbrk(type, "*()\\") != NULL) return ((nss_status_t)NSS_NOTFOUND); @@ -446,11 +447,12 @@ get_wild(ldap_backend_ptr be, nss_XbyY_args_t *argp, int getby_flag) switch (getby_flag) { case NSS_DBOP_EXECATTR_BYID: ret = snprintf(searchfilter, sizeof (searchfilter), - _EXEC_GETEXECID, id, policy, ISWILD(type)); + _EXEC_GETEXECID, id, ISWILD(policy), ISWILD(type)); if (ret >= sizeof (searchfilter) || ret < 0) goto go_out; ret = snprintf(userdata, sizeof (userdata), - _EXEC_GETEXECID_SSD, id, policy, ISWILD(type)); + _EXEC_GETEXECID_SSD, id, ISWILD(policy), + ISWILD(type)); if (ret >= sizeof (userdata) || ret < 0) goto go_out; break; @@ -458,12 +460,12 @@ get_wild(ldap_backend_ptr be, nss_XbyY_args_t *argp, int getby_flag) case NSS_DBOP_EXECATTR_BYNAMEID: ret = snprintf(searchfilter, sizeof (searchfilter), _EXEC_GETEXECNAMEID, name, id, - policy, ISWILD(type)); + ISWILD(policy), ISWILD(type)); if (ret >= sizeof (searchfilter) || ret < 0) goto go_out; ret = snprintf(userdata, sizeof (userdata), _EXEC_GETEXECNAMEID_SSD, name, id, - policy, ISWILD(type)); + ISWILD(policy), ISWILD(type)); if (ret >= sizeof (userdata) || ret < 0) goto go_out; break; @@ -484,8 +486,8 @@ go_out: } static nss_status_t -exec_attr_process_val(ldap_backend_ptr be, nss_XbyY_args_t *argp) { - +exec_attr_process_val(ldap_backend_ptr be, nss_XbyY_args_t *argp) +{ _priv_execattr *_priv_exec = (_priv_execattr *)(argp->key.attrp); int stat, nss_stat = NSS_SUCCESS; @@ -497,10 +499,10 @@ exec_attr_process_val(ldap_backend_ptr be, nss_XbyY_args_t *argp) { if (argp->buf.result != NULL) { /* file format -> execstr_t */ stat = (*argp->str2ent)(be->buffer, - be->buflen, - argp->buf.result, - argp->buf.buffer, - argp->buf.buflen); + be->buflen, + argp->buf.result, + argp->buf.buffer, + argp->buf.buflen); if (stat == NSS_STR_PARSE_SUCCESS) { argp->returnval = argp->buf.result; argp->returnlen = 1; /* irrelevant */ @@ -544,16 +546,16 @@ getbynam(ldap_backend_ptr be, void *a) const char *policy = _priv_exec->policy; const char *type = _priv_exec->type; - if (strpbrk(policy, "*()\\") != NULL || + if (policy != NULL && strpbrk(policy, "*()\\") != NULL || type != NULL && strpbrk(type, "*()\\") != NULL || _ldap_filter_name(name, _priv_exec->name, sizeof (name)) != 0) return ((nss_status_t)NSS_NOTFOUND); ret = snprintf(searchfilter, sizeof (searchfilter), - _EXEC_GETEXECNAME, name, policy, ISWILD(type)); + _EXEC_GETEXECNAME, name, ISWILD(policy), ISWILD(type)); if (ret >= sizeof (searchfilter) || ret < 0) return ((nss_status_t)NSS_NOTFOUND); ret = snprintf(userdata, sizeof (userdata), - _EXEC_GETEXECNAME_SSD, name, policy, ISWILD(type)); + _EXEC_GETEXECNAME_SSD, name, ISWILD(policy), ISWILD(type)); if (ret >= sizeof (userdata) || ret < 0) return ((nss_status_t)NSS_NOTFOUND); diff --git a/usr/src/lib/sun_sas/common/Sun_sasFreeLibrary.c b/usr/src/lib/sun_sas/common/Sun_sasFreeLibrary.c index b30e975fe1..78b2100e89 100644 --- a/usr/src/lib/sun_sas/common/Sun_sasFreeLibrary.c +++ b/usr/src/lib/sun_sas/common/Sun_sasFreeLibrary.c @@ -48,6 +48,7 @@ Sun_sasFreeLibrary(void) open_handle_index = 1; unlock(&all_hbas_lock); (void) mutex_destroy(&all_hbas_lock); + (void) mutex_destroy(&open_handles_lock); /* free sysevent handle. */ if (gSysEventHandle != NULL) diff --git a/usr/src/lib/sun_sas/common/Sun_sasLoadLibrary.c b/usr/src/lib/sun_sas/common/Sun_sasLoadLibrary.c index b5df2a4cc8..45164c5058 100644 --- a/usr/src/lib/sun_sas/common/Sun_sasLoadLibrary.c +++ b/usr/src/lib/sun_sas/common/Sun_sasLoadLibrary.c @@ -27,6 +27,11 @@ #include <sun_sas.h> +mutex_t all_hbas_lock = DEFAULTMUTEX; +mutex_t open_handles_lock = DEFAULTMUTEX; +HBA_UINT16 open_handle_index; +HBA_UINT32 hba_count; + /* * Loads the HBA Library. Must be called before calling any HBA library * functions @@ -53,23 +58,17 @@ HBA_STATUS Sun_sasLoadLibrary() { } hba_count = 0; open_handle_index = 1; - /* Initialize the read-write lock */ - if (mutex_init(&all_hbas_lock, USYNC_THREAD, NULL)) { - log(LOG_DEBUG, ROUTINE, - "Unable to initialize lock in LoadLibrary for reason \"%s\"", - strerror(errno)); - return (HBA_STATUS_ERROR); - } + /* grab write lock */ lock(&all_hbas_lock); start = gethrtime(); if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { - log(LOG_DEBUG, ROUTINE, - "Unable to load device tree for reason \"%s\"", - strerror(errno)); - unlock(&all_hbas_lock); - return (HBA_STATUS_ERROR); + log(LOG_DEBUG, ROUTINE, + "Unable to load device tree: \"%s\"", + strerror(errno)); + unlock(&all_hbas_lock); + return (HBA_STATUS_ERROR); } end = gethrtime(); duration = end - start; @@ -79,9 +78,9 @@ HBA_STATUS Sun_sasLoadLibrary() { /* At load time, we only gather libdevinfo information */ if (devtree_get_all_hbas(root) == HBA_STATUS_OK) { - atLeastOneHBA = B_TRUE; + atLeastOneHBA = B_TRUE; } else { - atLeastOneFailure = B_TRUE; + atLeastOneFailure = B_TRUE; } di_fini(root); @@ -90,13 +89,14 @@ HBA_STATUS Sun_sasLoadLibrary() { /* Now determine what status code to return */ if (atLeastOneHBA) { - /* We've got at least one HBA and possibly some failures */ - return (HBA_STATUS_OK); - } else if (atLeastOneFailure) { - /* We have no HBAs but have failures */ - return (HBA_STATUS_ERROR); - } else { - /* We have no HBAs and no failures */ - return (HBA_STATUS_OK); + /* We've got at least one HBA and possibly some failures */ + return (HBA_STATUS_OK); + } + if (atLeastOneFailure) { + /* We have no HBAs but have failures */ + return (HBA_STATUS_ERROR); } + + /* We have no HBAs and no failures */ + return (HBA_STATUS_OK); } diff --git a/usr/src/lib/sun_sas/common/devtree_hba_disco.c b/usr/src/lib/sun_sas/common/devtree_hba_disco.c index bfd584008b..2017f97e26 100644 --- a/usr/src/lib/sun_sas/common/devtree_hba_disco.c +++ b/usr/src/lib/sun_sas/common/devtree_hba_disco.c @@ -33,6 +33,8 @@ #include <inttypes.h> #include <ctype.h> +struct sun_sas_hba *global_hba_head; + /* free hba port info for the given hba */ static void free_hba_port(struct sun_sas_hba *hba_ptr) diff --git a/usr/src/lib/sun_sas/common/sun_sas.h b/usr/src/lib/sun_sas/common/sun_sas.h index d6872e5628..75344e6de3 100644 --- a/usr/src/lib/sun_sas/common/sun_sas.h +++ b/usr/src/lib/sun_sas/common/sun_sas.h @@ -107,11 +107,11 @@ extern "C" { /* misc */ #define SUN_MICROSYSTEMS "Sun Microsystems, Inc." -mutex_t all_hbas_lock; -mutex_t open_handles_lock; -mutex_t log_file_lock; -HBA_UINT32 hba_count; -HBA_UINT16 open_handle_index; +extern mutex_t all_hbas_lock; +extern mutex_t open_handles_lock; +extern mutex_t log_file_lock; +extern HBA_UINT32 hba_count; +extern HBA_UINT16 open_handle_index; /* Internal structures that aren't exposed to clients */ @@ -136,7 +136,7 @@ struct sun_sas_hba { struct sun_sas_port *first_port; }; -struct sun_sas_hba *global_hba_head; +extern struct sun_sas_hba *global_hba_head; struct ScsiEntryList { SMHBA_SCSIENTRY entry; diff --git a/usr/src/man/man1m/nvmeadm.1m b/usr/src/man/man1m/nvmeadm.1m index feb699dd79..32620b6747 100644 --- a/usr/src/man/man1m/nvmeadm.1m +++ b/usr/src/man/man1m/nvmeadm.1m @@ -11,8 +11,9 @@ .\" .\" Copyright 2016 Nexenta Systems, Inc. All rights reserved. .\" Copyright 2019 Western Digital Corporation. +.\" Copyright 2021 Oxide Computer Company .\" -.Dd June 27, 2019 +.Dd March 24, 2021 .Dt NVMEADM 1M .Os .Sh NAME @@ -25,20 +26,24 @@ .Nm .Op Fl dv .Cm list -.Op Ar ctl[/ns][,...] +.Oo +.Fl p +.Fl o Ar field Ns [,...] +.Oc +.Op Ar ctl[/ns] Ns [,...] .Nm .Op Fl dv .Cm identify -.Ar ctl[/ns][,...] +.Ar ctl[/ns] Ns [,...] .Nm .Op Fl dv .Cm get-logpage -.Ar ctl[/ns][,...] +.Ar ctl[/ns] Ns [,...] .Ar logpage .Nm .Op Fl dv .Cm get-features -.Ar ctl[/ns][,...] +.Ar ctl[/ns] Ns [,...] .Op Ar feature-list .Nm .Op Fl dv @@ -180,16 +185,68 @@ logpage. .It Xo .Nm .Cm list -.Op Ar ctl[/ns][,...] +.Oo +.Fl p +.Fl o Ar field Ns [,...] +.Oc +.Op Ar ctl[/ns] Ns [,...] .Xc Lists the NVMe controllers and their namespaces in the system and prints a 1-line summary of their basic properties for each. If a list of controllers and/or namespaces is given then the listing is limited to those devices. +By default, output is human-readable; however, a parsable form can +controlled by using the following options: +.Bl -tag -width Fl +.It Fl p +Rather than printing human-readable output, outputs an entry for each of +the specified controllers and namespaces. +If no controllers or namespaces are given as arguments, then the primary +namespace of each controller is listed and if the +.Fl v +option is specified, then all of the namespaces for a controller are +listed. +This option requires that output fields be selected with the +.Fl o +option. +.It Fl o Ar field Ns [,...] +A comma-separated list of one or more output fields to be used. +Fields are listed below and the name is case insensitive. +.El +.Pp +The following fields can be specified when using the parsable form: +.Bl -tag -width CAPACITY +.It Sy MODEL +The model number of the device, generally containing information about +both the manufacturer and the product. +.It Sy SERIAL +The NVMe controller's serial number. +.It Sy FWREV +The controller's firmware revision. +.It Sy VERSION +The version of the NVMe specification the controller supports. +.It Sy SIZE +The logical size in bytes of the namespace. +.It Sy CAPACITY +The amount of logical bytes that the namespace may actually have allocated at +any time. +This may be different than size due to the use of thin provisioning or due to +administrative action. +.It Sy USED +The number of bytes used in the namespace. +.It Sy INSTANCE +The name of the device node and instance of it. +.It Sy NAMESPACE +The numerical value of the namespace which can be used as part of other +.Nm +operations. +.It Sy DISK +The name of the disk device that corresponds to the namespace, if any. +.El .It Xo .Nm .Cm identify -.Ar ctl[/ns][,...] +.Ar ctl[/ns] Ns [,...] .Xc Print detailed information about the specified controllers and/or namespaces. @@ -202,7 +259,7 @@ admin command in the NVMe specification. .It Xo .Nm .Cm get-logpage -.Ar ctl[/ns][,...] +.Ar ctl[/ns] Ns [,...] .Ar logpage .Xc Print the specified log page of the specified controllers and/or namespaces. @@ -225,7 +282,7 @@ admin command in the NVMe specification. .It Xo .Nm .Cm get-features -.Ar ctl[/ns][,...] +.Ar ctl[/ns] Ns [,...] .Op Ar feature-list .Xc Prints information about the specified features, or all features if diff --git a/usr/src/pkg/manifests/diagnostic-pci.mf b/usr/src/pkg/manifests/diagnostic-pci.mf index c666862ebd..a2a1610726 100644 --- a/usr/src/pkg/manifests/diagnostic-pci.mf +++ b/usr/src/pkg/manifests/diagnostic-pci.mf @@ -23,4 +23,5 @@ dir path=usr group=sys dir path=usr/lib dir path=usr/lib/pci file path=usr/lib/pci/pcidb mode=0555 +file path=usr/lib/pci/pcieadm mode=0555 license lic_CDDL license=lic_CDDL diff --git a/usr/src/pkg/manifests/system-test-utiltest.mf b/usr/src/pkg/manifests/system-test-utiltest.mf index 963821dff2..914154281f 100644 --- a/usr/src/pkg/manifests/system-test-utiltest.mf +++ b/usr/src/pkg/manifests/system-test-utiltest.mf @@ -77,6 +77,7 @@ dir path=opt/util-tests/tests/mdb/format dir path=opt/util-tests/tests/mdb/options dir path=opt/util-tests/tests/mdb/typedef dir path=opt/util-tests/tests/mergeq +dir path=opt/util-tests/tests/pci dir path=opt/util-tests/tests/sed dir path=opt/util-tests/tests/sed/regress.multitest.out dir path=opt/util-tests/tests/sleep @@ -1686,7 +1687,23 @@ file path=opt/util-tests/tests/mdb/typedef/tst.union.mdb mode=0444 file path=opt/util-tests/tests/mdb/typedef/tst.union.mdb.out mode=0444 file path=opt/util-tests/tests/mergeq/mqt mode=0555 file path=opt/util-tests/tests/mergeq/wqt mode=0555 +file path=opt/util-tests/tests/pci/bridge-efilt-p.out mode=0444 +file path=opt/util-tests/tests/pci/bridge-efilt.out mode=0444 +file path=opt/util-tests/tests/pci/bridge-ht-p.out mode=0444 +file path=opt/util-tests/tests/pci/bridge-ht.msi-p.out mode=0444 +file path=opt/util-tests/tests/pci/bridge-ht.msi.command-p.out mode=0444 +file path=opt/util-tests/tests/pci/bridge-ht.out mode=0444 +file path=opt/util-tests/tests/pci/bridge.pci mode=0444 +file path=opt/util-tests/tests/pci/header0-basic-L.out mode=0444 +file path=opt/util-tests/tests/pci/header0-basic-LH.out mode=0444 +file path=opt/util-tests/tests/pci/header0-basic-n.out mode=0444 +file path=opt/util-tests/tests/pci/header0-basic.out mode=0444 +file path=opt/util-tests/tests/pci/header0-parse.out mode=0444 +file path=opt/util-tests/tests/pci/igb-ltr-p.out mode=0444 +file path=opt/util-tests/tests/pci/igb-ltr.out mode=0444 +file path=opt/util-tests/tests/pci/igb.pci mode=0444 file path=opt/util-tests/tests/pcidbtest mode=0555 +file path=opt/util-tests/tests/pcieadmtest mode=0555 file path=opt/util-tests/tests/printf_test mode=0555 file path=opt/util-tests/tests/sed/multi_test mode=0555 file path=opt/util-tests/tests/sed/regress.multitest.out/1.1 mode=0444 diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run index c4ea9ed8e1..9b8ba57fbf 100644 --- a/usr/src/test/util-tests/runfiles/default.run +++ b/usr/src/test/util-tests/runfiles/default.run @@ -88,3 +88,4 @@ tests = ['custr_remove', 'custr_trunc'] tests = ['sed_addr', 'multi_test'] [/opt/util-tests/tests/pcidbtest] +[/opt/util-tests/tests/pcieadmtest] diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile index 77a20d6059..1d73f5dc94 100644 --- a/usr/src/test/util-tests/tests/Makefile +++ b/usr/src/test/util-tests/tests/Makefile @@ -20,6 +20,6 @@ SUBDIRS = date dis dladm iconv libnvpair_json libsff printf xargs grep_xpg4 SUBDIRS += demangle mergeq workq chown ctf smbios libjedec awk make sleep -SUBDIRS += bunyan libcustr find mdb sed head pcidb +SUBDIRS += bunyan libcustr find mdb sed head pcidb pcieadm include $(SRC)/test/Makefile.com diff --git a/usr/src/test/util-tests/tests/pcieadm/Makefile b/usr/src/test/util-tests/tests/pcieadm/Makefile new file mode 100644 index 0000000000..59a995f0ea --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/Makefile @@ -0,0 +1,59 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2021 Oxide Computer Company +# + +include $(SRC)/cmd/Makefile.cmd +include $(SRC)/test/Makefile.com + +ROOTOPTPKG = $(ROOT)/opt/util-tests/tests +ROOTOPTPCI = $(ROOT)/opt/util-tests/tests/pci +PROG = pcieadmtest +DATAFILES = bridge.pci igb.pci \ + header0-basic.out \ + header0-basic-L.out \ + header0-basic-LH.out \ + header0-basic-n.out \ + header0-parse.out \ + igb-ltr.out \ + igb-ltr-p.out \ + bridge-ht.out \ + bridge-ht-p.out \ + bridge-ht.msi-p.out \ + bridge-ht.msi.command-p.out \ + bridge-efilt.out \ + bridge-efilt-p.out + +ROOTPROG = $(PROG:%=$(ROOTOPTPKG)/%) +ROOTDATA = $(DATAFILES:%=$(ROOTOPTPCI)/%) +$(ROOTDATA) := FILEMODE = 0444 + +all: + +install: $(ROOTDATA) $(ROOTPROG) + +clobber: clean + +clean: + +$(ROOTOPTPKG): + $(INS.dir) + +$(ROOTOPTPCI): $(ROOTOPTPKG) + $(INS.dir) + +$(ROOTOPTPCI)/%: % $(ROOTOPTPCI) + $(INS.file) + +$(ROOTOPTPKG)/%: %.ksh $(ROOTOPTPKG) + $(INS.rename) diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out new file mode 100644 index 0000000000..63e630294d --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out @@ -0,0 +1,2 @@ +pcie.linksts:0x7043 +pcieadm: filter 'atelier' did not match any fields diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out new file mode 100644 index 0000000000..f3b2bf7680 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out @@ -0,0 +1,10 @@ +pcieadm: filter 'atelier' did not match any fields +PCI Express Capability (0x10) + Link Status: 0x7043 + |--> Link Speed: 8.0 GT/s (0x3) + |--> Link Width: 0x4 + |--> Link Training: no (0x0) + |--> Slot Clock Configuration: common (0x1000) + |--> Data Link Layer Link Active: yes (0x2000) + |--> Link Bandwidth Management Status: change occurred (0x4000) + |--> Link Autonomous Bandwidth Status: no change (0x0) diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out new file mode 100644 index 0000000000..09ead24746 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out @@ -0,0 +1 @@ +pcieadm: filter 'ht' did not match any fields diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out new file mode 100644 index 0000000000..2cd1fe59bf --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out @@ -0,0 +1 @@ +pcieadm: filter 'ht.msi' did not match any fields diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out new file mode 100644 index 0000000000..095e12d8a1 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out @@ -0,0 +1 @@ +0xa803:ht.msi.command diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.out new file mode 100644 index 0000000000..ba2899a347 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.out @@ -0,0 +1,4 @@ +HyperTransport Capability - MSI Mapping (0x8) + Command: 0xa803 + |--> Enable: enabled (0x1) + |--> Fixed: enabled (0x2) diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge.pci b/usr/src/test/util-tests/tests/pcieadm/bridge.pci Binary files differnew file mode 100644 index 0000000000..f85ffa4f6e --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/bridge.pci diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out new file mode 100644 index 0000000000..c52c00d5cc --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out @@ -0,0 +1,3 @@ +SHORT HUMAN +header0.vendor Vendor ID +header0.device Device ID diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out new file mode 100644 index 0000000000..5dda4c444b --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out @@ -0,0 +1,2 @@ +header0.vendor Vendor ID +header0.device Device ID diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out new file mode 100644 index 0000000000..874bb0db55 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out @@ -0,0 +1,3 @@ +Device /dev/stdin -- Type 0 Header (header0) + Vendor ID (header0.vendor): 0x8086 -- Intel Corporation + Device ID (header0.device): 0x1521 -- I350 Gigabit Network Connection diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic.out new file mode 100644 index 0000000000..93e067f828 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic.out @@ -0,0 +1,3 @@ +Device /dev/stdin -- Type 0 Header + Vendor ID: 0x8086 -- Intel Corporation + Device ID: 0x1521 -- I350 Gigabit Network Connection diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-parse.out b/usr/src/test/util-tests/tests/pcieadm/header0-parse.out new file mode 100644 index 0000000000..d56cf26062 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/header0-parse.out @@ -0,0 +1,2 @@ +header0.vendor:0x8086 +header0.device:0x1521 diff --git a/usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out b/usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out new file mode 100644 index 0000000000..c6a9d1bce0 --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out @@ -0,0 +1 @@ +pcieadm: filter 'ltr' did not match any fields diff --git a/usr/src/test/util-tests/tests/pcieadm/igb-ltr.out b/usr/src/test/util-tests/tests/pcieadm/igb-ltr.out new file mode 100644 index 0000000000..f6a4f26cbd --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/igb-ltr.out @@ -0,0 +1,11 @@ +Latency Tolerance Reporting Capability (0x18) + Capability Header: 0x1d010018 + |--> Capability ID: 0x18 + |--> Capability Version: 0x1 + |--> Next Capability Offset: 0x1d0 + Max Snoop Latency: 0x0 + |--> Latency Value: 0x0 + |--> Latency Scale: 1 ns (0x0) + Max No-Snoop Latency: 0x0 + |--> Latency Value: 0x0 + |--> Latency Scale: 1 ns (0x0) diff --git a/usr/src/test/util-tests/tests/pcieadm/igb.pci b/usr/src/test/util-tests/tests/pcieadm/igb.pci Binary files differnew file mode 100644 index 0000000000..bcee63f32d --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/igb.pci diff --git a/usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh b/usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh new file mode 100644 index 0000000000..d68cf0b7cc --- /dev/null +++ b/usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh @@ -0,0 +1,160 @@ +#!/usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2021 Oxide Computer Company +# + +unalias -a +set -o pipefail + +pcieadm_arg0="$(basename $0)" +pcieadm_prog="/usr/lib/pci/pcieadm" +pcieadm_data="$(dirname $0)/pci" +pcieadm_exit=0 +pcieadm_tmpfile="/tmp/pcieadmtest.$$" + +warn() +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "TEST FAILED: $pcieadm_arg0: $msg" >&2 + pcieadm_exit=1 +} + +pcieadm_bad_args() +{ + if $pcieadm_prog $@ 2>/dev/null 1>/dev/null; then + warn "should have failed with args "$@", but passed" + return + fi + + printf "TEST PASSED: invalid arguments %s\n" "$*" +} + +pcieadm_validate_output() +{ + typeset input="$pcieadm_data/$1" + shift + typeset outfile="$pcieadm_data/$1" + shift + typeset expexit=$1 + shift + + $pcieadm_prog $@ <$input >"$pcieadm_tmpfile" 2>&1 + if (( $? != expexit)); then + warn "$@: mismatched exit status, found $?, expected $expexit" + fi + + if ! diff $outfile $pcieadm_tmpfile; then + warn "$@: output mismatched" + else + printf "TEST PASSED: %s\n" "$*" + fi +} + + +if [[ -n $PCIEADM ]]; then + pcieadm_prog=$PCIEADM +fi + +# +# Before we begin execution, set up the environment such that we have a +# standard locale and that umem will help us catch mistakes. +# +export LC_ALL=C.UTF-8 +export LD_PRELOAD=libumem.so +export UMEM_DEBUG=default + +if [[ ! -d $pcieadm_data ]]; then + printf "failed to find data directory %s\n" "$pcieadm_data" >&2 + exit 1 +fi + +# +# First work through bad options. +# +pcieadm_bad_args +pcieadm_bad_args -d +pcieadm_bad_args foobar +pcieadm_bad_args save-cfgspace +pcieadm_bad_args save-cfgspace -a +pcieadm_bad_args save-cfgspace -d +pcieadm_bad_args save-cfgspace -d final +pcieadm_bad_args save-cfgspace -a -d fantasy +pcieadm_bad_args show-devs -h +pcieadm_bad_args show-devs -p +pcieadm_bad_args show-devs -s -o +pcieadm_bad_args show-cfgspace +pcieadm_bad_args show-cfgspace -d -H +pcieadm_bad_args show-cfgspace -d +pcieadm_bad_args show-cfgspace -f +pcieadm_bad_args show-cfgspace -h +pcieadm_bad_args show-cfgspace -L +pcieadm_bad_args show-cfgspace -L -n -f "$pcieadm_data/igb.pci" +pcieadm_bad_args show-cfgspace -L -p -f "$pcieadm_data/igb.pci" +pcieadm_bad_args show-cfgspace -p -f "$pcieadm_data/igb.pci" +pcieadm_bad_args show-cfgspace -o foo -f "$pcieadm_data/igb.pci" +pcieadm_bad_args show-cfgspace -L -o foo -f "$pcieadm_data/igb.pci" + +# +# Test different output cases +# +pcieadm_validate_output igb.pci header0-basic.out 0 \ + show-cfgspace -f /dev/stdin header0.vendor header0.device +pcieadm_validate_output igb.pci header0-basic-L.out 0 \ + show-cfgspace -L -f /dev/stdin header0.vendor header0.device +pcieadm_validate_output igb.pci header0-basic-n.out 0 \ + show-cfgspace -n -f /dev/stdin header0.vendor header0.device +pcieadm_validate_output igb.pci header0-basic-LH.out 0 \ + show-cfgspace -L -H -f /dev/stdin header0.vendor header0.device + +# +# Specific filter behavior. We want to validate the following: +# +# o An inexact filter (e.g. a cap or subcap) matches in human mode, +# but not parsable. +# o An exact filter will show its contents in human mode, but not +# parsable. +# o A missing filter causes to exit non-zero, but still show what we +# found with other filters or because of a prefix match. +# +pcieadm_validate_output igb.pci igb-ltr.out 0 \ + show-cfgspace -f /dev/stdin ltr +pcieadm_validate_output igb.pci igb-ltr-p.out 1 \ + show-cfgspace -p -o short,value -f /dev/stdin ltr +pcieadm_validate_output igb.pci header0-parse.out 0 \ + show-cfgspace -p -o short,value -f /dev/stdin header0.vendor header0.device +pcieadm_validate_output bridge.pci bridge-ht.out 0 \ + show-cfgspace -f /dev/stdin ht +pcieadm_validate_output bridge.pci bridge-ht.out 0 \ + show-cfgspace -f /dev/stdin ht.msi +pcieadm_validate_output bridge.pci bridge-ht.out 0 \ + show-cfgspace -f /dev/stdin ht.msi.command +pcieadm_validate_output bridge.pci bridge-ht-p.out 1 \ + show-cfgspace -p -o value,short -f /dev/stdin ht +pcieadm_validate_output bridge.pci bridge-ht.msi-p.out 1 \ + show-cfgspace -p -o value,short -f /dev/stdin ht.msi +pcieadm_validate_output bridge.pci bridge-ht.msi.command-p.out 0 \ + show-cfgspace -p -o value,short -f /dev/stdin ht.msi.command +pcieadm_validate_output bridge.pci bridge-efilt.out 1 \ + show-cfgspace -f /dev/stdin pcie.linksts atelier +pcieadm_validate_output bridge.pci bridge-efilt-p.out 1 \ + show-cfgspace -p -o short,value -f /dev/stdin pcie.linksts atelier + +if (( pcieadm_exit == 0 )); then + printf "All tests passed successfully!\n" +fi + +rm -f "$pcieadm_tmpfile" +exit $pcieadm_exit diff --git a/usr/src/tools/Makefile.tools b/usr/src/tools/Makefile.tools index 9fd747751d..315a10b6c6 100644 --- a/usr/src/tools/Makefile.tools +++ b/usr/src/tools/Makefile.tools @@ -55,9 +55,10 @@ ELFSIGN_O= $(TRUE) LDLIBS= LDFLAGS= $(MAPFILE.NES:%=-Wl,-M%) $(MAPFILE.NED:%=-Wl,-M%) \ $(MAPFILE.PGA:%=-Wl,-M%) \ - $(ZASSERTDEFLIB)=libc.so \ $(BDIRECT) +NATIVE_LIBS += libc.so + # To work around a bootstrapping problem, we can't rely on cw(1) knowing how # to translate -shared as we may be using an older one to build the current # tools. diff --git a/usr/src/ucblib/libucb/sparc/sys/signal.c b/usr/src/ucblib/libucb/sparc/sys/signal.c index 3e8d414f85..dee23c3c4b 100644 --- a/usr/src/ucblib/libucb/sparc/sys/signal.c +++ b/usr/src/ucblib/libucb/sparc/sys/signal.c @@ -25,17 +25,13 @@ */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ -/* All Rights Reserved */ +/* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ -#pragma ident "%Z%%M% %I% %E% SMI" - -/*LINTLIBRARY*/ - /* * 4.3BSD signal compatibility functions * @@ -480,12 +476,13 @@ ucbsigvec(int sig, struct sigvec *nvec, struct sigvec *ovec) * This allows a child of vfork(2) to set signal handlers * to SIG_IGN or SIG_DFL without affecting the parent. */ - if ((void (*)(int))nhandler != SIG_DFL && - (void (*)(int))nhandler != SIG_IGN) { + if ((void (*)(int))(uintptr_t)nhandler != SIG_DFL && + (void (*)(int))(uintptr_t)nhandler != SIG_IGN) { _siguhandler[sig] = nhandler; - nact.sa_handler = (void (*)(int))ucbsigvechandler; + nact.sa_handler = + (void (*)(int))(uintptr_t)ucbsigvechandler; } else { - nact.sa_handler = (void (*)(int))nhandler; + nact.sa_handler = (void (*)(int))(uintptr_t)nhandler; } mask2set(nvec->sv_mask, &nact.sa_mask); if (sig == SIGKILL || sig == SIGSTOP) @@ -566,7 +563,8 @@ ucbsignal(int s, void (*a)(int)))(int) static int mask[NSIG]; static int flags[NSIG]; - nsv.sv_handler = (void (*) (int, int, struct sigcontext *, char *)) a; + nsv.sv_handler = + (void (*) (int, int, struct sigcontext *, char *))(uintptr_t)a; nsv.sv_mask = mask[s]; nsv.sv_flags = flags[s]; if (ucbsigvec(s, &nsv, &osv) < 0) diff --git a/usr/src/uts/common/brand/lx/procfs/lx_proc.h b/usr/src/uts/common/brand/lx/procfs/lx_proc.h index 723dfff560..be95e7e471 100644 --- a/usr/src/uts/common/brand/lx/procfs/lx_proc.h +++ b/usr/src/uts/common/brand/lx/procfs/lx_proc.h @@ -225,6 +225,7 @@ typedef enum lxpr_nodetype { LXPR_SYS_KERNEL_RANDDIR, /* /proc/sys/kernel/random */ LXPR_SYS_KERNEL_RAND_BOOTID, /* /proc/sys/kernel/random/boot_id */ LXPR_SYS_KERNEL_RAND_ENTAVL, /* /proc/sys/kernel/random/entropy_avail */ + LXPR_SYS_KERNEL_RAND_UUID, /* /proc/sys/kernel/random/uuid */ LXPR_SYS_KERNEL_SEM, /* /proc/sys/kernel/sem */ LXPR_SYS_KERNEL_SHMALL, /* /proc/sys/kernel/shmall */ LXPR_SYS_KERNEL_SHMMAX, /* /proc/sys/kernel/shmmax */ diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c index 575acd59a2..d573825652 100644 --- a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c +++ b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c @@ -248,6 +248,7 @@ static void lxpr_read_sys_kernel_osrel(lxpr_node_t *, lxpr_uiobuf_t *); static void lxpr_read_sys_kernel_pid_max(lxpr_node_t *, lxpr_uiobuf_t *); static void lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *, lxpr_uiobuf_t *); static void lxpr_read_sys_kernel_rand_entavl(lxpr_node_t *, lxpr_uiobuf_t *); +static void lxpr_read_sys_kernel_rand_uuid(lxpr_node_t *, lxpr_uiobuf_t *); static void lxpr_read_sys_kernel_sem(lxpr_node_t *, lxpr_uiobuf_t *); static void lxpr_read_sys_kernel_shmall(lxpr_node_t *, lxpr_uiobuf_t *); static void lxpr_read_sys_kernel_shmmax(lxpr_node_t *, lxpr_uiobuf_t *); @@ -590,6 +591,7 @@ static lxpr_dirent_t sys_kerneldir[] = { static lxpr_dirent_t sys_randdir[] = { { LXPR_SYS_KERNEL_RAND_BOOTID, "boot_id" }, { LXPR_SYS_KERNEL_RAND_ENTAVL, "entropy_avail" }, + { LXPR_SYS_KERNEL_RAND_UUID, "uuid" }, }; #define SYS_RANDDIRFILES (sizeof (sys_randdir) / sizeof (sys_randdir[0])) @@ -932,6 +934,7 @@ static void (*lxpr_read_function[])() = { lxpr_read_invalid, /* /proc/sys/kernel/random */ lxpr_read_sys_kernel_rand_bootid, /* /proc/sys/kernel/random/boot_id */ lxpr_read_sys_kernel_rand_entavl, /* .../kernel/random/entropy_avail */ + lxpr_read_sys_kernel_rand_uuid, /* .../kernel/random/uuid */ lxpr_read_sys_kernel_sem, /* /proc/sys/kernel/sem */ lxpr_read_sys_kernel_shmall, /* /proc/sys/kernel/shmall */ lxpr_read_sys_kernel_shmmax, /* /proc/sys/kernel/shmmax */ @@ -1101,6 +1104,7 @@ static vnode_t *(*lxpr_lookup_function[])() = { lxpr_lookup_sys_kdir_randdir, /* /proc/sys/kernel/random */ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/random/boot_id */ lxpr_lookup_not_a_dir, /* .../kernel/random/entropy_avail */ + lxpr_lookup_not_a_dir, /* /proc/sys/kernel/random/uuid */ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/sem */ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmall */ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmmax */ @@ -1270,6 +1274,7 @@ static int (*lxpr_readdir_function[])() = { lxpr_readdir_sys_kdir_randdir, /* /proc/sys/kernel/random */ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/random/boot_id */ lxpr_readdir_not_a_dir, /* .../kernel/random/entropy_avail */ + lxpr_readdir_not_a_dir, /* /proc/sys/kernel/random/uuid */ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/sem */ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmall */ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmmax */ @@ -4923,7 +4928,25 @@ lxpr_read_sys_kernel_pid_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) lxpr_uiobuf_printf(uiobuf, "%d\n", maxpid); } -/* ARGSUSED */ +static void +lxpr_gen_uuid(char *uuid, size_t size) +{ + uint8_t r[16]; + if (random_get_bytes(r, sizeof (r)) != 0) { + (void) random_get_pseudo_bytes(r, sizeof (r)); + } + /* Set UUID version to 4 (random) */ + r[6] = 0x40 | (r[6] & 0x0f); + /* Set UUID variant to 1 */ + r[8] = 0x80 | (r[8] & 0x3f); + + (void) snprintf(uuid, size, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x" + "-%02x%02x%02x%02x%02x%02x", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], + r[9], r[10], r[11], r[12], r[13], r[14], r[15]); +} + static void lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) { @@ -4937,13 +4960,11 @@ lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) * safe choice if you need to identify a specific boot on a specific * booted kernel. * - * We'll just generate a random ID if necessary. On Linux the format - * appears to resemble a uuid but since it is not documented to be a - * uuid, we don't worry about that. + * On Linux the format appears to resemble a uuid so stick with that. */ zone_t *zone = LXPTOZ(lxpnp); lx_zone_data_t *lxzd = ztolxzd(zone); - char bootid[LX_BOOTID_LEN]; + char bootid[UUID_PRINTABLE_STRING_LENGTH]; ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RAND_BOOTID); ASSERT(zone->zone_brand == &lx_brand); @@ -4951,30 +4972,7 @@ lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) mutex_enter(&lxzd->lxzd_lock); if (lxzd->lxzd_bootid[0] == '\0') { - int i; - - for (i = 0; i < 5; i++) { - u_longlong_t n; - char s[32]; - - (void) random_get_bytes((uint8_t *)&n, sizeof (n)); - switch (i) { - case 0: (void) snprintf(s, sizeof (s), "%08llx", n); - s[8] = '\0'; - break; - case 4: (void) snprintf(s, sizeof (s), "%012llx", n); - s[12] = '\0'; - break; - default: (void) snprintf(s, sizeof (s), "%04llx", n); - s[4] = '\0'; - break; - } - if (i > 0) - (void) strlcat(lxzd->lxzd_bootid, "-", - sizeof (lxzd->lxzd_bootid)); - (void) strlcat(lxzd->lxzd_bootid, s, - sizeof (lxzd->lxzd_bootid)); - } + lxpr_gen_uuid(lxzd->lxzd_bootid, sizeof (lxzd->lxzd_bootid)); } (void) strlcpy(bootid, lxzd->lxzd_bootid, sizeof (bootid)); mutex_exit(&lxzd->lxzd_lock); @@ -4995,6 +4993,24 @@ lxpr_read_sys_kernel_rand_entavl(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) lxpr_uiobuf_printf(uiobuf, "%d\n", swrand_stats.ss_entEst); } +static void +lxpr_read_sys_kernel_rand_uuid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) +{ + /* + * Each read from this read-only file should return a new + * random 128-bit UUID string in the standard UUID format. + */ + zone_t *zone = LXPTOZ(lxpnp); + char uuid[UUID_PRINTABLE_STRING_LENGTH]; + + ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RAND_UUID); + ASSERT(zone->zone_brand == &lx_brand); + + lxpr_gen_uuid(uuid, sizeof (uuid)); + + lxpr_uiobuf_printf(uiobuf, "%s\n", uuid); +} + /* ARGSUSED */ static void lxpr_read_sys_kernel_sem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h index 85aa5e34bd..35b1bddb03 100644 --- a/usr/src/uts/common/brand/lx/sys/lx_brand.h +++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h @@ -41,6 +41,7 @@ #include <sys/cpuvar.h> #include <sys/lx_futex.h> #include <sys/lx_userhz.h> +#include <sys/uuid.h> #endif #ifdef __cplusplus @@ -397,9 +398,6 @@ typedef struct lx_proc_data { #define LX_AFF_ULONGS (LX_NCPU / (8 * sizeof (ulong_t))) typedef ulong_t lx_affmask_t[LX_AFF_ULONGS]; -/* Length of proc boot_id string */ -#define LX_BOOTID_LEN 37 - /* * Flag values for uc_brand_data[0] in the ucontext_t: */ @@ -637,7 +635,7 @@ typedef struct lx_zone_data { char lxzd_kernel_release[LX_KERN_RELEASE_MAX]; char lxzd_kernel_version[LX_KERN_VERSION_MAX]; ksocket_t lxzd_ioctl_sock; - char lxzd_bootid[LX_BOOTID_LEN]; /* procfs boot_id */ + char lxzd_bootid[UUID_PRINTABLE_STRING_LENGTH]; /* procfs boot_id */ gid_t lxzd_ttygrp; /* tty gid for pty chown */ vfs_t *lxzd_cgroup; /* cgroup for this zone */ pid_t lxzd_lockd_pid; /* pid of NFS lockd */ diff --git a/usr/src/uts/common/io/mlxcx/mlxcx.c b/usr/src/uts/common/io/mlxcx/mlxcx.c index 9aae5244de..f74d093b9c 100644 --- a/usr/src/uts/common/io/mlxcx/mlxcx.c +++ b/usr/src/uts/common/io/mlxcx/mlxcx.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2020, The University of Queensland + * Copyright 2021, The University of Queensland * Copyright (c) 2018, Joyent, Inc. * Copyright 2020 RackTop Systems, Inc. */ @@ -2365,12 +2365,28 @@ mlxcx_setup_eq(mlxcx_t *mlxp, uint_t vec, uint64_t events) return (B_FALSE); } mleq->mleq_state |= MLXCX_EQ_INTR_ENABLED; + mleq->mleq_state |= MLXCX_EQ_ATTACHING; mlxcx_arm_eq(mlxp, mleq); mutex_exit(&mleq->mleq_mtx); return (B_TRUE); } +static void +mlxcx_eq_set_attached(mlxcx_t *mlxp) +{ + uint_t vec; + mlxcx_event_queue_t *mleq; + + for (vec = 0; vec < mlxp->mlx_intr_count; ++vec) { + mleq = &mlxp->mlx_eqs[vec]; + + mutex_enter(&mleq->mleq_mtx); + mleq->mleq_state &= ~MLXCX_EQ_ATTACHING; + mutex_exit(&mleq->mleq_mtx); + } +} + static boolean_t mlxcx_setup_async_eqs(mlxcx_t *mlxp) { @@ -2764,7 +2780,10 @@ mlxcx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) * Set up asynchronous event queue which handles control type events * like PAGE_REQUEST and CMD completion events. * - * This will enable and arm the interrupt on EQ 0. + * This will enable and arm the interrupt on EQ 0. Note that only page + * reqs and cmd completions will be handled until we call + * mlxcx_eq_set_attached further down (this way we don't need an extra + * set of locks over the mlxcx_t sub-structs not allocated yet) */ if (!mlxcx_setup_async_eqs(mlxp)) { goto err; @@ -2891,6 +2910,12 @@ mlxcx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) } mlxp->mlx_attach |= MLXCX_ATTACH_MAC_HDL; + /* + * This tells the interrupt handlers they can start processing events + * other than cmd completions and page requests. + */ + mlxcx_eq_set_attached(mlxp); + return (DDI_SUCCESS); err: diff --git a/usr/src/uts/common/io/mlxcx/mlxcx.h b/usr/src/uts/common/io/mlxcx/mlxcx.h index e28fe89806..68da65765f 100644 --- a/usr/src/uts/common/io/mlxcx/mlxcx.h +++ b/usr/src/uts/common/io/mlxcx/mlxcx.h @@ -10,7 +10,7 @@ */ /* - * Copyright 2020, The University of Queensland + * Copyright 2021, The University of Queensland * Copyright (c) 2018, Joyent, Inc. * Copyright 2020 RackTop Systems, Inc. */ @@ -318,6 +318,7 @@ typedef enum { MLXCX_EQ_INTR_ENABLED = 1 << 5, /* ddi_intr_enable()'d */ MLXCX_EQ_INTR_ACTIVE = 1 << 6, /* 'rupt handler running */ MLXCX_EQ_INTR_QUIESCE = 1 << 7, /* 'rupt handler to quiesce */ + MLXCX_EQ_ATTACHING = 1 << 8, /* mlxcx_attach still running */ } mlxcx_eventq_state_t; typedef struct mlxcx_bf { diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_gld.c b/usr/src/uts/common/io/mlxcx/mlxcx_gld.c index 941eb0f9e7..2c41f4ddeb 100644 --- a/usr/src/uts/common/io/mlxcx/mlxcx_gld.c +++ b/usr/src/uts/common/io/mlxcx/mlxcx_gld.c @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2020, the University of Queensland + * Copyright (c) 2021, the University of Queensland * Copyright 2020 RackTop Systems, Inc. */ @@ -1493,6 +1493,8 @@ mlxcx_register_mac(mlxcx_t *mlxp) VERIFY3U(mlxp->mlx_nports, ==, 1); port = &mlxp->mlx_ports[0]; + mutex_enter(&port->mlp_mtx); + mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; mac->m_driver = mlxp; mac->m_dip = mlxp->mlx_dip; @@ -1510,6 +1512,8 @@ mlxcx_register_mac(mlxcx_t *mlxp) } mac_free(mac); + mutex_exit(&port->mlp_mtx); + mlxcx_update_link_state(mlxp, port); return (ret == 0); diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_intr.c b/usr/src/uts/common/io/mlxcx/mlxcx_intr.c index 53ea4d683e..e2f5141171 100644 --- a/usr/src/uts/common/io/mlxcx/mlxcx_intr.c +++ b/usr/src/uts/common/io/mlxcx/mlxcx_intr.c @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2020, the University of Queensland + * Copyright (c) 2021, the University of Queensland * Copyright 2020 RackTop Systems, Inc. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ @@ -422,7 +422,9 @@ mlxcx_update_link_state(mlxcx_t *mlxp, mlxcx_port_t *port) default: ls = LINK_STATE_UNKNOWN; } - mac_link_update(mlxp->mlx_mac_hdl, ls); + + if (mlxp->mlx_mac_hdl != NULL) + mac_link_update(mlxp->mlx_mac_hdl, ls); mutex_exit(&port->mlp_mtx); } @@ -762,10 +764,16 @@ mlxcx_intr_async(caddr_t arg, caddr_t arg2) DTRACE_PROBE2(event, mlxcx_t *, mlxp, mlxcx_eventq_ent_t *, ent); + /* + * Handle events which can be processed while we're still in + * mlxcx_attach(). Everything on the mlxcx_t which these events + * use must be allocated and set up prior to the call to + * mlxcx_setup_async_eqs(). + */ switch (ent->mleqe_event_type) { case MLXCX_EVENT_CMD_COMPLETION: mlxcx_cmd_completion(mlxp, ent); - break; + continue; case MLXCX_EVENT_PAGE_REQUEST: func = from_be16(ent->mleqe_page_request. mled_page_request_function_id); @@ -783,7 +791,7 @@ mlxcx_intr_async(caddr_t arg, caddr_t arg2) mutex_exit(¶m->mla_mtx); mlxcx_warn(mlxp, "Unexpected page request " "whilst another is pending"); - break; + continue; } param->mla_pages.mlp_npages = (int32_t)from_be32(ent->mleqe_page_request. @@ -795,7 +803,20 @@ mlxcx_intr_async(caddr_t arg, caddr_t arg2) taskq_dispatch_ent(mlxp->mlx_async_tq, mlxcx_pages_task, param, 0, ¶m->mla_tqe); - break; + continue; + } + + /* + * All other events should be ignored while in attach. + */ + mutex_enter(&mleq->mleq_mtx); + if (mleq->mleq_state & MLXCX_EQ_ATTACHING) { + mutex_exit(&mleq->mleq_mtx); + continue; + } + mutex_exit(&mleq->mleq_mtx); + + switch (ent->mleqe_event_type) { case MLXCX_EVENT_PORT_STATE: portn = get_bits8( ent->mleqe_port_state.mled_port_state_port_num, @@ -1055,17 +1076,30 @@ mlxcx_intr_n(caddr_t arg, caddr_t arg2) } mleq->mleq_badintrs = 0; + mutex_enter(&mleq->mleq_mtx); ASSERT(mleq->mleq_state & MLXCX_EQ_ARMED); mleq->mleq_state &= ~MLXCX_EQ_ARMED; +#if defined(DEBUG) + /* + * If we're still in mlxcx_attach and an intr_n fired, something really + * weird is going on. This shouldn't happen in the absence of a driver + * or firmware bug, so in the interests of minimizing branches in this + * function this check is under DEBUG. + */ + if (mleq->mleq_state & MLXCX_EQ_ATTACHING) { + mutex_exit(&mleq->mleq_mtx); + mlxcx_warn(mlxp, "intr_n (%u) fired during attach, disabling " + "vector", mleq->mleq_intr_index); + mlxcx_fm_ereport(mlxp, DDI_FM_DEVICE_INVAL_STATE); + ddi_fm_service_impact(mlxp->mlx_dip, DDI_SERVICE_LOST); + (void) ddi_intr_disable(mlxp->mlx_intr_handles[ + mleq->mleq_intr_index]); + goto done; + } +#endif + mutex_exit(&mleq->mleq_mtx); for (; ent != NULL; ent = mlxcx_eq_next(mleq)) { - if (ent->mleqe_event_type != MLXCX_EVENT_COMPLETION) { - mlxcx_fm_ereport(mlxp, DDI_FM_DEVICE_INVAL_STATE); - ddi_fm_service_impact(mlxp->mlx_dip, DDI_SERVICE_LOST); - (void) ddi_intr_disable(mlxp->mlx_intr_handles[ - mleq->mleq_intr_index]); - goto done; - } ASSERT3U(ent->mleqe_event_type, ==, MLXCX_EVENT_COMPLETION); probe.mlcq_num = @@ -1075,7 +1109,7 @@ mlxcx_intr_n(caddr_t arg, caddr_t arg2) mutex_exit(&mleq->mleq_mtx); if (mlcq == NULL) - continue; + goto update_eq; mlwq = mlcq->mlcq_wq; diff --git a/usr/src/uts/common/io/nvme/nvme.c b/usr/src/uts/common/io/nvme/nvme.c index 89debf9b07..8215adaed6 100644 --- a/usr/src/uts/common/io/nvme/nvme.c +++ b/usr/src/uts/common/io/nvme/nvme.c @@ -1103,6 +1103,22 @@ nvme_submit_cmd_common(nvme_qpair_t *qp, nvme_cmd_t *cmd) cmd->nc_completed = B_FALSE; /* + * Now that we hold the queue pair lock, we must check whether or not + * the controller has been listed as dead (e.g. was removed due to + * hotplug). This is necessary as otherwise we could race with + * nvme_remove_callback(). Because this has not been enqueued, we don't + * call nvme_unqueue_cmd(), which is why we must manually decrement the + * semaphore. + */ + if (cmd->nc_nvme->n_dead) { + taskq_dispatch_ent(qp->nq_cq->ncq_cmd_taskq, cmd->nc_callback, + cmd, TQ_NOSLEEP, &cmd->nc_tqent); + sema_v(&qp->nq_sema); + mutex_exit(&qp->nq_mutex); + return; + } + + /* * Try to insert the cmd into the active cmd array at the nq_next_cmd * slot. If the slot is already occupied advance to the next slot and * try again. This can happen for long running commands like async event diff --git a/usr/src/uts/common/io/pciex/pcie.c b/usr/src/uts/common/io/pciex/pcie.c index 35a0190be7..df6b2d189b 100644 --- a/usr/src/uts/common/io/pciex/pcie.c +++ b/usr/src/uts/common/io/pciex/pcie.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Joyent, Inc. + * Copyright 2021 Oxide Computer Company */ #include <sys/sysmacros.h> @@ -1189,11 +1190,18 @@ pcie_capture_speeds(dev_info_t *dip) uint16_t vers, status; uint32_t cap, cap2, ctl2; pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); + dev_info_t *rcdip; if (!PCIE_IS_PCIE(bus_p)) return; - vers = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP); + rcdip = pcie_get_rc_dip(dip); + if (bus_p->bus_cfg_hdl == NULL) { + vers = pci_cfgacc_get16(rcdip, bus_p->bus_bdf, + bus_p->bus_pcie_off + PCIE_PCIECAP); + } else { + vers = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP); + } if (vers == PCI_EINVAL16) return; vers &= PCIE_PCIECAP_VER_MASK; @@ -1207,10 +1215,17 @@ pcie_capture_speeds(dev_info_t *dip) ctl2 = 0; break; case PCIE_PCIECAP_VER_2_0: - cap2 = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP2); + if (bus_p->bus_cfg_hdl == NULL) { + cap2 = pci_cfgacc_get32(rcdip, bus_p->bus_bdf, + bus_p->bus_pcie_off + PCIE_LINKCAP2); + ctl2 = pci_cfgacc_get16(rcdip, bus_p->bus_bdf, + bus_p->bus_pcie_off + PCIE_LINKCTL2); + } else { + cap2 = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP2); + ctl2 = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL2); + } if (cap2 == PCI_EINVAL32) cap2 = 0; - ctl2 = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL2); if (ctl2 == PCI_EINVAL16) ctl2 = 0; break; @@ -1219,8 +1234,15 @@ pcie_capture_speeds(dev_info_t *dip) return; } - status = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS); - cap = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP); + if (bus_p->bus_cfg_hdl == NULL) { + status = pci_cfgacc_get16(rcdip, bus_p->bus_bdf, + bus_p->bus_pcie_off + PCIE_LINKSTS); + cap = pci_cfgacc_get32(rcdip, bus_p->bus_bdf, + bus_p->bus_pcie_off + PCIE_LINKCAP); + } else { + status = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS); + cap = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP); + } if (status == PCI_EINVAL16 || cap == PCI_EINVAL32) return; @@ -1659,6 +1681,8 @@ initial_done: pcie_init_plat(dip); + pcie_capture_speeds(dip); + final_done: PCIE_DBG("Add %s(dip 0x%p, bdf 0x%x, secbus 0x%x)\n", diff --git a/usr/src/uts/common/io/vioif/vioif.c b/usr/src/uts/common/io/vioif/vioif.c index cac90d3073..368af5381d 100644 --- a/usr/src/uts/common/io/vioif/vioif.c +++ b/usr/src/uts/common/io/vioif/vioif.c @@ -12,7 +12,7 @@ /* * Copyright 2013 Nexenta Inc. All rights reserved. * Copyright (c) 2014, 2016 by Delphix. All rights reserved. - * Copyright 2019 Joyent, Inc. + * Copyright 2021 Joyent, Inc. * Copyright 2019 Joshua M. Clulow <josh@sysmgr.org> */ @@ -328,6 +328,32 @@ vioif_rx_free_callback(caddr_t free_arg) mutex_exit(&vif->vif_mutex); } +static vioif_ctrlbuf_t * +vioif_ctrlbuf_alloc(vioif_t *vif) +{ + vioif_ctrlbuf_t *cb; + + VERIFY(MUTEX_HELD(&vif->vif_mutex)); + + if ((cb = list_remove_head(&vif->vif_ctrlbufs)) != NULL) { + vif->vif_nctrlbufs_alloc++; + } + + return (cb); +} + +static void +vioif_ctrlbuf_free(vioif_t *vif, vioif_ctrlbuf_t *cb) +{ + VERIFY(MUTEX_HELD(&vif->vif_mutex)); + + VERIFY3U(vif->vif_nctrlbufs_alloc, >, 0); + vif->vif_nctrlbufs_alloc--; + + virtio_chain_clear(cb->cb_chain); + list_insert_head(&vif->vif_ctrlbufs, cb); +} + static void vioif_free_bufs(vioif_t *vif) { @@ -408,6 +434,37 @@ vioif_free_bufs(vioif_t *vif) vif->vif_rxbufs_mem = NULL; vif->vif_rxbufs_capacity = 0; } + + if (vif->vif_has_ctrlq) { + VERIFY3U(vif->vif_nctrlbufs_alloc, ==, 0); + for (uint_t i = 0; i < vif->vif_ctrlbufs_capacity; i++) { + vioif_ctrlbuf_t *cb = &vif->vif_ctrlbufs_mem[i]; + + /* + * Ensure that this ctrlbuf is now in the free list + */ + VERIFY(list_link_active(&cb->cb_link)); + list_remove(&vif->vif_ctrlbufs, cb); + + if (cb->cb_dma != NULL) { + virtio_dma_free(cb->cb_dma); + cb->cb_dma = NULL; + } + + if (cb->cb_chain != NULL) { + virtio_chain_free(cb->cb_chain); + cb->cb_chain = NULL; + } + } + VERIFY(list_is_empty(&vif->vif_ctrlbufs)); + if (vif->vif_ctrlbufs_mem != NULL) { + kmem_free(vif->vif_ctrlbufs_mem, + sizeof (vioif_ctrlbuf_t) * + vif->vif_ctrlbufs_capacity); + vif->vif_ctrlbufs_mem = NULL; + vif->vif_ctrlbufs_capacity = 0; + } + } } static int @@ -434,6 +491,16 @@ vioif_alloc_bufs(vioif_t *vif) list_create(&vif->vif_rxbufs, sizeof (vioif_rxbuf_t), offsetof(vioif_rxbuf_t, rb_link)); + if (vif->vif_has_ctrlq) { + vif->vif_ctrlbufs_capacity = MIN(VIRTIO_NET_CTRL_BUFS, + virtio_queue_size(vif->vif_ctrl_vq)); + vif->vif_ctrlbufs_mem = kmem_zalloc( + sizeof (vioif_ctrlbuf_t) * vif->vif_ctrlbufs_capacity, + KM_SLEEP); + } + list_create(&vif->vif_ctrlbufs, sizeof (vioif_ctrlbuf_t), + offsetof(vioif_ctrlbuf_t, cb_link)); + /* * Do not loan more than half of our allocated receive buffers into * the networking stack. @@ -450,6 +517,9 @@ vioif_alloc_bufs(vioif_t *vif) for (uint_t i = 0; i < vif->vif_rxbufs_capacity; i++) { list_insert_tail(&vif->vif_rxbufs, &vif->vif_rxbufs_mem[i]); } + for (uint_t i = 0; i < vif->vif_ctrlbufs_capacity; i++) { + list_insert_tail(&vif->vif_ctrlbufs, &vif->vif_ctrlbufs_mem[i]); + } /* * Start from the DMA attribute template common to both transmit and @@ -486,6 +556,26 @@ vioif_alloc_bufs(vioif_t *vif) } /* + * Control queue buffers are also small (less than a page), so we'll + * also request a single cookie for them. + */ + for (vioif_ctrlbuf_t *cb = list_head(&vif->vif_ctrlbufs); cb != NULL; + cb = list_next(&vif->vif_ctrlbufs, cb)) { + if ((cb->cb_dma = virtio_dma_alloc(vif->vif_virtio, + VIOIF_CTRL_SIZE, &attr, + DDI_DMA_STREAMING | DDI_DMA_RDWR, KM_SLEEP)) == NULL) { + goto fail; + } + VERIFY3U(virtio_dma_ncookies(cb->cb_dma), ==, 1); + + if ((cb->cb_chain = virtio_chain_alloc(vif->vif_ctrl_vq, + KM_SLEEP)) == NULL) { + goto fail; + } + virtio_chain_data_set(cb->cb_chain, cb); + } + + /* * The receive buffers are larger, and we can tolerate a large number * of segments. Adjust the SGL entry count, setting aside one segment * for the virtio net header. @@ -533,6 +623,128 @@ fail: } static int +vioif_ctrlq_req(vioif_t *vif, uint8_t class, uint8_t cmd, void *data, + size_t datalen) +{ + vioif_ctrlbuf_t *cb = NULL; + virtio_chain_t *vic = NULL; + uint8_t *p = NULL; + uint64_t pa = 0; + uint8_t *ackp = NULL; + struct virtio_net_ctrlq_hdr hdr = { + .vnch_class = class, + .vnch_command = cmd, + }; + const size_t hdrlen = sizeof (hdr); + const size_t acklen = 1; /* the ack is always 1 byte */ + size_t totlen = hdrlen + datalen + acklen; + int r = DDI_SUCCESS; + + /* + * We shouldn't be called unless the ctrlq feature has been + * negotiated with the host + */ + VERIFY(vif->vif_has_ctrlq); + + mutex_enter(&vif->vif_mutex); + cb = vioif_ctrlbuf_alloc(vif); + if (cb == NULL) { + vif->vif_noctrlbuf++; + mutex_exit(&vif->vif_mutex); + r = DDI_FAILURE; + goto done; + } + mutex_exit(&vif->vif_mutex); + + if (totlen > virtio_dma_size(cb->cb_dma)) { + vif->vif_ctrlbuf_toosmall++; + r = DDI_FAILURE; + goto done; + } + + /* + * Clear the entire buffer. Technically not necessary, but useful + * if trying to troubleshoot an issue, and probably not a bad idea + * to not let any old data linger. + */ + p = virtio_dma_va(cb->cb_dma, 0); + bzero(p, virtio_dma_size(cb->cb_dma)); + + /* + * We currently do not support VIRTIO_F_ANY_LAYOUT. That means, + * that we must put the header, the data, and the ack in their + * own respective descriptors. Since all the currently supported + * control queue commands take _very_ small amounts of data, we + * use a single DMA buffer for all of it, but use 3 descriptors to + * reference (respectively) the header, the data, and the ack byte + * within that memory to adhere to the virtio spec. + * + * If we add support for control queue features such as custom + * MAC filtering tables, which might require larger amounts of + * memory, we likely will want to add more sophistication here + * and optionally use additional allocated memory to hold that + * data instead of a fixed size buffer. + * + * Copy the header. + */ + bcopy(&hdr, p, sizeof (hdr)); + pa = virtio_dma_cookie_pa(cb->cb_dma, 0); + if ((r = virtio_chain_append(cb->cb_chain, + pa, hdrlen, VIRTIO_DIR_DEVICE_READS)) != DDI_SUCCESS) { + goto done; + } + + /* + * Copy the request data + */ + p = virtio_dma_va(cb->cb_dma, hdrlen); + bcopy(data, p, datalen); + if ((r = virtio_chain_append(cb->cb_chain, + pa + hdrlen, datalen, VIRTIO_DIR_DEVICE_READS)) != DDI_SUCCESS) { + goto done; + } + + /* + * We already cleared the buffer, so don't need to copy out a 0 for + * the ack byte. Just add a descriptor for that spot. + */ + ackp = virtio_dma_va(cb->cb_dma, hdrlen + datalen); + if ((r = virtio_chain_append(cb->cb_chain, + pa + hdrlen + datalen, acklen, + VIRTIO_DIR_DEVICE_WRITES)) != DDI_SUCCESS) { + goto done; + } + + virtio_dma_sync(cb->cb_dma, DDI_DMA_SYNC_FORDEV); + virtio_chain_submit(cb->cb_chain, B_TRUE); + + /* + * Spin waiting for response. + */ + mutex_enter(&vif->vif_mutex); + while ((vic = virtio_queue_poll(vif->vif_ctrl_vq)) == NULL) { + mutex_exit(&vif->vif_mutex); + delay(drv_usectohz(1000)); + mutex_enter(&vif->vif_mutex); + } + + virtio_dma_sync(cb->cb_dma, DDI_DMA_SYNC_FORCPU); + VERIFY3P(virtio_chain_data(vic), ==, cb); + mutex_exit(&vif->vif_mutex); + + if (*ackp != VIRTIO_NET_CQ_OK) { + r = DDI_FAILURE; + } + +done: + mutex_enter(&vif->vif_mutex); + vioif_ctrlbuf_free(vif, cb); + mutex_exit(&vif->vif_mutex); + + return (r); +} + +static int vioif_m_multicst(void *arg, boolean_t add, const uint8_t *mcst_addr) { /* @@ -549,11 +761,25 @@ vioif_m_multicst(void *arg, boolean_t add, const uint8_t *mcst_addr) static int vioif_m_setpromisc(void *arg, boolean_t on) { - /* - * Even though we cannot currently enable promiscuous mode, we return - * success here to allow tools like snoop(1M) to continue to function. - */ - return (0); + vioif_t *vif = arg; + uint8_t val = on ? 1 : 0; + + if (!vif->vif_has_ctrlq_rx) { + /* + * While most hypervisors support the control queue, bhyve + * (or more specifically viona) on illumos currently does not. + * + * Until that support is added to viona, we pretend + * the request always succeeds to match the historic behavior + * of the illumos vioif driver. Once that support has been + * added to viona, we should do the correct thing and return + * ENOTSUP + */ + return (0); + } + + return (vioif_ctrlq_req(vif, VIRTIO_NET_CTRL_RX, + VIRTIO_NET_CTRL_RX_PROMISC, &val, sizeof (val))); } static int @@ -1655,6 +1881,17 @@ vioif_check_features(vioif_t *vif) vif->vif_tx_tso6 = 1; } } + + if (vioif_has_feature(vif, VIRTIO_NET_F_CTRL_VQ)) { + vif->vif_has_ctrlq = 1; + + /* + * The VIRTIO_NET_F_CTRL_VQ feature must be enabled if there's + * any chance of the VIRTIO_NET_F_CTRL_RX being enabled. + */ + if (vioif_has_feature(vif, VIRTIO_NET_F_CTRL_RX)) + vif->vif_has_ctrlq_rx = 1; + } } static int @@ -1726,6 +1963,13 @@ vioif_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) goto fail; } + if (vioif_has_feature(vif, VIRTIO_NET_F_CTRL_VQ) && + (vif->vif_ctrl_vq = virtio_queue_alloc(vio, + VIRTIO_NET_VIRTQ_CONTROL, "ctrlq", NULL, vif, + B_FALSE, VIOIF_MAX_SEGS)) == NULL) { + goto fail; + } + if (virtio_init_complete(vio, vioif_select_interrupt_types()) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "failed to complete Virtio init"); @@ -1734,6 +1978,8 @@ vioif_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) virtio_queue_no_interrupt(vif->vif_rx_vq, B_TRUE); virtio_queue_no_interrupt(vif->vif_tx_vq, B_TRUE); + if (vif->vif_ctrl_vq != NULL) + virtio_queue_no_interrupt(vif->vif_ctrl_vq, B_TRUE); mutex_init(&vif->vif_mutex, NULL, MUTEX_DRIVER, virtio_intr_pri(vio)); mutex_enter(&vif->vif_mutex); diff --git a/usr/src/uts/common/io/vioif/vioif.h b/usr/src/uts/common/io/vioif/vioif.h index 9f750c9b8a..8e7dae320c 100644 --- a/usr/src/uts/common/io/vioif/vioif.h +++ b/usr/src/uts/common/io/vioif/vioif.h @@ -10,7 +10,7 @@ */ /* - * Copyright 2019 Joyent, Inc. + * Copyright 2021 Joyent, Inc. */ /* @@ -167,7 +167,9 @@ extern "C" { VIRTIO_NET_F_HOST_TSO6 | \ VIRTIO_NET_F_HOST_ECN | \ VIRTIO_NET_F_MAC | \ - VIRTIO_NET_F_MTU) + VIRTIO_NET_F_MTU | \ + VIRTIO_NET_F_CTRL_VQ | \ + VIRTIO_NET_F_CTRL_RX) /* * VIRTIO NETWORK HEADER @@ -201,6 +203,37 @@ struct virtio_net_hdr { #define VIRTIO_NET_HDR_GSO_TCPV6 4 #define VIRTIO_NET_HDR_GSO_ECN 0x80 +/* + * VIRTIO CONTROL VIRTQUEUE HEADER + * + * This structure appears at the start of each control virtqueue request. + */ +struct virtio_net_ctrlq_hdr { + uint8_t vnch_class; + uint8_t vnch_command; +} __packed; + +/* + * Contol Queue Classes + */ +#define VIRTIO_NET_CTRL_RX 0 + +/* + * CTRL_RX commands + */ +#define VIRTIO_NET_CTRL_RX_PROMISC 0 +#define VIRTIO_NET_CTRL_RX_ALLMULTI 1 +#define VIRTIO_NET_CTRL_RX_ALLUNI 2 +#define VIRTIO_NET_CTRL_RX_NOMULTI 3 +#define VIRTIO_NET_CTRL_RX_NOUNI 4 +#define VIRTIO_NET_CTRL_RX_NOBCAST 5 + +/* + * Control queue ack values + */ +#define VIRTIO_NET_CQ_OK 0 +#define VIRTIO_NET_CQ_ERR 1 + /* * DRIVER PARAMETERS @@ -216,6 +249,13 @@ struct virtio_net_hdr { #define VIRTIO_NET_RX_BUFS 256 /* + * Initially, only use a single buf for control queue requests (when + * present). If this becomes a bottleneck, we can simply increase this + * value as necessary. + */ +#define VIRTIO_NET_CTRL_BUFS 1 + +/* * The virtio net header and the first buffer segment share the same DMA * allocation. We round up the virtio header size to a multiple of 4 and add 2 * bytes so that the IP header, which starts immediately after the 14 or 18 @@ -261,6 +301,12 @@ struct virtio_net_hdr { */ #define VIOIF_TX_INLINE_SIZE (2 * 1024) +/* + * Control queue messages are very small. This is a rather arbitrary small + * bufer size that should be sufficiently large for any control queue + * messages we will send. + */ +#define VIOIF_CTRL_SIZE 256 /* * TYPE DEFINITIONS @@ -289,6 +335,15 @@ typedef struct vioif_rxbuf { list_node_t rb_link; } vioif_rxbuf_t; +typedef struct vioif_ctrlbuf { + vioif_t *cb_vioif; + + virtio_dma_t *cb_dma; + virtio_chain_t *cb_chain; + + list_node_t cb_link; +} vioif_ctrlbuf_t; + /* * Transmit buffers are also allocated in advance. DMA memory is allocated for * the virtio net header, and to hold small packets. Larger packets are mapped @@ -346,6 +401,7 @@ struct vioif { virtio_queue_t *vif_rx_vq; virtio_queue_t *vif_tx_vq; + virtio_queue_t *vif_ctrl_vq; /* TX virtqueue management resources */ boolean_t vif_tx_corked; @@ -366,6 +422,9 @@ struct vioif { */ unsigned int vif_mac_from_host:1; + unsigned int vif_has_ctrlq:1; + unsigned int vif_has_ctrlq_rx:1; + uint_t vif_mtu; uint_t vif_mtu_max; uint8_t vif_mac[ETHERADDRL]; @@ -395,6 +454,11 @@ struct vioif { uint_t vif_rxcopy_thresh; uint_t vif_txcopy_thresh; + list_t vif_ctrlbufs; + uint_t vif_nctrlbufs_alloc; + uint_t vif_ctrlbufs_capacity; + vioif_ctrlbuf_t *vif_ctrlbufs_mem; + /* * Statistics visible through mac: */ @@ -424,6 +488,9 @@ struct vioif { uint64_t vif_txfail_indirect_limit; uint64_t vif_stat_tx_reclaim; + + uint64_t vif_noctrlbuf; + uint64_t vif_ctrlbuf_toosmall; }; #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/pci.h b/usr/src/uts/common/sys/pci.h index d62d19c3a5..5dd6762ab5 100644 --- a/usr/src/uts/common/sys/pci.h +++ b/usr/src/uts/common/sys/pci.h @@ -621,6 +621,8 @@ extern "C" { #define PCI_CAP_ID_MSI_X 0x11 /* MSI-X supported */ #define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Config supported */ #define PCI_CAP_ID_FLR 0x13 /* Function Level Reset supported */ +#define PCI_CAP_ID_EA 0x14 /* Enhanced Allocation */ +#define PCI_CAP_ID_FPB 0x15 /* Flattening Portal Bridge */ /* * Capability next entry pointer values @@ -909,13 +911,16 @@ typedef struct pcix_attr { #define PCI_MSI_CTRL 0x02 /* MSI control register, 2 bytes */ #define PCI_MSI_ADDR_OFFSET 0x04 /* MSI 32-bit msg address, 4 bytes */ #define PCI_MSI_32BIT_DATA 0x08 /* MSI 32-bit msg data, 2 bytes */ +#define PCI_MSI_32BIT_EXTDATA 0x0A /* MSI 32-bit msg ext data, 2 bytes */ #define PCI_MSI_32BIT_MASK 0x0C /* MSI 32-bit mask bits, 4 bytes */ #define PCI_MSI_32BIT_PENDING 0x10 /* MSI 32-bit pending bits, 4 bytes */ /* * PCI Message Signalled Interrupts (MSI) capability entry offsets for 64-bit */ +#define PCI_MSI_64BIT_ADDR 0x08 /* MSI 64-bit upper address, 4 bytes */ #define PCI_MSI_64BIT_DATA 0x0C /* MSI 64-bit msg data, 2 bytes */ +#define PCI_MSI_64BIT_EXTDATA 0x0E /* MSI 64-bit msg ext data, 2 bytes */ #define PCI_MSI_64BIT_MASKBITS 0x10 /* MSI 64-bit mask bits, 4 bytes */ #define PCI_MSI_64BIT_PENDING 0x14 /* MSI 64-bit pending bits, 4 bytes */ diff --git a/usr/src/uts/common/sys/pcie.h b/usr/src/uts/common/sys/pcie.h index e8f91a1390..840c31a328 100644 --- a/usr/src/uts/common/sys/pcie.h +++ b/usr/src/uts/common/sys/pcie.h @@ -536,6 +536,7 @@ extern "C" { #define PCIE_EXT_CAP_NEXT_PTR_MASK 0xFFF #define PCIE_EXT_CAP_NEXT_PTR_NULL 0x0 +#define PCIE_EXT_CAP_MAX_PTR 0x3c0 /* max. number of caps */ /* * PCI-Express Enhanced Capability Identifier Values @@ -559,6 +560,7 @@ extern "C" { #define PCIE_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virt. */ #define PCIE_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virt. */ #define PCIE_EXT_CAP_ID_MULTICAST 0x12 /* Multicast Services */ +#define PCIE_EXT_CAP_ID_PGREQ 0x13 /* Page Request */ #define PCIE_EXT_CAP_ID_EA 0x14 /* Enhanced Allocation */ #define PCIE_EXT_CAP_ID_RESIZE_BAR 0x15 /* Resizable BAR */ #define PCIE_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */ @@ -573,11 +575,15 @@ extern "C" { #define PCIE_EXT_CAP_ID_FRS 0x21 /* Function Ready Stat. Queue */ #define PCIE_EXT_CAP_ID_RTR 0x22 /* Readiness Time Reporting */ #define PCIE_EXT_CAP_ID_DVS 0x23 /* Designated Vendor-Specific */ +#define PCIE_EXT_CAP_ID_VFRBAR 0x24 /* VF Resizable BAR */ #define PCIE_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ -#define PCIE_EXT_CAP_ID_PL16GTE 0x26 /* Physical Layer 16.0 GT/s */ +#define PCIE_EXT_CAP_ID_PL16GT 0x26 /* Physical Layer 16.0 GT/s */ #define PCIE_EXT_CAP_ID_LANE_MARGIN 0x27 /* Lane Margining */ #define PCIE_EXT_CAP_ID_HIEARCHY_ID 0x28 /* Hierarchy ID */ #define PCIE_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Mgmt */ +#define PCIE_EXT_CAP_ID_PL32GT 0x2A /* Physical Layer 32.0 GT/s */ +#define PCIE_EXT_CAP_ID_AP 0x2B /* Alternate Protocol */ +#define PCIE_EXT_CAP_ID_SFI 0x2C /* Sys. Firmware Intermediary */ /* * PCI-Express Advanced Error Reporting Extended Capability Offsets @@ -596,6 +602,7 @@ extern "C" { #define PCIE_AER_RE_STS 0x30 /* Root Error Status */ #define PCIE_AER_CE_SRC_ID 0x34 /* Error Source ID */ #define PCIE_AER_ERR_SRC_ID 0x36 /* Error Source ID */ +#define PCIE_AER_TLP_PRE_LOG 0x38 /* TLP Prefix Log */ /* Bridges Only */ #define PCIE_AER_SUCE_STS 0x2c /* Secondary UCE Status */ diff --git a/usr/src/uts/i86pc/io/vmm/vmm.c b/usr/src/uts/i86pc/io/vmm/vmm.c index ad3a9f548f..dac488713a 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm.c +++ b/usr/src/uts/i86pc/io/vmm/vmm.c @@ -39,7 +39,7 @@ * * Copyright 2015 Pluribus Networks Inc. * Copyright 2021 Joyent, Inc. - * Copyright 2020 Oxide Computer Company + * Copyright 2021 Oxide Computer Company */ #include <sys/cdefs.h> @@ -2933,7 +2933,15 @@ vm_inject_init(struct vm *vm, int vcpuid) vcpu = &vm->vcpu[vcpuid]; vcpu_lock(vcpu); vcpu->run_state |= VRS_PEND_INIT; + /* + * As part of queuing the INIT request, clear any pending SIPI. It + * would not otherwise survive across the reset of the vCPU when it + * undergoes the requested INIT. We would not want it to linger when it + * could be mistaken as a subsequent (after the INIT) SIPI request. + */ + vcpu->run_state &= ~VRS_PEND_SIPI; vcpu_notify_event_locked(vcpu, VCPU_NOTIFY_EXIT); + vcpu_unlock(vcpu); return (0); } diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c index 9267befd19..e668ee35a6 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c @@ -1873,14 +1873,14 @@ vmm_do_vm_destroy_locked(vmm_softc_t *sc, boolean_t clean_zsd, *hma_release = B_FALSE; - if (clean_zsd) { - vmm_zsd_rem_vm(sc); - } - if (vmm_drv_purge(sc) != 0) { return (EINTR); } + if (clean_zsd) { + vmm_zsd_rem_vm(sc); + } + /* Clean up devmem entries */ vmmdev_devmem_purge(sc); |