1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2026-05-01 00:07:19 +02:00

Update libraries and make it build on windows.

Still gets some warnings because compilers have changed. But should work.
This commit is contained in:
Sandu Liviu Catalin
2025-06-25 22:34:23 +03:00
parent 520a5eacc5
commit f2b7499f85
3038 changed files with 251668 additions and 273857 deletions
+208
View File
@@ -0,0 +1,208 @@
DNS Service Discovery and Zero Configuration Networking Overview
DNS-SD
!!!Introduction
A growing number of embedded devices are connected to TCP/IP networks.
And many of these devices are no longer passive network nodes, waiting
for someone else to control them. They are full-blown network citizens,
actively communicating with their peers and often relying on the network
services provided by other devices to do their job. For this to work,
all these devices must be configured properly. Configuring the network
parameters (e.g., IP address, netmask, etc.) of a device is a tedious task,
as many devices do not have an appropriate user interface to do this comfortably.
This is especially an issue with consumer devices, where the user might not even have the
necessary technical knowledge to configure such a device. And as the
number of devices in a network grows, configuring each device separately
is no longer practical. There from comes the need for the automatic
configuration of network devices and the automatic discovery of network
services. In recent years, the industry has come up with a variety of
different technologies and specifications to address this.
!!!Fundamental Issues
Generally, there are three fundamental issues that must be dealt with
for the automatic discovery of network devices and services, and three
more issues that must be dealt with for the actual invocation or use of
the discovered services.
!!Address Assignment
Every device must be assigned a unique network address. In case of
TCP/IP networks this can be done with the help of a DHCP (Dynamic Host
Configuration Protocol) server. Should, however, no DHCP server be
available (for example, in typical home user networks), another way of
assigning an IP addresses must be found. Apart from manual
configuration, which is often undesirable, a method called APIPA
(Automatic Private IP Addressing, or AutoIP for short) is used. In this
case, a device's TCP/IP stack randomly chooses an IP address in the
private (link-local) IP address range 169.254.0.0 to 169.254.255.255. To
prevent two or more devices from accidentally selecting the same address,
each device must probe, using the ARP (Address Resolution Protocol),
whether the chosen address is available.
!!Name Resolution
Whenever a user (or device) wants to access another device over the
network, he usually wants to refer to the device by its name, not by its
IP address. In a typical TCP/IP network, a DNS (Domain Name System)
server is used to map domain names to IP addresses. Again, if no DNS
server is available, such as in a typical home network, another way of
resolving names to IP addresses is required. Multicast DNS (mDNS), as
used by Zeroconf, is such an alternative technology.
!!Service Discovery
A user or device must be able to find a service provided by one or more
devices in the network. The important part here is that the user (or
device) usually does not care which device implements the service, as
long as the service with specific properties is available and
accessible. A typical example for service discovery is: <*I need to print
a document in color. Give me an IP address and port number where I can
send the print job to, using the Internet Printing Protocol (IPP), so
that the document will be printed in color.*>
What all technologies for service discovery have in common is, that they
make use of IP multicasting. IP multicasting uses addresses in a special
address range (224.0.0.0 to 239.255.255.255). A packet (typically, a UDP
packet) sent to a multicast address is received by all hosts listening
to that specific address.
Service discovery is implemented in the following way:
- An application or device that needs a certain service sends a request
describing the required properties of the service to a specific multicast
address (and port number).
- Other applications or devices on the same network receive the request,
and if they provide the requested service themselves (or know another device
that implements the service), respond with a message describing where the
service can be found.
- The application or device searching for the service collects all responses,
and from the responses chooses the one service provider it is going to use.
In addition, devices that join or leave a network can send announcements to other
devices describing the availability of the services they provide.
!!Service Description
Once a certain service has been discovered, it may be necessary to
obtain more information about the service. For example, if a service
consists of multiple operations, it is necessary to find out exactly
what operations are supported, and what arguments must be passed to
them. This is the purpose of service description.
In case only well-defined service protocols are used (e.g., HTTP,
Internet Printing, or media streaming), service description is not
necessary, because the only information needed to access the service is
the network address (IP address and port number, or URI), and this
information can be obtained by service discovery. In other cases, the
information obtained via service discovery is insufficient to
successfully access the service. In such a case, service discovery only
returns the address of a network resource that provides detailed
information about the capabilities of the service, and how to access
them.
!!Service Invocation
After an application has obtained enough information about the services
it wants to access -- either via service discovery alone, or together
with service description, the application will access or invoke them.
This is usually beyond the scope of most service discovery technologies,
and the domain of specialized technologies and protocols. Examples for
such technologies are HTTP (HyperText Transfer Protocol), SOAP,
Java RMI (Remote Method Invocation), CORBA (Common Object Request Broker
Architecture), or media streaming protocols such as RTSP
(Real Time Streaming Protocol).
!!Service Presentation
Some technologies (UPnP and Jini) provide facilities to present a device
specific user interface to the user, via another device (e.g., a central
management workstation, the user's PC or TV set). Such a user interface
can be used to allow the user to directly interact with a device, in
order to configure it, or to use some of its functions not available
otherwise.
This requires that the user interface is implemented in a device
independent way. One way to do this is to implement the user interface
in HTML (HyperText Markup Language), served by an embedded web server
built into the device. Another way is to implement the user interface in
Java, so that it can be run everywhere a Java Virtual Machine is
available. The user interface code than talks to the device over a
network connection, using a possibly proprietary protocol.
!!!Zero Configuration Networking (Zeroconf)
Zero Configuration Networking (Zeroconf) is a technology originally developed
by Apple and promoted under the trademark name Bonjour. Zeroconf is
built upon technologies known as multicast DNS (mDNS) and DNS
Service Discovery (DNS-SD), which themselves are based on the proven
DNS protocol. Its most popular uses are in network printers, network
cameras and for sharing music using Apple's iTunes music jukebox
software. Open source implementations are available from Apple and
others for all important platforms.
Zeroconf uses DHCP and AutoIP for address assignment if no DHCP server
is available. A special variant of the well-known DNS protocol,
multicast DNS, is used for name resolution in case no DNS server is
available. In mDNS, a name resolution query is sent not directly to a
DNS server, as with traditional DNS, but to a multicast address. All
network devices supporting Zeroconf and listening to this multicast
address respond to name resolution queries, if they have the requested
information.
Finally, for service discovery, an extension of the DNS protocol called
DNS Service Discovery is used. DNS-SD can be used both with multicast
DNS (the usual way), or with traditional unicast queries to a DNS
server. With the additional support for DNS Update and DNS Long Lived
Queries, two extensions of the DNS protocol, Zeroconf can be used to
announce services across the global internet. In this case, an ordinary
DNS server is used to make the service information available. Periodic
automatic updates of the DNS server's database ensure that its service
information is up to date.
A major advantage of Zeroconf is that it is based on proven technology.
Also, it can be implemented in a very resource efficient way, making it
a good choice for resource constrained embedded devices.
Beside heavy use by Apple in many of its applications for Mac OS X and
Windows (iTunes), Zeroconf is popular among manufacturers of printers
and network cameras.
!!Zeroconf Implementations
Two implementations of Zeroconf are currently in widespread use.
Apple's Bonjour was the first implementation of Zeroconf. The
implementation is available under an open source license and
can be used on different platforms like Mac OS X, Windows, Linux or
VxWorks. Bonjour is an integrated part of Mac OS X and iOS.
A Windows installer (and SDK) is available as well.
Avahi is another open source implementation of Zeroconf, targeted
mostly at Linux, although it can also be used on other POSIX platforms
like FreeBSD or Solaris. Avahi has been integrated into many
Linux distributions such as Debian, Ubuntu, SuSE and others.
!!The POCO DNS-SD Library
The POCO DNS-SD library provides an easy-to-use
and unified programming interface for integrating Zeroconf features
(service discovery and host name resolution) into a C++ application.
The library does not implement its own mDNS and DNS-SD protocol stacks,
but rather uses an existing Zeroconf implementation for that purpose.
Apple's Bonjour and Avahi can be used as backend for the DNS-SD library.
A great advantage of the library is that it provides a unified
programming interface. For the programmer, it's completely
transparent whether Avahi or Bonjour is used as backend.
@@ -0,0 +1,406 @@
DNS-SD Tuturial And User Guide
DNS-SD
!!!Introduction
The POCO DNS-SD POCO library provides an easy-to-use and
unified programming interface for integrating Zeroconf features
(service discovery and host name resolution) into a C++ application.
The Applied Informatics DNS-SD library does not
implement its own mDNS and DNS-SD protocol stacks, but rather uses
an existing Zeroconf implementation for that purpose. Apple's Bonjour
and Avahi can be used as backend for the DNS-SD library.
A great advantage of the library is that it provides a unified
programming interface. For the programmer, it's completely
transparent whether Avahi or Bonjour is used as backend.
!!!Programming Basics
The DNS-SD library provides an asynchronous programming interface.
This means that the application starts a browse or resolve operation
by calling a member function of the Poco::DNSSD::DNSSDBrowser class,
and this function returns immediately. The actual browse or resolve
operation (which involves sending queries over the network and
receiving responses from other hosts) is carried out in a separate
thread, and as soon as results become available, these are reported
to the application via events.
!!Event Handlers
The event handlers registered by the application should
complete their work and return as quick as possible, otherwise they may
interfere with DNS-SD processing. Event handlers should never
wait for other events to happen. Specifically, they must never
wait for an other DNSSDBrowser event to happen, as this will
result in a deadlock.
!!Service Types
Service types (or registration types) are the most important concept when
handling with DNS Service Discovery. A service type consists of a
short name (maximum of 15 characters) specifying the protocol implemented
by the service (prepended by an underscore), followed by a dot, followed
by a name identifying the primary transport protocol, which is either
"_tcp" or "_udp". Service types should be registered at <[dns-sd.org]>.
A list of currently registered service types, as well as information on how
to register a new service type can be found at the
[[http://www.dns-sd.org/ServiceTypes.html DNS-SD website]].
Examples for service types are "_daap._tcp" (Digital Audio Access Protocol,
the protocol used by iTunes music sharing), "_http._tcp" (web server)
or "_printer._tcp" for a printing service.
Service names are not case sensitive.
!!!Programming Tasks
In the following sections, the basic programming Tasks
that need to be performed when working with the DNS-SD library
are described.
!!Initializing the DNS-SD Library
The DNS-SD core library only defines the classes that are part
of the programming interfaces, it does not provide an actual
implementation of these interfaces. So, in addition to the DNS-SD
core library, a backend library must be linked with the application
as well. Depending on which backend library is used, either Apple's
Bonjour or Avahi will be used.
Before the DNS-SD library can be used it must be initialized.
This is done by calling the Poco::DNSSD::initializeDNSSD() function.
This function is actually defined implemented in a backend library,
so the backend libraries's header file must be included.
It is good practice to control which backend header is being
included via the preprocessor. For a cross-platform application,
one would use Avahi on Linux platforms and Bonjour on Mac OS X and
Windows platforms.
Typically, the <[#include]> statements for the DNS-SD library
would be as follows:
#include "Poco/DNSSD/DNSSDResponder.h"
#include "Poco/DNSSD/DNSSDBrowser.h"
#if POCO_OS == POCO_OS_LINUX && !defined(POCO_DNSSD_USE_BONJOUR)
#include "Poco/DNSSD/Avahi/Avahi.h"
#else
#include "Poco/DNSSD/Bonjour/Bonjour.h"
#endif
----
These statements will include the header files for the Poco::DNSSD::DNSSDResponder
and Poco::DNSSD::DNSSDBrowser classes, as well as the core header
file for a backend. Note that an application that only registers a service,
but does not browse for services, does not need to include the
<*Poco/DNSSD/DNSSDBrowser.h*> header file.
The Poco::DNSSD::initializeDNSSD() function must be called before an
instance of the Poco::DNSSD::DNSSDResponder class is created. If the
application uses the Poco::Util::Application class (or its server
counterpart), this can happen in the constructor of the application
subclass. It is also good practice to uninitialize the DNS-SD library
when the application exits by calling Poco::DNSSD::uninitializeDNSSD(),
which can be done in the application class destructor.
After initializing the DNS-SD library, the application should
create an instance of the Poco::DNSSD::DNSSDResponder class.
This class provides the main entry point into the DNS-SD library.
Although it is possible to create more than one instance of
the Poco::DNSSD::DNSSDResponder class, application programmers
should refrain from doing so. The Poco::DNSSD::DNSSDResponder
object should be kept alive during the entire lifetime of
the application, or at least as long as DNS-SD services
are required.
After the responder object has been created, its <[start()]> method
must be called. This will start a thread that handles all
DNS-SD related network activity for the application.
Similarly, when the application terminates, or when DNS-SD
services are no longer required, the <[stop()]> method should
be called to orderly shut-down the background thread.
!!Registering A Service
Registering a service, and thus making it discoverable to other
DNS-SD capable applications, is a two step process.
First, an instance of Poco::DNSSD::Service must be created
and properly initialized. At least the following information
must be specified:
- the service type (e.g., "_http._tcp"), and
- the port number of the service.
Other information can be specified, but defaults will be
used if not. This includes:
- The service name, which defaults to the local hosts's machine name.
- The network interface (by its interface index), on which the
service shall be announced. The default is to announce the
service on all interfaces (interface index is zero).
- The domain, on which the service will be announced.
- The domain name of the host providing the service.
- Service properties, which will be announced in the TXT
record of the service.
The service properties (basically, a list of key-value pairs)
can be used to provide additional information
necessary for invoking the service. These will be announced along
with the basic service information (host name, port number, etc.).
The content of the service properties is specific to the application
or network protocol the application uses.
When specifying service properties, a few restrictions must be
considered:
- The total length of a key-value pair must not exceed 254 bytes.
- The total length of all key-value pairs, including two additional
bytes for each pair, must not exceed 65535 bytes.
- The length of the key should not exceed nine bytes.
- Values can contain text strings or arbitrary binary values, and
can also be empty.
The following code shows how to register and publish a HTTP server
(running on port 8080) on a Zeroconf network:
Poco::DNSSD::DNSSDResponder dnssdResponder;
dnssdResponder.start();
Poco::DNSSD::Service service("_http._tcp", 8080);
Poco::DNSSD::ServiceHandle serviceHandle = dnssdResponder.registerService(service);
----
Another example, the following code shows how to register
and publish a network postscript-capable printer on a Zeroconf network.
Poco::DNSSD::DNSSDResponder dnssdResponder;
dnssdResponder.start();
Poco::DNSSD::Service::Properties props;
props.add("txtvers", "1");
props.add("pdl", "application/postscript");
props.add("qtotal", "1");
props.add("rp", "ThePrinter");
props.add("ty", "A Postscript Printer");
Poco::DNSSD::Service service(0, "The Printer", "", "_printer._tcp", "", "", 515, props);
Poco::DNSSD::ServiceHandle serviceHandle = dnssdResponder.registerService(service);
----
!Unregistering A Service
The <[registerService()]> method returns a Poco::DNSSD::ServiceHandle object.
This object is used to unregister the service when it's no longer available,
by passing it as argument to the <[unregisterService()]> method.
dnssdResponder.unregisterService(serviceHandle);
----
!Handling Registration Errors
The above code examples don't do a very good job of error handling.
Registration on the network may fail for various reasons.
The Poco::DNSSD::DNSSDResponder class provides two events that can be
used to check the status of a service registration.
If the registration was successful, the <[serviceRegistered]> event
will be fired. If registration failed, the <[serviceRegistrationFailed]>
event will be fired. Please see the class documentation for a
description of the available event arguments.
Usually, there's not much an application can do when service registration
fails. This is especially true for embedded devices, which often don't
even have a way to communicate this error to the user. However, an application
should at least log a registration error in a log file, to help with
error diagnostics.
Handling the <[serviceRegistered]> event is only necessary if the application
needs to know the actual service name used to announce the service.
In case of a name conflict (duplicate service names on the network), the
name specified when registering the service may have been changed by
the Bonjour or Avahi backend.
The following example shows an event handler (delegate) function for handling
registration errors.
void onError(const void* sender, const Poco::DNSSD::DNSSDResponder::ErrorEventArgs& args)
{
std::cerr
<< "Service registration failed: "
<< args.error.message()
<< " (" << args.error.code() << ")"
<< std::endl;
}
----
To register this function as delegate for the <[serviceRegistrationFailed]> event:
dnssdResponder.serviceRegistrationFailed += Poco::delegate(onError);
----
!!Browsing For Services
To discover available services of a specific type on the network, a browse operation
for a specific service type must be initiated. For this purpose, the
Poco::DNSSD::DNSSDBrowser class is used. An instance of this class can be obtained
from the Poco::DNSSD::DNSSDResponder object, by calling the <[browswer()]> method.
After a browse operation for a service type has been started, services becoming
available or unavailable will be reported via events. A service that has been
discovered will be reported via the <[serviceFound]> event.
If a service is no longer available, it will be reported via the <[serviceRemoved]>
event. The name, type and domain of the discovered service can be obtained from the
Poco::DNSSD::Service object passed as event argument.
The following sample shows how to write a delegate function for
the <[serviceFound]> event.
void onServiceFound(const void* sender, const Poco::DNSSD::DNSSDBrowser::ServiceEventArgs& args)
{
std::cout << "Service Found: \n"
<< " Name: " << args.service.name() << "\n"
<< " Domain: " << args.service.domain() << "\n"
<< " Type: " << args.service.type() << "\n"
<< " Interface: " << args.service.networkInterface() << "\n" << std::endl;
}
----
The next sample shows how to start a browse operation:
Poco::DNSSD::DNSSDResponder dnssdResponder;
dnssdResponder.start();
dnssdResponder.browser().serviceFound += Poco::delegate(onServiceFound);
Poco::DNSSD::BrowseHandle bh = dnssdResponder.browser().browse("_printer._tcp", "");
----
Poco::DNSSD::DNSSDBrowser::browse() returns a Poco::DNSSD::BrowseHandle object, which can
later be used to cancel a browse operation, by passing it to the <[cancel()]> method, as
shown in the following example:
dnssdResponder.browser().cancel(bh);
----
After a service has been discovered, the next step is resolving the service, to obtain
its host name, port number and properties, so that the service can be invoked.
!!Resolving A Service
Like browsing for services, resolving a service is an asynchronous operation.
A resolve operation is started with a call to <[resolve()]>, passing the
Poco::DNSSD::Service object obtained from the <[serviceFound]> event as
argument. Once the service has been resolved, the result is reported via
the <[serviceResolved]> event. The resolve operation can be started
directly from the <[serviceFound]> event handler, as shown in the
following sample:
void onServiceFound(const void* sender, const Poco::DNSSD::DNSSDBrowser::ServiceEventArgs& args)
{
std::cout << "Service Found: \n"
<< " Name: " << args.service.name() << "\n"
<< " Domain: " << args.service.domain() << "\n"
<< " Type: " << args.service.type() << "\n"
<< " Interface: " << args.service.networkInterface() << "\n" << std::endl;
reinterpret_cast<Poco::DNSSD::DNSSDBrowser*>(const_cast<void*>(sender))->resolve(args.service);
}
----
After a successful resolve, the service host name, port number and properties
are available through the Poco::DNSSD::Service object passed to the event handler,
as shown in the sample below:
void onServiceResolved(const void* sender, const Poco::DNSSD::DNSSDBrowser::ServiceEventArgs& args)
{
std::cout << "Service Resolved: \n"
<< " Name: " << args.service.name() << "\n"
<< " Full Name: " << args.service.fullName() << "\n"
<< " Domain: " << args.service.domain() << "\n"
<< " Type: " << args.service.type() << "\n"
<< " Interface: " << args.service.networkInterface() << "\n"
<< " Host: " << args.service.host() << "\n"
<< " Port: " << args.service.port() << "\n"
<< " Properties: \n";
for (Poco::DNSSD::Service::Properties::ConstIterator it = args.service.properties().begin(); it != args.service.properties().end(); ++it)
{
std::cout << " " << it->first << ": " << it->second << "\n";
}
std::cout << std::endl;
}
----
Of course, the event delegate for the <[serviceResolved]> event must be registered:
dnssdResponder.browser().serviceResolved += Poco::delegate(onServiceResolved);
----
!!Resolving A Service's Host Name
The last step necessary before invoking a service is to resolve the service's host name
into an IP address. On systems where mDNS is integrated into the DNS resolver (e.g.,
Mac OS X, Windows with Bonjour or most Linux distributions with Avahi), this
can simply be done by creating a Poco::Net::SocketAddress instance from the service's
host name and port number. However if the systems's DNS resolver cannot handle
Multicast DNS queries, the host name must be resolved through the
Poco::DNSSD::DNSSDBrowser::resolveHost() method. Like resolving a service, resolving
a host name is an asynchronous operation, and the result will be reported via
an event -- the <[hostResolved]> event.
The following sample shows how to implement the delegate function for the
<[hostResolved]> event:
void onHostResolved(const void* sender, const Poco::DNSSD::DNSSDBrowser::ResolveHostEventArgs& args)
{
std::cout << "Host Resolved: \n"
<< " Host: " << args.host << "\n"
<< " Interface: " << args.networkInterface << "\n"
<< " Address: " << args.address.toString() << "\n"
<< " TTL: " << args.ttl << "\n" << std::endl;
}
----
Like with resolving a service, it is possible to initiate resolving a host name directly
from within the event delegate for the <[serviceResolved]> event.
!!!Advanced Programming Tasks
!!Enumerating Domains
Available domains for browsing and registration can be enumerated by calling the
Poco::DNSSD::DNSSDBrowser::enumerateBrowseDomains() and
Poco::DNSSD::DNSSDBrowser::enumerateRegistrationDomains() methods. As usual,
results are reported via events.
!!Registering And Browsing For Records
Additional DNS records for a specific service can be published on a Zeroconf network
by invoking the Poco::DNSSD::DNSSDResponder::addRecord() method. It is also possible
to alter a published record, or remove it. Records can be queried by invoking
Poco::DNSSD::DNSSDBrowser::queryRecord(). Results are reported via
events.
!!Enumerating Available Service Types
It is possible to enumerate all available services types on a domain by
browsing for the special service type "_services._dns-sd._udp".
Results will be reported via the <[serviceDiscovered]> event.
The service type (without the primary transport protocol part,
as in "_http") can be obtained from the service name stored
in the Poco::DNSSD::Service object passed as event argument.
The primary transport protocol and the domain can be obtained
from the service type.