網絡協議之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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM