読者です 読者をやめる 読者になる 読者になる

Thanks Driven Life

日々是感謝

LAN 内の AppleTV の IP アドレスを取得する Emacs Lisp

追記 2013/01/29 23:40

appletv.local ではなく airplay.tcp.local を検索することで AppleTV ではなく AirPlay 対応のデバイス全て検索可能になりました。 僕は持ってないので検証できませんが!

追記 2013/01/17 17:40

zeroconf.el 使えば普通にできそうですね!家帰ってから試してみます

経緯

  1. 諸事情があって AppleTV の IP アドレスを取得したい。Emacs Lisp で。
  2. elcuervo / airplay を参考にしたら tenderlove / dnssd 使ってた
  3. dnssd の Emacs Lisp 版がない
    • あるかもしれない。あったら誰か教えてください
  4. dnssd gem を完全にコピーする必要もなくて、結局 AppleTV の IP さえわかればいいので以下になりました

一応取れる。まあいいか。

本当はこうしたかった

;; Overwrote `dns-make-network-process' that have been written in dns.el
(letf (((symbol-function 'dns-make-network-process)
        (lambda (server)
          (let ((coding-system-for-read 'binary)
                (coding-system-for-write 'binary))
            (make-network-process
             :name "dns"
             :coding 'binary
             :buffer (current-buffer)
             :host "224.0.0.251"
             :service 5353
             :type 'datagram)))))
  (dns-query "appletv.local"))
  1. dns-query を使いたかった
  2. dns-make-network-process っつーマクロで service が "domain" 固定だった
  3. ↑ のように letf で丸ごと置き換えちゃおう
  4. dns.elc を load している場合、dns-query 内の dns-make-network-process 部分が展開されてるので、letf が無力

dns.el(.gz) を手動で読みこんで dns-query を eval してもいいんだけど、まあそれはあれなのでこの形にしました。

余談

dns-write や dns-read の存在知るまで、 bindat 使って自力で DNS パケット作ったりしてた。つらい。 せっかくだから載せておく

(require 'bindat)

(setq dns-packet-header-spec
      '((:id      u16)
        (:flags   u16)
        (:qdcount u16)
        (:ancount u16)
        (:nscount u16)
        (:arcount u16)))

(setq data-spec '((:length u8)
                  (:data   str (:length))))

(setq footer-spec '((:qtype  u24)
                    (:qclass u16)))

(defun qname (domain)
  (mapconcat
   (lambda (x)
     (bindat-pack data-spec
                  `((:length . ,(length x))
                    (:data   . ,x))))
   (split-string domain "\\.")
   ""))

(setq binary (concat
              (bindat-pack dns-packet-header-spec
                           `((:id      . #x1021)
                             (:flags   . 0)
                             (:qdcount . 1)
                             (:ancount . 0)
                             (:nscount . 0)
                             (:arcount . 0)))

              (qname "appletv.local")

              (bindat-pack footer-spec `((:qtype  . #x0001)
                                         (:qclass . #x0001)))))
;; binary は ↓ と同じになる
;; (dns-write `((id #x1021)
;;              (opcode query)
;;              (queries (("appletv.local" (type A))))))

参考