In order to understand the configuration directives and how they work, it is necessary to take a step back and look what the Jabber Server really is.
The Jabber Server is a daemon jabberd that manages the flow of data between various components, components which collectively make up the Jabber service. There are different components, each of which perform different kinds of tasks, and there is a basic set of components that are required for a simple Jabber Server such as the one we configured and installed in the previous chapter.
The following list shows what the basic Jabber components are and what services they provide. It's worth considering the original and most well-known application of Jabber—instant messaging—and a Jabber design feature (distributed server architecture) to put this list into context and make better sense of it.
We need to be able to manage users' sessions while they're connected to the server. The component that does this is called the Jabber Session Manager (JSM), and provides IM features such as message store and forward and roster management as well as managing sessions.
This is the component that manages the connections between clients and the server. It is known internally as 'c2s'.
If there's a requirement to send a message from a user on one Jabber server to a user on another Jabber server, we need a way for the servers to connect to each other. This component establishes and manages server-to-server connections, and is known internally as 's2s'.
As in any server system, the ability to log events (error messages, notices, alerts and so on) is essential. The logging component allows us to effect such logging.
There will be some server-side storage requirements, for example, to hold authentication information and data such as last connect time; not to mention storage of rosters, personal details, and private data. The Data storage component does this for us. It is known internally as the 'xdb' component. xdb stands for XML Data Base.
Last but not least, we may need some way to resolve names of hosts that the Jabber server doesn't recognize as "local", as in the Server (to Server) Connection context. This component is known internally as 'dnsrv'.
Figure 4-1 shows the relationship between jabberd and the components. These components are the engines that serve and process XML messages, providing their services, and jabberd is the backbone along which messages are routed.
Figure 4-1. jabberd and the components
+---+
| |
+-------------+ | <----- jabberd backbone
| Client (to | | |
| server) |==| |
| connections | | | +-------------+
+-------------+ | | | Session |
| |==| management |
+-------------+ | | | |
| Logging | | | +-------------+
| |==| |
| | | | +-------------+
+-------------+ | | | Server (to |
| |==| server) |
+-------------+ | | | connections |
| Data | | | +-------------+
| storage |==| |
| | | | +-------------+
+-------------+ | | | Hostname |
| |==| resolution |
+.............+ | | | |
: : | | +-------------+
: ... :==| |
: : : :
+.............+ : :
: :
+---+As seen in Figure 4-1, the jabberd backbone acts as the central artery or "hub", managing the "peripheral" components that are attached to it. The management of these components encompasses controlling and overseeing how they connect and coordinating the flow of data between them. Certain types of components receive only certain types of data. There is a distinction made between three different types of component:
log
xdb
service
The different component types handle different types of data packets. Each packet is in the form of a distinct, fully-formed XML fragment and is identified by the outermost element name in the XML fragment. This element name is matched up to a particular component type.
log components handle <log/> packets; you can guess that these are the components that provide logging services.
On receipt of a <log/> data packet a logging component will (hopefully) do something useful with it, like write it to a file or to STDERR.
The <log/> packet shown in Example 4-1 is being used to record the successful connection and authentication of user dj, on yak, using the Jabber client JabberIM.
xdb components handle <xdb/> packets. The <xdb/> packets carry data and storage/retrieval requests to and from the xdb components which provide the Data Storage services.
On receipt of an <xdb/> data packet an xdb component will retrieve data from or write data to a storage system such as a collection of flat files or an RDBMS.
The <xdb/> packet shown in Example 4-2 is carrying a request from the session manager to retrieve the preferences stored in a private namespace for the user dj (on Jabber server yak by the Jabber client JabberIM).
service components handle the three main building blocks on which the Jabber functionality is based (the <message/>, <presence/>, and <iq/> packets). You can find out more about these building blocks in Part II of this book.
In addition, the service component also handles the <route/> packets, which are used internally by jabberd to move packets around between components. For example, the Session Management component is the component that usually handles client authentication. It receives any incoming authorization requests received by and passed on from the Client (to Server) Connections component. However, it may be that the administrator has configured the Jabber server to use a different (third party) component, developed by another group or company, to handle the authorizations. In this case the request is routed from one component (Session Management) to another (the third-party authorization component).
So unlike the log and xdb components, which handle data packets whose element names match the component type (<log/> and <xdb/>), the service component is an umbrella component designed to handle packets with different element names.
Example 4-3 shows two typical service packets.
Example 4-3. Two service packets
<route to='dj@yak/81F2220' from='15@c2s/80EE868'>
<presence>
<status>Online</status>
</presence>
</route>
<message id="jim_id_7" to="sabine@merlix" type="chat">
<x xmlns="jabber:x:event">
<composing/>
</x>
<thread>3A378DF2B70F6A53A9C317CF526C6B7A</thread>
<body>Hi there</body>
</message>The first is an internal <route/> packet, which is carrying a <presence/> packet from the Client (to Server) Connections component, identified by the 'c2s' part of the from attribute, to the Session Management component (where the session identifier 81F2220 [1] is significant in the to attribute.). [2]
The second is a <message/> packet, which contains the message itself ("Hi there") as well as other information (a message event and a conversation thread identifier; these are examined in detail in Part II of the book).
It isn't necessarily the case, however, that all xdb components will handle all <xdb/> packets, or all service components will handle all <presence/> packets. The configuration, described later in this chapter, determines how the components announce themselves and state their readiness to receive and handle packets.
The phrase "delivery tree" is often used in Jabber terminology to signify a component or components that handle certain types of packet. The path a packet makes as it descends the collection of decision branches that guide it to the component or components that will handle it. For example, an 'xdb type component' is sometimes referred to as an xdb Delivery Tree. Considering the division of components into different types that handle different packet types is perhaps easier to visualize as a tree, as shown in Figure 4-2.
Figure 4-2. The Jabber Delivery Tree
| +-------+-----------------------------+ | | | component log xdb service types | | | | | +----------+----------+----------+ | | | | | | packets <log/> <xdb/> <message/> <presence/> <iq/> <route/> handled
The Jabber Delivery Tree shows which component types can handle what sorts of packets in the Jabber world.
The notion of components providing distinct services and being coordinated by a central mechanism (jabberd) suggests a certain amount of independence and individuality—a plug-in architecture—and that is what Jabber is. The components described earlier, and others too, are "plugged in" to the Jabber backbone according to the requirements of the server.
The idea is that once you have the basic services like Session Management, Client (to Server) Connectivity and Data Storage, you plug in whatever you need to suit the server's requirements. For example if you need conferencing facilities, you can plug in the Conferencing component. If you need user directory facilities, you can plug in the Jabber User Directory (JUD) component. If you need a bridge to the Yahoo! Instant Messaging system, you can plug in the Yahoo! Transport component. [3] Indeed, you can write your own components to provide services that are not available off the shelf and plug those in too.
So, how are components 'plugged in' to the Jabber server backbone? [4] Well, there are three methods:
Library load
TCP sockets
STDIO
Let's examine each one in turn.
The core components of a Jabber Server providing IM services are connected using the library load method. This simply means that the component sources are compiled into shared object (.so) libraries and loaded into the main Jabber process (jabberd).
The components are written specially with the Jabber backbone in mind and contain standard registration routines that utilize functions in the core Jabber libraries. These routines are used to bind the component relationship with jabberd (for example there is a 'heartbeat' mechanism through which the components are monitored) and to specify packet receipt requirements. [5]
The library load method is represented in the configuration by the <load/> tag, which wraps the library (or libraries) that should be loaded. Example 4-4 and Example 4-5 show excerpts from the standard jabber.xml configuration file where we can see components being plugged in using the library load method.
Example 4-4 shows the Client (to Server) Connections (c2s) component, which has been written and compiled as a .so library, being connected using the library load method.
Example 4-4. Loading of the c2s component with library load
<load> <pthsock_client>./pthsock/pthsock_client.so</pthsock_client> </load>
In this example, we see the "simpler" form of the <load/> tag: inside the tag we have:
<pthsock_client>./pthsock/pthsock_client.so</pthsock_client>
which specifies two things:
Which library to load (./pthsock/pthsock_client.so).
The name of the component registration routine that should be called by jabberd once the library has been loaded. The name of the routine is the name given to the tag that wraps the library filename; in this example it's pthsock_client(), denoted by <pthsock_client/>.
Example 4-5 shows multiple .so libraries being loaded when a component is connected; the form of the <load/> tag is slightly more involved.
Example 4-5. Loading of the jsm component with library load
<load main="jsm"> <jsm>./jsm/jsm.so</jsm> <mod_echo>./jsm/jsm.so</mod_echo> <mod_roster>./jsm/jsm.so</mod_roster> <mod_time>./jsm/jsm.so</mod_time> <mod_vcard>./jsm/jsm.so</mod_vcard> <mod_last>./jsm/jsm.so</mod_last> <mod_version>./jsm/jsm.so</mod_version> <mod_announce>./jsm/jsm.so</mod_announce> <mod_agents>./jsm/jsm.so</mod_agents> <mod_browse>./jsm/jsm.so</mod_browse> <mod_admin>./jsm/jsm.so</mod_admin> <mod_filter>./jsm/jsm.so</mod_filter> <mod_offline>./jsm/jsm.so</mod_offline> <mod_presence>./jsm/jsm.so</mod_presence> <mod_auth_plain>./jsm/jsm.so</mod_auth_plain> <mod_auth_digest>./jsm/jsm.so</mod_auth_digest> <mod_auth_0k>./jsm/jsm.so</mod_auth_0k> <mod_log>./jsm/jsm.so</mod_log> <mod_register>./jsm/jsm.so</mod_register> <mod_xml>./jsm/jsm.so</mod_xml> </load>
Here we see multiple libraries being loaded to form the Session Management (the JSM) component, known as "jsm".
What happens is this:
jabberd loads the library in the tag that's pointed to by the main="" attribute of the <load/> tag; in this example it's the library ./jsm/jsm.so:
<jsm>./jsm/jsm.so</jsm>
jabberd then invokes the registration routine called jsm()
jsm loads the rest of the modules defined within the <load/> tag (mod_echo, mod_roster, mod_time, and so on), invoking each module's registration routine (mod_echo(), mod_roster(), mod_time(), and so on) as they're loaded.
In case you're wondering, all the modules that belong to the jsm are actually compiled into a single .so library - which is why all the .so references in this example are the same.
Another method for connecting components to the Jabber backbone uses a TCP sockets connection. This means that a component connected in this way can reside on the same or a different server to the one running jabberd. So instead of being loaded directly into the jabberd backbone, TCP sockets-connected components exist and run as separate entities and can be started and stopped independently. [6]
The configuration syntax for defining a connection point for a component that is going to connect to the backbone via TCP sockets looks like this: [7]
<accept> <ip>127.0.0.1</ip> <port>9001</port> <secret>shhh</secret> </accept>
As <load/> is to the library load method, so <accept/> is to the TCP sockets method.
The <accept/> tag usually has three child tags - <ip/>, <port/> and <secret/>. [8] Specify an IP address and port to which the component will connect; if you want the socket to be network interface independent, you can write <ip/> (an empty tag) to listen on your specified port on all (INADDR_ANY) IP addresses. The <secret/> tag is used in the handshake when the component connects to the backbone, so that it can be authenticated.
More information on connecting components with <accept/> can be found in Part II.
XML Streams Entity to entity connections in the Jabber world pass data to each other using a technique called XML streams, which is essentially the exchange of data in the form of XML fragments in "streaming" mode over a network connection. During the lifetime of a connection between two entities, two complete XML documents will be passed between the entities (if A connects to B, A will send B a document, fragment by fragment, and B will send A a document, fragment by fragment). At the start of the connection, the first things to be exchanged are the document headers—the outermost (document root) XML tags. The root tag in each document is <stream/>. XML namespaces are defined for the content of the XML documents to be exchanged. These namespaces represent what sort of entity connection is taking place. In the case of a Jabber client to Jabber server connection, the namespace is jabber:client. In the case of connections based on the TCP sockets connection method, (using <accept/>), the namespace is jabber:component:accept. To initiate a connection and conversation, the component will initiate the conversation by sending something like this to Jabber: <?xml version="1.0"?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams"
xmlns="jabber:component:accept"
to="component.name">to Jabber. More detailed information on how XML streams work can be found in Chapter 5. |
The TCP sockets component connect method is used to connect an external component to the Jabber backbone via a socket connection through which streamed XML documents are exchanged. There is another way for components to connect and exchange XML document streams with the Jabber backbone—using the STDIO connection method.
While the TCP sockets method requires external components to be independently started and stopped, the STDIO method represents a mechanism whereby the jabberd process starts the external component itself. The component to start is specified inside an <exec/> tag. Example 4-6 shows how the STDIO method is specified in the configuration.
Example 4-6. Invoking an external component with STDIO
<exec>/path/to/component.py -option a -option b</exec>
Here we see that the component is a Python program and is being passed some switches at startup.
So where's the socket connection in this method? There isn't one. The XML documents are exchanged through standard I/O (STDIO). The component writes XML fragments to STDOUT, and these are received on the Jabber backbone. The component receives XML fragments destined for it on STDIN, fragments which are written out from the Jabber backbone.
Just as a component connected using the TCP sockets method sends an opening document fragment, the component connected with this STDIO method sends an opening document fragment to initiate a connection and conversation:
<?xml version="1.0"?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams"
xmlns="jabber:component:exec"
to="component.name">Notice how the namespace that describes this type of conversation is:
jabber:component:exec
No secret is required in this case because it is assumed that the component can be trusted if it is specified in the configuration and execution is initiated by jabberd itself.
| [1] | This identifier is a hexadecimal representation of the user's session id within the JSM, carried internally as a JID resource in the routing information. |
| [2] | the '15' is the identifier for the socket on which the the pertinent client connection has been made |
| [3] | Actually, you can also connect to a component running on another Jabber server - see later in this Chapter. |
| [4] | The log component(s) are actually part of the backbone, and as such do not need to be 'plugged in' |
| [5] | components receive packets based upon their type (log, xdb or service), upon a <host/> configuration specification that we'll see later in this chapter, and can also specify when in the delivery sequence they are to receive the packets |
| [6] | It is actually possible to modify the contents of the configuration while the Jabber server is running, and then send a hangup (HUP) signal to the processes, which causes a re-read of the configuration and new library loaded components can be started and old ones can be stopped. There are however currently some problems with this, that will be addressed in the next round of bug fixes. |
| [7] | The tag name for this TCP sockets configuration stanza is <accept/>, reflecting the low-level socket library call accept() to which it directly relates. |
| [8] | There is a fourth tag <timeout/> with which you can control the heartbeat monitor of this component connection, which defaults to a value of 10 if not explicitly specified (it seldom is). |