xmpp_server.c

Go to the documentation of this file.
00001 /*
00002  * $Id: xmpp_server.c 4585 2008-08-06 08:20:30Z klaus_darilion $
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  */
00026 /*! \file
00027  * \brief Kamailio XMPP :: XMPP server implementation (limited functionality)
00028  *  \ingroup xmpp
00029  */
00030 
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 
00095 #include "xmpp.h"
00096 #include "xmpp_api.h"
00097 #include "network.h"
00098 #include "xode.h"
00099 
00100 #include <arpa/inet.h>
00101 
00102 /* XXX hack */
00103 #define DB_KEY "this-be-a-random-key"
00104 
00105 #define CONN_DEAD 0
00106 #define CONN_INBOUND 1
00107 #define CONN_OUTBOUND   2
00108 
00109 struct xmpp_private_data {
00110    int fd;     /* outgoing stream socket */
00111    int listen_fd; /* listening socket */
00112    int in_fd;  /* incoming stream socket */
00113    int running;
00114 };
00115 
00116 struct xmpp_connection {
00117    struct xmpp_connection *next;
00118 
00119    char *domain;
00120    int type;
00121    int fd;
00122    char *stream_id;
00123    xode_pool pool;
00124    xode_stream stream;
00125    xode todo;  /* backlog of outgoing messages, if any */
00126 };
00127 
00128 static char local_secret[64] = { 0, };
00129 
00130 static void in_stream_node_callback(int type, xode node, void *arg);
00131 static void out_stream_node_callback(int type, xode node, void *arg);
00132 
00133 static struct xmpp_connection *conn_list = NULL;
00134 
00135 static struct xmpp_connection *conn_new(int type, int fd, char *domain)
00136 {
00137    struct xmpp_connection *conn = NULL;
00138    
00139    conn = malloc(sizeof(struct xmpp_connection));
00140 
00141    if(conn==NULL) 
00142    {
00143       LM_ERR("out of memory\n");
00144       return NULL;
00145    }
00146    
00147    memset(conn, 0, sizeof(struct xmpp_connection));
00148    conn->domain = domain ? strdup(domain) : NULL;
00149    conn->type = type;
00150    conn->fd = fd;
00151    conn->todo = xode_new_tag("todo");
00152 
00153    conn->pool = xode_pool_new();
00154    conn->stream = xode_stream_new(conn->pool,
00155       (type==CONN_INBOUND)?in_stream_node_callback:out_stream_node_callback,
00156       conn);
00157    
00158    conn->next = conn_list;
00159    conn_list = conn;
00160    return conn;
00161 }
00162 
00163 static void conn_free(struct xmpp_connection *conn)
00164 {
00165    struct xmpp_connection **last_p, *link;
00166    
00167    last_p = &conn_list;
00168    for (link = conn_list; link; link = link->next) {
00169       if (link == conn) {
00170          *last_p = link->next;
00171          break;
00172       }
00173       last_p = &link->next;
00174    }
00175 
00176    if (conn->todo)
00177       xode_free(conn->todo);
00178    xode_pool_free(conn->pool);
00179    if (conn->fd != -1)
00180       close(conn->fd);
00181    if (conn->stream_id)
00182       free(conn->stream_id);
00183    if (conn->domain)
00184       free(conn->domain);
00185    free(conn);
00186 }
00187 
00188 static struct xmpp_connection *conn_find_domain(char *domain, int type)
00189 {
00190    struct xmpp_connection *conn;
00191    
00192    for (conn = conn_list; conn; conn = conn->next)
00193       if (conn->domain && !strcasecmp(conn->domain, domain) 
00194             && conn->type == type)
00195          return conn;
00196    return NULL;
00197 }
00198 
00199 /*
00200 static struct xmpp_connection *conn_find_fd(int fd)
00201 {
00202    struct xmpp_connection *conn;
00203    
00204    for (conn = conn_list; conn; conn = conn->next)
00205       if (conn->fd == fd)
00206          return conn;
00207    return NULL;
00208 }
00209 */
00210 
00211 /*****************************************************************************/
00212 
00213 static int xode_send(int fd, xode x)
00214 {
00215    char *str = xode_to_str(x);
00216    int len = strlen(str);
00217    
00218    LM_DBG("xode_send->%d [%s]\n", fd, str);
00219 
00220    if (net_send(fd, str, len) != len) {
00221       LM_ERR("send() failed: %s\n", strerror(errno));
00222       return -1;
00223    }
00224    return len;
00225 }
00226 
00227 static int xode_send_domain(char *domain, xode x)
00228 {
00229    struct xmpp_connection *conn;
00230 
00231    if ((conn = conn_find_domain(domain, CONN_OUTBOUND))) {
00232       xode_send(conn->fd, x);
00233       xode_free(x);
00234    } else {
00235       if((conn = conn_new(CONN_OUTBOUND, -1, domain))==0)
00236          return -1;
00237       xode_insert_node(conn->todo, x);
00238    }
00239    return 1;
00240 }
00241 
00242 static void out_stream_node_callback(int type, xode node, void *arg)
00243 {
00244    struct xmpp_connection *conn = (struct xmpp_connection *) arg;
00245    struct xmpp_connection *in_conn = NULL;
00246    char *tag;
00247    xode x;
00248 
00249    LM_DBG("outstream callback: %d: %s\n", type, 
00250          node?xode_get_name(node):"n/a");
00251 
00252    if (conn->domain)
00253       in_conn = conn_find_domain(conn->domain, CONN_INBOUND);
00254 
00255    switch (type) {
00256    case XODE_STREAM_ROOT:
00257       x = xode_new_tag("db:result");
00258       xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00259       xode_put_attrib(x, "from", xmpp_domain);
00260       xode_put_attrib(x, "to", conn->domain);
00261       //xode_insert_cdata(x, DB_KEY, -1);
00262       xode_insert_cdata(x, db_key(local_secret, conn->domain,
00263                xode_get_attrib(node, "id")), -1);
00264       xode_send(conn->fd, x);
00265       xode_free(x);
00266 
00267       break;
00268    case XODE_STREAM_NODE:
00269       tag = xode_get_name(node);
00270 
00271       if (!strcmp(tag, "db:verify")) {
00272          char *from = xode_get_attrib(node, "from");
00273          char *to = xode_get_attrib(node, "to");
00274          char *id = xode_get_attrib(node, "id");
00275          char *type = xode_get_attrib(node, "type");
00276          /* char *cdata = xode_get_data(node); */
00277          
00278          if (!strcmp(type, "valid") || !strcmp(type, "invalid")) {
00279             /* got a reply, report it */
00280             x = xode_new_tag("db:result");
00281             xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00282             xode_put_attrib(x, "from", to);
00283             xode_put_attrib(x, "to", from);
00284             xode_put_attrib(x, "id", id);
00285             xode_put_attrib(x, "type", type);
00286             if (in_conn)
00287                xode_send(in_conn->fd, x);
00288             else
00289                LM_ERR("need to send reply to domain '%s', but no inbound"
00290                      " connection found\n", from);
00291             xode_free(x);
00292          }
00293       } else if (!strcmp(tag, "db:result")) {
00294          char *type = xode_get_attrib(node, "type");
00295          
00296          if (type && !strcmp(type, "valid")) {
00297             /* the remote server has successfully authenticated us,
00298              * we can now send data */
00299             for (x = xode_get_firstchild(conn->todo); x;
00300                   x = xode_get_nextsibling(x)) {
00301                LM_DBG("sending todo tag '%s'\n", xode_get_name(x));
00302                xode_send(conn->fd, x);
00303             }
00304             xode_free(conn->todo);
00305             conn->todo = NULL;
00306          }
00307       }
00308       break;
00309    case XODE_STREAM_ERROR:
00310       LM_ERR("outstream error\n");
00311       /* fall-through */
00312    case XODE_STREAM_CLOSE:
00313       conn->type = CONN_DEAD;
00314       break;
00315    }
00316    xode_free(node);
00317 }
00318 
00319 static void in_stream_node_callback(int type, xode node, void *arg) 
00320 {
00321    struct xmpp_connection *conn = (struct xmpp_connection *) arg;
00322    char *tag;
00323    xode x;
00324 
00325    LM_DBG("instream callback: %d: %s\n",
00326          type, node ? xode_get_name(node) : "n/a");
00327    switch (type) {
00328    case XODE_STREAM_ROOT:
00329       conn->stream_id = strdup(random_secret());
00330       net_printf(conn->fd,
00331          "<?xml version='1.0'?>"
00332          "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:server' version='1.0'"
00333          " xmlns:db='jabber:server:dialback' id='%s' from='%s'>", conn->stream_id, xmpp_domain);
00334       net_printf(conn->fd,"<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>");
00335       break;
00336    case XODE_STREAM_NODE:
00337       tag = xode_get_name(node);
00338 
00339       if (!strcmp(tag, "db:result")) {
00340          char *from = xode_get_attrib(node, "from");
00341          char *to = xode_get_attrib(node, "to");
00342          /* char *id = xode_get_attrib(node, "id"); */
00343          char *type = xode_get_attrib(node, "type");
00344          char *cdata = xode_get_data(node);
00345          
00346          if (!type) {
00347             if (conn->domain) {
00348                LM_DBG("connection %d has old domain '%s'\n",conn->fd,
00349                      conn->domain);
00350                free(conn->domain);
00351             }
00352             conn->domain = strdup(from);
00353             LM_DBG("connection %d set domain '%s'\n",
00354                   conn->fd, conn->domain);
00355 
00356             /* it's a request; send verification over outgoing connection */
00357             x = xode_new_tag("db:verify");
00358             xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00359             xode_put_attrib(x, "from", to);
00360             xode_put_attrib(x, "to", from);
00361             //xode_put_attrib(x, "id", "someid"); /* XXX fix ID */
00362             xode_put_attrib(x, "id", conn->stream_id);
00363             xode_insert_cdata(x, cdata, -1);
00364             xode_send_domain(from, x);
00365          }        
00366       } else if (!strcmp(tag, "db:verify")) {
00367          char *from = xode_get_attrib(node, "from");
00368          char *to = xode_get_attrib(node, "to");
00369          char *id = xode_get_attrib(node, "id");
00370          char *type = xode_get_attrib(node, "type");
00371          char *cdata = xode_get_data(node);
00372          
00373          if (!type) {
00374             /* it's a request */
00375             x = xode_new_tag("db:verify");
00376             xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
00377             xode_put_attrib(x, "from", to);
00378             xode_put_attrib(x, "to", from);
00379             xode_put_attrib(x, "id", id);
00380             //if (cdata && !strcmp(cdata, DB_KEY)) {
00381             if (cdata && !strcmp(cdata, db_key(local_secret, from, id))) {
00382                xode_put_attrib(x, "type", "valid");
00383             } else {
00384                xode_put_attrib(x, "type", "invalid");
00385             }
00386             xode_send(conn->fd, x);
00387             xode_free(x);
00388          }        
00389       } else if (!strcmp(tag, "message")) {
00390          char *from = xode_get_attrib(node, "from");
00391          char *to = xode_get_attrib(node, "to");
00392          char *type = xode_get_attrib(node, "type");
00393          xode body = xode_get_tag(node, "body");
00394          char *msg;
00395          
00396          if (!type)
00397             type = "chat";
00398          if (!strcmp(type, "error")) { 
00399             LM_DBG("received message error stanza\n");
00400             goto out;
00401          }
00402          
00403          if (!from || !to || !body) {
00404             LM_DBG("invalid <message/> attributes\n");
00405             goto out;
00406          }
00407 
00408          if (!(msg = xode_get_data(body)))
00409             msg = "";
00410          xmpp_send_sip_msg(
00411             encode_uri_xmpp_sip(from),
00412             decode_uri_xmpp_sip(to),
00413             msg);
00414       } else if (!strcmp(tag, "presence")) {
00415          /* run presence callbacks */
00416       }
00417       break;
00418 
00419       break;
00420    case XODE_STREAM_ERROR:
00421       LM_ERR("instream error\n");
00422       /* fall-through */
00423    case XODE_STREAM_CLOSE:
00424       conn->type = CONN_DEAD;
00425       break;
00426    }
00427 out:
00428    xode_free(node);
00429 }
00430 
00431 static void do_send_message_server(struct xmpp_pipe_cmd *cmd)
00432 {
00433    char *domain;
00434    xode x;
00435 
00436    LM_DBG("rom=[%s] to=[%s] body=[%s]\n", cmd->from,cmd->to, cmd->body);
00437 
00438    x = xode_new_tag("message");
00439    xode_put_attrib(x, "xmlns", "jabber:client");
00440    xode_put_attrib(x, "id", cmd->id); // XXX
00441    xode_put_attrib(x, "from", encode_uri_sip_xmpp(cmd->from));
00442    xode_put_attrib(x, "to", decode_uri_sip_xmpp(cmd->to));
00443    xode_put_attrib(x, "type", "chat");
00444    xode_insert_cdata(xode_insert_tag(x, "body"), cmd->body, -1);
00445 
00446    domain = extract_domain(decode_uri_sip_xmpp(cmd->to));
00447    xode_send_domain(domain, x);
00448 }
00449 
00450 int xmpp_server_child_process(int data_pipe)
00451 {
00452    int rv;
00453    int listen_fd;
00454    fd_set fdset;
00455    struct xmpp_connection *conn;
00456    
00457    snprintf(local_secret, sizeof(local_secret), "%s", random_secret());
00458 
00459    while ((listen_fd = net_listen(xmpp_domain, xmpp_port)) < 0) {
00460       /* ugh. */
00461       sleep(3);
00462    }
00463 
00464    while (1) {
00465       FD_ZERO(&fdset);
00466       FD_SET(data_pipe, &fdset);
00467       FD_SET(listen_fd, &fdset);
00468       
00469       /* check for dead connections */
00470       for (conn = conn_list; conn; ) {
00471          struct xmpp_connection *next = conn->next;
00472 
00473          if (conn->type == CONN_DEAD)
00474             conn_free(conn);
00475          conn = next;
00476       }
00477 
00478       for (conn = conn_list; conn; conn = conn->next) {
00479          /* check if we need to set up a connection */
00480          if (conn->type == CONN_OUTBOUND && conn->fd == -1) {
00481             if ((conn->fd = net_connect(conn->domain, xmpp_port)) >= 0)
00482             {
00483                net_printf(conn->fd,
00484                   "<?xml version='1.0'?>"
00485                   "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:server' version='1.0' "
00486                   "xmlns:db='jabber:server:dialback' to='%s' from='%s'>",
00487                   conn->domain, xmpp_domain);
00488                net_printf(conn->fd,
00489                            "<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>");
00490             } else {
00491                conn->type = CONN_DEAD;
00492             }
00493          }     
00494 
00495          if (conn->fd != -1)
00496             FD_SET(conn->fd, &fdset);
00497       }
00498 
00499       rv = select(FD_SETSIZE, &fdset, NULL, NULL, NULL);
00500       if (rv < 0) {
00501          LM_ERR("select() failed: %s\n", strerror(errno));
00502       } else if (!rv) {
00503          /* timeout */
00504       } else {
00505          for (conn = conn_list; conn; conn = conn->next) {
00506             if (conn->fd != -1 && FD_ISSET(conn->fd, &fdset)) {
00507                char *buf = net_read_static(conn->fd);
00508                if (!buf) {
00509                   conn->type = CONN_DEAD;
00510                } else {
00511                   LM_DBG("stream (fd %d, domain '%s') read\n[%s]\n",
00512                         conn->fd, conn->domain, buf);
00513                   xode_stream_eat(conn->stream, buf, strlen(buf));
00514                }
00515             }
00516          }
00517 
00518          if (FD_ISSET(listen_fd, &fdset)) {
00519             struct sockaddr_in sin;
00520             unsigned int len = sizeof(sin);
00521             int fd;
00522 
00523             if ((fd = accept(listen_fd,(struct sockaddr*)&sin, &len))<0) {
00524                LM_ERR("accept() failed: %s\n", strerror(errno));
00525             } else {
00526                LM_DBG("accept()ed connection from %s:%d\n",
00527                      inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
00528                conn_new(CONN_INBOUND, fd, NULL);
00529             }
00530          }
00531 
00532          if (FD_ISSET(data_pipe, &fdset)) {
00533             struct xmpp_pipe_cmd *cmd;
00534 
00535             if (read(data_pipe, &cmd, sizeof(cmd)) != sizeof(cmd)) {
00536                LM_ERR("failed to read from command pipe: %s\n",
00537                      strerror(errno));
00538             } else {
00539                LM_DBG("got pipe cmd %d\n", cmd->type);
00540                switch (cmd->type) {
00541                case XMPP_PIPE_SEND_MESSAGE:
00542                   do_send_message_server(cmd);
00543                   break;
00544                case XMPP_PIPE_SEND_PACKET:
00545                case XMPP_PIPE_SEND_PSUBSCRIBE:
00546                case XMPP_PIPE_SEND_PNOTIFY:
00547                   break;
00548                }
00549                xmpp_free_pipe_cmd(cmd);
00550             }
00551          }
00552       }
00553    }
00554    return 0;
00555 }
00556 

Generated on Fri May 25 00:00:35 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6