jabberd2  2.2.16
s2s/out.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 #define _GNU_SOURCE
00022 #include <string.h>
00023 
00024 #include "s2s.h"
00025 
00026 #include <idna.h>
00027 
00028 /*
00029  * we handle packets going from the router to the world, and stuff
00030  * that comes in on connections we initiated.
00031  *
00032  * action points:
00033  *
00034  *   out_packet(s2s, nad) - send this packet out
00035  *     - extract to domain
00036  *     - get dbconn for this domain using out_route
00037  *       - if dbconn not available bounce packet
00038  *       - DONE
00039  *     - if conn in progress (tcp)
00040  *       - add packet to queue for this domain
00041  *       - DONE
00042  *     - if dbconn state valid for this domain, or packet is dialback
00043  *       - send packet
00044  *       - DONE
00045  *     - if dbconn state invalid for this domain
00046  *       - bounce packet (502)
00047  *       - DONE
00048  *     - add packet to queue for this domain
00049  *     - if dbconn state inprogress for this domain
00050  *       - DONE
00051  *     - out_dialback(dbconn, from, to)
00052  *
00053  *   out_route(s2s, route, out, allow_bad)
00054  *     - if dbconn not found
00055  *       - check internal resolver cache for domain
00056  *       - if not found
00057  *         - ask resolver for name
00058  *         - DONE
00059  *       - if outgoing ip/port is to be reused
00060  *         - get dbconn for any valid ip/port
00061  *         - if dbconn not found
00062  *            - create new dbconn
00063  *            - initiate connect to ip/port
00064  *            - DONE
00065  *       - create new dbconn
00066  *       - initiate connect to ip/port
00067  *       - DONE
00068  *
00069  *   out_dialback(dbconn, from, to) - initiate dialback
00070  *     - generate dbkey: sha1(secret+remote+stream id)
00071  *     - send auth request: <result to='them' from='us'>dbkey</result>
00072  *     - set dbconn state for this domain to inprogress
00073  *     - DONE
00074  *
00075  *   out_resolve(s2s, query) - responses from resolver
00076  *     - store ip/port/ttl in resolver cache
00077  *     - flush domain queue -> out_packet(s2s, domain)
00078  *     - DONE
00079  *
00080  *   event_STREAM - ip/port open
00081  *     - get dbconn for this sx
00082  *     - for each route handled by this conn, out_dialback(dbconn, from, to)
00083  *     - DONE
00084  *
00085  *   event_PACKET: <result from='them' to='us' type='xxx'/> - response to our auth request
00086  *     - get dbconn for this sx
00087  *     - if type valid
00088  *       - set dbconn state for this domain to valid
00089  *       - flush dbconn queue for this domain -> out_packet(s2s, pkt)
00090  *       - DONE
00091  *     - set dbconn state for this domain to invalid
00092  *     - bounce dbconn queue for this domain (502)
00093  *     - DONE
00094  *
00095  *   event_PACKET: <verify from='them' to='us' id='123' type='xxx'/> - incoming stream authenticated
00096  *     - get dbconn for given id
00097  *     - if type is valid
00098  *       - set dbconn state for this domain to valid
00099  *     - send result: <result to='them' from='us' type='xxx'/>
00100  *     - DONE
00101  */
00102 
00103 /* forward decls */
00104 static int _out_mio_callback(mio_t m, mio_action_t a, mio_fd_t fd, void *data, void *arg);
00105 static int _out_sx_callback(sx_t s, sx_event_t e, void *data, void *arg);
00106 static void _out_result(conn_t out, nad_t nad);
00107 static void _out_verify(conn_t out, nad_t nad);
00108 static void _dns_result_aaaa(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data);
00109 static void _dns_result_a(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data);
00110 
00112 static void _out_packet_queue(s2s_t s2s, pkt_t pkt) {
00113     char *rkey = s2s_route_key(NULL, pkt->from->domain, pkt->to->domain);
00114     jqueue_t q = (jqueue_t) xhash_get(s2s->outq, rkey);
00115 
00116     if(q == NULL) {
00117         log_debug(ZONE, "creating new out packet queue for '%s'", rkey);
00118         q = jqueue_new();
00119         q->key = rkey;
00120         xhash_put(s2s->outq, q->key, (void *) q);
00121     } else {
00122         free(rkey);
00123     }
00124 
00125     log_debug(ZONE, "queueing packet for '%s'", q->key);
00126 
00127     jqueue_push(q, (void *) pkt, 0);
00128 }
00129 
00130 static void _out_dialback(conn_t out, char *rkey, int rkeylen) {
00131     char *c, *dbkey, *tmp;
00132     nad_t nad;
00133     int elem, ns;
00134     int from_len, to_len;
00135     time_t now;
00136 
00137     now = time(NULL);
00138 
00139     c = memchr(rkey, '/', rkeylen);
00140     from_len = c - rkey;
00141     c++;
00142     to_len = rkeylen - (c - rkey);
00143 
00144     /* kick off the dialback */
00145     tmp = strndup(c, to_len);
00146     dbkey = s2s_db_key(NULL, out->s2s->local_secret, tmp, out->s->id);
00147     free(tmp);
00148 
00149     nad = nad_new();
00150 
00151     /* request auth */
00152     ns = nad_add_namespace(nad, uri_DIALBACK, "db");
00153     elem = nad_append_elem(nad, ns, "result", 0);
00154     nad_set_attr(nad, elem, -1, "from", rkey, from_len);
00155     nad_set_attr(nad, elem, -1, "to", c, to_len);
00156     nad_append_cdata(nad, dbkey, strlen(dbkey), 1);
00157 
00158     log_debug(ZONE, "sending auth request for %.*s (key %s)", rkeylen, rkey, dbkey);
00159     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, rkeylen, rkey);
00160 
00161     /* off it goes */
00162     sx_nad_write(out->s, nad);
00163 
00164     free(dbkey);
00165 
00166     /* we're in progress now */
00167     xhash_put(out->states, pstrdupx(xhash_pool(out->states), rkey, rkeylen), (void *) conn_INPROGRESS);
00168 
00169     /* record the time that we set conn_INPROGRESS state */
00170     xhash_put(out->states_time, pstrdupx(xhash_pool(out->states_time), rkey, rkeylen), (void *) now);
00171 }
00172 
00173 void _out_dns_mark_bad(conn_t out, char *ipport) {
00174     dnsres_t bad;
00175     int ipport_allocated = 0;
00176 
00177     if (out->s2s->dns_bad_timeout > 0) {
00178         /* mark this host as bad */
00179         if(ipport == NULL) {
00180             ipport_allocated = 1;
00181             ipport = dns_make_ipport(out->ip, out->port);
00182         }
00183         bad = xhash_get(out->s2s->dns_bad, ipport);
00184         if (bad == NULL) {
00185             bad = (dnsres_t) calloc(1, sizeof(struct dnsres_st));
00186             bad->key = ipport;
00187             xhash_put(out->s2s->dns_bad, ipport, bad);
00188         } else if(ipport_allocated) {
00189             free(ipport);
00190         }
00191         bad->expiry = time(NULL) + out->s2s->dns_bad_timeout;
00192     }
00193 }
00194 
00195 int dns_select(s2s_t s2s, char *ip, int *port, time_t now, dnscache_t dns, int allow_bad) {
00196     /* list of results */
00197     dnsres_t l_reuse[DNS_MAX_RESULTS];
00198     dnsres_t l_aaaa[DNS_MAX_RESULTS];
00199     dnsres_t l_a[DNS_MAX_RESULTS];
00200     dnsres_t l_bad[DNS_MAX_RESULTS];
00201     /* running weight sums of results */
00202     int rw_reuse[DNS_MAX_RESULTS];
00203     int rw_aaaa[DNS_MAX_RESULTS];
00204     int rw_a[DNS_MAX_RESULTS];
00205     int s_reuse = 0, s_aaaa = 0, s_a = 0, s_bad = 0; /* count */
00206     int p_reuse = 0, p_aaaa = 0, p_a = 0; /* list prio */
00207     int wt_reuse = 0, wt_aaaa = 0, wt_a = 0; /* weight total */
00208     int c_expired_good = 0;
00209     union xhashv xhv;
00210     dnsres_t res;
00211     char *ipport;
00212     int ipport_len;
00213     char *c;
00214     int c_len;
00215     char *tmp;
00216 
00217     /* for all results:
00218      * - if not expired
00219      *   - put highest priority reuseable addrs into list1
00220      *   - put highest priority ipv6 addrs into list2
00221      *   - put highest priority ipv4 addrs into list3
00222      *   - put bad addrs into list4
00223      * - pick weighted random entry from first non-empty list
00224      */
00225 
00226     if (dns->results == NULL) {
00227         log_debug(ZONE, "negative cache entry for '%s'", dns->name);
00228         return -1;
00229     }
00230     log_debug(ZONE, "selecting DNS result for '%s'", dns->name);
00231 
00232     xhv.dnsres_val = &res;
00233     if (xhash_iter_first(dns->results)) {
00234         dnsres_t bad = NULL;
00235         do {
00236             xhash_iter_get(dns->results, (const char **) &ipport, &ipport_len, xhv.val);
00237 
00238             if (s2s->dns_bad_timeout > 0)
00239                 bad = xhash_getx(s2s->dns_bad, ipport, ipport_len);
00240 
00241             if (now > res->expiry) {
00242                 /* good host? */
00243                 if (bad == NULL)
00244                     c_expired_good++;
00245 
00246                 log_debug(ZONE, "host '%s' expired", res->key);
00247                 continue;
00248             } else if (bad != NULL && !(now > bad->expiry)) {
00249                 /* bad host (connection failure) */
00250                 l_bad[s_bad++] = res;
00251 
00252                 log_debug(ZONE, "host '%s' bad", res->key);
00253             } else if (s2s->out_reuse && xhash_getx(s2s->out_host, ipport, ipport_len) != NULL) {
00254                 /* existing connection */
00255                 log_debug(ZONE, "host '%s' exists", res->key);
00256                 if (s_reuse == 0 || p_reuse > res->prio) {
00257                     p_reuse = res->prio;
00258                     s_reuse = 0;
00259                     wt_reuse = 0;
00260 
00261                     log_debug(ZONE, "reset prio list, using prio %d", res->prio);
00262                 }
00263                 if (res->prio <= p_reuse) {
00264                     l_reuse[s_reuse] = res;
00265                     wt_reuse += res->weight;
00266                     rw_reuse[s_reuse] = wt_reuse;
00267                     s_reuse++;
00268 
00269                     log_debug(ZONE, "added host with weight %d (%d), running weight %d",
00270                         (res->weight >> 8), res->weight, wt_reuse);
00271                 } else {
00272                     log_debug(ZONE, "ignored host with prio %d", res->prio);
00273                 }
00274             } else if (memchr(ipport, ':', ipport_len) != NULL) {
00275                 /* ipv6 */
00276                 log_debug(ZONE, "host '%s' IPv6", res->key);
00277                 if (s_aaaa == 0 || p_aaaa > res->prio) {
00278                     p_aaaa = res->prio;
00279                     s_aaaa = 0;
00280                     wt_aaaa = 0;
00281 
00282                     log_debug(ZONE, "reset prio list, using prio %d", res->prio);
00283                 }
00284                 if (res->prio <= p_aaaa) {
00285                     l_aaaa[s_aaaa] = res;
00286                     wt_aaaa += res->weight;
00287                     rw_aaaa[s_aaaa] = wt_aaaa;
00288                     s_aaaa++;
00289 
00290                     log_debug(ZONE, "added host with weight %d (%d), running weight %d",
00291                         (res->weight >> 8), res->weight, wt_aaaa);
00292                 } else {
00293                     log_debug(ZONE, "ignored host with prio %d", res->prio);
00294                 }
00295             } else {
00296                 /* ipv4 */
00297                 log_debug(ZONE, "host '%s' IPv4", res->key);
00298                 if (s_a == 0 || p_a > res->prio) {
00299                     p_a = res->prio;
00300                     s_a = 0;
00301                     wt_a = 0;
00302 
00303                     log_debug(ZONE, "reset prio list, using prio %d", res->prio);
00304                 }
00305                 if (res->prio <= p_a) {
00306                     l_a[s_a] = res;
00307                     wt_a += res->weight;
00308                     rw_a[s_a] = wt_a;
00309                     s_a++;
00310 
00311                     log_debug(ZONE, "added host with weight %d (%d), running weight %d",
00312                         (res->weight >> 8), res->weight, wt_a);
00313                 } else {
00314                     log_debug(ZONE, "ignored host with prio %d", res->prio);
00315                 }
00316             }
00317         } while(xhash_iter_next(dns->results));
00318     }
00319 
00320     /* pick a result at weighted random (RFC 2782)
00321      * all weights are guaranteed to be >= 16 && <= 16776960
00322      * (assuming max 50 hosts, the total/running sums won't exceed 2^31)
00323      */
00324     ipport = NULL;
00325     if (s_reuse > 0) {
00326         int i, r;
00327 
00328         log_debug(ZONE, "using existing hosts, total weight %d", wt_reuse);
00329         assert((wt_reuse + 1) > 0);
00330 
00331         r = rand() % (wt_reuse + 1);
00332         log_debug(ZONE, "random number %d", r);
00333 
00334         for (i = 0; i < s_reuse; i++)
00335             if (rw_reuse[i] >= r) {
00336                 log_debug(ZONE, "selected host '%s', running weight %d",
00337                     l_reuse[i]->key, rw_reuse[i]);
00338 
00339                 ipport = l_reuse[i]->key;
00340                 break;
00341             }
00342     } else if (s_aaaa > 0 && (s_a == 0 || p_aaaa <= p_a)) {
00343         int i, r;
00344 
00345         log_debug(ZONE, "using IPv6 hosts, total weight %d", wt_aaaa);
00346         assert((wt_aaaa + 1) > 0);
00347 
00348         r = rand() % (wt_aaaa + 1);
00349         log_debug(ZONE, "random number %d", r);
00350 
00351         for (i = 0; i < s_aaaa; i++)
00352             if (rw_aaaa[i] >= r) {
00353                 log_debug(ZONE, "selected host '%s', running weight %d",
00354                     l_aaaa[i]->key, rw_aaaa[i]);
00355 
00356                 ipport = l_aaaa[i]->key;
00357                 break;
00358             }
00359     } else if (s_a > 0) {
00360         int i, r;
00361 
00362         log_debug(ZONE, "using IPv4 hosts, total weight %d", wt_a);
00363         assert((wt_a + 1) > 0);
00364 
00365         r = rand() % (wt_a + 1);
00366         log_debug(ZONE, "random number %d", r);
00367 
00368         for (i = 0; i < s_a; i++)
00369             if (rw_a[i] >= r) {
00370                 log_debug(ZONE, "selected host '%s', running weight %d",
00371                     l_a[i]->key, rw_a[i]);
00372 
00373                 ipport = l_a[i]->key;
00374                 break;
00375             }
00376     } else if (s_bad > 0) {
00377         ipport = l_bad[rand() % s_bad]->key;
00378 
00379         log_debug(ZONE, "using bad hosts, allow_bad=%d", allow_bad);
00380 
00381         /* there are expired good hosts, expire cache immediately */
00382         if (c_expired_good > 0) {
00383             log_debug(ZONE, "expiring this DNS cache entry, %d expired hosts",
00384                 c_expired_good);
00385 
00386             dns->expiry = 0;
00387         }
00388 
00389         if (!allow_bad)
00390             return -1;
00391     }
00392 
00393     /* results cannot all expire before the collection does */
00394     assert(ipport != NULL);
00395 
00396     /* copy the ip and port to the packet */
00397     ipport_len = strlen(ipport);
00398     c = strchr(ipport, '/');
00399     strncpy(ip, ipport, c-ipport);
00400     ip[c-ipport] = '\0';
00401     c++;
00402     c_len = ipport_len - (c - ipport);
00403     tmp = strndup(c, c_len);
00404     *port = atoi(tmp);
00405     free(tmp);
00406 
00407     return 0;
00408 }
00409 
00411 int out_route(s2s_t s2s, char *route, int routelen, conn_t *out, int allow_bad) {
00412     dnscache_t dns;
00413     char ipport[INET6_ADDRSTRLEN + 16], *dkey, *c;
00414     time_t now;
00415     int reuse = 0;
00416     char ip[INET6_ADDRSTRLEN] = {0};
00417     int port, c_len, from_len;
00418 
00419     c = memchr(route, '/', routelen);
00420     from_len = c - route;
00421     c++;
00422     c_len = routelen - (c - route);
00423     dkey = strndup(c, c_len);
00424 
00425     log_debug(ZONE, "trying to find connection for '%s'", dkey);
00426     *out = (conn_t) xhash_get(s2s->out_dest, dkey);
00427     if(*out == NULL) {
00428         log_debug(ZONE, "connection for '%s' not found", dkey);
00429 
00430         /* check resolver cache for ip/port */
00431         dns = xhash_get(s2s->dnscache, dkey);
00432         if(dns == NULL) {
00433             /* new resolution */
00434             log_debug(ZONE, "no dns for %s, preparing for resolution", dkey);
00435 
00436             dns = (dnscache_t) calloc(1, sizeof(struct dnscache_st));
00437 
00438             strcpy(dns->name, dkey);
00439 
00440             xhash_put(s2s->dnscache, dns->name, (void *) dns);
00441 
00442 #if 0
00443             /* this is good for testing */
00444             dns->pending = 0;
00445             strcpy(dns->ip, "127.0.0.1");
00446             dns->port = 3000;
00447             dns->expiry = time(NULL) + 99999999;
00448 #endif
00449         }
00450 
00451         /* resolution in progress */
00452         if(dns->pending) {
00453             log_debug(ZONE, "pending resolution");
00454             free(dkey);
00455             return 0;
00456         }
00457 
00458         /* has it expired (this is 0 for new cache objects, so they're always expired */
00459         now = time(NULL); /* each entry must be expired no earlier than the collection */
00460         if(now > dns->expiry) {
00461             /* resolution required */
00462             log_debug(ZONE, "requesting resolution for %s", dkey);
00463 
00464             dns->init_time = time(NULL);
00465             dns->pending = 1;
00466 
00467             dns_resolve_domain(s2s, dns);
00468             free(dkey);
00469             return 0;
00470         }
00471 
00472         /* dns is valid */
00473         if (dns_select(s2s, ip, &port, now, dns, allow_bad)) {
00474             /* failed to find anything acceptable */
00475             free(dkey);
00476             return -1;
00477         }
00478 
00479         /* re-request resolution if dns_select expired the data */
00480         if (now > dns->expiry) {
00481             /* resolution required */
00482             log_debug(ZONE, "requesting resolution for %s", dkey);
00483 
00484             dns->init_time = time(NULL);
00485             dns->pending = 1;
00486 
00487             dns_resolve_domain(s2s, dns);
00488 
00489             free(dkey);
00490             return 0;
00491         }
00492 
00493         /* generate the ip/port pair, this is the hash key for the conn */
00494         snprintf(ipport, INET6_ADDRSTRLEN + 16, "%s/%d", ip, port);
00495 
00496         /* try to re-use an existing connection */
00497         if (s2s->out_reuse)
00498             *out = (conn_t) xhash_get(s2s->out_host, ipport);
00499 
00500         if (*out != NULL) {
00501             log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] using connection for '%s'", (*out)->fd->fd, (*out)->ip, (*out)->port, dkey);
00502 
00503             /* associate existing connection with domain */
00504             xhash_put(s2s->out_dest, s2s->out_reuse ? pstrdup(xhash_pool((*out)->routes), dkey) : dkey, (void *) *out);
00505 
00506             reuse = 1;
00507         } else{
00508             /* no conn, create one */
00509             *out = (conn_t) calloc(1, sizeof(struct conn_st));
00510 
00511             (*out)->s2s = s2s;
00512 
00513             (*out)->key = strdup(ipport);
00514             if (s2s->out_reuse)
00515                 (*out)->dkey = NULL;
00516             else
00517                 (*out)->dkey = dkey;
00518 
00519             strcpy((*out)->ip, ip);
00520             (*out)->port = port;
00521 
00522             (*out)->states = xhash_new(101);
00523             (*out)->states_time = xhash_new(101);
00524 
00525             (*out)->routes = xhash_new(101);
00526 
00527             (*out)->init_time = time(NULL);
00528 
00529             if (s2s->out_reuse)
00530                 xhash_put(s2s->out_host, (*out)->key, (void *) *out);
00531             xhash_put(s2s->out_dest, s2s->out_reuse ? pstrdup(xhash_pool((*out)->routes), dkey) : dkey, (void *) *out);
00532 
00533             xhash_put((*out)->routes, pstrdupx(xhash_pool((*out)->routes), route, routelen), (void *) 1);
00534 
00535             /* connect */
00536             log_debug(ZONE, "initiating connection to %s", ipport);
00537 
00538             /* APPLE: multiple origin_ips may be specified; use IPv6 if possible or otherwise IPv4 */
00539             int ip_is_v6 = 0;
00540             if (strchr(ip, ':') != NULL)
00541                 ip_is_v6 = 1;
00542             int i;
00543             for (i = 0; i < s2s->origin_nips; i++) {
00544                 // only bother with mio_connect if the src and dst IPs are of the same type
00545                 if ((ip_is_v6 && (strchr(s2s->origin_ips[i], ':') != NULL)) ||          // both are IPv6
00546                             (! ip_is_v6 && (strchr(s2s->origin_ips[i], ':') == NULL)))  // both are IPv4
00547                     (*out)->fd = mio_connect(s2s->mio, port, ip, s2s->origin_ips[i], _out_mio_callback, (void *) *out);
00548 
00549                 if ((*out)->fd != NULL) break;
00550             }
00551 
00552             if ((*out)->fd == NULL) {
00553                 log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] mio_connect error: %s (%d)", -1, (*out)->ip, (*out)->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR);
00554 
00555                 _out_dns_mark_bad(*out, ipport);
00556 
00557                 if (s2s->out_reuse)
00558                    xhash_zap(s2s->out_host, (*out)->key);
00559                 xhash_zap(s2s->out_dest, dkey);
00560 
00561                 xhash_free((*out)->states);
00562                 xhash_free((*out)->states_time);
00563 
00564                 xhash_free((*out)->routes);
00565 
00566                 free((*out)->key);
00567                 free((*out)->dkey);
00568                 free(*out);
00569                 *out = NULL;
00570 
00571                 /* try again without allowing bad hosts */
00572                 return out_route(s2s, route, routelen, out, 0);
00573             } else {
00574                 log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] outgoing connection for '%s'", (*out)->fd->fd, (*out)->ip, (*out)->port, dkey);
00575 
00576                 (*out)->s = sx_new(s2s->sx_env, (*out)->fd->fd, _out_sx_callback, (void *) *out);
00577 
00578 #ifdef HAVE_SSL
00579                 /* Send a stream version of 1.0 if we can do STARTTLS */
00580                 if(s2s->sx_ssl != NULL) {
00581                     sx_client_init((*out)->s, S2S_DB_HEADER, uri_SERVER, dkey, pstrdupx(xhash_pool((*out)->routes), route, from_len), "1.0");
00582                 } else {
00583                     sx_client_init((*out)->s, S2S_DB_HEADER, uri_SERVER, NULL, NULL, NULL);
00584                 }
00585 #else
00586                 sx_client_init((*out)->s, S2S_DB_HEADER, uri_SERVER, NULL, NULL, NULL);
00587 #endif
00588                 /* dkey is now used by the hash table */
00589                 return 0;
00590             }
00591         }
00592     } else {
00593         log_debug(ZONE, "connection for '%s' found (%d %s/%d)", dkey, (*out)->fd->fd, (*out)->ip, (*out)->port);
00594     }
00595 
00596     /* connection in progress, or re-using connection: add to routes list */
00597     if (!(*out)->online || reuse) {
00598         if (xhash_getx((*out)->routes, route, routelen) == NULL)
00599             xhash_put((*out)->routes, pstrdupx(xhash_pool((*out)->routes), route, routelen), (void *) 1);
00600     }
00601 
00602     free(dkey);
00603     return 0;
00604 }
00605 
00606 void out_pkt_free(pkt_t pkt)
00607 {
00608     nad_free(pkt->nad);
00609     jid_free(pkt->from);
00610     jid_free(pkt->to);
00611     free(pkt);
00612 }
00613 
00615 int out_packet(s2s_t s2s, pkt_t pkt) {
00616     char *rkey;
00617     int rkeylen;
00618     conn_t out;
00619     conn_state_t state;
00620     int ret;
00621 
00622     /* perform check against whitelist */
00623     if (s2s->enable_whitelist > 0 &&
00624             (pkt->to->domain != NULL) &&
00625             (s2s_domain_in_whitelist(s2s, pkt->to->domain) == 0)) {
00626         log_write(s2s->log, LOG_NOTICE, "sending a packet to domain not in the whitelist, dropping it");
00627         if (pkt->to != NULL)
00628             jid_free(pkt->to);
00629         if (pkt->from != NULL)
00630             jid_free(pkt->from);
00631         if (pkt->nad != NULL)
00632             nad_free(pkt->nad);
00633         free(pkt);
00634 
00635         return;
00636     }
00637 
00638     /* new route key */
00639     rkey = s2s_route_key(NULL, pkt->from->domain, pkt->to->domain);
00640     rkeylen = strlen(rkey);
00641 
00642     /* get a connection */
00643     ret = out_route(s2s, rkey, rkeylen, &out, 1);
00644 
00645     if (out == NULL) {
00646         /* connection not available, queue packet */
00647         _out_packet_queue(s2s, pkt);
00648 
00649         /* check if out_route was successful in attempting a connection */
00650         if (ret) {
00651             /* bounce queue */
00652             out_bounce_route_queue(s2s, rkey, rkeylen, stanza_err_SERVICE_UNAVAILABLE);
00653 
00654             free(rkey);
00655             return -1;
00656         }
00657 
00658         free(rkey);
00659         return 0;
00660     }
00661 
00662     /* connection in progress */
00663     if(!out->online) {
00664         log_debug(ZONE, "connection in progress, queueing packet");
00665 
00666         _out_packet_queue(s2s, pkt);
00667 
00668         free(rkey);
00669         return 0;
00670     }
00671 
00672     /* connection state */
00673     state = (conn_state_t) xhash_get(out->states, rkey);
00674 
00675     /* valid conns or dialback packets */
00676     if(state == conn_VALID || pkt->db) {
00677         log_debug(ZONE, "writing packet for %s to outgoing conn %d", rkey, out->fd->fd);
00678 
00679         /* send it straight out */
00680         if(pkt->db) {
00681             /* if this is a db:verify packet, increment counter and set timestamp */
00682             if(NAD_ENAME_L(pkt->nad, 0) == 6 && strncmp("verify", NAD_ENAME(pkt->nad, 0), 6) == 0) {
00683                 out->verify++;
00684                 out->last_verify = time(NULL);
00685             }
00686 
00687             /* dialback packet */
00688             sx_nad_write(out->s, pkt->nad);
00689         } else {
00690             /* if the outgoing stanza has a jabber:client namespace, remove it so that the stream jabber:server namespaces will apply (XMPP 11.2.2) */
00691             int ns = nad_find_namespace(pkt->nad, 1, uri_CLIENT, NULL);
00692             if(ns >= 0) {
00693                /* clear the namespaces of elem 0 (internal route element) and elem 1 (message|iq|presence) */
00694                pkt->nad->elems[0].ns = -1;
00695                pkt->nad->elems[0].my_ns = -1;
00696                pkt->nad->elems[1].ns = -1;
00697                pkt->nad->elems[1].my_ns = -1;
00698             }
00699 
00700             /* send it out */
00701             sx_nad_write_elem(out->s, pkt->nad, 1);
00702         }
00703 
00704         /* update timestamp */
00705         out->last_packet = time(NULL);
00706 
00707         jid_free(pkt->from);
00708         jid_free(pkt->to);
00709         free(pkt);
00710 
00711         free(rkey);
00712         return 0;
00713     }
00714 
00715     /* can't be handled yet, queue */
00716     _out_packet_queue(s2s, pkt);
00717 
00718     /* if dialback is in progress, then we're done for now */
00719     if(state == conn_INPROGRESS) {
00720         free(rkey);
00721         return 0;
00722     }
00723 
00724     /* this is a new route - send dialback auth request to piggyback on the existing connection */
00725     if (out->s2s->require_tls == 0 || out->s->ssf > 0) {
00726     _out_dialback(out, rkey, rkeylen);
00727     }
00728     free(rkey);
00729     return 0;
00730 }
00731 
00732 char *dns_make_ipport(char *host, int port) {
00733     char *c;
00734     assert(port > 0 && port < 65536);
00735 
00736     c = (char *) malloc(strlen(host) + 7);
00737     sprintf(c, "%s/%d", host, port);
00738     return c;
00739 }
00740 
00741 static void _dns_add_result(dnsquery_t query, char *ip, int port, int prio, int weight, unsigned int ttl) {
00742     char *ipport = dns_make_ipport(ip, port);
00743     dnsres_t res = xhash_get(query->results, ipport);
00744 
00745     if (res != NULL) {
00746         if (prio < res->prio)
00747             res->prio = prio;
00748 
00749         if (prio < res->prio) {
00750             /* duplicate host at lower prio - reset weight */
00751             res->weight = weight;
00752         } else if (prio == res->prio) {
00753             /* duplicate host at same prio - add to weight */
00754             res->weight += weight;
00755             if (res->weight > (65535 << 8))
00756                 res->weight = (65535 << 8);
00757         }
00758 
00759         if (ttl > res->expiry)
00760             res->expiry = ttl;
00761 
00762         if (ttl > query->expiry)
00763             query->expiry = ttl;
00764 
00765         log_debug(ZONE, "dns result updated for %s@%p: %s (%d/%d/%d)", query->name, query, ipport,
00766             res->prio, (res->weight >> 8), res->expiry);
00767     } else if (xhash_count(query->results) < DNS_MAX_RESULTS) {
00768         res = pmalloc(xhash_pool(query->results), sizeof(struct dnsres_st));
00769         res->key = pstrdup(xhash_pool(query->results), ipport);
00770         res->prio = prio;
00771         res->weight = weight;
00772         res->expiry = ttl;
00773 
00774         if (ttl > query->expiry)
00775             query->expiry = ttl;
00776 
00777         xhash_put(query->results, res->key, res);
00778 
00779         log_debug(ZONE, "dns result added for %s@%p: %s (%d/%d/%d)", query->name, query, ipport,
00780             res->prio, (res->weight >> 8), res->expiry);
00781     } else {
00782         log_debug(ZONE, "dns result ignored for %s@%p: %s (%d/%d/%d)", query->name, query, ipport,
00783             prio, (weight >> 8), ttl);
00784     }
00785 
00786     free(ipport);
00787 }
00788 
00789 static void _dns_add_host(dnsquery_t query, char *ip, int port, int prio, int weight, unsigned int ttl) {
00790     char *ipport = dns_make_ipport(ip, port);
00791     dnsres_t res = xhash_get(query->hosts, ipport);
00792 
00793     /* update host weights:
00794      *  RFC 2482 "In the presence of records containing weights greater
00795      *  than 0, records with weight 0 should have a very small chance of
00796      *  being selected."
00797      * 0       -> 16
00798      * 1-65535 -> 256-16776960
00799      */
00800     if (weight == 0)
00801         weight = 1 << 4;
00802     else
00803         weight <<= 8;
00804 
00805     if (res != NULL) {
00806         if (prio < res->prio)
00807             res->prio = prio;
00808 
00809         if (prio < res->prio) {
00810             /* duplicate host at lower prio - reset weight */
00811             res->weight = weight;
00812         } else if (prio == res->prio) {
00813             /* duplicate host at same prio - add to weight */
00814             res->weight += weight;
00815             if (res->weight > (65535 << 8))
00816                 res->weight = (65535 << 8);
00817         }
00818 
00819         if (ttl > res->expiry)
00820             res->expiry = ttl;
00821 
00822         log_debug(ZONE, "dns host updated for %s@%p: %s (%d/%d/%d)", query->name, query, ipport,
00823             res->prio, (res->weight >> 8), res->expiry);
00824     } else if (xhash_count(query->hosts) < DNS_MAX_RESULTS) {
00825         res = pmalloc(xhash_pool(query->hosts), sizeof(struct dnsres_st));
00826         res->key = pstrdup(xhash_pool(query->hosts), ipport);
00827         res->prio = prio;
00828         res->weight = weight;
00829         res->expiry = ttl;
00830 
00831         xhash_put(query->hosts, res->key, res);
00832 
00833         log_debug(ZONE, "dns host added for %s@%p: %s (%d/%d/%d)", query->name, query, ipport,
00834             res->prio, (res->weight >> 8), res->expiry);
00835     } else {
00836         log_debug(ZONE, "dns host ignored for %s@%p: %s (%d/%d/%d)", query->name, query, ipport,
00837             prio, (weight >> 8), ttl);
00838     }
00839 
00840     free(ipport);
00841 }
00842 
00843 /* this function is called with a NULL ctx to start the SRV process */
00844 static void _dns_result_srv(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data) {
00845     dnsquery_t query = data;
00846     assert(query != NULL);
00847     query->query = NULL;
00848 
00849     if (ctx != NULL && result == NULL) {
00850         log_debug(ZONE, "dns failure for %s@%p: SRV %s (%d)", query->name, query,
00851             query->s2s->lookup_srv[query->srv_i], dns_status(ctx));
00852     } else if (result != NULL) {
00853         int i;
00854 
00855         log_debug(ZONE, "dns response for %s@%p: SRV %s %d (%d)", query->name, query,
00856             result->dnssrv_qname, result->dnssrv_nrr, result->dnssrv_ttl);
00857 
00858         for (i = 0; i < result->dnssrv_nrr; i++) {
00859             if (strlen(result->dnssrv_srv[i].name) > 0
00860                     && result->dnssrv_srv[i].port > 0
00861                     && result->dnssrv_srv[i].port < 65536) {
00862                 log_debug(ZONE, "dns response for %s@%p: SRV %s[%d] %s/%d (%d/%d)", query->name,
00863                     query, result->dnssrv_qname, i,
00864                     result->dnssrv_srv[i].name, result->dnssrv_srv[i].port,
00865                     result->dnssrv_srv[i].priority, result->dnssrv_srv[i].weight);
00866 
00867                 _dns_add_host(query, result->dnssrv_srv[i].name,
00868                     result->dnssrv_srv[i].port, result->dnssrv_srv[i].priority,
00869                     result->dnssrv_srv[i].weight, result->dnssrv_ttl);
00870             }
00871         }
00872 
00873         free(result);
00874     }
00875 
00876     /* check next SRV service name */
00877     query->srv_i++;
00878     if (query->srv_i < query->s2s->lookup_nsrv) {
00879         log_debug(ZONE, "dns request for %s@%p: SRV %s", query->name, query,
00880             query->s2s->lookup_srv[query->srv_i]);
00881 
00882         query->query = dns_submit_srv(NULL, query->name, query->s2s->lookup_srv[query->srv_i], "tcp",
00883             DNS_NOSRCH, _dns_result_srv, query);
00884 
00885         /* if submit failed, call ourselves with a NULL result */
00886         if (query->query == NULL)
00887             _dns_result_srv(ctx, NULL, query);
00888     } else {
00889         /* no more SRV records to check, resolve hosts */
00890         if (xhash_count(query->hosts) > 0) {
00891             _dns_result_a(NULL, NULL, query);
00892 
00893         /* no SRV records returned, resolve hostname */
00894         } else {
00895             query->cur_host = strdup(query->name);
00896             query->cur_port = 5269;
00897             query->cur_prio = 0;
00898             query->cur_weight = 0;
00899             query->cur_expiry = 0;
00900             if (query->s2s->resolve_aaaa) {
00901                 log_debug(ZONE, "dns request for %s@%p: AAAA %s", query->name, query, query->name);
00902 
00903                 query->query = dns_submit_a6(NULL, query->name,
00904                     DNS_NOSRCH, _dns_result_aaaa, query);
00905 
00906                 /* if submit failed, call ourselves with a NULL result */
00907                 if (query->query == NULL)
00908                     _dns_result_aaaa(ctx, NULL, query);
00909             } else {
00910                 log_debug(ZONE, "dns request for %s@%p: A %s", query->name, query, query->name);
00911 
00912                 query->query = dns_submit_a4(NULL, query->name,
00913                     DNS_NOSRCH, _dns_result_a, query);
00914 
00915                 /* if submit failed, call ourselves with a NULL result */
00916                 if (query->query == NULL)
00917                     _dns_result_a(ctx, NULL, query);
00918             }
00919         }
00920     }
00921 }
00922 
00923 static void _dns_result_aaaa(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data) {
00924     dnsquery_t query = data;
00925     char ip[INET6_ADDRSTRLEN];
00926     int i;
00927     assert(query != NULL);
00928     query->query = NULL;
00929 
00930     if (ctx != NULL && result == NULL) {
00931         log_debug(ZONE, "dns failure for %s@%p: AAAA %s (%d)", query->name, query,
00932             query->cur_host, dns_status(ctx));
00933     } else if (result != NULL) {
00934         log_debug(ZONE, "dns response for %s@%p: AAAA %s %d (%d)", query->name, query,
00935             result->dnsa6_qname, result->dnsa6_nrr, result->dnsa6_ttl);
00936 
00937         if (query->cur_expiry > 0 && result->dnsa6_ttl > query->cur_expiry)
00938             result->dnsa6_ttl = query->cur_expiry;
00939 
00940         for (i = 0; i < result->dnsa6_nrr; i++) {
00941             if (inet_ntop(AF_INET6, &result->dnsa6_addr[i], ip, INET6_ADDRSTRLEN) != NULL) {
00942                 log_debug(ZONE, "dns response for %s@%p: AAAA %s[%d] %s/%d", query->name,
00943                     query, result->dnsa6_qname, i, ip, query->cur_port);
00944 
00945                 _dns_add_result(query, ip, query->cur_port,
00946                     query->cur_prio, query->cur_weight, result->dnsa6_ttl);
00947             }
00948         }
00949     }
00950 
00951     if (query->cur_host != NULL) {
00952         /* do ipv4 resolution too */
00953         log_debug(ZONE, "dns request for %s@%p: A %s", query->name, query, query->cur_host);
00954 
00955         query->query = dns_submit_a4(NULL, query->cur_host,
00956             DNS_NOSRCH, _dns_result_a, query);
00957 
00958         /* if submit failed, call ourselves with a NULL result */
00959         if (query->query == NULL)
00960             _dns_result_a(ctx, NULL, query);
00961     } else {
00962         /* uh-oh */
00963         log_debug(ZONE, "dns result for %s@%p: AAAA host vanished...", query->name, query);
00964         _dns_result_a(NULL, NULL, query);
00965     }
00966 
00967     free(result);
00968 }
00969 
00970 /* try /etc/hosts if the A process did not return any results */
00971 static int _etc_hosts_lookup(const char *cszName, char *szIP, const int ciMaxIPLen) {
00972 #define EHL_LINE_LEN 260
00973     int iSuccess = 0;
00974     size_t iLen;
00975     char szLine[EHL_LINE_LEN + 1]; /* one extra for the space character (*) */
00976     char *pcStart, *pcEnd;
00977     FILE *fHosts;
00978 
00979     do {
00980         /* initialization */
00981         fHosts = NULL;
00982 
00983         /* sanity checks */
00984         if ((cszName == NULL) || (szIP == NULL) || (ciMaxIPLen <= 0))
00985             break;
00986         szIP[0] = 0;
00987 
00988         /* open the hosts file */
00989 #ifdef _WIN32
00990         pcStart = getenv("WINDIR");
00991         if (pcStart != NULL) {
00992             sprintf(szLine, "%s\\system32\\drivers\\etc\\hosts", pcStart);
00993         } else {
00994             strcpy(szLine, "C:\\WINDOWS\\system32\\drivers\\etc\\hosts");
00995         }
00996 #else
00997         strcpy(szLine, "/etc/hosts");
00998 #endif
00999         fHosts = fopen(szLine, "r");
01000         if (fHosts == NULL)
01001             break;
01002 
01003         /* read line by line ... */
01004         while (fgets(szLine, EHL_LINE_LEN, fHosts) != NULL) {
01005             /* remove comments */
01006             pcStart = strchr (szLine, '#');
01007             if (pcStart != NULL)
01008                 *pcStart = 0;
01009             strcat(szLine, " "); /* append a space character for easier parsing (*) */
01010 
01011             /* first to appear: IP address */
01012             iLen = strspn(szLine, "1234567890.");
01013             if ((iLen < 7) || (iLen > 15)) /* superficial test for anything between x.x.x.x and xxx.xxx.xxx.xxx */
01014                 continue;
01015             pcEnd = szLine + iLen;
01016             *pcEnd = 0;
01017             pcEnd++; /* not beyond the end of the line yet (*) */
01018 
01019             /* check strings separated by blanks, tabs or newlines */
01020             pcStart = pcEnd + strspn(pcEnd, " \t\n");
01021             while (*pcStart != 0) {
01022                 pcEnd = pcStart + strcspn(pcStart, " \t\n");
01023                 *pcEnd = 0;
01024                 pcEnd++; /* not beyond the end of the line yet (*) */
01025 
01026                 if (strcasecmp(pcStart, cszName) == 0) {
01027                     strncpy(szIP, szLine, ciMaxIPLen - 1);
01028                     szIP[ciMaxIPLen - 1] = '\0';
01029                     iSuccess = 1;
01030                     break;
01031                 }
01032 
01033                 pcStart = pcEnd + strspn(pcEnd, " \t\n");
01034             }
01035             if (iSuccess)
01036                 break;
01037         }
01038     } while (0);
01039 
01040     if (fHosts != NULL)
01041         fclose(fHosts);
01042 
01043     return (iSuccess);
01044 }
01045 
01046 /* this function is called with a NULL ctx to start the A/AAAA process */
01047 static void _dns_result_a(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data) {
01048     dnsquery_t query = data;
01049     assert(query != NULL);
01050     query->query = NULL;
01051 
01052     if (ctx != NULL && result == NULL) {
01053 #define DRA_IP_LEN 16
01054         char szIP[DRA_IP_LEN];
01055         if (_etc_hosts_lookup (query->name, szIP, DRA_IP_LEN)) {
01056             log_debug(ZONE, "/etc/lookup for %s@%p: %s (%d)", query->name,
01057                 query, szIP, query->s2s->etc_hosts_ttl);
01058 
01059             _dns_add_result (query, szIP, query->cur_port,
01060                 query->cur_prio, query->cur_weight, query->s2s->etc_hosts_ttl);
01061         } else {
01062             log_debug(ZONE, "dns failure for %s@%p: A %s (%d)", query->name, query,
01063                 query->cur_host, dns_status(ctx));
01064         }
01065     } else if (result != NULL) {
01066         char ip[INET_ADDRSTRLEN];
01067         int i;
01068 
01069         log_debug(ZONE, "dns response for %s@%p: A %s %d (%d)", query->name,
01070             query, result->dnsa4_qname, result->dnsa4_nrr, result->dnsa4_ttl);
01071 
01072         if (query->cur_expiry > 0 && result->dnsa4_ttl > query->cur_expiry)
01073             result->dnsa4_ttl = query->cur_expiry;
01074 
01075         for (i = 0; i < result->dnsa4_nrr; i++) {
01076             if (inet_ntop(AF_INET, &result->dnsa4_addr[i], ip, INET_ADDRSTRLEN) != NULL) {
01077                 log_debug(ZONE, "dns response for %s@%p: A %s[%d] %s/%d", query->name,
01078                     query, result->dnsa4_qname, i, ip, query->cur_port);
01079 
01080                 _dns_add_result(query, ip, query->cur_port,
01081                     query->cur_prio, query->cur_weight, result->dnsa4_ttl);
01082             }
01083         }
01084 
01085         free(result);
01086     }
01087 
01088     /* resolve the next host in the list */
01089     if (xhash_iter_first(query->hosts)) {
01090         char *ipport, *c, *tmp;
01091         int ipport_len, ip_len, port_len;
01092         dnsres_t res;
01093         union xhashv xhv;
01094 
01095         xhv.dnsres_val = &res;
01096 
01097         /* get the first entry */
01098         xhash_iter_get(query->hosts, (const char **) &ipport, &ipport_len, xhv.val);
01099 
01100         /* remove the host from the list */
01101         xhash_iter_zap(query->hosts);
01102 
01103         c = memchr(ipport, '/', ipport_len);
01104         ip_len = c - ipport;
01105         c++;
01106         port_len = ipport_len - (c - ipport);
01107 
01108         /* resolve hostname */
01109         free(query->cur_host);
01110         query->cur_host = strndup(ipport, ip_len);
01111         tmp = strndup(c, port_len);
01112         query->cur_port = atoi(tmp);
01113         free(tmp);
01114         query->cur_prio = res->prio;
01115         query->cur_weight = res->weight;
01116         query->cur_expiry = res->expiry;
01117         log_debug(ZONE, "dns ttl for %s@%p limited to %d", query->name, query, query->cur_expiry);
01118 
01119         if (query->s2s->resolve_aaaa) {
01120             log_debug(ZONE, "dns request for %s@%p: AAAA %s", query->name, query, query->cur_host);
01121 
01122             query->query = dns_submit_a6(NULL, query->cur_host, DNS_NOSRCH, _dns_result_aaaa, query);
01123 
01124             /* if submit failed, call ourselves with a NULL result */
01125             if (query->query == NULL)
01126                 _dns_result_aaaa(ctx, NULL, query);
01127         } else {
01128             log_debug(ZONE, "dns request for %s@%p: A %s", query->name, query, query->cur_host);
01129 
01130             query->query = dns_submit_a4(NULL, query->cur_host, DNS_NOSRCH, _dns_result_a, query);
01131 
01132             /* if submit failed, call ourselves with a NULL result */
01133             if (query->query == NULL)
01134                 _dns_result_a(ctx, NULL, query);
01135         }
01136 
01137     /* finished */
01138     } else {
01139         time_t now = time(NULL);
01140         char *domain;
01141 
01142         free(query->cur_host);
01143         query->cur_host = NULL;
01144 
01145         log_debug(ZONE, "dns requests for %s@%p complete: %d (%d)", query->name,
01146             query, xhash_count(query->results), query->expiry);
01147 
01148         /* update query TTL */
01149         if (query->expiry > query->s2s->dns_max_ttl)
01150             query->expiry = query->s2s->dns_max_ttl;
01151 
01152         if (query->expiry < query->s2s->dns_min_ttl)
01153             query->expiry = query->s2s->dns_min_ttl;
01154 
01155         query->expiry += now;
01156 
01157         /* update result TTLs - the query expiry MUST NOT be longer than all result expiries */
01158         if (xhash_iter_first(query->results)) {
01159             union xhashv xhv;
01160             dnsres_t res;
01161 
01162             xhv.dnsres_val = &res;
01163 
01164             do {
01165                 xhash_iter_get(query->results, NULL, NULL, xhv.val);
01166 
01167                 if (res->expiry > query->s2s->dns_max_ttl)
01168                     res->expiry = query->s2s->dns_max_ttl;
01169 
01170                 if (res->expiry < query->s2s->dns_min_ttl)
01171                     res->expiry = query->s2s->dns_min_ttl;
01172 
01173                 res->expiry += now;
01174             } while(xhash_iter_next(query->results));
01175         }
01176 
01177         xhash_free(query->hosts);
01178         query->hosts = NULL;
01179         if (idna_to_unicode_8z8z(query->name, &domain, 0) != IDNA_SUCCESS) {
01180             log_write(query->s2s->log, LOG_ERR, "idna dns decode for %s failed", query->name);
01181             /* fake empty results to shortcut resolution failure */
01182             xhash_free(query->results);
01183             query->results = xhash_new(71);
01184             query->expiry = time(NULL) + 99999999;
01185             domain = strdup(query->name);
01186         }
01187         out_resolve(query->s2s, domain, query->results, query->expiry);
01188         free(domain);
01189         free(query->name);
01190         free(query);
01191     }
01192 }
01193 
01194 void dns_resolve_domain(s2s_t s2s, dnscache_t dns) {
01195     dnsquery_t query = (dnsquery_t) calloc(1, sizeof(struct dnsquery_st));
01196 
01197     query->s2s = s2s;
01198     query->results = xhash_new(71);
01199     if (idna_to_ascii_8z(dns->name, &query->name, 0) != IDNA_SUCCESS) {
01200         log_write(s2s->log, LOG_ERR, "idna dns encode for %s failed", dns->name);
01201         /* shortcut resolution failure */
01202         query->expiry = time(NULL) + 99999999;
01203         out_resolve(query->s2s, dns->name, query->results, query->expiry);
01204         return;
01205     }
01206     query->hosts = xhash_new(71);
01207     query->srv_i = -1;
01208     query->expiry = 0;
01209     query->cur_host = NULL;
01210     query->cur_port = 0;
01211     query->cur_expiry = 0;
01212     query->query = NULL;
01213     dns->query = query;
01214 
01215     log_debug(ZONE, "dns resolve for %s@%p started", query->name, query);
01216 
01217     /* - resolve all SRV records to host/port
01218      * - if no results, include domain/5269
01219      * - resolve all host/port combinations
01220      * - return result
01221      */
01222     _dns_result_srv(NULL, NULL, query);
01223 }
01224 
01226 void out_resolve(s2s_t s2s, char *domain, xht results, time_t expiry) {
01227     dnscache_t dns;
01228 
01229     /* no results, resolve failed */
01230     if(xhash_count(results) == 0) {
01231         dns = xhash_get(s2s->dnscache, domain);
01232         if (dns != NULL) {
01233             /* store negative DNS cache */
01234             xhash_free(dns->results);
01235             dns->query = NULL;
01236             dns->results = NULL;
01237             dns->expiry = expiry;
01238             dns->pending = 0;
01239         }
01240 
01241         log_write(s2s->log, LOG_NOTICE, "dns lookup for %s failed", domain);
01242 
01243         /* bounce queue */
01244         out_bounce_domain_queues(s2s, domain, stanza_err_REMOTE_SERVER_NOT_FOUND);
01245 
01246         xhash_free(results);
01247         return;
01248     }
01249 
01250     log_write(s2s->log, LOG_NOTICE, "dns lookup for %s returned %d result%s (ttl %d)",
01251         domain, xhash_count(results), xhash_count(results)!=1?"s":"", expiry - time(NULL));
01252 
01253     /* get the cache entry */
01254     dns = xhash_get(s2s->dnscache, domain);
01255 
01256     if(dns == NULL) {
01257         /* retry using punycode */
01258         char *punydomain;
01259         if (idna_to_ascii_8z(domain, &punydomain, 0) == IDNA_SUCCESS) {
01260             dns = xhash_get(s2s->dnscache, punydomain);
01261             free(punydomain);
01262         }
01263     }
01264 
01265     if(dns == NULL) {
01266         log_write(s2s->log, LOG_ERR, "weird, never requested %s resolution", domain);
01267         return;
01268     }
01269 
01270     /* fill it out */
01271     xhash_free(dns->results);
01272     dns->query = NULL;
01273     dns->results = results;
01274     dns->expiry = expiry;
01275     dns->pending = 0;
01276 
01277     out_flush_domain_queues(s2s, domain);
01278 
01279     /* delete the cache entry if caching is disabled */
01280     if (!s2s->dns_cache_enabled && !dns->pending) {
01281         xhash_free(dns->results);
01282         xhash_zap(s2s->dnscache, domain);
01283         free(dns);
01284     }
01285 }
01286 
01288 static int _out_mio_callback(mio_t m, mio_action_t a, mio_fd_t fd, void *data, void *arg) {
01289     conn_t out = (conn_t) arg;
01290     char ipport[INET6_ADDRSTRLEN + 17];
01291     int nbytes;
01292 
01293     switch(a) {
01294         case action_READ:
01295             log_debug(ZONE, "read action on fd %d", fd->fd);
01296 
01297             /* they did something */
01298             out->last_activity = time(NULL);
01299 
01300             ioctl(fd->fd, FIONREAD, &nbytes);
01301             if(nbytes == 0) {
01302                 sx_kill(out->s);
01303                 return 0;
01304             }
01305 
01306             return sx_can_read(out->s);
01307 
01308         case action_WRITE:
01309             log_debug(ZONE, "write action on fd %d", fd->fd);
01310 
01311             /* update activity timestamp */
01312             out->last_activity = time(NULL);
01313 
01314             return sx_can_write(out->s);
01315 
01316         case action_CLOSE:
01317             log_debug(ZONE, "close action on fd %d", fd->fd);
01318 
01319             jqueue_push(out->s2s->dead, (void *) out->s, 0);
01320 
01321             log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] disconnect, packets: %i", fd->fd, out->ip, out->port, out->packet_count);
01322 
01323 
01324             if (out->s2s->out_reuse) {
01325                 /* generate the ip/port pair */
01326                 snprintf(ipport, INET6_ADDRSTRLEN + 16, "%s/%d", out->ip, out->port);
01327 
01328                 xhash_zap(out->s2s->out_host, ipport);
01329             }
01330 
01331             if (xhash_iter_first(out->routes)) {
01332                 char *rkey;
01333                 int rkeylen;
01334                 char *c;
01335                 int c_len;
01336 
01337                 /* remove all the out_dest entries */
01338                 do {
01339                     xhash_iter_get(out->routes, (const char **) &rkey, &rkeylen, NULL);
01340                     c = memchr(rkey, '/', rkeylen);
01341                     c++;
01342                     c_len = rkeylen - (c - rkey);
01343 
01344                     log_debug(ZONE, "route '%.*s'", rkeylen, rkey);
01345                     if (xhash_getx(out->s2s->out_dest, c, c_len) != NULL) {
01346                         log_debug(ZONE, "removing dest entry for '%.*s'", c_len, c);
01347                         xhash_zapx(out->s2s->out_dest, c, c_len);
01348                     }
01349                 } while(xhash_iter_next(out->routes));
01350             }
01351 
01352             if (xhash_iter_first(out->routes)) {
01353                 char *rkey;
01354                 int rkeylen;
01355                 jqueue_t q;
01356                 int npkt;
01357 
01358                 /* retry all the routes */
01359                 do {
01360                     xhash_iter_get(out->routes, (const char **) &rkey, &rkeylen, NULL);
01361 
01362                     q = xhash_getx(out->s2s->outq, rkey, rkeylen);
01363                     if (out->s2s->retry_limit > 0 && q != NULL && jqueue_age(q) > out->s2s->retry_limit) {
01364                         log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] retry limit reached for '%.*s' queue", fd->fd, out->ip, out->port, rkeylen, rkey);
01365                         q = NULL;
01366                     }
01367 
01368                     if (q != NULL && (npkt = jqueue_size(q)) > 0 && xhash_get(out->states, rkey) != (void*) conn_INPROGRESS) {
01369                         conn_t retry;
01370 
01371                         log_debug(ZONE, "retrying connection for '%.*s' queue", rkeylen, rkey);
01372                         if (!out_route(out->s2s, rkey, rkeylen, &retry, 0)) {
01373                             log_debug(ZONE, "retry successful");
01374 
01375                             if (retry != NULL) {
01376                                 /* flush queue */
01377                                 out_flush_route_queue(out->s2s, rkey, rkeylen);
01378                             }
01379                         } else {
01380                             log_debug(ZONE, "retry failed");
01381 
01382                             /* bounce queue */
01383                             out_bounce_route_queue(out->s2s, rkey, rkeylen, stanza_err_SERVICE_UNAVAILABLE);
01384                             _out_dns_mark_bad(out, NULL);
01385                         }
01386                     } else {
01387                         /* bounce queue */
01388                         out_bounce_route_queue(out->s2s, rkey, rkeylen, stanza_err_REMOTE_SERVER_TIMEOUT);
01389                         _out_dns_mark_bad(out, NULL);
01390                     }
01391                 } while(xhash_iter_next(out->routes));
01392             }
01393 
01394             jqueue_push(out->s2s->dead_conn, (void *) out, 0);
01395 
01396         case action_ACCEPT:
01397             break;
01398     }
01399 
01400     return 0;
01401 }
01402 
01403 void send_dialbacks(conn_t out)
01404 {
01405   char *rkey;
01406   int rkeylen;
01407 
01408   if (out->s2s->dns_bad_timeout > 0) {
01409       dnsres_t bad = xhash_get(out->s2s->dns_bad, out->key);
01410 
01411       if (bad != NULL) {
01412           log_debug(ZONE, "removing bad host entry for '%s'", out->key);
01413           xhash_zap(out->s2s->dns_bad, out->key);
01414           free(bad->key);
01415           free(bad);
01416       }
01417   }
01418 
01419   if (xhash_iter_first(out->routes)) {
01420        log_debug(ZONE, "sending dialback packets for %s", out->key);
01421        do {
01422             xhash_iter_get(out->routes, (const char **) &rkey, &rkeylen, NULL);
01423             _out_dialback(out, rkey, rkeylen);
01424           } while(xhash_iter_next(out->routes));
01425   }
01426 
01427   return;
01428 }
01429 
01430 static int _out_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) {
01431     conn_t out = (conn_t) arg;
01432     sx_buf_t buf = (sx_buf_t) data;
01433     int len, ns, elem, starttls = 0;
01434     sx_error_t *sxe;
01435     nad_t nad;
01436 
01437     switch(e) {
01438         case event_WANT_READ:
01439             log_debug(ZONE, "want read");
01440             mio_read(out->s2s->mio, out->fd);
01441             break;
01442 
01443         case event_WANT_WRITE:
01444             log_debug(ZONE, "want write");
01445             mio_write(out->s2s->mio, out->fd);
01446             break;
01447 
01448         case event_READ:
01449             log_debug(ZONE, "reading from %d", out->fd->fd);
01450 
01451             /* do the read */
01452             len = recv(out->fd->fd, buf->data, buf->len, 0);
01453 
01454             if(len < 0) {
01455                 if(MIO_WOULDBLOCK) {
01456                     buf->len = 0;
01457                     return 0;
01458                 }
01459 
01460                 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);
01461 
01462                 if (!out->online) {
01463                     _out_dns_mark_bad(out, NULL);
01464                 }
01465 
01466                 sx_kill(s);
01467 
01468                 return -1;
01469             }
01470 
01471             else if(len == 0) {
01472                 /* they went away */
01473                 sx_kill(s);
01474 
01475                 return -1;
01476             }
01477 
01478             log_debug(ZONE, "read %d bytes", len);
01479 
01480             buf->len = len;
01481 
01482             return len;
01483 
01484         case event_WRITE:
01485             log_debug(ZONE, "writing to %d", out->fd->fd);
01486 
01487             len = send(out->fd->fd, buf->data, buf->len, 0);
01488             if(len >= 0) {
01489                 log_debug(ZONE, "%d bytes written", len);
01490                 return len;
01491             }
01492 
01493             if(MIO_WOULDBLOCK)
01494                 return 0;
01495 
01496             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);
01497 
01498             if (!out->online) {
01499                 _out_dns_mark_bad(out, NULL);
01500             }
01501 
01502             sx_kill(s);
01503 
01504             return -1;
01505 
01506         case event_ERROR:
01507             sxe = (sx_error_t *) data;
01508             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);
01509 
01510             /* mark as bad if we did not manage to connect or there is unrecoverable stream error */
01511             if (!out->online ||
01512                     (sxe->code == SX_ERR_STREAM &&
01513                         (strstr(sxe->specific, "host-gone") ||        /* it's not there now */
01514                          strstr(sxe->specific, "host-unknown") ||     /* they do not service the host */
01515                          strstr(sxe->specific, "not-authorized") ||   /* they do not want us there */
01516                          strstr(sxe->specific, "see-other-host") ||   /* we do not support redirections yet */
01517                          strstr(sxe->specific, "system-shutdown") ||  /* they are going down */
01518                          strstr(sxe->specific, "policy-violation") || /* they do not want us there */
01519                          strstr(sxe->specific, "remote-connection-failed") ||  /* the required remote entity is gone */
01520                          strstr(sxe->specific, "unsupported-encoding") ||      /* they do not like our encoding */
01521                          strstr(sxe->specific, "undefined-condition") ||       /* something bad happend */
01522                          strstr(sxe->specific, "internal-server-error") ||     /* that server is broken */
01523                          strstr(sxe->specific, "unsupported-version")          /* they do not support our stream version */
01524                         ))) {
01525                 _out_dns_mark_bad(out, NULL);
01526             }
01527 
01528             sx_kill(s);
01529 
01530             return -1;
01531 
01532         case event_OPEN:
01533             log_debug(ZONE, "OPEN event for %s", out->key);
01534             break;
01535 
01536         case event_STREAM:
01537             /* check stream version - NULl = pre-xmpp (some jabber1 servers) */
01538             log_debug(ZONE, "STREAM event for %s stream version is %s", out->key, out->s->res_version);
01539 
01540             /* first time, bring them online */
01541             if(!out->online) {
01542                 log_debug(ZONE, "outgoing conn to %s is online", out->key);
01543 
01544                 /* if no stream version from either side, kick off dialback for each route, */
01545                 /* otherwise wait for stream features */
01546                 if (((out->s->res_version==NULL) || (out->s2s->sx_ssl == NULL)) && out->s2s->require_tls == 0) {
01547                      log_debug(ZONE, "no stream version, sending dialbacks for %s immediately", out->key);
01548                      out->online = 1;
01549                      send_dialbacks(out);
01550                 } else
01551                      log_debug(ZONE, "outgoing conn to %s - waiting for STREAM features", out->key);
01552             }
01553 
01554             break;
01555 
01556         case event_PACKET:
01557             /* we're counting packets */
01558             out->packet_count++;
01559             out->s2s->packet_count++;
01560 
01561             nad = (nad_t) data;
01562 
01563             /* watch for the features packet - STARTTLS and/or SASL*/
01564             if ((out->s->res_version!=NULL)
01565                  && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_STREAMS)
01566                  && strncmp(uri_STREAMS, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_STREAMS)) == 0
01567                  && NAD_ENAME_L(nad, 0) == 8 && strncmp("features", NAD_ENAME(nad, 0), 8) == 0) {
01568                 log_debug(ZONE, "got the stream features packet");
01569 
01570 #ifdef HAVE_SSL
01571                 /* starttls if we can */
01572                 if(out->s2s->sx_ssl != NULL && s->ssf == 0) {
01573                     ns = nad_find_scoped_namespace(nad, uri_TLS, NULL);
01574                     if(ns >= 0) {
01575                         elem = nad_find_elem(nad, 0, ns, "starttls", 1);
01576                         if(elem >= 0) {
01577                             log_debug(ZONE, "got STARTTLS in stream features");
01578                             if(sx_ssl_client_starttls(out->s2s->sx_ssl, s, out->s2s->local_pemfile) == 0) {
01579                                 starttls = 1;
01580                                 nad_free(nad);
01581                                 return 0;
01582                             }
01583                             log_write(out->s2s->log, LOG_ERR, "unable to establish encrypted session with peer");
01584                         }
01585                     }
01586                 }
01587 
01588                 /* If we're not establishing a starttls connection, send dialbacks */
01589                 if (!starttls) {
01590                     if (out->s2s->require_tls == 0 || s->ssf > 0) {
01591                      log_debug(ZONE, "No STARTTLS, sending dialbacks for %s", out->key);
01592                      out->online = 1;
01593                      send_dialbacks(out);
01594                     } else {
01595                         log_debug(ZONE, "No STARTTLS, dialbacks disabled for non-TLS connections, cannot complete negotiation");
01596                     }
01597                 }
01598 #else
01599                 if (out->s2s->require_tls == 0) {
01600                 out->online = 1;
01601                 send_dialbacks(out);
01602                 }
01603 #endif
01604             }
01605 
01606 
01607             /* we only accept dialback packets */
01608             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) {
01609                 log_debug(ZONE, "got a non-dialback packet on an outgoing conn, dropping it");
01610                 nad_free(nad);
01611                 return 0;
01612             }
01613 
01614             /* and then only result and verify */
01615             if(NAD_ENAME_L(nad, 0) == 6) {
01616                 if(strncmp("result", NAD_ENAME(nad, 0), 6) == 0) {
01617                     _out_result(out, nad);
01618                     return 0;
01619                 }
01620 
01621                 if(strncmp("verify", NAD_ENAME(nad, 0), 6) == 0) {
01622                     _out_verify(out, nad);
01623                     return 0;
01624                 }
01625             }
01626 
01627             log_debug(ZONE, "unknown dialback packet, dropping it");
01628 
01629             nad_free(nad);
01630             return 0;
01631 
01632         case event_CLOSED:
01633             if (out->fd != NULL) {
01634             mio_close(out->s2s->mio, out->fd);
01635                 out->fd = NULL;
01636             }
01637             return -1;
01638     }
01639 
01640     return 0;
01641 }
01642 
01644 static void _out_result(conn_t out, nad_t nad) {
01645     int attr;
01646     jid_t from, to;
01647     char *rkey;
01648     int rkeylen;
01649 
01650     attr = nad_find_attr(nad, 0, -1, "from", NULL);
01651     if(attr < 0 || (from = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) {
01652         log_debug(ZONE, "missing or invalid from on db result packet");
01653         nad_free(nad);
01654         return;
01655     }
01656 
01657     attr = nad_find_attr(nad, 0, -1, "to", NULL);
01658     if(attr < 0 || (to = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) {
01659         log_debug(ZONE, "missing or invalid to on db result packet");
01660         jid_free(from);
01661         nad_free(nad);
01662         return;
01663     }
01664 
01665     rkey = s2s_route_key(NULL, to->domain, from->domain);
01666     rkeylen = strlen(rkey);
01667 
01668     /* key is valid */
01669     if(nad_find_attr(nad, 0, -1, "type", "valid") >= 0) {
01670         log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] outgoing route '%s' is now valid%s%s", out->fd->fd, out->ip, out->port, rkey, (out->s->flags & SX_SSL_WRAPPER) ? ", TLS negotiated" : "", out->s->compressed ? ", ZLIB compression enabled" : "");
01671 
01672         xhash_put(out->states, pstrdup(xhash_pool(out->states), rkey), (void *) conn_VALID);    /* !!! small leak here */
01673 
01674         log_debug(ZONE, "%s valid, flushing queue", rkey);
01675 
01676         /* flush the queue */
01677         out_flush_route_queue(out->s2s, rkey, rkeylen);
01678 
01679         free(rkey);
01680 
01681         jid_free(from);
01682         jid_free(to);
01683 
01684         nad_free(nad);
01685 
01686         return;
01687     }
01688 
01689     /* invalid */
01690     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);
01691 
01692     /* close connection */
01693     log_write(out->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] closing connection", out->fd->fd, out->ip, out->port);
01694 
01695     /* report stream error */
01696     sx_error(out->s, stream_err_INVALID_ID, "dialback negotiation failed");
01697 
01698     /* close the stream */
01699     sx_close(out->s);
01700 
01701     /* bounce queue */
01702     out_bounce_route_queue(out->s2s, rkey, rkeylen, stanza_err_SERVICE_UNAVAILABLE);
01703 
01704     free(rkey);
01705 
01706     jid_free(from);
01707     jid_free(to);
01708 
01709     nad_free(nad);
01710 }
01711 
01713 static void _out_verify(conn_t out, nad_t nad) {
01714     int attr, ns;
01715     jid_t from, to;
01716     conn_t in;
01717     char *rkey;
01718     int valid;
01719 
01720     attr = nad_find_attr(nad, 0, -1, "from", NULL);
01721     if(attr < 0 || (from = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) {
01722         log_debug(ZONE, "missing or invalid from on db verify packet");
01723         nad_free(nad);
01724         return;
01725     }
01726 
01727     attr = nad_find_attr(nad, 0, -1, "to", NULL);
01728     if(attr < 0 || (to = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) {
01729         log_debug(ZONE, "missing or invalid to on db verify packet");
01730         jid_free(from);
01731         nad_free(nad);
01732         return;
01733     }
01734 
01735     attr = nad_find_attr(nad, 0, -1, "id", NULL);
01736     if(attr < 0) {
01737         log_debug(ZONE, "missing id on db verify packet");
01738         jid_free(from);
01739         jid_free(to);
01740         nad_free(nad);
01741         return;
01742     }
01743 
01744     /* get the incoming conn */
01745     in = xhash_getx(out->s2s->in, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
01746     if(in == NULL) {
01747         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));
01748         jid_free(from);
01749         jid_free(to);
01750         nad_free(nad);
01751         return;
01752     }
01753 
01754     rkey = s2s_route_key(NULL, to->domain, from->domain);
01755 
01756     attr = nad_find_attr(nad, 0, -1, "type", "valid");
01757     if(attr >= 0) {
01758         xhash_put(in->states, pstrdup(xhash_pool(in->states), rkey), (void *) conn_VALID);
01759         log_write(in->s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] incoming route '%s' is now valid%s%s", in->fd->fd, in->ip, in->port, rkey, (in->s->flags & SX_SSL_WRAPPER) ? ", TLS negotiated" : "", in->s->compressed ? ", ZLIB compression enabled" : "");
01760         valid = 1;
01761     } else {
01762         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);
01763         valid = 0;
01764     }
01765 
01766     free(rkey);
01767 
01768     nad_free(nad);
01769 
01770     /* decrement outstanding verify counter */
01771     --out->verify;
01772 
01773     /* let them know what happened */
01774     nad = nad_new();
01775 
01776     ns = nad_add_namespace(nad, uri_DIALBACK, "db");
01777     nad_append_elem(nad, ns, "result", 0);
01778     nad_append_attr(nad, -1, "to", from->domain);
01779     nad_append_attr(nad, -1, "from", to->domain);
01780     nad_append_attr(nad, -1, "type", valid ? "valid" : "invalid");
01781 
01782     /* off it goes */
01783     sx_nad_write(in->s, nad);
01784 
01785     /* if invalid, close the stream */
01786     if (!valid) {
01787         /* generate stream error */
01788         sx_error(in->s, stream_err_INVALID_ID, "dialback negotiation failed");
01789 
01790         /* close the incoming stream */
01791         sx_close(in->s);
01792     }
01793 
01794     jid_free(from);
01795     jid_free(to);
01796 }
01797 
01798 /* bounce all packets in the queues for domain */
01799 int out_bounce_domain_queues(s2s_t s2s, const char *domain, int err)
01800 {
01801   char *rkey;
01802   int rkeylen;
01803   int pktcount = 0;
01804 
01805   if (xhash_iter_first(s2s->outq)) {
01806       do {
01807           xhash_iter_get(s2s->outq, (const char **) &rkey, &rkeylen, NULL);
01808           if(s2s_route_key_match(NULL, (char *) domain, rkey, rkeylen))
01809               pktcount += out_bounce_route_queue(s2s, rkey, rkeylen, err);
01810       } while(xhash_iter_next(s2s->outq));
01811   }
01812 
01813   return pktcount;
01814 }
01815 
01816 /* bounce all packets in the queue for route */
01817 int out_bounce_route_queue(s2s_t s2s, char *rkey, int rkeylen, int err)
01818 {
01819   jqueue_t q;
01820   pkt_t pkt;
01821   int pktcount = 0;
01822 
01823   q = xhash_getx(s2s->outq, rkey, rkeylen);
01824   if(q == NULL)
01825      return 0;
01826 
01827   while((pkt = jqueue_pull(q)) != NULL) {
01828      /* only packets with content, in namespace jabber:client and not already errors */
01829      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 && nad_find_attr(pkt->nad, 0, -1, "error", NULL) < 0) {
01830          sx_nad_write(s2s->router, stanza_tofrom(stanza_tofrom(stanza_error(pkt->nad, 1, err), 1), 0));
01831          pktcount++;
01832      }
01833      else
01834          nad_free(pkt->nad);
01835 
01836      jid_free(pkt->to);
01837      jid_free(pkt->from);
01838      free(pkt);
01839   }
01840 
01841   /* delete queue and remove domain from queue hash */
01842   log_debug(ZONE, "deleting out packet queue for %.*s", rkeylen, rkey);
01843   rkey = q->key;
01844   jqueue_free(q);
01845   xhash_zap(s2s->outq, rkey);
01846   free(rkey);
01847 
01848   return pktcount;
01849 }
01850 
01851 int out_bounce_conn_queues(conn_t out, int err)
01852 {
01853   char *rkey;
01854   int rkeylen;
01855   int pktcount = 0;
01856 
01857   /* bounce queues for all domains handled by this connection - iterate through routes */
01858   if (xhash_iter_first(out->routes)) {
01859       do {
01860           xhash_iter_get(out->routes, (const char **) &rkey, &rkeylen, NULL);
01861           pktcount += out_bounce_route_queue(out->s2s, rkey, rkeylen, err);
01862       } while(xhash_iter_next(out->routes));
01863   }
01864 
01865   return pktcount;
01866 }
01867 
01868 void out_flush_domain_queues(s2s_t s2s, const char *domain) {
01869   char *rkey;
01870   int rkeylen;
01871   char *c;
01872   int c_len;
01873 
01874   if (xhash_iter_first(s2s->outq)) {
01875       do {
01876           xhash_iter_get(s2s->outq, (const char **) &rkey, &rkeylen, NULL);
01877           c = memchr(rkey, '/', rkeylen);
01878           c++;
01879           c_len = rkeylen - (c - rkey);
01880           if (strncmp(domain, c, c_len) == 0)
01881               out_flush_route_queue(s2s, rkey, rkeylen);
01882       } while(xhash_iter_next(s2s->outq));
01883   }
01884 }
01885 
01886 void out_flush_route_queue(s2s_t s2s, char *rkey, int rkeylen) {
01887     jqueue_t q;
01888     pkt_t pkt;
01889     int npkt, i, ret;
01890 
01891     q = xhash_getx(s2s->outq, rkey, rkeylen);
01892     if(q == NULL)
01893         return;
01894 
01895     npkt = jqueue_size(q);
01896     log_debug(ZONE, "flushing %d packets for '%.*s' to out_packet", npkt, rkeylen, rkey);
01897 
01898     for(i = 0; i < npkt; i++) {
01899         pkt = jqueue_pull(q);
01900         if(pkt) {
01901             ret = out_packet(s2s, pkt);
01902             if (ret) {
01903                 /* uh-oh. the queue was deleted...
01904                    q and pkt have been freed
01905                    if q->key == rkey, rkey has also been freed */
01906                 return;
01907             }
01908         }
01909     }
01910 
01911     /* delete queue for route and remove route from queue hash */
01912     if (jqueue_size(q) == 0) {
01913         log_debug(ZONE, "deleting out packet queue for '%.*s'", rkeylen, rkey);
01914         rkey = q->key;
01915         jqueue_free(q);
01916         xhash_zap(s2s->outq, rkey);
01917         free(rkey);
01918     } else {
01919         log_debug(ZONE, "emptied queue gained more packets...");
01920     }
01921 }