I recently upgraded my jabberd server from version 1 to version 2. If you haven't looked at the two versions, it's a fairly major upgrade. The structure of the programs is totally different- the older version runs as modules within a single process, where the new version runs as four separate processes plus an extra for each "realm" (domain name) you are hosting on the server. I currently have three domains, which means that I have a total of seven processes.
I ran into a few problems in getting it set up:
At some point over the last month, the jabberstudio.org server was hacked, and the server administrators decided to consider all code as "tainted" and therefore not available for download, until such time as the actual developers of each package were able to inspect and certify the code for their packages. At the time I did this (late February 2005) the jabberd2 package had not been certified yet, so I was not able to download the package from the official site. The last version's web site was still there, and had a copy of the MD5 checksum of the file, so I was able to find a copy of the file itself for download at another web site, and verify it against the MD5 checksum from the official site.
I am running three different "realms" on my server, and the official documentation has all of one sentence explaining how to set up multiple domains. This is actually the primary reason I'm writing this page, so there is a simple explanation, WITH EXAMPLES, of how to set it all up.
I prefer using daemontools to manage the services on my machines, especially where jabberd2 consists of five or more processes, ALL of which must be running in order for it all to work.
The following is a cleaned-up version of how I got it working on my own server. It does have my own domain name as the first domain on the jabber server, but the other two have been replaced with dummy values.
The official home of jabberd2 is http://jabberd.jabberstudio.org/. This is where to find the official documentation, and is where you should download the source code.
When I originally wrote this page, the jabberstudio web server had been cracked and all downloadable files had been taken off-line until they could be verified against the developers' original files. I was offering a copy of the jabberd2 source code from my own server, but since the files on the jabberstudio server have been verified (and the version I was offering is now out of date) I have removed the file from my server. Please download the software from the official jabberd2 site (using the link above.)
Also... and I cannot stress this strongly enough. PLEASE read through the official documentation and use my web site only to "fill in the blanks" where you may need help.
Before compiling the software, you need to create a userid for the jabberd2 server to run as. Most systems use "jabber" for this purpose, I see no problem with adopting the same standard.
(as root)
# groupadd jabber
# useradd -g jabber jabber
You will probably also need to install a few other libraries first. These libraries should be available through your system's normal update mechanism. In my case, running Whitebox Enterprise Linux, I was able to install most of them using a yum install command, but I had to get the source RPM for one of them (the libidn library) from a Fedora Core 3 repository, build my own binary RPM files, and install from those.
Because there are so many types of systems out there, I cannot hope to include complete directions for every system. The official documentation has links to the web sites of the packages involved, as well as detailed instructions for the OpenSSL library.
On my server I am using MySQL for both authentication and data storage. The actual instructions were...
(as root)
# yum install openssl-devel mysql-devel
...
# rpmbuild -ta libidn-0.5.12.tar.gz
...
# rpm -ivh /usr/src/redhat/RPMS/i386/libidn-*
...
Once the pre-requisite packages are installed, the next step is to compile the software. Again, please see the official documentation for detailed instructions. Below is a list of the commands I used on my own server.
Note that I create a shell script which runs the ./configure command. This is for two reasons:
(as a non-root user)
% tar xvzf jabberd-2.0s6.tar.gz
...
% cd jabberd-2.0s6
% nano go
The contents of "go" are...
#!/bin/sh
export CFLAGS="-I/usr/kerberos/include -I/usr/include/mysql -L/usr/lib/mysql"
./configure \
--enable-ssl \
--enable-idn \
--enable-mysql \
--enable-debug
% chmod 700 go
% ./go
...
% make
...
% su -
Password:
# make install
...
# chown -R root:jabber /usr/local/etc/jabber
# chmod -R g=u-w,o= /usr/local/etc/jabber
# exit
%
At this point, the programs which make up the server are installed and need to be configured for your system.
Again, the official documentation details how to do the basic configuration and testing for the server.
The first step for me was to set the host name in sm.xml and c2s.xml. I chose one of the three domain names and set it up exactly as shown in the documentation.
The next step for me was to set up the MySQL database. Again, the documentation did a good job of showing how it's done.
(as a non-root user)
% cd .../jabberd2-2.0s6/tools
% mysql -u root -p
Enter password:
...
mysql> \. db-setup.mysql
...
mysql> GRANT select,insert,delete,update ON jabberd2.*
-> to jabberd2@localhost
IDENTIFIED BY 'make-up-a-password';
mysql> \q
On my system I did have to create the symbolic link so that /tmp/mysql.sock pointed to the right place.
(as root)
# ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock
After setting up the MySQL database, I set up sm.xml to use MySQL as a storage mechanism, and then set up c2s.xml to use MySQL as the authentication source, both as shown in the documentation.
I then tested the server to make sure it worked correctly, at least for the one domain I had already set up. It didn't work correctly the first few times- it turned out I had made a few typos when editing the config files.
Also, the testing ended up taking a lot longer than it normally would because I missed one little thing... the documentation mentions this, but in my opinion does not highlight it strongly enough:
If jabberd does not start, make sure that any previous instances have stopped.
The problem I kept running into was this: I tried to start the server, and the first few processes started correctly but then another process failed to start because of a typo in the config file. When I fixed the typo and tried to start again, the first process failed because another instance of that process was already running.
Remember that jabberd2 actually consists of five or more processes, and whenever you start it using the /usr/local/bin/jabberd script, ALL of the existing processes must have stopped. Once I realized this, I was able to start the server up correctly every time.
When daemontools runs a service, it expects the program to send any logging output to the "standard out" channel. Many programs can be configured to send their logging output to "standard error", therefore it is common to see the expression "2>&1" in the "run" script for a service.
The jabberd programs can be configured to send their log output directly to the "standard out" channel, therefore the "2>&1" syntax is not really needed (although I have added it to the "run" scripts below, it doesn't hurt anything and just in case something does happen to be sent to "standard error", it will end up in the log so you can find it.
To configure each program to send its log output to "standard out", add this line to the .xml file which controls the service (using the "s2s" service as an example here...)
<s2s>
...
<log type='stdout' />
...
</s2s>
The next step is to make it work with more than one domain. The documention mentions in passing that this is possible, but it does not really explain how to make it work. This shortcoming is the primary reason I'm writing this web page.
The key to using multiple domains is to realize that each "sm" (session manager) process is responsible for managing sessions for one specific domain name. If your server will be handling three domain names, you will need three "sm" processes.
Each "sm" process will need its own configuration file. You already have a good config file for the first domain, and you can copy this for each additional domain, provided you change two things at the beginning of the file: the <id> and <pidfile> values must be unique for all jabberd2 processes.
What I did is the following:
(as root)
# cd /usr/local/etc/jabber
# cp sm.xml sm-jms1.net.xml
# chown root:jabber sm-jms1.net.xml
# chmod 640 sm-jms1.net.xml
# nano sm-jms1.net.xml
Changed the <id> tag from localhost
to jms1.net
Changed the <pidfile> tag from .../sm.pid
to .../sm-jms1.net.pid
# cp sm.xml sm-domain1.com.xml
# chown root:jabber sm-domain1.com.xml
# chmod 640 sm-domain1.com.xml
# nano sm-domain1.com.xml
Changed the <id> tag from localhost
to domain1.com
Changed the <pidfile> tag from .../sm.pid
to .../sm-domain1.com.pid
# cp sm.xml sm-domain2.org.xml
# chown root:jabber sm-domain2.org.xml
# chmod 640 sm-domain2.org.xml
# nano sm-domain2.org.xml
Changed the <id> tag from localhost
to domain2.org
Changed the <pidfile> tag from .../sm.pid
to .../sm-domain2.org.pid
After changing the files, you have to tell the c2s process that the other domains exist, otherwise it won't allow connections to them.
(as root)
# nano c2s.xml
Find this block (about 65 lines from the top, if your c2s.xml file
hasn't been extensively modified):
<!-- Local network configuration -->
<local>
<!-- Who we identify ourselves as. This should correspond to the
ID (host) that the session manager thinks it is. You can
specify more than one to support virtual hosts, as long as you
have additional session manager instances on the network to
handle those hosts. The realm attribute specifies the auth/reg
or SASL authentication realm for the host. If the attribute is
not specified, the realm will be selected by the SASL
mechanism, or will be the same as the ID itself. Be aware that
users are assigned to a realm, not a host, so two hosts in the
same realm will have the same users.
If no realm is specified, it will be set to be the same as the
ID. -->
<id>localhost</id>
<!-- <id realm='company'>localhost</id> -->
Replace the <id>localhost</id> line with the
following lines:
<id realm='jms1.net'>jms1.net</id>
<id realm='domain1.com'>domain1.com</id>
<id realm='domain2.org'>domain2.org</id>
The last step is to make sure that the /usr/local/bin/jabberd script knows to start the new sm processes.
# nano jabberd.cfg
Find the existing line which starts with "sm", remove it, and replace
it with the following:
sm /usr/local/etc/jabberd/sm-jms1.net.xml
sm /usr/local/etc/jabberd/sm-domain1.com.xml
sm /usr/local/etc/jabberd/sm-domain2.org.xml
You should now be able to run /usr/local/bin/jabber as the jabber user without any error messages. While it's running you should be able to do ps axww (or ps -elf, depending on your system) and see three copies of /usr/local/bin/sm running. You should also be able to log into all three of the jabber domains.
My preference is to use daemontools to manage services on my machine- primarily because if a service stops, daemontools will automatically start it back up within five seconds. If you are not already familiar with daemontools, or do not wish to use it for your jabber server, you should refer to the documentation to set up the automatic startup and shutdown using RC scripts.
Otherwise, the documentation also includes instructions on how to use daemontools to start and stop the jabberd2 server. I am including sample "run" and "log/run" scripts here for your convenience.
c2s.run can be installed as the "run" script for your c2s service.
resolver.run can be installed as the "run" script for your c2s service.
router.run can be installed as the "run" script for your c2s service.
s2s.run can be installed as the "run" script for your c2s service.
sm.run can be installed as the "run" script for each of your sm services. If you are serving multiple domains, you will need to set up a separate instance of the sm service for each domain. Note that if you have changed the filename of your sm.xml config file (in order to serve multiple domains, or for any other reason) you will need to change the filename in this script as well.
log.run can be installed as the "log/run" script for all of your services.
The "proper" way for a client (or another server) to find your jabber server is using SRV records defined in your DNS data. If the SRV records don't exist, it will fall back to using A records. However, if you own a domain but your jabber server is on a different machine from your web site, you need to set up SRV records so your clients and other jabber servers can find you.
SRV records (defined in RFC 2052) exist as a way to allow individual services to be delagated to not just a specific machine, but a specific port number on a machine. Most service have "assigned" port numbers- 80 for HTTP, 110 for POP3, and so forth. However, if the protocol is designed to use SRV records to locate a specific server, you can run the service on any port you like, and clients will still be able to find you. So if you want to run your jabber server with non-standard port numbers, you can put your custom port numbers into your SRV records, and your clients and other servers would be able to find the jabber server without any problems.
There are three SRV records which should exist for any jabber server:
_jabber._tcp.domain.xyz -> host.domain:5269
_xmpp-server._tcp.domain.xyz -> host.domain:5269
_xmpp-client._tcp.domain.xyz -> host.domain:5222
The first two records are used by other jabber servers to find your server for server-to-server (s2s) connections. The third one is used by jabber clients (usually a "chat program" on somebody's desktop) for client-to-server (c2s) connections.
SRV records have two other numbers associated with them- a "priority" and a "weight".
The priority number works like the priority numbers associated with MX records- if multiple SRV records are found, a client should try the host with the lowest priority first.
The weight number is used for load balancing. If a client finds multiple SRV records with the same priority, it should choose one of the hosts based on a weighted pseudo-random choice. For example, if three hosts have the same priority, "host1" has a weight of 1, "host2" has a weight of 2, and "host3" has a weight of 3, then it should choose "host1" one time out of six, "host2" two times out of six, and "host3" three times out of six.
RFC 2052, which defines SRV records, recommends that if you aren't actually using the weight values (i.e. if you only have one host, or if you have multiple hosts and all of them have different priorities) then you should set the weight values to zero, to make the records easier for humans to deal with.
Both of these values are unsigned 16-bit numbers, which means their ranges are 0-65535. For my single server, I just use zero for both values.
In addition, there are two TCP port numbers which are normally used by a jabber server.
The S2S Port is used for server-to-server connections (i.e. if a user on another jabber server wishes to send a message to a user on your jabber server, their server connects to your server in order to pass the message.) This is normally port 5269.
The C2S Port is used by jabber clients to connect to your server. This is normally port 5222.
In addition, the original jabber protocol supports an SSL-encrypted version of the C2S protocol, which is expected to be one number higher than the normal C2S port- so if your jabber server's C2S port is 5222, then the SSL-encrypted C2S port would be 5223.
tinydns does not have a built-in record type for SRV records, however you can use the ":" record type to create a record by specifying the raw binary data for an SRV record. (This is documented on djb's web site.) In order to do this, you need to know the numeric record type you wish to create (which is 33 for SRV records), and the binary data to be returned.
This is an example of what the three required SRV records look like, in tinydns format:
:_jabber._tcp.domain.xyz:33:\000\000\000\000\024\225\006domain\003xyz\000
:_xmpp-server._tcp.domain.xyz:33:\000\000\000\000\024\225\006domain\003xyz\000
:_xmpp-client._tcp.domain.xyz:33:\000\000\000\000\024\146\006domain\003xyz\000
The "\nnn" notation specifies a byte, with "nnn" being the value in octal (base-eight) notation. The first "\000\000" (with the red background) is the priority value, the second "\000\000" (with the green background) is the weight value, and the third value (with the blue background) is the port number. The two bytes for each value are specified in "network order", meaning they are in order from "most significant" to "least significant".
The hostname providing the service (after the port number) is specified in "label sequence" format, as defined in RFC 1035 section 4.1.2. Each "label" consists of a single byte telling the length of the label, followed by the label itself- so the label "\003xyz" has "\003" (a byte with the value 3) followed by the three bytes "xyz". The "label sequence" ends with a label whose length is zero.
And if we do the math, we find that the port number for the last line in this example, "\024\146", is 5222 decimal:
Octal Binary Decimal 024 -> 00 010 100 -> 20 146 -> 01 100 110 -> 102 256*20 + 102 = 5120+102 = 5222
It's actually easier to create SRV records under BIND, because it knows how to handle SRV records on its own, without having to resort to building the raw binary data yourself. The records look like this:
_jabber._tcp.domain.xyz. IN SRV
0 0
5269 jabber.domain.xyz.
_xmpp-server._tcp.domain.xyz. IN SRV
0 0
5269 jabber.domain.xyz.
_xmpp-client._tcp.domain.xyz. IN SRV
0 0
5222 jabber.domain.xyz.
As in the tinydns example, the number with the red background is the priority, the green background is the weight, and the blue background is the port number.
And as usual when dealing with BIND, don't forget the "." at the end of any names, make sure to increment the serial number in the SOA record, and run "ndc reload" to make the new records active.
When I first set up my Jabber server, there was a tinydns Record Maker written by Rob Mayoff which generated the records automatically. However, when I looked again just now (2007-08-31), it didn't seem to be working- I got a page full of un-rendered PHP and HTML script instead of the web page I had seen and used previously.
So I wrote an AJAX-based tinydns SRV record generator of my own. You can enter the necessary data below, and the "Generate" button will give you the actual lines which may be copied and pasted into your tinydns "data" file. If you're interested in how it works, the AJAX logic is embedded into the source code of this page, and the server-side piece can be downloaded from this link.
Results:
After writing this, I decided it might make more sense to also write a pure javascript version of the record generator. I am not a Javascript expert, I think the fact that it works at all is pretty remarkable. The pure javascript page generates both tinydns and BIND data, where the AJAX version above only generates tinydns data.