123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- /*
- * $Id$
- *
- * XMPP Module
- * This file is part of Kamailio, a free SIP server.
- *
- * Copyright (C) 2006 Voice Sistem S.R.L.
- *
- * Kamailio is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * Kamailio is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Author: Andreea Spirea
- *
- */
- /*! \file
- * \brief Kamailio XMPP :: XMPP server implementation (limited functionality)
- * \ingroup xmpp
- */
- /*
- * An inbound SIP message:
- * from sip:user1@domain1 to sip:user2*domain2@gateway_domain
- * is translated to an XMPP message:
- * from user1*domain1@xmpp_domain to user2@domain2
- *
- * An inbound XMPP message:
- * from user1@domain1 to user2*domain2@xmpp_domain
- * is translated to a SIP message:
- * from sip:user1*domain1@gateway_domain to sip:user2@domain2
- *
- * Where '*' is the domain_separator, and gateway_domain and
- * xmpp_domain are defined below.
- */
- /*
- * 2-way dialback sequence with xmppd2:
- *
- * Originating server (us) Receiving server (them) Authoritative server (us)
- * ----------------------- ----------------------- -------------------------
- * | | |
- * | establish connection | |
- * |------------------------------>| |
- * | send stream header | |
- * |------------------------------>| |
- * | send stream header | |
- * |<------------------------------| |
- * | send db:result request | |
- * |------------------------------>| |
- * | establish connection |
- * |------------------------------>|
- * | send stream header |
- * |------------------------------>|
- * | send stream header |
- * |<------------------------------|
- * | send db:result request |
- * |------------------------------>|
- * | send db:verify request |
- * |------------------------------>|
- * | send db:verify response |
- * |<------------------------------|
- * | send db:result response |
- * |------------------------------>|
- * | send db:verify request |
- * |<------------------------------|
- * | send db:verify response |
- * |------------------------------>|
- * | send db:result response |
- * |<------------------------------|
- * : : :
- * : : :
- * | outgoing <message/> | :
- * |------------------------------>| :
- * | incoming <message/> |
- * |------------------------------>|
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <string.h>
- #include "../../sr_module.h"
- #include "../../cfg/cfg_struct.h"
- #include "xmpp.h"
- #include "xmpp_api.h"
- #include "network.h"
- #include "xode.h"
- #include <arpa/inet.h>
- /* XXX hack */
- #define DB_KEY "this-be-a-random-key"
- #define CONN_DEAD 0
- #define CONN_INBOUND 1
- #define CONN_OUTBOUND 2
- struct xmpp_private_data {
- int fd; /* outgoing stream socket */
- int listen_fd; /* listening socket */
- int in_fd; /* incoming stream socket */
- int running;
- };
- struct xmpp_connection {
- struct xmpp_connection *next;
- char *domain;
- int type;
- int fd;
- char *stream_id;
- xode_pool pool;
- xode_stream stream;
- xode todo; /* backlog of outgoing messages, if any */
- };
- static char local_secret[64] = { 0, };
- static void in_stream_node_callback(int type, xode node, void *arg);
- static void out_stream_node_callback(int type, xode node, void *arg);
- static struct xmpp_connection *conn_list = NULL;
- static struct xmpp_connection *conn_new(int type, int fd, char *domain)
- {
- struct xmpp_connection *conn = NULL;
-
- conn = malloc(sizeof(struct xmpp_connection));
- if(conn==NULL)
- {
- LM_ERR("out of memory\n");
- return NULL;
- }
-
- memset(conn, 0, sizeof(struct xmpp_connection));
- conn->domain = domain ? strdup(domain) : NULL;
- conn->type = type;
- conn->fd = fd;
- conn->todo = xode_new_tag("todo");
- conn->pool = xode_pool_new();
- conn->stream = xode_stream_new(conn->pool,
- (type==CONN_INBOUND)?in_stream_node_callback:out_stream_node_callback,
- conn);
-
- conn->next = conn_list;
- conn_list = conn;
- return conn;
- }
- static void conn_free(struct xmpp_connection *conn)
- {
- struct xmpp_connection **last_p, *link;
-
- last_p = &conn_list;
- for (link = conn_list; link; link = link->next) {
- if (link == conn) {
- *last_p = link->next;
- break;
- }
- last_p = &link->next;
- }
- if (conn->todo)
- xode_free(conn->todo);
- xode_pool_free(conn->pool);
- if (conn->fd != -1)
- close(conn->fd);
- if (conn->stream_id)
- free(conn->stream_id);
- if (conn->domain)
- free(conn->domain);
- free(conn);
- }
- static struct xmpp_connection *conn_find_domain(char *domain, int type)
- {
- struct xmpp_connection *conn;
-
- for (conn = conn_list; conn; conn = conn->next)
- if (conn->domain && !strcasecmp(conn->domain, domain)
- && conn->type == type)
- return conn;
- return NULL;
- }
- /*
- static struct xmpp_connection *conn_find_fd(int fd)
- {
- struct xmpp_connection *conn;
-
- for (conn = conn_list; conn; conn = conn->next)
- if (conn->fd == fd)
- return conn;
- return NULL;
- }
- */
- /*****************************************************************************/
- static int xode_send(int fd, xode x)
- {
- char *str = xode_to_str(x);
- int len = strlen(str);
-
- LM_DBG("xode_send->%d [%s]\n", fd, str);
- if (net_send(fd, str, len) != len) {
- LM_ERR("send() failed: %s\n", strerror(errno));
- return -1;
- }
- return len;
- }
- static int xode_send_domain(char *domain, xode x)
- {
- struct xmpp_connection *conn;
- if ((conn = conn_find_domain(domain, CONN_OUTBOUND))) {
- xode_send(conn->fd, x);
- xode_free(x);
- } else {
- if((conn = conn_new(CONN_OUTBOUND, -1, domain))==0)
- return -1;
- xode_insert_node(conn->todo, x);
- }
- return 1;
- }
- static void out_stream_node_callback(int type, xode node, void *arg)
- {
- struct xmpp_connection *conn = (struct xmpp_connection *) arg;
- struct xmpp_connection *in_conn = NULL;
- char *tag;
- xode x;
- LM_DBG("outstream callback: %d: %s\n", type,
- node?xode_get_name(node):"n/a");
- if (conn->domain)
- in_conn = conn_find_domain(conn->domain, CONN_INBOUND);
- switch (type) {
- case XODE_STREAM_ROOT:
- x = xode_new_tag("db:result");
- xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
- xode_put_attrib(x, "from", xmpp_domain);
- xode_put_attrib(x, "to", conn->domain);
- //xode_insert_cdata(x, DB_KEY, -1);
- xode_insert_cdata(x, db_key(local_secret, conn->domain,
- xode_get_attrib(node, "id")), -1);
- xode_send(conn->fd, x);
- xode_free(x);
- break;
- case XODE_STREAM_NODE:
- tag = xode_get_name(node);
- if (!strcmp(tag, "db:verify")) {
- char *from = xode_get_attrib(node, "from");
- char *to = xode_get_attrib(node, "to");
- char *id = xode_get_attrib(node, "id");
- char *type = xode_get_attrib(node, "type");
- /* char *cdata = xode_get_data(node); */
-
- if (!strcmp(type, "valid") || !strcmp(type, "invalid")) {
- /* got a reply, report it */
- x = xode_new_tag("db:result");
- xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
- xode_put_attrib(x, "from", to);
- xode_put_attrib(x, "to", from);
- xode_put_attrib(x, "id", id);
- xode_put_attrib(x, "type", type);
- if (in_conn)
- xode_send(in_conn->fd, x);
- else
- LM_ERR("need to send reply to domain '%s', but no inbound"
- " connection found\n", from);
- xode_free(x);
- }
- } else if (!strcmp(tag, "db:result")) {
- char *type = xode_get_attrib(node, "type");
-
- if (type && !strcmp(type, "valid")) {
- /* the remote server has successfully authenticated us,
- * we can now send data */
- for (x = xode_get_firstchild(conn->todo); x;
- x = xode_get_nextsibling(x)) {
- LM_DBG("sending todo tag '%s'\n", xode_get_name(x));
- xode_send(conn->fd, x);
- }
- xode_free(conn->todo);
- conn->todo = NULL;
- }
- }
- break;
- case XODE_STREAM_ERROR:
- LM_ERR("outstream error\n");
- /* fall-through */
- case XODE_STREAM_CLOSE:
- conn->type = CONN_DEAD;
- break;
- }
- xode_free(node);
- }
- static void in_stream_node_callback(int type, xode node, void *arg)
- {
- struct xmpp_connection *conn = (struct xmpp_connection *) arg;
- char *tag;
- xode x;
- LM_DBG("instream callback: %d: %s\n",
- type, node ? xode_get_name(node) : "n/a");
- switch (type) {
- case XODE_STREAM_ROOT:
- conn->stream_id = strdup(random_secret());
- net_printf(conn->fd,
- "<?xml version='1.0'?>"
- "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:server' version='1.0'"
- " xmlns:db='jabber:server:dialback' id='%s' from='%s'>", conn->stream_id, xmpp_domain);
- net_printf(conn->fd,"<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>");
- break;
- case XODE_STREAM_NODE:
- tag = xode_get_name(node);
- if (!strcmp(tag, "db:result")) {
- char *from = xode_get_attrib(node, "from");
- char *to = xode_get_attrib(node, "to");
- /* char *id = xode_get_attrib(node, "id"); */
- char *type = xode_get_attrib(node, "type");
- char *cdata = xode_get_data(node);
-
- if (!type) {
- if (conn->domain) {
- LM_DBG("connection %d has old domain '%s'\n",conn->fd,
- conn->domain);
- free(conn->domain);
- }
- conn->domain = strdup(from);
- LM_DBG("connection %d set domain '%s'\n",
- conn->fd, conn->domain);
- /* it's a request; send verification over outgoing connection */
- x = xode_new_tag("db:verify");
- xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
- xode_put_attrib(x, "from", to);
- xode_put_attrib(x, "to", from);
- //xode_put_attrib(x, "id", "someid"); /* XXX fix ID */
- xode_put_attrib(x, "id", conn->stream_id);
- xode_insert_cdata(x, cdata, -1);
- xode_send_domain(from, x);
- }
- } else if (!strcmp(tag, "db:verify")) {
- char *from = xode_get_attrib(node, "from");
- char *to = xode_get_attrib(node, "to");
- char *id = xode_get_attrib(node, "id");
- char *type = xode_get_attrib(node, "type");
- char *cdata = xode_get_data(node);
-
- if (!type) {
- /* it's a request */
- x = xode_new_tag("db:verify");
- xode_put_attrib(x, "xmlns:db", "jabber:server:dialback");
- xode_put_attrib(x, "from", to);
- xode_put_attrib(x, "to", from);
- xode_put_attrib(x, "id", id);
- //if (cdata && !strcmp(cdata, DB_KEY)) {
- if (cdata && !strcmp(cdata, db_key(local_secret, from, id))) {
- xode_put_attrib(x, "type", "valid");
- } else {
- xode_put_attrib(x, "type", "invalid");
- }
- xode_send(conn->fd, x);
- xode_free(x);
- }
- } else if (!strcmp(tag, "message")) {
- char *from = xode_get_attrib(node, "from");
- char *to = xode_get_attrib(node, "to");
- char *type = xode_get_attrib(node, "type");
- xode body = xode_get_tag(node, "body");
- char *msg;
-
- if (!type)
- type = "chat";
- if (!strcmp(type, "error")) {
- LM_DBG("received message error stanza\n");
- goto out;
- }
-
- if (!from || !to || !body) {
- LM_DBG("invalid <message/> attributes\n");
- goto out;
- }
- if (!(msg = xode_get_data(body)))
- msg = "";
- xmpp_send_sip_msg(
- encode_uri_xmpp_sip(from),
- decode_uri_xmpp_sip(to),
- msg);
- } else if (!strcmp(tag, "presence")) {
- /* run presence callbacks */
- }
- break;
- break;
- case XODE_STREAM_ERROR:
- LM_ERR("instream error\n");
- /* fall-through */
- case XODE_STREAM_CLOSE:
- conn->type = CONN_DEAD;
- break;
- }
- out:
- xode_free(node);
- }
- static void do_send_message_server(struct xmpp_pipe_cmd *cmd)
- {
- char *domain;
- xode x;
- LM_DBG("rom=[%s] to=[%s] body=[%s]\n", cmd->from,cmd->to, cmd->body);
- x = xode_new_tag("message");
- xode_put_attrib(x, "xmlns", "jabber:client");
- xode_put_attrib(x, "id", cmd->id); // XXX
- xode_put_attrib(x, "from", encode_uri_sip_xmpp(cmd->from));
- xode_put_attrib(x, "to", decode_uri_sip_xmpp(cmd->to));
- xode_put_attrib(x, "type", "chat");
- xode_insert_cdata(xode_insert_tag(x, "body"), cmd->body, -1);
- domain = extract_domain(decode_uri_sip_xmpp(cmd->to));
- xode_send_domain(domain, x);
- }
- int xmpp_server_child_process(int data_pipe)
- {
- int rv;
- int listen_fd;
- fd_set fdset;
- struct xmpp_connection *conn;
-
- snprintf(local_secret, sizeof(local_secret), "%s", random_secret());
- while ((listen_fd = net_listen(xmpp_domain, xmpp_port)) < 0) {
- /* ugh. */
- sleep(3);
- }
- while (1) {
- FD_ZERO(&fdset);
- FD_SET(data_pipe, &fdset);
- FD_SET(listen_fd, &fdset);
-
- /* check for dead connections */
- for (conn = conn_list; conn; ) {
- struct xmpp_connection *next = conn->next;
- if (conn->type == CONN_DEAD)
- conn_free(conn);
- conn = next;
- }
- for (conn = conn_list; conn; conn = conn->next) {
- /* check if we need to set up a connection */
- if (conn->type == CONN_OUTBOUND && conn->fd == -1) {
- if ((conn->fd = net_connect(conn->domain, xmpp_port)) >= 0)
- {
- net_printf(conn->fd,
- "<?xml version='1.0'?>"
- "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:server' version='1.0' "
- "xmlns:db='jabber:server:dialback' to='%s' from='%s'>",
- conn->domain, xmpp_domain);
- net_printf(conn->fd,
- "<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>");
- } else {
- conn->type = CONN_DEAD;
- }
- }
- if (conn->fd != -1)
- FD_SET(conn->fd, &fdset);
- }
- rv = select(FD_SETSIZE, &fdset, NULL, NULL, NULL);
- /* update the local config framework structures */
- cfg_update();
- if (rv < 0) {
- LM_ERR("select() failed: %s\n", strerror(errno));
- } else if (!rv) {
- /* timeout */
- } else {
- for (conn = conn_list; conn; conn = conn->next) {
- if (conn->fd != -1 && FD_ISSET(conn->fd, &fdset)) {
- char *buf = net_read_static(conn->fd);
- if (!buf) {
- conn->type = CONN_DEAD;
- } else {
- LM_DBG("stream (fd %d, domain '%s') read\n[%s]\n",
- conn->fd, conn->domain, buf);
- xode_stream_eat(conn->stream, buf, strlen(buf));
- }
- }
- }
- if (FD_ISSET(listen_fd, &fdset)) {
- struct sockaddr_in sin;
- unsigned int len = sizeof(sin);
- int fd;
- if ((fd = accept(listen_fd,(struct sockaddr*)&sin, &len))<0) {
- LM_ERR("accept() failed: %s\n", strerror(errno));
- } else {
- LM_DBG("accept()ed connection from %s:%d\n",
- inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
- conn_new(CONN_INBOUND, fd, NULL);
- }
- }
- if (FD_ISSET(data_pipe, &fdset)) {
- struct xmpp_pipe_cmd *cmd;
- if (read(data_pipe, &cmd, sizeof(cmd)) != sizeof(cmd)) {
- LM_ERR("failed to read from command pipe: %s\n",
- strerror(errno));
- } else {
- LM_DBG("got pipe cmd %d\n", cmd->type);
- switch (cmd->type) {
- case XMPP_PIPE_SEND_MESSAGE:
- do_send_message_server(cmd);
- break;
- case XMPP_PIPE_SEND_PACKET:
- case XMPP_PIPE_SEND_PSUBSCRIBE:
- case XMPP_PIPE_SEND_PNOTIFY:
- break;
- }
- xmpp_free_pipe_cmd(cmd);
- }
- }
- }
- }
- return 0;
- }
|