#include "dialback.h"
char *dialback_randstr(void)
{
static char ret[41];
sprintf(ret,"%d",rand());
shahash_r(ret,ret);
return ret;
}
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);
if(jp == NULL)
{
log_warn(md->d->i->id, "dropping invalid packet from server: %s",xmlnode2str(x));
xmlnode_free(x);
return;
}
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;
}
struct miodc
{
miod md;
HASHTABLE ht;
jid key;
};
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);
if(mdc->ht == mdc->md->d->out_ok_db){
unregister_instance(mdc->md->d->i, mdc->key->server);
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);
if(ht == md->d->out_ok_db || ht == md->d->out_ok_legacy)
{
dialback_ip_set(md->d, key, md->m->ip);
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;
old = (xmlnode)ghash_get(d->nscache,host->server);
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);
xmlnode_free(old);
}
result dialback_packets(instance i, dpacket dp, void *arg)
{
db d = (db)arg;
xmlnode x = dp->x;
char *ip = NULL;
if(dp->type == p_ROUTE)
{
x = xmlnode_get_firstchild(x);
ip = xmlnode_get_attrib(dp->x,"ip");
}
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");
dialback_in_verify(d, x);
return r_DONE;
}
dialback_out_packet(d, x, ip);
return r_DONE;
}
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;
}
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;
}
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));
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)
d->secret = pstrdup(i->p,dialback_randstr());
if(xmlnode_get_tag(cfg,"legacy") != NULL)
d->legacy = 1;
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;
}
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;
}
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;
if(set_rate == 1) mio_rate(m, rate_time, rate_points);
if(set_karma == 1) mio_karma2(m, &k);
}
else
{
mio m;
m = mio_listen(5269,NULL,dialback_in_read,(void*)d, MIO_LISTEN_XML);
if(m == NULL) return;
if(set_rate == 1) mio_rate(m, rate_time, rate_points);
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);
}