3 # Copyright (c) 2017-2019 Apple Inc. All rights reserved.
5 # This script is currently for Apple Internal use only.
9 declare -r script=${BASH_SOURCE[0]}
10 declare -r dnssdutil
=${dnssdutil:-dnssdutil}
12 # The serviceTypesOfInterest array is initialized with commonly-debugged service types or service types whose records can
13 # provide useful debugging information, e.g., _airport._tcp in case an AirPort base station is a WiFi network's access
14 # point. Note: Additional service types can be added with the '-s' option.
16 serviceTypesOfInterest
=(
17 _airplay._tcp
# AirPlay
18 _airport._tcp
# AirPort Base Station
19 _companion
-link._tcp
# Companion Link
20 _hap._tcp
# HomeKit Accessory Protocol
21 _homekit._tcp
# HomeKit
22 _raop._tcp
# Remote Audio Output Protocol
25 #============================================================================================================================
27 #============================================================================================================================
32 echo "Usage: $( basename "${script}" ) [options]"
35 echo " -s Specifies a service type of interest, e.g., _airplay._tcp, _raop._tcp, etc. Can be used more than once."
36 echo " -V Display version of this script and exit."
40 #============================================================================================================================
42 #============================================================================================================================
46 echo "$( date '+%Y-%m-%d %H:%M:%S%z' ): $*"
49 #============================================================================================================================
51 #============================================================================================================================
56 if [ -d "${workPath}" ]; then
57 LogOut
"$*" >> "${workPath}/log.txt"
61 #============================================================================================================================
63 #============================================================================================================================
71 #============================================================================================================================
73 #============================================================================================================================
77 LogMsg
"Exiting due to signal."
78 trap '' SIGINT SIGTERM
84 #============================================================================================================================
86 #============================================================================================================================
90 if [ -d "${tempPath}" ]; then
95 #============================================================================================================================
97 #============================================================================================================================
102 if [ -n "${1}" ]; then
103 suffix
="-${1//[^A-Za-z0-9._-]/_}"
105 LogMsg
"Getting mDNSResponder state dump."
106 dns
-sd -O -stdout &> "${workPath}/state-dump${suffix}.txt"
109 #============================================================================================================================
111 #============================================================================================================================
115 LogMsg
"Running netstat -g -n -s"
116 netstat
-g -n -s &> "${workPath}/netstat-g-n-s.txt"
119 #============================================================================================================================
121 #============================================================================================================================
125 LogMsg
"Starting tcpdump."
126 tcpdump
-n -w "${workPath}/tcpdump.pcapng" &> "${workPath}/tcpdump.txt" &
128 tcpdump
-i lo0
-n -w "${workPath}/tcpdump-loopback.pcapng" &> "${workPath}/tcpdump-loopback.txt" 'udp port 5353' &
129 tcpdumpLoopbackPID
=$
!
132 #============================================================================================================================
133 # SaveExistingPacketCaptures
134 #============================================================================================================================
136 SaveExistingPacketCaptures
()
138 LogMsg
"Saving existing mDNS packet captures."
139 mkdir "${workPath}/pcaps"
140 for file in /tmp
/mdns
-tcpdump.pcapng
*; do
141 [ -e "${file}" ] || continue
142 baseName
=$( basename "${file}" | sed -E 's/^mdns-tcpdump.pcapng([0-9]+)$
/mdns
-tcpdump-\
1.pcapng
/' )
143 gzip < "${file}" > "${workPath}/pcaps/${baseName}.gz"
147 #============================================================================================================================
149 #============================================================================================================================
153 LogMsg "Stopping tcpdump."
154 kill -TERM "${tcpdumpPID}"
155 kill -TERM "${tcpdumpLoopbackPID}"
158 #============================================================================================================================
159 # RunInterfaceMulticastTests
160 #============================================================================================================================
162 RunInterfaceMulticastTests()
165 local -r allHostsV4=224.0.0.1
166 local -r allHostsV6=ff02::1
167 local -r mDNSV4=224.0.0.251
168 local -r mDNSV6=ff02::fb
169 local -r log="${workPath}/mcast-test-log-${ifname}.txt"
170 local serviceList=( $( "${dnssdutil}" queryrecord -i "${ifname}" -A -t ptr -n _services._dns-sd._udp.local -l 6 | sed -E -n 's/.*(_.*_(tcp|udp)\.local\.)$/\1/p' ) )
171 serviceList
+=( "${serviceTypesOfInterest[@]/%/.local.}" )
172 serviceList
=( $( IFS=$'\n' sort -f -u <<< "${serviceList[*]}" ) )
174 LogOut
"List of services: ${serviceList[*]}" >> "${log}"
176 # Ping IPv4 broadcast address.
178 local broadcastAddr
=$( ifconfig "${ifname}" inet | awk '$5 == "broadcast" {print $6}' )
179 if [ -n "${broadcastAddr}" ]; then
180 LogOut
"Pinging ${broadcastAddr} on interface ${ifname}." >> "${log}"
181 ping -t 5 -b "${ifname}" "${broadcastAddr}" &> "${workPath}/ping-broadcast-${ifname}.txt"
183 LogOut
"No IPv4 broadcast address for ${ifname}." >> "${log}"
186 # Ping All Hosts IPv4 multicast address.
188 local routeOutput
=$( route -n get -ifscope "${ifname}" "${allHostsV4}" 2> /dev/null )
189 if [ -n "${routeOutput}" ]; then
190 LogOut
"Pinging ${allHostsV4} on interface ${ifname}." >> "${log}"
191 ping -t 5 -b "${ifname}" "${allHostsV4}" &> "${workPath}/ping-all-hosts-${ifname}.txt"
193 LogOut
"No route to ${allHostsV4} on interface ${ifname}." >> "${log}"
196 # Ping mDNS IPv4 multicast address.
198 routeOutput
=$( route -n get -ifscope "${ifname}" "${mDNSV4}" 2> /dev/null )
199 if [ -n "${routeOutput}" ]; then
200 LogOut
"Pinging ${mDNSV4} on interface ${ifname}." >> "${log}"
201 ping -t 5 -b "${ifname}" "${mDNSV4}" &> "${workPath}/ping-mDNS-${ifname}.txt"
203 LogOut
"No route to ${mDNSV4} on interface ${ifname}." >> "${log}"
206 # Ping All Hosts IPv6 multicast address.
208 routeOutput
=$( route -n get -ifscope "${ifname}" -inet6 "${allHostsV6}" 2> /dev/null )
209 if [ -n "${routeOutput}" ]; then
210 LogOut
"Pinging ${allHostsV6} on interface ${ifname}." >> "${log}"
211 ping6
-c 6 -I "${ifname}" "${allHostsV6}" &> "${workPath}/ping6-all-hosts-${ifname}.txt"
213 LogOut
"No route to ${allHostsV6} on interface ${ifname}." >> "${log}"
216 # Ping mDNS IPv6 multicast address.
218 routeOutput
=$( route -n get -ifscope "${ifname}" -inet6 "${mDNSV6}" 2> /dev/null )
219 if [ -n "${routeOutput}" ]; then
220 LogOut
"Pinging ${mDNSV6} on interface ${ifname}." >> "${log}"
221 ping6
-c 6 -I "${ifname}" "${mDNSV6}" &> "${workPath}/ping6-mDNS-${ifname}.txt"
223 LogOut
"No route to ${mDNSV6} on interface ${ifname}." >> "${log}"
226 # Send mDNS queries for services.
228 for service
in "${serviceList[@]}"; do
229 LogOut
"Sending mDNS queries for ${service} on interface ${ifname}." >> "${log}"
230 for(( i
= 1; i
<= 3; ++i
)); do
232 "${dnssdutil}" mdnsquery
-i "${ifname}" -n "${service}" -t ptr
-r 2
234 "${dnssdutil}" mdnsquery
-i "${ifname}" -n "${service}" -t ptr
-r 1 --QU -p 5353
236 done >> "${workPath}/mdnsquery-${ifname}.txt" 2>&1
240 #============================================================================================================================
242 #============================================================================================================================
246 local -r interfaces
=( $( ifconfig -l -u ) )
247 local -r skipPrefixes
=( ap awdl bridge ipsec llw p2p pdp_ip pktap UDC utun
)
253 LogMsg
"List of interfaces: ${interfaces[*]}"
254 for ifname
in "${interfaces[@]}"; do
256 for prefix
in "${skipPrefixes[@]}"; do
257 if [[ ${ifname} =~ ^
${prefix}[0-9]*$
]]; then
264 ifconfig
${ifname} | egrep -q '\binet6?\b'
265 if [ $?
-ne 0 ]; then
274 LogMsg
"Starting interface multicast tests for ${ifname}."
275 RunInterfaceMulticastTests
"${ifname}" & pids
+=( $
! )
278 LogMsg
"Waiting for interface multicast tests to complete..."
279 for pid
in "${pids[@]}"; do
282 LogMsg
"All interface multicast tests completed."
285 #============================================================================================================================
287 #============================================================================================================================
293 if [ "${#serviceTypesOfInterest[@]}" -gt 0 ]; then
294 for serviceType
in "${serviceTypesOfInterest[@]}"; do
295 typeArgs
+=( "-t" "${serviceType}" )
298 LogMsg
"Running dnssdutil browseAll command for service types of interest."
299 "${dnssdutil}" browseAll
-A -d local -b 10 -c 10 "${typeArgs[@]}" &> "${workPath}/browseAll-STOI.txt"
302 LogMsg
"Running general dnssdutil browseAll command."
303 "${dnssdutil}" browseAll
-A -d local -b 10 -c 10 &> "${workPath}/browseAll.txt"
306 #============================================================================================================================
308 #============================================================================================================================
312 [[ $( sw_vers -productName ) =~ ^Mac\ OS
]]
315 #============================================================================================================================
317 #============================================================================================================================
321 local -r workdir
=$( basename "${workPath}" )
326 parentDir
=/var
/mobile
/Library
/Logs
/CrashReporter
328 local -r archivePath
="${parentDir}/${workdir}.tar.gz"
330 LogMsg
"Archiving logs."
332 tar -C "${tempPath}" -czf "${archivePath}" "${workdir}"
333 if [ -e "${archivePath}" ]; then
334 echo "Created log archive at ${archivePath}"
335 echo "*** Please run sysdiagnose NOW. ***"
336 echo "Attach both the log archive and the sysdiagnose archive to the radar."
341 echo "Failed to create archive at ${archivePath}."
346 #============================================================================================================================
348 #============================================================================================================================
353 local -r productName
=$( sw_vers -productName )
354 if [ -n "${productName}" ]; then
355 suffix
+="_${productName}"
360 model
=$( sysctl -n hw.model )
363 model
=$( gestalt_query -undecorated DeviceName )
365 if [ -n "${model}" ]; then
369 local -r buildVersion
=$( sw_vers -buildVersion )
370 if [ -n "${buildVersion}" ]; then
371 suffix
+="_${buildVersion}"
374 suffix
=${suffix//[^A-Za-z0-9._-]/_}
376 printf "bonjour-mcast-diags_$( date '+%Y.%m.%d_%H-%M-%S%z' )${suffix}"
379 #============================================================================================================================
381 #============================================================================================================================
385 while getopts ":s:hV" option
; do
392 serviceType
=$( awk '{print tolower($0)}' <<< "${OPTARG}" )
393 if [[ ${serviceType} =~ ^_[-a-z0-9]*\._(tcp|udp)$ ]]; then
394 serviceTypesOfInterest+=( "${serviceType}" )
396 ErrQuit "Service type '${OPTARG}' is malformed."
400 echo "$( basename "${script}" ) version ${version}"
404 ErrQuit "option '${OPTARG}' requires an argument."
407 ErrQuit "unknown option '${OPTARG}'."
412 [ "${OPTIND}" -gt "$#" ] || ErrQuit "unexpected argument \"${!OPTIND}\"."
415 if [ "${EUID}" -ne 0 ]; then
416 echo "Re-launching with sudo"
417 exec sudo "${script}" "$@"
420 [ "${EUID}" -eq 0 ] || ErrQuit "$( basename "${script}" ) needs to be run as root."
423 tempPath=$( mktemp -d -q ) || ErrQuit "Failed to make temp directory."
424 workPath="${tempPath}/$( CreateWorkDirName )"
425 mkdir "${workPath}" || ErrQuit "Failed to make work directory."
427 trap SignalHandler SIGINT SIGTERM
428 trap ExitHandler EXIT
430 LogMsg "About: $( basename "${script}" ) version ${version} ($( md5 -q "${script}" ))."
431 if [ "${dnssdutil}" != "dnssdutil" ]; then
432 if [ -x "$( which "${dnssdutil}" )" ]; then
433 LogMsg "Using $( "${dnssdutil}" -V ) at $( which "${dnssdutil}" )."
435 LogMsg "WARNING: dnssdutil (${dnssdutil}) isn't an executable.
"
439 serviceTypesOfInterest=( $( IFS=$'\n' sort -u <<< "${serviceTypesOfInterest[*]}" ) )
441 GetStateDump 'before'
444 SaveExistingPacketCaptures