본문 바로가기

Maker Movement

구글 클라우드 프린트(2) - mDNS 구현

mDNS는 multicast DNS(Domain Name System)의 약어로 이 기술을 이용한 것으로는 애플사의 Bonjour서비스가 대표적이다. 일상생활에서는 가정 혹은 회사에서 네트워크에 연결된 프린터 혹은 다양한 멀티미디어 장치들이 자동으로 검색되는 것은 mDNS 기술을 이용했을 가능성이 크다.


IETF 규약

  • RFC 6762 : Multicast DNS 
  • RFC 6763 : DNS Based Service Discovery
  • RFC 1035 : Domain Names - Implementation & Specification
위의 규약은 토대가 되는 것인데, 확장된 내용을 담고 있는 많은 규약들이 더 있다. mDNS는 DNS규약을 토대로 정의 되었고, 통신메시지 형식 또한 DNS방식을 따른다. mDNS 규약에는 통신메시지 형식외 많은 내용을 담고 있다. 예를 들면 '정보를 요청하는 주기에 대한 정의' 등이 있다.

통신메시지 형식

통신관련 기술을 구현하는 개발자 입장에서는 메시지 형식에 대한 정의가 중요할 듯 싶다. 메시지 형식은 규약을 확인해도 되고 인터넷에서 쉽게 관련 자료를 구할 수 있다. 여기서는 간단히 다루고 대신 실재 메시지 예를 들겠다.

메시지 기본 형식 (출처:RFC-1035)

    +---------------------+

    |        Header       |

    +---------------------+

    |       Question      | the question for the name server

    +---------------------+

    |        Answer       | RRs answering the question

    +---------------------+

    |      Authority      | RRs pointing toward an authority

    +---------------------+

    |      Additional     | RRs holding additional information

    +---------------------+


Header 형식 (출처:RFC-1035)

                                    1  1  1  1  1  1

      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

    |                      ID                       |

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

    |                    QDCOUNT                    |

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

    |                    ANCOUNT                    |

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

    |                    NSCOUNT                    |

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

    |                    ARCOUNT                    |

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+


<< Header 항목 설명 >>

 항 목

설명 및 정의 (DNS) 

 mDNS

 ID

 조회문-응답문 짝을 맞추기 위한 구분값

 0으로 설정한다.

 QR

 조회문이면 0, 응답문이면 1

 

 OPCODE

 조회문의 종류를 구분하는 값

 전송시 0으로 설정, 수신시 무시

 AA

 응답문, 응답서버가 해당 도메인 담당서버

 전송시 0으로 설정, 수신시 무시

 TC

 추가 메시지 유무

 전송시 0으로 설정, 수신시 무시

 RD

 조회문, recursive query 옵션

 전송시 0으로 설정, 수신시 무시

 RA

 recursive query 지원 여부

 전송시 0으로 설정, 수신시 무시

 Z

 여분비트, RFC-2535에서 2개의 비트가 사용된다

 전송시 0으로 설정, 수신시 무시

 RCODE

 응답문, 응답코드

 전송시 0으로 설정, 수신시 무시

 QDCOUNT

 조회 갯수

 

 ANCOUNT

 응답 갯수

 

 NSCOUNT

 응답문, 응답코드

 

 ARCOUNT

 응답문, 응답코드

 



<< 조회문(Query Message)  예 >>

 항 목

바이트 스트림

 의 미

 Header

 00 00 00 00

 

 QDCOUNT

 00 02

 조회 2개

 ANCOUNT

 00 00

 0개

 NSCOUNT

 00 00

 0개

 ARCOUNT

 00 00

 0개

 첫번째 Q:QNAME

 10

 16바이트 NAME

 5F 69 70 73 63 61 6E 6E
 65 72 62 65 61 63 6F 6E
 _ipscannerbeacon
 04

 4바이트 NAME

 5F 74 63 70 _tcp
 05

 5바이트 NAME

 6C 6F 63 61 6C

 local

 00 QNAME 끝

 첫번째 Q:QTYPE

 00 0C PTR
 첫번째 Q:QCLASS

 80 01

 첫번째비트 ON:Unicast 선호

 QCLASS: IN (Internet)

 두번째 Q:QNAME

 13

 19바이트 NAME

 5F 69 70 73 5F 6D 61 63
 73 79 6E 63 5F 63 6C 69
 65 6E 74

 _ips_macsync_client

 C0

 NAME 포인터 & QNAME 끝

 1D

 NAME 포인터:주소:1D

 _tcp

 두번째 Q:QTYPE

 00 0C PTR

 두번째 Q:QCLASS

 80 01 첫번째비트 ON:Unicast 선호

 QCLASS: IN (Internet)



<< 응답문(Response Message) 예 >>

 항 목

바이트 스트림

 의 미

 Header

 00 00 84 00

 QR-비트 ON

 QDCOUNT

 00 00

 0개

 ANCOUNT

 00 01

 응답 1개

 NSCOUNT 00 00 0개
 ARCOUNT

 00 01

 추가정보 1개

 응답:NAME

 0C

 12바이트 스트림

 5F 73 6C 65 65 70 2D 70

 72 6F 78 79

 _sleep-proxy
 04

 4바이트 스트림

 5F 75 64 70 _udp
 05

 5바이트 스트림

 6C 6F 63 61 6C local
 00

 NAME 끝

 응답:TYPE

 00 0C

 PTR

 응답:CLASS

 00 01

 IN (Internet)

 응답:TTL

 00 00 00 00 00 00 00 00

 0초 = 정보 유지 안함

 응답:데이터길이 00 19 25바이트 스트림
 응답:데이터 XX 

 XX XX 2D XX XX 2D XX XX

 2D XX XX 2E 31 20 41 70

 70 6C 65 20 54 56

 XX-XX-XX-XX.1 Apple TV

 XX XX 
 추가:NAME 00

 NAME 끝

 추가:TYPE 00 29 

 추가:CLASS

 05 A0 

 추가:TTL

 00 00 11 94

 4500초동안 정보 유지

 추가:데이터길이

 00 12

 18바이트 스트림
 추가:데이터

 00 04
 00 0E

 XX XX XX XX XX XX XX XX

 XX XX XX XX

 MAC주소등 정보



자바 mDNS 테스트 코드

그동안 DNS모듈을 구현할 일이 없었는데,  이번에 관련 규약을 읽어보니 원래 DNS규약은 적은 양인듯 한데, 점점 많은 확장 규약들이 추가되서 상당히 방대한 양이었다. mDNS를 구현한 오픈소스 라이브러리를 구해서 사용해도 되지만 왠지 상당히 무겁지 않을까 생각된다. 만약 조회문에 대해 간단히 응답만하는 mDNS모듈을 구성한다면, 직접 구현하는 것도 모듈을 가볍게 하기에 도움이 된다고 본다. 혹시 도움이 될 지 모르지만 테스트 목적으로 작성한  Multicast 통신 샘플 코드를 추가한다.


mDNS는 224.0.0.251 : 5353 을 사용한다.


DatagramChannel mc = null;


try

{

InetAddress addr = InetAddress.getByName("127.0.0.1");

NetworkInterface ni = NetworkInterface.getByName("en1");


// Datagram 채널을 연다.

// "StandardProtocolFamily.INET"을 생략하면

// 예외가 발생함 ==> IPv6 socket cannot join IPv4 multicast group

mc = DatagramChannel.open(StandardProtocolFamily.INET);


// 몇가지 옵션을 설정

mc.setOption(StandardSocketOptions.SO_REUSEADDR, true);

mc.setOption(StandardSocketOptions.IP_MULTICAST_IF, ni);


// 포트를 5353에 맞춘다.

mc.bind(new InetSocketAddress(5353));


// 224.0.0.251 multicsting 그룹에 합류한다.

InetAddress group = InetAddress.getByName("224.0.0.251");

MembershipKey key = mc.join(group, ni);


ByteBuffer buf = ByteBuffer.allocate(512);

while (true)

{

if ( key.isValid() )

{

// read함수를 쓸 경우 connect를 요구한다

SocketAddress sender = mc.receive(buf);

buf.flip();

System.out.println("sender=" + sender + "\n" + ByteBufferUtils.serialize(buf));

buf.clear();

}

else

{

System.out.println("trying to connect");

try { Thread.sleep(1000); } catch(InterruptedException e) { }

}

}

}

catch(Exception e)

{

throw e;

}


finally

{

try { mc.close(); } catch(Exception e) { }

}



마치며

구글 클라우드 프린트 문서에 따르면, 크롬브라우저가 mDNS규약에 따라 로컬네트워크에 연결된 장치들을 검색한다. 약속된 장치이름으로 검색을 하는데, DNS-SD 서비스 이름은 _privet._tcp 이다. 장치는 두가지 PTR 정보를 제공할 수 있으면 된다.

  • _privet._tcp.local
  • _printer._sub._privet._tcp.local
다음에 크롬브라우저에서 검색되는 부분을 구현하려 한다.