示例#1
0
文件: io.c 项目: marising/jabberd2
/** send a new nad out */
int _sx_nad_write(sx_t s, nad_t nad, int elem) {
    const char *out;
    int len;

    /* silently drop it if we're closing or closed */
    if(s->state >= state_CLOSING) {
        log_debug(ZONE, "stream closed, dropping outgoing packet");
        nad_free(nad);
        return 1;
    }

    /* run it through the plugins */
    if(_sx_chain_nad_write(s, nad, elem) == 0)
        return 1;

    /* serialise it */
    nad_print(nad, elem, &out, &len);

    _sx_debug(ZONE, "queueing for write: %.*s", len, out);

    /* ready to go */
    jqueue_push(s->wbufq, _sx_buffer_new(out, len, NULL, NULL), 0);

    nad_free(nad);

    /* things to write */
    s->want_write = 1;

    return 0;
}
示例#2
0
文件: router.c 项目: 6wei/jabberd2
static void _router_process_route(component_t comp, nad_t nad) {
    int atype, ato, afrom;
    unsigned int dest;
    struct jid_st sto, sfrom;
    jid_static_buf sto_buf, sfrom_buf;
    jid_t to = NULL, from = NULL;
    routes_t targets;
    component_t target;
    union xhashv xhv;

    /* init static jid */
    jid_static(&sto,&sto_buf);
    jid_static(&sfrom,&sfrom_buf);

    if(nad_find_attr(nad, 0, -1, "error", NULL) >= 0) {
        log_debug(ZONE, "dropping error packet, trying to avoid loops");
        nad_free(nad);
        return;
    }

    atype = nad_find_attr(nad, 0, -1, "type", NULL);
    ato = nad_find_attr(nad, 0, -1, "to", NULL);
    afrom = nad_find_attr(nad, 0, -1, "from", NULL);

    if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato));
    if(afrom >= 0) from = jid_reset(&sfrom, NAD_AVAL(nad, afrom), NAD_AVAL_L(nad, afrom));

    /* unicast */
    if(atype < 0) {
        if(to == NULL || from == NULL) {
            log_debug(ZONE, "unicast route with missing or invalid to or from, bouncing");
            nad_set_attr(nad, 0, -1, "error", "400", 3);
            _router_comp_write(comp, nad);
            return;
        }
        
        log_debug(ZONE, "unicast route from %s to %s", from->domain, to->domain);

        /* check the from */
        if(xhash_get(comp->routes, from->domain) == NULL) {
            log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to send a packet from '%s', but that name is not bound to this component", comp->ip, comp->port, from->domain);
            nad_set_attr(nad, 0, -1, "error", "401", 3);
            _router_comp_write(comp, nad);
            return;
        }

        /* filter it */
        if(comp->r->filter != NULL) {
            int ret = filter_packet(comp->r, nad);
            if(ret == stanza_err_REDIRECT) {
                ato = nad_find_attr(nad, 0, -1, "to", NULL);
                if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato));
            }
            else if(ret > 0) {
                log_debug(ZONE, "packet filtered out: %s (%s)", _stanza_errors[ret - stanza_err_BAD_REQUEST].name, _stanza_errors[ret - stanza_err_BAD_REQUEST].code);
                nad_set_attr(nad, 0, -1, "error", _stanza_errors[ret - stanza_err_BAD_REQUEST].code, 3);
                _router_comp_write(comp, nad);
                return;
            }
        }

        /* find a target */
        targets = xhash_get(comp->r->routes, to->domain);
        if(targets == NULL) {
            if(comp->r->default_route != NULL && strcmp(from->domain, comp->r->default_route) == 0) {
                log_debug(ZONE, "%s is unbound, bouncing", from->domain);
                nad_set_attr(nad, 0, -1, "error", "404", 3);
                _router_comp_write(comp, nad);
                return;
            }
            targets = xhash_get(comp->r->routes, comp->r->default_route);
        }

        if(targets == NULL) {
            log_debug(ZONE, "%s is unbound, and no default route, bouncing", to->domain);
            nad_set_attr(nad, 0, -1, "error", "404", 3);
            _router_comp_write(comp, nad);
            return;
        }

        /* copy to any log sinks */
        if(xhash_count(comp->r->log_sinks) > 0)
            xhash_walk(comp->r->log_sinks, _router_route_log_sink, (void *) nad);

        /* get route candidate */
        if(targets->ncomp == 1) {
            dest = 0;
        }
        else {
            switch(targets->rtype) {
                case route_MULTI_TO:
                    ato = nad_find_attr(nad, 1, -1, "to", NULL);
                    if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato));
                    else {
                        ato = nad_find_attr(nad, 1, -1, "target", NULL);
                        if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato));
                        else {
                            const char *out; int len;
                            nad_print(nad, 0, &out, &len);
                            log_write(comp->r->log, LOG_ERR, "Cannot get destination for multiple route: %.*s", len, out);
                        }
                    }
                    break;
                case route_MULTI_FROM:
                    ato = nad_find_attr(nad, 1, -1, "from", NULL);
                    if(ato >= 0) to = jid_reset(&sto, NAD_AVAL(nad, ato), NAD_AVAL_L(nad, ato));
                    else {
                        const char *out; int len;
                        nad_print(nad, 0, &out, &len);
                        log_write(comp->r->log, LOG_ERR, "Cannot get source for multiple route: %.*s", len, out);
                    }
                    break;
                default:
                    log_write(comp->r->log, LOG_ERR, "Multiple components bound to single component route '%s'", targets->name);
                    /* simulate no 'to' info in this case */
            }
            if(to->node == NULL || strlen(to->node) == 0) {
                /* no node in destination JID - going random */
                dest = rand();
                log_debug(ZONE, "randomized to %u %% %d = %d", dest, targets->ncomp, dest % targets->ncomp);
            }
            else {
                /* use JID hash */
                unsigned char hashval[20];
                unsigned int *val;
                int i;
                
                shahash_raw(jid_user(to), hashval);
                
                val = (unsigned int *) hashval;
                dest = *val;
                for(i=1; i < 20 / (sizeof(unsigned int)/sizeof(unsigned char)); i++, val++) {
                    dest ^= *val;
                }
                dest >>= 2;

                log_debug(ZONE, "JID %s hashed to %u %% %d = %d", jid_user(to), dest, targets->ncomp, dest % targets->ncomp);

                /* jid_user() calls jid_expand() which may allocate some memory in _user and _full */
                if (to->_user != NULL )
                    free(to->_user);
                if (to->_full != NULL )
                    free(to->_full);
            }
            dest = dest % targets->ncomp;
        }

        target = targets->comp[dest];

        /* push it out */
        log_debug(ZONE, "writing route for '%s'*%u to %s, port %d", to->domain, dest+1, target->ip, target->port);

        /* if logging enabled, log messages that match our criteria */
        if (comp->r->message_logging_enabled && comp->r->message_logging_file != NULL) {
            int attr_msg_to;
            int attr_msg_from;
            int attr_route_to;
            int attr_route_from;
            jid_t jid_msg_from = NULL;
            jid_t jid_msg_to = NULL;
            jid_t jid_route_from = NULL;
            jid_t jid_route_to = NULL;

            if ((NAD_ENAME_L(nad, 1) == 7 && strncmp("message", NAD_ENAME(nad, 1), 7) == 0) &&		// has a "message" element 
                ((attr_route_from = nad_find_attr(nad, 0, -1, "from", NULL)) >= 0) &&
                ((attr_route_to = nad_find_attr(nad, 0, -1, "to", NULL)) >= 0) &&
                ((strncmp(NAD_AVAL(nad, attr_route_to), "c2s", 3)) != 0) &&							// ignore messages to "c2s" or we'd have dups
                ((jid_route_from = jid_new(NAD_AVAL(nad, attr_route_from), NAD_AVAL_L(nad, attr_route_from))) != NULL) &&	// has valid JID source in route
                ((jid_route_to = jid_new(NAD_AVAL(nad, attr_route_to), NAD_AVAL_L(nad, attr_route_to))) != NULL) &&		// has valid JID destination in route
                ((attr_msg_from = nad_find_attr(nad, 1, -1, "from", NULL)) >= 0) &&
                ((attr_msg_to = nad_find_attr(nad, 1, -1, "to", NULL)) >= 0) &&
                ((jid_msg_from = jid_new(NAD_AVAL(nad, attr_msg_from), NAD_AVAL_L(nad, attr_msg_from))) != NULL) &&	// has valid JID source in message 
                ((jid_msg_to = jid_new(NAD_AVAL(nad, attr_msg_to), NAD_AVAL_L(nad, attr_msg_to))) != NULL))			// has valid JID dest in message
            {
                message_log(nad, comp->r, jid_full(jid_msg_from), jid_full(jid_msg_to));
            }
            if (jid_msg_from != NULL)
                jid_free(jid_msg_from);
            if (jid_msg_to != NULL)
                jid_free(jid_msg_to);
            if (jid_route_from != NULL)
                jid_free(jid_route_from);
            if (jid_route_to != NULL)
                jid_free(jid_route_to);
        }

        _router_comp_write(target, nad);

        return;
    }

    /* broadcast */
    if(NAD_AVAL_L(nad, atype) == 9 && strncmp("broadcast", NAD_AVAL(nad, atype), 9) == 0) {
        if(from == NULL) {
            log_debug(ZONE, "broadcast route with missing or invalid from, bouncing");
            nad_set_attr(nad, 0, -1, "error", "400", 3);
            _router_comp_write(comp, nad);
            return;
        }
        
        log_debug(ZONE, "broadcast route from %s", from->domain);

        /* check the from */
        if(xhash_get(comp->routes, from->domain) == NULL) {
            log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to send a packet from '%s', but that name is not bound to this component", comp->ip, comp->port, from->domain);
            nad_set_attr(nad, 0, -1, "error", "401", 3);
            _router_comp_write(comp, nad);
            return;
        }

        /* loop the components and distribute */
        if(xhash_iter_first(comp->r->components))
            do {
                xhv.comp_val = &target;
                xhash_iter_get(comp->r->components, NULL, NULL, xhv.val);

                if(target != comp) {
                    log_debug(ZONE, "writing broadcast to %s, port %d", target->ip, target->port);

                    _router_comp_write(target, nad_copy(nad));
                }
            } while(xhash_iter_next(comp->r->components));

        nad_free(nad);

        return;
    }

    log_debug(ZONE, "unknown route type '%.*s', dropping", NAD_AVAL_L(nad, atype), NAD_AVAL(nad, atype));

    nad_free(nad);
}
示例#3
0
文件: io.c 项目: marising/jabberd2
/** handler for read data */
void _sx_process_read(sx_t s, sx_buf_t buf) {
    sx_error_t sxe;
    nad_t nad;
    char *errstring;
    int i;
    int ns, elem;

    /* Note that buf->len can validly be 0 here, if we got data from
       the socket but the plugin didn't return anything to us (e.g. a
       SSL packet was split across a tcp segment boundary) */

    /* count bytes parsed */
    s->pbytes += buf->len;

    /* parse it */
    if(XML_Parse(s->expat, buf->data, buf->len, 0) == 0) {
        /* only report error we haven't already */
        if(!s->fail) {
            /* parse error */
            errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat));

            _sx_debug(ZONE, "XML parse error: %s, character %d: %.*s",
                      errstring, XML_GetCurrentByteIndex(s->expat) - s->tbytes, buf->len, buf->data);
            _sx_gen_error(sxe, SX_ERR_XML_PARSE, "XML parse error", errstring);
            _sx_event(s, event_ERROR, (void *) &sxe);

            _sx_error(s, stream_err_XML_NOT_WELL_FORMED, errstring);
            _sx_close(s);

            _sx_buffer_free(buf);

            return;
        }

        /* !!! is this the right thing to do? we should probably set
         *     s->fail and let the code further down handle it. */
        _sx_buffer_free(buf);

        return;
    }

    /* check if the stanza size limit is exceeded (it wasn't reset by parser) */
    if(s->rbytesmax && s->pbytes > s->rbytesmax) {
        /* parse error */
        _sx_debug(ZONE, "maximum stanza size (%d) exceeded by reading %d bytes", s->rbytesmax, s->pbytes);

        errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat));

        _sx_gen_error(sxe, SX_ERR_XML_PARSE, "stream read error", "Maximum stanza size exceeded");
        _sx_event(s, event_ERROR, (void *) &sxe);

        _sx_error(s, stream_err_POLICY_VIOLATION, errstring);
        _sx_close(s);

        _sx_buffer_free(buf);

        return;
    }

    /* count bytes processed */
    s->tbytes += buf->len;

    /* done with the buffer */
    _sx_buffer_free(buf);

    /* process completed nads */
    if(s->state >= state_STREAM)
        while((nad = jqueue_pull(s->rnadq)) != NULL) {
            int plugin_error;
#ifdef SX_DEBUG
            const char *out; int len;
            nad_print(nad, 0, &out, &len);
            _sx_debug(ZONE, "completed nad: %.*s", len, out);
#endif

            /* check for errors */
            if(NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_STREAMS) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_STREAMS, strlen(uri_STREAMS)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "error", 5) == 0) {

                errstring = NULL;

                /* get text error description if available - XMPP 4.7.2 */
                if((ns = nad_find_scoped_namespace(nad, uri_STREAM_ERR, NULL)) >= 0)
                    if((elem = nad_find_elem(nad, 0, ns, "text", 1)) >= 0)
                        if(NAD_CDATA_L(nad, elem) > 0) {
                            errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, elem) + 1));
                            sprintf(errstring, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
                        }

                /* if not available, look for legacy error text as in <stream:error>description</stream:error> */
                if (errstring == NULL && NAD_CDATA_L(nad, 0) > 0) {
                    errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, 0) + 1));
                    sprintf(errstring, "%.*s", NAD_CDATA_L(nad, 0), NAD_CDATA(nad, 0));
                }

                /* if not available, log the whole packet for debugging */
                if (errstring == NULL) {
                    const char *xml;
                    int xlen;

                    nad_print(nad, 0, &xml, &xlen);
                    errstring = (char *) malloc(sizeof(char) * (xlen + 1));
                    sprintf(errstring, "%.*s", xlen, xml);
                }

                if(s->state < state_CLOSING) {
                    _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", errstring);
                    _sx_event(s, event_ERROR, (void *) &sxe);
                    _sx_state(s, state_CLOSING);
                }

                free(errstring);

                nad_free(nad);

                break;
            }

            /* check for close */
            if ((s->flags & SX_WEBSOCKET_WRAPPER) && NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_XFRAMING) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_XFRAMING, strlen(uri_XFRAMING)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "close", 5) == 0) {
                _sx_debug(ZONE, "<close/> frame @ depth %d", s->depth);
                s->fail = 1;
                break;
            }

            /* run it by the plugins */
            if(_sx_chain_nad_read(s, nad) == 0)
                return;

            /* now let the plugins process the completed nad */
            plugin_error = 0;
            if(s->env != NULL)
                for(i = 0; i < s->env->nplugins; i++)
                    if(s->env->plugins[i]->process != NULL) {
                        int plugin_ret;
                        plugin_ret = (s->env->plugins[i]->process)(s, s->env->plugins[i], nad);
                        if(plugin_ret == 0) {
                            plugin_error ++;
                            break;
                        }
                    }

            /* hand it to the app */
            if ((plugin_error == 0) && (s->state < state_CLOSING))
                _sx_event(s, event_PACKET, (void *) nad);
        }

    /* something went wrong, bail */
    if(s->fail) {
        _sx_close(s);

        return;
    }

    /* stream was closed */
    if(s->depth < 0 && s->state < state_CLOSING) {
        /* close the stream if necessary */

        if(s->state >= state_STREAM_SENT) {
            if (s->flags & SX_WEBSOCKET_WRAPPER)
                jqueue_push(s->wbufq, _sx_buffer_new("<close xmlns='" uri_XFRAMING "' />", sizeof(uri_XFRAMING) + 17, NULL, NULL), 0);
            else
                jqueue_push(s->wbufq, _sx_buffer_new("</stream:stream>", 16, NULL, NULL), 0);
            s->want_write = 1;
        }

        _sx_state(s, state_CLOSING);

        return;
    }
}