]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/Scripts/bonjour-mcast-diagnose
mDNSResponder-878.200.35.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Scripts / bonjour-mcast-diagnose
1 #! /bin/bash
2 #
3 # Copyright (c) 2017-2018 Apple Inc. All rights reserved.
4 #
5 # This script is currently for Apple Internal use only.
6 #
7
8 version=1.4
9 script=${BASH_SOURCE[0]}
10 dnssdutil=${dnssdutil:-dnssdutil}
11
12 #============================================================================================================================
13 # PrintUsage
14 #============================================================================================================================
15
16 PrintUsage()
17 {
18 echo ""
19 echo "Usage: $( basename "${script}" ) [options]"
20 echo ""
21 echo "Options:"
22 echo " -V Display version of this script and exit."
23 echo ""
24 }
25
26 #============================================================================================================================
27 # LogOut
28 #============================================================================================================================
29
30 LogOut()
31 {
32 echo "$( date '+%Y-%m-%d %H:%M:%S%z' ): $*"
33 }
34
35 #============================================================================================================================
36 # LogMsg
37 #============================================================================================================================
38
39 LogMsg()
40 {
41 echo "$*"
42 if [ -d "${workPath}" ]; then
43 LogOut "$*" >> "${workPath}/log.txt"
44 fi
45 }
46
47 #============================================================================================================================
48 # ErrQuit
49 #============================================================================================================================
50
51 ErrQuit()
52 {
53 echo "error: $*"
54 exit 1
55 }
56
57 #============================================================================================================================
58 # SignalHandler
59 #============================================================================================================================
60
61 SignalHandler()
62 {
63 LogMsg "Exiting due to signal."
64 trap '' SIGINT SIGTERM
65 pkill -TERM -P $$
66 wait
67 exit 2
68 }
69
70 #============================================================================================================================
71 # ExitHandler
72 #============================================================================================================================
73
74 ExitHandler()
75 {
76 if [ -d "${tempPath}" ]; then
77 rm -fr "${tempPath}"
78 fi
79 }
80
81 #============================================================================================================================
82 # RunNetStat
83 #============================================================================================================================
84
85 RunNetStat()
86 {
87 LogMsg "Running netstat -g -n -s"
88 netstat -g -n -s &> "${workPath}/netstat-g-n-s.txt"
89 }
90
91 #============================================================================================================================
92 # StartPacketCapture
93 #============================================================================================================================
94
95 StartPacketCapture()
96 {
97 LogMsg "Starting tcpdump."
98 tcpdump -n -w "${workPath}/tcpdump.pcapng" &> "${workPath}/tcpdump.txt" &
99 tcpdumpPID=$!
100 }
101
102 #============================================================================================================================
103 # SaveExistingPacketCaptures
104 #============================================================================================================================
105
106 SaveExistingPacketCaptures()
107 {
108 LogMsg "Saving existing mDNS packet captures."
109 mkdir "${workPath}/pcaps"
110 for file in /tmp/mdns-tcpdump.pcapng*; do
111 [ -e "${file}" ] || continue
112 baseName=$( sed -E 's/^mdns-tcpdump.pcapng([0-9]+)$/mdns-tcpdump-\1.pcapng/' <<< "$( basename ${file} )" )
113 gzip < ${file} > "${workPath}/pcaps/${baseName}.gz"
114 done
115 }
116
117 #============================================================================================================================
118 # StopPacketCapture
119 #============================================================================================================================
120
121 StopPacketCapture()
122 {
123 LogMsg "Stopping tcpdump."
124 kill -TERM ${tcpdumpPID}
125 }
126
127 #============================================================================================================================
128 # RunInterfaceMulticastTests
129 #============================================================================================================================
130
131 RunInterfaceMulticastTests()
132 {
133 local ifname="$1"
134 local allHostsV4=224.0.0.1
135 local allHostsV6=ff02::1
136 local mDNSV4=224.0.0.251
137 local mDNSV6=ff02::fb
138 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' | sort -u ) )
139 local log="${workPath}/mcast-test-log-${ifname}.txt"
140
141 LogOut "List of services: ${serviceList[*]}" >> "${log}"
142 # Ping All Hosts IPv4 multicast address.
143
144 local routeOutput=$( route -n get -ifscope ${ifname} "${allHostsV4}" 2> /dev/null )
145 if [ -n "${routeOutput}" ]; then
146 LogOut "Pinging "${allHostsV4}" on interface ${ifname}." >> "${log}"
147 ping -t 5 -b ${ifname} "${allHostsV4}" &> "${workPath}/ping-all-hosts-${ifname}.txt"
148 else
149 LogOut "No route to "${allHostsV4}" on interface ${ifname}." >> "${log}"
150 fi
151
152 # Ping mDNS IPv4 multicast address.
153
154 routeOutput=$( route -n get -ifscope ${ifname} "${mDNSV4}" 2> /dev/null )
155 if [ -n "${routeOutput}" ]; then
156 LogOut "Pinging "${mDNSV4}" on interface ${ifname}." >> "${log}"
157 ping -t 5 -b ${ifname} "${mDNSV4}" &> "${workPath}/ping-mDNS-${ifname}.txt"
158 else
159 LogOut "No route to "${mDNSV4}" on interface ${ifname}." >> "${log}"
160 fi
161
162 # Ping All Hosts IPv6 multicast address.
163
164 routeOutput=$( route -n get -ifscope ${ifname} -inet6 "${allHostsV6}" 2> /dev/null )
165 if [ -n "${routeOutput}" ]; then
166 LogOut "Pinging "${allHostsV6}" on interface ${ifname}." >> "${log}"
167 ping6 -c 6 -I ${ifname} "${allHostsV6}" &> "${workPath}/ping6-all-hosts-${ifname}.txt"
168 else
169 LogOut "No route to "${allHostsV6}" on interface ${ifname}." >> "${log}"
170 fi
171
172 # Ping mDNS IPv6 multicast address.
173
174 routeOutput=$( route -n get -ifscope ${ifname} -inet6 "${mDNSV6}" 2> /dev/null )
175 if [ -n "${routeOutput}" ]; then
176 LogOut "Pinging "${mDNSV6}" on interface ${ifname}." >> "${log}"
177 ping6 -c 6 -I ${ifname} "${mDNSV6}" &> "${workPath}/ping6-mDNS-${ifname}.txt"
178 else
179 LogOut "No route to "${mDNSV6}" on interface ${ifname}." >> "${log}"
180 fi
181
182 # Send mDNS queries for services.
183
184 for service in "${serviceList[@]}"; do
185 LogOut "Sending mDNS queries for "${service}" on interface ${ifname}." >> "${log}"
186 for(( i = 1; i <= 3; ++i )); do
187 printf "\n"
188 "${dnssdutil}" mdnsquery -i "${ifname}" -n "${service}" -t ptr -r 2
189 printf "\n"
190 "${dnssdutil}" mdnsquery -i "${ifname}" -n "${service}" -t ptr -r 1 --QU -p 5353
191 printf "\n"
192 done >> "${workPath}/mdnsquery-${ifname}.txt" 2>&1
193 done
194 }
195
196 #============================================================================================================================
197 # RunMulticastTests
198 #============================================================================================================================
199
200 RunMulticastTests()
201 {
202 local interfaces=( $( ifconfig -l -u ) )
203 local skipPrefixes=( ap awdl bridge ipsec lo p2p pdp_ip pktap UDC utun )
204 local ifname=""
205 local pid=""
206 local pids=()
207
208 LogMsg "List of interfaces: ${interfaces[*]}"
209 for ifname in "${interfaces[@]}"; do
210 local skip=false
211 for prefix in ${skipPrefixes[@]}; do
212 if [[ ${ifname} =~ ^${prefix}[0-9]*$ ]]; then
213 skip=true
214 break
215 fi
216 done
217
218 if [ "${skip}" != "true" ]; then
219 ifconfig ${ifname} | grep -q inet
220 if [ $? -ne 0 ]; then
221 skip=true
222 fi
223 fi
224
225 if [ "${skip}" == "true" ]; then
226 continue
227 fi
228
229 LogMsg "Starting interface multicast tests for ${ifname}."
230 RunInterfaceMulticastTests "${ifname}" & pids+=($!)
231 done
232
233 LogMsg "Waiting for interface multicast tests to complete..."
234 for pid in "${pids[@]}"; do
235 wait "${pid}"
236 done
237 LogMsg "All interface multicast tests completed."
238 }
239
240 #============================================================================================================================
241 # RunBrowseTest
242 #============================================================================================================================
243
244 RunBrowseTest()
245 {
246 LogMsg "Running dnssdutil browseAll command."
247 "${dnssdutil}" browseAll -A -d local -b 10 -c 10 &> "${workPath}/browseAll.txt"
248 }
249
250 #============================================================================================================================
251 # IsMacOS
252 #============================================================================================================================
253
254 IsMacOS()
255 {
256 [[ $( sw_vers -productName ) =~ ^Mac\ OS ]]
257 }
258
259 #============================================================================================================================
260 # ArchiveLogs
261 #============================================================================================================================
262
263 ArchiveLogs()
264 {
265 local workdir=$( basename "${workPath}" )
266 local archivePath="${dstPath}/${workdir}.tar.gz"
267
268 LogMsg "Archiving logs."
269 echo "---"
270 tar -C "${tempPath}" -czf "${archivePath}" "${workdir}"
271 if [ -e "${archivePath}" ]; then
272 echo "Created log archive at ${archivePath}"
273 echo "*** Please run sysdiagnose NOW. ***"
274 echo "Attach both the log archive and the sysdiagnose archive to the radar."
275 if IsMacOS; then
276 open "${dstPath}"
277 fi
278 else
279 echo "Failed to create archive at ${archivePath}."
280 fi
281 echo "---"
282 }
283
284 #============================================================================================================================
285 # CreateWorkDirName
286 #============================================================================================================================
287
288 CreateWorkDirName()
289 {
290 local suffix=""
291 local productName=$( sw_vers -productName )
292 if [ -n "${productName}" ]; then
293 suffix+="_${productName}"
294 fi
295
296 local model=""
297 if IsMacOS; then
298 model=$( sysctl -n hw.model )
299 model=${model//,/-}
300 else
301 model=$( gestalt_query -undecorated DeviceName )
302 fi
303 if [ -n "${model}" ]; then
304 suffix+="_${model}"
305 fi
306
307 local buildVersion=$( sw_vers -buildVersion )
308 if [ -n "${buildVersion}" ]; then
309 suffix+="_${buildVersion}"
310 fi
311
312 suffix=${suffix//[^A-Za-z0-9._-]/_}
313
314 printf "bonjour-mcast-diags_$( date '+%Y.%m.%d_%H-%M-%S%z' )${suffix}"
315 }
316
317 #============================================================================================================================
318 # main
319 #============================================================================================================================
320
321 main()
322 {
323 while getopts ":hV" option; do
324 case "${option}" in
325 h)
326 PrintUsage
327 exit 0
328 ;;
329 V)
330 echo "$( basename "${script}" ) version ${version}"
331 exit 0
332 ;;
333 :)
334 ErrQuit "option '${OPTARG}' requires an argument."
335 ;;
336 *)
337 ErrQuit "unknown option '${OPTARG}'."
338 ;;
339 esac
340 done
341
342 [ "${OPTIND}" -gt "$#" ] || ErrQuit "unexpected argument \""${!OPTIND}"\"."
343
344 if IsMacOS; then
345 if [ "${EUID}" -ne 0 ]; then
346 echo "Re-launching with sudo"
347 exec sudo ${script}
348 fi
349 dstPath=/var/tmp
350 else
351 [ "${EUID}" -eq 0 ] || ErrQuit "$( basename "${script}" ) needs to be run as root."
352 dstPath=/var/mobile/Library/Logs/CrashReporter
353 fi
354
355 tempPath=$( mktemp -d -q ) || ErrQuit "Failed to make temp directory."
356 workPath="${tempPath}/$( CreateWorkDirName )"
357 mkdir "${workPath}" || ErrQuit "Failed to make work directory."
358
359 trap SignalHandler SIGINT SIGTERM
360 trap ExitHandler EXIT
361
362 LogMsg "About: $( basename "${script}" ) version ${version} ($( md5 -q ${script} ))."
363 if [ "${dnssdutil}" != "dnssdutil" ]; then
364 if [ -x "$( which "${dnssdutil}" )" ]; then
365 LogMsg "Using $( "${dnssdutil}" -V ) at $( which "${dnssdutil}" )."
366 else
367 LogMsg "WARNING: dnssdutil (${dnssdutil}) isn't an executable."
368 fi
369 fi
370
371 RunNetStat
372 StartPacketCapture
373 SaveExistingPacketCaptures
374 RunBrowseTest
375 RunMulticastTests
376 StopPacketCapture
377 ArchiveLogs
378 }
379
380 main "$@"