xmpp_server.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
00003  *
00004  * XMPP Module
00005  * This file is part of Kamailio, a free SIP server.
00006  *
00007  * Copyright (C) 2006 Voice Sistem S.R.L.
00008  *
00009  * Kamailio is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version
00013  *
00014  * Kamailio is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  *
00023  * Author: Andreea Spirea
00024  *
00025  */
00031 /*
00032  * An inbound SIP message:
00033  *   from sip:user1@domain1 to sip:user2*domain2@gateway_domain
00034  * is translated to an XMPP message:
00035  *   from user1*domain1@xmpp_domain to user2@domain2
00036  *
00037  * An inbound XMPP message:
00038  *   from user1@domain1 to user2*domain2@xmpp_domain
00039  * is translated to a SIP message:
00040  *   from sip:user1*domain1@gateway_domain to sip:user2@domain2
00041  *
00042  * Where '*' is the domain_separator, and gateway_domain and
00043  * xmpp_domain are defined below.
00044  */
00045 
00046 /*
00047  * 2-way dialback sequence with xmppd2:
00048  *
00049  *  Originating server (us)         Receiving server (them)      Authoritative server (us)
00050  *  -----------------------         -----------------------      -------------------------
00051  *           |                               |                               |
00052  *           |    establish connection       |                               |
00053  *           |------------------------------>|                               |
00054  *           |    send stream header         |                               |
00055  *           |------------------------------>|                               |
00056  *           |    send stream header         |                               |
00057  *           |<------------------------------|                               |
00058  *           |    send db:result request     |                               |
00059  *           |------------------------------>|                               |
00060  *                                           |    establish connection       |
00061  *                                           |------------------------------>|
00062  *                                           |    send stream header         |
00063  *                                           |------------------------------>|
00064  *                                           |    send stream header         |
00065  *                                           |<------------------------------|
00066  *                                           |    send db:result request     |
00067  *                                           |------------------------------>|
00068  *           |    send db:verify request     |
00069  *           |------------------------------>|
00070  *           |    send db:verify response    |
00071  *           |<------------------------------|
00072  *                                           |    send db:result response    |
00073  *                                           |------------------------------>|
00074  *                                           |    send db:verify request     |
00075  *                                           |<------------------------------|
00076  *                                           |    send db:verify response    |
00077  *                                           |------------------------------>|
00078  *           |    send db:result response    |
00079  *           |<------------------------------|
00080  *           :                               :                               :
00081  *           :                               :                               :
00082  *           |    outgoing <message/>        |                               :
00083  *           |------------------------------>|                               :
00084  *                                           |    incoming <message/>        |
00085  *                                           |------------------------------>|
00086  */
00087 
00088 #include <stdio.h>
00089 #include <stdlib.h>
00090 #include <errno.h>
00091 #include <string.h>
00092 
00093 #include "../../sr_module.h"
00094 #include "../../cfg/cfg_struct.h"
00095 
00096 #include "xmpp.h"
00097 #include "xmpp_api.h"
00098 #include "network.h"
00099 #include "xode.h"
00100 
00101 #include <arpa/inet.h>
00102 
00103 /* XXX hack */
00104 #define DB_KEY  "this-be-a-random-key"
00105 
00106 #define CONN_DEAD       0
00107 #define CONN_INBOUND    1
00108 #define CONN_OUTBOUND   2
00109 
00110 struct xmpp_private_data {
00111         int fd;         /* outgoing stream socket */
00112         int listen_fd;  /* listening socket */
00113         int in_fd;      /* incoming stream socket */
00114         int running;
00115 };
00116 
00117 struct xmpp_connection {
00118         struct xmpp_connection *next;
00119 
00120         char *domain;
00121         int type;
00122         int fd;
00123         char *stream_id;
00124         xode_pool pool;
00125         xode_stream stream;
00126         xode todo;      /* backlog of outgoing messages, if any */
00127 };
00128 
00129 static char local_secret[64] = { 0, };
00130 
00131 static void in_stream_node_callback(int type, xode node, void *arg);
00132 static void out_stream_node_callback(int type, xode node, void *arg);
00133 
00134 static struct xmpp_connection *conn_list = NULL;
00135 
00136 static struct xmpp_connection *conn_new(int type, int fd, char *domain)
00137 {
00138         struct xmpp_connection *conn = NULL;
00139         
00140         conn = malloc(sizeof(struct xmpp_connection));
00141 
00142         if(conn==NULL) 
00143         {
00144                 LM_ERR("out of memory\n");
00145                 return NULL;
00146         }
00147         
00148         memset(conn, 0, sizeof(struct xmpp_connection));
00149         conn->domain = domain ? strdup(domain) : NULL;
00150         conn->type = type;
00151         conn->fd = fd;
00152         conn->todo = xode_new_tag("todo");
00153 
00154         conn->pool = xode_pool_new();
00155         conn->stream = xode_stream_new(conn->pool,
00156                 (type==CONN_INBOUND)?in_stream_node_callback:out_stream_node_callback,
00157                 conn);
00158         
00159         conn->next = conn_list;
00160         conn_list = conn;
00161         return conn;
00162 }
00163 
00164 static void conn_free(struct xmpp_connection *conn)
00165 {
00166         struct xmpp_connection **last_p, *link;
00167         
00168         last_p = &conn_list;
00169         for (link = conn_list; link; link = link->next) {
00170                 if (link == conn) {
00171                         *last_p = link->next;
00172                         break;
00173                 }
00174                 last_p = &link->next;
00175         }
00176 
00177         if (conn->todo)
00178                 xode_free(conn->todo);
00179         xode_pool_free(conn->pool);
00180         if (conn->fd != -1)
00181                 close(conn->fd);
00182         if (conn->stream_id)
00183                 free(conn->stream_id);
00184         if (conn->domain)
00185                 free(conn->domain);
00186         free(conn);
00187 }
00188 
00189 static struct xmpp_connection *conn_find_domain(char *domain, int type)
00190 {
00191         struct xmpp_connection *conn;
00192         
00193         for (conn = conn_list; conn; conn = conn->next)
00194                 if (conn->domain && !strcasecmp(conn->domain, domain) 
00195                                 && conn->type == type)
00196                         return conn;
00197         return NULL;
00198 }
00199 
00200 /*
00201 static struct xmpp_connection *conn_find_fd(int fd)
00202 {
00203         struct xmpp_connection *conn;
00204         
00205         for (conn = conn_list; conn; conn = conn->next)
00206                 if (conn->fd == fd)
00207                         return conn;
00208         return NULL;
00209 }
00210 */
00211 
00212 /*****************************************************************************/
00213 
00214 static int xode_send(int fd, xode x)
00215 {
00216         char *str = xode_to_str(x);
00217         int len = strlen(str);
00218         
00219         LM_DBG("xode_send->%d [%s]\n", fd, str);
00220 
00221         if (net_send(fd, str, len) != len) {
00222                 LM_ERR("send() failed: %s\n", strerror(errno));
00223                 return -1;
00224         }
00225         return len;
00226 }
00227 
00228 static int xode_send_domain(char *domain, xode x)
00229 {
00230         struct xmpp_connection *conn;
00231 
00232         if ((conn = conn_find_domain(domain, CONN_OUTBOUND))) {
00233                 xode_send(conn->fd, x);
00234                 xode_free(x);
00235         } else {
00236                 if((conn = conn_new(CONN_OUTBOUND, -1, domain))==0)
00237                         return -1;
00238                 xode_insert_node(conn->todo, x);
00239         }
00240         return 1;
00241 }
00242 
00243 static void out_stream_node_callback(int type, xode node, void *arg)
00244 {
00245         struct xmpp_connection *conn = (struct xmpp_connection *) arg;
00246         struct xmpp_connection *in_conn = NULL;
00247         char *tag;
00248         xode x;
00249 
00250         LM_DBG("outstream callback: %d: %s\n", type, 
00251                         node?xode_get_name(node):"n/a");
00252 
00253         if (conn->domain)
00254                 in_conn = conn_find_domain(conn->domain, CONN_INBOUND);
00255 
00256         switch (type) {
00257         case XODE_STREAM_ROOT:
00258                 x = xode_new_tag("db:result");
00259                 xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00260                 xode_put_attrib(x, "from", xmpp_domain);
00261                 xode_put_attrib(x, "to", conn->domain);
00262                 //xode_insert_cdata(x, DB_KEY, -1);
00263                 xode_insert_cdata(x, db_key(local_secret, conn->domain,
00264                                         xode_get_attrib(node, "id")), -1);
00265                 xode_send(conn->fd, x);
00266                 xode_free(x);
00267 
00268                 break;
00269         case XODE_STREAM_NODE:
00270                 tag = xode_get_name(node);
00271 
00272                 if (!strcmp(tag, "db:verify")) {
00273                         char *from = xode_get_attrib(node, "from");
00274                         char *to = xode_get_attrib(node, "to");
00275                         char *id = xode_get_attrib(node, "id");
00276                         char *type = xode_get_attrib(node, "type");
00277                         /* char *cdata = xode_get_data(node); */
00278                         
00279                         if (!strcmp(type, "valid") || !strcmp(type, "invalid")) {
00280                                 /* got a reply, report it */
00281                                 x = xode_new_tag("db:result");
00282                                 xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00283                                 xode_put_attrib(x, "from", to);
00284                                 xode_put_attrib(x, "to", from);
00285                                 xode_put_attrib(x, "id", id);
00286                                 xode_put_attrib(x, "type", type);
00287                                 if (in_conn)
00288                                         xode_send(in_conn->fd, x);
00289                                 else
00290                                         LM_ERR("need to send reply to domain '%s', but no inbound"
00291                                                         " connection found\n", from);
00292                                 xode_free(x);
00293                         }
00294                 } else if (!strcmp(tag, "db:result")) {
00295                         char *type = xode_get_attrib(node, "type");
00296                         
00297                         if (type && !strcmp(type, "valid")) {
00298                                 /* the remote server has successfully authenticated us,
00299                                  * we can now send data */
00300                                 for (x = xode_get_firstchild(conn->todo); x;
00301                                                 x = xode_get_nextsibling(x)) {
00302                                         LM_DBG("sending todo tag '%s'\n", xode_get_name(x));
00303                                         xode_send(conn->fd, x);
00304                                 }
00305                                 xode_free(conn->todo);
00306                                 conn->todo = NULL;
00307                         }
00308                 }
00309                 break;
00310         case XODE_STREAM_ERROR:
00311                 LM_ERR("outstream error\n");
00312                 /* fall-through */
00313         case XODE_STREAM_CLOSE:
00314                 conn->type = CONN_DEAD;
00315                 break;
00316         }
00317         xode_free(node);
00318 }
00319 
00320 static void in_stream_node_callback(int type, xode node, void *arg) 
00321 {
00322         struct xmpp_connection *conn = (struct xmpp_connection *) arg;
00323         char *tag;
00324         xode x;
00325 
00326         LM_DBG("instream callback: %d: %s\n",
00327                         type, node ? xode_get_name(node) : "n/a");
00328         switch (type) {
00329         case XODE_STREAM_ROOT:
00330                 conn->stream_id = strdup(random_secret());
00331                 net_printf(conn->fd,
00332                         "<?xml version='1.0'?>"
00333                         "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:server' version='1.0'"
00334                         " xmlns:db='jabber:server:dialback' id='%s' from='%s'>", conn->stream_id, xmpp_domain);
00335                 net_printf(conn->fd,"<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>");
00336                 break;
00337         case XODE_STREAM_NODE:
00338                 tag = xode_get_name(node);
00339 
00340                 if (!strcmp(tag, "db:result")) {
00341                         char *from = xode_get_attrib(node, "from");
00342                         char *to = xode_get_attrib(node, "to");
00343                         /* char *id = xode_get_attrib(node, "id"); */
00344                         char *type = xode_get_attrib(node, "type");
00345                         char *cdata = xode_get_data(node);
00346                         
00347                         if (!type) {
00348                                 if (conn->domain) {
00349                                         LM_DBG("connection %d has old domain '%s'\n",conn->fd,
00350                                                         conn->domain);
00351                                         free(conn->domain);
00352                                 }
00353                                 conn->domain = strdup(from);
00354                                 LM_DBG("connection %d set domain '%s'\n",
00355                                                 conn->fd, conn->domain);
00356 
00357                                 /* it's a request; send verification over outgoing connection */
00358                                 x = xode_new_tag("db:verify");
00359                                 xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00360                                 xode_put_attrib(x, "from", to);
00361                                 xode_put_attrib(x, "to", from);
00362                                 //xode_put_attrib(x, "id", "someid"); /* XXX fix ID */
00363                                 xode_put_attrib(x, "id", conn->stream_id);
00364                                 xode_insert_cdata(x, cdata, -1);
00365                                 xode_send_domain(from, x);
00366                         }                       
00367                 } else if (!strcmp(tag, "db:verify")) {
00368                         char *from = xode_get_attrib(node, "from");
00369                         char *to = xode_get_attrib(node, "to");
00370                         char *id = xode_get_attrib(node, "id");
00371                         char *type = xode_get_attrib(node, "type");
00372                         char *cdata = xode_get_data(node);
00373                         
00374                         if (!type) {
00375                                 /* it's a request */
00376                                 x = xode_new_tag("db:verify");
00377                                 xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00378                                 xode_put_attrib(x, "from", to);
00379                                 xode_put_attrib(x, "to", from);
00380                                 xode_put_attrib(x, "id", id);
00381                                 //if (cdata && !strcmp(cdata, DB_KEY)) {
00382                                 if (cdata && !strcmp(cdata, db_key(local_secret, from, id))) {
00383                                         xode_put_attrib(x, "type", "valid");
00384                                 } else {
00385                                         xode_put_attrib(x, "type", "invalid");
00386                                 }
00387                                 xode_send(conn->fd, x);
00388                                 xode_free(x);
00389                         }                       
00390                 } else if (!strcmp(tag, "message")) {
00391                         char *from = xode_get_attrib(node, "from");
00392                         char *to = xode_get_attrib(node, "to");
00393                         char *type = xode_get_attrib(node, "type");
00394                         xode body = xode_get_tag(node, "body");
00395                         char *msg;
00396                         
00397                         if (!type)
00398                                 type = "chat";
00399                         if (!strcmp(type, "error")) {   
00400                                 LM_DBG("received message error stanza\n");
00401                                 goto out;
00402                         }
00403                         
00404                         if (!from || !to || !body) {
00405                                 LM_DBG("invalid <message/> attributes\n");
00406                                 goto out;
00407                         }
00408 
00409                         if (!(msg = xode_get_data(body)))
00410                                 msg = "";
00411                         xmpp_send_sip_msg(
00412                                 encode_uri_xmpp_sip(from),
00413                                 decode_uri_xmpp_sip(to),
00414                                 msg);
00415                 } else if (!strcmp(tag, "presence")) {
00416                         /* run presence callbacks */
00417                 }
00418                 break;
00419 
00420                 break;
00421         case XODE_STREAM_ERROR:
00422                 LM_ERR("instream error\n");
00423                 /* fall-through */
00424         case XODE_STREAM_CLOSE:
00425                 conn->type = CONN_DEAD;
00426                 break;
00427         }
00428 out:
00429         xode_free(node);
00430 }
00431 
00432 static void do_send_message_server(struct xmpp_pipe_cmd *cmd)
00433 {
00434         char *domain;
00435         xode x;
00436 
00437         LM_DBG("rom=[%s] to=[%s] body=[%s]\n", cmd->from,cmd->to, cmd->body);
00438 
00439         x = xode_new_tag("message");
00440         xode_put_attrib(x, "xmlns", "jabber:client");
00441         xode_put_attrib(x, "id", cmd->id); // XXX
00442         xode_put_attrib(x, "from", encode_uri_sip_xmpp(cmd->from));
00443         xode_put_attrib(x, "to", decode_uri_sip_xmpp(cmd->to));
00444         xode_put_attrib(x, "type", "chat");
00445         xode_insert_cdata(xode_insert_tag(x, "body"), cmd->body, -1);
00446 
00447         domain = extract_domain(decode_uri_sip_xmpp(cmd->to));
00448         xode_send_domain(domain, x);
00449 }
00450 
00451 int xmpp_server_child_process(int data_pipe)
00452 {
00453         int rv;
00454         int listen_fd;
00455         fd_set fdset;
00456         struct xmpp_connection *conn;
00457         
00458         snprintf(local_secret, sizeof(local_secret), "%s", random_secret());
00459 
00460         while ((listen_fd = net_listen(xmpp_domain, xmpp_port)) < 0) {
00461                 /* ugh. */
00462                 sleep(3);
00463         }
00464 
00465         while (1) {
00466                 FD_ZERO(&fdset);
00467                 FD_SET(data_pipe, &fdset);
00468                 FD_SET(listen_fd, &fdset);
00469                 
00470                 /* check for dead connections */
00471                 for (conn = conn_list; conn; ) {
00472                         struct xmpp_connection *next = conn->next;
00473 
00474                         if (conn->type == CONN_DEAD)
00475                                 conn_free(conn);
00476                         conn = next;
00477                 }
00478 
00479                 for (conn = conn_list; conn; conn = conn->next) {
00480                         /* check if we need to set up a connection */
00481                         if (conn->type == CONN_OUTBOUND && conn->fd == -1) {
00482                                 if ((conn->fd = net_connect(conn->domain, xmpp_port)) >= 0)
00483                                 {
00484                                         net_printf(conn->fd,
00485                                                 "<?xml version='1.0'?>"
00486                                                 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:server' version='1.0' "
00487                                                 "xmlns:db='jabber:server:dialback' to='%s' from='%s'>",
00488                                                 conn->domain, xmpp_domain);
00489                                         net_printf(conn->fd,
00490                            "<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>");
00491                                 } else {
00492                                         conn->type = CONN_DEAD;
00493                                 }
00494                         }               
00495 
00496                         if (conn->fd != -1)
00497                                 FD_SET(conn->fd, &fdset);
00498                 }
00499 
00500                 rv = select(FD_SETSIZE, &fdset, NULL, NULL, NULL);
00501 
00502                 /* update the local config framework structures */
00503                 cfg_update();
00504 
00505                 if (rv < 0) {
00506                         LM_ERR("select() failed: %s\n", strerror(errno));
00507                 } else if (!rv) {
00508                         /* timeout */
00509                 } else {
00510                         for (conn = conn_list; conn; conn = conn->next) {
00511                                 if (conn->fd != -1 && FD_ISSET(conn->fd, &fdset)) {
00512                                         char *buf = net_read_static(conn->fd);
00513                                         if (!buf) {
00514                                                 conn->type = CONN_DEAD;
00515                                         } else {
00516                                                 LM_DBG("stream (fd %d, domain '%s') read\n[%s]\n",
00517                                                                 conn->fd, conn->domain, buf);
00518                                                 xode_stream_eat(conn->stream, buf, strlen(buf));
00519                                         }
00520                                 }
00521                         }
00522 
00523                         if (FD_ISSET(listen_fd, &fdset)) {
00524                                 struct sockaddr_in sin;
00525                                 unsigned int len = sizeof(sin);
00526                                 int fd;
00527 
00528                                 if ((fd = accept(listen_fd,(struct sockaddr*)&sin, &len))<0) {
00529                                         LM_ERR("accept() failed: %s\n", strerror(errno));
00530                                 } else {
00531                                         LM_DBG("accept()ed connection from %s:%d\n",
00532                                                         inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
00533                                         conn_new(CONN_INBOUND, fd, NULL);
00534                                 }
00535                         }
00536 
00537                         if (FD_ISSET(data_pipe, &fdset)) {
00538                                 struct xmpp_pipe_cmd *cmd;
00539 
00540                                 if (read(data_pipe, &cmd, sizeof(cmd)) != sizeof(cmd)) {
00541                                         LM_ERR("failed to read from command pipe: %s\n",
00542                                                         strerror(errno));
00543                                 } else {
00544                                         LM_DBG("got pipe cmd %d\n", cmd->type);
00545                                         switch (cmd->type) {
00546                                         case XMPP_PIPE_SEND_MESSAGE:
00547                                                 do_send_message_server(cmd);
00548                                                 break;
00549                                         case XMPP_PIPE_SEND_PACKET:
00550                                         case XMPP_PIPE_SEND_PSUBSCRIBE:
00551                                         case XMPP_PIPE_SEND_PNOTIFY:
00552                                                 break;
00553                                         }
00554                                         xmpp_free_pipe_cmd(cmd);
00555                                 }
00556                         }
00557                 }
00558         }
00559         return 0;
00560 }
00561