/* -------------------------------------------------------------------------- * * License * * The contents of this file are subject to the Jabber Open Source License * Version 1.0 (the "JOSL"). You may not copy or use this file, in either * source code or executable form, except in compliance with the JOSL. You * may obtain a copy of the JOSL at http://www.jabber.org/ or at * http://www.opensource.org/. * * Software distributed under the JOSL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL * for the specific language governing rights and limitations under the * JOSL. * * Copyrights * * Portions created by or assigned to Jabber.com, Inc. are * Copyright (c) 1999-2002 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. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License Version 2 or later (the "GPL"), in which case * the provisions of the GPL are applicable instead of those above. If you * wish to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the JOSL, * indicate your decision by deleting the provisions above and replace them * with the notice and other provisions required by the GPL. If you do not * delete the provisions above, a recipient may use your version of this file * under either the JOSL or the GPL. * * * --------------------------------------------------------------------------*/ /* ../load/pthsock_server.so pthsock-s2s.127.0.0.1 ../load/pthsock_server.so DIALBACK: A->B A: ... B->A B: ... A: A->B B: */ #include "dialback.h" /* we need a decently random string in a few places */ char *dialback_randstr(void) { static char ret[41]; sprintf(ret,"%d",rand()); shahash_r(ret,ret); return ret; } /* convenience */ char *dialback_merlin(pool p, char *secret, char *to, char *challenge) { static char res[41]; shahash_r(secret, res); shahash_r(spools(p, res, to, p), res); shahash_r(spools(p, res, challenge, p), res); log_debug(ZONE,"merlin casts his spell(%s+%s+%s) %s",secret,to,challenge,res); return res; } void dialback_miod_write(miod md, xmlnode x) { md->count++; md->last = time(NULL); mio_write(md->m, x, NULL, -1); } void dialback_miod_read(miod md, xmlnode x) { jpacket jp = jpacket_new(x); /* only accept valid jpackets! */ if(jp == NULL) { log_warn(md->d->i->id, "dropping invalid packet from server: %s",xmlnode2str(x)); xmlnode_free(x); return; } /* send it on! */ md->count++; md->last = time(NULL); deliver(dpacket_new(x),md->d->i); } miod dialback_miod_new(db d, mio m) { miod md; md = pmalloco(m->p, sizeof(_miod)); md->m = m; md->d = d; md->last = time(NULL); return md; } /******* little wrapper to keep our hash tables in check ****/ struct miodc { miod md; HASHTABLE ht; jid key; }; /* clean up a hashtable entry containing this miod */ void _dialback_miod_hash_cleanup(void *arg) { struct miodc *mdc = (struct miodc *)arg; if(ghash_get(mdc->ht,jid_full(mdc->key)) == mdc->md) ghash_remove(mdc->ht,jid_full(mdc->key)); log_debug(ZONE,"miod cleaning out socket %d with key %s to hash %X",mdc->md->m->fd, jid_full(mdc->key), mdc->ht); /* cool place for logging, eh? interesting way of detecting things too, *g* */ if(mdc->ht == mdc->md->d->out_ok_db){ unregister_instance(mdc->md->d->i, mdc->key->server); /* dynamic host resolution thingie */ log_record(mdc->key->server, "out", "dialback", "%d %s %s", mdc->md->count, mdc->md->m->ip, mdc->key->resource); }else if(mdc->ht == mdc->md->d->out_ok_legacy){ unregister_instance(mdc->md->d->i, mdc->key->server); log_record(mdc->key->server, "out", "legacy", "%d %s %s", mdc->md->count, mdc->md->m->ip, mdc->key->resource); }else if(mdc->ht == mdc->md->d->in_ok_db){ log_record(mdc->key->server, "in", "dialback", "%d %s %s", mdc->md->count, mdc->md->m->ip, mdc->key->resource); }else if(mdc->ht == mdc->md->d->in_ok_legacy){ log_record(mdc->key->server, "in", "legacy", "%d %s %s", mdc->md->count, mdc->md->m->ip, mdc->key->resource); } } void dialback_miod_hash(miod md, HASHTABLE ht, jid key) { struct miodc *mdc; log_debug(ZONE,"miod registering socket %d with key %s to hash %X",md->m->fd, jid_full(key), ht); mdc = pmalloco(md->m->p,sizeof(struct miodc)); mdc->md = md; mdc->ht = ht; mdc->key = jid_new(md->m->p,jid_full(key)); pool_cleanup(md->m->p, _dialback_miod_hash_cleanup, (void *)mdc); ghash_put(ht, jid_full(mdc->key), md); /* dns saver, only when registering on outgoing hosts dynamically */ if(ht == md->d->out_ok_db || ht == md->d->out_ok_legacy) { dialback_ip_set(md->d, key, md->m->ip); /* save the ip since it won't be going through the dnsrv anymore */ register_instance(md->d->i, key->server); } } char *dialback_ip_get(db d, jid host, char *ip) { char *ret; if(host == NULL) return NULL; if(ip != NULL) return ip; ret = pstrdup(host->p,xmlnode_get_attrib((xmlnode)ghash_get(d->nscache,host->server),"i")); log_debug(ZONE,"returning cached ip %s for %s",ret,host->server); return ret; } void dialback_ip_set(db d, jid host, char *ip) { xmlnode cache, old; if(host == NULL || ip == NULL) return; /* first, get existing cache so we can dump it later */ old = (xmlnode)ghash_get(d->nscache,host->server); /* new cache */ cache = xmlnode_new_tag("d"); xmlnode_put_attrib(cache,"h",host->server); xmlnode_put_attrib(cache,"i",ip); ghash_put(d->nscache,xmlnode_get_attrib(cache,"h"),(void*)cache); log_debug(ZONE,"cached ip %s for %s",ip,host->server); /* free any old entry that's been replaced */ xmlnode_free(old); } /* phandler callback, send packets to another server */ result dialback_packets(instance i, dpacket dp, void *arg) { db d = (db)arg; xmlnode x = dp->x; char *ip = NULL; /* routes are from dnsrv w/ the needed ip */ if(dp->type == p_ROUTE) { x = xmlnode_get_firstchild(x); ip = xmlnode_get_attrib(dp->x,"ip"); } /* all packets going to our "id" go to the incoming handler, * it uses that id to send out db:verifies to other servers, * and end up here when they bounce */ if(j_strcmp(xmlnode_get_attrib(x,"to"),d->i->id) == 0) { xmlnode_put_attrib(x,"to",xmlnode_get_attrib(x,"ofrom")); xmlnode_hide_attrib(x,"ofrom"); /* repair the addresses */ dialback_in_verify(d, x); return r_DONE; } dialback_out_packet(d, x, ip); return r_DONE; } /* callback for walking each miod-value host hash tree */ int _dialback_beat_idle(void *arg, const void *key, void *data) { miod md = (miod)data; if(((int)*(time_t*)arg - md->last) >= md->d->timeout_idle) { log_debug(ZONE,"Idle Timeout on socket %d to %s",md->m->fd, md->m->ip); mio_close(md->m); } return 1; } /* heartbeat checker for timed out idle hosts */ result dialback_beat_idle(void *arg) { db d = (db)arg; time_t ttmp; log_debug(ZONE,"dialback idle check"); time(&ttmp); ghash_walk(d->out_ok_db,_dialback_beat_idle,(void*)&ttmp); ghash_walk(d->out_ok_legacy,_dialback_beat_idle,(void*)&ttmp); ghash_walk(d->in_ok_db,_dialback_beat_idle,(void*)&ttmp); ghash_walk(d->in_ok_legacy,_dialback_beat_idle,(void*)&ttmp); return r_DONE; } /*** everything starts here ***/ void dialback(instance i, xmlnode x) { db d; xmlnode cfg, cur; struct karma k; int max; int rate_time, rate_points; int set_rate = 0, set_karma=0; log_debug(ZONE,"dialback loading"); srand(time(NULL)); /* get the config */ cfg = xdb_get(xdb_cache(i),jid_new(xmlnode_pool(x),"config@-internal"),"jabber:config:dialback"); max = j_atoi(xmlnode_get_tag_data(cfg,"maxhosts"),997); d = pmalloco(i->p,sizeof(_db)); d->nscache = ghash_create(max,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); d->out_connecting = ghash_create(67,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); d->out_ok_db = ghash_create(max,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); d->out_ok_legacy = ghash_create(max,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); d->in_id = ghash_create(max,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); d->in_ok_db = ghash_create(max,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); d->in_ok_legacy = ghash_create(max,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); d->i = i; d->timeout_idle = j_atoi(xmlnode_get_tag_data(cfg,"idletimeout"),900); d->timeout_packets = j_atoi(xmlnode_get_tag_data(cfg,"queuetimeout"),30); d->secret = pstrdup(i->p,xmlnode_get_tag_data(cfg,"secret")); if(d->secret == NULL) /* if there's no configured secret, make one on the fly */ d->secret = pstrdup(i->p,dialback_randstr()); if(xmlnode_get_tag(cfg,"legacy") != NULL) d->legacy = 1; /* Get rate info if it exists */ if((cur = xmlnode_get_tag(cfg, "rate")) != NULL) { rate_time = j_atoi(xmlnode_get_attrib(cur, "time"), 0); rate_points = j_atoi(xmlnode_get_attrib(cur, "points"), 0); set_rate = 1; /* set to true */ } /* Get karma info if it exists */ if((cur = xmlnode_get_tag(cfg, "karma")) != NULL) { k.val = j_atoi(xmlnode_get_tag_data(cur, "init"), KARMA_INIT); k.max = j_atoi(xmlnode_get_tag_data(cur, "max"), KARMA_MAX); k.inc = j_atoi(xmlnode_get_tag_data(cur, "inc"), KARMA_INC); k.dec = j_atoi(xmlnode_get_tag_data(cur, "dec"), KARMA_DEC); k.restore = j_atoi(xmlnode_get_tag_data(cur, "restore"), KARMA_RESTORE); k.penalty = j_atoi(xmlnode_get_tag_data(cur, "penalty"), KARMA_PENALTY); k.reset_meter = j_atoi(xmlnode_get_tag_data(cur, "resetmeter"), KARMA_RESETMETER); set_karma = 1; /* set to true */ } if((cur = xmlnode_get_tag(cfg,"ip")) != NULL) for(;cur != NULL; xmlnode_hide(cur), cur = xmlnode_get_tag(cfg,"ip")) { mio m; m = mio_listen(j_atoi(xmlnode_get_attrib(cur,"port"),5269),xmlnode_get_data(cur),dialback_in_read,(void*)d, MIO_LISTEN_XML); if(m == NULL) return; /* Set New rate and points */ if(set_rate == 1) mio_rate(m, rate_time, rate_points); /* Set New karma values */ if(set_karma == 1) mio_karma2(m, &k); } else /* no special config, use defaults */ { mio m; m = mio_listen(5269,NULL,dialback_in_read,(void*)d, MIO_LISTEN_XML); if(m == NULL) return; /* Set New rate and points */ if(set_rate == 1) mio_rate(m, rate_time, rate_points); /* Set New karma values */ if(set_karma == 1) mio_karma2(m, &k); } register_phandler(i,o_DELIVER,dialback_packets,(void*)d); register_beat(d->timeout_idle, dialback_beat_idle, (void *)d); register_beat(d->timeout_packets, dialback_out_beat_packets, (void *)d); xmlnode_free(cfg); }