mirror of
https://github.com/moparisthebest/xeps
synced 2024-11-25 02:32:18 -05:00
1.1pre1
git-svn-id: file:///home/ksmith/gitmigration/svn/xmpp/trunk@1483 4b5297f7-1745-476d-ba37-a9c6900126ab
This commit is contained in:
parent
2464c28ba8
commit
8cef7f68e5
228
xep-0174.xml
228
xep-0174.xml
@ -26,6 +26,12 @@
|
|||||||
<shortname>linklocal</shortname>
|
<shortname>linklocal</shortname>
|
||||||
<registry/>
|
<registry/>
|
||||||
&stpeter;
|
&stpeter;
|
||||||
|
<revision>
|
||||||
|
<version>1.1pre1</version>
|
||||||
|
<date>in progress, last updated 2007-12-19</date>
|
||||||
|
<initials>psa</initials>
|
||||||
|
<remark><p>Corrected several DNS errors in the text and examples; added friendly How It Works section; updated to reflect version 1.5 of XEP-0115.</p></remark>
|
||||||
|
</revision>
|
||||||
<revision>
|
<revision>
|
||||||
<version>1.0</version>
|
<version>1.0</version>
|
||||||
<date>2007-06-12</date>
|
<date>2007-06-12</date>
|
||||||
@ -135,12 +141,85 @@
|
|||||||
<remark><p>First draft.</p></remark>
|
<remark><p>First draft.</p></remark>
|
||||||
</revision>
|
</revision>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section1 topic='Introduction' anchor='intro'>
|
<section1 topic='Introduction' anchor='intro'>
|
||||||
|
<section2 topic='Motivation' anchor='motivation'>
|
||||||
<p>XMPP as defined in &rfc3920; does not support direct client-to-client interactions, since it requires authentication with a server: an XMPP client is allowed access to the network only once it has authenticated with a server, and the server must not grant access if authentication fails for any reason. If an unauthenticated client attempts to communicate directly with another client, such communication will fail because all XMPP communications are sent through one or more servers and a client cannot inject messages onto the network without first authenticating with a server.</p>
|
<p>XMPP as defined in &rfc3920; does not support direct client-to-client interactions, since it requires authentication with a server: an XMPP client is allowed access to the network only once it has authenticated with a server, and the server must not grant access if authentication fails for any reason. If an unauthenticated client attempts to communicate directly with another client, such communication will fail because all XMPP communications are sent through one or more servers and a client cannot inject messages onto the network without first authenticating with a server.</p>
|
||||||
<p>However, it is possible to establish an XMPP-like communications system on a local network using zero-configuration networking. In this situation, the clients obviate the XMPP requirement for authentication with a server by relying on zero-configuration networking to establish link-local communication using the _presence._tcp DNS SRV service type. Once discovery has been completed, the clients are then able to exchange messages and other structured data using the XMPP &MESSAGE; and &IQ; stanzas.</p>
|
<p>However, it is possible to establish an XMPP-like communications system on a local network using zero-configuration networking. In this situation, the clients obviate the XMPP requirement for authentication with a server by relying on zero-configuration networking to establish link-local communication using the _presence._tcp DNS SRV service type. Once discovery has been completed, the clients are then able to exchange messages and other structured data using the XMPP &MESSAGE; and &IQ; stanzas.</p>
|
||||||
<p>Link-local messaging is restricted to the local network because of how zero-configuration networking works. It is impossible for clients that communicate via link-local addresses to insert messages into an XMPP network, which is why this kind of local "mesh" is most accurately referred to as an XMPP-like system that exists outside the context of existing XMPP networks (though see the <link url='#security'>Security Considerations</link> regarding the ability to "forward" messages from a local mesh to an XMPP network or vice-versa).</p>
|
<p>Link-local messaging is restricted to the local network because of how zero-configuration networking works. It is impossible for clients that communicate via link-local addresses to insert messages into an XMPP network, which is why this kind of local "mesh" is most accurately referred to as an XMPP-like system that exists outside the context of existing XMPP networks (though see the <link url='#security'>Security Considerations</link> regarding the ability to "forward" messages from a local mesh to an XMPP network or vice-versa).</p>
|
||||||
<p>Such a local "mesh" can be quite valuable in certain circumstances. For instance, participants in a trade show or conference, users of the same WiFi hotspot, or employees on the same local area network can communicate without the need for a pre-configured server. For this reason, support for link-local messaging has been a feature of Apple's iChat client when operating in Bonjour (formerly Rendezvous) mode. Because it is desirable for other Jabber clients to support such functionality, this document describes how to use zero-configuration networking as the basis for link-local communication.</p>
|
<p>Such a local "mesh" can be quite valuable in certain circumstances. For instance, participants in a trade show or conference, users of the same WiFi hotspot, or employees on the same local area network can communicate without the need for a pre-configured server. For this reason, support for link-local messaging has been a feature of Apple's iChat client when operating in Bonjour (formerly Rendezvous) mode. Because it is desirable for other Jabber clients to support such functionality, this document describes how to use zero-configuration networking as the basis for link-local communication.</p>
|
||||||
|
</section2>
|
||||||
|
<section2 topic='How It Works' anchor='howitworks'>
|
||||||
|
<p>This section provides a friendly introduction to link-local messaging.</p>
|
||||||
|
<p>Imagine that you are a Shakespearean character named Juliet. You are are using your laptop computer (a machine named "pronto") at a wifi hotspot in downtown Verona and you want to find other people to chat with on an ad-hoc basis (i.e., not people in your normal XMPP roster). Therefore your chat client advertises a link-local address of "juliet@pronto" so that other people can dynamically find you at the hotspot. Your client does this by invoking a daemon on your machine that supports DNS-based Service Discovery ("DNS-SD") as defined in &dnssd; and Multicast DNS ("mDNS") as defined in &mdns;. As a result, the daemon stores the following DNS records and listens for multicast DNS queries asking for them:</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
_presence._tcp.local. PTR juliet@pronto._presence._tcp.local.
|
||||||
|
|
||||||
|
juliet@pronto._presence._tcp.local. SRV 5562 pronto.local.
|
||||||
|
|
||||||
|
pronto.local. A 10.2.1.187
|
||||||
|
]]></code>
|
||||||
|
<p>The meaning of these records is as follows:</p>
|
||||||
|
<ul>
|
||||||
|
<li>The PTR ("pointer") record (see &rfc2317; and &rfc1886;) says that there is a service of type "presence" on the local subnet (".local.") called "juliet@pronto" and that the service communicates over TCP.</li>
|
||||||
|
<li>The SRV record (see &rfc2782;) maps the service instance "juliet@pronto" to the machine "pronto.local." on port 5562.</li>
|
||||||
|
<li>The A record specifies the IP address 10.2.1.187 at which the "pronto" machine will listen for connections.</li>
|
||||||
|
</ul>
|
||||||
|
<p>Your chat client also wants to advertise some information about you (subject to your control so that you don't divulge private information), so it invokes the mDNS daemon to also stores some DNS TXT records (see &rfc1464;):</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
juliet IN TXT "txtvers=1"
|
||||||
|
juliet IN TXT "1st=Juliet"
|
||||||
|
juliet IN TXT "email=juliet@capulet.lit"
|
||||||
|
juliet IN TXT "hash=sha-1"
|
||||||
|
juliet IN TXT "jid=juliet@capulet.lit"
|
||||||
|
juliet IN TXT "last=Capulet"
|
||||||
|
juliet IN TXT "msg=Hanging out downtown"
|
||||||
|
juliet IN TXT "nick=JuliC"
|
||||||
|
juliet IN TXT "node=http://www.adiumx.com"
|
||||||
|
juliet IN TXT "phsh=a3839614e1a382bcfebbcf20464f519e81770813"
|
||||||
|
juliet IN TXT "port.p2pj=5562"
|
||||||
|
juliet IN TXT "status=avail"
|
||||||
|
juliet IN TXT "v=1.1.4"
|
||||||
|
juliet IN TXT "vc=CA!"
|
||||||
|
juliet IN TXT "ver=66/0NaeaBKkwk85efJTGmU47vXI="
|
||||||
|
]]></code>
|
||||||
|
<p>Other people at the hotspot may also advertise similar DNS records for use on the local link. Essentially, the mDNS daemons running on all of the machines at the hotspot collectively manage the ".local." domain, which has meaning only at the hotspot (not across the broader Internet). Queries and responses for services on the local link occur via link-local multicast over UDP port 5353 instead of via normal DNS unicast over UDP port 53. When a new machine joins the local link, it can send out queries for any number of service types, to which the other machines will reply. For the purpose of link-local messaging we are interested only in the "presence" service, but many other services could exist on the local link.</p>
|
||||||
|
<p>Now let us imagine that a fine young gentleman named Romeo joins the hotspot and that his chat client (actually his mDNS daemon) sends out link-local multicast queries for link-local services of type "presence" (i.e., PTR records that match "_presence._tcp.local.", followed by appropriate SRV, A, and TXT record queries to discover detailed information about those services). His client will then discover (among others) a service named "juliet@pronto" at IP address 10.2.1.187 and port 5562, with some intriguing TXT records. Being a romantic fellow, he then initiates a chat with you by opening an XML stream to the advertised IP address and port.</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
<stream:stream
|
||||||
|
xmlns='jabber:client'
|
||||||
|
xmlns:stream='http://etherx.jabber.org/streams'
|
||||||
|
from='juliet@pronto'
|
||||||
|
to='romeo@forza'
|
||||||
|
version='1.0'>
|
||||||
|
]]></code>
|
||||||
|
<p>Your client then responds with a response stream header (perhaps subject to user approval -- it's not always safe to chat with strangers!).</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
<stream:stream
|
||||||
|
xmlns='jabber:client'
|
||||||
|
xmlns:stream='http://etherx.jabber.org/streams'
|
||||||
|
from='romeo@forza'
|
||||||
|
to='juliet@pronto'
|
||||||
|
version='1.0'>
|
||||||
|
]]></code>
|
||||||
|
<p>Romeo then sends you an XMPP message.</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
<message to='romeo@forza' from='juliet@pronto'>
|
||||||
|
<body>Hey, just saying hi!</body>
|
||||||
|
</message>
|
||||||
|
]]></code>
|
||||||
|
<p>You chat with Romeo for a while, then your client closes the stream.</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
</stream:stream>
|
||||||
|
]]></code>
|
||||||
|
<p>And Romeo's client does the same.</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
</stream:stream>
|
||||||
|
]]></code>
|
||||||
|
<p>Finally you decides to head home so your mDNS daemon sends a Multicast DNS "Goodbye" packet for the your PTR record. As a result, everyone else at the hotspot receives a Multicast DNS "Remove" event, at which point they cancel any outstanding A, SRV, TXT, or NULL record queries related to your presence service.</p>
|
||||||
|
</section2>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Glossary' anchor='glossary'>
|
<section1 topic='Glossary' anchor='glossary'>
|
||||||
<table caption='Terminology'>
|
<table caption='Terminology'>
|
||||||
<tr>
|
<tr>
|
||||||
@ -153,7 +232,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>DNS-SD</td>
|
<td>DNS-SD</td>
|
||||||
<td>A convention for naming and structuring DNS SRV records such that a client can dynamically discover a domain for a service using only standard DNS queries. See &dnssd;. For a full list of registered DNS-SD records, see <<link url='http://www.dns-sd.org/ServiceTypes.html'>http://www.dns-sd.org/ServiceTypes.html</link>>.</td>
|
<td>A convention for naming and structuring DNS SRV records such that a client can dynamically discover a domain for a service using only standard DNS queries. See <cite>draft-cheshire-dnsext-dns-sd</cite>. For a full list of registered DNS-SD records, see <<link url='http://www.dns-sd.org/ServiceTypes.html'>http://www.dns-sd.org/ServiceTypes.html</link>>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Link-local address</td>
|
<td>Link-local address</td>
|
||||||
@ -161,7 +240,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Multicast DNS (mDNS)</td>
|
<td>Multicast DNS (mDNS)</td>
|
||||||
<td>A technology that provides the ability to perform DNS-like operations on a local link in the absence of any conventional unicast DNS server. See &mdns;.</td>
|
<td>A technology that provides the ability to perform DNS-like operations on a local link in the absence of any conventional unicast DNS server. See <cite>draft-cheshire-dnsext-multicastdns</cite>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Zero-configuration networking</td>
|
<td>Zero-configuration networking</td>
|
||||||
@ -169,9 +248,16 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='DNS Records' anchor='dns'>
|
<section1 topic='DNS Records' anchor='dns'>
|
||||||
<p>In order to advertise its availability for link-local messaging, a client MUST publish four different kinds of DNS records:</p>
|
<p>In order to advertise its availability for link-local messaging, a client MUST publish four different kinds of DNS records:</p>
|
||||||
<ol>
|
<ol>
|
||||||
|
<li>
|
||||||
|
<p>A PTR record of the following form:</p>
|
||||||
|
<code><![CDATA[
|
||||||
|
_presence._tcp.local. PTR username@machine-name._presence._tcp.local.
|
||||||
|
]]></code>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>An address ("A" or "AAAA") record of the following form: <note>The IP address MAY be either an IPv4 address or an IPv6 address.</note></p>
|
<p>An address ("A" or "AAAA") record of the following form: <note>The IP address MAY be either an IPv4 address or an IPv6 address.</note></p>
|
||||||
<code><![CDATA[
|
<code><![CDATA[
|
||||||
@ -179,23 +265,17 @@ machine-name.local. A ip-address
|
|||||||
]]></code>
|
]]></code>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>An "SRV" record (see &rfc2782;) of the following form:</p>
|
<p>An SRV record of the following form:</p>
|
||||||
<code><![CDATA[
|
<code><![CDATA[
|
||||||
_presence._tcp <ttl> IN SRV <priority> <weight> port-number username@machine-name.local.
|
_presence._tcp <ttl> SRV <priority> <weight> port-number machine-name.local.
|
||||||
]]></code>
|
]]></code>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>A "PTR" record (see &rfc2317; and &rfc1886;) of the following form:</p>
|
<p>Optionally, various TXT records of the following form, as further described in the <link url='#txt'>TXT Records</link> section of this document:</p>
|
||||||
<code><![CDATA[
|
|
||||||
_presence._tcp.local. port-number IN PTR username@machine-name._presence._tcp.local.
|
|
||||||
]]></code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>Optionally, various "TXT" records (see &rfc1464;) of the following form, as further described in to the <link url='#txt'>TXT Records</link> section of this document:</p>
|
|
||||||
<code><![CDATA[
|
<code><![CDATA[
|
||||||
|
<owner> IN <ttl> TXT "txtvers=1"
|
||||||
<owner> IN <ttl> TXT "1st=user-first-name"
|
<owner> IN <ttl> TXT "1st=user-first-name"
|
||||||
<owner> IN <ttl> TXT "email=user-email-address"
|
<owner> IN <ttl> TXT "email=user-email-address"
|
||||||
<owner> IN <ttl> TXT "ext=optional-list-of-extensions"
|
|
||||||
<owner> IN <ttl> TXT "jid=user-jabber-id"
|
<owner> IN <ttl> TXT "jid=user-jabber-id"
|
||||||
<owner> IN <ttl> TXT "last=user-last-name"
|
<owner> IN <ttl> TXT "last=user-last-name"
|
||||||
<owner> IN <ttl> TXT "msg=freeform-availability-status"
|
<owner> IN <ttl> TXT "msg=freeform-availability-status"
|
||||||
@ -204,40 +284,41 @@ _presence._tcp.local. port-number IN PTR username@machine-name._presence._tcp.lo
|
|||||||
<owner> IN <ttl> TXT "phsh=sha1-hash-of-avatar"
|
<owner> IN <ttl> TXT "phsh=sha1-hash-of-avatar"
|
||||||
<owner> IN <ttl> TXT "port.p2pj=5562"
|
<owner> IN <ttl> TXT "port.p2pj=5562"
|
||||||
<owner> IN <ttl> TXT "status=avail-away-or-dnd"
|
<owner> IN <ttl> TXT "status=avail-away-or-dnd"
|
||||||
<owner> IN <ttl> TXT "txtvers=1"
|
|
||||||
<owner> IN <ttl> TXT "vc=capabilities-string"
|
<owner> IN <ttl> TXT "vc=capabilities-string"
|
||||||
<owner> IN <ttl> TXT "ver=application-version"
|
<owner> IN <ttl> TXT "ver=application-version"
|
||||||
]]></code>
|
]]></code>
|
||||||
|
<p>Note: In accordance with Section 6.7 of <cite>draft-cheshire-dnsext-dns-sd</cite>, the "txtvers" record should be the first record specified.</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>The "machine-name" is the name of the computer, the "username" is the system username of the principal currently logged into the computer, the "port" may be any unassigned port number, and the "ip-address" is the physical address of the computer on the local network.</p>
|
<p>The "machine-name" is the name of the computer, the "username" is the system username of the principal currently logged into the computer, the "port" may be any unassigned port number, and the "ip-address" is the physical address of the computer on the local network.</p>
|
||||||
<p>So, for example, if the machine name is "roundabout", the username is "stpeter", the chosen port is "5562", the IP address is "10.2.1.187", and the personal information is that associated with the author of this document, the DNS records would be as follows:</p>
|
<p>So, for example, if the machine name is "pronto", the username is "juliet", the chosen port is "5562", the IP address is "10.2.1.187", and the personal information is that associated with the author of this document, the DNS records would be as follows:</p>
|
||||||
<code><![CDATA[
|
<code><![CDATA[
|
||||||
roundabout.local. A 10.2.1.187
|
_presence._tcp.local. PTR juliet@pronto._presence._tcp.local.
|
||||||
|
|
||||||
_presence._tcp IN SRV 5562 stpeter@roundabout.local.
|
juliet@pronto._presence._tcp.local. SRV 5562 pronto.local.
|
||||||
|
|
||||||
_presence._tcp.local. 5562 IN PTR stpeter@roundabout._presence._tcp.local.
|
pronto.local. A 10.2.1.187
|
||||||
|
|
||||||
psa IN TXT "1st=Peter"
|
juliet IN TXT "txtvers=1"
|
||||||
psa IN TXT "email=stpeter@jabber.org"
|
juliet IN TXT "1st=Juliet"
|
||||||
psa IN TXT "ext=rcd sgc auxvideo sgs mvideo avavail avcap maudio"
|
juliet IN TXT "email=juliet@capulet.lit"
|
||||||
psa IN TXT "jid=stpeter@jabber.org"
|
juliet IN TXT "hash=sha-1"
|
||||||
psa IN TXT "last=Saint-Andre"
|
juliet IN TXT "jid=juliet@capulet.lit"
|
||||||
psa IN TXT "msg=IETF 66 Montreal"
|
juliet IN TXT "last=Capulet"
|
||||||
psa IN TXT "nick=stpeter"
|
juliet IN TXT "msg=Hanging out downtown"
|
||||||
psa IN TXT "node=http://www.apple.com/ichat/caps"
|
juliet IN TXT "nick=JuliC"
|
||||||
psa IN TXT "phsh=a3839614e1a382bcfebbcf20464f519e81770813"
|
juliet IN TXT "node=http://www.adiumx.com"
|
||||||
psa IN TXT "port.p2pj=5562"
|
juliet IN TXT "phsh=a3839614e1a382bcfebbcf20464f519e81770813"
|
||||||
psa IN TXT "status=avail"
|
juliet IN TXT "port.p2pj=5562"
|
||||||
psa IN TXT "txtvers=1"
|
juliet IN TXT "status=avail"
|
||||||
psa IN TXT "vc=CA!"
|
juliet IN TXT "v=1.1.4"
|
||||||
psa IN TXT "ver=524"
|
juliet IN TXT "vc=CA!"
|
||||||
|
juliet IN TXT "ver=66/0NaeaBKkwk85efJTGmU47vXI="
|
||||||
|
|
||||||
]]></code>
|
]]></code>
|
||||||
<p>The IPv4 and IPv6 addresses associated with a machine may vary depending on the local network to which the machine is connected. For example, on an Ethernet connection the physical address might be "10.2.1.187" but when the machine is connected to a wireless network, its physical address might change to "10.10.1.179".</p>
|
<p>The IPv4 and IPv6 addresses associated with a machine may vary depending on the local network to which the machine is connected. For example, on an Ethernet connection the physical address might be "192.168.0.100" but when the machine is connected to a wireless network the physical address might change to "10.10.1.187".</p>
|
||||||
<p>If the machine name asserted by a client is already taken by another machine on the network, the client MUST assert a different machine name, which SHOULD be formed by adding the character "-" and digit "1" to the end of the machine name string (e.g., "roundabout-1"), adding the character "-" and digit "2" if the resulting machine name is already taken (e.g., "roundabout-2"), and similarly incrementing the digit until a unique machine name is constructed.</p>
|
<p>If the machine name asserted by a client is already taken by another machine on the network, the client MUST assert a different machine name, which SHOULD be formed by adding the character "-" and digit "1" to the end of the machine name string (e.g., "pronto-1"), adding the character "-" and digit "2" if the resulting machine name is already taken (e.g., "pronto-2"), and similarly incrementing the digit until a unique machine name is constructed.</p>
|
||||||
<p>If the username asserted by a client is already taken by another application on the machine, the client MUST assert a different username, which SHOULD be formed by adding the character "-" and digit "1" to the end of the username string (e.g., "stpeter-1"), adding the character "-" and digit "2" if the resulting username is already taken (e.g., "stpeter-2"), and similarly incrementing the digit until a unique username is constructed.</p>
|
<p>If the username asserted by a client is already taken by another application on the machine, the client MUST assert a different username, which SHOULD be formed by adding the character "-" and digit "1" to the end of the username string (e.g., "juliet-1"), adding the character "-" and digit "2" if the resulting username is already taken (e.g., "juliet-2"), and similarly incrementing the digit until a unique username is constructed.</p>
|
||||||
<section2 topic='TXT Records' anchor='txt'>
|
<section2 topic='TXT Records' anchor='txt'>
|
||||||
<p>DNS-SD enables service definitions to include various TXT records that specify parameters to be used in the context of the relevant service type. The ®ISTRAR; shall maintain a registry of TXT records for use with the _presence._tcp service type, as specified in the <link url='#registrar'>XMPP Registrar Considerations</link> section of this document.</p>
|
<p>DNS-SD enables service definitions to include various TXT records that specify parameters to be used in the context of the relevant service type. The ®ISTRAR; shall maintain a registry of TXT records for use with the _presence._tcp service type, as specified in the <link url='#registrar'>XMPP Registrar Considerations</link> section of this document.</p>
|
||||||
<p>It is OPTIONAL to include any of these TXT records, and an implementation MUST NOT fail (i.e., MUST enable link-local messaging) even if none of the TXT records are provided by another local entity.</p>
|
<p>It is OPTIONAL to include any of these TXT records, and an implementation MUST NOT fail (i.e., MUST enable link-local messaging) even if none of the TXT records are provided by another local entity.</p>
|
||||||
@ -245,15 +326,19 @@ psa IN TXT "ver=524"
|
|||||||
<p>Note: See the <link url='#security'>Security Considerations</link> section of this document regarding the inclusion of information that may have an impact on personal privacy (e.g., the "1st", "last", "nick", "email", and "jid" records).</p>
|
<p>Note: See the <link url='#security'>Security Considerations</link> section of this document regarding the inclusion of information that may have an impact on personal privacy (e.g., the "1st", "last", "nick", "email", and "jid" records).</p>
|
||||||
</section2>
|
</section2>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Discovering Other Users' anchor='disco'>
|
<section1 topic='Discovering Other Users' anchor='disco'>
|
||||||
<p>In order to discover other users, a client sends an mDNS request for PTR records that match "_presence._tcp.local.". The client then receives replies from all machines that advertise support for link-local messaging. <note>The replies will include a record corresponding the client itself; the client MUST filter out this result.</note> The client MAY then find out detailed information about each machine by sending SRV and TXT queries to "username@machine-name._presence._tcp.local." for each machine (however, to preserve bandwidth, the client SHOULD NOT send these queries unless it is about to initiate communications with the other user, and it MUST cancel the queries after it has received a response). Note: The presence name to be used for display in a link-local "roster" SHOULD be obtained from the <Instance> portion of the received PTR record for each user; however, the client MAY instead display a name or nickname derived from the TXT records if available.</p>
|
<p>In order to discover other users, a client sends an mDNS request for PTR records that match "_presence._tcp.local.". The client then receives replies from all machines that advertise support for link-local messaging. <note>The replies will include a record corresponding the client itself; the client MUST filter out this result.</note> The client MAY then find out detailed information about each machine by sending SRV and TXT queries to "machine-name.local." for each machine (however, to preserve bandwidth, the client SHOULD NOT send these queries unless it is about to initiate communications with the other user, and it MUST cancel the queries after it has received a response). Note: The presence name to be used for display in a link-local "roster" SHOULD be obtained from the <Instance> portion of the received PTR record for each user; however, the client MAY instead display a name or nickname derived from the TXT records if available.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Exchanging Presence' anchor='presence'>
|
<section1 topic='Exchanging Presence' anchor='presence'>
|
||||||
<p>When the _presence._tcp service is used, presence is exchanged via the format described in the <link url='#txt'>TXT Records</link> section of this document. In particular, presence information is not pushed as in XMPP (see &rfc3921;). Instead, clients listen for presence announcements from other local entities. Recommended rates for sending updates can be found in <cite>Multicast DNS</cite>.</p>
|
<p>When the _presence._tcp service is used, presence is exchanged via the format described in the <link url='#txt'>TXT Records</link> section of this document. In particular, presence information is not pushed as in XMPP (see &rfc3921;). Instead, clients listen for presence announcements from other local entities. Recommended rates for sending updates can be found in <cite>Multicast DNS</cite>.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Discovering Capabilities' anchor='caps'>
|
<section1 topic='Discovering Capabilities' anchor='caps'>
|
||||||
<p>Because link-local communication does not involve the exchange of XMPP presence, it is not possible to use &xep0115; for capabilities discovery. Therefore, it is RECOMMENDED to instead include the node and ver TXT records (and OPTIONAL to include the ext TXT record). The values of these records MUST be the same as the values for the 'node', 'ver', and 'ext' attributes that are advertised for the application in XMPP presence (if any) via the Entity Capabilities protocol as described in <cite>XEP-0115</cite>.</p>
|
<p>Because link-local communication does not involve the exchange of XMPP presence, it is not possible to use &xep0115; for capabilities discovery. Therefore, it is RECOMMENDED to instead include the node, hash, and ver TXT records (and OPTIONAL to include the ext and v TXT records). The values of these records MUST be the same as the values for the 'node', 'hash', 'ver', 'ext', and 'v' attributes that are advertised for the application in normal XMPP presence (if any) via the Entity Capabilities protocol as described in <cite>XEP-0115</cite>.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Initiating a Messaging Session' anchor='initiate'>
|
<section1 topic='Initiating a Messaging Session' anchor='initiate'>
|
||||||
<p>In order to exchange link-local messages, the initiator and recipient MUST first establish XML streams between themselves, as is familiar from <cite>RFC 3920</cite>.</p>
|
<p>In order to exchange link-local messages, the initiator and recipient MUST first establish XML streams between themselves, as is familiar from <cite>RFC 3920</cite>.</p>
|
||||||
<p>First, the initiator opens a TCP connection at the IP address and port discovered via the DNS lookup for a local entity and opens an XML stream to the recipient, which SHOULD include 'to' and 'from' address:</p>
|
<p>First, the initiator opens a TCP connection at the IP address and port discovered via the DNS lookup for a local entity and opens an XML stream to the recipient, which SHOULD include 'to' and 'from' address:</p>
|
||||||
@ -261,8 +346,8 @@ psa IN TXT "ver=524"
|
|||||||
<stream:stream
|
<stream:stream
|
||||||
xmlns='jabber:client'
|
xmlns='jabber:client'
|
||||||
xmlns:stream='http://etherx.jabber.org/streams'
|
xmlns:stream='http://etherx.jabber.org/streams'
|
||||||
from='stpeter@roundabout'
|
from='juliet@pronto'
|
||||||
to='hildjj@wolfram'
|
to='romeo@forza'
|
||||||
version='1.0'>
|
version='1.0'>
|
||||||
]]></example>
|
]]></example>
|
||||||
<p>Note: If the initiator supports stream features and the other stream-related aspects of XMPP 1.0 as specified in <cite>RFC 3920</cite>, then it SHOULD include the version='1.0' flag as shown in the previous example.</p>
|
<p>Note: If the initiator supports stream features and the other stream-related aspects of XMPP 1.0 as specified in <cite>RFC 3920</cite>, then it SHOULD include the version='1.0' flag as shown in the previous example.</p>
|
||||||
@ -271,8 +356,8 @@ psa IN TXT "ver=524"
|
|||||||
<stream:stream
|
<stream:stream
|
||||||
xmlns='jabber:client'
|
xmlns='jabber:client'
|
||||||
xmlns:stream='http://etherx.jabber.org/streams'
|
xmlns:stream='http://etherx.jabber.org/streams'
|
||||||
from='hildjj@wolfram'
|
from='romeo@forza'
|
||||||
to='stpeter@roundabout'
|
to='juliet@pronto'
|
||||||
version='1.0'>
|
version='1.0'>
|
||||||
]]></example>
|
]]></example>
|
||||||
<p>If both the initiator and recipient included the version='1.0' flag, the recipient SHOULD also send stream features as specified in <cite>RFC 3920</cite>:</p>
|
<p>If both the initiator and recipient included the version='1.0' flag, the recipient SHOULD also send stream features as specified in <cite>RFC 3920</cite>:</p>
|
||||||
@ -283,14 +368,16 @@ psa IN TXT "ver=524"
|
|||||||
]]></example>
|
]]></example>
|
||||||
<p>The exchange of stream headers results in an unencrypted and unauthenticated channel between the two entities. See the <link url='#security'>Security Considerations</link> section of this document regarding methods for authenticating and encrypting the stream.</p>
|
<p>The exchange of stream headers results in an unencrypted and unauthenticated channel between the two entities. See the <link url='#security'>Security Considerations</link> section of this document regarding methods for authenticating and encrypting the stream.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Exchanging Messages' anchor='exchange'>
|
<section1 topic='Exchanging Messages' anchor='exchange'>
|
||||||
<p>Once the streams are established, either entity then can send XMPP message or IQ stanzas by specifying 'to' and 'from' addresses using the logical local addresses: <note>The to and from addresses MUST be of the form "username@machine-name" as discovered via SRV (this is the <Instance> portion of the Service Instance Name).</note></p>
|
<p>Once the streams are established, either entity then can send XMPP message or IQ stanzas by specifying 'to' and 'from' addresses using the logical local addresses: <note>The to and from addresses MUST be of the form "username@machine-name" as discovered via SRV (this is the <Instance> portion of the Service Instance Name).</note></p>
|
||||||
<example caption="Sending a Message"><![CDATA[
|
<example caption="Sending a Message"><![CDATA[
|
||||||
<message to='hildjj@wolfram' from='stpeter@roundabout'>
|
<message to='romeo@forza' from='juliet@pronto'>
|
||||||
<body>Hey, I'm testing out link-local messaging!</body>
|
<body>Hey, just saying hi!</body>
|
||||||
</message>
|
</message>
|
||||||
]]></example>
|
]]></example>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Ending a Messaging Session' anchor='end'>
|
<section1 topic='Ending a Messaging Session' anchor='end'>
|
||||||
<p>To end the chat, either party closes the XML stream:</p>
|
<p>To end the chat, either party closes the XML stream:</p>
|
||||||
<example caption="Ending the Chat"><![CDATA[
|
<example caption="Ending the Chat"><![CDATA[
|
||||||
@ -302,9 +389,11 @@ psa IN TXT "ver=524"
|
|||||||
]]></example>
|
]]></example>
|
||||||
<p>Both parties then MUST close the TCP connection between them.</p>
|
<p>Both parties then MUST close the TCP connection between them.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Going Offline' anchor='offline'>
|
<section1 topic='Going Offline' anchor='offline'>
|
||||||
<p>In order to go offline, a client MUST send a Multicast DNS "Goodbye" packet for the user's PTR record. As a result, all other entities on the local network will receive a Multicast DNS "Remove" event, at which point they MUST cancel any outstanding TXT, SRV, or NULL record queries for the offline user.</p>
|
<p>In order to go offline, a client MUST send a Multicast DNS "Goodbye" packet for the user's PTR record. As a result, all other entities on the local network will receive a Multicast DNS "Remove" event, at which point they MUST cancel any outstanding TXT, SRV, or NULL record queries for the offline user.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Implementation Notes' anchor='impl'>
|
<section1 topic='Implementation Notes' anchor='impl'>
|
||||||
<section2 topic='Multiple Network Interfaces' anchor='impl-network'>
|
<section2 topic='Multiple Network Interfaces' anchor='impl-network'>
|
||||||
<p>Devices that use link-local messaging may have multiple network interfaces. As a result, it is possible to discover the same entity multiple times. Even if a client discovers the same presence name on multiple network interfaces, it MUST show only one entity in the local roster. In addition, because local IP addresses can be dynamically re-assigned, the client SHOULD NOT store the IP address to be used for communications when it discovers that address in the initial DNS lookup phase; instead, it SHOULD delay sending the Multicast DNS query until the client is ready to communicate with the other entity.</p>
|
<p>Devices that use link-local messaging may have multiple network interfaces. As a result, it is possible to discover the same entity multiple times. Even if a client discovers the same presence name on multiple network interfaces, it MUST show only one entity in the local roster. In addition, because local IP addresses can be dynamically re-assigned, the client SHOULD NOT store the IP address to be used for communications when it discovers that address in the initial DNS lookup phase; instead, it SHOULD delay sending the Multicast DNS query until the client is ready to communicate with the other entity.</p>
|
||||||
@ -319,11 +408,13 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
<p>If a user changes their picture, the user's client MUST update the NULL record with the contents of the new picture, calculate a new picture hash, and then update the "phsh" value in the TXT record with the new hash value. Since all users logged into local presence are monitoring for TXT record changes, they will see that the "phsh" value was changed; if they wish to view the new icon, their clients SHOULD issue a new Multicast DNS query to retrieve the updated picture.</p>
|
<p>If a user changes their picture, the user's client MUST update the NULL record with the contents of the new picture, calculate a new picture hash, and then update the "phsh" value in the TXT record with the new hash value. Since all users logged into local presence are monitoring for TXT record changes, they will see that the "phsh" value was changed; if they wish to view the new icon, their clients SHOULD issue a new Multicast DNS query to retrieve the updated picture.</p>
|
||||||
</section2>
|
</section2>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Internationalization Considerations' anchor='i18n'>
|
<section1 topic='Internationalization Considerations' anchor='i18n'>
|
||||||
<p><cite>RFC 1035</cite> does not allow characters outside the &ascii; character range in DNS A records. Therefore the "machine-name" portion of an A record as used for link-local messaging MUST NOT contain characters outside the US-ASCII character range.</p>
|
<p><cite>RFC 1035</cite> does not allow characters outside the &ascii; character range in DNS A records. Therefore the "machine-name" portion of an A record as used for link-local messaging MUST NOT contain characters outside the US-ASCII character range.</p>
|
||||||
<p>Although <cite>RFC 2317</cite> and <cite>RFC 2782</cite> do not allow characters outside the US-ASCII character range in PTR and SRV records respectively, Section 4.1 of <cite>DNS-Based Service Discovery</cite> recommends support for UTF-8-encoded Unicode characters in the <Instance> portion of Service Instance Names, which in link-local messaging is the "username@machine-name" portion of the PTR or SRV record. This document adheres to the recommendation in <cite>DNS-Based Service Discovery</cite>. However, as mentioned above, the "machine-name" portion of the <Instance> portion MUST NOT contain characters outside the US-ASCII range.</p>
|
<p>Although <cite>RFC 2317</cite> and <cite>RFC 2782</cite> do not allow characters outside the US-ASCII character range in PTR and SRV records respectively, Section 4.1 of <cite>DNS-Based Service Discovery</cite> recommends support for UTF-8-encoded Unicode characters in the <Instance> portion of Service Instance Names, which in link-local messaging is the "username@machine-name" portion of the PTR or SRV record. This document adheres to the recommendation in <cite>DNS-Based Service Discovery</cite>. However, as mentioned above, the "machine-name" portion of the <Instance> portion MUST NOT contain characters outside the US-ASCII range.</p>
|
||||||
<p>Although <cite>RFC 1464</cite> does not allow characters outside the US-ASCII character range in TXT records, Section 6.5 of <cite>DNS-Based Service Discovery</cite> mentions support for UTF-8-encoded Unicode characters in text record values (e.g., values of the TXT "msg" name). This document adheres to the recommendation in <cite>DNS-Based Service Discovery</cite>.</p>
|
<p>Although <cite>RFC 1464</cite> does not allow characters outside the US-ASCII character range in TXT records, Section 6.5 of <cite>DNS-Based Service Discovery</cite> mentions support for UTF-8-encoded Unicode characters in text record values (e.g., values of the TXT "msg" name). This document adheres to the recommendation in <cite>DNS-Based Service Discovery</cite>.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Security Considerations' anchor='security'>
|
<section1 topic='Security Considerations' anchor='security'>
|
||||||
<section2 topic='Authentication and Encryption' anchor='security-auth'>
|
<section2 topic='Authentication and Encryption' anchor='security-auth'>
|
||||||
<p>XMPP networks use TLS (&rfc2246;) for channel encryption, SASL (&rfc4422;) for authentication, and the Domain Name System (&rfc1034;) for weak validation of server hostnames; these technologies help to ensure the identity of sending entities and to encrypt XML streams. By contrast, zero-configuration networking uses dynamic discovery and asserted machine names as the basis of sender identity. Therefore, link-local messaging does not result in authenticated identities in the same way that XMPP itself does, nor does it provide for an encrypted channel between local entities.</p>
|
<p>XMPP networks use TLS (&rfc2246;) for channel encryption, SASL (&rfc4422;) for authentication, and the Domain Name System (&rfc1034;) for weak validation of server hostnames; these technologies help to ensure the identity of sending entities and to encrypt XML streams. By contrast, zero-configuration networking uses dynamic discovery and asserted machine names as the basis of sender identity. Therefore, link-local messaging does not result in authenticated identities in the same way that XMPP itself does, nor does it provide for an encrypted channel between local entities.</p>
|
||||||
@ -344,6 +435,7 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
<p>The TXT records optionally advertised as part of this protocol MAY result in exposure of privacy-sensitive information about a human user (such as full name, email address, and Jabber ID). A client MUST allow a user to disable publication of this personal information (e.g., via client configuration).</p>
|
<p>The TXT records optionally advertised as part of this protocol MAY result in exposure of privacy-sensitive information about a human user (such as full name, email address, and Jabber ID). A client MUST allow a user to disable publication of this personal information (e.g., via client configuration).</p>
|
||||||
</section2>
|
</section2>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='IANA Considerations' anchor='iana'>
|
<section1 topic='IANA Considerations' anchor='iana'>
|
||||||
<p>DNS-SD service type names are not yet managed by &IANA;. Section 19 of <cite>DNS-Based Service Discovery</cite> proposes an IANA allocation policy for unique application protocol or service type names. Until the proposal is adopted and in force, Section 19 points to <<link url='http://www.dns-sd.org/ServiceTypes.html'>http://www.dns-sd.org/ServiceTypes.html</link>> regarding registration of service type names for DNS-SD.</p>
|
<p>DNS-SD service type names are not yet managed by &IANA;. Section 19 of <cite>DNS-Based Service Discovery</cite> proposes an IANA allocation policy for unique application protocol or service type names. Until the proposal is adopted and in force, Section 19 points to <<link url='http://www.dns-sd.org/ServiceTypes.html'>http://www.dns-sd.org/ServiceTypes.html</link>> regarding registration of service type names for DNS-SD.</p>
|
||||||
<p>Before this specification was written, there was an existing registration for the "presence" service type, with registration information as follows:</p>
|
<p>Before this specification was written, there was an existing registration for the "presence" service type, with registration information as follows:</p>
|
||||||
@ -367,6 +459,7 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='XMPP Registrar Considerations' anchor='registrar'>
|
<section1 topic='XMPP Registrar Considerations' anchor='registrar'>
|
||||||
<section2 topic='Link-Local Messaging TXT Records Registry' anchor='registrar-linklocal'>
|
<section2 topic='Link-Local Messaging TXT Records Registry' anchor='registrar-linklocal'>
|
||||||
<p>The ®ISTRAR; maintains a registry of TXT records advertised in the context of link-local messaging (see &LINKLOCAL;).</p>
|
<p>The ®ISTRAR; maintains a registry of TXT records advertised in the context of link-local messaging (see &LINKLOCAL;).</p>
|
||||||
@ -412,8 +505,17 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
<desc>
|
<desc>
|
||||||
A space-separated list of extensions; the value of this record MUST
|
A space-separated list of extensions; the value of this record MUST
|
||||||
be the same as that provided via normal XMPP presence (if applicable)
|
be the same as that provided via normal XMPP presence (if applicable)
|
||||||
in the 'ext' attribute specified in XEP-0115; for details, refer to
|
in the 'ext' attribute specified in Entity Capabilities (XEP-0115).
|
||||||
the Discovering Capabilities section of XEP-0174.
|
</desc>
|
||||||
|
<status>optional</status>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record>
|
||||||
|
<name>hash</name>
|
||||||
|
<desc>
|
||||||
|
The hashing algorithm used to generated the 'ver' attribute in
|
||||||
|
Entity Capabilities (XEP-0115) and therefore the ver TXT record
|
||||||
|
in Link-Local Messaging.
|
||||||
</desc>
|
</desc>
|
||||||
<status>recommended</status>
|
<status>recommended</status>
|
||||||
</record>
|
</record>
|
||||||
@ -453,8 +555,7 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
<desc>
|
<desc>
|
||||||
A unique identifier for the application; the value of this record MUST
|
A unique identifier for the application; the value of this record MUST
|
||||||
be the same as that provided via normal XMPP presence (if applicable)
|
be the same as that provided via normal XMPP presence (if applicable)
|
||||||
in the 'node' attribute specified in XEP-0115; for details, refer to
|
in the 'node' attribute specified in Entity Capabilities (XEP-0115).
|
||||||
the Discovering Capabilities section of XEP-0174.
|
|
||||||
</desc>
|
</desc>
|
||||||
<status>recommended</status>
|
<status>recommended</status>
|
||||||
</record>
|
</record>
|
||||||
@ -489,10 +590,11 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
<record>
|
<record>
|
||||||
<name>status</name>
|
<name>status</name>
|
||||||
<desc>
|
<desc>
|
||||||
The presence availability of the user. Allowable values are "avail", "away",
|
The presence availability of the user. Allowable values are "avail",
|
||||||
and "dnd", which map to mere XMPP presence (the user is available) and the
|
"away", and "dnd", which map to mere XMPP presence (the user is
|
||||||
XMPP <show/> values of "away" and "dnd", respectively; if the status
|
available) and the XMPP <show/> values of "away" and "dnd",
|
||||||
record is not included, the status should be assumed to be "avail".
|
respectively; if the status record is not included, the status should
|
||||||
|
be assumed to be "avail".
|
||||||
</desc>
|
</desc>
|
||||||
<status>recommended</status>
|
<status>recommended</status>
|
||||||
</record>
|
</record>
|
||||||
@ -501,11 +603,22 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
<name>txtvers</name>
|
<name>txtvers</name>
|
||||||
<desc>
|
<desc>
|
||||||
The version of the TXT records supported by the client. For backwards
|
The version of the TXT records supported by the client. For backwards
|
||||||
compatibility this is hardcoded at "1".
|
compatibility this is hardcoded at "1". This TXT record should be the
|
||||||
|
first one provided, in accordance with the DNS-SD specification.
|
||||||
</desc>
|
</desc>
|
||||||
<status>deprecated</status>
|
<status>deprecated</status>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record>
|
||||||
|
<name>v</name>
|
||||||
|
<desc>
|
||||||
|
The application version; the value of this record MUST be the same
|
||||||
|
as that provided via normal XMPP presence (if applicable) in the 'v'
|
||||||
|
attribute specified in Entity Capabilities (XEP-0115).
|
||||||
|
</desc>
|
||||||
|
<status>optional</status>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record>
|
<record>
|
||||||
<name>vc</name>
|
<name>vc</name>
|
||||||
<desc>
|
<desc>
|
||||||
@ -528,10 +641,11 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
<record>
|
<record>
|
||||||
<name>ver</name>
|
<name>ver</name>
|
||||||
<desc>
|
<desc>
|
||||||
The application version; the value of this record MUST be the same as that
|
A hashed string that defines the XMPP service discovery (XEP-0030)
|
||||||
provided via normal XMPP presence (if applicable) in the 'ver' attribute
|
identity of the application and the XMPP service discovery features
|
||||||
specified in XEP-0115; for details, refer to the Discovering Capabilities
|
supported by the application; the value of this record MUST be the
|
||||||
section of XEP-0174.
|
same as that provided via normal XMPP presence (if applicable) in
|
||||||
|
the 'ver' attribute specified in Entity Capabilities (XEP-0115).
|
||||||
</desc>
|
</desc>
|
||||||
<status>recommended</status>
|
<status>recommended</status>
|
||||||
</record>
|
</record>
|
||||||
@ -540,7 +654,9 @@ _presence._tcp.local. IN NULL raw-binary-data-here
|
|||||||
</section3>
|
</section3>
|
||||||
</section2>
|
</section2>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
<section1 topic='Acknowledgements' anchor='ack'>
|
<section1 topic='Acknowledgements' anchor='ack'>
|
||||||
<p>Thanks to Jens Alfke, Justin Karneges, Marc Krochmal, Eric St. Onge, and Sjoerd Simons for their input.</p>
|
<p>Thanks to Emanuele Aina, Jens Alfke, Marco Barisione, Justin Karneges, Marc Krochmal, Eric St. Onge, and Sjoerd Simons for their input.</p>
|
||||||
</section1>
|
</section1>
|
||||||
|
|
||||||
</xep>
|
</xep>
|
||||||
|
Loading…
Reference in New Issue
Block a user