/* * jabberd - Jabber Open Source Server * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney, * Ryan Eatmon, Robert Norris * * This program 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. * * This program 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, MA02111-1307USA */ #include "s2s.h" /* * we handle packets going from the router to the world, and stuff * that comes in on connections we initiated. * * action points: * * out_packet(s2s, nad) - send this packet out * - extract to domain * - check internal resolver cache for ip/port * - if not found * - add packet to queue for this domain * - ask resolver for name * - DONE * - get dbconn for this ip/port * - if dbconn not found * - add packet to queue for this domain * - create new dbconn (key ip/port) * - initiate connect to ip/port * - DONE * - if conn in progress (tcp) * - add packet to queue for this domain * - DONE * - if dbconn state valid for this domain, or packet is dialback * - send packet * - DONE * - if dbconn state invalid for this domain * - bounce packet (502) * - DONE * - add packet to queue for this domain * - if dbconn state inprogress for this domain * - DONE * - out_dialback(dbconn, from, to) * * out_dialback(dbconn, from, to) - initiate dialback * - generate dbkey: sha1(secret+remote+stream id) * - send auth request: dbkey * - set dbconn state for this domain to inprogress * - DONE * * out_resolve(s2s, nad) - responses from resolver * - store ip/port/ttl in resolver cache * - flush domain queue -> out_packet(s2s, nad) * - for each packet in queue for this domain * - DONE * * event_STREAM - ip/port open * - get dbconn for this sx * - for each route handled by this conn, out_dialback(dbconn, from, to) * - DONE * * event_PACKET: - response to our auth request * - get dbconn for this sx * - if type valid * - set dbconn state for this domain to valid * - flush dbconn queue for this domain -> out_packet(s2s, pkt) * - DONE * - set dbconn state for this domain to invalid * - bounce dbconn queue for this domain (502) * - DONE * * event_PACKET: - incoming stream authenticated * - get dbconn for given id * - if type is valid * - set dbconn state for this domain to valid * - send result: * - DONE */ /* forward decls */ static int _out_mio_callback(mio_t m, mio_action_t a, mio_fd_t fd, void *data, void *arg); static int _out_sx_callback(sx_t s, sx_event_t e, void *data, void *arg); static void _out_result(conn_t out, nad_t nad); static void _out_verify(conn_t out, nad_t nad); /** queue the packet */ static void _out_packet_queue(s2s_t s2s, pkt_t pkt) { jqueue_t q = (jqueue_t) xhash_get(s2s->outq, pkt->to->domain); if(q == NULL) { log_debug(ZONE, "creating new out packet queue for %s", pkt->to->domain); q = jqueue_new(); xhash_put(s2s->outq, pstrdup(xhash_pool(s2s->outq), pkt->to->domain), (void *) q); } log_debug(ZONE, "queueing packet for %s", pkt->to->domain); jqueue_push(q, (void *) pkt, 0); } static void _out_dialback(conn_t out, char *rkey) { char *c, *dbkey; nad_t nad; int ns; time_t now; now = time(NULL); c = strchr(rkey, '/'); *c = '\0'; c++; /* kick off the dialback */ dbkey = s2s_db_key(NULL, out->s2s->local_secret, c, out->s->id); nad = nad_new(out->s->nad_cache); /* request auth */ ns = nad_add_namespace(nad, uri_DIALBACK, "db"); nad_append_elem(nad, ns, "result", 0); nad_append_attr(nad, -1, "from", rkey); nad_append_attr(nad, -1, "to", c); nad_append_cdata(nad, dbkey, strlen(dbkey), 1); c--; *c = '/'; log_debug(ZONE, "sending auth request for %s (key %s)", rkey, dbkey); log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] sending dialback auth request for route '%s'", out->fd->fd, out->ip, out->port, rkey); /* off it goes */ sx_nad_write(out->s, nad); free(dbkey); /* we're in progress now */ xhash_put(out->states, pstrdup(xhash_pool(out->states), rkey), (void *) conn_INPROGRESS); /* record the time that we set conn_INPROGRESS state */ xhash_put(out->states_time, pstrdup(xhash_pool(out->states_time), rkey), (void *) now); } /** send a packet out */ void out_packet(s2s_t s2s, pkt_t pkt) { int ns; dnscache_t dns; nad_t nad; char ipport[INET6_ADDRSTRLEN + 16], *rkey; conn_t out; conn_state_t state; /* check resolver cache for ip/port */ dns = xhash_get(s2s->dnscache, pkt->to->domain); if(dns == NULL) { /* new resolution */ log_debug(ZONE, "no dns for %s, preparing for resolution", pkt->to->domain); dns = (dnscache_t) malloc(sizeof(struct dnscache_st)); memset(dns, 0, sizeof(struct dnscache_st)); strcpy(dns->name, pkt->to->domain); xhash_put(s2s->dnscache, dns->name, (void *) dns); #if 0 /* this is good for testing */ dns->pending = 0; strcpy(dns->ip, "127.0.0.1"); dns->port = 3000; dns->expiry = time(NULL) + 99999999; #endif } /* resolution in progress */ if(dns->pending) { log_debug(ZONE, "pending resolution, queueing packet"); _out_packet_queue(s2s, pkt); return; } /* has it expired (this is 0 for new cache objects, so they're always expired */ if(time(NULL) > dns->expiry) { /* it has, queue the packet */ _out_packet_queue(s2s, pkt); /* resolution required */ log_debug(ZONE, "requesting resolution for %s", pkt->to->domain); nad = nad_new(s2s->router->nad_cache); ns = nad_add_namespace(nad, uri_COMPONENT, NULL); nad_append_elem(nad, ns, "route", 0); nad_append_attr(nad, -1, "from", s2s->id); nad_append_attr(nad, -1, "to", s2s->local_resolver); ns = nad_add_namespace(nad, uri_RESOLVER, NULL); nad_append_elem(nad, ns, "resolve", 1); nad_append_attr(nad, -1, "type", "query"); nad_append_attr(nad, -1, "name", pkt->to->domain); sx_nad_write(s2s->router, nad); dns->init_time = time(NULL); dns->pending = 1; return; } /* dns is valid */ strcpy(pkt->ip, dns->ip); pkt->port = dns->port; /* generate the ip/port pair, this is the hash key for the conn */ snprintf(ipport, INET6_ADDRSTRLEN + 16, "%s/%d", pkt->ip, pkt->port); out = (conn_t) xhash_get(s2s->out, ipport); /* new route key */ rkey = s2s_route_key(NULL, pkt->from->domain, pkt->to->domain); /* if no connection, queue the packet and set up connection */ if(out == NULL) { _out_packet_queue(s2s, pkt); /* no conn, create one */ out = (conn_t) malloc(sizeof(struct conn_st)); memset(out, 0, sizeof(struct conn_st)); out->s2s = s2s; out->key = strdup(ipport); strcpy(out->ip, pkt->ip); out->port = pkt->port; out->states = xhash_new(101); out->states_time = xhash_new(101); out->routes = xhash_new(101); out->init_time = time(NULL); xhash_put(s2s->out, out->key, (void *) out); xhash_put(out->routes, pstrdup(xhash_pool(out->routes), rkey), (void *) 1); /* connect */ log_debug(ZONE, "initiating connection to %s", ipport); out->fd = mio_connect(s2s->mio, pkt->port, pkt->ip, _out_mio_callback, (void *) out); if (out->fd == NULL) { log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] mio_connect error: %s (%d)", -1, out->ip, out->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR); /* bounce queues */ out_bounce_queue(s2s, pkt->to->domain, stanza_err_SERVICE_UNAVAILABLE); xhash_zap(s2s->out, out->key); xhash_free(out->states); xhash_free(out->states_time); xhash_free(out->routes); free(out->key); free(out); } else { log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] outgoing connection", out->fd->fd, out->ip, out->port); out->s = sx_new(s2s->sx_env, out->fd->fd, _out_sx_callback, (void *) out); #ifdef HAVE_SSL /* Send a stream version of 1.0 if we can do STARTTLS */ if(out->s2s->sx_ssl != NULL && out->s2s->local_pemfile != NULL) { sx_client_init(out->s, S2S_DB_HEADER, uri_SERVER, pkt->to->domain, NULL, "1.0"); } else { sx_client_init(out->s, S2S_DB_HEADER, uri_SERVER, NULL, NULL, NULL); } #else sx_client_init(out->s, S2S_DB_HEADER, uri_SERVER, NULL, NULL, NULL); #endif } free(rkey); return; } /* connection in progress */ if(!out->online) { log_debug(ZONE, "connection in progress, queueing packet"); _out_packet_queue(s2s, pkt); xhash_put(out->routes, pstrdup(xhash_pool(out->routes), rkey), (void *) 1); free(rkey); return; } /* connection state */ state = (conn_state_t) xhash_get(out->states, rkey); /* valid conns or dialback packets */ if(state == conn_VALID || pkt->db) { log_debug(ZONE, "writing packet for %s to outgoing conn %s", rkey, ipport); /* send it straight out */ if(pkt->db) { /* if this is a db:verify packet, increment counter and set timestamp */ if(NAD_ENAME_L(pkt->nad, 0) == 6 && strncmp("verify", NAD_ENAME(pkt->nad, 0), 6) == 0) { out->verify++; out->last_verify = time(NULL); } /* dialback packet */ sx_nad_write(out->s, pkt->nad); } else { /* if the outgoing stanza has a jabber:client namespace, remove it so that the stream jabber:server namespaces will apply (XMPP 11.2.2) */ int ns = nad_find_namespace(pkt->nad, 1, uri_CLIENT, NULL); if(ns >= 0) { /* clear the namespaces of elem 0 (internal route element) and elem 1 (message|iq|presence) */ pkt->nad->elems[0].ns = -1; pkt->nad->elems[0].my_ns = -1; pkt->nad->elems[1].ns = -1; pkt->nad->elems[1].my_ns = -1; } /* send it out */ sx_nad_write_elem(out->s, pkt->nad, 1); } /* update timestamp */ out->last_packet = time(NULL); jid_free(pkt->from); jid_free(pkt->to); free(pkt); free(rkey); return; } /* can't be handled yet, queue */ _out_packet_queue(s2s, pkt); /* if dialback is in progress, then we're done for now */ if(state == conn_INPROGRESS) { free(rkey); return; } /* this is a new route - send dialback auth request to piggyback on the existing connection */ _out_dialback(out, rkey); free(rkey); } /** responses from the resolver */ void out_resolve(s2s_t s2s, nad_t nad) { int attr, port = 0, ttl = 0, npkt, i; jid_t name; char ip[INET6_ADDRSTRLEN], str[16]; dnscache_t dns; jqueue_t q; pkt_t pkt; attr = nad_find_attr(nad, 1, -1, "name", NULL); name = jid_new(s2s->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr)); /* no results, resolve failed */ if(nad->ecur == 2) { dns = xhash_get(s2s->dnscache, name->domain); xhash_zap(s2s->dnscache, name->domain); free(dns); log_write(s2s->log, LOG_NOTICE, "dns lookup for %s failed", name->domain); /* bounce queue */ out_bounce_queue(s2s, name->domain, stanza_err_REMOTE_SERVER_NOT_FOUND); /* delete queue for domain and remove domain from queue hash */ q = (jqueue_t) xhash_get(s2s->outq, name->domain); if(q != NULL) jqueue_free(q); xhash_zap(s2s->outq, name->domain); jid_free(name); nad_free(nad); return; } snprintf(ip, INET6_ADDRSTRLEN, "%.*s", NAD_CDATA_L(nad, 2), NAD_CDATA(nad, 2)); attr = nad_find_attr(nad, 2, -1, "port", NULL); if(attr >= 0) { snprintf(str, 16, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); port = atoi(str); } if(port == 0) port = 5269; attr = nad_find_attr(nad, 2, -1, "ttl", NULL); if(attr >= 0) { snprintf(str, 16, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); ttl = atoi(str); } log_debug(ZONE, "%s resolved to %s, port %d, ttl %d", name->domain, ip, port, ttl); /* get the cache entry */ dns = xhash_get(s2s->dnscache, name->domain); if(dns == NULL) { log_debug(ZONE, "weird, we never requested this"); jid_free(name); nad_free(nad); return; } /* fill it out */ strcpy(dns->ip, ip); dns->port = port; dns->expiry = time(NULL) + ttl; dns->pending = 0; q = (jqueue_t) xhash_get(s2s->outq, name->domain); npkt = jqueue_size(q); if(q == NULL || npkt == 0) { /* weird */ log_debug(ZONE, "nonexistent or empty queue for domain, we're done"); jid_free(name); nad_free(nad); return; } log_debug(ZONE, "flushing %d packets to out_packet", npkt); for(i = 0; i < npkt; i++) { pkt = jqueue_pull(q); if(pkt) out_packet(s2s, pkt); } jid_free(name); nad_free(nad); } /** mio callback for outgoing conns */ static int _out_mio_callback(mio_t m, mio_action_t a, mio_fd_t fd, void *data, void *arg) { conn_t out = (conn_t) arg; char ipport[INET6_ADDRSTRLEN + 17]; int nbytes; switch(a) { case action_READ: log_debug(ZONE, "read action on fd %d", fd->fd); /* they did something */ out->last_activity = time(NULL); ioctl(fd->fd, FIONREAD, &nbytes); if(nbytes == 0) { sx_kill(out->s); return 0; } return sx_can_read(out->s); case action_WRITE: log_debug(ZONE, "write action on fd %d", fd->fd); /* update activity timestamp */ out->last_activity = time(NULL); return sx_can_write(out->s); case action_CLOSE: log_debug(ZONE, "close action on fd %d", fd->fd); /* bounce queues */ out_bounce_conn_queues(out, stanza_err_SERVICE_UNAVAILABLE); jqueue_push(out->s2s->dead, (void *) out->s, 0); /* generate the ip/port pair */ snprintf(ipport, INET6_ADDRSTRLEN + 16, "%s/%d", out->ip, out->port); log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] disconnect, packets: %i", fd->fd, out->ip, out->port, out->packet_count); xhash_zap(out->s2s->out, ipport); jqueue_push(out->s2s->dead_conn, (void *) out, 0); case action_ACCEPT: break; } return 0; } void send_dialbacks(conn_t out) { char *rkey; if (xhash_iter_first(out->routes)) { log_debug(ZONE, "sending dialback packets for %s", out->key); do { xhash_iter_get(out->routes, (const char **) &rkey, NULL); _out_dialback(out, rkey); } while(xhash_iter_next(out->routes)); } return; } static int _out_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) { conn_t out = (conn_t) arg; sx_buf_t buf = (sx_buf_t) data; int len, ns, elem, starttls = 0; sx_error_t *sxe; nad_t nad; switch(e) { case event_WANT_READ: log_debug(ZONE, "want read"); mio_read(out->s2s->mio, out->fd); break; case event_WANT_WRITE: log_debug(ZONE, "want write"); mio_write(out->s2s->mio, out->fd); break; case event_READ: log_debug(ZONE, "reading from %d", out->fd->fd); /* do the read */ len = recv(out->fd->fd, buf->data, buf->len, 0); if(len < 0) { if(errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN) { buf->len = 0; return 0; } log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] read error: %s (%d)", out->fd->fd, out->ip, out->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR); sx_kill(s); return -1; } else if(len == 0) { /* they went away */ sx_kill(s); return -1; } log_debug(ZONE, "read %d bytes", len); buf->len = len; return len; case event_WRITE: log_debug(ZONE, "writing to %d", out->fd->fd); len = send(out->fd->fd, buf->data, buf->len, 0); if(len >= 0) { log_debug(ZONE, "%d bytes written", len); return len; } if(errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN) return 0; log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] write error: %s (%d)", out->fd->fd, out->ip, out->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR); sx_kill(s); return -1; case event_ERROR: sxe = (sx_error_t *) data; log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] error: %s (%s)", out->fd->fd, out->ip, out->port, sxe->generic, sxe->specific); break; case event_OPEN: log_debug(ZONE, "OPEN event for %s", out->key); break; case event_STREAM: /* check stream version - NULl = pre-xmpp (some jabber1 servers) */ log_debug(ZONE, "STREAM event for %s stream version is %s", out->key, out->s->res_version); /* first time, bring them online */ if(!out->online) { log_debug(ZONE, "outgoing conn to %s is online", out->key); /* if no stream version from either side, kick off dialback for each route, */ /* otherwise wait for stream features */ if ((out->s->res_version==NULL) || (out->s2s->sx_ssl == NULL) || (out->s2s->local_pemfile == NULL)) { log_debug(ZONE, "no stream version, sending dialbacks for %s immediately", out->key); out->online = 1; send_dialbacks(out); } else log_debug(ZONE, "outgoing conn to %s - waiting for STREAM features", out->key); } break; case event_PACKET: /* we're counting packets */ out->packet_count++; out->s2s->packet_count++; nad = (nad_t) data; /* watch for the features packet - STARTTLS and/or SASL*/ if ((out->s->res_version!=NULL) && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_STREAMS) && strncmp(uri_STREAMS, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_STREAMS)) == 0 && NAD_ENAME_L(nad, 0) == 8 && strncmp("features", NAD_ENAME(nad, 0), 8) == 0) { log_debug(ZONE, "got the stream features packet"); #ifdef HAVE_SSL /* starttls if we can */ if(out->s2s->sx_ssl != NULL && out->s2s->local_pemfile != NULL && s->ssf == 0) { ns = nad_find_scoped_namespace(nad, uri_TLS, NULL); if(ns >= 0) { elem = nad_find_elem(nad, 0, ns, "starttls", 1); if(elem >= 0) { log_debug(ZONE, "got STARTTLS in stream features"); if(sx_ssl_client_starttls(out->s2s->sx_ssl, s, out->s2s->local_pemfile) == 0) { starttls = 1; nad_free(nad); return 0; } log_write(out->s2s->log, LOG_ERR, "unable to establish encrypted session with peer"); } } } /* If we're not establishing a starttls connection, send dialbacks */ if (!starttls) { log_debug(ZONE, "No STARTTLS, sending dialbacks for %s", out->key); out->online = 1; send_dialbacks(out); } #else out->online = 1; send_dialbacks(out); #endif } /* we only accept dialback packets */ if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != uri_DIALBACK_L || strncmp(uri_DIALBACK, NAD_NURI(nad, NAD_ENS(nad, 0)), uri_DIALBACK_L) != 0) { log_debug(ZONE, "got a non-dialback packet on an outgoing conn, dropping it"); nad_free(nad); return 0; } /* and then only result and verify */ if(NAD_ENAME_L(nad, 0) == 6) { if(strncmp("result", NAD_ENAME(nad, 0), 6) == 0) { _out_result(out, nad); return 0; } if(strncmp("verify", NAD_ENAME(nad, 0), 6) == 0) { _out_verify(out, nad); return 0; } } log_debug(ZONE, "unknown dialback packet, dropping it"); nad_free(nad); return 0; case event_CLOSED: mio_close(out->s2s->mio, out->fd); return -1; } return 0; } /** process incoming auth responses */ static void _out_result(conn_t out, nad_t nad) { int attr; jid_t from, to; char *rkey, *c; jqueue_t q; int npkt, i; pkt_t pkt; attr = nad_find_attr(nad, 0, -1, "from", NULL); if(attr < 0 || (from = jid_new(out->s2s->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid from on db result packet"); nad_free(nad); return; } attr = nad_find_attr(nad, 0, -1, "to", NULL); if(attr < 0 || (to = jid_new(out->s2s->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid to on db result packet"); jid_free(from); nad_free(nad); return; } rkey = s2s_route_key(NULL, to->domain, from->domain); /* key is valid */ if(nad_find_attr(nad, 0, -1, "type", "valid") >= 0) { log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] outgoing route '%s' is now valid%s", out->fd->fd, out->ip, out->port, rkey, out->s->ssf ? ", TLS negotiated" : ""); xhash_put(out->states, pstrdup(xhash_pool(out->states), rkey), (void *) conn_VALID); /* !!! small leak here */ log_debug(ZONE, "%s valid, flushing queue", rkey); /* to domain */ c = strchr(rkey, '/'); c++; /* flush the queue */ q = (jqueue_t) xhash_get(out->s2s->outq, c); if(q == NULL || (npkt = jqueue_size(q)) == 0) { /* weird */ log_debug(ZONE, "nonexistent or empty queue for domain, we're done"); free(rkey); jid_free(from); jid_free(to); nad_free(nad); return; } log_debug(ZONE, "flushing %d packets to out_packet", npkt); for(i = 0; i < npkt; i++) { pkt = jqueue_pull(q); out_packet(out->s2s, pkt); } free(rkey); jid_free(from); jid_free(to); nad_free(nad); return; } /* invalid */ log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] outgoing route '%s' is now invalid", out->fd->fd, out->ip, out->port, rkey); /* close connection */ log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] closing connection", out->fd->fd, out->ip, out->port); /* report stream error */ sx_error(out->s, stream_err_INVALID_ID, "dialback negotiation failed"); /* close the stream */ sx_close(out->s); free(rkey); jid_free(from); jid_free(to); nad_free(nad); } /** incoming stream authenticated */ static void _out_verify(conn_t out, nad_t nad) { int attr, ns; jid_t from, to; conn_t in; char *rkey; int valid; attr = nad_find_attr(nad, 0, -1, "from", NULL); if(attr < 0 || (from = jid_new(out->s2s->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid from on db verify packet"); nad_free(nad); return; } attr = nad_find_attr(nad, 0, -1, "to", NULL); if(attr < 0 || (to = jid_new(out->s2s->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "missing or invalid to on db verify packet"); jid_free(from); nad_free(nad); return; } attr = nad_find_attr(nad, 0, -1, "id", NULL); if(attr < 0) { log_debug(ZONE, "missing id on db verify packet"); jid_free(from); jid_free(to); nad_free(nad); return; } /* get the incoming conn */ in = xhash_getx(out->s2s->in, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr)); if(in == NULL) { log_debug(ZONE, "got a verify for incoming conn %.*s, but it doesn't exist, dropping the packet", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr)); jid_free(from); jid_free(to); nad_free(nad); return; } rkey = s2s_route_key(NULL, to->domain, from->domain); attr = nad_find_attr(nad, 0, -1, "type", "valid"); if(attr >= 0) { xhash_put(in->states, pstrdup(xhash_pool(in->states), rkey), (void *) conn_VALID); log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] incoming route '%s' is now valid%s", in->fd->fd, in->ip, in->port, rkey, in->s->ssf ? ", TLS negotiated" : ""); valid = 1; } else { log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] incoming route '%s' is now invalid", in->fd->fd, in->ip, in->port, rkey); valid = 0; } free(rkey); nad_free(nad); /* decrement outstanding verify counter */ --out->verify; /* let them know what happened */ nad = nad_new(in->s->nad_cache); ns = nad_add_namespace(nad, uri_DIALBACK, "db"); nad_append_elem(nad, ns, "result", 0); nad_append_attr(nad, -1, "to", from->domain); nad_append_attr(nad, -1, "from", to->domain); nad_append_attr(nad, -1, "type", valid ? "valid" : "invalid"); /* off it goes */ sx_nad_write(in->s, nad); /* if invalid, close the stream */ if (!valid) { /* generate stream error */ sx_error(in->s, stream_err_INVALID_ID, "dialback negotiation failed"); /* close the incoming stream */ sx_close(in->s); } jid_free(from); jid_free(to); } /* bounce all packets in the queue for domain */ int out_bounce_queue(s2s_t s2s, const char *domain, int err) { jqueue_t q; pkt_t pkt; int pktcount = 0; q = xhash_get(s2s->outq, domain); if(q == NULL) return 0; while((pkt = jqueue_pull(q)) != NULL) { if(pkt->nad->ecur > 1 && NAD_NURI_L(pkt->nad, NAD_ENS(pkt->nad, 1)) == strlen(uri_CLIENT) && strncmp(NAD_NURI(pkt->nad, NAD_ENS(pkt->nad, 1)), uri_CLIENT, strlen(uri_CLIENT)) == 0) { sx_nad_write(s2s->router, stanza_tofrom(stanza_tofrom(stanza_error(pkt->nad, 1, err), 1), 0)); pktcount++; } else nad_free(pkt->nad); jid_free(pkt->to); jid_free(pkt->from); free(pkt); } return pktcount; } int out_bounce_conn_queues(conn_t out, int err) { char *c; char *rkey; /* bounce queues for all domains handled by this connection - iterate through routes */ if (xhash_iter_first(out->routes)) { do { xhash_iter_get(out->routes, (const char **) &rkey, NULL); c = strchr(rkey, '/'); c++; out_bounce_queue(out->s2s, c, err); } while(xhash_iter_next(out->routes)); } return 0; }