タイムサーバーの生存調査スクリプト(time.apple.com の一部がダウン)

macOS の時刻が狂う原因を調べていたら、time.apple.com の一部がダウン相当だとが分かった。(Apple には連絡済みですが改善の兆しはありません)

IPv6 通信が有効な場合、時刻を拾えない場合があります。(恐らく日本地域のみ)

iPhone / iPad では通信がWi-Fiのみ環境だと time-ios.apple.com となるようですが time.apple.com と同じサーバーのようです。

macOS はタイムサーバーの設定がありますが、iPhone / iPad では IPv6 を無効にする(DNS設定を手動でIPv6サーバーを排除)しか手段がなさそうです。


### タイムサーバーの反応の有無を簡易的に調べるスクリプト

macOS の sntp コマンドは約1秒でタイムアウトになるようなので、既定のタイムアウトを1秒としています。

```python:check_time_server.py
#!/usr/bin/env python3

import argparse
import socket
import sys

CHECK = {None: '--', False: 'NG', True: 'OK'}
PORT = 123

flag_dryrun = False
flag_verbose = False
timeout_second = 1
message = print


def verbose(msg):
    return flag_verbose and message(msg)


def try_sntp(addr):
    pkt = bytearray(4*12)
    pkt[0] = (3 << 3) | 3

    ipaddr = addr[4][0]
    sock = socket.socket(family=addr[0], type=addr[1], proto=socket.IPPROTO_UDP)
    if timeout_second > 0:
        sock.settimeout(timeout_second)
    try:
        sres = sock.sendto(pkt, (ipaddr, PORT))
        rres = sock.recvfrom(64)
    except OSError as e:
        message(f'Error: {e} ({ipaddr})')
        sock.close()
        return False
    sock.close()
    return True


def check_server(hostname):
    message(f'Server: {hostname}')
    addrs = socket.getaddrinfo(hostname, PORT, proto=socket.IPPROTO_UDP)
    verbose('Address:')
    hostmap = [{}, {}]
    for addr in addrs:
        ipaddr = addr[4][0]
        try:
            sname = socket.gethostbyaddr(ipaddr)[0]
        except OSError as e:
            verbose(f'Error: gethostbyaddr("{ipaddr}"): {e}')
            sname = hostname
        verbose(f'  {ipaddr:39} : {sname}')
        success = None
        if not flag_dryrun:
            success = try_sntp(addr)
        hmap = hostmap[int(addr[0] == socket.AF_INET6)]
        if sname not in hmap:
            hmap[sname] = []
        hmap[sname].append([ipaddr, success])
    for host in sorted(set(sum((list(h.keys()) for h in hostmap), []))):
        message('\n'.join([
            f'{host}:',
            *[f'  {CHECK[check]} - {addr}' for hmap in hostmap
              for addr, check in sorted(hmap.get(host, []))]]))


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbose', action='store_true')
    parser.add_argument('-d', '--dryrun', action='store_true')
    parser.add_argument('-t', '--timeout', type=int, default=1)
    parser.add_argument('server', metavar='SERVER')
    args = parser.parse_args()
    flag_dryrun = args.dryrun
    flag_verbose = args.verbose
    timeout_second = args.timeout
    check_server(args.server)
```

```console:実行結果
# NICT(ntp.nict.jp) の場合
$ python3 check_time_server.py ntp.nict.jp
Server: ntp.nict.jp
ntp-a2.nict.go.jp:
  OK - 133.243.238.243
  OK - 2001:df0:232:eea0::fff3
ntp-a3.nict.go.jp:
  OK - 133.243.238.244
  OK - 2001:df0:232:eea0::fff4
ntp-b2.nict.go.jp:
  OK - 133.243.238.163
ntp-b3.nict.go.jp:
  OK - 133.243.238.164
ntp-k1.nict.jp:
  OK - 61.205.120.130
ntp.nict.jp:
  OK - 2001:ce8:78::2

# time.google.com の場合
$ python3 check_time_server.py time.google.com
Server: time.google.com
time1.google.com:
  OK - 216.239.35.0
  OK - 2001:4860:4806::
time2.google.com:
  OK - 216.239.35.4
  OK - 2001:4860:4806:4::
time3.google.com:
  OK - 216.239.35.8
  OK - 2001:4860:4806:8::
time4.google.com:
  OK - 216.239.35.12
  OK - 2001:4860:4806:c::

# time.apple.com の場合
$ python3 check_time_server.py time.apple.com
Server: time.apple.com
Error: timed out (2403:300:a0c:4000::1f2)
Error: timed out (2403:300:a0c:3000::1e2)
Error: timed out (2403:300:a16:4000::1f2)
jptyo5-ntp-001.aaplimg.com:
  OK - 17.253.68.125
jptyo5-ntp-002.aaplimg.com:
  OK - 17.253.68.253
  NG - 2403:300:a0c:4000::1f2
jptyo5-ntp-003.aaplimg.com:
  NG - 2403:300:a0c:3000::1e2
jptyo5-ntp-004.aaplimg.com:
  OK - 17.253.68.251
krsel6-ntp-002.aaplimg.com:
  NG - 2403:300:a16:4000::1f2
$ python3 check_time_server.py time.apple.com
Server: time.apple.com
Error: timed out (2403:300:a0c:4000::1f2)
Error: timed out (2403:300:a0c:3000::1e2)
jptyo5-ntp-001.aaplimg.com:
  OK - 17.253.68.125
jptyo5-ntp-002.aaplimg.com:
  OK - 17.253.68.253
  NG - 2403:300:a0c:4000::1f2
jptyo5-ntp-003.aaplimg.com:
  NG - 2403:300:a0c:3000::1e2
jptyo5-ntp-004.aaplimg.com:
  OK - 17.253.68.251
krsel6-ntp-002.aaplimg.com:
  OK - 2403:300:a16:4000::1f2
```

time.apple.com の IPv6 サーバー jp* は全滅です。

うちからはサーバー kr* がネットワーク的に遠いからか屢々 NG になります。

正常化したら macOS のタイムサーバー設定はデフォルト(time.apple.com.)に戻したい…