网络协议之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的源码阅读
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: Responder.c,v $
Revision 1.30 2005/10/26 22:21:16 cheshire
<rdar://problem/4149841> Potential buffer overflow in mDNSResponderPosix Revision 1.29 2005/03/04 21:35:33 cheshire
<rdar://problem/4037201> Services.txt file not parsed properly when it contains more than one service Revision 1.28 2005/01/11 01:55:26 ksekar
Fix compile errors in Posix debug build Revision 1.27 2004/12/01 04:28:43 cheshire
<rdar://problem/3872803> Darwin patches for Solaris and Suse
Use version of daemon() provided in mDNSUNP.c instead of local copy Revision 1.26 2004/11/30 22:37:01 cheshire
Update copyright dates and add "Mode: C; tab-width: 4" headers Revision 1.25 2004/11/11 02:00:51 cheshire
Minor fixes to getopt, error message Revision 1.24 2004/11/09 19:32:10 rpantos
Suggestion from Ademar de Souza Reis Jr. to allow comments in services file Revision 1.23 2004/09/17 01:08:54 cheshire
Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
declared in that file are ONLY appropriate to single-address-space embedded applications.
For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. Revision 1.22 2004/09/16 01:58:22 cheshire
Fix compiler warnings Revision 1.21 2004/06/15 03:48:07 cheshire
Update mDNSResponderPosix to take multiple name=val arguments in a sane way Revision 1.20 2004/05/18 23:51:26 cheshire
Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers Revision 1.19 2004/03/12 08:03:14 cheshire
Update comments Revision 1.18 2004/01/25 00:00:55 cheshire
Change to use mDNSOpaque16fromIntVal() instead of shifting and masking Revision 1.17 2003/12/11 19:11:55 cheshire
Fix compiler warning Revision 1.16 2003/08/14 02:19:55 cheshire
<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord Revision 1.15 2003/08/12 19:56:26 cheshire
Update to APSL 2.0 Revision 1.14 2003/08/06 18:20:51 cheshire
Makefile cleanup Revision 1.13 2003/07/23 00:00:04 cheshire
Add comments Revision 1.12 2003/07/15 01:55:16 cheshire
<rdar://problem/3315777> Need to implement service registration with subtypes Revision 1.11 2003/07/14 18:11:54 cheshire
Fix stricter compiler warnings Revision 1.10 2003/07/10 20:27:31 cheshire
<rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string Revision 1.9 2003/07/02 21:19:59 cheshire
<rdar://problem/3313413> Update copyright notices, etc., in source code comments Revision 1.8 2003/06/18 05:48:41 cheshire
Fix warnings Revision 1.7 2003/05/06 00:00:50 cheshire
<rdar://problem/3248914> Rationalize naming of domainname manipulation functions Revision 1.6 2003/03/08 00:35:56 cheshire
Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") Revision 1.5 2003/02/20 06:48:36 cheshire
<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
Reviewed by: Josh Graessley, Bob Bradley Revision 1.4 2003/01/28 03:07:46 cheshire
Add extra parameter to mDNS_RenameAndReregisterService(),
and add support for specifying a domain other than dot-local. Revision 1.3 2002/09/21 20:44:53 zarzycki
Added APSL info Revision 1.2 2002/09/19 04:20:44 cheshire
Remove high-ascii characters that confuse some systems Revision 1.1 2002/09/17 06:24:35 cheshire
First checkin */ #include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above
#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include <assert.h>
#include <stdio.h> // For printf()
#include <stdlib.h> // For exit() etc.
#include <string.h> // For strlen() etc.
#include <unistd.h> // For select()
#include <errno.h> // For errno, EINTR
#include <signal.h>
#include <fcntl.h> #if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Globals
#endif static mDNS mDNSStorage; // mDNS core uses this to store its globals
static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals static const char *gProgramName = "mDNSResponderPosix"; #if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Signals
#endif static volatile mDNSBool gReceivedSigUsr1;
static volatile mDNSBool gReceivedSigHup;
static volatile mDNSBool gStopNow; // We support 4 signals.
//
// o SIGUSR1 toggles verbose mode on and off in debug builds
// o SIGHUP triggers the program to re-read its preferences.
// o SIGINT causes an orderly shutdown of the program.
// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
// o SIGKILL kills us dead (easy to implement :-)
//
// There are fatal race conditions in our signal handling, but there's not much
// we can do about them while remaining within the Posix space. Specifically,
// if a signal arrives after we test the globals its sets but before we call
// select, the signal will be dropped. The user will have to send the signal
// again. Unfortunately, Posix does not have a "sigselect" to atomically
// modify the signal mask and start a select. static void HandleSigUsr1(int sigraised)
// If we get a SIGUSR1 we toggle the state of the
// verbose mode.
{
assert(sigraised == SIGUSR1);
gReceivedSigUsr1 = mDNStrue;
} static void HandleSigHup(int sigraised)
// A handler for SIGHUP that causes us to break out of the
// main event loop when the user kill 1's us. This has the
// effect of triggered the main loop to deregister the
// current services and re-read the preferences.
{
assert(sigraised == SIGHUP);
gReceivedSigHup = mDNStrue;
} static void HandleSigInt(int sigraised)
// A handler for SIGINT that causes us to break out of the
// main event loop when the user types ^C. This has the
// effect of quitting the program.
{
assert(sigraised == SIGINT); if (gMDNSPlatformPosixVerboseLevel > ) {
fprintf(stderr, "\nSIGINT\n");
}
gStopNow = mDNStrue;
} static void HandleSigQuit(int sigraised)
// If we get a SIGQUIT the user is desperate and we
// just call mDNS_Close directly. This is definitely
// not safe (because it could reenter mDNS), but
// we presume that the user has already tried the safe
// alternatives.
{
assert(sigraised == SIGQUIT); if (gMDNSPlatformPosixVerboseLevel > ) {
fprintf(stderr, "\nSIGQUIT\n");
}
mDNS_Close(&mDNSStorage);
exit();
} #if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Parameter Checking
#endif static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
// Checks that richTextName is reasonable
// label and, if it isn't and printExplanation is true, prints
// an explanation of why not.
{
mDNSBool result = mDNStrue;
if (result && strlen(richTextName) > ) {
if (printExplanation) {
fprintf(stderr,
"%s: Service name is too long (must be 63 characters or less)\n",
gProgramName);
}
result = mDNSfalse;
}
if (result && richTextName[] == ) {
if (printExplanation) {
fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
}
result = mDNSfalse;
}
return result;
} static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
// Checks that serviceType is a reasonable service type
// label and, if it isn't and printExplanation is true, prints
// an explanation of why not.
{
mDNSBool result; result = mDNStrue;
if (result && strlen(serviceType) > ) {
if (printExplanation) {
fprintf(stderr,
"%s: Service type is too long (must be 63 characters or less)\n",
gProgramName);
}
result = mDNSfalse;
}
if (result && serviceType[] == ) {
if (printExplanation) {
fprintf(stderr,
"%s: Service type can't be empty\n",
gProgramName);
}
result = mDNSfalse;
}
return result;
} static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation)
// Checks that portNumber is a reasonable port number
// and, if it isn't and printExplanation is true, prints
// an explanation of why not.
{
mDNSBool result; result = mDNStrue;
if (result && (portNumber <= || portNumber > )) {
if (printExplanation) {
fprintf(stderr,
"%s: Port number specified by -p must be in range 1..65535\n",
gProgramName);
}
result = mDNSfalse;
}
return result;
} #if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Command Line Arguments
#endif static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid";
static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
static const char kDefaultServiceDomain[] = "local.";
enum {
kDefaultPortNumber =
}; static void PrintUsage()
{
fprintf(stderr,
"Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
gProgramName);
fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n");
fprintf(stderr, " 0 = no debugging info (default)\n");
fprintf(stderr, " 1 = standard debugging info\n");
fprintf(stderr, " 2 = intense debugging info\n");
fprintf(stderr, " can be cycled kill -USR1\n");
fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n");
fprintf(stderr, " -n uses 'name' as the service name (required)\n");
fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber);
fprintf(stderr, " -f reads a service list from 'file'\n");
fprintf(stderr, " -b forces daemon (background) mode\n");
fprintf(stderr, " -P uses 'pidfile' as the PID file\n");
fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile);
fprintf(stderr, " only meaningful if -b also specified\n");
fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n");
fprintf(stderr, " MUST be the last command-line argument;\n");
fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n");
} static mDNSBool gAvoidPort53 = mDNStrue;
static const char *gServiceName = "";
static const char *gServiceType = kDefaultServiceType;
static const char *gServiceDomain = kDefaultServiceDomain;
static mDNSu8 gServiceText[sizeof(RDataBody)];
static mDNSu16 gServiceTextLen = ;
static int gPortNumber = kDefaultPortNumber;
static const char *gServiceFile = "";
static mDNSBool gDaemon = mDNSfalse;
static const char *gPIDFile = kDefaultPIDFile; static void ParseArguments(int argc, char **argv)
// Parses our command line arguments into the global variables
// listed above.
{
int ch; // Set gProgramName to the last path component of argv[0] gProgramName = strrchr(argv[], '/');
if (gProgramName == NULL) {
gProgramName = argv[];
} else {
gProgramName += ;
} // Parse command line options using getopt. do {
ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
if (ch != -) {
switch (ch) {
case 'v':
gMDNSPlatformPosixVerboseLevel = atoi(optarg);
if (gMDNSPlatformPosixVerboseLevel < || gMDNSPlatformPosixVerboseLevel > ) {
fprintf(stderr,
"%s: Verbose mode must be in the range 0..2\n",
gProgramName);
exit();
}
break;
case 'r':
gAvoidPort53 = mDNSfalse;
break;
case 'n':
gServiceName = optarg;
if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
exit();
}
break;
case 't':
gServiceType = optarg;
if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
exit();
}
break;
case 'd':
gServiceDomain = optarg;
break;
case 'p':
gPortNumber = atol(optarg);
if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
exit();
}
break;
case 'f':
gServiceFile = optarg;
break;
case 'b':
gDaemon = mDNStrue;
break;
case 'P':
gPIDFile = optarg;
break;
case 'x':
while (optind < argc)
{
gServiceText[gServiceTextLen] = strlen(argv[optind]);
memcpy(gServiceText+gServiceTextLen+, argv[optind], gServiceText[gServiceTextLen]);
gServiceTextLen += + gServiceText[gServiceTextLen];
optind++;
}
ch = -;
break;
case '?':
default:
PrintUsage();
exit();
break;
}
}
} while (ch != -); // Check for any left over command line arguments. if (optind != argc) {
PrintUsage();
fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
exit();
} // Check for inconsistency between the arguments. if ( (gServiceName[] == ) && (gServiceFile[] == ) ) {
PrintUsage();
fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
exit();
}
} #if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Registration
#endif typedef struct PosixService PosixService; struct PosixService {
ServiceRecordSet coreServ;
PosixService *next;
int serviceID;
}; static PosixService *gServiceList = NULL; static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
// mDNS core calls this routine to tell us about the status of
// our registration. The appropriate action to take depends
// entirely on the value of status.
{
switch (status) { case mStatus_NoError:
debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c);
// Do nothing; our name was successfully registered. We may
// get more call backs in the future.
break; case mStatus_NameConflict:
debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c); // In the event of a conflict, this sample RegistrationCallback
// just calls mDNS_RenameAndReregisterService to automatically
// pick a new unique name for the service. For a device such as a
// printer, this may be appropriate. For a device with a user
// interface, and a screen, and a keyboard, the appropriate response
// may be to prompt the user and ask them to choose a new name for
// the service.
//
// Also, what do we do if mDNS_RenameAndReregisterService returns an
// error. Right now I have no place to send that error to. status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
assert(status == mStatus_NoError);
break; case mStatus_MemFree:
debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c); // When debugging is enabled, make sure that thisRegistration
// is not on our gServiceList. #if !defined(NDEBUG)
{
PosixService *cursor; cursor = gServiceList;
while (cursor != NULL) {
assert(&cursor->coreServ != thisRegistration);
cursor = cursor->next;
}
}
#endif
free(thisRegistration);
break; default:
debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
break;
}
} static int gServiceID = ; static mStatus RegisterOneService(const char * richTextName,
const char * serviceType,
const char * serviceDomain,
const mDNSu8 text[],
mDNSu16 textLen,
long portNumber)
{
mStatus status;
PosixService * thisServ;
domainlabel name;
domainname type;
domainname domain; status = mStatus_NoError;
thisServ = (PosixService *) malloc(sizeof(*thisServ));
if (thisServ == NULL) {
status = mStatus_NoMemoryErr;
}
if (status == mStatus_NoError) {
MakeDomainLabelFromLiteralString(&name, richTextName);
MakeDomainNameFromDNSNameString(&type, serviceType);
MakeDomainNameFromDNSNameString(&domain, serviceDomain);
status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
&name, &type, &domain, // Name, type, domain
NULL, mDNSOpaque16fromIntVal(portNumber),
text, textLen, // TXT data, length
NULL, , // Subtypes
mDNSInterface_Any, // Interface ID
RegistrationCallback, thisServ); // Callback and context
}
if (status == mStatus_NoError) {
thisServ->serviceID = gServiceID;
gServiceID += ; thisServ->next = gServiceList;
gServiceList = thisServ; if (gMDNSPlatformPosixVerboseLevel > ) {
fprintf(stderr,
"%s: Registered service %d, name '%s', type '%s', port %ld\n",
gProgramName,
thisServ->serviceID,
richTextName,
serviceType,
portNumber);
}
} else {
if (thisServ != NULL) {
free(thisServ);
}
}
return status;
} static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp)
// Read a line, skipping over any blank lines or lines starting with '#'
{
mDNSBool good, skip;
do {
good = (fgets(buf, bufSize, fp) != NULL);
skip = (good && (buf[] == '#'));
} while (good && skip);
if (good)
{
int len = strlen( buf);
if ( buf[len - ] == '\r' || buf[len - ] == '\n')
buf[len - ] = '\0';
}
return good;
} static mStatus RegisterServicesInFile(const char *filePath)
{
mStatus status = mStatus_NoError;
FILE * fp = fopen(filePath, "r");
int junk; if (fp == NULL) {
status = mStatus_UnknownErr;
}
if (status == mStatus_NoError) {
mDNSBool good = mDNStrue;
do {
int ch;
char name[];
char type[];
const char *dom = kDefaultServiceDomain;
char rawText[];
mDNSu8 text[sizeof(RDataBody)];
unsigned int textLen = ;
char port[]; // Skip over any blank lines.
do ch = fgetc(fp); while ( ch == '\n' || ch == '\r' );
if (ch != EOF) good = (ungetc(ch, fp) == ch); // Read three lines, check them for validity, and register the service.
good = ReadALine(name, sizeof(name), fp);
if (good) {
good = ReadALine(type, sizeof(type), fp);
}
if (good) {
char *p = type;
while (*p && *p != ' ') p++;
if (*p) {
*p = ;
dom = p+;
}
}
if (good) {
good = ReadALine(port, sizeof(port), fp);
}
if (good) {
good = CheckThatRichTextNameIsUsable(name, mDNSfalse)
&& CheckThatServiceTypeIsUsable(type, mDNSfalse)
&& CheckThatPortNumberIsUsable(atol(port), mDNSfalse);
}
if (good) {
while () {
int len;
if (!ReadALine(rawText, sizeof(rawText), fp)) break;
len = strlen(rawText);
if (len <= )
{
unsigned int newlen = textLen + + len;
if (len == || newlen >= sizeof(text)) break;
text[textLen] = len;
memcpy(text + textLen + , rawText, len);
textLen = newlen;
}
else
fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n",
gProgramName, name, type, port);
}
}
if (good) {
status = RegisterOneService(name, type, dom, text, textLen, atol(port));
if (status != mStatus_NoError) {
fprintf(stderr, "%s: Failed to register service, name = %s, type = %s, port = %s\n",
gProgramName, name, type, port);
status = mStatus_NoError; // keep reading
}
}
} while (good && !feof(fp)); if ( ! good ) {
fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
}
} if (fp != NULL) {
junk = fclose(fp);
assert(junk == );
} return status;
} static mStatus RegisterOurServices(void)
{
mStatus status; status = mStatus_NoError;
if (gServiceName[] != ) {
status = RegisterOneService(gServiceName,
gServiceType,
gServiceDomain,
gServiceText, gServiceTextLen,
gPortNumber);
}
if (status == mStatus_NoError && gServiceFile[] != ) {
status = RegisterServicesInFile(gServiceFile);
}
return status;
} static void DeregisterOurServices(void)
{
PosixService *thisServ;
int thisServID; while (gServiceList != NULL) {
thisServ = gServiceList;
gServiceList = thisServ->next; thisServID = thisServ->serviceID; mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ); if (gMDNSPlatformPosixVerboseLevel > ) {
fprintf(stderr,
"%s: Deregistered service %d\n",
gProgramName,
thisServ->serviceID);
}
}
} #if COMPILER_LIKES_PRAGMA_MARK
#pragma mark **** Main
#endif int main(int argc, char **argv)
{
mStatus status;
int result; // Parse our command line arguments. This won't come back if there's an error. ParseArguments(argc, argv); // If we're told to run as a daemon, then do that straight away.
// Note that we don't treat the inability to create our PID
// file as an error. Also note that we assign getpid to a long
// because printf has no format specified for pid_t. if (gDaemon) {
if (gMDNSPlatformPosixVerboseLevel > ) {
fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
}
daemon(,);
{
FILE *fp;
int junk; fp = fopen(gPIDFile, "w");
if (fp != NULL) {
fprintf(fp, "%ld\n", (long) getpid());
junk = fclose(fp);
assert(junk == );
}
}
} else {
if (gMDNSPlatformPosixVerboseLevel > ) {
fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
}
} status = mDNS_Init(&mDNSStorage, &PlatformStorage,
mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
mDNS_Init_AdvertiseLocalAddresses,
mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
if (status != mStatus_NoError) return(); status = RegisterOurServices();
if (status != mStatus_NoError) return(); signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid>
signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C
signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed)
signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid> while (!gStopNow)
{
int nfds = ;
fd_set readfds;
struct timeval timeout;
int result; // 1. Set up the fd_set as usual here.
// This example client has no file descriptors of its own,
// but a real application would call FD_SET to add them to the set here
FD_ZERO(&readfds); // 2. Set up the timeout.
// This example client has no other work it needs to be doing,
// so we set an effectively infinite timeout
timeout.tv_sec = 0x3FFFFFFF;
timeout.tv_usec = ; // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); // 4. Call select as normal
verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
result = select(nfds, &readfds, NULL, NULL, &timeout); if (result < )
{
verbosedebugf("select() returned %d errno %d", result, errno);
if (errno != EINTR) gStopNow = mDNStrue;
else
{
if (gReceivedSigUsr1)
{
gReceivedSigUsr1 = mDNSfalse;
gMDNSPlatformPosixVerboseLevel += ;
if (gMDNSPlatformPosixVerboseLevel > )
gMDNSPlatformPosixVerboseLevel = ;
if ( gMDNSPlatformPosixVerboseLevel > )
fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
}
if (gReceivedSigHup)
{
if (gMDNSPlatformPosixVerboseLevel > )
fprintf(stderr, "\nSIGHUP\n");
gReceivedSigHup = mDNSfalse;
DeregisterOurServices();
status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
if (status != mStatus_NoError) break;
status = RegisterOurServices();
if (status != mStatus_NoError) break;
}
}
}
else
{
// 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
mDNSPosixProcessFDSet(&mDNSStorage, &readfds); // 6. This example client has no other work it needs to be doing,
// but a real client would do its work here
// ... (do work) ...
}
} debugf("Exiting"); DeregisterOurServices();
mDNS_Close(&mDNSStorage); if (status == mStatus_NoError) {
result = ;
} else {
result = ;
}
if ( (result != ) || (gMDNSPlatformPosixVerboseLevel > ) ) {
fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result);
} return result;
}
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.-, Fedora Core ),
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 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: . The application calls mDNS_Init, which in turns calls the platform
(mDNSPlatformInit). . 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). . 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. . 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. . 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. () 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. .0a1 (Jul ) was a prerelease version that was distributed
internally at Apple. .0a2 (Jul ) was a prerelease version that was distributed
internally at Apple. .0a3 (Aug ) 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 Aug To Do List
----------
• port to a System V that's not Solaris
• use sig_atomic_t for signal to main thread flags
ReadMe.txt
网络协议之mDNS20170217的更多相关文章
- PYTHON黑帽编程1.5 使用WIRESHARK练习网络协议分析
Python黑帽编程1.5 使用Wireshark练习网络协议分析 1.5.0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks At ...
- 基础笔记(三):网络协议之Tcp、Http
目录 一.网络协议 二.TCP(Transmission Control Protocol,传输控制协议) TCP头格式 TCP协议中的三次握手和四次挥手 TCP报文抓取工具 三.HTTP(Hyper ...
- C# RFID windows 服务 网络协议方式
上篇话说是串口方式操作RFID设备. 下面介绍网络协议方式. 设备支持断线重连. 那我们的服务也不能差了不是. 所以这个服务类也是支持的哦. 不解释上代码: namespace Rfid { /// ...
- CCNA网络工程师学习进程(3)常规网络设计模型与基本的网络协议
本节介绍分层的网络设计模型与基本的网络协议,包括ARP协议,ICMP协议和IP协议. (1)三层网络架构: 一个好的园区网设计应该是一个分层的设计.一般分为接入层.汇聚层(分布层).核 ...
- 对TCP/IP网络协议的深入浅出归纳
前段时间做了一个开发,涉及到网络编程,开发过程比较顺利,但任务完成后始终觉得有一些疑惑.主要是因为对网络协议不太熟悉,对一些概念也没弄清楚.后来 我花了一些时间去了解这些网络协议,现在对TCP/IP网 ...
- linux网络协议
网络协议 本章节主要介绍linxu网络模型.以及常用的网络协议分析以太网协议.IP协议.TCP协议.UDP协议 一.网络模型 TCP/IP分层模型的四个协议层分别完成以下的功能: 第一层 网络接口层 ...
- CcTalk (网络协议)(转)
ccTalk (发音作"see-see-talk")是一种广泛使用的串行协议,遍及货币交易和销售时点情报系统行业.如硬币和纸币验钞机等外部设备在多元化的自动支付设备如交通,票务,投 ...
- iOS网络协议 HTTP/TCP/IP浅析
一.TCP/IP协议 话说两台电脑要通讯就必须遵守共同的规则,就好比两个人要沟通就必须使用共同的语言一样.一个只懂英语的人,和一个只懂中文的人由于没有共同的语言(规则)就没办法沟通.两台电 ...
- 转:对TCP/IP网络协议的深入浅出归纳
转自:http://blog.jobbole.com/74795/ 前段时间做了一个开发,涉及到网络编程,开发过程比较顺利,但任务完成后始终觉得有一些疑惑.主要是因为对网络协议不太熟悉,对一些概念也没 ...
随机推荐
- 原生WebGL场景中绘制多个圆锥圆柱
前几天解决了原生WebGL开发中的一个问题,就是在一个场景中绘制多个几何网格特征不同的模型,比如本文所做的绘制多个圆锥和圆柱在同一个场景中,今天抽空把解决的办法记录下来,同时也附上代码.首先声明,圆柱 ...
- [MYSQL]练习(一)
本文转载自:http://www.cnblogs.com/DreamDrive/p/6193530.html 我只是想做一个自己的运维知识库,所以迫不得已做了搬运工 建表 DROP TABLE DEP ...
- MongoDB开启权限认证
MongoDB默认安装完后,如果在配置文件中没有加上auth = true,是没有用户权限认证的,这样对于一个数据库来说是相对不安全的,尤其是在外网的情况下. 接下来是配置权限的过程: //切入到 ...
- Spring单元测试集成H2数据库
项目源代码在:Spring-H2测试 H2简介 H2数据库是一种由Java编写的,极小,速度极快,可嵌入式的数据库.非常适合用在单元测试等数据不需要保存的场景下面. 以下时其官网的介绍: {% blo ...
- MSCOCO - pycocoDemo 学习版
Reference: https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoDemo.ipynb https://git ...
- LeetCode 289. Game of Life (C++)
题目: According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a ce ...
- mininet实验 脚本实现控制交换机行为
写在前面 本文参考 通过这个实验,我学习到了另一种下流表的方式. 下流表有两种方式(我目前了解): 通过controller下发. 通过OvS提供的API直接向OvS交换机下流表. 本实验脚本已经把相 ...
- FivePlus——分工理解
最终的游戏方案 游戏采用回合制,每回合双方英雄各自轮流选择移动和攻击以及大招,选择结束进行结算 英雄/小兵/塔的攻击力/大招效果参照作业要求,如果发现不均衡再进行调整 UI界面考虑使用QT或者命令行界 ...
- java沙盒
JAVA的安全模型不同于传统的安全方法,传统的安全方法中,大多数操作系统允许应用程序充分访问系统资源,在操作系统不提供安全保护的机器里,运行环境不能被信任.为了弥补这个缺陷,安全策略经常要求在应用程序 ...
- JavaScript设计模式学习之路——面向对象的思想
今天,我拿到了张容铭写的这本<JavaScript设计模式>这本书,开始了关于JavaScript更深一点的学习. 看到这本书开始的时候,虽然之前通过看书.一些比较好的视频的讲解,对Jav ...