Exemplo n.º 1
0
static int _sx_compress_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
    _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
    int ret;
    sx_error_t sxe;

    /* only bothering if they asked for wrappermode */
    if(!(s->flags & SX_COMPRESS_WRAPPER) || !s->compressed)
        return 1;

    _sx_debug(ZONE, "in _sx_compress_wio");

    /* move the data into the zlib write buffer */
    if(buf->len > 0) {
        _sx_debug(ZONE, "loading %d bytes into zlib write buffer", buf->len);

        _sx_buffer_alloc_margin(sc->wbuf, 0, buf->len);
        memcpy(sc->wbuf->data + sc->wbuf->len, buf->data, buf->len);
        sc->wbuf->len += buf->len;

        _sx_buffer_clear(buf);
    }

    /* compress the data */
    if(sc->wbuf->len > 0) {
        sc->wstrm.avail_in = sc->wbuf->len;
        sc->wstrm.next_in = sc->wbuf->data;
        /* deflate() on write buffer until there is data to compress */
        do {
            /* make place for deflated data */
            _sx_buffer_alloc_margin(buf, 0, sc->wbuf->len + SX_COMPRESS_CHUNK);

                sc->wstrm.avail_out = sc->wbuf->len + SX_COMPRESS_CHUNK;
            sc->wstrm.next_out = buf->data + buf->len;

            ret = deflate(&(sc->wstrm), Z_SYNC_FLUSH);
            assert(ret != Z_STREAM_ERROR);

            buf->len += sc->wbuf->len + SX_COMPRESS_CHUNK - sc->wstrm.avail_out;

        } while (sc->wstrm.avail_out == 0);

        if(ret != Z_OK || sc->wstrm.avail_in != 0) {
            /* throw an error */
            _sx_gen_error(sxe, SX_ERR_COMPRESS, "compression error", "Error during compression");
            _sx_event(s, event_ERROR, (void *) &sxe);

            sx_error(s, stream_err_INTERNAL_SERVER_ERROR, "Error during compression");
            sx_close(s);

            return -2;  /* fatal */
        }

        sc->wbuf->len = sc->wstrm.avail_in;
        sc->wbuf->data = sc->wstrm.next_in;
    }

    _sx_debug(ZONE, "passing %d bytes from zlib write buffer", buf->len);

    return 1;
}
Exemplo n.º 2
0
static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
    sasl_conn_t *sasl;
    sx_error_t sxe;
    int *x, len;
    char *out;

    sasl = ((_sx_sasl_data_t) s->plugin_data[p->index])->sasl;

    /* if there's no security layer, don't bother */
    sasl_getprop(sasl, SASL_SSF, (const void **) &x);
    if(*x == 0)
        return 1;

    _sx_debug(ZONE, "doing sasl decode");

    /* decode the input */
    if (sasl_decode(sasl, buf->data, buf->len, (const char **) &out, &len)
      != SASL_OK) {
      /* Fatal error */
      _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", "sasl_decode failed, closing stream");
      _sx_event(s, event_ERROR, (void *) &sxe);
      _sx_state(s, state_CLOSING);
      return -1;
    }
    
    /* replace the buffer */
    _sx_buffer_set(buf, out, len, NULL);

    _sx_debug(ZONE, "%d bytes decoded from sasl channel", len);
    
    return 1;
}
Exemplo n.º 3
0
int sx_compress_client_compress(sx_plugin_t p, sx_t s, const char *pemfile) {
    assert((int) (p != NULL));
    assert((int) (s != NULL));

    /* sanity */
    if(s->type != type_CLIENT || s->state != state_STREAM) {
        _sx_debug(ZONE, "wrong conn type or state for client compress");
        return 1;
    }

    /* check if we're already compressed */
    if((s->flags & SX_COMPRESS_WRAPPER) || s->compressed) {
        _sx_debug(ZONE, "channel already compressed");
        return 1;
    }

    _sx_debug(ZONE, "initiating compress sequence");

    /* go */
    jqueue_push(s->wbufq, _sx_buffer_new("<compress xmlns='" uri_COMPRESS "'><method>zlib</method></compress>", sizeof(uri_COMPRESS)-1 + 51, NULL, NULL), 0);
    s->want_write = 1;
    _sx_event(s, event_WANT_WRITE, NULL);

    return 0;
}
Exemplo n.º 4
0
static int _sx_sasl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
    sx_error_t sxe;
    int len, ret;
    char *out;
    Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];

    _sx_debug(ZONE, "doing sasl decode");

    /* decode the input */
    ret = gsasl_decode(sd, buf->data, buf->len, &out, &len);
    if (ret != GSASL_OK) {
        _sx_debug(ZONE, "gsasl_decode failed (%d): %s", ret, gsasl_strerror (ret));
        /* Fatal error */
        _sx_gen_error(sxe, SX_ERR_AUTH, "SASL Stream decoding failed", (char*) gsasl_strerror (ret));
        _sx_event(s, event_ERROR, (void *) &sxe);
        return -1;
    }
    
    /* replace the buffer */
    _sx_buffer_set(buf, out, len, NULL);
    free(out);

    _sx_debug(ZONE, "%d bytes decoded from sasl channel", len);
    
    return 1;
}
Exemplo n.º 5
0
static int _sx_sasl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
    sasl_conn_t *sasl;
    int *x, len, pos, reslen, maxbuf;
    char *out, *result;
    int sasl_ret;
    sx_error_t sxe;

    sasl = ((_sx_sasl_data_t) s->plugin_data[p->index])->sasl;

    /* if there's no security layer, don't bother */
    sasl_getprop(sasl, SASL_SSF, (const void **) &x);
    if(*x == 0)
        return 1;

    _sx_debug(ZONE, "doing sasl encode");

    /* can only encode x bytes at a time */
    sasl_getprop(sasl, SASL_MAXOUTBUF, (const void **) &x);
    maxbuf = *x;

    /* encode the output */
    pos = 0;
    result = NULL; reslen = 0;
    while(pos < buf->len) {
        if((buf->len - pos) < maxbuf)
            maxbuf = buf->len - pos;

        sasl_ret = sasl_encode(sasl, &buf->data[pos], maxbuf, (const char **) &out, &len);
        if (sasl_ret != SASL_OK) { 
            _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", "sasl_encode failed, closing stream");
            _sx_event(s, event_ERROR, (void *) &sxe);
            _sx_state(s, state_CLOSING);
            return 1;
        }
        
        result = (char *) realloc(result, sizeof(char) * (reslen + len));
        memcpy(&result[reslen], out, len);
        reslen += len;

        pos += maxbuf;
    }
    
    /* replace the buffer */
    _sx_buffer_set(buf, result, reslen, result);

    _sx_debug(ZONE, "%d bytes encoded for sasl channel", buf->len);
    
    return 1;
}
Exemplo n.º 6
0
/** 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;
}
Exemplo n.º 7
0
Arquivo: env.c Projeto: 6wei/jabberd2
sx_plugin_t sx_env_plugin(sx_env_t env, sx_plugin_init_t init, ...) {
    sx_plugin_t p;
    int ret;
    va_list args;

    assert((int) (env != NULL));
    assert((int) (init != NULL));

    va_start(args, init);

    p = (sx_plugin_t) calloc(1, sizeof(struct _sx_plugin_st));

    p->env = env;
    p->index = env->nplugins;

    ret = (init)(env, p, args);
    va_end(args);

    if(ret != 0) {
        free(p);
        return NULL;
    }

    env->plugins = (sx_plugin_t *) realloc(env->plugins, sizeof(sx_plugin_t) * (env->nplugins + 1));
    env->plugins[env->nplugins] = p;
    env->nplugins++;

    _sx_debug(ZONE, "plugin initialised (index %d)", p->index);

    return p;
}
Exemplo n.º 8
0
//** send an extended error with custom contents other than text */
// Ideally should be merged with sx_error.  sx_error should permit additional content beneath the <stream:error> element, other than a <text> node.
void _sx_error_extended(sx_t s, int err, const char *content) {
    int len = 0;
    sx_buf_t buf;

    /* build the string */
    if(s->state < state_STREAM) len = strlen(uri_STREAMS) + 61;
    len += strlen(uri_STREAMS) + strlen(uri_STREAM_ERR) + strlen(_stream_errors[err]) + 58;
    if(content != NULL) len += strlen(content) + strlen(_stream_errors[err]) + 2;

    buf = _sx_buffer_new(NULL, len, NULL, NULL);
    len = 0;

    if(s->state < state_STREAM)
        len = sprintf(buf->data, "<stream:stream xmlns:stream='" uri_STREAMS "' version='1.0'>");

    if(content == NULL)
        len += sprintf(&(buf->data[len]), "<stream:error xmlns:stream='" uri_STREAMS "'><%s xmlns='" uri_STREAM_ERR "'/></stream:error>", _stream_errors[err]);
    else
        len += sprintf(&(buf->data[len]), "<stream:error xmlns:stream='" uri_STREAMS "'><%s xmlns='" uri_STREAM_ERR "'>%s</%s></stream:error>", _stream_errors[err], content, _stream_errors[err]);

    if(s->state < state_STREAM)
        len += sprintf(&(buf->data[len]), "</stream:stream>");

    buf->len--;
    assert(len == buf->len);

    _sx_debug(ZONE, "prepared error: %.*s", buf->len, buf->data);

    /* go */
    jqueue_push(s->wbufq, buf, 0);

    /* stuff to write */
    s->want_write = 1;
}
Exemplo n.º 9
0
/* code stolen from SSL_CTX_set_verify(3) */
static int _sx_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
    char    buf[256];
    X509   *err_cert;
    int     err, depth;

    err_cert = X509_STORE_CTX_get_current_cert(ctx);
    err = X509_STORE_CTX_get_error(ctx);
    depth = X509_STORE_CTX_get_error_depth(ctx);

    /*
     * Ignore errors when we can't get CRLs in the certificate
     */
    if (!preverify_ok && err == X509_V_ERR_UNABLE_TO_GET_CRL) {
    	_sx_debug(ZONE, "ignoring verify error:num=%d:%s:depth=%d:%s\n", err,
    	                 X509_verify_cert_error_string(err), depth, buf);
    	preverify_ok = 1;
    }

    /*
     * Retrieve the pointer to the SSL of the connection currently treated
     * and the application specific data stored into the SSL object.
     */
    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);

    if (!preverify_ok) {
        _sx_debug(ZONE, "verify error:num=%d:%s:depth=%d:%s\n", err,
                 X509_verify_cert_error_string(err), depth, buf);
    }
    else
    {
        _sx_debug(ZONE, "OK! depth=%d:%s", depth, buf);
    }

    /*
     * At this point, err contains the last verification error. We can use
     * it for something special
     */
    if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT))
    {
      X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
      _sx_debug(ZONE, "issuer= %s\n", buf);
    }

    return preverify_ok;
 }
Exemplo n.º 10
0
static void _sx_compress_notify_compress(sx_t s, void *arg) {

    _sx_debug(ZONE, "preparing for compress");

    _sx_reset(s);

    /* start listening */
    sx_server_init(s, s->flags | SX_COMPRESS_WRAPPER);
}
Exemplo n.º 11
0
/** we can write */
static int _sx_get_pending_write(sx_t s) {
    sx_buf_t in, out;
    int ret;

    assert(s != NULL);

    if (s->wbufpending != NULL) {
    /* there's already a pending buffer ready to write */
    return 0;
    }

    /* get the first buffer off the queue */
    in = jqueue_pull(s->wbufq);
    if(in == NULL) {
        /* if there was a write event, and something is interested,
       we still have to tell the plugins */
        in = _sx_buffer_new(NULL, 0, NULL, NULL);
    }

    /* if there's more to write, we want to make sure we get it */
    s->want_write = jqueue_size(s->wbufq);

    /* make a copy for processing */
    out = _sx_buffer_new(in->data, in->len, in->notify, in->notify_arg);

    _sx_debug(ZONE, "encoding %d bytes for writing: %.*s", in->len, in->len, in->data);

    /* run it by the plugins */
    ret = _sx_chain_io_write(s, out);
    if(ret <= 0) {
    /* TODO/!!!: Are we leaking the 'out' buffer here? How about the 'in' buffer? */
        if(ret == -1) {
            /* temporary failure, push it back on the queue */
            jqueue_push(s->wbufq, in, (s->wbufq->front != NULL) ? s->wbufq->front->priority : 0);
            s->want_write = 1;
        } else if(ret == -2) {
            /* permanent failure, its all over */
            /* !!! shut down */
            s->want_read = s->want_write = 0;
            return -1;
        }

        /* done */
        return 0;
    }

    _sx_buffer_free(in);

    if (out->len == 0)
    /* if there's nothing to write, then we're done */
        _sx_buffer_free(out);
    else
        s->wbufpending = out;

    return 0;
}
Exemplo n.º 12
0
Arquivo: ack.c Projeto: 6wei/jabberd2
/** process handshake packets from the client */
static int _sx_ack_process(sx_t s, sx_plugin_t p, nad_t nad) {
    int attr;

    /* not interested if we're not a server */
    if(s->type != type_SERVER)
        return 1;

    /* only want ack packets */
    if((NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_ACK) || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_ACK, strlen(uri_ACK)) != 0))
        return 1;

    /* pings */
    if(NAD_ENAME_L(nad, 0) == 4 && strncmp(NAD_ENAME(nad, 0), "ping", 4) == 0) {
        jqueue_push(s->wbufq, _sx_buffer_new("<ack:pong/>", 11, NULL, NULL), 0);
        s->want_write = 1;

        /* handled the packet */
        nad_free(nad);
        return 0;
    }

    /* enable only when authenticated */
    if(s->state == state_OPEN && NAD_ENAME_L(nad, 0) == 6 && strncmp(NAD_ENAME(nad, 0), "enable", 6) == 0) {
        jqueue_push(s->wbufq, _sx_buffer_new("<ack:enabled/>", 14, NULL, NULL), 254);
        s->want_write = 1;

        s->plugin_data[p->index] = (void *) 1;

        /* handled the packet */
        nad_free(nad);
        return 0;
    }

    /* 'r' or 'a' when enabled */
    if(s->plugin_data[p->index] != NULL && NAD_ENAME_L(nad, 0) == 1 && (strncmp(NAD_ENAME(nad, 0), "r", 1) == 0 || strncmp(NAD_ENAME(nad, 0), "a", 1) == 0) ) {
        attr = nad_find_attr(nad, 0, -1, "c", NULL);
        if(attr >= 0) {
            char *buf = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, attr) + 13 + 1));
            snprintf(buf, NAD_AVAL_L(nad, attr) + 13 + 1, "<ack:a b='%.*s'/>", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
            jqueue_push(s->wbufq, _sx_buffer_new(buf, NAD_AVAL_L(nad, attr) + 13, NULL, NULL), 255);
            free(buf);
            s->want_write = 1;
        }
        
        /* handled the packet */
        nad_free(nad);
        return 0;
    }

    _sx_debug(ZONE, "unhandled ack namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
    nad_free(nad);
    return 0;
}
Exemplo n.º 13
0
/** sx features callback */
static void _address_features(sx_t s, sx_plugin_t p, nad_t nad) {
    int ns;

    /* offer feature only when not authenticated yet */
    if(s->state >= state_OPEN)
        return;

    _sx_debug(ZONE, "adding address feature");

    ns = nad_add_namespace(nad, uri_ADDRESS_FEATURE, NULL);
    nad_append_elem(nad, ns, "address", 1);
    nad_append_cdata(nad, s->ip, strlen(s->ip), 2);
}
Exemplo n.º 14
0
/** args: none */
int sx_compress_init(sx_env_t env, sx_plugin_t p, va_list args) {

    _sx_debug(ZONE, "initialising compression plugin");

    p->client = _sx_compress_new;
    p->server = _sx_compress_new;
    p->rio = _sx_compress_rio;
    p->wio = _sx_compress_wio;
    p->features = _sx_compress_features;
    p->process = _sx_compress_process;
    p->free = _sx_compress_free;

    return 0;
}
Exemplo n.º 15
0
static void _sx_compress_features(sx_t s, sx_plugin_t p, nad_t nad) {
    int ns;

    /* if the session is already compressed, or the app told us not to,
	 * or STARTTLS is required and stream is not encrypted yet, then we don't offer anything */
    if(s->compressed || !(s->flags & SX_COMPRESS_OFFER) || ((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0))
        return;

    _sx_debug(ZONE, "offering compression");

    ns = nad_add_namespace(nad, uri_COMPRESS_FEATURE, NULL);
    nad_append_elem(nad, ns, "compression", 1);
    nad_append_elem(nad, ns, "method", 2);
    nad_append_cdata(nad, "zlib", 4, 3);
}
Exemplo n.º 16
0
/** make the stream authenticated second time round */
static void _sx_sasl_stream(sx_t s, sx_plugin_t p) {
    Gsasl_session *sd = (Gsasl_session *) s->plugin_data[p->index];

    /* do nothing the first time */
    if(sd == NULL)
        return;

    /* are we auth'd? */
    if(NULL == gsasl_property_fast(sd, GSASL_AUTHID)) {
        _sx_debug(ZONE, "not auth'd, not advancing to auth'd state yet");
        return;
    }

    /* otherwise, its auth time */
    _sx_sasl_open(s, sd);
}
Exemplo n.º 17
0
/** send raw data out */
int _sx_raw_write(sx_t s, const char *buf, int len) {
    /* siltently drop it if we're closing or closed */
    if(s->state >= state_CLOSING) {
        log_debug(ZONE, "stream closed, dropping outgoing raw data");
        return 1;
    }

    _sx_debug(ZONE, "queuing for write: %.*s", len, buf);

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

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

    return 0;
}
Exemplo n.º 18
0
/** move the stream to the auth state */
void _sx_sasl_open(sx_t s, Gsasl_session *sd) {
    char *method, *authzid;
    const char *realm = NULL;
    struct sx_sasl_creds_st creds = {NULL, NULL, NULL, NULL};
    _sx_sasl_t ctx = gsasl_session_hook_get(sd);
    const char *mechname = gsasl_mechanism_name (sd);

    /* get the method */
    method = (char *) malloc(sizeof(char) * (strlen(mechname) + 6));
    sprintf(method, "SASL/%s", mechname);

    /* and the authorization identifier */
    creds.authzid = gsasl_property_fast(sd, GSASL_AUTHZID);
    creds.authnid = gsasl_property_fast(sd, GSASL_AUTHID);
    creds.realm   = gsasl_property_fast(sd, GSASL_REALM);

    if(0 && ctx && ctx->cb) { /* not supported yet */
        if((ctx->cb)(sx_sasl_cb_CHECK_AUTHZID, &creds, NULL, s, ctx->cbarg)!=sx_sasl_ret_OK) {
            _sx_debug(ZONE, "stream authzid: %s verification failed, not advancing to auth state", creds.authzid);
            free(method);
            return;
        }
    } else if (NULL != gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME)) {
        creds.authzid = strdup(gsasl_property_fast(sd, GSASL_GSSAPI_DISPLAY_NAME));
        authzid = NULL;
    } else {
        /* override unchecked arbitrary authzid */
        if(creds.realm && creds.realm[0] != '\0') {
            realm = creds.realm;
        } else {
            realm = s->req_to;
        }
        authzid = (char *) malloc(sizeof(char) * (strlen(creds.authnid) + strlen(realm) + 2));
        sprintf(authzid, "%s@%s", creds.authnid, realm);
        creds.authzid = authzid;
    }

    /* proceed stream to authenticated state */
    sx_auth(s, method, creds.authzid);

    free(method);
    if(authzid) free(authzid);
}
Exemplo n.º 19
0
/** send an error */
void _sx_error(sx_t s, int err, const char *text) {
    int len = 0;
    sx_buf_t buf;

    /* open stream if not already */
    if(s->state < state_STREAM) {
        if (s->flags & SX_WEBSOCKET_WRAPPER)
            jqueue_push(s->wbufq, _sx_buffer_new("<open xmlns='" uri_XFRAMING "' version='1.0' />", sizeof(uri_XFRAMING) + 30, NULL, NULL), 0);
        else
            jqueue_push(s->wbufq, _sx_buffer_new("<stream:stream xmlns:stream='" uri_STREAMS "' version='1.0'>", sizeof(uri_STREAMS) + 44, NULL, NULL), 0);
    }

    /* build the error */
    len = strlen(uri_STREAMS) + strlen(uri_STREAM_ERR) + strlen(_stream_errors[err]) + 58;
    if(text != NULL) len += strlen(uri_STREAM_ERR) + strlen(text) + 22;

    buf = _sx_buffer_new(NULL, len, NULL, NULL);

    if(text == NULL)
        len = sprintf(buf->data, "<stream:error xmlns:stream='" uri_STREAMS "'><%s xmlns='" uri_STREAM_ERR "'/></stream:error>", _stream_errors[err]);
    else
        len = sprintf(buf->data, "<stream:error xmlns:stream='" uri_STREAMS "'><%s xmlns='" uri_STREAM_ERR "'/><text xmlns='" uri_STREAM_ERR "'>%s</text></stream:error>", _stream_errors[err], text);

    buf->len--;
    assert(len == buf->len);

    _sx_debug(ZONE, "prepared error: %.*s", buf->len, buf->data);
    jqueue_push(s->wbufq, buf, 0);

    /* close the stream if needed */
    if(s->state < state_STREAM) {
        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);
    }

    /* stuff to write */
    s->want_write = 1;
}
Exemplo n.º 20
0
static void _sx_compress_new(sx_t s, sx_plugin_t p) {
    _sx_compress_conn_t sc;

    /* only bothering if they asked for wrappermode */
    if(!(s->flags & SX_COMPRESS_WRAPPER) || s->compressed)
        return;

    _sx_debug(ZONE, "preparing for compressed connect for %d", s->tag);

    sc = (_sx_compress_conn_t) calloc(1, sizeof(struct _sx_compress_conn_st));

    /* initialize streams */
    sc->rstrm.zalloc = Z_NULL;
    sc->rstrm.zfree = Z_NULL;
    sc->rstrm.opaque = Z_NULL;
    sc->rstrm.avail_in = 0;
    sc->rstrm.next_in = Z_NULL;
    inflateInit(&(sc->rstrm));

    sc->wstrm.zalloc = Z_NULL;
    sc->wstrm.zfree = Z_NULL;
    sc->wstrm.opaque = Z_NULL;
    deflateInit(&(sc->wstrm), Z_DEFAULT_COMPRESSION);

    /* read and write buffers */
    sc->rbuf = _sx_buffer_new(NULL, 0, NULL, NULL);
    sc->wbuf = _sx_buffer_new(NULL, 0, NULL, NULL);

    s->plugin_data[p->index] = (void *) sc;

    /* bring the plugin online */
    _sx_chain_io_plugin(s, p);

    /* mark stream compressed */
    s->compressed = 1;
}
Exemplo n.º 21
0
static int _sx_compress_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
    _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
    int ret;
    sx_error_t sxe;

    /* only bothering if they asked for wrappermode */
    if(!(s->flags & SX_COMPRESS_WRAPPER) || !s->compressed)
        return 1;

    _sx_debug(ZONE, "in _sx_compress_rio");

    /* move the data into the zlib read buffer */
    if(buf->len > 0) {
        _sx_debug(ZONE, "loading %d bytes into zlib read buffer", buf->len);

        _sx_buffer_alloc_margin(sc->rbuf, 0, buf->len);
        memcpy(sc->rbuf->data + sc->rbuf->len, buf->data, buf->len);
        sc->rbuf->len += buf->len;

        _sx_buffer_clear(buf);
    }

    /* decompress the data */
    if(sc->rbuf->len > 0) {
        sc->rstrm.avail_in = sc->rbuf->len;
        sc->rstrm.next_in = sc->rbuf->data;
        /* run inflate() on read buffer while able to fill the output buffer */
        do {
            /* make place for inflated data */
            _sx_buffer_alloc_margin(buf, 0, SX_COMPRESS_CHUNK);

            sc->rstrm.avail_out = SX_COMPRESS_CHUNK;
            sc->rstrm.next_out = buf->data + buf->len;

            ret = inflate(&(sc->rstrm), Z_SYNC_FLUSH);
            assert(ret != Z_STREAM_ERROR);
            switch (ret) {
            case Z_NEED_DICT:
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
                /* throw an error */
                _sx_gen_error(sxe, SX_ERR_COMPRESS, "compression error", "Error during decompression");
                _sx_event(s, event_ERROR, (void *) &sxe);

                sx_error(s, stream_err_INVALID_XML, "Error during decompression");
                sx_close(s);

                return -2;
            }

            buf->len += SX_COMPRESS_CHUNK - sc->rstrm.avail_out;

        } while (sc->rstrm.avail_out == 0);

        sc->rbuf->len = sc->rstrm.avail_in;
        sc->rbuf->data = sc->rstrm.next_in;
    }

    _sx_debug(ZONE, "passing %d bytes from zlib read buffer", buf->len);

    /* flag if we want to read */
    if(sc->rbuf->len > 0)
    s->want_read = 1;

    if(buf->len == 0)
        return 0;

    return 1;
}
Exemplo n.º 22
0
static int _sx_compress_process(sx_t s, sx_plugin_t p, nad_t nad) {
    int flags;
    char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
    sx_error_t sxe;

    /* not interested if we're a server and we never offered it */
    if(s->type == type_SERVER && !(s->flags & SX_COMPRESS_OFFER))
        return 1;

    /* only want compress packets */
    if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != sizeof(uri_COMPRESS)-1 || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_COMPRESS, sizeof(uri_COMPRESS)-1) != 0)
        return 1;

    /* compress from client */
    if(s->type == type_SERVER) {
        if(NAD_ENAME_L(nad, 0) == 8 && strncmp(NAD_ENAME(nad, 0), "compress", 8) == 0) {
            nad_free(nad);

            /* can't go on if we've been here before */
            if(s->compressed) {
                _sx_debug(ZONE, "compress requested on already compressed channel, dropping packet");
                return 0;
            }

            _sx_debug(ZONE, "compress requested, setting up");

            /* go ahead */
            jqueue_push(s->wbufq, _sx_buffer_new("<compressed xmlns='" uri_COMPRESS "'/>", sizeof(uri_COMPRESS)-1 + 22, _sx_compress_notify_compress, NULL), 0);
            s->want_write = 1;

            /* handled the packet */
            return 0;
        }
    }

    else if(s->type == type_CLIENT) {
        /* kick off the handshake */
        if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "compressed", 7) == 0) {
            nad_free(nad);

            /* save interesting bits */
            flags = s->flags;

            if(s->ns != NULL) ns = strdup(s->ns);

            if(s->req_to != NULL) to = strdup(s->req_to);
            if(s->req_from != NULL) from = strdup(s->req_from);
            if(s->req_version != NULL) version = strdup(s->req_version);

            /* reset state */
            _sx_reset(s);

            _sx_debug(ZONE, "server ready for compression, starting");

            /* second time round */
            sx_client_init(s, flags | SX_COMPRESS_WRAPPER, ns, to, from, version);

            /* free bits */
            if(ns != NULL) free(ns);
            if(to != NULL) free(to);
            if(from != NULL) free(from);
            if(version != NULL) free(version);

            return 0;
        }

        /* busted server */
        if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "failure", 7) == 0) {
            nad_free(nad);

            _sx_debug(ZONE, "server can't handle compression, business as usual");

            _sx_gen_error(sxe, SX_ERR_COMPRESS_FAILURE, "compress failure", "Server was unable to establish compression");
            _sx_event(s, event_ERROR, (void *) &sxe);

            return 0;
        }
    }

    _sx_debug(ZONE, "unknown compress namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
    nad_free(nad);
    return 0;
}
Exemplo n.º 23
0
/** we can read */
int sx_can_read(sx_t s) {
    sx_buf_t in, out;
    int read, ret;

    assert((int) (s != NULL));

    /* do we care? */
    if(!s->want_read && s->state < state_CLOSING)
        return 0;           /* no more thanks */

    _sx_debug(ZONE, "%d ready for reading", s->tag);

    /* new buffer */
    in = _sx_buffer_new(NULL, 1024, NULL, NULL);

    /* get them to read stuff */
    read = _sx_event(s, event_READ, (void *) in);

    /* bail if something went wrong */
    if(read < 0) {
        _sx_buffer_free(in);
        s->want_read = 0;
        s->want_write = 0;
        return 0;
    }

    if(read == 0) {
        /* nothing to read
         * should never happen because we did get a read event,
         * thus there is something to read, or error handled
         * via (read < 0) block before (errors return -1) */
        _sx_debug(ZONE, "decoded 0 bytes read data - this should not happen");
        _sx_buffer_free(in);

    } else {
        _sx_debug(ZONE, "passed %d read bytes", in->len);

        /* count bytes read */
        s->rbytes += in->len;

        /* make a copy for processing */
        out = _sx_buffer_new(in->data, in->len, in->notify, in->notify_arg);

        /* run it by the plugins */
        ret = _sx_chain_io_read(s, out);

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

        if(ret <= 0) {
            if(ret < 0) {
                /* permanent failure, its all over */
                /* !!! shut down */
                s->want_read = s->want_write = 0;
            }

            _sx_buffer_free(in);
            _sx_buffer_free(out);

            /* done */
            if(s->want_write) _sx_event(s, event_WANT_WRITE, NULL);
            return s->want_read;
        }

        _sx_buffer_free(in);

        _sx_debug(ZONE, "decoded read data (%d bytes): %.*s", out->len, out->len, out->data);

        /* into the parser with you */
        _sx_process_read(s, out);
    }

    /* if we've written everything, and we're closed, then inform the app it can kill us */
    if(s->want_write == 0 && s->state == state_CLOSING) {
        _sx_state(s, state_CLOSED);
        _sx_event(s, event_CLOSED, NULL);
        return 0;
    }

    if(s->state == state_CLOSED)
        return 0;

    if(s->want_write) _sx_event(s, event_WANT_WRITE, NULL);
    return s->want_read;
}
Exemplo n.º 24
0
/** 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;
    }
}
Exemplo n.º 25
0
int sx_can_write(sx_t s) {
    sx_buf_t out;
    int ret, written;

    assert((int) (s != NULL));

    /* do we care? */
    if(!s->want_write && s->state < state_CLOSING)
        return 0;           /* no more thanks */

    _sx_debug(ZONE, "%d ready for writing", s->tag);

    ret = _sx_get_pending_write(s);
    if (ret < 0) {
        /* fatal error */
        _sx_debug(ZONE, "fatal error after attempt to write on fd %d", s->tag);
        /* permanent error so inform the app it can kill us */
        sx_kill(s);
        return 0;
    }

    /* if there's nothing to write, then we're done */
    if(s->wbufpending == NULL) {
        if(s->want_read) _sx_event(s, event_WANT_READ, NULL);
        return s->want_write;
    }

    out = s->wbufpending;
    s->wbufpending = NULL;

    /* get the callback to do the write */
    _sx_debug(ZONE, "handing app %d bytes to write", out->len);
    written = _sx_event(s, event_WRITE, (void *) out);

    if(written < 0) {
        /* bail if something went wrong */
        _sx_buffer_free(out);
        s->want_read = 0;
        s->want_write = 0;
        return 0;
    } else if(written < out->len) {
        /* if not fully written, this buffer is still pending */
        out->len -= written;
        out->data += written;
        s->wbufpending = out;
        s->want_write ++;
    } else {
        /* notify */
        if(out->notify != NULL)
            (out->notify)(s, out->notify_arg);

        /* done with this */
        _sx_buffer_free(out);
    }

    /* if we've written everything, and we're closed, then inform the app it can kill us */
    if(s->want_write == 0 && s->state == state_CLOSING) {
        _sx_state(s, state_CLOSED);
        _sx_event(s, event_CLOSED, NULL);
        return 0;
    }

    if(s->state == state_CLOSED)
        return 0;

    if(s->want_read) _sx_event(s, event_WANT_READ, NULL);
    return s->want_write;
}
Exemplo n.º 26
0
static int _sx_sasl_canon_user(sasl_conn_t *conn, void *ctx, const char *user, unsigned ulen, unsigned flags, const char *user_realm, const char *out_user, unsigned out_umax, unsigned *out_ulen) {
    char *buf;
    char principal[3072];
    char out_buf[3072]; // node(1023) + '@'(1) + domain/realm(1023) + '@'(1) + krb domain(1023) + '\0'(1)
    _sx_sasl_data_t sd = (_sx_sasl_data_t)ctx;
    char user_null_term[1024];

    if (ulen > (sizeof(user_null_term)-1)) {
        _sx_debug(ZONE, "Got a SASL argument \"user\" that exceeds our maximum length, rejecting");
        return SASL_BADAUTH;
    }
    // make a NULL terminated copy for ourself
    memcpy(user_null_term, user, ulen);
    user_null_term[ulen] = '\0';

    sasl_getprop(conn, SASL_MECHNAME, (const void **) &buf);
    if (strncmp(buf, "GSSAPI", 7) == 0) {
        // Reformat the user argument for odkerb_get_im_handle
        // (Remove the default realm from string if necessary)
        char adjusted_user[1024];
        char *s = strdup(user_null_term);
        if (s) {
            char *c = strsep(&s, "@");
            if (c) {
                strlcpy(adjusted_user, c, sizeof(adjusted_user));
                c = strsep(&s, "@");
                if (c) {
                    // should be the default realm - ignore
                    c = strsep(&s, "@");
                    if (c) {
                        // should be a foreign realm that we want to check
                        strlcat(adjusted_user, "@", sizeof(adjusted_user));
                        strlcat(adjusted_user, c, sizeof(adjusted_user));
                    }
                } else {
                    _sx_debug(ZONE, "Notice: unexpected format of SASL \"user\" argument: %s", user_null_term);
                }
            } else {
                _sx_debug(ZONE, "Error getting SASL argument \"user\"");
                free(s);
                return SASL_BADAUTH;
            }
            free(s);
        } else {
            _sx_debug(ZONE, "Error copying SASL argument \"user\"");
            return SASL_BADAUTH;
        }

        snprintf(principal, sizeof(principal), "%s@%s", adjusted_user, user_realm);
        if (odkerb_get_im_handle(principal, sd->stream->req_to, "JABBER:", out_buf, 
                    ((out_umax > sizeof(out_buf)) ? sizeof(out_buf) : out_umax)) == 0) {
            strlcpy(out_user, out_buf, out_umax); 
            *out_ulen = strlen(out_user);
            _sx_debug(ZONE, "Got IM handle: %s for user %s, realm %s", out_buf, user_null_term, user_realm);
        } else {
            return SASL_BADAUTH;
        }
    }
    else if (strncmp(buf, "ANONYMOUS", 10) == 0) {
        sd->ctx->cb(sx_sasl_cb_GEN_AUTHZID, NULL, (void **)&buf, sd->stream, sd->ctx->cbarg);
        strncpy(out_user, buf, out_umax);
        out_user[out_umax]='\0';
        *out_ulen=strlen(out_user);
    } else {
        memcpy(out_user,user,ulen);
        *out_ulen = ulen;
    }
    return SASL_OK;
}