网络协议之mDNS20170217

DNS(Domain Name System,域名系统)因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。DNS协议运行在UDP协议之上,使用端口号53。在RFC文档中RFC 2181对DNS有规范说明,RFC 2136对DNS的动态更新进行说明,RFC 2308对DNS查询的反向缓存进行说明。

一、mDNS    具体协议规范地址如下 : http://www.ietf.org/rfc/rfc6762.txt

mdns 即多播dns(Multicast DNS),mDNS主要实现了在没有传统DNS服务器的情况下使局域网内的主机实现相互发现和通信,使用的端口为5353,遵从dns协议,使用现有的DNS信息结构、名语法和资源记录类型。并且没有指定新的操作代码或响应代码。

 

在局域网中,设备和设备之前相互通信需要知道对方的ip地址的,大多数情况,设备的ip不是静态ip地址,而是通过dhcp 协议动态分配的ip 地址,如何设备发现呢,就是要mdns大显身手,例如:现在物联网设备和app之间的通信,要么app通过广播,要么通过组播,发一些特定信息,感兴趣设备应答,实现局域网设备的发现,当然mdns 比这强大的多

1.mDNS 基于 UDP 协议。

组播地址: 组播地址使用的是D类地址,地址范围为:224.0.0.0—239.255.255.255

 

2.mdns 工作原理简单描述:

 

mdns 使用组播地址为: 224.0.0.251 (ipv6: FF02::FB) 端口为5353,mdns 是用于局域网内部的,并且主机的域名为.local 结尾,每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,我是谁(域名),和我的IP地址是多少。然后其他有mdns服务的主机就会响应,也会告诉你,它是谁(域名),它的IP地址是多少。 当然设备需要服务时,就是使用mdns 查询域名对对应的ip地址,对应的设备收到该报文后同样通过组播方式应答,此时其他主机设备也是可以收到该应答报文,其他主机也会记录域名和ip 以及ttl 等,更新缓存

 

比如,A主机进入局域网,开启了 mDNS 服务,并向 mDNS 服务注册以下信息:我提供 FTP 服务,我的IP是 192.168.1.101,端口是 21。当B主机进入局域网,并向 B 主机的 mDNS 服务请求,我要找局域网内 FTP 服务器,B主机的 mDNS 就会去局域网内向其他的 mDNS 询问,并且最终告诉你,有一个IP地址为 192.168.1.101,端口号是 21 的主机,也就是 A 主机提供 FTP 服务,所以 B 主机就知道了 A 主机的 IP 地址和端口号了。

 

大概的原理就是这样子,mDNS提供的服务要远远多于这个,当然服务多但并不复杂。

3.mDNSResponder与Bonjour的关系:

The mDNSResponder project is a component of Bonjour,

Apple's ease-of-use IP networking initiative:

<http://developer.apple.com/bonjour/>

Bonjour是法语中的Hello之意。它是Apple公司为基于组播域名服务(multicast DNS)的开放性零配置网络标准所起的名字。使用Bonjour的设备在网络中自动组播它们自己的服务信息并监听其它设备的服务信息。设备之间就像在打招呼,这也是该技术命名为Bonjour的原因。Bonjour使得局域网中的系统和服务即使在没有网络管理员的情况下也很容易被找到。

举一个简单的例子:在局域网中,如果要进行打印服务,必须先知道打印服务器的IP地址。此IP地址一般由IT部门的人负责分配,然后他还得全员发邮件以公示此地址。有了Bonjour以后,打印服务器自己会依据零配置网络标准在局域网内部找到一个可用的IP并注册一个打印服务,名为“print service”之类的。当客户端需要打印服务时,会先搜索网络内部的打印服务器。由于不知道打印服务器的IP地址,客户端只能根据诸如"print service"的名字去查找打印机。在Bonjour的帮助下,客户端最终能找到这台注册了“print service”名字的打印机,并获得它的IP地址以及端口号。

从Bonjour角度来看,该技术主要解决了三个问题:

  • Addressing:即为主机分配IP。Bonjour的Addressing处理比较简单,即每个主机在网络内部的地址可选范围内找一个IP,然后查看网络内部是否有其他主机再用。如果该IP没有被分配的话,它将使用此IP。
  • Naming:Naming解决的是host名和IP地址的对应关系。Bonjour采用的是Multiple DNS技术,即DNS查询消息将通过UDP组播方式发送。一旦网络内部某个机器发现查询的机器名和自己设置的一样,就回复这条请求。此外,Bonjour还拓展了MDNS的用途,即除了能查找host外,还支持对service的查找。不过,Bonjour的Naming有一个限制,即网络内部不能有重名的host或service。
  • Service Discovery:SD基于上面的Naming工作,它使得应用程序能查找到网络内部的服务,并解析该服务对应的IP地址和端口号。应用程序一旦得到服务的IP地址和端口号,就可以直接和该服务建立交互关系。

Bonjour技术在Mac OS以及Itunes、Iphone上都得到了广泛应用。为了进一步推广,Apple通过开源工程mdnsresponder将其开源出来。在Windows平台上,它将生成一个后台程序mdnsresponder。在Android平台上(或者说支持POSIX的Linux平台)它是一个名为mdnsd的程序。不过,不论是mdnsresponder还是mdnsd,应用开发者要做的仅仅是利用Bonjour的API向它们发起服务注册、服务查询和服务解析等请求并接收来自它们的处理结果。

下面我们将介绍Bonjour API中使用最多的三个函数,它们分别是服务注册、服务查询和服务解析。理解这三个函数的功能也是理解MDnsSdListener的基础。

使用Bonjour API必须包含如下的头文件和动态库,并连接到:

#include <dns_sd.h>  //必须包含此头文件

libmdnssd.so  //链接到此so

Bonjour中,服务注册的API为DNSServiceRegister,原型如图1所示:

                    

图1  DNSServiceRegister原型

该函数的解释如下:

  • sdRef:代表一个未初始化的DNSService实体。其类型DNSServiceRef是指针。该参数最终由DNSServiceRegister函数分配内存并初始化。
  • flags:表示当网络内部有重名服务时的冲突处理。默认是按顺序修改服务名。例如要注册的服务名为“printer”,当检测到重名冲突时,就可改名为“printer(1)”。
  • interfaceIndex:表示该服务输出到主机的哪些网络接口上。值-1表示仅对本机支持,也就是该服务的用在loop接口上。
  • name:表示服务名,为空的话就取机器名。
  • regtype:服务类型,用字符串表达。Bonjour要求格式为"_服务名._传输协议",例如"_ftp._tcp"。目前传输协议仅支持TCP和UDP。
  • domian和host一般都为空。
  • port表示该服务的端口。如果为0的话,Bonjour会自动分配一个。
  • txtLen以及txtRecord字符串用来描述该服务。一般都设置为空。
  • callBack:设置回调函数。该服注册的请求结果都会通过它回调给客户端。
  • context:上下文指针,由应用程序设置。

当客户端需要搜索网络内部特定服务时,需要使用DNSServiceBrowser API,其原型如图2所示:

                     

图2  DNSServiceBrowser原型

其中:

  • sdref、interfaceIndex、regtype、domain以及context含义与DNSServiceRegister一样。
  • flags:在本函数中没有作用。
  • callBack:为DNSServiceBrowser处理结果的回调通知接口。

当客户端想获得指定服务的IP和端口号时,需要使用DNSServiceResolve API,其原型如图3所示:

                      

图3  DNSServiceResolve原型

其中:

  • name、regtype和domain都从DNSServiceBrowse函数的处理结果中获得。
  • callBack用于通知DNSServiceResolve的处理结果。该回调函数将返回服务的IP地址和端口号。

如果需要了解Bonjour安卓中的使用方法及原理,请阅读该部分的原文: http://blog.csdn.net/innost/article/details/8629139

 

4.Linux中的使用方法:

附件提供了mDNS的源码,分析源码我们就可以知道如何编译安装以及如何使用:

在mDNSResponder-107.5\mDNSPosix目录中的Responder.c文件中的main我们可以看到

调用了PrintUsage函数中提供了用法说明:

根据上面的描述,使用shell命令调用的例子:

//mDNSResponderPosix来源于bonjour,服务注册

sprintf(buf, "mDNSResponderPosix -n %s -t _ipc_http._tcp. " "-d local. -p 5959 &", gpCC->ccArg.pDevId);//-n 服务名,-t 服务类型,-d域名,-p端口号,

    system(buf);//后台运行

再看main函数中执行过程:

初始化后调用了RegisterOurServices函数把要提供的服务注册进去(追踪下去就可以看到最终调用了mdnscore.c中的mDNS_RegisterService函数):

最后如果想详细了解可以阅读如下文件,或者网上搜索下载mDNS的源码阅读

  1 /* -*- Mode: C; tab-width: 4 -*-
  2  *
  3  * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
  4  *
  5  * @APPLE_LICENSE_HEADER_START@
  6  * 
  7  * This file contains Original Code and/or Modifications of Original Code
  8  * as defined in and that are subject to the Apple Public Source License
  9  * Version 2.0 (the 'License'). You may not use this file except in
 10  * compliance with the License. Please obtain a copy of the License at
 11  * http://www.opensource.apple.com/apsl/ and read it before using this
 12  * file.
 13  * 
 14  * The Original Code and all software distributed under the License are
 15  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 16  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 17  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 18  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 19  * Please see the License for the specific language governing rights and
 20  * limitations under the License.
 21  * 
 22  * @APPLE_LICENSE_HEADER_END@
 23 
 24     Change History (most recent first):
 25 
 26 $Log: Responder.c,v $
 27 Revision 1.30  2005/10/26 22:21:16  cheshire
 28 <rdar://problem/4149841> Potential buffer overflow in mDNSResponderPosix
 29 
 30 Revision 1.29  2005/03/04 21:35:33  cheshire
 31 <rdar://problem/4037201> Services.txt file not parsed properly when it contains more than one service
 32 
 33 Revision 1.28  2005/01/11 01:55:26  ksekar
 34 Fix compile errors in Posix debug build
 35 
 36 Revision 1.27  2004/12/01 04:28:43  cheshire
 37 <rdar://problem/3872803> Darwin patches for Solaris and Suse
 38 Use version of daemon() provided in mDNSUNP.c instead of local copy
 39 
 40 Revision 1.26  2004/11/30 22:37:01  cheshire
 41 Update copyright dates and add "Mode: C; tab-width: 4" headers
 42 
 43 Revision 1.25  2004/11/11 02:00:51  cheshire
 44 Minor fixes to getopt, error message
 45 
 46 Revision 1.24  2004/11/09 19:32:10  rpantos
 47 Suggestion from Ademar de Souza Reis Jr. to allow comments in services file
 48 
 49 Revision 1.23  2004/09/17 01:08:54  cheshire
 50 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
 51   The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
 52   declared in that file are ONLY appropriate to single-address-space embedded applications.
 53   For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
 54 
 55 Revision 1.22  2004/09/16 01:58:22  cheshire
 56 Fix compiler warnings
 57 
 58 Revision 1.21  2004/06/15 03:48:07  cheshire
 59 Update mDNSResponderPosix to take multiple name=val arguments in a sane way
 60 
 61 Revision 1.20  2004/05/18 23:51:26  cheshire
 62 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
 63 
 64 Revision 1.19  2004/03/12 08:03:14  cheshire
 65 Update comments
 66 
 67 Revision 1.18  2004/01/25 00:00:55  cheshire
 68 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
 69 
 70 Revision 1.17  2003/12/11 19:11:55  cheshire
 71 Fix compiler warning
 72 
 73 Revision 1.16  2003/08/14 02:19:55  cheshire
 74 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
 75 
 76 Revision 1.15  2003/08/12 19:56:26  cheshire
 77 Update to APSL 2.0
 78 
 79 Revision 1.14  2003/08/06 18:20:51  cheshire
 80 Makefile cleanup
 81 
 82 Revision 1.13  2003/07/23 00:00:04  cheshire
 83 Add comments
 84 
 85 Revision 1.12  2003/07/15 01:55:16  cheshire
 86 <rdar://problem/3315777> Need to implement service registration with subtypes
 87 
 88 Revision 1.11  2003/07/14 18:11:54  cheshire
 89 Fix stricter compiler warnings
 90 
 91 Revision 1.10  2003/07/10 20:27:31  cheshire
 92 <rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string
 93 
 94 Revision 1.9  2003/07/02 21:19:59  cheshire
 95 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
 96 
 97 Revision 1.8  2003/06/18 05:48:41  cheshire
 98 Fix warnings
 99 
100 Revision 1.7  2003/05/06 00:00:50  cheshire
101 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
102 
103 Revision 1.6  2003/03/08 00:35:56  cheshire
104 Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
105 
106 Revision 1.5  2003/02/20 06:48:36  cheshire
107 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
108 Reviewed by: Josh Graessley, Bob Bradley
109 
110 Revision 1.4  2003/01/28 03:07:46  cheshire
111 Add extra parameter to mDNS_RenameAndReregisterService(),
112 and add support for specifying a domain other than dot-local.
113 
114 Revision 1.3  2002/09/21 20:44:53  zarzycki
115 Added APSL info
116 
117 Revision 1.2  2002/09/19 04:20:44  cheshire
118 Remove high-ascii characters that confuse some systems
119 
120 Revision 1.1  2002/09/17 06:24:35  cheshire
121 First checkin
122 
123 */
124 
125 #include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above
126 #include "mDNSPosix.h"    // Defines the specific types needed to run mDNS on this platform
127 
128 #include <assert.h>
129 #include <stdio.h>            // For printf()
130 #include <stdlib.h>            // For exit() etc.
131 #include <string.h>            // For strlen() etc.
132 #include <unistd.h>            // For select()
133 #include <errno.h>            // For errno, EINTR
134 #include <signal.h>
135 #include <fcntl.h>
136 
137 #if COMPILER_LIKES_PRAGMA_MARK
138 #pragma mark ***** Globals
139 #endif
140 
141 static mDNS mDNSStorage;       // mDNS core uses this to store its globals
142 static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
143 
144 static const char *gProgramName = "mDNSResponderPosix";
145 
146 #if COMPILER_LIKES_PRAGMA_MARK
147 #pragma mark ***** Signals
148 #endif
149 
150 static volatile mDNSBool gReceivedSigUsr1;
151 static volatile mDNSBool gReceivedSigHup;
152 static volatile mDNSBool gStopNow;
153 
154 // We support 4 signals.
155 //
156 // o SIGUSR1 toggles verbose mode on and off in debug builds
157 // o SIGHUP  triggers the program to re-read its preferences.
158 // o SIGINT  causes an orderly shutdown of the program.
159 // o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
160 // o SIGKILL kills us dead (easy to implement :-)
161 //
162 // There are fatal race conditions in our signal handling, but there's not much 
163 // we can do about them while remaining within the Posix space.  Specifically, 
164 // if a signal arrives after we test the globals its sets but before we call 
165 // select, the signal will be dropped.  The user will have to send the signal 
166 // again.  Unfortunately, Posix does not have a "sigselect" to atomically 
167 // modify the signal mask and start a select.
168 
169 static void HandleSigUsr1(int sigraised)
170     // If we get a SIGUSR1 we toggle the state of the 
171     // verbose mode.
172 {
173     assert(sigraised == SIGUSR1);
174     gReceivedSigUsr1 = mDNStrue;
175 }
176 
177 static void HandleSigHup(int sigraised)
178     // A handler for SIGHUP that causes us to break out of the 
179     // main event loop when the user kill 1's us.  This has the 
180     // effect of triggered the main loop to deregister the 
181     // current services and re-read the preferences.
182 {
183     assert(sigraised == SIGHUP);
184     gReceivedSigHup = mDNStrue;
185 }
186 
187 static void HandleSigInt(int sigraised)
188     // A handler for SIGINT that causes us to break out of the 
189     // main event loop when the user types ^C.  This has the 
190     // effect of quitting the program.
191 {
192     assert(sigraised == SIGINT);
193     
194     if (gMDNSPlatformPosixVerboseLevel > 0) {
195         fprintf(stderr, "\nSIGINT\n");
196     }
197     gStopNow = mDNStrue;
198 }
199 
200 static void HandleSigQuit(int sigraised)
201     // If we get a SIGQUIT the user is desperate and we 
202     // just call mDNS_Close directly.  This is definitely 
203     // not safe (because it could reenter mDNS), but 
204     // we presume that the user has already tried the safe 
205     // alternatives.
206 {
207     assert(sigraised == SIGQUIT);
208 
209     if (gMDNSPlatformPosixVerboseLevel > 0) {
210         fprintf(stderr, "\nSIGQUIT\n");
211     }
212     mDNS_Close(&mDNSStorage);
213     exit(0);
214 }
215 
216 #if COMPILER_LIKES_PRAGMA_MARK
217 #pragma mark ***** Parameter Checking
218 #endif
219 
220 static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
221     // Checks that richTextName is reasonable 
222     // label and, if it isn't and printExplanation is true, prints 
223     // an explanation of why not.
224 {
225     mDNSBool result = mDNStrue;
226     if (result && strlen(richTextName) > 63) {
227         if (printExplanation) {
228             fprintf(stderr, 
229                     "%s: Service name is too long (must be 63 characters or less)\n", 
230                     gProgramName);
231         }
232         result = mDNSfalse;
233     }
234     if (result && richTextName[0] == 0) {
235         if (printExplanation) {
236             fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
237         }
238         result = mDNSfalse;
239     }
240     return result;
241 }
242 
243 static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
244     // Checks that serviceType is a reasonable service type 
245     // label and, if it isn't and printExplanation is true, prints 
246     // an explanation of why not.
247 {
248     mDNSBool result;
249     
250     result = mDNStrue;
251     if (result && strlen(serviceType) > 63) {
252         if (printExplanation) {
253             fprintf(stderr, 
254                     "%s: Service type is too long (must be 63 characters or less)\n", 
255                     gProgramName);
256         }
257         result = mDNSfalse;
258     }
259     if (result && serviceType[0] == 0) {
260         if (printExplanation) {
261             fprintf(stderr, 
262                     "%s: Service type can't be empty\n", 
263                     gProgramName);
264         }
265         result = mDNSfalse;
266     }
267     return result;
268 }
269 
270 static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation)
271     // Checks that portNumber is a reasonable port number
272     // and, if it isn't and printExplanation is true, prints 
273     // an explanation of why not.
274 {
275     mDNSBool result;
276     
277     result = mDNStrue;
278     if (result && (portNumber <= 0 || portNumber > 65535)) {
279         if (printExplanation) {
280             fprintf(stderr, 
281                     "%s: Port number specified by -p must be in range 1..65535\n", 
282                     gProgramName);
283         }
284         result = mDNSfalse;
285     }
286     return result;
287 }
288 
289 #if COMPILER_LIKES_PRAGMA_MARK
290 #pragma mark ***** Command Line Arguments
291 #endif
292 
293 static const char kDefaultPIDFile[]     = "/var/run/mDNSResponder.pid";
294 static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
295 static const char kDefaultServiceDomain[] = "local.";
296 enum {
297     kDefaultPortNumber = 548
298 };
299 
300 static void PrintUsage()
301 {
302     fprintf(stderr, 
303             "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n", 
304             gProgramName);
305     fprintf(stderr, "          -v verbose mode, level is a number from 0 to 2\n");
306     fprintf(stderr, "             0 = no debugging info (default)\n");
307     fprintf(stderr, "             1 = standard debugging info\n");
308     fprintf(stderr, "             2 = intense debugging info\n");
309     fprintf(stderr, "             can be cycled kill -USR1\n");
310     fprintf(stderr, "          -r also bind to port 53 (port 5353 is always bound)\n");
311     fprintf(stderr, "          -n uses 'name' as the service name (required)\n");
312     fprintf(stderr, "          -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
313     fprintf(stderr, "          -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
314     fprintf(stderr, "          -p uses 'port' as the port number (default is '%d')\n",  kDefaultPortNumber);
315     fprintf(stderr, "          -f reads a service list from 'file'\n");
316     fprintf(stderr, "          -b forces daemon (background) mode\n");
317     fprintf(stderr, "          -P uses 'pidfile' as the PID file\n");
318     fprintf(stderr, "             (default is '%s')\n",  kDefaultPIDFile);
319     fprintf(stderr, "             only meaningful if -b also specified\n");
320     fprintf(stderr, "          -x stores name=val in TXT record (default is empty).\n");
321     fprintf(stderr, "             MUST be the last command-line argument;\n");
322     fprintf(stderr, "             all subsequent arguments after -x are treated as name=val pairs.\n");
323 }
324 
325 static   mDNSBool  gAvoidPort53      = mDNStrue;
326 static const char *gServiceName      = "";
327 static const char *gServiceType      = kDefaultServiceType;
328 static const char *gServiceDomain    = kDefaultServiceDomain;
329 static mDNSu8      gServiceText[sizeof(RDataBody)];
330 static mDNSu16     gServiceTextLen   = 0;
331 static        int  gPortNumber       = kDefaultPortNumber;
332 static const char *gServiceFile      = "";
333 static   mDNSBool  gDaemon           = mDNSfalse;
334 static const char *gPIDFile          = kDefaultPIDFile;
335 
336 static void ParseArguments(int argc, char **argv)
337     // Parses our command line arguments into the global variables 
338     // listed above.
339 {
340     int ch;
341     
342     // Set gProgramName to the last path component of argv[0]
343     
344     gProgramName = strrchr(argv[0], '/');
345     if (gProgramName == NULL) {
346         gProgramName = argv[0];
347     } else {
348         gProgramName += 1;
349     }
350     
351     // Parse command line options using getopt.
352     
353     do {
354         ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
355         if (ch != -1) {
356             switch (ch) {
357                 case 'v':
358                     gMDNSPlatformPosixVerboseLevel = atoi(optarg);
359                     if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
360                         fprintf(stderr, 
361                                 "%s: Verbose mode must be in the range 0..2\n", 
362                                 gProgramName);
363                         exit(1);
364                     }
365                     break;
366                 case 'r':
367                     gAvoidPort53 = mDNSfalse;
368                     break;
369                 case 'n':
370                     gServiceName = optarg;
371                     if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
372                         exit(1);
373                     }
374                     break;
375                 case 't':
376                     gServiceType = optarg;
377                     if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
378                         exit(1);
379                     }
380                     break;
381                 case 'd':
382                     gServiceDomain = optarg;
383                     break;
384                 case 'p':
385                     gPortNumber = atol(optarg);
386                     if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
387                         exit(1);
388                     }
389                     break;
390                 case 'f':
391                     gServiceFile = optarg;
392                     break;
393                 case 'b':
394                     gDaemon = mDNStrue;
395                     break;
396                 case 'P':
397                     gPIDFile = optarg;
398                     break;
399                 case 'x':
400                     while (optind < argc)
401                         {
402                         gServiceText[gServiceTextLen] = strlen(argv[optind]);
403                         memcpy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
404                         gServiceTextLen += 1 + gServiceText[gServiceTextLen];
405                         optind++;
406                         }
407                     ch = -1;
408                     break;
409                 case '?':
410                 default:
411                     PrintUsage();
412                     exit(1);
413                     break;
414             }
415         }
416     } while (ch != -1);
417 
418     // Check for any left over command line arguments.
419     
420     if (optind != argc) {
421         PrintUsage();
422         fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
423         exit(1);
424     }
425     
426     // Check for inconsistency between the arguments.
427     
428     if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) {
429         PrintUsage();
430         fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
431         exit(1);
432     }
433 }
434 
435 #if COMPILER_LIKES_PRAGMA_MARK
436 #pragma mark ***** Registration
437 #endif
438 
439 typedef struct PosixService PosixService;
440 
441 struct PosixService {
442     ServiceRecordSet coreServ;
443     PosixService *next;
444     int serviceID;
445 };
446 
447 static PosixService *gServiceList = NULL;
448 
449 static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
450     // mDNS core calls this routine to tell us about the status of 
451     // our registration.  The appropriate action to take depends 
452     // entirely on the value of status.
453 {
454     switch (status) {
455 
456         case mStatus_NoError:      
457             debugf("Callback: %##s Name Registered",   thisRegistration->RR_SRV.resrec.name->c); 
458             // Do nothing; our name was successfully registered.  We may 
459             // get more call backs in the future.
460             break;
461 
462         case mStatus_NameConflict: 
463             debugf("Callback: %##s Name Conflict",     thisRegistration->RR_SRV.resrec.name->c); 
464 
465             // In the event of a conflict, this sample RegistrationCallback 
466             // just calls mDNS_RenameAndReregisterService to automatically 
467             // pick a new unique name for the service. For a device such as a 
468             // printer, this may be appropriate.  For a device with a user 
469             // interface, and a screen, and a keyboard, the appropriate response 
470             // may be to prompt the user and ask them to choose a new name for 
471             // the service.
472             //
473             // Also, what do we do if mDNS_RenameAndReregisterService returns an 
474             // error.  Right now I have no place to send that error to.
475             
476             status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
477             assert(status == mStatus_NoError);
478             break;
479 
480         case mStatus_MemFree:      
481             debugf("Callback: %##s Memory Free",       thisRegistration->RR_SRV.resrec.name->c); 
482             
483             // When debugging is enabled, make sure that thisRegistration 
484             // is not on our gServiceList.
485             
486             #if !defined(NDEBUG)
487                 {
488                     PosixService *cursor;
489                     
490                     cursor = gServiceList;
491                     while (cursor != NULL) {
492                         assert(&cursor->coreServ != thisRegistration);
493                         cursor = cursor->next;
494                     }
495                 }
496             #endif
497             free(thisRegistration);
498             break;
499 
500         default:                   
501             debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status); 
502             break;
503     }
504 }
505 
506 static int gServiceID = 0;
507 
508 static mStatus RegisterOneService(const char *  richTextName, 
509                                   const char *  serviceType, 
510                                   const char *  serviceDomain, 
511                                   const mDNSu8  text[],
512                                   mDNSu16       textLen,
513                                   long          portNumber)
514 {
515     mStatus             status;
516     PosixService *      thisServ;
517     domainlabel         name;
518     domainname          type;
519     domainname          domain;
520     
521     status = mStatus_NoError;
522     thisServ = (PosixService *) malloc(sizeof(*thisServ));
523     if (thisServ == NULL) {
524         status = mStatus_NoMemoryErr;
525     }
526     if (status == mStatus_NoError) {
527         MakeDomainLabelFromLiteralString(&name,  richTextName);
528         MakeDomainNameFromDNSNameString(&type, serviceType);
529         MakeDomainNameFromDNSNameString(&domain, serviceDomain);
530         status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
531                 &name, &type, &domain,                // Name, type, domain
532                 NULL, mDNSOpaque16fromIntVal(portNumber),
533                 text, textLen,                        // TXT data, length
534                 NULL, 0,                            // Subtypes
535                 mDNSInterface_Any,                    // Interface ID
536                 RegistrationCallback, thisServ);    // Callback and context
537     }
538     if (status == mStatus_NoError) {
539         thisServ->serviceID = gServiceID;
540         gServiceID += 1;
541 
542         thisServ->next = gServiceList;
543         gServiceList = thisServ;
544 
545         if (gMDNSPlatformPosixVerboseLevel > 0) {
546             fprintf(stderr, 
547                     "%s: Registered service %d, name '%s', type '%s', port %ld\n", 
548                     gProgramName, 
549                     thisServ->serviceID, 
550                     richTextName,
551                     serviceType,
552                     portNumber);
553         }
554     } else {
555         if (thisServ != NULL) {
556             free(thisServ);
557         }
558     }
559     return status;
560 }
561 
562 static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp)
563 // Read a line, skipping over any blank lines or lines starting with '#'
564 {
565     mDNSBool good, skip;
566     do {
567         good = (fgets(buf, bufSize, fp) != NULL);
568         skip = (good && (buf[0] == '#'));
569     } while (good && skip);
570     if (good)
571     {
572         int        len = strlen( buf);
573         if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
574             buf[len - 1] = '\0';
575     }
576     return good;
577 }
578 
579 static mStatus RegisterServicesInFile(const char *filePath)
580 {
581     mStatus     status = mStatus_NoError;
582     FILE *      fp = fopen(filePath, "r");
583     int         junk;
584     
585     if (fp == NULL) {
586         status = mStatus_UnknownErr;
587     }
588     if (status == mStatus_NoError) {
589         mDNSBool good = mDNStrue;
590         do {
591             int         ch;
592             char name[256];
593             char type[256];
594             const char *dom = kDefaultServiceDomain;
595             char rawText[1024];
596             mDNSu8  text[sizeof(RDataBody)];
597             unsigned int textLen = 0;
598             char port[256];
599 
600             // Skip over any blank lines.
601             do ch = fgetc(fp); while ( ch == '\n' || ch == '\r' );
602             if (ch != EOF) good = (ungetc(ch, fp) == ch);
603             
604             // Read three lines, check them for validity, and register the service.
605             good = ReadALine(name, sizeof(name), fp);               
606             if (good) {
607                 good = ReadALine(type, sizeof(type), fp);
608             }
609             if (good) {
610                 char *p = type;
611                 while (*p && *p != ' ') p++;
612                 if (*p) {
613                     *p = 0;
614                     dom = p+1;
615                 }
616             }
617             if (good) {
618                 good = ReadALine(port, sizeof(port), fp);
619             }
620             if (good) {
621                 good =     CheckThatRichTextNameIsUsable(name, mDNSfalse)
622                         && CheckThatServiceTypeIsUsable(type, mDNSfalse)
623                         && CheckThatPortNumberIsUsable(atol(port), mDNSfalse);
624             }
625             if (good) {
626                 while (1) {
627                     int len;
628                     if (!ReadALine(rawText, sizeof(rawText), fp)) break;
629                     len = strlen(rawText);
630                     if (len <= 255)
631                         {
632                         unsigned int newlen = textLen + 1 + len;
633                         if (len == 0 || newlen >= sizeof(text)) break;
634                         text[textLen] = len;
635                         memcpy(text + textLen + 1, rawText, len);
636                         textLen = newlen;
637                         }
638                     else
639                         fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n", 
640                             gProgramName, name, type, port);
641                 }
642             }
643             if (good) {
644                 status = RegisterOneService(name, type, dom, text, textLen, atol(port));
645                 if (status != mStatus_NoError) {
646                     fprintf(stderr, "%s: Failed to register service, name = %s, type = %s, port = %s\n", 
647                             gProgramName, name, type, port);
648                     status = mStatus_NoError;       // keep reading
649                 }
650             }
651         } while (good && !feof(fp));
652 
653         if ( ! good ) {
654             fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
655         }
656     }
657     
658     if (fp != NULL) {
659         junk = fclose(fp);
660         assert(junk == 0);
661     }
662     
663     return status;
664 }
665 
666 static mStatus RegisterOurServices(void)
667 {
668     mStatus status;
669     
670     status = mStatus_NoError;
671     if (gServiceName[0] != 0) {
672         status = RegisterOneService(gServiceName, 
673                                     gServiceType, 
674                                     gServiceDomain, 
675                                     gServiceText, gServiceTextLen, 
676                                     gPortNumber);
677     }
678     if (status == mStatus_NoError && gServiceFile[0] != 0) {
679         status = RegisterServicesInFile(gServiceFile);
680     }
681     return status;
682 }
683 
684 static void DeregisterOurServices(void)
685 {
686     PosixService *thisServ;
687     int thisServID;
688     
689     while (gServiceList != NULL) {
690         thisServ = gServiceList;
691         gServiceList = thisServ->next;
692 
693         thisServID = thisServ->serviceID;
694         
695         mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
696 
697         if (gMDNSPlatformPosixVerboseLevel > 0) {
698             fprintf(stderr, 
699                     "%s: Deregistered service %d\n",
700                     gProgramName, 
701                     thisServ->serviceID);
702         }
703     }
704 }
705 
706 #if COMPILER_LIKES_PRAGMA_MARK
707 #pragma mark **** Main
708 #endif
709 
710 int main(int argc, char **argv)
711 {
712     mStatus status;
713     int     result;
714 
715     // Parse our command line arguments.  This won't come back if there's an error.
716     
717     ParseArguments(argc, argv);
718 
719     // If we're told to run as a daemon, then do that straight away.
720     // Note that we don't treat the inability to create our PID 
721     // file as an error.  Also note that we assign getpid to a long 
722     // because printf has no format specified for pid_t.
723     
724     if (gDaemon) {
725         if (gMDNSPlatformPosixVerboseLevel > 0) {
726             fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
727         }
728         daemon(0,0);
729         {
730             FILE *fp;
731             int  junk;
732             
733             fp = fopen(gPIDFile, "w");
734             if (fp != NULL) {
735                 fprintf(fp, "%ld\n", (long) getpid());
736                 junk = fclose(fp);
737                 assert(junk == 0);
738             }
739         }
740     } else {
741         if (gMDNSPlatformPosixVerboseLevel > 0) {
742             fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
743         }
744     }
745 
746     status = mDNS_Init(&mDNSStorage, &PlatformStorage,
747         mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
748         mDNS_Init_AdvertiseLocalAddresses,
749         mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
750     if (status != mStatus_NoError) return(2);
751 
752     status = RegisterOurServices();
753     if (status != mStatus_NoError) return(2);
754     
755     signal(SIGHUP,  HandleSigHup);      // SIGHUP has to be sent by kill -HUP <pid>
756     signal(SIGINT,  HandleSigInt);      // SIGINT is what you get for a Ctrl-C
757     signal(SIGQUIT, HandleSigQuit);     // SIGQUIT is what you get for a Ctrl-\ (indeed)
758     signal(SIGUSR1, HandleSigUsr1);     // SIGUSR1 has to be sent by kill -USR1 <pid>
759 
760     while (!gStopNow)
761         {
762         int nfds = 0;
763         fd_set readfds;
764         struct timeval timeout;
765         int result;
766         
767         // 1. Set up the fd_set as usual here.
768         // This example client has no file descriptors of its own,
769         // but a real application would call FD_SET to add them to the set here
770         FD_ZERO(&readfds);
771         
772         // 2. Set up the timeout.
773         // This example client has no other work it needs to be doing,
774         // so we set an effectively infinite timeout
775         timeout.tv_sec = 0x3FFFFFFF;
776         timeout.tv_usec = 0;
777         
778         // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
779         mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
780         
781         // 4. Call select as normal
782         verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
783         result = select(nfds, &readfds, NULL, NULL, &timeout);
784         
785         if (result < 0)
786             {
787             verbosedebugf("select() returned %d errno %d", result, errno);
788             if (errno != EINTR) gStopNow = mDNStrue;
789             else
790                 {
791                 if (gReceivedSigUsr1)
792                     {
793                     gReceivedSigUsr1 = mDNSfalse;
794                     gMDNSPlatformPosixVerboseLevel += 1;
795                     if (gMDNSPlatformPosixVerboseLevel > 2)
796                         gMDNSPlatformPosixVerboseLevel = 0;
797                     if ( gMDNSPlatformPosixVerboseLevel > 0 )
798                         fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
799                     }
800                 if (gReceivedSigHup)
801                     {
802                     if (gMDNSPlatformPosixVerboseLevel > 0)
803                         fprintf(stderr, "\nSIGHUP\n");
804                     gReceivedSigHup = mDNSfalse;
805                     DeregisterOurServices();
806                     status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
807                     if (status != mStatus_NoError) break;
808                     status = RegisterOurServices();
809                     if (status != mStatus_NoError) break;
810                     }
811                 }
812             }
813         else
814             {
815             // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
816             mDNSPosixProcessFDSet(&mDNSStorage, &readfds);
817             
818             // 6. This example client has no other work it needs to be doing,
819             // but a real client would do its work here
820             // ... (do work) ...
821             }
822         }
823 
824     debugf("Exiting");
825     
826     DeregisterOurServices();
827     mDNS_Close(&mDNSStorage);
828 
829     if (status == mStatus_NoError) {
830         result = 0;
831     } else {
832         result = 2;
833     }
834     if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
835         fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result);
836     }
837     
838     return result;
839 }
Respond.c
ReadMe About mDNSPosix
----------------------

mDNSPosix is a port of Apple's Multicast DNS and DNS Service Discovery
code to Posix platforms.

Multicast DNS and DNS Service Discovery are technologies that allow you
to register IP-based services and browse the network for those services.
For more information about mDNS, see the mDNS web site.

  <http://www.multicastdns.org/>

Multicast DNS is part of a family of technologies resulting from the
efforts of the IETF Zeroconf working group.  For information about
other Zeroconf technologies, see the Zeroconf web site.

  <http://www.zeroconf.org/>

Apple uses the trade mark "Bonjour" to describe our implementation of
Zeroconf technologies.  This sample is designed to show how easy it is
to make a device "Bonjour compatible".

The "Bonjour" trade mark can also be licensed at no charge for
inclusion on your own products, packaging, manuals, promotional
materials, or web site. For details and licensing terms, see

  <http://developer.apple.com/bonjour/>

The code in this sample was compiled and tested on Mac OS X (10.1.x,
10.2, 10.3), Solaris (SunOS 5.8), Linux (Redhat 2.4.9-21, Fedora Core 1), 
and OpenBSD (2.9). YMMV.


Packing List
------------

The sample uses the following directories:

o mDNSCore -- A directory containing the core mDNS code.  This code
  is written in pure ANSI C and has proved to be very portable.
  Every platform needs this core protocol engine code.

o mDNSShared -- A directory containing useful code that's not core to
  the main protocol engine itself, but nonetheless useful, and used by
  more than one (but not necessarily all) platforms.

o mDNSPosix -- The files that are specific to Posix platforms: Linux,
  Solaris, FreeBSD, NetBSD, OpenBSD, etc. This code will also work on
  OS X, though that's not its primary purpose.

o Clients -- Example client code showing how to use the API to the
  services provided by the daemon.


Building the Code
-----------------

The sample does not use autoconf technology, primarily because I didn't
want to delay shipping while I learnt how to use it.  Thus the code
builds using a very simple make file.  To build the sample you should
cd to the mDNSPosix directory and type "make os=myos", e.g.

    make os=panther

For Linux you would change that to:

    make os=linux

There are definitions for each of the platforms I ported to.  If you're
porting to any other platform please add appropriate definitions for it
and send us the diffs so they can be incorporated into the main
distribution.


Using the Sample
----------------
When you compile, you will get:

o Main products for general-purpose use (e.g. on a desktop computer):
  - mdnsd
  - libmdns
  - nss_mdns (See nss_ReadMe.txt for important information about nss_mdns)

o Standalone products for dedicated devices (printer, network camera, etc.)
  - mDNSClientPosix
  - mDNSResponderPosix
  - mDNSProxyResponderPosix

o Testing and Debugging tools
  - dns-sd command-line tool (from the "Clients" folder)
  - mDNSNetMonitor
  - mDNSIdentify

As root type "make install" to install eight things:
o mdnsd                   (usually in /usr/sbin)
o libmdns                 (usually in /usr/lib)
o dns_sd.h                (usually in /usr/include)
o startup scripts         (e.g. in /etc/rc.d)
o manual pages            (usually in /usr/share/man)
o dns-sd tool             (usually in /usr/bin)
o nss_mdns                (usually in /lib)
o nss configuration files (usually in /etc)

The "make install" concludes by executing the startup script
(usually "/etc/init.d/mdns start") to start the daemon running.
You shouldn't need to reboot unless you really want to.

Once the daemon is running, you can use the dns-sd test tool
to exercise all the major functionality of the daemon. Running
"dns-sd" with no arguments gives a summary of the available options.
This test tool is also described in detail, with several examples,
in Chapter 6 of the O'Reilly "Zero Configuration Networking" book.


How It Works
------------
                                                   +--------------------+
                                                   | Client Application |
   +----------------+                              +--------------------+
   |  uds_daemon.c  | <--- Unix Domain Socket ---> |      libmdns       |
   +----------------+                              +--------------------+
   |    mDNSCore    |
   +----------------+
   |  mDNSPosix.c   |
   +----------------+

mdnsd is divided into three sections.

o mDNSCore is the main protocol engine
o mDNSPosix.c provides the glue it needs to run on a Posix OS
o uds_daemon.c exports a Unix Domain Socket interface to
  the services provided by mDNSCore

Client applications link with the libmdns, which implements the functions
defined in the dns_sd.h header file, and implements the IPC protocol
used to communicate over the Unix Domain Socket interface to the daemon.

Note that, strictly speaking, nss_mdns could be just another client of
mdnsd, linking with libmdns just like any other client. However, because
of its central role in the normal operation of multicast DNS, it is built
and installed along with the other essential system support components.


Clients for Embedded Systems
----------------------------

For small devices with very constrained resources, with a single address
space and (typically) no virtual memory, the uds_daemon.c/UDS/libmdns
layer may be eliminated, and the Client Application may live directly
on top of mDNSCore:

    +--------------------+
    | Client Application |
    +--------------------+
    |      mDNSCore      |
    +--------------------+
    |    mDNSPosix.c     |
    +--------------------+

Programming to this model is more work, so using the daemon and its
library is recommended if your platform is capable of that.

The runtime behaviour when using the embedded model is as follows:

1. The application calls mDNS_Init, which in turns calls the platform
   (mDNSPlatformInit).

2. mDNSPlatformInit gets a list of interfaces (get_ifi_info) and registers
   each one with the core (mDNS_RegisterInterface).  For each interface
   it also creates a multicast socket (SetupSocket).

3. The application then calls select() repeatedly to handle file descriptor
   events. Before calling select() each time, the application calls
   mDNSPosixGetFDSet() to give mDNSPosix.c a chance to add its own file
   descriptors to the set, and then after select() returns, it calls
   mDNSPosixProcessFDSet() to give mDNSPosix.c a chance to receive and
   process any packets that may have arrived.

4. When the core needs to send a UDP packet it calls
   mDNSPlatformSendUDP.  That routines finds the interface that
   corresponds to the source address requested by the core, and
   sends the datagram using the UDP socket created for the
   interface.  If the socket is flow send-side controlled it just
   drops the packet.

5. When SocketDataReady runs it uses a complex routine,
   "recvfrom_flags", to actually receive the packet.  This is required
   because the core needs information about the packet that is
   only available via the "recvmsg" call, and that call is complex
   to implement in a portable way.  I got my implementation of
   "recvfrom_flags" from Stevens' "UNIX Network Programming", but
   I had to modify it further to work with Linux.

One thing to note is that the Posix platform code is very deliberately
not multi-threaded.  I do everything from a main loop that calls
"select()".  This is good because it avoids all the problems that often
accompany multi-threaded code. If you decide to use threads in your
platform, you will have to implement the mDNSPlatformLock() and
mDNSPlatformUnlock() calls which are currently no-ops in mDNSPosix.c.


Once you've built the embedded samples you can test them by first
running the client, as shown below.

  quinn% build/mDNSClientPosix
  Hit ^C when you're bored waiting for responses.

By default the client starts a search for AppleShare servers and then
sits and waits, printing a message when services appear and disappear.

To continue with the test you should start the responder in another
shell window.

  quinn% build/mDNSResponderPosix -n Foo

This will start the responder and tell it to advertise a AppleShare
service "Foo".  In the client window you will see the client print out
the following as the service shows up on the network.

  quinn% build/mDNSClientPosix
  Hit ^C when you're bored waiting for responses.
  *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'

Back in the responder window you can quit the responder cleanly using
SIGINT (typically ^C).

  quinn% build/mDNSResponderPosix -n Foo
  ^C
  quinn%

As the responder quits it will multicast that the "Foo" service is
disappearing and the client will see that notification and print a
message to that effect (shown below).  Finally, when you're done with
the client you can use SIGINT to quit it.

  quinn% build/mDNSClientPosix
  Hit ^C when you're bored waiting for responses.
  *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
  *** Lost  name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
  ^C
  quinn%

If things don't work, try starting each program in verbose mode (using
the "-v 1" option, or very verbose mode with "-v 2") to see if there's
an obvious cause.

That's it for the core functionality.  Each program supports a variety
of other options.  For example, you can advertise and browse for a
different service type using the "-t type" option.  Use the "-?" option
on each program for more user-level information.


Caveats
-------
Currently the program uses a simple make file.

The Multicast DNS protocol can also operate locally over the loopback
interface, but this exposed some problems with the underlying network
stack in early versions of Mac OS X and may expose problems with other
network stacks too.

o On Mac OS X 10.1.x the code failed to start on the loopback interface
  because the IP_ADD_MEMBERSHIP option returns ENOBUFS.

o On Mac OS X 10.2 the loopback-only case failed because
  "sendto" calls fails with error EHOSTUNREACH. (3016042)

Consequently, the code will attempt service discovery on the loopback
interface only if no other interfaces are available.

I haven't been able to test the loopback-only case on other platforms
because I don't have access to the physical machine.


Licencing
---------
This code is distributed under the Apple Public Source License.
Information about the licence is included at the top of each source file.


Credits and Version History
---------------------------
If you find any problems with this sample, mail <dts@apple.com> and I
will try to fix them up.

1.0a1 (Jul 2002) was a prerelease version that was distributed
internally at Apple.

1.0a2 (Jul 2002) was a prerelease version that was distributed
internally at Apple.

1.0a3 (Aug 2002) was the first shipping version.  The core mDNS code is
the code from Mac OS 10.2 (Jaguar) GM.

Share and Enjoy

Apple Developer Technical Support
Networking, Communications, Hardware

6 Aug 2002


To Do List
----------
• port to a System V that's not Solaris
• use sig_atomic_t for signal to main thread flags
ReadMe.txt

 

posted @ 2017-02-17 11:48  yuweifeng  阅读(22679)  评论(4编辑  收藏  举报