]> git.saurik.com Git - apple/mdnsresponder.git/blame - mDNSMacOSX/Scripts/bonjour-mcast-diagnose
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Scripts / bonjour-mcast-diagnose
CommitLineData
2682e09e
A
1#! /bin/bash
2#
f0cc3e7b 3# Copyright (c) 2017-2019 Apple Inc. All rights reserved.
2682e09e
A
4#
5# This script is currently for Apple Internal use only.
6#
7
f0cc3e7b
A
8declare -r version=1.6
9declare -r script=${BASH_SOURCE[0]}
10declare -r dnssdutil=${dnssdutil:-dnssdutil}
11
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.
15
16serviceTypesOfInterest=(
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
23)
2682e09e
A
24
25#============================================================================================================================
26# PrintUsage
27#============================================================================================================================
28
29PrintUsage()
30{
31 echo ""
32 echo "Usage: $( basename "${script}" ) [options]"
33 echo ""
34 echo "Options:"
f0cc3e7b 35 echo " -s Specifies a service type of interest, e.g., _airplay._tcp, _raop._tcp, etc. Can be used more than once."
2682e09e
A
36 echo " -V Display version of this script and exit."
37 echo ""
38}
39
40#============================================================================================================================
41# LogOut
42#============================================================================================================================
43
44LogOut()
45{
46 echo "$( date '+%Y-%m-%d %H:%M:%S%z' ): $*"
47}
48
49#============================================================================================================================
50# LogMsg
51#============================================================================================================================
52
53LogMsg()
54{
55 echo "$*"
56 if [ -d "${workPath}" ]; then
57 LogOut "$*" >> "${workPath}/log.txt"
58 fi
59}
60
61#============================================================================================================================
62# ErrQuit
63#============================================================================================================================
64
65ErrQuit()
66{
67 echo "error: $*"
68 exit 1
69}
70
71#============================================================================================================================
72# SignalHandler
73#============================================================================================================================
74
75SignalHandler()
76{
77 LogMsg "Exiting due to signal."
78 trap '' SIGINT SIGTERM
79 pkill -TERM -P $$
80 wait
81 exit 2
82}
83
84#============================================================================================================================
85# ExitHandler
86#============================================================================================================================
87
88ExitHandler()
89{
90 if [ -d "${tempPath}" ]; then
91 rm -fr "${tempPath}"
92 fi
93}
94
f0cc3e7b
A
95#============================================================================================================================
96# GetStateDump
97#============================================================================================================================
98
99GetStateDump()
100{
101 local suffix=''
102 if [ -n "${1}" ]; then
103 suffix="-${1//[^A-Za-z0-9._-]/_}"
104 fi
105 LogMsg "Getting mDNSResponder state dump."
106 dns-sd -O -stdout &> "${workPath}/state-dump${suffix}.txt"
107}
108
2682e09e
A
109#============================================================================================================================
110# RunNetStat
111#============================================================================================================================
112
113RunNetStat()
114{
115 LogMsg "Running netstat -g -n -s"
116 netstat -g -n -s &> "${workPath}/netstat-g-n-s.txt"
117}
118
119#============================================================================================================================
120# StartPacketCapture
121#============================================================================================================================
122
123StartPacketCapture()
124{
125 LogMsg "Starting tcpdump."
126 tcpdump -n -w "${workPath}/tcpdump.pcapng" &> "${workPath}/tcpdump.txt" &
127 tcpdumpPID=$!
f0cc3e7b
A
128 tcpdump -i lo0 -n -w "${workPath}/tcpdump-loopback.pcapng" &> "${workPath}/tcpdump-loopback.txt" 'udp port 5353' &
129 tcpdumpLoopbackPID=$!
2682e09e
A
130}
131
132#============================================================================================================================
133# SaveExistingPacketCaptures
134#============================================================================================================================
135
136SaveExistingPacketCaptures()
137{
138 LogMsg "Saving existing mDNS packet captures."
139 mkdir "${workPath}/pcaps"
140 for file in /tmp/mdns-tcpdump.pcapng*; do
141 [ -e "${file}" ] || continue
f0cc3e7b
A
142 baseName=$( basename "${file}" | sed -E 's/^mdns-tcpdump.pcapng([0-9]+)$/mdns-tcpdump-\1.pcapng/' )
143 gzip < "${file}" > "${workPath}/pcaps/${baseName}.gz"
2682e09e
A
144 done
145}
146
147#============================================================================================================================
148# StopPacketCapture
149#============================================================================================================================
150
151StopPacketCapture()
152{
153 LogMsg "Stopping tcpdump."
f0cc3e7b
A
154 kill -TERM "${tcpdumpPID}"
155 kill -TERM "${tcpdumpLoopbackPID}"
2682e09e
A
156}
157
158#============================================================================================================================
159# RunInterfaceMulticastTests
160#============================================================================================================================
161
162RunInterfaceMulticastTests()
163{
f0cc3e7b
A
164 local -r ifname=${1}
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[*]}" ) )
2682e09e
A
173
174 LogOut "List of services: ${serviceList[*]}" >> "${log}"
f0cc3e7b
A
175
176 # Ping IPv4 broadcast address.
177
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"
182 else
183 LogOut "No IPv4 broadcast address for ${ifname}." >> "${log}"
184 fi
185
2682e09e
A
186 # Ping All Hosts IPv4 multicast address.
187
f0cc3e7b 188 local routeOutput=$( route -n get -ifscope "${ifname}" "${allHostsV4}" 2> /dev/null )
2682e09e 189 if [ -n "${routeOutput}" ]; then
f0cc3e7b
A
190 LogOut "Pinging ${allHostsV4} on interface ${ifname}." >> "${log}"
191 ping -t 5 -b "${ifname}" "${allHostsV4}" &> "${workPath}/ping-all-hosts-${ifname}.txt"
2682e09e 192 else
f0cc3e7b 193 LogOut "No route to ${allHostsV4} on interface ${ifname}." >> "${log}"
2682e09e
A
194 fi
195
196 # Ping mDNS IPv4 multicast address.
197
f0cc3e7b 198 routeOutput=$( route -n get -ifscope "${ifname}" "${mDNSV4}" 2> /dev/null )
2682e09e 199 if [ -n "${routeOutput}" ]; then
f0cc3e7b
A
200 LogOut "Pinging ${mDNSV4} on interface ${ifname}." >> "${log}"
201 ping -t 5 -b "${ifname}" "${mDNSV4}" &> "${workPath}/ping-mDNS-${ifname}.txt"
2682e09e 202 else
f0cc3e7b 203 LogOut "No route to ${mDNSV4} on interface ${ifname}." >> "${log}"
2682e09e
A
204 fi
205
206 # Ping All Hosts IPv6 multicast address.
207
f0cc3e7b 208 routeOutput=$( route -n get -ifscope "${ifname}" -inet6 "${allHostsV6}" 2> /dev/null )
2682e09e 209 if [ -n "${routeOutput}" ]; then
f0cc3e7b
A
210 LogOut "Pinging ${allHostsV6} on interface ${ifname}." >> "${log}"
211 ping6 -c 6 -I "${ifname}" "${allHostsV6}" &> "${workPath}/ping6-all-hosts-${ifname}.txt"
2682e09e 212 else
f0cc3e7b 213 LogOut "No route to ${allHostsV6} on interface ${ifname}." >> "${log}"
2682e09e
A
214 fi
215
216 # Ping mDNS IPv6 multicast address.
217
f0cc3e7b 218 routeOutput=$( route -n get -ifscope "${ifname}" -inet6 "${mDNSV6}" 2> /dev/null )
2682e09e 219 if [ -n "${routeOutput}" ]; then
f0cc3e7b
A
220 LogOut "Pinging ${mDNSV6} on interface ${ifname}." >> "${log}"
221 ping6 -c 6 -I "${ifname}" "${mDNSV6}" &> "${workPath}/ping6-mDNS-${ifname}.txt"
2682e09e 222 else
f0cc3e7b 223 LogOut "No route to ${mDNSV6} on interface ${ifname}." >> "${log}"
2682e09e
A
224 fi
225
226 # Send mDNS queries for services.
227
228 for service in "${serviceList[@]}"; do
f0cc3e7b 229 LogOut "Sending mDNS queries for ${service} on interface ${ifname}." >> "${log}"
2682e09e
A
230 for(( i = 1; i <= 3; ++i )); do
231 printf "\n"
232 "${dnssdutil}" mdnsquery -i "${ifname}" -n "${service}" -t ptr -r 2
233 printf "\n"
234 "${dnssdutil}" mdnsquery -i "${ifname}" -n "${service}" -t ptr -r 1 --QU -p 5353
235 printf "\n"
236 done >> "${workPath}/mdnsquery-${ifname}.txt" 2>&1
237 done
238}
239
240#============================================================================================================================
241# RunMulticastTests
242#============================================================================================================================
243
244RunMulticastTests()
245{
f0cc3e7b
A
246 local -r interfaces=( $( ifconfig -l -u ) )
247 local -r skipPrefixes=( ap awdl bridge ipsec llw p2p pdp_ip pktap UDC utun )
248 local -a pids
249 local ifname
250 local skip
251 local pid
2682e09e
A
252
253 LogMsg "List of interfaces: ${interfaces[*]}"
254 for ifname in "${interfaces[@]}"; do
f0cc3e7b
A
255 skip=false
256 for prefix in "${skipPrefixes[@]}"; do
2682e09e
A
257 if [[ ${ifname} =~ ^${prefix}[0-9]*$ ]]; then
258 skip=true
259 break
260 fi
261 done
262
f0cc3e7b
A
263 if ! "${skip}"; then
264 ifconfig ${ifname} | egrep -q '\binet6?\b'
2682e09e
A
265 if [ $? -ne 0 ]; then
266 skip=true
267 fi
268 fi
269
f0cc3e7b 270 if "${skip}"; then
2682e09e
A
271 continue
272 fi
273
274 LogMsg "Starting interface multicast tests for ${ifname}."
f0cc3e7b 275 RunInterfaceMulticastTests "${ifname}" & pids+=( $! )
2682e09e
A
276 done
277
278 LogMsg "Waiting for interface multicast tests to complete..."
279 for pid in "${pids[@]}"; do
280 wait "${pid}"
281 done
282 LogMsg "All interface multicast tests completed."
283}
284
285#============================================================================================================================
286# RunBrowseTest
287#============================================================================================================================
288
289RunBrowseTest()
290{
f0cc3e7b
A
291 local -a typeArgs
292
293 if [ "${#serviceTypesOfInterest[@]}" -gt 0 ]; then
294 for serviceType in "${serviceTypesOfInterest[@]}"; do
295 typeArgs+=( "-t" "${serviceType}" )
296 done
297
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"
300 fi
301
302 LogMsg "Running general dnssdutil browseAll command."
2682e09e
A
303 "${dnssdutil}" browseAll -A -d local -b 10 -c 10 &> "${workPath}/browseAll.txt"
304}
305
306#============================================================================================================================
307# IsMacOS
308#============================================================================================================================
309
310IsMacOS()
311{
312 [[ $( sw_vers -productName ) =~ ^Mac\ OS ]]
313}
314
315#============================================================================================================================
316# ArchiveLogs
317#============================================================================================================================
318
319ArchiveLogs()
320{
f0cc3e7b
A
321 local -r workdir=$( basename "${workPath}" )
322 local parentDir
323 if IsMacOS; then
324 parentDir=/var/tmp
325 else
326 parentDir=/var/mobile/Library/Logs/CrashReporter
327 fi
328 local -r archivePath="${parentDir}/${workdir}.tar.gz"
2682e09e
A
329
330 LogMsg "Archiving logs."
331 echo "---"
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."
337 if IsMacOS; then
f0cc3e7b 338 open "${parentDir}"
2682e09e
A
339 fi
340 else
341 echo "Failed to create archive at ${archivePath}."
342 fi
343 echo "---"
344}
345
346#============================================================================================================================
347# CreateWorkDirName
348#============================================================================================================================
349
350CreateWorkDirName()
351{
f0cc3e7b
A
352 local suffix=''
353 local -r productName=$( sw_vers -productName )
2682e09e
A
354 if [ -n "${productName}" ]; then
355 suffix+="_${productName}"
356 fi
357
f0cc3e7b 358 local model
2682e09e
A
359 if IsMacOS; then
360 model=$( sysctl -n hw.model )
361 model=${model//,/-}
362 else
363 model=$( gestalt_query -undecorated DeviceName )
364 fi
365 if [ -n "${model}" ]; then
366 suffix+="_${model}"
367 fi
368
f0cc3e7b 369 local -r buildVersion=$( sw_vers -buildVersion )
2682e09e
A
370 if [ -n "${buildVersion}" ]; then
371 suffix+="_${buildVersion}"
372 fi
373
374 suffix=${suffix//[^A-Za-z0-9._-]/_}
375
376 printf "bonjour-mcast-diags_$( date '+%Y.%m.%d_%H-%M-%S%z' )${suffix}"
377}
378
379#============================================================================================================================
380# main
381#============================================================================================================================
382
383main()
384{
f0cc3e7b 385 while getopts ":s:hV" option; do
2682e09e
A
386 case "${option}" in
387 h)
388 PrintUsage
389 exit 0
390 ;;
f0cc3e7b
A
391 s)
392 serviceType=$( awk '{print tolower($0)}' <<< "${OPTARG}" )
393 if [[ ${serviceType} =~ ^_[-a-z0-9]*\._(tcp|udp)$ ]]; then
394 serviceTypesOfInterest+=( "${serviceType}" )
395 else
396 ErrQuit "Service type '${OPTARG}' is malformed."
397 fi
398 ;;
2682e09e
A
399 V)
400 echo "$( basename "${script}" ) version ${version}"
401 exit 0
402 ;;
403 :)
404 ErrQuit "option '${OPTARG}' requires an argument."
405 ;;
406 *)
407 ErrQuit "unknown option '${OPTARG}'."
408 ;;
409 esac
410 done
411
f0cc3e7b 412 [ "${OPTIND}" -gt "$#" ] || ErrQuit "unexpected argument \"${!OPTIND}\"."
2682e09e
A
413
414 if IsMacOS; then
415 if [ "${EUID}" -ne 0 ]; then
416 echo "Re-launching with sudo"
f0cc3e7b 417 exec sudo "${script}" "$@"
2682e09e 418 fi
2682e09e
A
419 else
420 [ "${EUID}" -eq 0 ] || ErrQuit "$( basename "${script}" ) needs to be run as root."
2682e09e
A
421 fi
422
423 tempPath=$( mktemp -d -q ) || ErrQuit "Failed to make temp directory."
424 workPath="${tempPath}/$( CreateWorkDirName )"
425 mkdir "${workPath}" || ErrQuit "Failed to make work directory."
426
427 trap SignalHandler SIGINT SIGTERM
428 trap ExitHandler EXIT
429
f0cc3e7b 430 LogMsg "About: $( basename "${script}" ) version ${version} ($( md5 -q "${script}" ))."
2682e09e
A
431 if [ "${dnssdutil}" != "dnssdutil" ]; then
432 if [ -x "$( which "${dnssdutil}" )" ]; then
433 LogMsg "Using $( "${dnssdutil}" -V ) at $( which "${dnssdutil}" )."
434 else
435 LogMsg "WARNING: dnssdutil (${dnssdutil}) isn't an executable."
436 fi
437 fi
438
f0cc3e7b
A
439 serviceTypesOfInterest=( $( IFS=$'\n' sort -u <<< "${serviceTypesOfInterest[*]}" ) )
440
441 GetStateDump 'before'
2682e09e
A
442 RunNetStat
443 StartPacketCapture
444 SaveExistingPacketCaptures
445 RunBrowseTest
446 RunMulticastTests
f0cc3e7b 447 GetStateDump 'after'
2682e09e
A
448 StopPacketCapture
449 ArchiveLogs
450}
451
452main "$@"