/* --------------------------------------------------------------------------
*
* License
*
* The contents of this file are subject to the Jabber Open Source License
* Version 1.0 (the "License"). You may not copy or use this file, in either
* source code or executable form, except in compliance with the License. You
* may obtain a copy of the License at http://www.jabber.com/license/ or at
* http://www.opensource.org/.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Copyrights
*
* Portions created by or assigned to Jabber.com, Inc. are
* Copyright (c) 1999-2000 Jabber.com, Inc. All Rights Reserved. Contact
* information for Jabber.com, Inc. is available at http://www.jabber.com/.
*
* Portions Copyright (c) 1998-1999 Jeremie Miller.
*
* Acknowledgements
*
* Special thanks to the Jabber Open Source Contributors for their
* suggestions and support of Jabber.
*
* --------------------------------------------------------------------------*/
#include "jabberd.h"
extern pool jabberd__runtime;
#define A_ERROR -1
#define A_READY 1
#define A_CLOSING 2
typedef struct queue_struct
{
int stamp;
xmlnode x;
struct queue_struct *next;
} *queue, _queue;
typedef struct accept_instance_st
{
mio m;
int state;
char *id;
pool p;
instance i;
char *ip;
char *secret;
int port;
int timeout;
int log;
queue q_start,q_end;
dpacket dplast;
pthread_mutex_t sem;
} *accept_instance, _accept_instance;
/* main thread ! */
void base_accept_kill(void *arg)
{
accept_instance ai = (accept_instance)arg;
log_debug(ZONE,"base accept kill");
if (ai->state == A_READY){
xmlnode h;
log_debug(ZONE,"send end");
/* send request to end transmission to wpj and other transports */
h = xmlnode_new_tag("endconnection");
if (ai->m != NULL) {
mio_write(ai->m, h, NULL, -1);
ai->state = A_CLOSING;
}
else
xmlnode_free(h);
}
}
void base_accept_queue(accept_instance ai, xmlnode x)
{
queue q;
if(ai == NULL || x == NULL) return;
pthread_mutex_lock(&(ai->sem));
q = pmalloco(xmlnode_pool(x),sizeof(_queue));
q->stamp = time(NULL);
q->x = x;
q->next = NULL;
/* is there something */
if (ai->q_end)
ai->q_end->next = q;
else
ai->q_start = q;
ai->q_end = q;
pthread_mutex_unlock(&(ai->sem));
}
/* Write packets to a xmlio object */
result base_accept_deliver(instance i, dpacket p, void* arg)
{
accept_instance ai = (accept_instance)arg;
/* Insert the message into the write_queue if we don't have a MIO socket yet.. */
if (ai->state == A_READY || ai->state == A_CLOSING )
{
if(ai->dplast == p) /* don't return packets that they sent us! circular reference! */
deliver_fail(p,"Circular Refernce Detected");
else
mio_write(ai->m, p->x, NULL, 0);
return r_DONE;
}
base_accept_queue(ai, p->x);
return r_DONE;
}
/* Handle incoming packets from the xstream associated with an MIO object */
void base_accept_process_xml(mio m, int state, void* arg, xmlnode x)
{
accept_instance ai = (accept_instance)arg;
xmlnode cur;
queue q, q2;
char hashbuf[41];
int socket_size = 32768;
int s = sizeof(int);
log_debug(ZONE, "process XML: m:%X state:%d, arg:%X, x:%X", m, state, arg, x);
switch(state)
{
case MIO_NEW:
setsockopt(m->fd,SOL_SOCKET,SO_SNDBUF,(char *)&socket_size,s);
setsockopt(m->fd,SOL_SOCKET,SO_RCVBUF,(char *)&socket_size,s);
socket_size = 1;
setsockopt(m->fd,SOL_SOCKET,SO_KEEPALIVE,(char *)&socket_size,s);
return;
case MIO_XML_ROOT:
/* Ensure request namespace is correct... */
if (j_strcmp(xmlnode_get_attrib(x, "xmlns"), "jabber:component:accept") != 0)
{
/* Log that the connected component sent an invalid namespace */
log_warn(ai->i->id, "Recv'd invalid namespace. Closing connection.");
/* Notify component with stream:error */
mio_write(m, NULL, SERROR_NAMESPACE, -1);
/* Close the socket and cleanup */
mio_close(m);
break;
}
/* Send header w/ proper namespace, using instance i */
cur = xstream_header("jabber:component:accept", NULL, ai->i->id);
/* Save stream ID for auth'ing later */
ai->id = pstrdup(ai->p, xmlnode_get_attrib(cur, "id"));
/* override if other connections */
ai->state = A_ERROR;
mio_write(m, NULL, xstream_header_char(cur), -1);
xmlnode_free(cur);
break;
case MIO_XML_NODE:
/* If aio has been authenticated previously, go ahead and deliver the packet */
/* deliver packets even if closing */
if(ai->state == A_READY || ai->state == A_CLOSING) {
/* Hide 1.0 style transports etherx:* attribs */
xmlnode_hide_attrib(x, "etherx:to");
xmlnode_hide_attrib(x, "etherx:from");
ai->dplast = dpacket_new(x);
/* FOR LOG */
if ((ai->log >= 0) && (ai->dplast)){
cur = NULL;
if(ai->dplast->type == p_ROUTE) {
cur = xmlnode_get_firstchild(ai->dplast->x);
}
else
if(ai->dplast->type == p_NORM) {
cur = ai->dplast->x;
}
if (cur){
if(strncmp(xmlnode_get_name(cur),"message",7) == 0) {
xmlnode curx;
__wplog->tab[ai->log]->messages++;
if ((curx = xmlnode_get_tag(cur,"x"))
&&(j_strncmp(xmlnode_get_attrib(curx,"type"),"req",3)== 0))
__wplog->tab[ai->log]->files++;
}
else if(strncmp(xmlnode_get_name(cur),"presence",8) == 0){
__wplog->tab[ai->log]->presences++;
}
else if(strncmp(xmlnode_get_name(cur),"iq",2) == 0){
__wplog->tab[ai->log]->queries++;
}
else if(strncmp(xmlnode_get_name(cur),"iq",2) == 0){
__wplog->tab[ai->log]->queries++;
}
}
}
/* END LOG */
/* xdb packet sends through socket goes to mtq */
if ((ai->dplast) && (ai->dplast->type == p_XDB)) {
/* XXX mtq_send */
deliver(ai->dplast, ai->i);
ai->dplast = NULL;
return;
}
else {
deliver(ai->dplast, ai->i);
ai->dplast = NULL;
return;
}
}
/* only other packets are handshakes */
if(j_strcmp(xmlnode_get_name(x), "handshake") != 0) {
mio_write(m, NULL, "Must send handshake first.", -1);
mio_close(m);
break;
}
/* Create and check a SHA hash of this instance's password & SID */
shahash_r(spools(xmlnode_pool(x), ai->id, ai->secret, xmlnode_pool(x)), hashbuf);
if(j_strcmp(hashbuf, xmlnode_get_data(x)) != 0) {
mio_write(m, NULL, "Invalid handshake", -1);
mio_close(m);
break;
}
/* Send a handshake confirmation with our start time */
cur = xmlnode_new_tag("handshake");
xmlnode_put_attrib(cur,"time",__wplog->server_start);
mio_write(m, cur, NULL, -1);
/* check for existing conenction and kill it */
if(ai->m != NULL) {
log_warn(ai->i->id, "Socket override by another connection from %s",mio_ip(m));
mio_write(ai->m, NULL, "Socket override by another connection.", -1);
mio_close(ai->m);
}
/* hook us up! */
ai->m = m;
ai->state = A_READY;
/* flush old queue */
pthread_mutex_lock(&(ai->sem));
q = ai->q_start;
while(q != NULL) {
q2 = q->next;
mio_write(m, q->x, NULL, 0);
q = q2;
}
ai->q_start = NULL;
ai->q_end = NULL;
pthread_mutex_unlock(&(ai->sem));
break;
case MIO_ERROR:
/* make sure it's the important one */
log_debug(ZONE,"m=%d ai->m = %d",m,ai->m);
if(m != ai->m)
return;
ai->state = A_ERROR;
/* clean up any tirds */
while((cur = mio_cleanup(m)) != NULL)
deliver_fail(dpacket_new(cur), "External Server Error");
return;
case MIO_CLOSED:
/* make sure it's the important one */
if(m != ai->m)
return;
log_debug(ZONE,"closing accepted socket");
ai->m = NULL;
ai->state = A_ERROR;
return;
default:
return;
}
xmlnode_free(x);
}
/* check the packet queue for stale packets */
result base_accept_beat(void *arg)
{
accept_instance ai = (accept_instance)arg;
queue last, cur, next;
int now = time(NULL);
pthread_mutex_lock(&(ai->sem));
cur = ai->q_start;
while(cur != NULL)
{
if( (now - cur->stamp) <= ai->timeout)
{
last = cur;
cur = cur->next;
continue;
}
/* timed out sukkah! */
next = cur->next;
/* if first */
if (ai->q_start == cur)
{
ai->q_start = next;
/* first and last */
if (ai->q_end == cur)
ai->q_end = next; /* next must be NULL in here */
}
else /* if last */
if (ai->q_end == cur)
{
ai->q_end = last;
}
else /* if not first, not last */
{
last->next = next;
}
deliver_fail(dpacket_new(cur->x),"Internal Timeout");
cur = next;
}
pthread_mutex_unlock(&(ai->sem));
return r_DONE;
}
result base_accept_config(instance id, xmlnode x, void *arg)
{
char *secret = xmlnode_get_tag_data(x, "secret");
accept_instance inst;
int port = j_atoi(xmlnode_get_tag_data(x, "port"),0);
if(id == NULL)
{
log_debug(ZONE,"base_accept_config validating configuration...");
if (port == 0 || (xmlnode_get_tag(x,"secret") == NULL))
{
xmlnode_put_attrib(x,"error"," requires the following subtags: , and ");
return r_ERR;
}
return r_PASS;
}
log_debug(ZONE,"base_accept_config performing configuration %s\n",xmlnode2str(x));
/* Setup the default sink for this instance */
inst = pmalloco(id->p, sizeof(_accept_instance));
inst->p = id->p;
inst->i = id;
inst->secret = secret;
inst->ip = xmlnode_get_tag_data(x,"ip");
inst->port = port;
inst->log = j_atoi(xmlnode_get_tag_data(x, "log"),-1);
inst->timeout = j_atoi(xmlnode_get_tag_data(x, "timeout"),30); /* 30 sec */
pthread_mutex_init(&(inst->sem),NULL);
/* Start a new listening thread and associate this tag with it */
if(mio_listen(inst->port, inst->ip, base_accept_process_xml, (void*)inst, NULL, mio_handlers_new(NULL, NULL, MIO_XML_PARSER)) == NULL)
{
xmlnode_put_attrib(x,"error"," unable to listen on the configured ip and port");
return r_ERR;
}
/* Register a packet handler and cleanup heartbeat for this instance */
register_phandler(id, o_DELIVER, base_accept_deliver, (void *)inst);
/* timeout check */
register_beat(inst->timeout, base_accept_beat, (void *)inst);
/* base shutdown kill */
register_shutdown(base_accept_kill, (void *)inst);
return r_DONE;
}
void base_accept(void)
{
log_debug(ZONE,"base_accept loading...\n");
register_config("accept",base_accept_config,NULL);
}