Changeset 576

Show
Ignore:
Timestamp:
18/05/08 17:53:13 (7 months ago)
Author:
smoku
Message:

Merged asynchronous domain resolving in s2s component support by Simon Arlott. Closes #206 #208 #216

Location:
trunk
Files:
1 removed
18 modified

Legend:

Unmodified
Added
Removed
  • trunk/AUTHORS

    r465 r576  
    159159    - XEP-0054 completness, server vcards 
    160160 
     161  Simon Arlott 
     162    - asunchronous DNS resolver for s2s 
     163    - TLS certificates for each vhost 
     164    - misc fixes 
     165 
    161166Thanks also go to the testers and bug reporters out there - you know who 
    162167you are. Special thanks in this capacity to Justin Kirby, whose many 
  • trunk/configure.ac

    r573 r576  
    367367fi 
    368368 
     369# udns 
     370AC_CHECK_LIB(udns, dns_init) 
     371if test "x-$ac_cv_lib_udns_dns_init" != "x-yes" ; then 
     372    AC_MSG_ERROR([UDNS library not found]) 
     373fi 
     374 
    369375# 
    370376# SASL backend selection 
     
    921927                 storage/Makefile 
    922928                 c2s/Makefile 
    923                  resolver/Makefile 
    924929                 router/Makefile 
    925930                 s2s/Makefile 
  • trunk/etc/jabberd.cfg.dist.in

    r2 r576  
    1414 
    1515@jabberd_router_bin@      @sysconfdir@/router.xml 
    16 @jabberd_resolver_bin@    @sysconfdir@/resolver.xml 
    1716@jabberd_sm_bin@          @sysconfdir@/sm.xml 
    1817@jabberd_s2s_bin@         @sysconfdir@/s2s.xml 
  • trunk/etc/Makefile.am

    r151 r576  
    1 sysconf_DATA = c2s.xml.dist resolver.xml.dist router.xml.dist s2s.xml.dist sm.xml.dist jabberd.cfg.dist router-users.xml.dist router-filter.xml.dist 
    2 EXTRA_DIST = c2s.xml.dist.in resolver.xml.dist.in router.xml.dist.in s2s.xml.dist.in sm.xml.dist.in jabberd.cfg.dist.in router-users.xml.dist.in router-filter.xml.dist.in 
     1sysconf_DATA = c2s.xml.dist router.xml.dist s2s.xml.dist sm.xml.dist jabberd.cfg.dist router-users.xml.dist router-filter.xml.dist 
     2EXTRA_DIST = c2s.xml.dist.in router.xml.dist.in s2s.xml.dist.in sm.xml.dist.in jabberd.cfg.dist.in router-users.xml.dist.in router-filter.xml.dist.in 
    33 
    44SUBDIRS = templates 
    55 
    6 jabberd_bin = router resolver sm s2s c2s 
     6jabberd_bin = router sm s2s c2s 
    77 
    88edit = sed \ 
  • trunk/etc/resolver.xml.dist.in

    r513 r576  
    1 <!-- Resolver configuration --> 
    2 <resolver> 
    3   <!-- Our id on the network (default: resolver) --> 
    4   <id>resolver</id> 
    5  
    6   <!-- The process ID file. Comment this out if you don't need to know 
    7        the process ID from outside the process (eg for control scripts) --> 
    8   <pidfile>@localstatedir@/jabberd/pid/resolver.pid</pidfile> 
    9  
    10   <!-- Router connection configuration --> 
    11   <router> 
    12     <!-- IP/port the router is waiting for connections on --> 
    13     <ip>127.0.0.1</ip>            <!-- default: 127.0.0.1 --> 
    14     <port>5347</port>             <!-- default: 5347 --> 
    15  
    16     <!-- Username/password to authenticate as --> 
    17     <user>jabberd</user>          <!-- default: jabberd --> 
    18     <pass>secret</pass>           <!-- default: secret --> 
    19  
    20     <!-- File containing an SSL certificate and private key to use when 
    21          setting up an encrypted channel with the router. From 
    22          SSL_CTX_use_certificate_chain_file(3): "The certificates must be 
    23          in PEM format and must be sorted starting with the subject's 
    24          certificate (actual client or server certificate), followed 
    25          by intermediate CA certificates if applicable, and ending 
    26          at the highest level (root) CA" (the latter one being optional). 
    27          If this is commented out, or the file can't be read, no attempt 
    28          will be made to establish an encrypted channel with the router. --> 
    29     <!-- 
    30     <pemfile>@sysconfdir@/server.pem</pemfile> 
    31     --> 
    32  
    33     <!-- Router connection retry --> 
    34     <retry> 
    35       <!-- If the connection to the router can't be established at 
    36            startup, we should try again this many times before exiting. 
    37            Use -1 to retry indefinitely. [default: 3] --> 
    38       <init>3</init> 
    39  
    40       <!-- If we lost the connection to the router during normal 
    41            operation (ie we've successfully connected to the router in 
    42            the past), we should try to reconnect this many times before 
    43            exiting. Use -1 to retry indefinitely. [default: 3] --> 
    44       <lost>3</lost> 
    45  
    46       <!-- Sleep for this many seconds before trying attempting a 
    47            reconnect. [default: 2] --> 
    48       <sleep>2</sleep> 
    49     </retry> 
    50   </router> 
    51  
    52   <!-- Log configuration - type is "syslog", "file" or "stdout" --> 
    53   <log type='syslog'> 
    54     <!-- If logging to syslog, this is the log ident --> 
    55     <ident>jabberd/resolver</ident> 
    56  
    57     <!-- If logging to syslog, this is the log facility 
    58          (local0 - local7)                        [default: local3] --> 
    59     <facility>local3</facility> 
    60  
    61     <!-- If logging to file, this is the filename of the logfile --> 
    62     <!-- 
    63     <file>@localstatedir@/jabberd/log/resolver.log</file> 
    64     --> 
    65   </log> 
    66  
    67   <!-- SRV records will be resolved in the following order. The first 
    68        one that returns something will be used (ie dereferenced via an 
    69        A/AAAA lookup). If no SRV records are found, resolver will 
    70        fallback to a straight A/AAAA lookup. --> 
    71   <lookup> 
    72     <!-- _xmpp-server._tcp is mandated by the XMPP spec --> 
    73     <srv>_xmpp-server._tcp</srv> 
    74  
    75     <!-- traditionally, _jabber._tcp has been used --> 
    76     <srv>_jabber._tcp</srv> 
    77   </lookup> 
    78  
    79   <!-- If this is enabled, the resolver will look up AAAA records as well 
    80        as A records. This is needed if you want s2s to use IPv6. --> 
    81   <!-- 
    82   <ipv6/> 
    83   --> 
    84  
    85 </resolver> 
    86 <!-- 
    87   vim: syntax=xml 
    88 --> 
  • trunk/etc/s2s.xml.dist.in

    r513 r576  
    149149         Checks will be run every n seconds. 
    150150 
    151          0 disables all checks.                       (default: 60) --> 
     151         0 disables all checks except DNS expiry.     (default: 60) --> 
    152152    <interval>60</interval> 
    153153 
     
    164164         3. Completing dialback (incoming and outgoing) 
    165165 
     166         If stage 1 connection establishment fails and there are 
     167         alternative hosts for this route that have not failed  
     168         recently, they will be tried too before finally giving up. 
     169 
    166170         0 disables queue expiry.                     (default: 60) --> 
    167171    <queue>60</queue> 
    168172 
     173    <!-- Queue retry timeout. 
     174 
     175         If the queue is older than this timeout, the connection 
     176         will not be retried even if there are alternative hosts 
     177         that have not failed recently. 
     178 
     179         0 disables retry expiry.                    (default: 300) --> 
     180    <retry>300</retry> 
     181 
    169182    <!-- Idle connection checks. 
    170183 
    171            Connections that have not sent data for longer than this many 
    172            seconds will be dropped. 
    173  
    174            0 disables idle timeouts.              (default: 86400) --> 
     184         Connections that have not sent data for longer than this many 
     185         seconds will be dropped. 
     186 
     187         0 disables idle timeouts.                 (default: 86400) --> 
    175188    <idle>86400</idle> 
    176189 
     
    185198    <keepalive>0</keepalive> 
    186199 
     200    <!-- Interval between DNS result/bad host expiry. 
     201 
     202         0 disables expiry checks.                 (default: 300) --> 
     203    <dnscache>300</dnscache> 
    187204  </check> 
    188205 
     
    195212  </stats> 
    196213 
     214  <lookup> 
     215     <!-- SRV TCP services will be resolved in the following order. The first 
     216          one that returns something will be used (ie dereferenced via an 
     217          A/AAAA lookup). If no SRV records are found, resolver will 
     218          fallback to a straight A/AAAA lookup. --> 
     219 
     220    <!-- xmpp-server is mandated by the XMPP spec --> 
     221    <srv>xmpp-server</srv> 
     222 
     223    <!-- traditionally, jabber has been used --> 
     224    <srv>jabber</srv> 
     225 
     226 
     227    <!-- If this is enabled, the resolver will look up AAAA records as well 
     228         as A records. This is needed if you want s2s to use IPv6. 
     229         Connection attempts will be made to all IPv6 hosts before trying 
     230         IPv4 (see bad host timeout below). --> 
     231    <!-- 
     232    <resolve-ipv6/> 
     233    --> 
     234 
     235    <!-- Minimum time that DNS lookup results are cached (overrides max below). --> 
     236    <min-ttl>30</min-ttl> 
     237 
     238    <!-- Maximum time that DNS lookup results are cached. --> 
     239    <max-ttl>86400</max-ttl> 
     240 
     241    <!-- Minimum time to wait before using hosts that we have failed to 
     242         establish a connection to (unless there are no alternatives). 
     243         Do not set this too low - it is required to detect permanent 
     244         problems like broken IPv6 connectivity in order to attempt IPv4. 
     245 
     246         0 disables bad host caching.               (default: 3600) --> 
     247    <bad-host-timeout>3600</bad-host-timeout> 
     248 
     249    <!-- Disable the DNS cache (negative caching will still be done). 
     250         This is likely to negatively impact performance while saving 
     251         a small amount of memory since multiple DNS requests must 
     252         then be made for every re-connection. --> 
     253    <!-- 
     254    <no-cache/> 
     255    --> 
     256  </lookup> 
     257 
     258  <!-- If this is enabled, domains which share the same host will re-use 
     259       existing outgoing connections. This is a potential security risk 
     260       as the SSL connection from the first domain will be re-used too. --> 
     261  <out-reuse-conn/> 
    197262</s2s> 
    198263<!-- 
  • trunk/Makefile.am

    r522 r576  
    11EXTRA_DIST = Doxyfile.in README.win32 README.protocol contrib UPGRADE 
    22 
    3 SUBDIRS = etc tools man mio subst sx util c2s resolver router s2s sm storage 
     3SUBDIRS = etc tools man mio subst sx util c2s router s2s sm storage 
    44 
    55.PHONY: docs 
  • trunk/man/Makefile.am

    r2 r576  
    1 man_MANS = jabberd.8 c2s.8 resolver.8 router.8 s2s.8 sm.8 
    2 EXTRA_DIST = jabberd.8.in c2s.8.in resolver.8.in router.8.in s2s.8.in sm.8.in 
     1man_MANS = jabberd.8 c2s.8 router.8 s2s.8 sm.8 
     2EXTRA_DIST = jabberd.8.in c2s.8.in router.8.in s2s.8.in sm.8.in 
    33 
    4 jabberd_bin = router resolver sm s2s c2s 
     4jabberd_bin = router sm s2s c2s 
    55 
    66edit = sed \ 
  • trunk/man/resolver.8.in

    r2 r576  
    1 .TH @jabberd_resolver_bin@ 8 "28 August 2003" "@VERSION@" "jabberd project" 
    2 .SH NAME 
    3 @jabberd_resolver_bin@ \- DNS resolver 
    4 .SH SYNOPSIS 
    5 .B @jabberd_resolver_bin@ 
    6 .I [-h] [-c config] [-D] 
    7 .SH DESCRIPTION 
    8 .BR @jabberd_resolver_bin@ 
    9 is a helper component for s2s. It performs DNS lookups of external servers as required. 
    10 .SH OPTIONS 
    11 .TP 
    12 .B \-h 
    13 Show summary of options. 
    14 .TP 
    15 .B \-c 
    16 Alternate configuration file to use. The compiled-in default is @sysconfdir@/resolver.xml. 
    17 .TP 
    18 .B \-D 
    19 Print debugging output. You should configure jabberd with the --enable-debug switch to enable this. 
    20 .SH SEE ALSO 
    21 .BR jabberd (8) 
    22 .BR @jabberd_c2s_bin@ (8) 
    23 .BR @jabberd_router_bin@ (8) 
    24 .BR @jabberd_s2s_bin@ (8) 
    25 .BR @jabberd_sm_bin@ (8) 
    26 .SH AUTHOR 
    27 Robert Norris <rob@cataclysm.cx> 
  • trunk/mio/mio.h

    r530 r576  
    116116                                   mio_handler_t app, void *arg); 
    117117 
     118  struct mio_fd_st *(*mio_register)(struct mio_st **m, int fd, 
     119                                   mio_handler_t app, void *arg); 
     120 
    118121  void (*mio_app)(struct mio_st **m, struct mio_fd_st *fd, 
    119122                  mio_handler_t app, void *arg); 
     
    140143#define mio_connect(m, port, hostip, app, arg) \ 
    141144    (*m)->mio_connect(m, port, hostip, app, arg) 
     145 
     146/** for adding an existing socket connected to this mio */ 
     147#define mio_register(m, fd, app, arg) \ 
     148    (*m)->mio_register(m, fd, app, arg) 
    142149 
    143150/** re-set the app handler */ 
  • trunk/mio/mio_impl.h

    r414 r576  
    447447    static struct mio_st mio_impl = { 
    448448        _mio_free, 
    449         _mio_listen, _mio_connect, 
     449        _mio_listen, _mio_connect, _mio_setup_fd, 
    450450        _mio_app, 
    451451        _mio_close, 
  • trunk/s2s/in.c

    r511 r576  
    140140 
    141141#ifdef HAVE_SSL 
    142             sx_server_init(in->s, S2S_DB_HEADER | ((s2s->local_pemfile != NULL) ? SX_SSL_STARTTLS_OFFER : 0) ); 
     142            sx_server_init(in->s, S2S_DB_HEADER | ((s2s->sx_ssl != NULL) ? SX_SSL_STARTTLS_OFFER : 0) ); 
    143143#else 
    144144            sx_server_init(in->s, S2S_DB_HEADER); 
  • trunk/s2s/main.c

    r567 r576  
    6767static void _s2s_config_expand(s2s_t s2s) { 
    6868    char *str, secret[41]; 
     69    config_elem_t elem; 
    6970    int i, r; 
    7071 
     
    121122    s2s->local_port = j_atoi(config_get_one(s2s->config, "local.port", 0), 0); 
    122123 
    123     s2s->local_resolver = config_get_one(s2s->config, "local.resolver", 0); 
    124     if(s2s->local_resolver == NULL) 
    125         s2s->local_resolver = "resolver"; 
    126  
    127124    if(config_get(s2s->config, "local.secret") != NULL) 
    128125        s2s->local_secret = strdup(config_get_one(s2s->config, "local.secret", 0)); 
     
    152149    s2s->check_keepalive = j_atoi(config_get_one(s2s->config, "check.keepalive", 0), 0); 
    153150    s2s->check_idle = j_atoi(config_get_one(s2s->config, "check.idle", 0), 86400); 
    154  
     151    s2s->check_dnscache = j_atoi(config_get_one(s2s->config, "check.dnscache", 0), 300); 
     152    s2s->retry_limit = j_atoi(config_get_one(s2s->config, "check.retry", 0), 300); 
     153 
     154    if((elem = config_get(s2s->config, "lookup.srv")) != NULL) { 
     155        s2s->lookup_srv = elem->values; 
     156        s2s->lookup_nsrv = elem->nvalues; 
     157    } 
     158 
     159    s2s->resolve_aaaa = config_count(s2s->config, "lookup.ipv6") ? 1 : 0; 
     160    s2s->dns_cache_enabled = config_count(s2s->config, "lookup.no-cache") ? 0 : 1; 
     161    s2s->dns_bad_timeout = j_atoi(config_get_one(s2s->config, "lookup.bad-host-timeout", 0), 3600); 
     162    s2s->dns_min_ttl = j_atoi(config_get_one(s2s->config, "lookup.min-ttl", 0), 30); 
     163    if (s2s->dns_min_ttl < 5) 
     164        s2s->dns_min_ttl = 5; 
     165    s2s->dns_max_ttl = j_atoi(config_get_one(s2s->config, "lookup.max-ttl", 0), 86400); 
     166    s2s->out_reuse = config_count(s2s->config, "out-conn-reuse") ? 1 : 0; 
    155167} 
    156168 
     
    273285    conn_t conn; 
    274286    time_t now; 
    275     char *domain, ipport[INET6_ADDRSTRLEN + 17], *key; 
     287    char *rkey, *key; 
    276288    jqueue_t q; 
    277289    dnscache_t dns; 
     290    char *c; 
    278291    union xhashv xhv; 
    279292 
     
    285298            do { 
    286299                xhv.jq_val = &q; 
    287                 xhash_iter_get(s2s->outq, (const char **) &domain, xhv.val); 
    288  
    289                 log_debug(ZONE, "running time checks for %s", domain); 
     300                xhash_iter_get(s2s->outq, (const char **) &rkey, xhv.val); 
     301 
     302                log_debug(ZONE, "running time checks for %s", rkey); 
     303                c = strchr(rkey, '/'); 
     304                c++; 
    290305 
    291306                /* dns lookup timeout check first */ 
    292                 dns = xhash_get(s2s->dnscache, domain); 
    293                 if(dns == NULL) 
    294                     continue; 
    295  
    296                 if(dns->pending) { 
    297                     log_debug(ZONE, "dns lookup pending for %s", domain); 
     307                dns = xhash_get(s2s->dnscache, c); 
     308                if(dns != NULL && dns->pending) { 
     309                    log_debug(ZONE, "dns lookup pending for %s", c); 
    298310                    if(now > dns->init_time + s2s->check_queue) { 
    299                         log_write(s2s->log, LOG_NOTICE, "dns lookup for %s timed out", domain); 
     311                        log_write(s2s->log, LOG_NOTICE, "dns lookup for %s timed out", c); 
    300312 
    301313                        /* bounce queue */ 
    302                         out_bounce_queue(s2s, domain, stanza_err_REMOTE_SERVER_NOT_FOUND); 
     314                        out_bounce_route_queue(s2s, rkey, stanza_err_REMOTE_SERVER_NOT_FOUND); 
    303315 
    304316                        /* expire pending dns entry */ 
    305317                        xhash_zap(s2s->dnscache, dns->name); 
     318                        xhash_free(dns->results); 
     319                        if (dns->query != NULL) { 
     320                            if (dns->query->query != NULL) 
     321                                dns_cancel(NULL, dns->query->query); 
     322                            xhash_free(dns->query->hosts); 
     323                            xhash_free(dns->query->results); 
     324                            free(dns->query->name); 
     325                            free(dns->query); 
     326                        } 
    306327                        free(dns); 
    307328                    } 
     
    310331                } 
    311332 
    312                 /* generate the ip/port pair */ 
    313                 snprintf(ipport, INET6_ADDRSTRLEN + 16, "%s/%d", dns->ip, dns->port); 
    314  
    315333                /* get the conn */ 
    316                 conn = xhash_get(s2s->out, ipport); 
     334                conn = xhash_get(s2s->out_dest, c); 
    317335                if(conn == NULL) { 
    318336                    if(jqueue_size(q) > 0) { 
    319337                       /* no pending conn? perhaps it failed? */ 
    320                        log_debug(ZONE, "no pending connection for %s, bouncing %i packets in queue", domain, jqueue_size(q)); 
     338                       log_debug(ZONE, "no pending connection for %s, bouncing %i packets in queue", c, jqueue_size(q)); 
    321339 
    322340                       /* bounce queue */ 
    323                        out_bounce_queue(s2s, domain, stanza_err_REMOTE_SERVER_TIMEOUT); 
     341                       out_bounce_route_queue(s2s, rkey, stanza_err_REMOTE_SERVER_TIMEOUT); 
    324342                    } 
    325343 
     
    329347                /* connect timeout check */ 
    330348                if(!conn->online && now > conn->init_time + s2s->check_queue) { 
    331                     log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] connection to %s timed out", conn->fd->fd, conn->ip, conn->port, domain); 
    332  
    333                     /* bounce queue */ 
    334                     out_bounce_queue(s2s, domain, stanza_err_REMOTE_SERVER_TIMEOUT); 
     349                    dnsres_t bad; 
     350                    char *ipport; 
     351 
     352                    log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] connection to %s timed out", conn->fd->fd, conn->ip, conn->port, c); 
     353 
     354                    if (s2s->dns_bad_timeout > 0) { 
     355                        /* mark this host as bad */ 
     356                        ipport = dns_make_ipport(conn->ip, conn->port); 
     357                        bad = xhash_get(s2s->dns_bad, ipport); 
     358                        if (bad == NULL) { 
     359                            bad = (dnsres_t) calloc(1, sizeof(struct dnsres_st)); 
     360                            bad->key = ipport; 
     361                            xhash_put(s2s->dns_bad, ipport, bad); 
     362                        } else { 
     363                            free(ipport); 
     364                        } 
     365                        bad->expiry = time(NULL) + s2s->dns_bad_timeout; 
     366                    } 
    335367 
    336368                    /* close connection as per XMPP/RFC3920 */ 
     369                    /* the close function will retry or bounce the queue */ 
    337370                    sx_close(conn->s); 
    338  
    339371                } 
    340372            } while(xhash_iter_next(s2s->outq)); 
     
    345377 
    346378        /* outgoing connections */ 
    347         if(xhash_iter_first(s2s->out)) 
    348             do { 
    349                 xhv.conn_val = &conn; 
    350                 xhash_iter_get(s2s->out, (const char **) &key, xhv.val); 
    351                 log_debug(ZONE, "checking dialback state for outgoing conn %s", key); 
    352                 if (_s2s_check_conn_routes(s2s, conn, "outgoing")) { 
    353                     log_debug(ZONE, "checking pending verify requests for outgoing conn %s", key); 
    354                     if (conn->verify > 0 && now > conn->last_verify + s2s->check_queue) { 
    355                         log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] dialback verify request timed out", conn->fd->fd, conn->ip, conn->port); 
    356                         sx_error(conn->s, stream_err_CONNECTION_TIMEOUT, "dialback verify request timed out"); 
    357                         sx_close(conn->s); 
     379        if(s2s->out_reuse) { 
     380            if(xhash_iter_first(s2s->out_host)) 
     381                do { 
     382                    xhv.conn_val = &conn; 
     383                    xhash_iter_get(s2s->out_host, (const char **) &key, xhv.val); 
     384                    log_debug(ZONE, "checking dialback state for outgoing conn %s", key); 
     385                    if (_s2s_check_conn_routes(s2s, conn, "outgoing")) { 
     386                        log_debug(ZONE, "checking pending verify requests for outgoing conn %s", key); 
     387                        if (conn->verify > 0 && now > conn->last_verify + s2s->check_queue) { 
     388                            log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] dialback verify request timed out", conn->fd->fd, conn->ip, conn->port); 
     389                            sx_error(conn->s, stream_err_CONNECTION_TIMEOUT, "dialback verify request timed out"); 
     390                            sx_close(conn->s); 
     391                        } 
    358392                    } 
    359                 } 
    360             } while(xhash_iter_next(s2s->out)); 
     393                } while(xhash_iter_next(s2s->out_host)); 
     394        } else { 
     395            if(xhash_iter_first(s2s->out_dest)) 
     396                do { 
     397                    xhv.conn_val = &conn; 
     398                    xhash_iter_get(s2s->out_dest, (const char **) &key, xhv.val); 
     399                    log_debug(ZONE, "checking dialback state for outgoing conn %s (%s)", conn->dkey, conn->key); 
     400                    if (_s2s_check_conn_routes(s2s, conn, "outgoing")) { 
     401                        log_debug(ZONE, "checking pending verify requests for outgoing conn %s (%s)", conn->dkey, conn->key); 
     402                        if (conn->verify > 0 && now > conn->last_verify + s2s->check_queue) { 
     403                            log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] dialback verify request timed out", conn->fd->fd, conn->ip, conn->port); 
     404                            sx_error(conn->s, stream_err_CONNECTION_TIMEOUT, "dialback verify request timed out"); 
     405                            sx_close(conn->s); 
     406                        } 
     407                    } 
     408                } while(xhash_iter_next(s2s->out_dest)); 
     409        } 
    361410 
    362411        /* incoming open streams */ 
     
    392441 
    393442    /* keepalives */ 
    394     if(xhash_iter_first(s2s->out)) 
    395         do { 
    396             xhv.conn_val = &conn; 
    397             xhash_iter_get(s2s->out, NULL, xhv.val); 
    398  
    399             if(s2s->check_keepalive > 0 && conn->last_activity > 0 && now > conn->last_activity + s2s->check_keepalive && conn->s->state >= state_STREAM) { 
    400                 log_debug(ZONE, "sending keepalive for %d", conn->fd->fd); 
    401  
    402                 sx_raw_write(conn->s, " ", 1); 
    403             } 
    404         } while(xhash_iter_next(s2s->out)); 
     443    if(s2s->out_reuse) { 
     444        if(xhash_iter_first(s2s->out_host)) 
     445            do { 
     446                xhv.conn_val = &conn; 
     447                xhash_iter_get(s2s->out_host, NULL, xhv.val); 
     448 
     449                if(s2s->check_keepalive > 0 && conn->last_activity > 0 && now > conn->last_activity + s2s->check_keepalive && conn->s->state >= state_STREAM) { 
     450                    log_debug(ZONE, "sending keepalive for %d", conn->fd->fd); 
     451 
     452                    sx_raw_write(conn->s, " ", 1); 
     453                } 
     454            } while(xhash_iter_next(s2s->out_host)); 
     455    } else { 
     456        if(xhash_iter_first(s2s->out_dest)) 
     457            do { 
     458                xhv.conn_val = &conn; 
     459                xhash_iter_get(s2s->out_dest, NULL, xhv.val); 
     460 
     461                if(s2s->check_keepalive > 0 && conn->last_activity > 0 && now > conn->last_activity + s2s->check_keepalive && conn->s->state >= state_STREAM) { 
     462                    log_debug(ZONE, "sending keepalive for %d", conn->fd->fd); 
     463 
     464                    sx_raw_write(conn->s, " ", 1); 
     465                } 
     466            } while(xhash_iter_next(s2s->out_dest)); 
     467    } 
    405468 
    406469    /* idle timeouts - disconnect connections through which no packets have been sent for <idle> seconds */ 
    407470    if(s2s->check_idle > 0) { 
    408471 
    409        /* outgoing connections */ 
    410         if(xhash_iter_first(s2s->out)) 
    411             do { 
    412                 xhv.conn_val = &conn; 
    413                 xhash_iter_get(s2s->out, (const char **) &key, xhv.val); 
    414                 log_debug(ZONE, "checking idle state for %s", key); 
    415                 if (conn->last_packet > 0 && now > conn->last_packet + s2s->check_idle && conn->s->state >= state_STREAM) { 
    416                     log_write(s2s->log, LOG_NOTICE, "[%d] [%s, port=%d] idle timeout", conn->fd->fd, conn->ip, conn->port)