jabberd2  2.2.16
c2s/c2s.c
Go to the documentation of this file.
00001 /* vim: set et ts=4 sw=4: */
00002 /*
00003  * jabberd - Jabber Open Source Server
00004  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00005  *                    Ryan Eatmon, Robert Norris
00006  *
00007  * This program is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00020  */
00021 
00022 #include "c2s.h"
00023 #include <stringprep.h>
00024 
00025 static int _c2s_client_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) {
00026     sess_t sess = (sess_t) arg;
00027     sx_buf_t buf = (sx_buf_t) data;
00028     int rlen, len, ns, elem, attr;
00029     sx_error_t *sxe;
00030     nad_t nad;
00031     char root[9];
00032     bres_t bres, ires;
00033     stream_redirect_t redirect;
00034 
00035     switch(e) {
00036         case event_WANT_READ:
00037             log_debug(ZONE, "want read");
00038             mio_read(sess->c2s->mio, sess->fd);
00039             break;
00040 
00041         case event_WANT_WRITE:
00042             log_debug(ZONE, "want write");
00043             mio_write(sess->c2s->mio, sess->fd);
00044             break;
00045 
00046         case event_READ:
00047             log_debug(ZONE, "reading from %d", sess->fd->fd);
00048 
00049             /* check rate limits */
00050             if(sess->rate != NULL) {
00051                 if(rate_check(sess->rate) == 0) {
00052 
00053                     /* inform the app if we haven't already */
00054                     if(!sess->rate_log) {
00055                         if(s->state >= state_STREAM && sess->resources != NULL)
00056                             log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s] is being byte rate limited", sess->fd->fd, jid_user(sess->resources->jid));
00057                         else
00058                             log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s, port=%d] is being byte rate limited", sess->fd->fd, sess->ip, sess->port);
00059 
00060                         sess->rate_log = 1;
00061                     }
00062 
00063                     return -1;
00064                 }
00065 
00066                 /* find out how much we can have */
00067                 rlen = rate_left(sess->rate);
00068                 if(rlen > buf->len)
00069                     rlen = buf->len;
00070             }
00071 
00072             /* no limit, just read as much as we can */
00073             else
00074                 rlen = buf->len;
00075 
00076             /* do the read */
00077             len = recv(sess->fd->fd, buf->data, rlen, 0);
00078 
00079             /* update rate limits */
00080             if(sess->rate != NULL)
00081                 rate_add(sess->rate, len);
00082 
00083             if(len < 0) {
00084                 if(MIO_WOULDBLOCK) {
00085                     buf->len = 0;
00086                     return 0;
00087                 }
00088 
00089                 if(s->state >= state_STREAM && sess->resources != NULL)
00090                     log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s] read error: %s (%d)", sess->fd->fd, jid_user(sess->resources->jid), MIO_STRERROR(MIO_ERROR), MIO_ERROR);
00091                 else
00092                     log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s, port=%d] read error: %s (%d)", sess->fd->fd, sess->ip, sess->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR);
00093 
00094                 sx_kill(s);
00095 
00096                 return -1;
00097             }
00098 
00099             else if(len == 0) {
00100                 /* they went away */
00101                 sx_kill(s);
00102 
00103                 return -1;
00104             }
00105 
00106             log_debug(ZONE, "read %d bytes", len);
00107 
00108             /* If the first chars are "GET " then it's for HTTP (GET ....)
00109                and if we configured http client forwarding to a real http server */
00110             if (sess->c2s->http_forward && !sess->active && !sess->sasl_authd
00111                 && sess->result == NULL && len >= 4 && strncmp("GET ", buf->data, 4) == 0) {
00112                 char* http =
00113                     "HTTP/1.0 301 Found\r\n"
00114                     "Location: %s\r\n"
00115                     "Server: " PACKAGE_STRING "\r\n"
00116                     "Expires: Fri, 10 Oct 1997 10:10:10 GMT\r\n"
00117                     "Pragma: no-cache\r\n"
00118                     "Cache-control: private\r\n"
00119                     "Connection: close\r\n\r\n";
00120                 char *answer;
00121 
00122                 len = strlen(sess->c2s->http_forward) + strlen(http);
00123                 answer = malloc(len * sizeof(char));
00124                 sprintf (answer, http, sess->c2s->http_forward);
00125 
00126                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] bouncing HTTP request to %s", sess->fd->fd, sess->c2s->http_forward);
00127 
00128                 /* send HTTP answer */
00129                 len = send(sess->fd->fd, answer, len-1, 0);
00130 
00131                 free(answer);
00132 
00133                 /* close connection */
00134                 sx_kill(s);
00135 
00136                 return -1;
00137             }
00138 
00139             buf->len = len;
00140 
00141             return len;
00142 
00143         case event_WRITE:
00144             log_debug(ZONE, "writing to %d", sess->fd->fd);
00145 
00146             len = send(sess->fd->fd, buf->data, buf->len, 0);
00147             if(len >= 0) {
00148                 log_debug(ZONE, "%d bytes written", len);
00149                 return len;
00150             }
00151 
00152             if(MIO_WOULDBLOCK)
00153                 return 0;
00154 
00155             if(s->state >= state_OPEN && sess->resources != NULL)
00156                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s] write error: %s (%d)", sess->fd->fd, jid_user(sess->resources->jid), MIO_STRERROR(MIO_ERROR), MIO_ERROR);
00157             else
00158                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s. port=%d] write error: %s (%d)", sess->fd->fd, sess->ip, sess->port, MIO_STRERROR(MIO_ERROR), MIO_ERROR);
00159 
00160             sx_kill(s);
00161 
00162             return -1;
00163 
00164         case event_ERROR:
00165             sxe = (sx_error_t *) data;
00166             if(sess->resources != NULL)
00167                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s] error: %s (%s)", sess->fd->fd, jid_user(sess->resources->jid), sxe->generic, sxe->specific);
00168             else
00169                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s, port=%d] error: %s (%s)", sess->fd->fd, sess->ip, sess->port, sxe->generic, sxe->specific);
00170 
00171             break;
00172 
00173         case event_STREAM:
00174 
00175             if(s->req_to == NULL) {
00176                 log_debug(ZONE, "no stream to provided, closing");
00177                 sx_error(s, stream_err_HOST_UNKNOWN, "no 'to' attribute on stream header");
00178                 sx_close(s);
00179 
00180                 return 0;
00181             }
00182 
00183             /* send a see-other-host error if we're configured to do so */
00184             redirect = (stream_redirect_t) xhash_get(sess->c2s->stream_redirects, s->req_to);
00185             if (redirect != NULL) {
00186                 log_debug(ZONE, "redirecting client's stream using see-other-host for domain: '%s'", s->req_to);
00187                 len = strlen(redirect->to_address) + strlen(redirect->to_port) + 1;
00188                 char *other_host = (char *) malloc(len+1);
00189                 snprintf(other_host, len+1, "%s:%s", redirect->to_address, redirect->to_port);
00190                 sx_error_extended(s, stream_err_SEE_OTHER_HOST, other_host);
00191                 free(other_host);
00192                 sx_close(s);
00193                 
00194                 return 0;
00195             }
00196 
00197             /* setup the host */
00198             sess->host = xhash_get(sess->c2s->hosts, s->req_to);
00199 
00200             if(sess->host == NULL && sess->c2s->vhost == NULL) {
00201                 log_debug(ZONE, "no host available for requested domain '%s'", s->req_to);
00202                 sx_error(s, stream_err_HOST_UNKNOWN, "service requested for unknown domain");
00203                 sx_close(s);
00204 
00205                 return 0;
00206             }
00207 
00208             if(xhash_get(sess->c2s->sm_avail, s->req_to) == NULL) {
00209                 log_debug(ZONE, "sm for domain '%s' is not online", s->req_to);
00210                 sx_error(s, stream_err_HOST_GONE, "session manager for requested domain is not available");
00211                 sx_close(s);
00212 
00213                 return 0;
00214             }
00215 
00216             if(sess->host == NULL) {
00217                 /* create host on-fly */
00218                 sess->host = (host_t) pmalloc(xhash_pool(sess->c2s->hosts), sizeof(struct host_st));
00219                 memcpy(sess->host, sess->c2s->vhost, sizeof(struct host_st));
00220                 sess->host->realm = pstrdup(xhash_pool(sess->c2s->hosts), s->req_to);
00221                 xhash_put(sess->c2s->hosts, pstrdup(xhash_pool(sess->c2s->hosts), s->req_to), sess->host);
00222             }
00223 
00224 #ifdef HAVE_SSL
00225             if(sess->host->host_pemfile != NULL)
00226                 sess->s->flags |= SX_SSL_STARTTLS_OFFER;
00227             if(sess->host->host_require_starttls)
00228                 sess->s->flags |= SX_SSL_STARTTLS_REQUIRE;
00229 #endif
00230             break;
00231 
00232         case event_PACKET:
00233             /* we're counting packets */
00234             sess->packet_count++;
00235             sess->c2s->packet_count++;
00236 
00237             /* check rate limits */
00238             if(sess->stanza_rate != NULL) {
00239                 if(rate_check(sess->stanza_rate) == 0) {
00240 
00241                     /* inform the app if we haven't already */
00242                     if(!sess->stanza_rate_log) {
00243                         if(s->state >= state_STREAM && sess->resources != NULL)
00244                             log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s] is being stanza rate limited", sess->fd->fd, jid_user(sess->resources->jid));
00245                         else
00246                             log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s, port=%d] is being stanza rate limited", sess->fd->fd, sess->ip, sess->port);
00247 
00248                         sess->stanza_rate_log = 1;
00249                     }
00250                 }
00251 
00252                 /* update rate limits */
00253                 rate_add(sess->stanza_rate, 1);
00254             }
00255 
00256             nad = (nad_t) data;
00257 
00258             /* we only want (message|presence|iq) in jabber:client, everything else gets dropped */
00259             snprintf(root, 9, "%.*s", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
00260             if(NAD_ENS(nad, 0) != nad_find_namespace(nad, 0, uri_CLIENT, NULL) ||
00261                (strcmp(root, "message") != 0 && strcmp(root, "presence") != 0 && strcmp(root, "iq") != 0)) {
00262                 nad_free(nad);
00263                 return 0;
00264             }
00265 
00266             /* resource bind */
00267             if((ns = nad_find_scoped_namespace(nad, uri_BIND, NULL)) >= 0 && (elem = nad_find_elem(nad, 0, ns, "bind", 1)) >= 0 && nad_find_attr(nad, 0, -1, "type", "set") >= 0) {
00268                 bres_t bres;
00269                 jid_t jid = jid_new(sess->s->auth_id, -1);
00270 
00271                 /* get the resource */
00272                 elem = nad_find_elem(nad, elem, ns, "resource", 1);
00273 
00274                 /* user-specified resource */
00275                 if(elem >= 0) {
00276                     char resource_buf[1024];
00277 
00278                     if(NAD_CDATA_L(nad, elem) == 0) {
00279                         log_debug(ZONE, "empty resource specified on bind");
00280                         sx_nad_write(sess->s, stanza_error(nad, 0, stanza_err_BAD_REQUEST));
00281 
00282                         return 0;
00283                     }
00284 
00285                     snprintf(resource_buf, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
00286                     /* Put resource into JID */
00287                     if (jid == NULL || jid_reset_components(jid, jid->node, jid->domain, resource_buf) == NULL) {
00288                         log_debug(ZONE, "invalid jid data");
00289                         sx_nad_write(sess->s, stanza_error(nad, 0, stanza_err_BAD_REQUEST));
00290 
00291                         return 0;
00292                     }
00293 
00294                     /* check if resource already bound */
00295                     for(bres = sess->resources; bres != NULL; bres = bres->next)
00296                         if(strcmp(bres->jid->resource, jid->resource) == 0){
00297                             log_debug(ZONE, "resource /%s already bound - generating", jid->resource);
00298                             jid_random_part(jid, jid_RESOURCE);
00299                         }
00300                 }
00301                 else {
00302                     /* generate random resource */
00303                     log_debug(ZONE, "no resource given - generating");
00304                     jid_random_part(jid, jid_RESOURCE);
00305                 }
00306 
00307                 /* attach new bound jid holder */
00308                 bres = (bres_t) calloc(1, sizeof(struct bres_st));
00309                 bres->jid = jid;
00310                 if(sess->resources != NULL) {
00311                     for(ires = sess->resources; ires->next != NULL; ires = ires->next);
00312                     ires->next = bres;
00313                 } else
00314                     sess->resources = bres;
00315 
00316                 sess->bound += 1;
00317 
00318                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] bound: jid=%s", sess->s->tag, jid_full(bres->jid));
00319 
00320                 /* build a result packet, we'll send this back to the client after we have a session for them */
00321                 sess->result = nad_new();
00322 
00323                 ns = nad_add_namespace(sess->result, uri_CLIENT, NULL);
00324 
00325                 nad_append_elem(sess->result, ns, "iq", 0);
00326                 nad_set_attr(sess->result, 0, -1, "type", "result", 6);
00327 
00328                 attr = nad_find_attr(nad, 0, -1, "id", NULL);
00329                 if(attr >= 0)
00330                     nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
00331 
00332                 ns = nad_add_namespace(sess->result, uri_BIND, NULL);
00333 
00334                 nad_append_elem(sess->result, ns, "bind", 1);
00335                 nad_append_elem(sess->result, ns, "jid", 2);
00336                 nad_append_cdata(sess->result, jid_full(bres->jid), strlen(jid_full(bres->jid)), 3);
00337 
00338                 /* our local id */
00339                 sprintf(bres->c2s_id, "%d", sess->s->tag);
00340 
00341                 /* start a session with the sm */
00342                 sm_start(sess, bres);
00343 
00344                 /* finished with the nad */
00345                 nad_free(nad);
00346 
00347                 /* handled */
00348                 return 0;
00349             }
00350 
00351             /* resource unbind */
00352             if((ns = nad_find_scoped_namespace(nad, uri_BIND, NULL)) >= 0 && (elem = nad_find_elem(nad, 0, ns, "unbind", 1)) >= 0 && nad_find_attr(nad, 0, -1, "type", "set") >= 0) {
00353                 char resource_buf[1024];
00354                 bres_t bres;
00355 
00356                 /* get the resource */
00357                 elem = nad_find_elem(nad, elem, ns, "resource", 1);
00358 
00359                 if(elem < 0 || NAD_CDATA_L(nad, elem) == 0) {
00360                     log_debug(ZONE, "no/empty resource given to unbind");
00361                     sx_nad_write(sess->s, stanza_error(nad, 0, stanza_err_BAD_REQUEST));
00362 
00363                     return 0;
00364                 }
00365 
00366                 snprintf(resource_buf, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
00367                 if(stringprep_xmpp_resourceprep(resource_buf, 1024) != 0) {
00368                     log_debug(ZONE, "cannot resourceprep");
00369                     sx_nad_write(sess->s, stanza_error(nad, 0, stanza_err_BAD_REQUEST));
00370 
00371                     return 0;
00372                 }
00373 
00374                 /* check if resource bound */
00375                 for(bres = sess->resources; bres != NULL; bres = bres->next)
00376                     if(strcmp(bres->jid->resource, resource_buf) == 0)
00377                         break;
00378 
00379                 if(bres == NULL) {
00380                     log_debug(ZONE, "resource /%s not bound", resource_buf);
00381                     sx_nad_write(sess->s, stanza_error(nad, 0, stanza_err_ITEM_NOT_FOUND));
00382 
00383                     return 0;
00384                 }
00385 
00386                 /* build a result packet, we'll send this back to the client after we close a session for them */
00387                 sess->result = nad_new();
00388 
00389                 ns = nad_add_namespace(sess->result, uri_CLIENT, NULL);
00390 
00391                 nad_append_elem(sess->result, ns, "iq", 0);
00392                 nad_set_attr(sess->result, 0, -1, "type", "result", 6);
00393 
00394                 attr = nad_find_attr(nad, 0, -1, "id", NULL);
00395                 if(attr >= 0)
00396                     nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
00397 
00398                 /* end a session with the sm */
00399                 sm_end(sess, bres);
00400 
00401                 /* finished with the nad */
00402                 nad_free(nad);
00403 
00404                 /* handled */
00405                 return 0;
00406             }
00407 
00408             /* pre-session requests */
00409             if(!sess->active && sess->sasl_authd && sess->result == NULL && strcmp(root, "iq") == 0 && nad_find_attr(nad, 0, -1, "type", "set") >= 0) {
00410                 log_debug(ZONE, "unrecognised pre-session packet, bye");
00411                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] unrecognized pre-session packet, closing stream", sess->s->tag);
00412 
00413                 sx_error(s, stream_err_NOT_AUTHORIZED, "unrecognized pre-session stanza");
00414                 sx_close(s);
00415 
00416                 nad_free(nad);
00417                 return 0;
00418             }
00419 
00420 #ifdef HAVE_SSL
00421             /* drop packets if they have to starttls and they haven't */
00422             if((sess->s->flags & SX_SSL_STARTTLS_REQUIRE) && sess->s->ssf == 0) {
00423                 log_debug(ZONE, "pre STARTTLS packet, dropping");
00424                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] got pre STARTTLS packet, dropping", sess->s->tag);
00425 
00426                 sx_error(s, stream_err_POLICY_VIOLATION, "STARTTLS is required for this stream");
00427 
00428                 nad_free(nad);
00429                 return 0;
00430             }
00431 #endif
00432 
00433             /* handle iq:auth packets */
00434             if(authreg_process(sess->c2s, sess, nad) == 0)
00435                 return 0;
00436 
00437             /* drop it if no session */
00438             if(!sess->active) {
00439                 log_debug(ZONE, "pre-session packet, bye");
00440                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] packet sent before session start, closing stream", sess->s->tag);
00441 
00442                 sx_error(s, stream_err_NOT_AUTHORIZED, "stanza sent before session start");
00443                 sx_close(s);
00444 
00445                 nad_free(nad);
00446                 return 0;
00447             }
00448 
00449             /* validate 'from' */
00450             assert(sess->resources != NULL);
00451             if(sess->bound > 1) {
00452                 bres = NULL;
00453                 if((attr = nad_find_attr(nad, 0, -1, "from", NULL)) >= 0)
00454                     for(bres = sess->resources; bres != NULL; bres = bres->next)
00455                         if(strncmp(jid_full(bres->jid), NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr)) == 0)
00456                             break;
00457 
00458                 if(bres == NULL) {
00459                     if(attr >= 0) {
00460                         log_debug(ZONE, "packet from: %.*s that has not bound the resource", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
00461                     } else {
00462                         log_debug(ZONE, "packet without 'from' on multiple resource stream");
00463                     }
00464 
00465                     sx_nad_write(sess->s, stanza_error(nad, 0, stanza_err_UNKNOWN_SENDER));
00466 
00467                     return 0;
00468                 }
00469             } else
00470                 bres = sess->resources;
00471 
00472             /* pass it on to the session manager */
00473             sm_packet(sess, bres, nad);
00474 
00475             break;
00476 
00477         case event_OPEN:
00478 
00479             /* only send a result and bring us online if this wasn't a sasl auth */
00480             if(strlen(s->auth_method) < 4 || strncmp("SASL", s->auth_method, 4) != 0) {
00481                 /* return the auth result to the client */
00482                 sx_nad_write(s, sess->result);
00483                 sess->result = NULL;
00484 
00485                 /* we're good to go */
00486                 sess->active = 1;
00487             }
00488 
00489             /* they sasl auth'd, so we only want the new-style session start */
00490             else {
00491                 log_write(sess->c2s->log, LOG_NOTICE, "[%d] %s authentication succeeded: %s %s:%d%s%s",
00492                     sess->s->tag, &sess->s->auth_method[5],
00493                     sess->s->auth_id, sess->s->ip, sess->s->port,
00494                     sess->s->ssf ? " TLS" : "", sess->s->compressed ? " ZLIB" : ""
00495                 );
00496                 sess->sasl_authd = 1;
00497             }
00498 
00499             break;
00500 
00501         case event_CLOSED:
00502             mio_close(sess->c2s->mio, sess->fd);
00503             sess->fd = NULL;
00504             return -1;
00505     }
00506 
00507     return 0;
00508 }
00509 
00510 static int _c2s_client_accept_check(c2s_t c2s, mio_fd_t fd, char *ip) {
00511     rate_t rt;
00512 
00513     if(access_check(c2s->access, ip) == 0) {
00514         log_write(c2s->log, LOG_NOTICE, "[%d] [%s] access denied by configuration", fd->fd, ip);
00515         return 1;
00516     }
00517 
00518     if(c2s->conn_rate_total != 0) {
00519         rt = (rate_t) xhash_get(c2s->conn_rates, ip);
00520         if(rt == NULL) {
00521             rt = rate_new(c2s->conn_rate_total, c2s->conn_rate_seconds, c2s->conn_rate_wait);
00522             xhash_put(c2s->conn_rates, pstrdup(xhash_pool(c2s->conn_rates), ip), (void *) rt);
00523             pool_cleanup(xhash_pool(c2s->conn_rates), (void (*)(void *)) rate_free, rt);
00524         }
00525 
00526         if(rate_check(rt) == 0) {
00527             log_write(c2s->log, LOG_NOTICE, "[%d] [%s] is being connect rate limited", fd->fd, ip);
00528             return 1;
00529         }
00530 
00531         rate_add(rt, 1);
00532     }
00533 
00534     return 0;
00535 }
00536 
00537 static int _c2s_client_mio_callback(mio_t m, mio_action_t a, mio_fd_t fd, void *data, void *arg) {
00538     sess_t sess = (sess_t) arg;
00539     c2s_t c2s = (c2s_t) arg;
00540     bres_t bres;
00541     struct sockaddr_storage sa;
00542     int namelen = sizeof(sa), port, nbytes, flags = 0;
00543 
00544     switch(a) {
00545         case action_READ:
00546             log_debug(ZONE, "read action on fd %d", fd->fd);
00547 
00548             /* they did something */
00549             sess->last_activity = time(NULL);
00550 
00551             ioctl(fd->fd, FIONREAD, &nbytes);
00552             if(nbytes == 0) {
00553                 sx_kill(sess->s);
00554                 return 0;
00555             }
00556 
00557             return sx_can_read(sess->s);
00558 
00559         case action_WRITE:
00560             log_debug(ZONE, "write action on fd %d", fd->fd);
00561 
00562             return sx_can_write(sess->s);
00563 
00564         case action_CLOSE:
00565             log_debug(ZONE, "close action on fd %d", fd->fd);
00566 
00567             log_write(sess->c2s->log, LOG_NOTICE, "[%d] [%s, port=%d] disconnect jid=%s, packets: %i", sess->fd->fd, sess->ip, sess->port, ((sess->resources)?((char*) jid_full(sess->resources->jid)):"unbound"), sess->packet_count);
00568 
00569             /* tell the sm to close their session */
00570             if(sess->active)
00571                 for(bres = sess->resources; bres != NULL; bres = bres->next)
00572                     sm_end(sess, bres);
00573 
00574             jqueue_push(sess->c2s->dead, (void *) sess->s, 0);
00575 
00576             xhash_zap(sess->c2s->sessions, sess->skey);
00577 
00578             jqueue_push(sess->c2s->dead_sess, (void *) sess, 0);
00579 
00580             break;
00581 
00582         case action_ACCEPT:
00583             log_debug(ZONE, "accept action on fd %d", fd->fd);
00584 
00585             getpeername(fd->fd, (struct sockaddr *) &sa, &namelen);
00586             port = j_inet_getport(&sa);
00587 
00588             log_write(c2s->log, LOG_NOTICE, "[%d] [%s, port=%d] connect", fd->fd, (char *) data, port);
00589 
00590             if(_c2s_client_accept_check(c2s, fd, (char *) data) != 0)
00591                 return 1;
00592 
00593             sess = (sess_t) calloc(1, sizeof(struct sess_st));
00594 
00595             sess->c2s = c2s;
00596 
00597             sess->fd = fd;
00598 
00599             sess->ip = strdup((char *) data);
00600             sess->port = port;
00601 
00602             /* they did something */
00603             sess->last_activity = time(NULL);
00604 
00605             sess->s = sx_new(c2s->sx_env, fd->fd, _c2s_client_sx_callback, (void *) sess);
00606             mio_app(m, fd, _c2s_client_mio_callback, (void *) sess);
00607 
00608             if(c2s->stanza_size_limit != 0)
00609                 sess->s->rbytesmax = c2s->stanza_size_limit;
00610 
00611             if(c2s->byte_rate_total != 0)
00612                 sess->rate = rate_new(c2s->byte_rate_total, c2s->byte_rate_seconds, c2s->byte_rate_wait);
00613 
00614             if(c2s->stanza_rate_total != 0)
00615                 sess->stanza_rate = rate_new(c2s->stanza_rate_total, c2s->stanza_rate_seconds, c2s->stanza_rate_wait);
00616 
00617             /* give IP to SX */
00618             sess->s->ip = sess->ip;
00619             sess->s->port = sess->port;
00620 
00621             /* find out which port this is */
00622             getsockname(fd->fd, (struct sockaddr *) &sa, &namelen);
00623             port = j_inet_getport(&sa);
00624 
00625             /* remember it */
00626             sprintf(sess->skey, "%d", fd->fd);
00627             xhash_put(c2s->sessions, sess->skey, (void *) sess);
00628 
00629             flags = SX_SASL_OFFER;
00630 #ifdef HAVE_SSL
00631             /* go ssl wrappermode if they're on the ssl port */
00632             if(port == c2s->local_ssl_port)
00633                 flags |= SX_SSL_WRAPPER;
00634 #endif
00635 #ifdef HAVE_LIBZ
00636             if(c2s->compression)
00637                 flags |= SX_COMPRESS_OFFER;
00638 #endif
00639             sx_server_init(sess->s, flags);
00640 
00641             break;
00642     }
00643 
00644     return 0;
00645 }
00646 
00647 static void _c2s_component_presence(c2s_t c2s, nad_t nad) {
00648     int attr;
00649     char from[1024];
00650     sess_t sess;
00651     union xhashv xhv;
00652 
00653     if((attr = nad_find_attr(nad, 0, -1, "from", NULL)) < 0) {
00654         nad_free(nad);
00655         return;
00656     }
00657 
00658     strncpy(from, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
00659     from[NAD_AVAL_L(nad, attr)] = '\0';
00660 
00661     if(nad_find_attr(nad, 0, -1, "type", NULL) < 0) {
00662         log_debug(ZONE, "component available from '%s'", from);
00663 
00664         log_debug(ZONE, "sm for serviced domain '%s' online", from);
00665 
00666         xhash_put(c2s->sm_avail, pstrdup(xhash_pool(c2s->sm_avail), from), (void *) 1);
00667 
00668         nad_free(nad);
00669         return;
00670     }
00671 
00672     if(nad_find_attr(nad, 0, -1, "type", "unavailable") < 0) {
00673         nad_free(nad);
00674         return;
00675     }
00676 
00677     log_debug(ZONE, "component unavailable from '%s'", from);
00678 
00679     if(xhash_get(c2s->sm_avail, from) != NULL) {
00680         log_debug(ZONE, "sm for serviced domain '%s' offline", from);
00681 
00682         if(xhash_iter_first(c2s->sessions))
00683             do {
00684                 xhv.sess_val = &sess;
00685                 xhash_iter_get(c2s->sessions, NULL, NULL, xhv.val);
00686 
00687                 if(sess->resources != NULL && strcmp(sess->resources->jid->domain, from) == 0) {
00688                     log_debug(ZONE, "killing session %s", jid_user(sess->resources->jid));
00689 
00690                     sess->active = 0;
00691                     if(sess->s) sx_close(sess->s);
00692                 }
00693             } while(xhash_iter_next(c2s->sessions));
00694 
00695         xhash_zap(c2s->sm_avail, from);
00696     }
00697 }
00698 
00699 int c2s_router_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) {
00700     c2s_t c2s = (c2s_t) arg;
00701     sx_buf_t buf = (sx_buf_t) data;
00702     sx_error_t *sxe;
00703     nad_t nad;
00704     int len, elem, from, c2sid, smid, action, id, ns, attr, scan, replaced;
00705     char skey[44];
00706     sess_t sess;
00707     bres_t bres, ires;
00708 
00709     switch(e) {
00710         case event_WANT_READ:
00711             log_debug(ZONE, "want read");
00712             mio_read(c2s->mio, c2s->fd);
00713             break;
00714 
00715         case event_WANT_WRITE:
00716             log_debug(ZONE, "want write");
00717             mio_write(c2s->mio, c2s->fd);
00718             break;
00719 
00720         case event_READ:
00721             log_debug(ZONE, "reading from %d", c2s->fd->fd);
00722 
00723             /* do the read */
00724             len = recv(c2s->fd->fd, buf->data, buf->len, 0);
00725 
00726             if(len < 0) {
00727                 if(MIO_WOULDBLOCK) {
00728                     buf->len = 0;
00729                     return 0;
00730                 }
00731 
00732                 log_write(c2s->log, LOG_NOTICE, "[%d] [router] read error: %s (%d)", c2s->fd->fd, MIO_STRERROR(MIO_ERROR), MIO_ERROR);
00733 
00734                 sx_kill(s);
00735 
00736                 return -1;
00737             }
00738 
00739             else if(len == 0) {
00740                 /* they went away */
00741                 sx_kill(s);
00742 
00743                 return -1;
00744             }
00745 
00746             log_debug(ZONE, "read %d bytes", len);
00747 
00748             buf->len = len;
00749 
00750             return len;
00751 
00752         case event_WRITE:
00753             log_debug(ZONE, "writing to %d", c2s->fd->fd);
00754 
00755             len = send(c2s->fd->fd, buf->data, buf->len, 0);
00756             if(len >= 0) {
00757                 log_debug(ZONE, "%d bytes written", len);
00758                 return len;
00759             }
00760 
00761             if(MIO_WOULDBLOCK) 
00762                 return 0;
00763 
00764             log_write(c2s->log, LOG_NOTICE, "[%d] [router] write error: %s (%d)", c2s->fd->fd, MIO_STRERROR(MIO_ERROR), MIO_ERROR);
00765 
00766             sx_kill(s);
00767 
00768             return -1;
00769 
00770         case event_ERROR:
00771             sxe = (sx_error_t *) data;
00772             log_write(c2s->log, LOG_NOTICE, "error from router: %s (%s)", sxe->generic, sxe->specific);
00773 
00774             if(sxe->code == SX_ERR_AUTH)
00775                 sx_close(s);
00776 
00777             break;
00778 
00779         case event_STREAM:
00780             break;
00781 
00782         case event_OPEN:
00783             log_write(c2s->log, LOG_NOTICE, "connection to router established");
00784 
00785             /* set connection attempts counter */
00786             c2s->retry_left = c2s->retry_lost;
00787 
00788             nad = nad_new();
00789             ns = nad_add_namespace(nad, uri_COMPONENT, NULL);
00790             nad_append_elem(nad, ns, "bind", 0);
00791             nad_append_attr(nad, -1, "name", c2s->id);
00792 
00793             log_debug(ZONE, "requesting component bind for '%s'", c2s->id);
00794 
00795             sx_nad_write(c2s->router, nad);
00796 
00797             return 0;
00798 
00799         case event_PACKET:
00800             nad = (nad_t) data;
00801 
00802             /* drop unqualified packets */
00803             if(NAD_ENS(nad, 0) < 0) {
00804                 nad_free(nad);
00805                 return 0;
00806             }
00807 
00808             /* watch for the features packet */
00809             if(s->state == state_STREAM) {
00810                 if(NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_STREAMS) || strncmp(uri_STREAMS, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_STREAMS)) != 0 || NAD_ENAME_L(nad, 0) != 8 || strncmp("features", NAD_ENAME(nad, 0), 8) != 0) {
00811                     log_debug(ZONE, "got a non-features packet on an unauth'd stream, dropping");
00812                     nad_free(nad);
00813                     return 0;
00814                 }
00815 
00816 #ifdef HAVE_SSL
00817                 /* starttls if we can */
00818                 if(c2s->sx_ssl != NULL && c2s->router_pemfile != NULL && s->ssf == 0) {
00819                     ns = nad_find_scoped_namespace(nad, uri_TLS, NULL);
00820                     if(ns >= 0) {
00821                         elem = nad_find_elem(nad, 0, ns, "starttls", 1);
00822                         if(elem >= 0) {
00823                             if(sx_ssl_client_starttls(c2s->sx_ssl, s, c2s->router_pemfile) == 0) {
00824                                 nad_free(nad);
00825                                 return 0;
00826                             }
00827                             log_write(c2s->log, LOG_ERR, "unable to establish encrypted session with router");
00828                         }
00829                     }
00830                 }
00831 #endif
00832 
00833                 /* !!! pull the list of mechanisms, and choose the best one.
00834                  *     if there isn't an appropriate one, error and bail */
00835 
00836                 /* authenticate */
00837                 sx_sasl_auth(c2s->sx_sasl, s, "jabberd-router", "DIGEST-MD5", c2s->router_user, c2s->router_pass);
00838 
00839                 nad_free(nad);
00840                 return 0;
00841             }
00842 
00843             /* watch for the bind response */
00844             if(s->state == state_OPEN && !c2s->online) {
00845                 if(NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_COMPONENT) || strncmp(uri_COMPONENT, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_COMPONENT)) != 0 || NAD_ENAME_L(nad, 0) != 4 || strncmp("bind", NAD_ENAME(nad, 0), 4) != 0) {
00846                     log_debug(ZONE, "got a packet from router, but we're not online, dropping");
00847                     nad_free(nad);
00848                     return 0;
00849                 }
00850 
00851                 /* catch errors */
00852                 attr = nad_find_attr(nad, 0, -1, "error", NULL);
00853                 if(attr >= 0) {
00854                     log_write(c2s->log, LOG_ERR, "router refused bind request (%.*s)", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
00855                     exit(1);
00856                 }
00857 
00858                 log_debug(ZONE, "coming online");
00859 
00860                 /* if we're coming online for the first time, setup listening sockets */
00861 #ifdef HAVE_SSL
00862                 if(c2s->server_fd == 0 && c2s->server_ssl_fd == 0) {
00863 #else
00864                 if(c2s->server_fd == 0) {
00865 #endif
00866                     if(c2s->local_port != 0) {
00867                         c2s->server_fd = mio_listen(c2s->mio, c2s->local_port, c2s->local_ip, _c2s_client_mio_callback, (void *) c2s);
00868                         if(c2s->server_fd == NULL)
00869                             log_write(c2s->log, LOG_ERR, "[%s, port=%d] failed to listen", c2s->local_ip, c2s->local_port);
00870                         else
00871                             log_write(c2s->log, LOG_NOTICE, "[%s, port=%d] listening for connections", c2s->local_ip, c2s->local_port);
00872                     } else
00873                         c2s->server_fd = NULL;
00874 
00875 #ifdef HAVE_SSL
00876                     if(c2s->local_ssl_port != 0 && c2s->local_pemfile != NULL) {
00877                         c2s->server_ssl_fd = mio_listen(c2s->mio, c2s->local_ssl_port, c2s->local_ip, _c2s_client_mio_callback, (void *) c2s);
00878                         if(c2s->server_ssl_fd == NULL)
00879                             log_write(c2s->log, LOG_ERR, "[%s, port=%d] failed to listen", c2s->local_ip, c2s->local_ssl_port);
00880                         else
00881                             log_write(c2s->log, LOG_NOTICE, "[%s, port=%d] listening for SSL connections", c2s->local_ip, c2s->local_ssl_port);
00882                     } else
00883                         c2s->server_ssl_fd = NULL;
00884 #endif
00885                 }
00886 
00887 #ifdef HAVE_SSL
00888                 if(c2s->server_fd == NULL && c2s->server_ssl_fd == NULL && c2s->pbx_pipe == NULL) {
00889                     log_write(c2s->log, LOG_ERR, "both normal and SSL ports are disabled, nothing to do!");
00890 #else
00891                 if(c2s->server_fd == NULL && c2s->pbx_pipe == NULL) {
00892                     log_write(c2s->log, LOG_ERR, "server port is disabled, nothing to do!");
00893 #endif
00894                     exit(1);
00895                 }
00896 
00897                 /* open PBX integration FIFO */
00898                 if(c2s->pbx_pipe != NULL)
00899                     c2s_pbx_init(c2s);
00900 
00901                 /* we're online */
00902                 c2s->online = c2s->started = 1;
00903                 log_write(c2s->log, LOG_NOTICE, "ready for connections", c2s->id);
00904 
00905                 nad_free(nad);
00906                 return 0;
00907             }
00908 
00909             /* need component packets */
00910             if(NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_COMPONENT) || strncmp(uri_COMPONENT, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_COMPONENT)) != 0) {
00911                 log_debug(ZONE, "wanted component packet, dropping");
00912                 nad_free(nad);
00913                 return 0;
00914             }
00915 
00916             /* component presence */
00917             if(NAD_ENAME_L(nad, 0) == 8 && strncmp("presence", NAD_ENAME(nad, 0), 8) == 0) {
00918                 _c2s_component_presence(c2s, nad);
00919                 return 0;
00920             }
00921 
00922             /* we want route */
00923             if(NAD_ENAME_L(nad, 0) != 5 || strncmp("route", NAD_ENAME(nad, 0), 5) != 0) { 
00924                 log_debug(ZONE, "wanted {component}route, dropping");
00925                 nad_free(nad);
00926                 return 0;
00927             }
00928 
00929             /* only handle unicasts */
00930             if(nad_find_attr(nad, 0, -1, "type", NULL) >= 0) {
00931                 log_debug(ZONE, "non-unicast packet, dropping");
00932                 nad_free(nad);
00933                 return 0;
00934             }
00935 
00936             /* need some payload */
00937             if(nad->ecur == 1) {
00938                 log_debug(ZONE, "no route payload, dropping");
00939                 nad_free(nad);
00940                 return 0;
00941             }
00942 
00943             ns = nad_find_namespace(nad, 1, uri_SESSION, NULL);
00944             if(ns < 0) {
00945                 log_debug(ZONE, "not a c2s packet, dropping");
00946                 nad_free(nad);
00947                 return 0;
00948             }
00949 
00950             /* figure out the session */
00951             c2sid = nad_find_attr(nad, 1, ns, "c2s", NULL);
00952             if(c2sid < 0) {
00953                 log_debug(ZONE, "no c2s id on payload, dropping");
00954                 nad_free(nad);
00955                 return 0;
00956             }
00957             snprintf(skey, sizeof(skey), "%.*s", NAD_AVAL_L(nad, c2sid), NAD_AVAL(nad, c2sid));
00958 
00959             /* find the session, quietly drop if we don't have it */
00960             sess = xhash_get(c2s->sessions, skey);
00961             if(sess == NULL) {
00962                 /* if we get this, the SM probably thinks the session is still active
00963                  * so we need to tell SM to free it up */
00964                 log_debug(ZONE, "no session for %s", skey);
00965 
00966                 /* check if it's a started action; otherwise we could end up in an infinite loop
00967                  * trying to tell SM to close in response to errors */
00968                 action = nad_find_attr(nad, 1, -1, "action", NULL);
00969                 if(action >= 0 && NAD_AVAL_L(nad, action) == 7 && strncmp("started", NAD_AVAL(nad, action), 7) == 0) {
00970                     int target;
00971                     bres_t tres;
00972                     sess_t tsess;
00973 
00974                     log_write(c2s->log, LOG_NOTICE, "session %s does not exist; telling sm to close", skey);
00975 
00976                     /* we don't have a session and we don't have a resource; we need to forge them both
00977                      * to get SM to close stuff */
00978                     target = nad_find_attr(nad, 1, -1, "target", NULL);
00979                     smid = nad_find_attr(nad, 1, ns, "sm", NULL);
00980                     if(target < 0 || smid < 0) {
00981                         char *buf;
00982                         int len;
00983                         nad_print(nad, 0, &buf, &len);
00984                         log_write(c2s->log, LOG_NOTICE, "sm sent an invalid start packet: %.*s", len, buf );
00985                         nad_free(nad);
00986                         return 0;
00987                     }
00988 
00989                     /* build temporary resource to close session for */
00990                     tres = NULL;
00991                     tres = (bres_t) calloc(1, sizeof(struct bres_st));
00992                     tres->jid = jid_new(NAD_AVAL(nad, target), NAD_AVAL_L(nad, target));
00993 
00994                     strncpy(tres->c2s_id, skey, sizeof(tres->c2s_id));
00995                     snprintf(tres->sm_id, sizeof(tres->sm_id), "%.*s", NAD_AVAL_L(nad, smid), NAD_AVAL(nad, smid));
00996 
00997                     /* make a temporary session */
00998                     tsess = (sess_t) calloc(1, sizeof(struct sess_st));
00999                     tsess->c2s = c2s;
01000                     tsess->result = nad_new();
01001                     strncpy(tsess->skey, skey, sizeof(tsess->skey));
01002 
01003                     /* end a session with the sm */
01004                     sm_end(tsess, tres);
01005 
01006                     /* free our temporary messes */
01007                     nad_free(tsess->result);
01008                     jid_free(tres->jid); //TODO will this crash?
01009                     free(tsess);
01010                     free(tres);
01011                 }
01012 
01013                 nad_free(nad);
01014                 return 0;
01015             }
01016 
01017             /* if they're pre-stream, then this is leftovers from a previous session */
01018             if(sess->s && sess->s->state < state_STREAM) {
01019                 log_debug(ZONE, "session %s is pre-stream", skey);
01020 
01021                 nad_free(nad);
01022                 return 0;
01023             }
01024 
01025             /* check the sm session id if they gave us one */
01026             smid = nad_find_attr(nad, 1, ns, "sm", NULL);
01027 
01028             /* get the action attribute */
01029             action = nad_find_attr(nad, 1, -1, "action", NULL);
01030 
01031             /* first user created packets - these are out of session */
01032             if(action >= 0 && NAD_AVAL_L(nad, action) == 7 && strncmp("created", NAD_AVAL(nad, action), 7) == 0) {
01033 
01034                 nad_free(nad);
01035 
01036                 /* return the result to the client */
01037                 sx_nad_write(sess->s, sess->result);
01038                 sess->result = NULL;
01039 
01040                 return 0;
01041             }
01042 
01043             /* route errors */
01044             if(nad_find_attr(nad, 0, -1, "error", NULL) >= 0) {
01045                 log_debug(ZONE, "routing error");
01046 
01047                 if(sess->s) {
01048                     sx_error(sess->s, stream_err_INTERNAL_SERVER_ERROR, "internal server error");
01049                     sx_close(sess->s);
01050                 }
01051 
01052                 nad_free(nad);
01053                 return 0;
01054             }
01055 
01056             /* all other packets need to contain an sm ID */
01057             if (smid < 0) {
01058                 log_debug(ZONE, "received packet from sm without an sm ID, dropping");
01059                 nad_free(nad);
01060                 return 0;
01061             }
01062 
01063             /* find resource that we got packet for */
01064             bres = NULL;
01065             if(smid >= 0)
01066                 for(bres = sess->resources; bres != NULL; bres = bres->next){
01067                     if(bres->sm_id[0] == '\0' || (strlen(bres->sm_id) == NAD_AVAL_L(nad, smid) && strncmp(bres->sm_id, NAD_AVAL(nad, smid), NAD_AVAL_L(nad, smid)) == 0))
01068                         break;
01069                 }
01070             if(bres == NULL) {
01071                 jid_t jid = NULL;
01072                 bres_t tres = NULL;
01073 
01074                 /* if it's a failure, just drop it */
01075                 if(nad_find_attr(nad, 1, ns, "failed", NULL) >= 0) {
01076                     nad_free(nad);
01077                     return 0;
01078                 }
01079 
01080                 /* build temporary resource to close session for */
01081                 tres = (bres_t) calloc(1, sizeof(struct bres_st));
01082                 if(sess->s) {
01083                     jid = jid_new(sess->s->auth_id, -1);
01084                     sprintf(tres->c2s_id, "%d", sess->s->tag);
01085                 }
01086                 else {
01087                     /* does not have SX - extract values from route packet */
01088                     int c2sid, target;
01089                     c2sid = nad_find_attr(nad, 1, ns, "c2s", NULL);
01090                     target = nad_find_attr(nad, 1, -1, "target", NULL);
01091                     if(c2sid < 0 || target < 0) {
01092                         log_debug(ZONE, "needed ids not found - c2sid:%d target:%d", c2sid, target);
01093                         nad_free(nad);
01094                         free(tres);
01095                         return 0;
01096                     }
01097                     jid = jid_new(NAD_AVAL(nad, target), NAD_AVAL_L(nad, target));
01098                     snprintf(tres->c2s_id, sizeof(tres->c2s_id), "%.*s", NAD_AVAL_L(nad, c2sid), NAD_AVAL(nad, c2sid));
01099                 }
01100                 tres->jid = jid;
01101                 snprintf(tres->sm_id, sizeof(tres->sm_id), "%.*s", NAD_AVAL_L(nad, smid), NAD_AVAL(nad, smid));
01102 
01103                 if(sess->resources) {
01104                     log_debug(ZONE, "expected packet from sm session %s, but got one from %.*s, ending sm session", sess->resources->sm_id, NAD_AVAL_L(nad, smid), NAD_AVAL(nad, smid));
01105                 } else {
01106                     log_debug(ZONE, "no resource bound yet, but got packet from sm session %.*s, ending sm session", NAD_AVAL_L(nad, smid), NAD_AVAL(nad, smid));
01107                 }
01108 
01109                 /* end a session with the sm */
01110                 sm_end(sess, tres);
01111 
01112                 /* finished with the nad */
01113                 nad_free(nad);
01114 
01115                 /* free temp objects */
01116                 jid_free(jid);
01117                 free(tres);
01118 
01119                 return 0;
01120             }
01121 
01122             /* session control packets */
01123             if(NAD_ENS(nad, 1) == ns && action >= 0) {
01124                 /* end responses */
01125 
01126                 /* !!! this "replaced" stuff is a hack - its really a subaction of "ended".
01127                  *     hurrah, another control protocol rewrite is needed :(
01128                  */
01129 
01130                 replaced = 0;
01131                 if(NAD_AVAL_L(nad, action) == 8 && strncmp("replaced", NAD_AVAL(nad, action), NAD_AVAL_L(nad, action)) == 0)
01132                     replaced = 1;
01133                 if(sess->active &&
01134                    (replaced || (NAD_AVAL_L(nad, action) == 5 && strncmp("ended", NAD_AVAL(nad, action), NAD_AVAL_L(nad, action)) == 0))) {
01135 
01136                     sess->bound -= 1;
01137                     /* no more resources bound? */
01138                     if(sess->bound < 1){
01139                         sess->active = 0;
01140 
01141                         if(sess->s) {
01142                             /* return the unbind result to the client */
01143                             if(sess->result != NULL) {
01144                                 sx_nad_write(sess->s, sess->result);
01145                                 sess->result = NULL;
01146                             }
01147 
01148                             if(replaced)
01149                                 sx_error(sess->s, stream_err_CONFLICT, NULL);
01150 
01151                             sx_close(sess->s);
01152 
01153                         } else {
01154                             // handle fake PBX sessions
01155                             if(sess->result != NULL) {
01156                                 nad_free(sess->result);
01157                                 sess->result = NULL;
01158                             }
01159                         }
01160 
01161                         nad_free(nad);
01162                         return 0;
01163                     }
01164 
01165                     /* else remove the bound resource */
01166                     if(bres == sess->resources) {
01167                         sess->resources = bres->next;
01168                     } else {
01169                         for(ires = sess->resources; ires != NULL; ires = ires->next)
01170                             if(ires->next == bres)
01171                                 break;
01172                         assert(ires != NULL);
01173                         ires->next = bres->next;
01174                     }
01175 
01176                     log_write(sess->c2s->log, LOG_NOTICE, "[%d] unbound: jid=%s", sess->s->tag, jid_full(bres->jid));
01177 
01178                     jid_free(bres->jid);
01179                     free(bres);
01180 
01181                     /* and return the unbind result to the client */
01182                     if(sess->result != NULL) {
01183                         sx_nad_write(sess->s, sess->result);
01184                         sess->result = NULL;
01185                     }
01186 
01187                     return 0;
01188                 }
01189 
01190                 id = nad_find_attr(nad, 1, -1, "id", NULL);
01191 
01192                 /* make sure the id matches */
01193                 if(id < 0 || bres->sm_request[0] == '\0' || strlen(bres->sm_request) != NAD_AVAL_L(nad, id) || strncmp(bres->sm_request, NAD_AVAL(nad, id), NAD_AVAL_L(nad, id)) != 0) {
01194                     if(id >= 0) {
01195                         log_debug(ZONE, "got a response with id %.*s, but we were expecting %s", NAD_AVAL_L(nad, id), NAD_AVAL(nad, id), bres->sm_request);
01196                     } else {
01197                         log_debug(ZONE, "got a response with no id, but we were expecting %s", bres->sm_request);
01198                     }
01199 
01200                     nad_free(nad);
01201                     return 0;
01202                 }
01203 
01204                 /* failed requests */
01205                 if(nad_find_attr(nad, 1, ns, "failed", NULL) >= 0) {
01206                     /* handled request */
01207                     bres->sm_request[0] = '\0';
01208 
01209                     /* we only care about failed start and create */
01210                     if((NAD_AVAL_L(nad, action) == 5 && strncmp("start", NAD_AVAL(nad, action), 5) == 0) ||
01211                        (NAD_AVAL_L(nad, action) == 6 && strncmp("create", NAD_AVAL(nad, action), 6) == 0)) {
01212 
01213                         /* create failed, so we need to remove them from authreg */
01214                         if(NAD_AVAL_L(nad, action) == 6 && c2s->ar->delete_user != NULL) {
01215                             if((c2s->ar->delete_user)(c2s->ar, bres->jid->node, sess->host->realm) != 0)
01216                                 log_write(c2s->log, LOG_NOTICE, "[%d] user creation failed, and unable to delete user credentials: user=%s, realm=%s", sess->s->tag, bres->jid->node, sess->host->realm);
01217                             else
01218                                 log_write(c2s->log, LOG_NOTICE, "[%d] user creation failed, so deleted user credentials: user=%s, realm=%s", sess->s->tag, bres->jid->node, sess->host->realm);
01219                         }
01220 
01221                         /* error the result and return it to the client */
01222                         sx_nad_write(sess->s, stanza_error(sess->result, 0, stanza_err_INTERNAL_SERVER_ERROR));
01223                         sess->result = NULL;
01224 
01225                         /* remove the bound resource */
01226                         if(bres == sess->resources) {
01227                             sess->resources = bres->next;
01228                         } else {
01229                             for(ires = sess->resources; ires != NULL; ires = ires->next)
01230                                 if(ires->next == bres)
01231                                     break;
01232                             assert(ires != NULL);
01233                             ires->next = bres->next;
01234                         }
01235 
01236                         jid_free(bres->jid);
01237                         free(bres);
01238 
01239                         nad_free(nad);
01240                         return 0;
01241                     }
01242 
01243                     log_debug(ZONE, "weird, got a failed session response, with a matching id, but the action is bogus *shrug*");
01244 
01245                     nad_free(nad);
01246                     return 0;
01247                 }
01248 
01249                 /* session started */
01250                 if(NAD_AVAL_L(nad, action) == 7 && strncmp("started", NAD_AVAL(nad, action), 7) == 0) {
01251                     /* handled request */
01252                     bres->sm_request[0] = '\0';
01253 
01254                     /* copy the sm id */
01255                     if(smid >= 0)
01256                         snprintf(bres->sm_id, sizeof(bres->sm_id), "%.*s", NAD_AVAL_L(nad, smid), NAD_AVAL(nad, smid));
01257 
01258                     /* and remember the SM that services us */
01259                     from = nad_find_attr(nad, 0, -1, "from", NULL);
01260                     sess->smcomp = malloc(NAD_AVAL_L(nad, from) + 1);
01261                     snprintf(sess->smcomp, NAD_AVAL_L(nad, from) + 1, "%.*s", NAD_AVAL_L(nad, from), NAD_AVAL(nad, from));
01262 
01263                     nad_free(nad);
01264 
01265                     /* bring them online, old-skool */
01266                     if(!sess->sasl_authd && sess->s) {
01267                         sx_auth(sess->s, "traditional", jid_full(bres->jid));
01268                         return 0;
01269                     }
01270 
01271                     if(sess->result) {
01272                         /* return the auth result to the client */
01273                         if(sess->s) sx_nad_write(sess->s, sess->result);
01274                         /* or follow-up the session creation with cached presence packet */
01275                         else sm_packet(sess, bres, sess->result);
01276                     }
01277                     sess->result = NULL;
01278 
01279                     /* we're good to go */
01280                     sess->active = 1;
01281 
01282                     return 0;
01283                 }
01284 
01285                 /* handled request */
01286                 bres->sm_request[0] = '\0';
01287 
01288                 log_debug(ZONE, "unknown action %.*s", NAD_AVAL_L(nad, id), NAD_AVAL(nad, id));
01289 
01290                 nad_free(nad);
01291 
01292                 return 0;
01293             }
01294 
01295             /* client packets */
01296             if(NAD_NURI_L(nad, NAD_ENS(nad, 1)) == strlen(uri_CLIENT) && strncmp(uri_CLIENT, NAD_NURI(nad, NAD_ENS(nad, 1)), strlen(uri_CLIENT)) == 0) {
01297                 if(!sess->active || !sess->s) {
01298                     /* its a strange world .. */
01299                     log_debug(ZONE, "Got packet for %s - dropping", !sess->s ? "session without stream (PBX pipe session?)" : "inactive session");
01300                     nad_free(nad);
01301                     return 0;
01302                 }
01303 
01304                 /* sm is bouncing something */
01305                 if(nad_find_attr(nad, 1, ns, "failed", NULL) >= 0) {
01306                     if(s) {
01307                         /* there's really no graceful way to handle this */
01308                         sx_error(s, stream_err_INTERNAL_SERVER_ERROR, "session manager failed control action");
01309                         sx_close(s);
01310                     }
01311 
01312                     nad_free(nad);
01313                     return 0;
01314                 }
01315 
01316                 /* we're counting packets */
01317                 sess->packet_count++;
01318                 sess->c2s->packet_count++;
01319 
01320                 /* remove sm specifics */
01321                 nad_set_attr(nad, 1, ns, "c2s", NULL, 0);
01322                 nad_set_attr(nad, 1, ns, "sm", NULL, 0);
01323 
01324                 /* forget about the internal namespace too */
01325                 if(nad->elems[1].ns == ns)
01326                     nad->elems[1].ns = nad->nss[ns].next;
01327 
01328                 else {
01329                     for(scan = nad->elems[1].ns; nad->nss[scan].next != -1 && nad->nss[scan].next != ns; scan = nad->nss[scan].next);
01330 
01331                     /* got it */
01332                     if(nad->nss[scan].next != -1)
01333                         nad->nss[scan].next = nad->nss[ns].next;
01334                 }
01335 
01336                 sx_nad_write_elem(sess->s, nad, 1);
01337 
01338                 return 0;
01339             }
01340 
01341             /* its something else */
01342             log_debug(ZONE, "unknown packet, dropping");
01343 
01344             nad_free(nad);
01345             return 0;
01346 
01347         case event_CLOSED:
01348             mio_close(c2s->mio, c2s->fd);
01349             c2s->fd = NULL;
01350             return -1;
01351     }
01352 
01353     return 0;
01354 }
01355 
01356 int c2s_router_mio_callback(mio_t m, mio_action_t a, mio_fd_t fd, void *data, void *arg) {
01357     c2s_t c2s = (c2s_t) arg;
01358     int nbytes;
01359 
01360     switch(a) {
01361         case action_READ:
01362             log_debug(ZONE, "read action on fd %d", fd->fd);
01363 
01364             ioctl(fd->fd, FIONREAD, &nbytes);
01365             if(nbytes == 0) {
01366                 sx_kill(c2s->router);
01367                 return 0;
01368             }
01369 
01370             return sx_can_read(c2s->router);
01371 
01372         case action_WRITE:
01373             log_debug(ZONE, "write action on fd %d", fd->fd);
01374             return sx_can_write(c2s->router);
01375 
01376         case action_CLOSE:
01377             log_debug(ZONE, "close action on fd %d", fd->fd);
01378             log_write(c2s->log, LOG_NOTICE, "connection to router closed");
01379 
01380             c2s_lost_router = 1;
01381 
01382             /* we're offline */
01383             c2s->online = 0;
01384 
01385             break;
01386 
01387         case action_ACCEPT:
01388             break;
01389     }
01390 
01391     return 0;
01392 }