From b89fc615f42c703d6100c78de04791708d190e5e Mon Sep 17 00:00:00 2001 From: Andy Fiddaman Date: Sat, 20 Mar 2021 14:32:28 +0000 Subject: 13658 exception_lists/cstyle has stale entries Reviewed by: Toomas Soome Approved by: Robert Mustacchi --- exception_lists/cstyle | 318 +++++++++++++++++++++++-------------------------- 1 file changed, 148 insertions(+), 170 deletions(-) diff --git a/exception_lists/cstyle b/exception_lists/cstyle index f56e94af8d..cebfcf435b 100644 --- a/exception_lists/cstyle +++ b/exception_lists/cstyle @@ -3,22 +3,21 @@ usr/src/cmd/acpi/acpidump/apdump.c usr/src/cmd/acpi/acpidump/apfiles.c usr/src/cmd/acpi/acpidump/apmain.c usr/src/cmd/acpi/acpidump/osunixdir.c -usr/src/cmd/acpi/acpidump/tbprint.c -usr/src/cmd/acpi/acpidump/tbxfroot.c -usr/src/cmd/acpi/acpidump/utbuffer.c +usr/src/common/acpica/tables/tbprint.c +usr/src/common/acpica/tables/tbxfroot.c +usr/src/common/acpica/utilities/utbuffer.c usr/src/cmd/acpi/acpixtract/acpixtract.[ch] usr/src/cmd/acpi/acpixtract/axmain.[ch] usr/src/cmd/acpi/acpixtract/axutils.[ch] usr/src/cmd/acpi/common/getopt.c -usr/src/cmd/acpi/common/utascii.c -usr/src/cmd/acpi/common/utdebug.c -usr/src/cmd/acpi/common/utexcep.c -usr/src/cmd/acpi/common/utglobal.c -usr/src/cmd/acpi/common/utmath.c -usr/src/cmd/acpi/common/utnonansi.c -usr/src/cmd/acpi/common/utprint.c -usr/src/cmd/acpi/common/utxferror.c -sr/src/cmd/ast/libshell/common/illumos_cmdlist.h +usr/src/common/acpica/utilities/utascii.c +usr/src/common/acpica/utilities/utdebug.c +usr/src/common/acpica/utilities/utexcep.c +usr/src/common/acpica/utilities/utglobal.c +usr/src/common/acpica/utilities/utmath.c +usr/src/common/acpica/utilities/utnonansi.c +usr/src/common/acpica/utilities/utxferror.c +usr/src/cmd/ast/libshell/common/illumos_cmdlist.h usr/src/cmd/cxgbetool/* usr/src/cmd/hal/tools/hal_set_property.c usr/src/cmd/krb5/kadmin/cli/kadmin_ct.c @@ -117,11 +116,9 @@ usr/src/grub/grub-0.97/stage2/stage1_5.c usr/src/lib/libadt_jni/com/sun/audit/AuditSession.h usr/src/lib/libc/port/gen/arc4random_uniform.c usr/src/lib/libdtrace_jni/java/native/LocalConsumer.h -usr/src/lib/libdwarf/common/cmplrs/dwarf_addr_finder.h usr/src/lib/libdwarf/common/config.h usr/src/lib/libdwarf/common/dwarf_abbrev.c usr/src/lib/libdwarf/common/dwarf_abbrev.h -usr/src/lib/libdwarf/common/dwarf_addr_finder.c usr/src/lib/libdwarf/common/dwarf_alloc.c usr/src/lib/libdwarf/common/dwarf_alloc.h usr/src/lib/libdwarf/common/dwarf_arange.c @@ -137,7 +134,6 @@ usr/src/lib/libdwarf/common/dwarf_form.c usr/src/lib/libdwarf/common/dwarf_frame.c usr/src/lib/libdwarf/common/dwarf_frame.h usr/src/lib/libdwarf/common/dwarf_frame2.c -usr/src/lib/libdwarf/common/dwarf_frame3.c usr/src/lib/libdwarf/common/dwarf_funcs.c usr/src/lib/libdwarf/common/dwarf_funcs.h usr/src/lib/libdwarf/common/dwarf_global.c @@ -149,7 +145,6 @@ usr/src/lib/libdwarf/common/dwarf_init_finish.c usr/src/lib/libdwarf/common/dwarf_leb.c usr/src/lib/libdwarf/common/dwarf_line.c usr/src/lib/libdwarf/common/dwarf_line.h -usr/src/lib/libdwarf/common/dwarf_line2.c usr/src/lib/libdwarf/common/dwarf_loc.c usr/src/lib/libdwarf/common/dwarf_loc.h usr/src/lib/libdwarf/common/dwarf_macro.c @@ -162,8 +157,6 @@ usr/src/lib/libdwarf/common/dwarf_print_lines.c usr/src/lib/libdwarf/common/dwarf_pubtypes.c usr/src/lib/libdwarf/common/dwarf_query.c usr/src/lib/libdwarf/common/dwarf_ranges.c -usr/src/lib/libdwarf/common/dwarf_sort_line.c -usr/src/lib/libdwarf/common/dwarf_string.c usr/src/lib/libdwarf/common/dwarf_stubs.c usr/src/lib/libdwarf/common/dwarf_types.c usr/src/lib/libdwarf/common/dwarf_types.h @@ -422,7 +415,6 @@ usr/src/lib/gss_mechs/mech_krb5/krb5/os/thread_safe.c usr/src/lib/gss_mechs/mech_krb5/krb5/os/unlck_file.c usr/src/lib/gss_mechs/mech_krb5/krb5/os/ustime.c usr/src/lib/gss_mechs/mech_krb5/krb5/os/write_msg.c -usr/src/lib/gss_mechs/mech_krb5/krb5/posix/daemon.c usr/src/lib/gss_mechs/mech_krb5/krb5/posix/setenv.c usr/src/lib/gss_mechs/mech_krb5/krb5/rcache/rc_base.h usr/src/lib/gss_mechs/mech_krb5/krb5/rcache/rc_conv.c @@ -1054,6 +1046,7 @@ usr/src/uts/common/io/i40e/core/virtchnl.h usr/src/uts/common/io/iwn/if_iwn.c usr/src/uts/common/io/iwn/if_iwnreg.h usr/src/uts/common/io/iwn/if_iwnvar.h +usr/src/uts/common/io/ixgbe/ixgbe_osdep.h usr/src/uts/common/io/ixgbe/core/ixgbe_82598.c usr/src/uts/common/io/ixgbe/core/ixgbe_82598.h usr/src/uts/common/io/ixgbe/core/ixgbe_82599.c @@ -1070,7 +1063,6 @@ usr/src/uts/common/io/ixgbe/core/ixgbe_dcb_82599.c usr/src/uts/common/io/ixgbe/core/ixgbe_dcb_82599.h usr/src/uts/common/io/ixgbe/core/ixgbe_mbx.c usr/src/uts/common/io/ixgbe/core/ixgbe_mbx.h -usr/src/uts/common/io/ixgbe/core/ixgbe_osdep.h usr/src/uts/common/io/ixgbe/core/ixgbe_phy.c usr/src/uts/common/io/ixgbe/core/ixgbe_phy.h usr/src/uts/common/io/ixgbe/core/ixgbe_type.h @@ -1140,154 +1132,142 @@ usr/src/uts/common/io/sfxge/common/siena_phy.c usr/src/uts/common/io/sfxge/common/siena_sram.c usr/src/uts/common/io/sfxge/common/siena_vpd.c usr/src/uts/common/sys/scsi/adapters/mpt_sas/mpi/* -usr/src/uts/intel/io/acpica/debugger/dbcmds.c -usr/src/uts/intel/io/acpica/debugger/dbdisply.c -usr/src/uts/intel/io/acpica/debugger/dbexec.c -usr/src/uts/intel/io/acpica/debugger/dbfileio.c -usr/src/uts/intel/io/acpica/debugger/dbhistry.c -usr/src/uts/intel/io/acpica/debugger/dbinput.c -usr/src/uts/intel/io/acpica/debugger/dbmethod.c -usr/src/uts/intel/io/acpica/debugger/dbnames.c -usr/src/uts/intel/io/acpica/debugger/dbstats.c -usr/src/uts/intel/io/acpica/debugger/dbutils.c -usr/src/uts/intel/io/acpica/debugger/dbxface.c -usr/src/uts/intel/io/acpica/disassembler/dmbuffer.c -usr/src/uts/intel/io/acpica/disassembler/dmnames.c -usr/src/uts/intel/io/acpica/disassembler/dmobject.c -usr/src/uts/intel/io/acpica/disassembler/dmopcode.c -usr/src/uts/intel/io/acpica/disassembler/dmresrc.c -usr/src/uts/intel/io/acpica/disassembler/dmresrcl.c -usr/src/uts/intel/io/acpica/disassembler/dmresrcs.c -usr/src/uts/intel/io/acpica/disassembler/dmutils.c -usr/src/uts/intel/io/acpica/disassembler/dmwalk.c -usr/src/uts/intel/io/acpica/dispatcher/dsargs.c -usr/src/uts/intel/io/acpica/dispatcher/dscontrol.c -usr/src/uts/intel/io/acpica/dispatcher/dsfield.c -usr/src/uts/intel/io/acpica/dispatcher/dsinit.c -usr/src/uts/intel/io/acpica/dispatcher/dsmethod.c -usr/src/uts/intel/io/acpica/dispatcher/dsmthdat.c -usr/src/uts/intel/io/acpica/dispatcher/dsobject.c -usr/src/uts/intel/io/acpica/dispatcher/dsopcode.c -usr/src/uts/intel/io/acpica/dispatcher/dsutils.c -usr/src/uts/intel/io/acpica/dispatcher/dswexec.c -usr/src/uts/intel/io/acpica/dispatcher/dswload.c -usr/src/uts/intel/io/acpica/dispatcher/dswload2.c -usr/src/uts/intel/io/acpica/dispatcher/dswscope.c -usr/src/uts/intel/io/acpica/dispatcher/dswstate.c -usr/src/uts/intel/io/acpica/events/evevent.c -usr/src/uts/intel/io/acpica/events/evglock.c -usr/src/uts/intel/io/acpica/events/evgpe.c -usr/src/uts/intel/io/acpica/events/evgpeblk.c -usr/src/uts/intel/io/acpica/events/evgpeinit.c -usr/src/uts/intel/io/acpica/events/evgpeutil.c -usr/src/uts/intel/io/acpica/events/evmisc.c -usr/src/uts/intel/io/acpica/events/evregion.c -usr/src/uts/intel/io/acpica/events/evrgnini.c -usr/src/uts/intel/io/acpica/events/evsci.c -usr/src/uts/intel/io/acpica/events/evxface.c -usr/src/uts/intel/io/acpica/events/evxfevnt.c -usr/src/uts/intel/io/acpica/events/evxfgpe.c -usr/src/uts/intel/io/acpica/events/evxfregn.c -usr/src/uts/intel/io/acpica/executer/exconfig.c -usr/src/uts/intel/io/acpica/executer/exconvrt.c -usr/src/uts/intel/io/acpica/executer/excreate.c -usr/src/uts/intel/io/acpica/executer/exdebug.c -usr/src/uts/intel/io/acpica/executer/exdump.c -usr/src/uts/intel/io/acpica/executer/exfield.c -usr/src/uts/intel/io/acpica/executer/exfldio.c -usr/src/uts/intel/io/acpica/executer/exmisc.c -usr/src/uts/intel/io/acpica/executer/exmutex.c -usr/src/uts/intel/io/acpica/executer/exnames.c -usr/src/uts/intel/io/acpica/executer/exoparg1.c -usr/src/uts/intel/io/acpica/executer/exoparg2.c -usr/src/uts/intel/io/acpica/executer/exoparg3.c -usr/src/uts/intel/io/acpica/executer/exoparg6.c -usr/src/uts/intel/io/acpica/executer/exprep.c -usr/src/uts/intel/io/acpica/executer/exregion.c -usr/src/uts/intel/io/acpica/executer/exresnte.c -usr/src/uts/intel/io/acpica/executer/exresolv.c -usr/src/uts/intel/io/acpica/executer/exresop.c -usr/src/uts/intel/io/acpica/executer/exstore.c -usr/src/uts/intel/io/acpica/executer/exstoren.c -usr/src/uts/intel/io/acpica/executer/exstorob.c -usr/src/uts/intel/io/acpica/executer/exsystem.c -usr/src/uts/intel/io/acpica/executer/exutils.c -usr/src/uts/intel/io/acpica/hardware/hwacpi.c -usr/src/uts/intel/io/acpica/hardware/hwgpe.c -usr/src/uts/intel/io/acpica/hardware/hwpci.c -usr/src/uts/intel/io/acpica/hardware/hwregs.c -usr/src/uts/intel/io/acpica/hardware/hwsleep.c -usr/src/uts/intel/io/acpica/hardware/hwtimer.c -usr/src/uts/intel/io/acpica/hardware/hwvalid.c -usr/src/uts/intel/io/acpica/hardware/hwxface.c -usr/src/uts/intel/io/acpica/namespace/nsaccess.c -usr/src/uts/intel/io/acpica/namespace/nsalloc.c -usr/src/uts/intel/io/acpica/namespace/nsdump.c -usr/src/uts/intel/io/acpica/namespace/nsdumpdv.c -usr/src/uts/intel/io/acpica/namespace/nseval.c -usr/src/uts/intel/io/acpica/namespace/nsinit.c -usr/src/uts/intel/io/acpica/namespace/nsload.c -usr/src/uts/intel/io/acpica/namespace/nsnames.c -usr/src/uts/intel/io/acpica/namespace/nsobject.c -usr/src/uts/intel/io/acpica/namespace/nsparse.c -usr/src/uts/intel/io/acpica/namespace/nspredef.c -usr/src/uts/intel/io/acpica/namespace/nsrepair.c -usr/src/uts/intel/io/acpica/namespace/nsrepair2.c -usr/src/uts/intel/io/acpica/namespace/nssearch.c -usr/src/uts/intel/io/acpica/namespace/nsutils.c -usr/src/uts/intel/io/acpica/namespace/nswalk.c -usr/src/uts/intel/io/acpica/namespace/nsxfeval.c -usr/src/uts/intel/io/acpica/namespace/nsxfname.c -usr/src/uts/intel/io/acpica/namespace/nsxfobj.c -usr/src/uts/intel/io/acpica/parser/psargs.c -usr/src/uts/intel/io/acpica/parser/psloop.c -usr/src/uts/intel/io/acpica/parser/psopcode.c -usr/src/uts/intel/io/acpica/parser/psparse.c -usr/src/uts/intel/io/acpica/parser/psscope.c -usr/src/uts/intel/io/acpica/parser/pstree.c -usr/src/uts/intel/io/acpica/parser/psutils.c -usr/src/uts/intel/io/acpica/parser/pswalk.c -usr/src/uts/intel/io/acpica/parser/psxface.c -usr/src/uts/intel/io/acpica/resources/rsaddr.c -usr/src/uts/intel/io/acpica/resources/rscalc.c -usr/src/uts/intel/io/acpica/resources/rscreate.c -usr/src/uts/intel/io/acpica/resources/rsdump.c -usr/src/uts/intel/io/acpica/resources/rsinfo.c -usr/src/uts/intel/io/acpica/resources/rsio.c -usr/src/uts/intel/io/acpica/resources/rsirq.c -usr/src/uts/intel/io/acpica/resources/rslist.c -usr/src/uts/intel/io/acpica/resources/rsmemory.c -usr/src/uts/intel/io/acpica/resources/rsmisc.c -usr/src/uts/intel/io/acpica/resources/rsutils.c -usr/src/uts/intel/io/acpica/resources/rsxface.c -usr/src/uts/intel/io/acpica/tables/tbfadt.c -usr/src/uts/intel/io/acpica/tables/tbfind.c -usr/src/uts/intel/io/acpica/tables/tbinstal.c -usr/src/uts/intel/io/acpica/tables/tbutils.c -usr/src/uts/intel/io/acpica/tables/tbxface.c -usr/src/uts/intel/io/acpica/tables/tbxfroot.c -usr/src/uts/intel/io/acpica/utilities/utalloc.c -usr/src/uts/intel/io/acpica/utilities/utcache.c -usr/src/uts/intel/io/acpica/utilities/utclib.c -usr/src/uts/intel/io/acpica/utilities/utcopy.c -usr/src/uts/intel/io/acpica/utilities/utdebug.c -usr/src/uts/intel/io/acpica/utilities/utdecode.c -usr/src/uts/intel/io/acpica/utilities/utdelete.c -usr/src/uts/intel/io/acpica/utilities/uteval.c -usr/src/uts/intel/io/acpica/utilities/utglobal.c -usr/src/uts/intel/io/acpica/utilities/utids.c -usr/src/uts/intel/io/acpica/utilities/utinit.c -usr/src/uts/intel/io/acpica/utilities/utlock.c -usr/src/uts/intel/io/acpica/utilities/utmath.c -usr/src/uts/intel/io/acpica/utilities/utmisc.c -usr/src/uts/intel/io/acpica/utilities/utmutex.c -usr/src/uts/intel/io/acpica/utilities/utobject.c -usr/src/uts/intel/io/acpica/utilities/utosi.c -usr/src/uts/intel/io/acpica/utilities/utresrc.c -usr/src/uts/intel/io/acpica/utilities/utstate.c -usr/src/uts/intel/io/acpica/utilities/uttrack.c -usr/src/uts/intel/io/acpica/utilities/utxface.c -usr/src/uts/intel/io/acpica/utilities/utxferror.c +usr/src/common/acpica/disassembler/dmbuffer.c +usr/src/common/acpica/disassembler/dmnames.c +usr/src/common/acpica/disassembler/dmopcode.c +usr/src/common/acpica/disassembler/dmresrc.c +usr/src/common/acpica/disassembler/dmresrcl.c +usr/src/common/acpica/disassembler/dmresrcs.c +usr/src/common/acpica/disassembler/dmutils.c +usr/src/common/acpica/disassembler/dmwalk.c +usr/src/common/acpica/dispatcher/dsargs.c +usr/src/common/acpica/dispatcher/dscontrol.c +usr/src/common/acpica/dispatcher/dsfield.c +usr/src/common/acpica/dispatcher/dsinit.c +usr/src/common/acpica/dispatcher/dsmethod.c +usr/src/common/acpica/dispatcher/dsmthdat.c +usr/src/common/acpica/dispatcher/dsobject.c +usr/src/common/acpica/dispatcher/dsopcode.c +usr/src/common/acpica/dispatcher/dsutils.c +usr/src/common/acpica/dispatcher/dswexec.c +usr/src/common/acpica/dispatcher/dswload.c +usr/src/common/acpica/dispatcher/dswload2.c +usr/src/common/acpica/dispatcher/dswscope.c +usr/src/common/acpica/dispatcher/dswstate.c +usr/src/common/acpica/events/evevent.c +usr/src/common/acpica/events/evglock.c +usr/src/common/acpica/events/evgpe.c +usr/src/common/acpica/events/evgpeblk.c +usr/src/common/acpica/events/evgpeinit.c +usr/src/common/acpica/events/evgpeutil.c +usr/src/common/acpica/events/evmisc.c +usr/src/common/acpica/events/evregion.c +usr/src/common/acpica/events/evrgnini.c +usr/src/common/acpica/events/evsci.c +usr/src/common/acpica/events/evxface.c +usr/src/common/acpica/events/evxfevnt.c +usr/src/common/acpica/events/evxfgpe.c +usr/src/common/acpica/events/evxfregn.c +usr/src/common/acpica/executer/exconfig.c +usr/src/common/acpica/executer/exconvrt.c +usr/src/common/acpica/executer/excreate.c +usr/src/common/acpica/executer/exdebug.c +usr/src/common/acpica/executer/exdump.c +usr/src/common/acpica/executer/exfield.c +usr/src/common/acpica/executer/exfldio.c +usr/src/common/acpica/executer/exmisc.c +usr/src/common/acpica/executer/exmutex.c +usr/src/common/acpica/executer/exnames.c +usr/src/common/acpica/executer/exoparg1.c +usr/src/common/acpica/executer/exoparg2.c +usr/src/common/acpica/executer/exoparg3.c +usr/src/common/acpica/executer/exoparg6.c +usr/src/common/acpica/executer/exprep.c +usr/src/common/acpica/executer/exregion.c +usr/src/common/acpica/executer/exresnte.c +usr/src/common/acpica/executer/exresolv.c +usr/src/common/acpica/executer/exresop.c +usr/src/common/acpica/executer/exstore.c +usr/src/common/acpica/executer/exstoren.c +usr/src/common/acpica/executer/exstorob.c +usr/src/common/acpica/executer/exsystem.c +usr/src/common/acpica/executer/exutils.c +usr/src/common/acpica/hardware/hwacpi.c +usr/src/common/acpica/hardware/hwgpe.c +usr/src/common/acpica/hardware/hwpci.c +usr/src/common/acpica/hardware/hwregs.c +usr/src/common/acpica/hardware/hwsleep.c +usr/src/common/acpica/hardware/hwtimer.c +usr/src/common/acpica/hardware/hwvalid.c +usr/src/common/acpica/hardware/hwxface.c +usr/src/common/acpica/namespace/nsaccess.c +usr/src/common/acpica/namespace/nsalloc.c +usr/src/common/acpica/namespace/nsdump.c +usr/src/common/acpica/namespace/nsdumpdv.c +usr/src/common/acpica/namespace/nseval.c +usr/src/common/acpica/namespace/nsinit.c +usr/src/common/acpica/namespace/nsload.c +usr/src/common/acpica/namespace/nsnames.c +usr/src/common/acpica/namespace/nsobject.c +usr/src/common/acpica/namespace/nsparse.c +usr/src/common/acpica/namespace/nspredef.c +usr/src/common/acpica/namespace/nsrepair.c +usr/src/common/acpica/namespace/nsrepair2.c +usr/src/common/acpica/namespace/nssearch.c +usr/src/common/acpica/namespace/nsutils.c +usr/src/common/acpica/namespace/nswalk.c +usr/src/common/acpica/namespace/nsxfeval.c +usr/src/common/acpica/namespace/nsxfname.c +usr/src/common/acpica/namespace/nsxfobj.c +usr/src/common/acpica/parser/psargs.c +usr/src/common/acpica/parser/psloop.c +usr/src/common/acpica/parser/psopcode.c +usr/src/common/acpica/parser/psparse.c +usr/src/common/acpica/parser/psscope.c +usr/src/common/acpica/parser/pstree.c +usr/src/common/acpica/parser/psutils.c +usr/src/common/acpica/parser/pswalk.c +usr/src/common/acpica/parser/psxface.c +usr/src/common/acpica/resources/rsaddr.c +usr/src/common/acpica/resources/rscalc.c +usr/src/common/acpica/resources/rscreate.c +usr/src/common/acpica/resources/rsdump.c +usr/src/common/acpica/resources/rsinfo.c +usr/src/common/acpica/resources/rsio.c +usr/src/common/acpica/resources/rsirq.c +usr/src/common/acpica/resources/rslist.c +usr/src/common/acpica/resources/rsmemory.c +usr/src/common/acpica/resources/rsmisc.c +usr/src/common/acpica/resources/rsutils.c +usr/src/common/acpica/resources/rsxface.c +usr/src/common/acpica/tables/tbfadt.c +usr/src/common/acpica/tables/tbfind.c +usr/src/common/acpica/tables/tbinstal.c +usr/src/common/acpica/tables/tbutils.c +usr/src/common/acpica/tables/tbxface.c +usr/src/common/acpica/tables/tbxfroot.c +usr/src/common/acpica/utilities/utalloc.c +usr/src/common/acpica/utilities/utcache.c +usr/src/common/acpica/utilities/utclib.c +usr/src/common/acpica/utilities/utcopy.c +usr/src/common/acpica/utilities/utdebug.c +usr/src/common/acpica/utilities/utdecode.c +usr/src/common/acpica/utilities/utdelete.c +usr/src/common/acpica/utilities/uteval.c +usr/src/common/acpica/utilities/utglobal.c +usr/src/common/acpica/utilities/utids.c +usr/src/common/acpica/utilities/utinit.c +usr/src/common/acpica/utilities/utlock.c +usr/src/common/acpica/utilities/utmath.c +usr/src/common/acpica/utilities/utmisc.c +usr/src/common/acpica/utilities/utmutex.c +usr/src/common/acpica/utilities/utobject.c +usr/src/common/acpica/utilities/utosi.c +usr/src/common/acpica/utilities/utresrc.c +usr/src/common/acpica/utilities/utstate.c +usr/src/common/acpica/utilities/uttrack.c +usr/src/common/acpica/utilities/utxface.c +usr/src/common/acpica/utilities/utxferror.c usr/src/uts/intel/sys/acpi/acapps.h usr/src/uts/intel/sys/acpi/accommon.h usr/src/uts/intel/sys/acpi/acconfig.h @@ -1383,7 +1363,6 @@ usr/src/cmd/bhyve/pci_virtio_rnd.c usr/src/cmd/bhyve/pci_virtio_scsi.c usr/src/cmd/bhyve/pci_xhci.[ch] usr/src/cmd/bhyve/pm.c -usr/src/cmd/bhyve/pmtmr.c usr/src/cmd/bhyve/post.c usr/src/cmd/bhyve/ps2kbd.[ch] usr/src/cmd/bhyve/ps2mouse.[ch] @@ -1400,7 +1379,6 @@ usr/src/cmd/bhyve/vga.[ch] usr/src/cmd/bhyve/vmgenc.[ch] usr/src/cmd/bhyve/virtio.[ch] usr/src/cmd/bhyve/xmsr.[ch] -usr/src/cmd/bhyveconsole/bhyveconsole.c usr/src/cmd/bhyvectl/bhyvectl.c usr/src/compat/bhyve/* usr/src/contrib/bhyve/* -- cgit v1.2.3 From a0570dbda2f64cada915185eb4e1b42c3b4bf755 Mon Sep 17 00:00:00 2001 From: Jason King Date: Wed, 16 May 2018 09:51:08 -0500 Subject: 13671 profiles -l can crash in ldap backend Reviewed by: Brian Bennett Reviewed by: Andy Fiddaman Approved by: Robert Mustacchi --- usr/src/lib/nsswitch/ldap/common/getexecattr.c | 32 ++++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) 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 @@ -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); -- cgit v1.2.3 From 81de4da4caf1b6a02c80229f9f948f69e09f9719 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Tue, 19 May 2020 14:00:25 +0300 Subject: 13626 libsldap: implicit conversion from 'enum ' Reviewed by: Yuri Pankov Approved by: Dan McDonald --- usr/src/lib/libsldap/common/ns_confmgr.c | 28 +++++++++++++--------------- usr/src/lib/libsldap/common/ns_reads.c | 14 +++++++------- 2 files changed, 20 insertions(+), 22 deletions(-) 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); -- cgit v1.2.3 From 561433436929270392bcec8fb5afe3f73597dd55 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Sat, 23 May 2020 20:01:19 +0300 Subject: 13533 rdist: symbol 'buf' is multiply-defined Reviewed by: C Fraire Reviewed by: Andy Fiddaman Approved by: Dan McDonald --- usr/src/cmd/cmd-inet/usr.bin/rdist/server.c | 220 +++++++++++++--------------- 1 file changed, 105 insertions(+), 115 deletions(-) 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 #include #include +#include /* * 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; -- cgit v1.2.3 From e4cc4004bc22385fe82642b211027316c120a5a5 Mon Sep 17 00:00:00 2001 From: Robert Mustacchi Date: Wed, 24 Mar 2021 15:36:11 -0700 Subject: 13672 nvmeadm list could support parsable output Reviewed by: Andy Fiddaman Reviewed by: Toomas Soome Reviewed by: Hans Rosenfeld Approved by: Richard Lowe --- usr/src/cmd/nvmeadm/Makefile | 4 +- usr/src/cmd/nvmeadm/nvmeadm.c | 144 +++++++++++++++++++++++++----------- usr/src/cmd/nvmeadm/nvmeadm.h | 30 ++++++++ usr/src/cmd/nvmeadm/nvmeadm_ofmt.c | 132 +++++++++++++++++++++++++++++++++ usr/src/cmd/nvmeadm/nvmeadm_print.c | 6 +- usr/src/man/man1m/nvmeadm.1m | 75 ++++++++++++++++--- 6 files changed, 332 insertions(+), 59 deletions(-) create mode 100644 usr/src/cmd/nvmeadm/nvmeadm_ofmt.c 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..9caee4445b 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,7 +240,6 @@ 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 }; @@ -308,26 +290,33 @@ main(int argc, char **argv) optind++; + /* + * 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. + */ + 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. */ - if ((optind == argc || (strncmp(argv[optind], "nvme", 4) != 0)) && + 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]; - /* * 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 (npa.npa_argv[0] != NULL && strchr(npa.npa_argv[0], ',') != NULL && cmd->c_multi == B_FALSE) { warnx("%s not allowed on multiple controllers", cmd->c_name); @@ -338,7 +327,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(npa.npa_argv[0], ",", &lasts); do { if (npa.npa_name != NULL) { tmp = strchr(npa.npa_name, '/'); @@ -372,6 +361,15 @@ main(int argc, char **argv) exit(exitcode); } +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) { @@ -394,9 +392,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 +552,63 @@ nvme_walk(nvme_process_arg_t *npa, di_node_t node) static void usage_list(const char *c_name) { - (void) fprintf(stderr, "%s [[/][,...]\n\n" + (void) fprintf(stderr, "%s " + "[-p -o field[,...]] [[/][,...]\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 +623,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 +648,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 +659,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 #include #include +#include #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 + +#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/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 -- cgit v1.2.3 From 472cd20d26008f77084ade4c2048159b98c2b705 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Sat, 20 Mar 2021 13:20:22 +0200 Subject: 13668 mdns: update to mDNSResponder-1310.80.1 Reviewed by: C Fraire Approved by: Robert Mustacchi --- exception_lists/cstyle | 2 +- exception_lists/wscheck | 2 +- usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile | 2 +- usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile | 7 +- usr/src/contrib/mDNSResponder/Clients/dns-sd.c | 442 +- usr/src/contrib/mDNSResponder/README | 9 +- usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.c | 280 -- usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.h | 62 - usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.c | 1205 ++--- usr/src/contrib/mDNSResponder/mDNSCore/DNSCommon.h | 63 +- usr/src/contrib/mDNSResponder/mDNSCore/DNSDigest.c | 79 +- usr/src/contrib/mDNSResponder/mDNSCore/anonymous.c | 623 --- usr/src/contrib/mDNSResponder/mDNSCore/anonymous.h | 31 - usr/src/contrib/mDNSResponder/mDNSCore/dnsproxy.h | 9 +- usr/src/contrib/mDNSResponder/mDNSCore/dnssec.h | 158 - usr/src/contrib/mDNSResponder/mDNSCore/mDNS.c | 4784 ++++++++++---------- usr/src/contrib/mDNSResponder/mDNSCore/mDNSDebug.h | 303 +- .../mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h | 823 ++-- usr/src/contrib/mDNSResponder/mDNSCore/nsec.h | 34 - usr/src/contrib/mDNSResponder/mDNSCore/uDNS.c | 1824 ++++---- usr/src/contrib/mDNSResponder/mDNSCore/uDNS.h | 63 +- .../contrib/mDNSResponder/mDNSPosix/PosixDaemon.c | 46 +- .../contrib/mDNSResponder/mDNSPosix/mDNSPosix.c | 825 +++- .../contrib/mDNSResponder/mDNSPosix/mDNSPosix.h | 69 +- usr/src/contrib/mDNSResponder/mDNSPosix/mDNSUNP.h | 25 +- .../mDNSResponder/mDNSPosix/posix_utilities.c | 28 + .../mDNSResponder/mDNSPosix/posix_utilities.h | 16 + .../mDNSResponder/mDNSShared/ClientRequests.c | 1068 +++++ .../mDNSResponder/mDNSShared/ClientRequests.h | 165 + .../mDNSResponder/mDNSShared/GenLinkedList.c | 8 +- .../mDNSResponder/mDNSShared/PlatformCommon.c | 572 ++- .../mDNSResponder/mDNSShared/PlatformCommon.h | 16 +- usr/src/contrib/mDNSResponder/mDNSShared/dns_sd.h | 288 +- .../mDNSResponder/mDNSShared/dns_sd_internal.h | 23 +- .../mDNSResponder/mDNSShared/dns_sd_private.h | 82 +- .../mDNSResponder/mDNSShared/dnssd_clientstub.c | 482 +- .../contrib/mDNSResponder/mDNSShared/dnssd_ipc.c | 28 +- .../contrib/mDNSResponder/mDNSShared/dnssd_ipc.h | 13 +- .../contrib/mDNSResponder/mDNSShared/mDNSDebug.c | 72 +- .../mDNSResponder/mDNSShared/mDNSFeatures.h | 50 + .../contrib/mDNSResponder/mDNSShared/uds_daemon.c | 4300 +++++++++--------- .../contrib/mDNSResponder/mDNSShared/uds_daemon.h | 73 +- usr/src/lib/libdns_sd/Makefile.com | 2 +- 43 files changed, 10484 insertions(+), 8572 deletions(-) delete mode 100644 usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.c delete mode 100644 usr/src/contrib/mDNSResponder/mDNSCore/CryptoAlg.h delete mode 100644 usr/src/contrib/mDNSResponder/mDNSCore/anonymous.c delete mode 100644 usr/src/contrib/mDNSResponder/mDNSCore/anonymous.h delete mode 100644 usr/src/contrib/mDNSResponder/mDNSCore/dnssec.h delete mode 100644 usr/src/contrib/mDNSResponder/mDNSCore/nsec.h create mode 100644 usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.c create mode 100644 usr/src/contrib/mDNSResponder/mDNSPosix/posix_utilities.h create mode 100644 usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.c create mode 100644 usr/src/contrib/mDNSResponder/mDNSShared/ClientRequests.h create mode 100644 usr/src/contrib/mDNSResponder/mDNSShared/mDNSFeatures.h diff --git a/exception_lists/cstyle b/exception_lists/cstyle index cebfcf435b..b92b19cfd1 100644 --- a/exception_lists/cstyle +++ b/exception_lists/cstyle @@ -102,7 +102,7 @@ usr/src/common/bzip2/decompress.c usr/src/common/bzip2/bzlib_private.h usr/src/common/bzip2/huffman.c usr/src/common/crypto/chacha/chacha.c -usr/src/contrib +usr/src/contrib/ usr/src/grub/grub-0.97/grub/asmstub.c usr/src/grub/grub-0.97/stage2/bios.c usr/src/grub/grub-0.97/stage2/builtins.c diff --git a/exception_lists/wscheck b/exception_lists/wscheck index b42c1e9971..d2a64a3f72 100644 --- a/exception_lists/wscheck +++ b/exception_lists/wscheck @@ -27,7 +27,7 @@ usr/src/uts/common/io/e1000api/* usr/src/uts/common/io/qede/* usr/src/uts/common/io/i40e/core/* usr/src/uts/common/io/ixgbe/core/* -usr/src/contrib/ast/* +usr/src/contrib/* usr/src/cmd/ast/libshell/misc/shell_styleguide.docbook # bhyve sources 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.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/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 // For errno, EINTR #include #include // For u_char +#ifdef APPLE_OSX_mDNSResponder +#include // For PRId64 +#endif // APPLE_OSX_mDNSResponder + +#if APPLE_OSX_mDNSResponder +#include +#include "xpc_clients.h" +#endif #ifdef _WIN32 #include @@ -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 + #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 [...] (Register a service)\n", arg0); - fprintf(stderr, "%s -B (Browse for service instances)\n", arg0); - fprintf(stderr, "%s -L (Resolve a service instance)\n", arg0); - fprintf(stderr, "%s -Q (Generic query for any record type)\n", arg0); - fprintf(stderr, "%s -Z (Output results in Zone File format)\n", arg0); - fprintf(stderr, "%s -G v4/v6/v4v6 (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 [...] (Register a service)\n", arg0); + fprintf(stderr, "%s -P [...] (Register Proxy)\n", arg0); + fprintf(stderr, "%s -B (Browse for service instances)\n", arg0); + fprintf(stderr, "%s -Z (Output results in Zone File format)\n", arg0); + fprintf(stderr, "%s -L (Resolve (‘lookup’) a service instance)\n", arg0); + fprintf(stderr, "%s -Q (Generic query for any record type)\n", arg0); + fprintf(stderr, "%s -q (Generic query, using SuppressUnusable)\n", arg0); + fprintf(stderr, "%s -G v4/v6/v4v6 (Get address information for hostname)\n", arg0); + fprintf(stderr, "%s -X udp/tcp/udptcp (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 (Query; reconfirming each result)\n", arg0); - fprintf(stderr, "%s -D (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 [...] (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 (NAT Port Mapping)\n", arg0); - fprintf(stderr, "%s -ble (Use kDNSServiceInterfaceIndexBLE)\n", arg0); - fprintf(stderr, "%s -g v4/v6/v4v6 (Validate address info for hostname with DNSSEC)\n", arg0); - fprintf(stderr, "%s -i (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 (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 (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 (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 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<>", 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), "<>", 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 . + + 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= 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 +#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 - // 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 -#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; inext = 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); } - // 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; iresrec.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) - { - *cflocal = rr; - cflocal = &rr->NextInCFList; - *cflocal = (CacheRecord*)1; - *cfp = &rr->NextInCFList; - } + 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 this packet record is marked unique, and our previous cached copy was not, then fix it - if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) { - DNSQuestion *q; - for (q = m->Questions; q; q=q->next) + // 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) { - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) - q->UniqueAnswers++; + *cflocal = cr; + cflocal = &cr->NextInCFList; + *cflocal = (CacheRecord*)1; + *cfp = &cr->NextInCFList; } - 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. - // 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 this packet record is marked unique, and our previous cached copy was not, then fix it + if (!(cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) { - if (!q->DuplicateOf && !q->LongLived && - ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + DNSQuestion *q; + 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 (CacheRecordAnswersQuestion(cr, q)) + q->UniqueAnswers++; } + cr->resrec.RecordType = m->rec.r.resrec.RecordType; } } - break; - } - 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, rr)); - if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) - { - rr->resrec.rroriginalttl = 1; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); + + 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. + // 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 } - break; - } - } - } - return rr; -} + else if (m->rec.r.resrec.rroriginalttl > 0) + { + DNSQuestion *q; -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; + m->mDNSStats.CacheRefreshed++; - 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; + if ((cr->resrec.mortality == Mortality_Ghost) && !cr->DelayDelivery) + { + cr->DelayDelivery = NonZeroTime(m->timenow); + debugf("mDNSCoreReceiveCacheCheck: Reset DelayDelivery for mortalityExpired EXP:%d RR %s", m->timenow - RRExpireTime(cr), CRDisplayString(m, cr)); + } - 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; + 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) + { + 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) + { + 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; + } + } + 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 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 + } } - 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 . + 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) { - debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); - AcceptableResponse = mDNSfalse; + 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 + { + // 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) - { - 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 + if ((rr->ProbingConflictCount == 0) || (m->MPktNum != rr->LastConflictPktNum)) { - 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. - // 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. + // 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; - } - - // Private domains are exempted irrespective of what the DNSServer says - if (IsPrivateDomain(m, q)) - { - LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype)); - return mDNSfalse; - } + mDNSBool suppress = mDNSfalse; + const char *reason = mDNSNULL; - if (!d) + if (q->BlockedByPolicy) { - 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 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 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; iDupSuppress[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 @@ -12905,20 +12852,58 @@ mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatu if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) IncrementLabelSuffix(&m->hostlabel, mDNSfalse); - // 3. Generate the FQDNs from the hostlabel, - // and make sure all SRV records, etc., are updated to reference our new hostname - mDNS_SetFQDN(m); - LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); - } - else if (result == mStatus_MemFree) - { - // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by - // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface - debugf("mDNS_HostNameCallback: MemFree (ignored)"); + // 3. Generate the FQDNs from the hostlabel, + // and make sure all SRV records, etc., are updated to reference our new hostname + mDNS_SetFQDN(m); + LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); + } + else if (result == mStatus_MemFree) + { + // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by + // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface + debugf("mDNS_HostNameCallback: MemFree (ignored)"); + } + else + 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); } - else - LogMsg("mDNS_HostNameCallback: Unknown error %d for registration of record %s", result, rr->resrec.name->c); } +#endif mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) { @@ -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; iSubTypes[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; iSubTypes[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 +#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), + * + */ + #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 '> 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 is required for for va_list support for the mDNS_vsnprintf declaration #endif -#include "mDNSDebug.h" #if APPLE_OSX_mDNSResponder #include -#include #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 +#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 @@ -1319,15 +1217,6 @@ struct NATTraversalInfo_struct #pragma mark - DNSServer & McastResolver structures and constants #endif -enum -{ - DNSServer_FlagDelete = 0x1, - DNSServer_FlagNew = 0x2, -#if APPLE_OSX_mDNSResponder - DNSServer_FlagUnreachable = 0x4, -#endif -}; - enum { McastResolver_FlagDelete = 1, @@ -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 + // 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 -#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 + : "") : "", + 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 +#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. 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 #include #include +#include #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 #include @@ -40,6 +39,7 @@ #include #include #include // platform support for UTC time +#include #if USES_NETLINK #include @@ -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 @@ -91,9 +79,23 @@ static int num_registered_interfaces = 0; 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) { - // + // 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 // for NULL +#include // for snprintf +#include +#include // 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 +#include +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) +#include + +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 . + 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 // Needed for fopen() etc. #include // Needed for close() +#include // Needed for malloc() #include // Needed for strlen() etc. #include // Needed for errno etc. #include // Needed for socket() etc. #include // Needed for sockaddr_in #include +#include +#include +#include +#include #if APPLE_OSX_mDNSResponder #include @@ -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 -// 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 #include #include -#include "dns_sd_internal.h" +#include "dns_sd_private.h" +#include "dnssd_clientstub_apple.h" +#include +#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 // For O_RDWR etc. + #include // For O_RDWR etc. #include #include #include @@ -91,9 +98,16 @@ static void syslog( int priority, const char * message, ...) #endif +#if CHECK_BUNDLE_VERSION +#include "bundle_utilities.h" +#include +#endif + +#if defined(_WIN32) // 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; isockfd, ((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 #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: +// 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 #include #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 // 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 #include "BLE.h" +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) +#include "mDNSMacOSX.h" +#include +#endif + +#if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) +#include +#endif -#if !NO_WCF +#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) +#include "QuerierSupport.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, QUERIER) && MDNSRESPONDER_SUPPORTS(APPLE, IPC_TLV) +#include "mdns_tlv.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, DNSSECv2) +#include "dnssec_v2.h" +#endif -#else -#define NO_WCF 1 -#endif // APPLE_OSX_mDNSResponder +#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)) - { - 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) +#if MDNSRESPONDER_SUPPORTS(APPLE, TRUST_ENFORCEMENT) + if (os_feature_enabled(mDNSResponder, bonjour_privacy) && + IsLocalDomain(rr->resrec.name)) { - 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 s", not "Zero or more 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) : "", - 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) : "", + 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) : "", 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) : "", 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 ":" indicated - // by AnonData being non-NULL which is in turn follwed by "," 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 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 ".bar.local." we always do a second unicast query in parallel. - // For names of the form ".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); + 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); -#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 + mDNSPlatformMemZero(&request->u.queryrecord, (mDNSu32)sizeof(request->u.queryrecord)); + request->terminate = NULL; - 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); - - 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; + +mDNSlocal mStatus _handle_addrinfo_request_start(request_state *request, const _addrinfo_start_params_t * const params) +{ + mStatus err; - 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; + 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); - 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) + 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) + 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 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) + case mdns_trust_status_denied: + case mdns_trust_status_pending: { - 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) - { - if (request->u.addrinfo.q6.TimeoutQuestion && !request->u.addrinfo.v6ans) - { - 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(""); - else if (truncated) LogMsgNoIdent("", count, m->rrauth.rrauth_totalused, authslot); + if (showheader) LogToFD(fd, ""); + else if (truncated) LogToFD(fd, "", 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(""); -} - -mDNSlocal char *AnonInfoToString(AnonymousInfo *ai, char *anonstr, int anstrlen) -{ - anonstr[0] = 0; - if (ai && ai->AnonData) - { - return (AnonDataToString(ai->AnonData, ai->AnonDataLen, anonstr, anstrlen)); - } - return anonstr; + if (showheader) LogToFD(fd, ""); } -mDNSlocal void LogOneAuthRecord(const AuthRecord *ar, mDNSs32 now, const char *const ifname) +mDNSlocal void LogOneAuthRecordToFD(int fd, const AuthRecord *ar, mDNSs32 now, const char *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(""); + if (showheader) LogToFD(fd, ""); } -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(""); + LogToFD(fd, "---------- Questions -----------"); + if (!m->Questions) LogToFD(fd, ""); 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(""); +#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, ""); 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(""); + LogToFD(fd, "---- Active UDS Client Requests ----"); + if (!all_requests) LogToFD(fd, ""); 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(""); + 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, ""); 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(""); - 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(""); - else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); + LogToFD(fd, "------ Auto Browse Domains -----"); + if (!AutoBrowseDomains) LogToFD(fd, ""); + else for (d=AutoBrowseDomains; d; d=d->next) LogToFD(fd, "%##s", d->name.c); - LogMsgNoIdent("--- Auto Registration Domains --"); - if (!AutoRegistrationDomains) LogMsgNoIdent(""); - else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); + LogToFD(fd, "--- Auto Registration Domains --"); + if (!AutoRegistrationDomains) LogToFD(fd, ""); + else for (d=AutoRegistrationDomains; d; d=d->next) LogToFD(fd, "%##s", d->name.c); - LogMsgNoIdent("--- Search Domains --"); - if (!SearchList) LogMsgNoIdent(""); + LogToFD(fd, "--- Search Domains --"); + if (!SearchList) LogToFD(fd, ""); 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(""); - } - 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 ---"); - - 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); + LogToFD(fd, "---- Task Scheduling Timers ----"); - 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 "); + LogToFD(fd, "NewQuestion "); 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 "); + LogToFD(fd, "NewLocalOnlyQuestions "); 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 "); + LogToFD(fd, "NewLocalRecords "); else - LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords)); - - LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " "); - LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " "); - 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 ? "" : " "); + LogToFD(fd, "LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " "); + 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,22 +159,9 @@ 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; @@ -175,12 +174,6 @@ struct request_state DNSQuestion q_autoall; } enumeration; struct - { - DNSQuestion q; - DNSQuestion *q2; - mDNSu8 ans; - } queryrecord; - struct { DNSQuestion qtxt; DNSQuestion qsrv; @@ -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/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 -- cgit v1.2.3 From ed0a9ca2a3cceedc0e6e889472de1cec55ca2faf Mon Sep 17 00:00:00 2001 From: Hans Rosenfeld Date: Mon, 9 Sep 2019 18:04:22 +0000 Subject: 11969 Attempting to attach an invalid nvme namespace will cause a panic Reviewed by: Robert Mustacchi Reviewed by: Jerry Jelinek Reviewed by: Andy Fiddaman Reviewed by: Yuri Pankov Approved by: Richard Lowe --- usr/src/uts/common/io/nvme/nvme.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/usr/src/uts/common/io/nvme/nvme.c b/usr/src/uts/common/io/nvme/nvme.c index d1fa87c881..e37d0598c3 100644 --- a/usr/src/uts/common/io/nvme/nvme.c +++ b/usr/src/uts/common/io/nvme/nvme.c @@ -4550,6 +4550,9 @@ nvme_ioctl_detach(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode, if (nsid == 0) return (EINVAL); + if (nvme->n_ns[nsid - 1].ns_ignore) + return (0); + rv = bd_detach_handle(nvme->n_ns[nsid - 1].ns_bd_hdl); if (rv != DDI_SUCCESS) rv = EBUSY; @@ -4580,6 +4583,14 @@ nvme_ioctl_attach(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode, kmem_free(idns, sizeof (nvme_identify_nsid_t)); + if (nvme->n_ns[nsid - 1].ns_ignore) + return (ENOTSUP); + + if (nvme->n_ns[nsid - 1].ns_bd_hdl == NULL) + nvme->n_ns[nsid - 1].ns_bd_hdl = bd_alloc_handle( + &nvme->n_ns[nsid - 1], &nvme_bd_ops, &nvme->n_prp_dma_attr, + KM_SLEEP); + rv = bd_attach_handle(nvme->n_dip, nvme->n_ns[nsid - 1].ns_bd_hdl); if (rv != DDI_SUCCESS) rv = EBUSY; -- cgit v1.2.3 From 91b4b5393fe18d32505f967d482b81eef7f68d22 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Thu, 18 Mar 2021 00:34:22 +0200 Subject: 13650 sendmail: writing 1 byte into a region of size 0 Reviewed by: Andy Fiddaman Approved by: Dan McDonald --- usr/src/cmd/sendmail/src/daemon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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'; -- cgit v1.2.3 From c1ba859699f12f01e0a0ae33b874b48f666aac62 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Mon, 18 May 2020 10:46:29 +0300 Subject: 13518 sun_sas: multiply-defined symbols Reviewed by: Yuri Pankov Approved by: Dan McDonald --- usr/src/lib/sun_sas/common/Sun_sasFreeLibrary.c | 1 + usr/src/lib/sun_sas/common/Sun_sasLoadLibrary.c | 44 ++++++++++++------------- usr/src/lib/sun_sas/common/devtree_hba_disco.c | 2 ++ usr/src/lib/sun_sas/common/sun_sas.h | 12 +++---- 4 files changed, 31 insertions(+), 28 deletions(-) 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 +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 #include +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; -- cgit v1.2.3 From fd8be61dc48514ff8e3bb97fdb04972e2765a6da Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Thu, 18 Mar 2021 11:42:09 +0200 Subject: 13651 libucb: cast between incompatible function types Reviewed by: Robert Mustacchi Approved by: Dan McDonald --- usr/src/ucblib/libucb/sparc/sys/signal.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) 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) -- cgit v1.2.3 From 3bbba396f8599b5a3225ae7d8bb0119abdfaa427 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Sun, 17 May 2020 15:31:37 +0300 Subject: 13681 libfruraw: writing 1 byte into a region of size 0 Reviewed by: Robert Mustacchi Approved by: Dan McDonald --- usr/src/lib/libfru/libfruraw/fruraw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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); } } -- cgit v1.2.3 From 35d41f28ed229ff7c40e0acb85ce4465390ac9bb Mon Sep 17 00:00:00 2001 From: Jason King Date: Mon, 16 Dec 2019 20:46:39 +0000 Subject: 13637 Support promiscuous mode on vioif interfaces Reviewed by: Joshua M. Clulow Approved by: Robert Mustacchi --- usr/src/uts/common/io/vioif/vioif.c | 248 +++++++++++++++++++++++++++++++++++- usr/src/uts/common/io/vioif/vioif.h | 71 ++++++++++- 2 files changed, 311 insertions(+), 8 deletions(-) diff --git a/usr/src/uts/common/io/vioif/vioif.c b/usr/src/uts/common/io/vioif/vioif.c index cac90d3073..822dbfa8b7 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 */ @@ -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 @@ -485,6 +555,26 @@ vioif_alloc_bufs(vioif_t *vif) KM_SLEEP); } + /* + * 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 @@ -532,6 +622,128 @@ fail: return (ENOMEM); } +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,15 @@ 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) { + return (ENOTSUP); + } + + return (vioif_ctrlq_req(vif, VIRTIO_NET_CTRL_RX, + VIRTIO_NET_CTRL_RX_PROMISC, &val, sizeof (val))); } static int @@ -1655,6 +1871,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 +1953,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 +1968,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 @@ -215,6 +248,13 @@ struct virtio_net_hdr { #define VIRTIO_NET_TX_BUFS 256 #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 @@ -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 -- cgit v1.2.3 From cf988e4abd3e7fd3815aa43c51aff413cc05ad45 Mon Sep 17 00:00:00 2001 From: Robert Mustacchi Date: Wed, 31 Mar 2021 10:19:13 -0700 Subject: 13690 nvmeadm get-logpage doesn't work after 13672 Reviewed by: Andy Fiddaman Reviewed by: Patrick Mooney --- usr/src/cmd/nvmeadm/nvmeadm.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/usr/src/cmd/nvmeadm/nvmeadm.c b/usr/src/cmd/nvmeadm/nvmeadm.c index 9caee4445b..e177e04d82 100644 --- a/usr/src/cmd/nvmeadm/nvmeadm.c +++ b/usr/src/cmd/nvmeadm/nvmeadm.c @@ -245,6 +245,7 @@ main(int argc, char **argv) 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) { @@ -303,7 +304,9 @@ main(int argc, char **argv) } /* - * All commands but "list" require a ctl/ns argument. + * 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) { @@ -312,11 +315,19 @@ main(int argc, char **argv) exit(-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 (npa.npa_argv[0] != NULL && strchr(npa.npa_argv[0], ',') != NULL && + if (ctrl != NULL && strchr(ctrl, ',') != NULL && cmd->c_multi == B_FALSE) { warnx("%s not allowed on multiple controllers", cmd->c_name); @@ -327,7 +338,7 @@ main(int argc, char **argv) /* * Get controller/namespace arguments and run command. */ - npa.npa_name = strtok_r(npa.npa_argv[0], ",", &lasts); + npa.npa_name = strtok_r(ctrl, ",", &lasts); do { if (npa.npa_name != NULL) { tmp = strchr(npa.npa_name, '/'); -- cgit v1.2.3 From 4ba84af54d7e87f645088b6bb6f9baac08e77373 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Tue, 30 Mar 2021 00:01:06 +0300 Subject: 13680 libfruraw: 'hash_obj' may be used uninitialized Reviewed by: Robert Mustacchi Approved by: Dan McDonald --- usr/src/lib/libfru/libfruraw/raw_access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: -- cgit v1.2.3 From dd699a3e07422d6135575d35b14927cd260ef32e Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Wed, 17 Mar 2021 12:11:07 +0200 Subject: 13682 libc: '_getcontext' specifies less restrictive attribute than its target Reviewed by: Robert Mustacchi Approved by: Gordon Ross --- usr/src/lib/libc/inc/libc.h | 5 +++++ usr/src/lib/libc/sparc/gen/getctxt.c | 9 ++++----- usr/src/lib/libc/sparcv9/gen/getctxt.c | 9 ++++----- 3 files changed, 13 insertions(+), 10 deletions(-) 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 @@ -221,6 +221,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 */ 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 #include +#include "libc.h" + +#pragma weak _getcontext = getcontext int getcontext(ucontext_t *ucp) 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 #include +#include "libc.h" + +#pragma weak _getcontext = getcontext int getcontext(ucontext_t *ucp) -- cgit v1.2.3 From 1d9fea2a5ece48bb3a7c454829f0c8a68b9fe5e1 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Wed, 17 Mar 2021 11:59:27 +0200 Subject: 13683 libc: uninitialized variables Reviewed by: C Fraire Approved by: Dan McDonald --- usr/src/lib/libc/sparc/fp/_D_cplx_mul.c | 4 +--- usr/src/lib/libc/sparc/fp/_F_cplx_mul.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_div.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_div_ix.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_div_rx.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_ix.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_lr_div_rx.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_lr_mul.c | 4 +--- usr/src/lib/libc/sparc/fp/_Q_cplx_mul.c | 4 +--- 10 files changed, 10 insertions(+), 30 deletions(-) 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, -- cgit v1.2.3 From 5eb35ba24893e16e4e108a951856b8d948493f28 Mon Sep 17 00:00:00 2001 From: Richard Lowe Date: Sun, 21 Mar 2021 17:54:36 -0500 Subject: 13675 use NATIVE_LIBS don't pass -zassert-deflib manually Reviewed by: Dan McDonald Reviewed by: Toomas Soome Approved by: Robert Mustacchi --- usr/src/cmd/sgs/elfdump/Makefile.com | 2 +- usr/src/cmd/sgs/tools/Makefile.com | 2 +- usr/src/lib/libc/amd64/Makefile | 4 +++- usr/src/lib/libc/i386/Makefile.com | 4 +++- usr/src/lib/libc/sparc/Makefile.com | 4 +++- usr/src/lib/libc/sparcv9/Makefile.com | 4 +++- usr/src/tools/Makefile.tools | 3 ++- 7 files changed, 16 insertions(+), 7 deletions(-) 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/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/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile index 717c549329..0e2d78a484 100644 --- a/usr/src/lib/libc/amd64/Makefile +++ b/usr/src/lib/libc/amd64/Makefile @@ -1241,7 +1241,9 @@ $(ASSYMDEP_OBJS:%=pics/%): assym.h # assym.h build rules GENASSYM_C = genassym.c -LDFLAGS.native = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) +LDFLAGS.native = $(LDASSERTS) $(BDIRECT) + +genassym := NATIVE_LIBS += libc.so genassym: $(GENASSYM_C) $(NATIVECC) $(NATIVE_CFLAGS) -Iinc -I$(LIBCDIR)/inc $(CPPFLAGS.native) \ diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com index a1c0297112..55bee236c2 100644 --- a/usr/src/lib/libc/i386/Makefile.com +++ b/usr/src/lib/libc/i386/Makefile.com @@ -1319,7 +1319,9 @@ $(ASSYMDEP_OBJS:%=pics/%): assym.h # assym.h build rules 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/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com index c699befe9c..9a8ca9ce52 100644 --- a/usr/src/lib/libc/sparc/Makefile.com +++ b/usr/src/lib/libc/sparc/Makefile.com @@ -1380,7 +1380,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/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com index 120e164f62..1058e950c9 100644 --- a/usr/src/lib/libc/sparcv9/Makefile.com +++ b/usr/src/lib/libc/sparcv9/Makefile.com @@ -1298,7 +1298,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/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. -- cgit v1.2.3 From 867228adfb0b4bbab1ff33e31ec607f5671c9047 Mon Sep 17 00:00:00 2001 From: Patrick Mooney Date: Wed, 17 Mar 2021 22:33:51 +0000 Subject: 13645 bhyve queues wrong SIPI Reviewed by: Yuri Pankov Reviewed by: Hans Rosenfeld Approved by: Robert Mustacchi --- usr/src/uts/i86pc/io/vmm/vmm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/usr/src/uts/i86pc/io/vmm/vmm.c b/usr/src/uts/i86pc/io/vmm/vmm.c index 1cd0b23a1c..047c6e0887 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 2018 Joyent, Inc. - * Copyright 2020 Oxide Computer Company + * Copyright 2021 Oxide Computer Company */ #include @@ -2922,7 +2922,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); } -- cgit v1.2.3 From 6d7e602c65f3fdc0a30011e57aa7ff3de1edaafe Mon Sep 17 00:00:00 2001 From: Bill Welliver Date: Fri, 2 Apr 2021 16:26:22 -0400 Subject: OS-8279 Link-local default route support in LX zones Actually-authored-by: William Welliver Reviewed by: Dan McDonald Reviewed by: Mike Zeller Approved by: Mike Zeller --- usr/src/lib/brand/lx/lx_init/lxinit.c | 221 ++++++++++++++++++++++++---------- 1 file changed, 156 insertions(+), 65 deletions(-) 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,11 +881,24 @@ 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) { @@ -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(); -- cgit v1.2.3 From b4adc50c2ffdb6ae8e81d8be3c37ed01066fe920 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Sat, 3 Apr 2021 11:57:13 +0300 Subject: 13693 loader: we should support pools without features Reviewed by: Yuri Pankov Reviewed by: Gergő Mihály Doma Approved by: Robert Mustacchi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- usr/src/boot/Makefile.version | 2 +- usr/src/boot/lib/libstand/zfs/zfsimpl.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 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 */ -- cgit v1.2.3 From f980a4bbce3d867e2bb5e61c180593f416d181a5 Mon Sep 17 00:00:00 2001 From: Richard Lowe Date: Tue, 30 Mar 2021 20:26:27 -0500 Subject: 13684 ld aborts when input object has no file name Reviewed by: Dan McDonald Reviewed by: Gordon Ross Approved by: Robert Mustacchi --- usr/src/cmd/sgs/libld/common/syms.c | 15 +++++++++++---- usr/src/cmd/sgs/libld/common/update.c | 4 +++- usr/src/cmd/sgs/tools/SUNWonld-README | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) 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/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 -- cgit v1.2.3 From 8054a0e4c809d98ffb44f17b9a8b932ca2c24b2c Mon Sep 17 00:00:00 2001 From: Jason King Date: Sun, 4 Apr 2021 11:35:47 -0500 Subject: 13695 Can't create VNICs over vioif after 13637 Reviewed by: Andy Fiddaman Reviewed by: Andrew Stormont Approved by: Robert Mustacchi --- usr/src/uts/common/io/vioif/vioif.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/usr/src/uts/common/io/vioif/vioif.c b/usr/src/uts/common/io/vioif/vioif.c index 822dbfa8b7..368af5381d 100644 --- a/usr/src/uts/common/io/vioif/vioif.c +++ b/usr/src/uts/common/io/vioif/vioif.c @@ -765,7 +765,17 @@ vioif_m_setpromisc(void *arg, boolean_t on) uint8_t val = on ? 1 : 0; if (!vif->vif_has_ctrlq_rx) { - return (ENOTSUP); + /* + * 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, -- cgit v1.2.3 From ffb6483089015eb90be1f5e7fc2a96c9929546a6 Mon Sep 17 00:00:00 2001 From: Jordan Paige Hendricks Date: Mon, 25 Mar 2019 17:22:13 +0000 Subject: 11698 Want NVMe Hotplug Support 11699 x86 pci configurator should not fail device teardown if device is gone 11700 DDI hotplug request handler resets connection handle state before performing state change operations 11701 ldi_handle dcmd segfaults occasionally Reviewed by: Robert Mustacchi Reviewed by: Rob Johnston Reviewed by: Paul Winder Approved by: Dan McDonald --- .../fm/modules/common/fabric-xlate/fabric-xlate.h | 31 ++- .../cmd/fm/modules/common/fabric-xlate/fx_fabric.c | 55 ++++- .../cmd/fm/modules/common/fabric-xlate/fx_subr.c | 20 +- usr/src/uts/common/io/nvme/nvme.c | 110 +++++++++- usr/src/uts/common/io/nvme/nvme_var.h | 22 +- usr/src/uts/common/io/pciex/hotplug/pciehpc.c | 15 ++ usr/src/uts/common/io/pciex/pcie.c | 7 + usr/src/uts/common/io/pciex/pcie_fault.c | 76 ++++++- usr/src/uts/common/os/ddi_hp_impl.c | 237 ++++++++++++++++++++- usr/src/uts/common/os/ddi_hp_ndi.c | 22 +- usr/src/uts/common/sys/ddi_hp.h | 8 +- usr/src/uts/common/sys/ddi_hp_impl.h | 6 + usr/src/uts/common/sys/pcie_impl.h | 14 ++ usr/src/uts/i86pc/io/pci/pci_common.h | 14 +- usr/src/uts/i86pc/io/pciex/npe.c | 147 ++++++++++++- usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c | 53 +++-- 16 files changed, 768 insertions(+), 69 deletions(-) diff --git a/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h b/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h index f33ea9ecd6..96e1a956af 100644 --- a/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h +++ b/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #ifndef _FABRIC_XLATE_H @@ -31,6 +32,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -45,6 +47,17 @@ extern "C" { #define PF_ADDR_PIO (1 << 1) #define PF_ADDR_CFG (1 << 2) + +/* + * The fabric ereport preparation functions (fab_prep_*) in fab_erpt_tbl_t + * structures may return an error if the ereport could not be set up properly. + * Typically, these errors are errnos. It is possible that based on incoming + * ereport payload data, we might not want to generate an ereport at all: In + * this case, the preparation functions may instead return PF_EREPORT_IGNORE, + * which is set at a high value so as not to collide with the errnos. + */ +#define PF_EREPORT_IGNORE INT_MAX + extern fmd_xprt_t *fab_fmd_xprt; /* FMD transport layer handle */ extern char fab_buf[]; @@ -121,8 +134,21 @@ typedef struct fab_data { uint16_t pcie_rp_ctl; /* root complex control register */ uint32_t pcie_rp_err_status; /* pcie root complex error status reg */ uint32_t pcie_rp_err_cmd; /* pcie root complex error cmd reg */ - uint16_t pcie_rp_ce_src_id; /* pcie root complex ce sourpe id */ - uint16_t pcie_rp_ue_src_id; /* pcie root complex ue sourpe id */ + uint16_t pcie_rp_ce_src_id; /* pcie root complex ce source id */ + uint16_t pcie_rp_ue_src_id; /* pcie root complex ue source id */ + + /* + * The slot register values refer to the registers of the component's + * parent slot, not the component itself. + * + * You should only use the register values -- i.e., + * pcie_slot_{cap,control,status} -- if pcie_slot_data_valid is set to + * true. + */ + boolean_t pcie_slot_data_valid; /* true if slot data is valid */ + uint32_t pcie_slot_cap; /* pcie slot capabilities */ + uint16_t pcie_slot_control; /* pcie slot control */ + uint16_t pcie_slot_status; /* pcie slot status */ /* Flags */ boolean_t pcie_rp_send_all; /* need to send ereports on all rps */ @@ -131,7 +157,6 @@ typedef struct fab_data { typedef struct fab_erpt_tbl { const char *err_class; /* Final Ereport Class */ uint32_t reg_bit; /* Error Bit Mask */ - /* Pointer to function that prepares the ereport body */ const char *tgt_class; /* Target Ereport Class */ } fab_erpt_tbl_t; diff --git a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c index 69ecf1aa8d..14ae738863 100644 --- a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c +++ b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c @@ -22,10 +22,13 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2019 Joyent, Inc. */ #include #include #include +#include #include "fabric-xlate.h" @@ -271,6 +274,24 @@ fab_pci_fabric_to_data(fmd_hdl_t *hdl, nvlist_t *nvl, fab_data_t *data) FAB_LOOKUP(32, "pcie_adv_rp_command", &data->pcie_rp_err_cmd); FAB_LOOKUP(16, "pcie_adv_rp_ce_src_id", &data->pcie_rp_ce_src_id); FAB_LOOKUP(16, "pcie_adv_rp_ue_src_id", &data->pcie_rp_ue_src_id); + + /* + * PCIe Parent Slot Registers + * + * These are only passed in the ereport if the parent PCIe component + * supports the registers and the registers have valid data. As such, we + * look up one slot register value first: If that value is present in + * the input ereport data, then we know the others should be there as + * well. We also set the pcie_slot_data_valid flag to ensure we know + * the slot register data is safe to use in the module. + */ + data->pcie_slot_data_valid = B_FALSE; + if (nvlist_lookup_uint32(nvl, "pcie_slot_cap", &data->pcie_slot_cap) == + 0) { + FAB_LOOKUP(16, "pcie_slot_control", &data->pcie_slot_control); + FAB_LOOKUP(16, "pcie_slot_status", &data->pcie_slot_status); + data->pcie_slot_data_valid = B_TRUE; + } } static int @@ -358,6 +379,38 @@ fab_prep_pcie_ue_erpt(fmd_hdl_t *hdl, fab_data_t *data, nvlist_t *erpt, PCIE_AER_CTL_FST_ERR_PTR_MASK); int err = fab_prep_basic_erpt(hdl, data->nvl, erpt, B_FALSE); + if (data->pcie_slot_data_valid) { + (void) nvlist_add_uint32(erpt, "pcie_slot_cap", + data->pcie_slot_cap); + (void) nvlist_add_uint16(erpt, "pcie_slot_control", + data->pcie_slot_control); + (void) nvlist_add_uint16(erpt, "pcie_slot_status", + data->pcie_slot_status); + + /* + * It is possible to see uncorrectable errors for a slot that + * are related to the slot's child device being physically + * removed from the slot. As such, in the case that the slot + * reports that it is empty, we do not want to generate an + * ereport for all errors. Generating an ereport here will cause + * the eft module to fault the device and io-retire to + * subsequently retire the device. Retiring the device makes + * little sense given that the device is physically gone; more + * confusingly, if plugged back into the slot, it would be + * marked retired already. + * + * The only error ignored for this case is Completion Timeout. + * It is possible more errors should be ignored, and if they + * are seen in the field it might be worth broadening the set + * of ignored errors. + */ + if (tbl->reg_bit == PCIE_AER_UCE_TO && + ((data->pcie_slot_status & + PCIE_SLOTSTS_PRESENCE_DETECTED) == 0x0)) { + return (PF_EREPORT_IGNORE); + } + } + /* Generate an ereport for this error bit. */ (void) snprintf(fab_buf, FM_MAX_CLASS, "ereport.io.%s.%s", PCIEX_ERROR_SUBCLASS, class); @@ -776,7 +829,7 @@ fab_xlate_pcie_erpts(fmd_hdl_t *hdl, fab_data_t *data) fmd_hdl_debug(hdl, "Sending Ereports Now"); - /* Go through the error logs and send the relavant reports */ + /* Go through the error logs and send the relevant reports */ for (tbl = fab_master_err_tbl; tbl->erpt_tbl; tbl++) { fab_send_erpt(hdl, data, tbl); } diff --git a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c index 8593144b28..94678dbd47 100644 --- a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c +++ b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include #include @@ -185,6 +186,7 @@ fab_send_erpt(fmd_hdl_t *hdl, fab_data_t *data, fab_err_tbl_t *tbl) fab_erpt_tbl_t *erpt_tbl, *entry; nvlist_t *erpt; uint32_t reg; + int err; erpt_tbl = tbl->erpt_tbl; if (tbl->reg_size == 16) { @@ -200,7 +202,9 @@ fab_send_erpt(fmd_hdl_t *hdl, fab_data_t *data, fab_err_tbl_t *tbl) if (nvlist_alloc(&erpt, NV_UNIQUE_NAME, 0) != 0) goto done; - if (tbl->fab_prep(hdl, data, erpt, entry) != 0) { + + err = tbl->fab_prep(hdl, data, erpt, entry); + if (err != 0 && err != PF_EREPORT_IGNORE) { fmd_hdl_debug(hdl, "Prepping ereport failed: " "class = %s\n", entry->err_class); nvlist_free(erpt); @@ -394,7 +398,7 @@ fab_find_rppath_by_devbdf(fmd_hdl_t *hdl, nvlist_t *nvl, pcie_req_id_t bdf) xmlXPathObjectPtr xpathObj; xmlNodeSetPtr nodes; xmlNodePtr devNode; - char *retval, *temp; + char *retval, *temp; char query[500]; int i, size, bus, dev, fn; char *hcpath; @@ -577,7 +581,7 @@ fail: char * fab_find_bdf(fmd_hdl_t *hdl, nvlist_t *nvl, pcie_req_id_t bdf) { - char *retval; + char *retval; char query[500]; int bus, dev, fn; char rcpath[255]; @@ -705,7 +709,7 @@ found: propgroup: /* Retrive the "dev" propval and return */ for (devNode = devNode->children; devNode; devNode = devNode->next) { - char *tprop; + char *tprop; tprop = GET_PROP(devNode, "name"); if (STRCMP(devNode->name, "propval") && @@ -866,8 +870,8 @@ fab_pr(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl) char * fab_get_rpdev(fmd_hdl_t *hdl) { - char *retval; - char query[500]; + char *retval; + char query[500]; (void) snprintf(query, sizeof (query), "//propval[" "@name='extended-capabilities' and contains(@value, '%s')]" @@ -888,8 +892,8 @@ fab_send_erpt_all_rps(fmd_hdl_t *hdl, nvlist_t *erpt) { xmlXPathObjectPtr xpathObj; xmlNodeSetPtr nodes; - char *rppath, *hbpath; - char query[600]; + char *rppath, *hbpath; + char query[600]; nvlist_t *detector, *nvl; uint_t i, size; size_t len; diff --git a/usr/src/uts/common/io/nvme/nvme.c b/usr/src/uts/common/io/nvme/nvme.c index e37d0598c3..8215adaed6 100644 --- a/usr/src/uts/common/io/nvme/nvme.c +++ b/usr/src/uts/common/io/nvme/nvme.c @@ -59,7 +59,7 @@ * but they share some driver state: the command array (holding pointers to * commands currently being processed by the hardware) and the active command * counter. Access to a submission queue and the shared state is protected by - * nq_mutex, completion queue is protected by ncq_mutex. + * nq_mutex; completion queue is protected by ncq_mutex. * * When a command is submitted to a queue pair the active command counter is * incremented and a pointer to the command is stored in the command array. The @@ -202,6 +202,23 @@ * device. * * + * NVMe Hotplug: + * + * The driver supports hot removal. The driver uses the NDI event framework + * to register a callback, nvme_remove_callback, to clean up when a disk is + * removed. In particular, the driver will unqueue outstanding I/O commands and + * set n_dead on the softstate to true so that other operations, such as ioctls + * and command submissions, fail as well. + * + * While the callback registration relies on the NDI event framework, the + * removal event itself is kicked off in the PCIe hotplug framework, when the + * PCIe bridge driver ("pcieb") gets a hotplug interrupt indicatating that a + * device was removed from the slot. + * + * The NVMe driver instance itself will remain until the final close of the + * device. + * + * * DDI UFM Support * * The driver supports the DDI UFM framework for reporting information about @@ -1066,6 +1083,10 @@ nvme_submit_admin_cmd(nvme_qpair_t *qp, nvme_cmd_t *cmd) static int nvme_submit_io_cmd(nvme_qpair_t *qp, nvme_cmd_t *cmd) { + if (cmd->nc_nvme->n_dead) { + return (EIO); + } + if (sema_tryp(&qp->nq_sema) == 0) return (EAGAIN); @@ -1081,6 +1102,22 @@ nvme_submit_cmd_common(nvme_qpair_t *qp, nvme_cmd_t *cmd) mutex_enter(&qp->nq_mutex); 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 @@ -3267,6 +3304,47 @@ nvme_fm_errcb(dev_info_t *dip, ddi_fm_error_t *fm_error, const void *arg) return (fm_error->fme_status); } +static void +nvme_remove_callback(dev_info_t *dip, ddi_eventcookie_t cookie, void *a, + void *b) +{ + nvme_t *nvme = a; + + nvme->n_dead = B_TRUE; + + /* + * Fail all outstanding commands, including those in the admin queue + * (queue 0). + */ + for (uint_t i = 0; i < nvme->n_ioq_count + 1; i++) { + nvme_qpair_t *qp = nvme->n_ioq[i]; + + mutex_enter(&qp->nq_mutex); + for (size_t j = 0; j < qp->nq_nentry; j++) { + nvme_cmd_t *cmd = qp->nq_cmd[j]; + nvme_cmd_t *u_cmd; + + if (cmd == NULL) { + continue; + } + + /* + * Since we have the queue lock held the entire time we + * iterate over it, it's not possible for the queue to + * change underneath us. Thus, we don't need to check + * that the return value of nvme_unqueue_cmd matches the + * requested cmd to unqueue. + */ + u_cmd = nvme_unqueue_cmd(nvme, qp, cmd->nc_sqe.sqe_cid); + taskq_dispatch_ent(qp->nq_cq->ncq_cmd_taskq, + cmd->nc_callback, cmd, TQ_NOSLEEP, &cmd->nc_tqent); + + ASSERT3P(u_cmd, ==, cmd); + } + mutex_exit(&qp->nq_mutex); + } +} + static int nvme_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { @@ -3290,6 +3368,17 @@ nvme_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) ddi_set_driver_private(dip, nvme); nvme->n_dip = dip; + /* Set up event handlers for hot removal. */ + if (ddi_get_eventcookie(nvme->n_dip, DDI_DEVI_REMOVE_EVENT, + &nvme->n_rm_cookie) != DDI_SUCCESS) { + goto fail; + } + if (ddi_add_event_handler(nvme->n_dip, nvme->n_rm_cookie, + nvme_remove_callback, nvme, &nvme->n_ev_rm_cb_id) != + DDI_SUCCESS) { + goto fail; + } + mutex_init(&nvme->n_minor.nm_mutex, NULL, MUTEX_DRIVER, NULL); nvme->n_strict_version = ddi_prop_get_int(DDI_DEV_T_ANY, dip, @@ -3603,6 +3692,12 @@ nvme_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) if (nvme->n_product != NULL) strfree(nvme->n_product); + /* Clean up hot removal event handler. */ + if (nvme->n_ev_rm_cb_id != NULL) { + (void) ddi_remove_event_handler(nvme->n_ev_rm_cb_id); + } + nvme->n_ev_rm_cb_id = NULL; + ddi_soft_state_free(nvme_state, instance); return (DDI_SUCCESS); @@ -3891,6 +3986,11 @@ static int nvme_bd_mediainfo(void *arg, bd_media_t *media) { nvme_namespace_t *ns = arg; + nvme_t *nvme = ns->ns_nvme; + + if (nvme->n_dead) { + return (EIO); + } media->m_nblks = ns->ns_block_count; media->m_blksize = ns->ns_block_size; @@ -3911,8 +4011,9 @@ nvme_bd_cmd(nvme_namespace_t *ns, bd_xfer_t *xfer, uint8_t opc) boolean_t poll; int ret; - if (nvme->n_dead) + if (nvme->n_dead) { return (EIO); + } cmd = nvme_create_nvm_cmd(ns, opc, xfer); if (cmd == NULL) @@ -3993,6 +4094,11 @@ static int nvme_bd_devid(void *arg, dev_info_t *devinfo, ddi_devid_t *devid) { nvme_namespace_t *ns = arg; + nvme_t *nvme = ns->ns_nvme; + + if (nvme->n_dead) { + return (EIO); + } /*LINTED: E_BAD_PTR_CAST_ALIGN*/ if (*(uint64_t *)ns->ns_eui64 != 0) { diff --git a/usr/src/uts/common/io/nvme/nvme_var.h b/usr/src/uts/common/io/nvme/nvme_var.h index fb8f4ba771..ea378b8be4 100644 --- a/usr/src/uts/common/io/nvme/nvme_var.h +++ b/usr/src/uts/common/io/nvme/nvme_var.h @@ -114,20 +114,23 @@ struct nvme_cq { struct nvme_qpair { size_t nq_nentry; + /* submission fields */ nvme_dma_t *nq_sqdma; nvme_sqe_t *nq_sq; uint_t nq_sqhead; uint_t nq_sqtail; uintptr_t nq_sqtdbl; + /* completion */ nvme_cq_t *nq_cq; - nvme_cmd_t **nq_cmd; - uint16_t nq_next_cmd; - uint_t nq_active_cmds; + /* shared structures for completion and submission */ + nvme_cmd_t **nq_cmd; /* active command array */ + uint16_t nq_next_cmd; /* next potential empty queue slot */ + uint_t nq_active_cmds; /* number of active cmds */ - kmutex_t nq_mutex; - ksema_t nq_sema; + kmutex_t nq_mutex; /* protects shared state */ + ksema_t nq_sema; /* semaphore to ensure q always has >= 1 empty slot */ }; struct nvme { @@ -188,7 +191,12 @@ struct nvme { nvme_identify_ctrl_t *n_idctl; + /* Pointer to the admin queue, which is always queue 0 in n_ioq. */ nvme_qpair_t *n_adminq; + /* + * All command queues, including the admin queue. + * Its length is: n_ioq_count + 1. + */ nvme_qpair_t **n_ioq; nvme_cq_t **n_cq; @@ -244,6 +252,10 @@ struct nvme { uint32_t n_vendor_event; uint32_t n_unknown_event; + /* hot removal NDI event handling */ + ddi_eventcookie_t n_rm_cookie; + ddi_callback_id_t n_ev_rm_cb_id; + /* DDI UFM handle */ ddi_ufm_handle_t *n_ufmh; /* Cached Firmware Slot Information log page */ diff --git a/usr/src/uts/common/io/pciex/hotplug/pciehpc.c b/usr/src/uts/common/io/pciex/hotplug/pciehpc.c index 5ce219bd2f..3e4beda495 100644 --- a/usr/src/uts/common/io/pciex/hotplug/pciehpc.c +++ b/usr/src/uts/common/io/pciex/hotplug/pciehpc.c @@ -395,6 +395,21 @@ pciehpc_intr(dev_info_t *dip) PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN); + /* + * If supported, notify the child device driver that the + * device is being removed. + */ + dev_info_t *cdip = ddi_get_child(dip); + if (cdip != NULL) { + ddi_eventcookie_t rm_cookie; + if (ddi_get_eventcookie(cdip, + DDI_DEVI_REMOVE_EVENT, + &rm_cookie) == DDI_SUCCESS) { + ndi_post_event(dip, cdip, rm_cookie, + NULL); + } + } + /* * Ask DDI Hotplug framework to change state to Empty */ diff --git a/usr/src/uts/common/io/pciex/pcie.c b/usr/src/uts/common/io/pciex/pcie.c index 22f191943c..35a0190be7 100644 --- a/usr/src/uts/common/io/pciex/pcie.c +++ b/usr/src/uts/common/io/pciex/pcie.c @@ -845,6 +845,13 @@ pcie_init_pfd(dev_info_t *dip) PCIE_ZALLOC(pf_pcix_ecc_regs_t); } } + + PCIE_SLOT_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_slot_regs_t); + PCIE_SLOT_REG(pfd_p)->pcie_slot_regs_valid = B_FALSE; + PCIE_SLOT_REG(pfd_p)->pcie_slot_cap = 0; + PCIE_SLOT_REG(pfd_p)->pcie_slot_control = 0; + PCIE_SLOT_REG(pfd_p)->pcie_slot_status = 0; + } else if (PCIE_IS_PCIX(bus_p)) { if (PCIE_IS_BDG(bus_p)) { PCIX_BDG_ERR_REG(pfd_p) = diff --git a/usr/src/uts/common/io/pciex/pcie_fault.c b/usr/src/uts/common/io/pciex/pcie_fault.c index 90563c1d1a..de558a9fc6 100644 --- a/usr/src/uts/common/io/pciex/pcie_fault.c +++ b/usr/src/uts/common/io/pciex/pcie_fault.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017, Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ #include @@ -200,7 +200,7 @@ pf_eh_exit(pcie_bus_t *bus_p) * for the root_pfd_p. * * "Root Complexes" such as NPE and PX should call scan_fabric using itself as - * the rdip. PCIe Root ports should call pf_scan_fabric using it's parent as + * the rdip. PCIe Root ports should call pf_scan_fabric using its parent as * the rdip. * * Scan fabric initiated from RCs are likely due to a fabric message, traps or @@ -587,6 +587,35 @@ pf_pcie_regs_gather(pf_data_t *pfd_p, pcie_bus_t *bus_p) PCIE_ROOTCTL); } + /* + * For eligible components, we gather Slot Register state. + * + * Eligible components are: + * - a Downstream Port or a Root Port with the Slot Implemented + * capability bit set + * - hotplug capable + * + * Slot register state is useful, for instance, to determine whether the + * Slot's child device is physically present (via the Slot Status + * register). + */ + if ((PCIE_IS_SWD(bus_p) || PCIE_IS_ROOT(bus_p)) && + PCIE_IS_HOTPLUG_ENABLED(PCIE_BUS2DIP(bus_p))) { + pf_pcie_slot_regs_t *pcie_slot_regs = PCIE_SLOT_REG(pfd_p); + pcie_slot_regs->pcie_slot_cap = PCIE_CAP_GET(32, bus_p, + PCIE_SLOTCAP); + pcie_slot_regs->pcie_slot_control = PCIE_CAP_GET(16, bus_p, + PCIE_SLOTCTL); + pcie_slot_regs->pcie_slot_status = PCIE_CAP_GET(16, bus_p, + PCIE_SLOTSTS); + + if (pcie_slot_regs->pcie_slot_cap != PCI_EINVAL32 && + pcie_slot_regs->pcie_slot_control != PCI_EINVAL16 && + pcie_slot_regs->pcie_slot_status != PCI_EINVAL16) { + pcie_slot_regs->pcie_slot_regs_valid = B_TRUE; + } + } + if (!PCIE_HAS_AER(bus_p)) return; @@ -838,7 +867,7 @@ pf_pci_find_rp_fault(pf_data_t *pfd_p, pcie_bus_t *bus_p) * Check to see if an error has been received that * requires a scan of the fabric. Count the number of * faults seen. If MUL CE/FE_NFE that counts for - * atleast 2 faults, so just return with full_scan. + * at least 2 faults, so just return with full_scan. */ if ((root_err & PCIE_AER_RE_STS_MUL_CE_RCVD) || (root_err & PCIE_AER_RE_STS_MUL_FE_NFE_RCVD)) { @@ -1232,7 +1261,7 @@ const pf_fab_err_tbl_t pcie_rp_tbl[] = { {PCIE_AER_UCE_FCP, pf_panic, PF_AFFECTED_SELF | PF_AFFECTED_CHILDREN, 0}, - {PCIE_AER_UCE_TO, pf_panic, + {PCIE_AER_UCE_TO, pf_analyse_to, PF_AFFECTED_ADDR, PF_AFFECTED_CHILDREN}, {PCIE_AER_UCE_CA, pf_no_panic, @@ -1916,16 +1945,35 @@ pf_analyse_sc(ddi_fm_error_t *derr, uint32_t bit, pf_data_t *dq_head_p, /* * PCIe Timeout error analyser. This error can be forgiven if it is marked as * CE Advisory. If it is marked as advisory, this means the HW can recover - * and/or retry the transaction automatically. + * and/or retry the transaction automatically. Additionally, if a device's + * parent slot reports that it is no longer physically present, we do not panic, + * as one would not expect a missing device to respond to a command. */ /* ARGSUSED */ static int pf_analyse_to(ddi_fm_error_t *derr, uint32_t bit, pf_data_t *dq_head_p, pf_data_t *pfd_p) { + dev_info_t *rpdip = PCIE_PFD2BUS(pfd_p)->bus_rp_dip; + pf_data_t *rppfd = PCIE_DIP2PFD(rpdip); + pf_pcie_slot_regs_t *p_pcie_slot_regs; + if (HAS_AER_LOGS(pfd_p, bit) && CE_ADVISORY(pfd_p)) return (PF_ERR_NO_PANIC); + p_pcie_slot_regs = PCIE_SLOT_REG(rppfd); + if (p_pcie_slot_regs->pcie_slot_regs_valid) { + /* + * If the device is reported gone from its parent slot, then it + * is expected that any outstanding commands would time out. In + * this case, do not panic. + */ + if ((p_pcie_slot_regs->pcie_slot_status & + PCIE_SLOTSTS_PRESENCE_DETECTED) == 0x0) { + return (PF_ERR_NO_PANIC); + } + } + return (PF_ERR_PANIC); } @@ -2970,6 +3018,24 @@ pf_send_ereport(ddi_fm_error_t *derr, pf_impl_t *impl) NULL); } + /* + * Slot Status registers + * + * Since we only gather these for certain types of components, + * only put these registers into the ereport if we have valid + * data. + */ + if (PCIE_SLOT_REG(pfd_p)->pcie_slot_regs_valid) { + fm_payload_set(ereport, + "pcie_slot_cap", DATA_TYPE_UINT32, + PCIE_SLOT_REG(pfd_p)->pcie_slot_cap, + "pcie_slot_control", DATA_TYPE_UINT16, + PCIE_SLOT_REG(pfd_p)->pcie_slot_control, + "pcie_slot_status", DATA_TYPE_UINT16, + PCIE_SLOT_REG(pfd_p)->pcie_slot_status, + NULL); + } + generic: /* IOV related information */ if (!PCIE_BDG_IS_UNASSIGNED(PCIE_PFD2BUS(impl->pf_dq_head_p))) { diff --git a/usr/src/uts/common/os/ddi_hp_impl.c b/usr/src/uts/common/os/ddi_hp_impl.c index 79165af9ff..38e575dbfd 100644 --- a/usr/src/uts/common/os/ddi_hp_impl.c +++ b/usr/src/uts/common/os/ddi_hp_impl.c @@ -21,12 +21,239 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2019 Joyent, Inc. */ /* * Sun DDI hotplug implementation specific functions */ +/* + * HOTPLUG FRAMEWORK + * + * The hotplug framework (also referred to "SHP", for "Solaris Hotplug + * Framework") refers to a large set of userland and kernel interfaces, + * including those in this file, that provide functionality related to device + * hotplug. + * + * Hotplug is a broad term that refers to both removal and insertion of devices + * on a live system. Such operations can have varying levels of notification to + * the system. Coordinated hotplug means that the operating system is notified + * in advance that a device will have a hotplug operation performed on it. + * Non-coordinated hotplug, also called "surprise removal", does not have such + * notification, and the device is simply removed or inserted from the system. + * + * The goals of a correct hotplug operation will vary based on the device. In + * general, though, we want the system to gracefully notice the device change + * and clean up (or create) any relevant structures related to using the device + * in the system. + * + * The goals of the hotplug framework are to provide common interfaces for nexus + * drivers, device drivers, and userland programs to build a foundation for + * implementing hotplug for a variety of devices. Notably, common support for + * PCIe devices is available. See also: the nexus driver for PCIe devices at + * uts/i86pc/io/pciex/npe.c. + * + * + * TERMINOLOGY + * + * The following terms may be useful when exploring hotplug-related code. + * + * PHYSICAL HOTPLUG + * Refers to hotplug operations on a physical hardware receptacle. + * + * VIRTUAL HOTPLUG + * Refers to hotplug operations on an arbitrary device node in the device + * tree. + * + * CONNECTION (often abbreviated "cn") + * A place where either physical or virtual hotplug happens. This is a more + * generic term to refer to "connectors" and "ports", which represent + * physical and virtual places where hotplug happens, respectively. + * + * CONNECTOR + * A place where physical hotplug happens. For example: a PCIe slot, a USB + * port, a SAS port, and a fiber channel port are all connectors. + * + * PORT + * A place where virtual hotplug happens. A port refers to an arbitrary + * place under a nexus dev_info node in the device tree. + * + * + * CONNECTION STATE MACHINE + * + * Connections have the states below. Connectors and ports are grouped into + * the same state machine. It is worth noting that the edges here are incomplete + * -- it is possible for a connection to move straight from ENABLED to EMPTY, + * for instance, if there is a surprise removal of its device. + * + * State changes are kicked off through two ways: + * - Through the nexus driver interface, ndi_hp_state_change_req. PCIe + * nexus drivers that pass a hotplug interrupt through to pciehpc will kick + * off state changes in this way. + * - Through coordinated removal, ddihp_modctl. Both cfgadm(1M) and + * hotplug(1M) pass state change requests through hotplugd, which uses + * modctl to request state changes to the DDI hotplug framework. That + * interface is ultimately implemented by ddihp_modctl. + * + * (start) + * | + * v + * EMPTY no component plugged into connector + * ^ + * v + * PRESENT component plugged into connector + * ^ + * v + * POWERED connector is powered + * ^ + * v + * ENABLED connector is fully functional + * | + * . + * . + * . + * v + * (create port) + * | + * v + * PORT EMPTY port has no device occupying it + * ^ + * v + * PORT PRESENT port occupied by device + * + * + * ARCHITECTURE DIAGRAM + * + * The following is a non-exhaustive summary of various components in the system + * that implement pieces of the hotplug framework. More detailed descriptions + * of some key components are below. + * + * +------------+ + * | cfgadm(1M) | + * +------------+ + * | + * +-------------------+ + * | SHP cfgadm plugin | + * +-------------------+ + * | + * +-------------+ +------------+ + * | hotplug(1M) |----------| libhotplug | + * +-------------+ +------------+ + * | + * +----------+ + * | hotplugd | + * +----------+ + * | + * +----------------+ + * | modctl (HP op) | + * +----------------+ + * | + * | + * User | + * =============================|=============================================== + * Kernel | + * | + * | + * +------------------------+ +----------------+ + * | DDI hotplug interfaces | --- | Device Drivers | + * +------------------------+ +----------------+ + * | | + * | +------------------------+ + * | | NDI hotplug interfaces | + * | +------------------------+ + * | | + * | | + * +-------------+ +--------------+ +---------------------------+ + * | `bus_hp_op` | -- |"pcie" module | --- | "npe" (PCIe nexus driver) | + * +-------------+ +--------------+ +---------------------------+ + * | | + * | +-------------------+ + * | | PCIe configurator | + * | +-------------------+ + * | + * +-------------------------------------+ + * | "pciehpc" (PCIe hotplug controller) | + * +-------------------------------------+ + * + * + * . + * . + * . + * . + * . + * | + * | + * +-----------------------------------+ + * | I/O Subsystem | + * | (LDI notifications and contracts) | + * +-----------------------------------+ + * + * + * KEY HOTPLUG SOFTWARE COMPONENTS + * + * CFGADM(1M) + * + * cfgadm is the canonical tool for hotplug operations. It can be used to + * list connections on the system and change their state in a coordinated + * fashion. For more information, see its manual page. + * + * + * HOTPLUG(1M) + * + * hotplug is a command line tool for managing hotplug connections for + * connectors. For more information, see its manual page. + * + * + * DDI HOTPLUG INTERFACES + * + * This part of the framework provides interfaces for changing device state + * for connectors, including onlining and offlining child devices. Many of + * these functions are defined in this file. + * + * + * NDI HOTPLUG INTERFACES + * + * Nexus drivers can define their own hotplug bus implementations by + * defining a bus_hp_op entry point. This entry point must implement + * a set of hotplug related commands, including getting, probing, and + * changing connection state, as well as port creation and removal. + * + * Nexus drivers may also want to use the following interfaces for + * implementing hotplug. Note that the PCIe Hotplug Controller ("pciehpc") + * already takes care of using these: + * ndi_hp_{register,unregister} + * ndi_hp_state_change_req + * ndi_hp_walk_cn + * + * PCIe nexus drivers should use the common entry point pcie_hp_common_ops, + * which implements hotplug commands for PCIe devices, calling into other + * parts of the framework as needed. + * + * + * NPE DRIVER ("npe") + * + * npe is the common nexus driver for PCIe devices on x86. It implements + * hotplug using the NDI interfaces. For more information, see + * uts/i86pc/io/pciex/npe.c. + * + * The equivalent driver for SPARC is "px". + * + * + * PCIe HOTPLUG CONTROLLER DRIVER ("pciehpc") + * + * All hotplug-capable PCIe buses will initialize their own PCIe HPC, + * including the pcieb and ppb drivers. The controller maintains + * hotplug-related state about the slots on its bus, including their status + * and port state. It also features a common implementation of handling + * hotplug-related PCIe interrupts. + * + * For more information, see its interfaces in + * uts/common/sys/hotplug/pci/pciehpc.h. + * + */ + #include #include #include @@ -163,7 +390,9 @@ done: } /* - * Return the state of Hotplug Connection (CN) + * Fetch the state of Hotplug Connection (CN). + * This function will also update the state and last changed timestamp in the + * connection handle structure if the state has changed. */ int ddihp_cn_getstate(ddi_hp_cn_handle_t *hdlp) @@ -597,7 +826,7 @@ ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp, curr_state == DDI_HP_CN_STATE_ENABLED) { /* * If the Connection goes to a lower state from ENABLED, - * then offline all children under it. + * then offline all children under it. */ rv = ddihp_cn_change_children_state(hdlp, B_FALSE); if (rv != DDI_SUCCESS) { @@ -640,7 +869,7 @@ ddihp_cn_pre_change_state(ddi_hp_cn_handle_t *hdlp, } /* - * Jobs after change state of a Connector: update last change time, + * Jobs after change state of a Connector: update state, last change time, * probe, online, sysevent, etc. */ static int @@ -813,7 +1042,7 @@ ddihp_cn_change_children_state(ddi_hp_cn_handle_t *hdlp, boolean_t online) NDI_SUCCESS) { cmn_err(CE_WARN, "(%s%d):" - " failed to dettach driver for the device" + " failed to detach driver for the device" " (%s%d) in the Connection %s\n", ddi_driver_name(dip), ddi_get_instance(dip), ddi_driver_name(cdip), diff --git a/usr/src/uts/common/os/ddi_hp_ndi.c b/usr/src/uts/common/os/ddi_hp_ndi.c index a41a12fc74..73c62dc6b9 100644 --- a/usr/src/uts/common/os/ddi_hp_ndi.c +++ b/usr/src/uts/common/os/ddi_hp_ndi.c @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2019 Joyent, Inc. */ /* @@ -380,13 +382,19 @@ ddihp_cn_req_handler(ddi_hp_cn_handle_t *hdlp, ASSERT(DEVI_BUSY_OWNED(dip)); - if (ddihp_cn_getstate(hdlp) != DDI_SUCCESS) { - DDI_HP_NEXDBG((CE_CONT, "ddihp_cn_req_handler: dip %p, " - "hdlp %p ddi_cn_getstate failed\n", (void *)dip, - (void *)hdlp)); - - return (NDI_UNCLAIMED); - } + /* + * We do not want to fetch the state first, as calling ddihp_cn_getstate + * will update the cn_state member of the connection handle. The + * connector's hotplug operations rely on this value to know how + * target_state compares to the last known state of the device and make + * decisions about whether to clean up, post sysevents about the state + * change, and so on. + * + * Instead, just carry out the request to change the state. The + * connector's hotplug operations will update the state in the + * connection handle after they complete their necessary state change + * actions. + */ if (hdlp->cn_info.cn_state != target_state) { ddi_hp_cn_state_t result_state = 0; diff --git a/usr/src/uts/common/sys/ddi_hp.h b/usr/src/uts/common/sys/ddi_hp.h index eadb88ed49..b88762a9f5 100644 --- a/usr/src/uts/common/sys/ddi_hp.h +++ b/usr/src/uts/common/sys/ddi_hp.h @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2019 Joyent, Inc. */ #ifndef _SYS_DDI_HP_H @@ -28,6 +30,9 @@ /* * Sun DDI hotplug support definitions + * + * See the big theory statement in uts/common/os/ddi_hp_impl.c for more + * information. */ #ifdef __cplusplus @@ -73,7 +78,8 @@ typedef enum { /* * ddi_hp_cn_info_t * - * Hotplug Connection (CN) information structure + * Hotplug Connection (CN) information structure. + * A Connection is either a Connector or a Port. */ typedef struct ddi_hp_cn_info { char *cn_name; /* Name of the Connection */ diff --git a/usr/src/uts/common/sys/ddi_hp_impl.h b/usr/src/uts/common/sys/ddi_hp_impl.h index fb220119dd..b52df77cac 100644 --- a/usr/src/uts/common/sys/ddi_hp_impl.h +++ b/usr/src/uts/common/sys/ddi_hp_impl.h @@ -21,6 +21,12 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. + */ + +/* + * See the big theory statement in uts/common/os/ddi_hp_impl.c for more + * information about the structures and functions defined here. */ #ifndef _SYS_DDI_HP_IMPL_H diff --git a/usr/src/uts/common/sys/pcie_impl.h b/usr/src/uts/common/sys/pcie_impl.h index d1d13625c2..442c55043c 100644 --- a/usr/src/uts/common/sys/pcie_impl.h +++ b/usr/src/uts/common/sys/pcie_impl.h @@ -166,6 +166,7 @@ extern "C" { #define PCIE_ADV_BDG_HDR(pfd_p, n) PCIE_ADV_BDG_REG(pfd_p)->pcie_sue_hdr[n] #define PCIE_ADV_RP_REG(pfd_p) \ PCIE_ADV_REG(pfd_p)->pcie_ext.pcie_adv_rp_regs +#define PCIE_SLOT_REG(pfd_p) pfd_p->pe_pcie_slot_regs #define PFD_AFFECTED_DEV(pfd_p) pfd_p->pe_affected_dev #define PFD_SET_AFFECTED_FLAG(pfd_p, aff_flag) \ PFD_AFFECTED_DEV(pfd_p)->pe_affected_flags = aff_flag @@ -262,6 +263,18 @@ typedef struct pf_pcie_err_regs { pf_pcie_adv_err_regs_t *pcie_adv_regs; /* pcie aer regs */ } pf_pcie_err_regs_t; +/* + * Slot register values for hotplug-capable Downstream Ports or Root Ports with + * the Slot Implemented capability bit set. We gather these to help determine + * whether the slot's child device is physically present. + */ +typedef struct pf_pcie_slot_regs { + boolean_t pcie_slot_regs_valid; /* true if register values are valid */ + uint32_t pcie_slot_cap; /* pcie slot capabilities register */ + uint16_t pcie_slot_control; /* pcie slot control register */ + uint16_t pcie_slot_status; /* pcie slot status register */ +} pf_pcie_slot_regs_t; + typedef enum { PF_INTR_TYPE_NONE = 0, PF_INTR_TYPE_FABRIC = 1, /* Fabric Message */ @@ -431,6 +444,7 @@ struct pf_data { pf_pcie_err_regs_t *pe_pcie_regs; /* PCIe error reg */ } pe_ext; pf_pcix_bdg_err_regs_t *pe_pcix_bdg_regs; /* PCI-X bridge regs */ + pf_pcie_slot_regs_t *pe_pcie_slot_regs; /* PCIe slot regs */ pf_data_t *pe_prev; /* Next error in queue */ pf_data_t *pe_next; /* Next error in queue */ boolean_t pe_rber_fatal; diff --git a/usr/src/uts/i86pc/io/pci/pci_common.h b/usr/src/uts/i86pc/io/pci/pci_common.h index 63fe4bb165..d5fa3bfd55 100644 --- a/usr/src/uts/i86pc/io/pci/pci_common.h +++ b/usr/src/uts/i86pc/io/pci/pci_common.h @@ -22,6 +22,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2019 Joyent, Inc. */ #ifndef _PCI_PCI_COMMON_H @@ -33,7 +35,7 @@ extern "C" { /* * Common header file with definitions shared between - * pci(7d) and npe(7d) + * pci(7D) and npe(7D) */ /* State structure. */ @@ -45,12 +47,18 @@ typedef struct pci_state { kmutex_t pci_mutex; kmutex_t pci_peek_poke_mutex; kmutex_t pci_err_mutex; + + /* + * The following members are only used by npe(7D). + * See uts/i86pc/io/pciex/npe.c for more information. + */ + ndi_event_hdl_t pci_ndi_event_hdl; } pci_state_t; /* * These are the access routines. - * The pci_bus_map sets the handle to point to these in pci(7d). - * The npe_bus_map sets the handle to point to these in npe(7d). + * The pci_bus_map sets the handle to point to these in pci(7D). + * The npe_bus_map sets the handle to point to these in npe(7D). */ uint8_t pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr); uint16_t pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr); diff --git a/usr/src/uts/i86pc/io/pciex/npe.c b/usr/src/uts/i86pc/io/pciex/npe.c index 4ef393ddb0..fcb68164ee 100644 --- a/usr/src/uts/i86pc/io/pciex/npe.c +++ b/usr/src/uts/i86pc/io/pciex/npe.c @@ -26,11 +26,35 @@ /* * Copyright 2012 Garrett D'Amore . All rights reserved. - * Copyright 2016 Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ /* - * Host to PCI-Express local bus driver + * npe (Nexus PCIe driver): Host to PCI-Express local bus driver + * + * npe serves as the driver for PCIe Root Complexes and as the nexus driver + * for PCIe devices. See also: npe(7D). For more information about hotplug, + * see the big theory statement at uts/common/os/ddi_hp_impl.c. + * + * + * NDI EVENT HANDLING SUPPORT + * + * npe supports NDI event handling. The only available event is surprise + * removal of a device. Child drivers can register surprise removal event + * callbacks by requesting an event cookie using ddi_get_eventcookie for + * the DDI_DEVI_REMOVE_EVENT and add their callback using + * ddi_add_event_handler. For an example, see the nvme driver in + * uts/common/io/nvme/nvme.c. + * + * The NDI events in npe are retrieved using NDI_EVENT_NOPASS, which + * prevent them from being propagated up the tree once they reach the npe's + * bus_get_eventcookie operations. This is important because npe maintains + * the state of PCIe devices and their receptacles, via the PCIe hotplug + * controller driver (pciehpc). + * + * Hot removal events are ultimately posted by the PCIe hotplug controller + * interrupt handler for hotplug events. Events are posted using the + * ndi_post_event interface. */ #include @@ -72,6 +96,15 @@ static int npe_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t, ddi_intr_handle_impl_t *, void *); static int npe_fm_init(dev_info_t *, dev_info_t *, int, ddi_iblock_cookie_t *); +static int npe_bus_get_eventcookie(dev_info_t *, dev_info_t *, char *, + ddi_eventcookie_t *); +static int npe_bus_add_eventcall(dev_info_t *, dev_info_t *, + ddi_eventcookie_t, void (*)(dev_info_t *, + ddi_eventcookie_t, void *, void *), + void *, ddi_callback_id_t *); +static int npe_bus_remove_eventcall(dev_info_t *, ddi_callback_id_t); +static int npe_bus_post_event(dev_info_t *, dev_info_t *, + ddi_eventcookie_t, void *); static int npe_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *); @@ -102,10 +135,10 @@ struct bus_ops npe_bus_ops = { ddi_dma_mctl, npe_ctlops, ddi_bus_prop_op, - 0, /* (*bus_get_eventcookie)(); */ - 0, /* (*bus_add_eventcall)(); */ - 0, /* (*bus_remove_eventcall)(); */ - 0, /* (*bus_post_event)(); */ + npe_bus_get_eventcookie, + npe_bus_add_eventcall, + npe_bus_remove_eventcall, + npe_bus_post_event, 0, /* (*bus_intr_ctl)(); */ 0, /* (*bus_config)(); */ 0, /* (*bus_unconfig)(); */ @@ -271,12 +304,27 @@ npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) return (ret); } +/* + * See big theory statement at the top of this file for more information about + * surprise removal events. + */ +#define NPE_EVENT_TAG_HOT_REMOVAL 0 +static ndi_event_definition_t npe_ndi_event_defs[1] = { + {NPE_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, + NDI_EVENT_POST_TO_ALL} +}; + +static ndi_event_set_t npe_ndi_events = { + NDI_EVENTS_REV1, ARRAY_SIZE(npe_ndi_event_defs), npe_ndi_event_defs +}; + /*ARGSUSED*/ static int npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { int instance = ddi_get_instance(devi); pci_state_t *pcip = NULL; + int ret; if (cmd == DDI_RESUME) { /* @@ -316,6 +364,22 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) if (pcie_init(devi, NULL) != DDI_SUCCESS) goto fail1; + ret = ndi_event_alloc_hdl(pcip->pci_dip, NULL, &pcip->pci_ndi_event_hdl, + NDI_SLEEP); + if (ret == NDI_SUCCESS) { + ret = ndi_event_bind_set(pcip->pci_ndi_event_hdl, + &npe_ndi_events, NDI_SLEEP); + if (ret != NDI_SUCCESS) { + dev_err(pcip->pci_dip, CE_WARN, "npe: failed to bind " + "NDI event set (error=%d)", ret); + goto fail1; + } + } else { + dev_err(pcip->pci_dip, CE_WARN, "npe: failed to allocate " + "event handle (error=%d)", ret); + goto fail1; + } + /* Second arg: initialize for pci_express root nexus */ if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS) goto fail2; @@ -352,11 +416,36 @@ npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) { int instance = ddi_get_instance(devi); pci_state_t *pcip; + int ret; pcip = ddi_get_soft_state(npe_statep, ddi_get_instance(devi)); switch (cmd) { case DDI_DETACH: + + /* + * Clean up event handling first, to ensure there are no + * oustanding callbacks registered. + */ + ret = ndi_event_unbind_set(pcip->pci_ndi_event_hdl, + &npe_ndi_events, NDI_SLEEP); + if (ret == NDI_SUCCESS) { + /* ndi_event_free_hdl always succeeds. */ + (void) ndi_event_free_hdl(pcip->pci_ndi_event_hdl); + } else { + /* + * The event set will only fail to unbind if there are + * outstanding callbacks registered for it, which + * probably means a child driver still has one + * registered and thus was not cleaned up properly + * before npe's detach routine was called. Consequently, + * we should fail the detach here. + */ + dev_err(pcip->pci_dip, CE_WARN, "npe: failed to " + "unbind NDI event set (error=%d)", ret); + return (DDI_FAILURE); + } + pcie_fab_fini_bus(devi, PCIE_BUS_INITIAL); /* Uninitialize pcitool support. */ @@ -373,6 +462,7 @@ npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) ddi_fm_fini(devi); ddi_soft_state_free(npe_statep, instance); + return (DDI_SUCCESS); case DDI_SUSPEND: @@ -414,7 +504,7 @@ static int npe_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, off_t offset, off_t len, caddr_t *vaddrp) { - int rnumber; + int rnumber; int space; ddi_acc_impl_t *ap; ddi_acc_hdl_t *hp; @@ -1111,6 +1201,49 @@ npe_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap, return (pcip->pci_fmcap); } +static int +npe_bus_get_eventcookie(dev_info_t *dip, dev_info_t *rdip, char *eventname, + ddi_eventcookie_t *cookiep) +{ + pci_state_t *pcip = ddi_get_soft_state(npe_statep, + ddi_get_instance(dip)); + + return (ndi_event_retrieve_cookie(pcip->pci_ndi_event_hdl, rdip, + eventname, cookiep, NDI_EVENT_NOPASS)); +} + +static int +npe_bus_add_eventcall(dev_info_t *dip, dev_info_t *rdip, + ddi_eventcookie_t cookie, void (*callback)(dev_info_t *dip, + ddi_eventcookie_t cookie, void *arg, void *bus_impldata), + void *arg, ddi_callback_id_t *cb_id) +{ + pci_state_t *pcip = ddi_get_soft_state(npe_statep, + ddi_get_instance(dip)); + + return (ndi_event_add_callback(pcip->pci_ndi_event_hdl, rdip, cookie, + callback, arg, NDI_SLEEP, cb_id)); +} + +static int +npe_bus_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) +{ + pci_state_t *pcip = ddi_get_soft_state(npe_statep, + ddi_get_instance(dip)); + return (ndi_event_remove_callback(pcip->pci_ndi_event_hdl, cb_id)); +} + +static int +npe_bus_post_event(dev_info_t *dip, dev_info_t *rdip, + ddi_eventcookie_t cookie, void *impl_data) +{ + pci_state_t *pcip = ddi_get_soft_state(npe_statep, + ddi_get_instance(dip)); + return (ndi_event_do_callback(pcip->pci_ndi_event_hdl, rdip, cookie, + impl_data)); + +} + /*ARGSUSED*/ static int npe_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used) diff --git a/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c b/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c index b482117c7c..3f890d8f07 100644 --- a/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c +++ b/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2019, Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ /* @@ -1251,7 +1251,7 @@ pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno) { dev_info_t *new_ntbridgechild; - int len, bus; + int len, bus; uint16_t vid; ddi_acc_handle_t config_handle; pci_bus_range_t pci_bus_range; @@ -1368,7 +1368,7 @@ pcicfg_is_ntbridge(dev_info_t *dip) static uint_t pcicfg_ntbridge_child(dev_info_t *dip) { - int len, val, rc = DDI_FAILURE; + int len, val, rc = DDI_FAILURE; dev_info_t *anode = dip; /* @@ -1398,7 +1398,7 @@ pcicfg_ntbridge_child(dev_info_t *dip) static uint_t pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase, - uint64_t *boundlen, uint_t space_type) + uint64_t *boundlen, uint_t space_type) { int length, found = DDI_FAILURE, acount, i, ibridge; pci_regspec_t *assigned; @@ -1584,6 +1584,7 @@ static int pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags, boolean_t is_pcie) { ddi_acc_handle_t handle; + int ret; /* * Free up resources associated with 'dip' @@ -1596,10 +1597,20 @@ pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags, boolean_t is_pcie) /* * disable the device */ - if (pcicfg_config_setup(dip, &handle) != PCICFG_SUCCESS) + + ret = pcicfg_config_setup(dip, &handle); + if (ret == PCICFG_SUCCESS) { + pcicfg_device_off(handle); + pcicfg_config_teardown(&handle); + } else if (ret != PCICFG_NODEVICE) { + /* + * It is possible the device no longer exists -- for instance, + * if the device has been pulled from a hotpluggable slot on the + * system. In this case, do not fail the teardown, though there + * is less to clean up. + */ return (PCICFG_FAILURE); - pcicfg_device_off(handle); - pcicfg_config_teardown(&handle); + } if (is_pcie) { /* @@ -2401,8 +2412,7 @@ pcicfg_get_mem(pcicfg_phdl_t *entry, uint32_t length, uint64_t *ans) } static void -pcicfg_get_io(pcicfg_phdl_t *entry, - uint32_t length, uint32_t *ans) +pcicfg_get_io(pcicfg_phdl_t *entry, uint32_t length, uint32_t *ans) { uint32_t new_io; uint64_t io_last; @@ -3189,7 +3199,7 @@ pcicfg_device_off(ddi_acc_handle_t config_handle) */ static int pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle, - uint8_t pcie_dev) + uint8_t pcie_dev) { int ret; uint16_t cap_id_loc, val; @@ -3361,7 +3371,7 @@ pcicfg_set_busnode_props(dev_info_t *dip, uint8_t pcie_device_type) static int pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle, - uint8_t pcie_dev) + uint8_t pcie_dev) { int ret; @@ -3521,8 +3531,8 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle, * Program the bus numbers into the bridge */ static void -pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle, -uint_t primary, uint_t secondary, uint_t subordinate) +pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle, uint_t primary, + uint_t secondary, uint_t subordinate) { DEBUG3("Setting bridge bus-range %d,%d,%d\n", primary, secondary, subordinate); @@ -3547,8 +3557,7 @@ uint_t primary, uint_t secondary, uint_t subordinate) * Put bridge registers into initial state */ static void -pcicfg_setup_bridge(pcicfg_phdl_t *entry, - ddi_acc_handle_t handle) +pcicfg_setup_bridge(pcicfg_phdl_t *entry, ddi_acc_handle_t handle) { /* * The highest bus seen during probing is the max-subordinate bus @@ -3607,8 +3616,7 @@ pcicfg_setup_bridge(pcicfg_phdl_t *entry, } static void -pcicfg_update_bridge(pcicfg_phdl_t *entry, - ddi_acc_handle_t handle) +pcicfg_update_bridge(pcicfg_phdl_t *entry, ddi_acc_handle_t handle) { uint_t length; @@ -3853,11 +3861,10 @@ failedconfig: * Sizing the BARs and update "reg" property */ static int -pcicfg_populate_reg_props(dev_info_t *new_child, - ddi_acc_handle_t config_handle) +pcicfg_populate_reg_props(dev_info_t *new_child, ddi_acc_handle_t config_handle) { int i; - uint32_t request; + uint32_t request; i = PCI_CONF_BASE0; @@ -5079,7 +5086,7 @@ pcicfg_config_teardown(ddi_acc_handle_t *handle) static int pcicfg_add_config_reg(dev_info_t *dip, - uint_t bus, uint_t device, uint_t func) + uint_t bus, uint_t device, uint_t func) { int reg[10] = { PCI_ADDR_CONFIG, 0, 0, 0, 0}; @@ -5104,8 +5111,8 @@ pcicfg_ari_configure(dev_info_t *dip) #ifdef DEBUG static void -debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, - uintptr_t a4, uintptr_t a5) +debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, + uintptr_t a5) { if (pcicfg_debug > 1) { prom_printf("pcicfg: "); -- cgit v1.2.3 From b4100263209f454c9f030b30aec0d337c7614e0e Mon Sep 17 00:00:00 2001 From: Andy Fiddaman Date: Fri, 2 Apr 2021 18:39:14 +0000 Subject: 13692 bhyve panic if vmm_drv_purge() fails Reviewed by: Mike Zeller Reviewed by: Patrick Mooney Approved by: Robert Mustacchi --- usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 557d32b764..ebec9bef99 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c @@ -1869,14 +1869,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); -- cgit v1.2.3 From 80d1a7bde98a8ab2881940a6fe6775073564f253 Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Thu, 10 Dec 2020 16:04:04 +1000 Subject: 13359 mlxcx_update_link_state can race against mlxcx_register_mac 13370 mlxcx_intr_n doing redundant check on mleqe_event_type Reviewed by: Robert Mustacchi Reviewed by: Andy Fiddaman Reviewed by: Paul Winder Approved by: Dan McDonald --- usr/src/uts/common/io/mlxcx/mlxcx.c | 29 +++++++++++++-- usr/src/uts/common/io/mlxcx/mlxcx.h | 3 +- usr/src/uts/common/io/mlxcx/mlxcx_gld.c | 6 +++- usr/src/uts/common/io/mlxcx/mlxcx_intr.c | 60 +++++++++++++++++++++++++------- 4 files changed, 81 insertions(+), 17 deletions(-) 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; -- cgit v1.2.3 From 7687d0d8812e33aceb40697eb2a8b408c1fe7b52 Mon Sep 17 00:00:00 2001 From: Robert Mustacchi Date: Mon, 18 Jan 2021 19:20:51 -0800 Subject: 13687 want tool for PCIe device, config space display Reviewed by: Andy Fiddaman Reviewed by: Ryan Zezeski Approved by: Dan McDonald --- exception_lists/copyright | 2 + usr/src/cmd/Makefile | 1 + usr/src/cmd/pcieadm/Makefile | 45 + usr/src/cmd/pcieadm/pcieadm.c | 624 +++ usr/src/cmd/pcieadm/pcieadm.h | 112 + usr/src/cmd/pcieadm/pcieadm_cfgspace.c | 5046 ++++++++++++++++++++ usr/src/cmd/pcieadm/pcieadm_devs.c | 568 +++ usr/src/pkg/manifests/diagnostic-pci.mf | 1 + usr/src/pkg/manifests/system-test-utiltest.mf | 17 + usr/src/test/util-tests/runfiles/default.run | 1 + usr/src/test/util-tests/tests/Makefile | 2 +- usr/src/test/util-tests/tests/pcieadm/Makefile | 59 + .../util-tests/tests/pcieadm/bridge-efilt-p.out | 2 + .../test/util-tests/tests/pcieadm/bridge-efilt.out | 10 + .../test/util-tests/tests/pcieadm/bridge-ht-p.out | 1 + .../util-tests/tests/pcieadm/bridge-ht.msi-p.out | 1 + .../tests/pcieadm/bridge-ht.msi.command-p.out | 1 + .../test/util-tests/tests/pcieadm/bridge-ht.out | 4 + usr/src/test/util-tests/tests/pcieadm/bridge.pci | Bin 0 -> 4096 bytes .../util-tests/tests/pcieadm/header0-basic-L.out | 3 + .../util-tests/tests/pcieadm/header0-basic-LH.out | 2 + .../util-tests/tests/pcieadm/header0-basic-n.out | 3 + .../util-tests/tests/pcieadm/header0-basic.out | 3 + .../util-tests/tests/pcieadm/header0-parse.out | 2 + .../test/util-tests/tests/pcieadm/igb-ltr-p.out | 1 + usr/src/test/util-tests/tests/pcieadm/igb-ltr.out | 11 + usr/src/test/util-tests/tests/pcieadm/igb.pci | Bin 0 -> 4096 bytes .../test/util-tests/tests/pcieadm/pcieadmtest.ksh | 160 + usr/src/uts/common/io/pciex/pcie.c | 34 +- usr/src/uts/common/sys/pci.h | 5 + usr/src/uts/common/sys/pcie.h | 9 +- 31 files changed, 6723 insertions(+), 7 deletions(-) create mode 100644 usr/src/cmd/pcieadm/Makefile create mode 100644 usr/src/cmd/pcieadm/pcieadm.c create mode 100644 usr/src/cmd/pcieadm/pcieadm.h create mode 100644 usr/src/cmd/pcieadm/pcieadm_cfgspace.c create mode 100644 usr/src/cmd/pcieadm/pcieadm_devs.c create mode 100644 usr/src/test/util-tests/tests/pcieadm/Makefile create mode 100644 usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/bridge-ht.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/bridge.pci create mode 100644 usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/header0-basic.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/header0-parse.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/igb-ltr.out create mode 100644 usr/src/test/util-tests/tests/pcieadm/igb.pci create mode 100644 usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh diff --git a/exception_lists/copyright b/exception_lists/copyright index 90d3d5d59d..0395a6f257 100644 --- a/exception_lists/copyright +++ b/exception_lists/copyright @@ -392,6 +392,8 @@ usr/src/test/util-tests/tests/grep_xpg4/files/test* usr/src/test/util-tests/tests/head/*.in usr/src/test/util-tests/tests/head/*.out usr/src/test/util-tests/tests/libsff/*.out +usr/src/test/util-tests/tests/pcieadm/*.out +usr/src/test/util-tests/tests/pcieadm/*.pci usr/src/test/zfs-tests/tests/functional/history/*Z usr/src/test/zfs-tests/tests/functional/history/*txt usr/src/tools/btxld/btx.h diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 3bc0bf2af4..22afdf737a 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/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ...\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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 +#include +#include +#include +#include +#include + +#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 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/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 0e68abbe97..44cd0ece10 100644 --- a/usr/src/pkg/manifests/system-test-utiltest.mf +++ b/usr/src/pkg/manifests/system-test-utiltest.mf @@ -68,6 +68,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 @@ -1675,7 +1676,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 578fa8b6c7..248f5b5b82 100644 --- a/usr/src/test/util-tests/runfiles/default.run +++ b/usr/src/test/util-tests/runfiles/default.run @@ -83,3 +83,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 41d692ff1b..2054eb5597 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 += libcustr find mdb sed head pcidb +SUBDIRS += 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 new file mode 100644 index 0000000000..f85ffa4f6e Binary files /dev/null and b/usr/src/test/util-tests/tests/pcieadm/bridge.pci differ 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 new file mode 100644 index 0000000000..bcee63f32d Binary files /dev/null and b/usr/src/test/util-tests/tests/pcieadm/igb.pci differ 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/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 @@ -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/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 */ -- cgit v1.2.3 From 307c10bceae0de25637bbcc688c2afb73dde01ff Mon Sep 17 00:00:00 2001 From: Claes Nästén Date: Tue, 6 Apr 2021 22:11:21 +0200 Subject: OS-8280 Add /proc/sys/kernel/random/uuid to LX brands Reviewed-by: Mike Zeller Approved-by: Dan McDonald --- usr/src/uts/common/brand/lx/procfs/lx_proc.h | 1 + usr/src/uts/common/brand/lx/procfs/lx_prvnops.c | 74 +++++++++++++++---------- usr/src/uts/common/brand/lx/sys/lx_brand.h | 6 +- 3 files changed, 48 insertions(+), 33 deletions(-) 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 #include #include +#include #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 */ -- cgit v1.2.3