📄 ssl_engine_io.c
字号:
if (!filter_ctx->pssl) { /* ssl_filter_io_shutdown was called */ return ap_pass_brigade(f->next, bb); } inctx = (bio_filter_in_ctx_t *)filter_ctx->pbioRead->ptr; outctx = (bio_filter_out_ctx_t *)filter_ctx->pbioWrite->ptr; /* When we are the writer, we must initialize the inctx * mode so that we block for any required ssl input, because * output filtering is always nonblocking. */ inctx->mode = AP_MODE_READBYTES; inctx->block = APR_BLOCK_READ; if ((status = ssl_io_filter_connect(filter_ctx)) != APR_SUCCESS) { return ssl_io_filter_error(f, bb, status); } while (!APR_BRIGADE_EMPTY(bb)) { apr_bucket *bucket = APR_BRIGADE_FIRST(bb); /* If it is a flush or EOS, we need to pass this down. * These types do not require translation by OpenSSL. */ if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) { if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) { status = outctx->rc; break; } if (APR_BUCKET_IS_EOS(bucket)) { /* * By definition, nothing can come after EOS. * which also means we can pass the rest of this brigade * without creating a new one since it only contains the * EOS bucket. */ if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { return status; } break; } else { /* bio_filter_out_flush() already passed down a flush bucket * if there was any data to be flushed. */ apr_bucket_delete(bucket); } } else if (AP_BUCKET_IS_EOC(bucket)) { /* The special "EOC" bucket means a shutdown is needed; * - turn off buffering in bio_filter_out_write * - issue the SSL_shutdown */ filter_ctx->nobuffer = 1; status = ssl_filter_io_shutdown(filter_ctx, f->c, 0); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_INFO, status, f->c, "SSL filter error shutting down I/O"); } if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { return status; } break; } else { /* filter output */ const char *data; apr_size_t len; status = apr_bucket_read(bucket, &data, &len, rblock); if (APR_STATUS_IS_EAGAIN(status)) { /* No data available: flush... */ if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) { status = outctx->rc; break; } rblock = APR_BLOCK_READ; continue; /* and try again with a blocking read. */ } rblock = APR_NONBLOCK_READ; if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) { break; } status = ssl_filter_write(f, data, len); apr_bucket_delete(bucket); if (status != APR_SUCCESS) { break; } } } return status;}/* 128K maximum buffer size by default. */#ifndef SSL_MAX_IO_BUFFER#define SSL_MAX_IO_BUFFER (128 * 1024)#endifstruct modssl_buffer_ctx { apr_bucket_brigade *bb; apr_pool_t *pool;};int ssl_io_buffer_fill(request_rec *r){ conn_rec *c = r->connection; struct modssl_buffer_ctx *ctx; apr_bucket_brigade *tempb; apr_off_t total = 0; /* total length buffered */ int eos = 0; /* non-zero once EOS is seen */ /* Create the context which will be passed to the input filter; * containing a setaside pool and a brigade which constrain the * lifetime of the buffered data. */ ctx = apr_palloc(r->pool, sizeof *ctx); apr_pool_create(&ctx->pool, r->pool); ctx->bb = apr_brigade_create(ctx->pool, c->bucket_alloc); /* ... and a temporary brigade. */ tempb = apr_brigade_create(r->pool, c->bucket_alloc); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "filling buffer"); do { apr_status_t rv; apr_bucket *e, *next; /* The request body is read from the protocol-level input * filters; the buffering filter will reinject it from that * level, allowing content/resource filters to run later, if * necessary. */ rv = ap_get_brigade(r->proto_input_filters, tempb, AP_MODE_READBYTES, APR_BLOCK_READ, 8192); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "could not read request body for SSL buffer"); return HTTP_INTERNAL_SERVER_ERROR; } /* Iterate through the returned brigade: setaside each bucket * into the context's pool and move it into the brigade. */ for (e = APR_BRIGADE_FIRST(tempb); e != APR_BRIGADE_SENTINEL(tempb) && !eos; e = next) { const char *data; apr_size_t len; next = APR_BUCKET_NEXT(e); if (APR_BUCKET_IS_EOS(e)) { eos = 1; } else if (!APR_BUCKET_IS_METADATA(e)) { rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "could not read bucket for SSL buffer"); return HTTP_INTERNAL_SERVER_ERROR; } total += len; } rv = apr_bucket_setaside(e, ctx->pool); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "could not setaside bucket for SSL buffer"); return HTTP_INTERNAL_SERVER_ERROR; } APR_BUCKET_REMOVE(e); APR_BRIGADE_INSERT_TAIL(ctx->bb, e); } ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "total of %" APR_OFF_T_FMT " bytes in buffer, eos=%d", total, eos); /* Fail if this exceeds the maximum buffer size. */ if (total > SSL_MAX_IO_BUFFER) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "request body exceeds maximum size for SSL buffer"); return HTTP_REQUEST_ENTITY_TOO_LARGE; } } while (!eos); apr_brigade_destroy(tempb); /* Insert the filter which will supply the buffered data. */ ap_add_input_filter(ssl_io_buffer, ctx, r, c); return 0;}/* This input filter supplies the buffered request body to the caller * from the brigade stored in f->ctx. */static apr_status_t ssl_io_filter_buffer(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t bytes){ struct modssl_buffer_ctx *ctx = f->ctx; apr_status_t rv; ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, "read from buffered SSL brigade, mode %d, " "%" APR_OFF_T_FMT " bytes", mode, bytes); if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) { return APR_ENOTIMPL; } if (mode == AP_MODE_READBYTES) { apr_bucket *e; /* Partition the buffered brigade. */ rv = apr_brigade_partition(ctx->bb, bytes, &e); if (rv && rv != APR_INCOMPLETE) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, "could not partition buffered SSL brigade"); ap_remove_input_filter(f); return rv; } /* If the buffered brigade contains less then the requested * length, just pass it all back. */ if (rv == APR_INCOMPLETE) { APR_BRIGADE_CONCAT(bb, ctx->bb); } else { apr_bucket *d = APR_BRIGADE_FIRST(ctx->bb); e = APR_BUCKET_PREV(e); /* Unsplice the partitioned segment and move it into the * passed-in brigade; no convenient way to do this with * the APR_BRIGADE_* macros. */ APR_RING_UNSPLICE(d, e, link); APR_RING_SPLICE_HEAD(&bb->list, d, e, apr_bucket, link); APR_BRIGADE_CHECK_CONSISTENCY(bb); APR_BRIGADE_CHECK_CONSISTENCY(ctx->bb); } } else { /* Split a line into the passed-in brigade. */ rv = apr_brigade_split_line(bb, ctx->bb, mode, bytes); if (rv) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, "could not split line from buffered SSL brigade"); ap_remove_input_filter(f); return rv; } } if (APR_BRIGADE_EMPTY(ctx->bb)) { apr_bucket *e = APR_BRIGADE_LAST(bb); /* Ensure that the brigade is terminated by an EOS if the * buffered request body has been entirely consumed. */ if (e == APR_BRIGADE_SENTINEL(bb) || !APR_BUCKET_IS_EOS(e)) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); } ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, "buffered SSL brigade now exhausted; removing filter"); ap_remove_input_filter(f); } return APR_SUCCESS;}static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c, SSL *ssl){ bio_filter_in_ctx_t *inctx; inctx = apr_palloc(c->pool, sizeof(*inctx)); filter_ctx->pInputFilter = ap_add_input_filter(ssl_io_filter, inctx, NULL, c); filter_ctx->pbioRead = BIO_new(&bio_filter_in_method); filter_ctx->pbioRead->ptr = (void *)inctx; inctx->ssl = ssl; inctx->bio_out = filter_ctx->pbioWrite; inctx->f = filter_ctx->pInputFilter; inctx->rc = APR_SUCCESS; inctx->mode = AP_MODE_READBYTES; inctx->cbuf.length = 0; inctx->bb = apr_brigade_create(c->pool, c->bucket_alloc); inctx->block = APR_BLOCK_READ; inctx->pool = c->pool; inctx->filter_ctx = filter_ctx;}void ssl_io_filter_init(conn_rec *c, SSL *ssl){ ssl_filter_ctx_t *filter_ctx; filter_ctx = apr_palloc(c->pool, sizeof(ssl_filter_ctx_t)); filter_ctx->nobuffer = 0; filter_ctx->pOutputFilter = ap_add_output_filter(ssl_io_filter, filter_ctx, NULL, c); filter_ctx->pbioWrite = BIO_new(&bio_filter_out_method); filter_ctx->pbioWrite->ptr = (void *)bio_filter_out_ctx_new(filter_ctx, c); ssl_io_input_add_filter(filter_ctx, c, ssl); SSL_set_bio(ssl, filter_ctx->pbioRead, filter_ctx->pbioWrite); filter_ctx->pssl = ssl; apr_pool_cleanup_register(c->pool, (void*)filter_ctx, ssl_io_filter_cleanup, apr_pool_cleanup_null); if (c->base_server->loglevel >= APLOG_DEBUG) { BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb); BIO_set_callback_arg(SSL_get_rbio(ssl), (void *)ssl); } return;}void ssl_io_filter_register(apr_pool_t *p){ /* This filter MUST be after the HTTP_HEADER filter, but it also must be * a resource-level filter so it has the request_rec. */ ap_register_output_filter ("UPGRADE_FILTER", ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5); ap_register_input_filter (ssl_io_filter, ssl_io_filter_input, NULL, AP_FTYPE_CONNECTION + 5); ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5); ap_register_input_filter (ssl_io_buffer, ssl_io_filter_buffer, NULL, AP_FTYPE_PROTOCOL - 1); return;}/* _________________________________________________________________**** I/O Data Debugging** _________________________________________________________________*/#define DUMP_WIDTH 16static void ssl_io_data_dump(server_rec *srvr, MODSSL_BIO_CB_ARG_TYPE *s, long len){ char buf[256]; char tmp[64]; int i, j, rows, trunc; unsigned char ch; trunc = 0; for(; (len > 0) && ((s[len-1] == ' ') || (s[len-1] == '\0')); len--) trunc++; rows = (len / DUMP_WIDTH); if ((rows * DUMP_WIDTH) < len) rows++; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr, "+-------------------------------------------------------------------------+"); for(i = 0 ; i< rows; i++) { apr_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH); apr_cpystrn(buf, tmp, sizeof(buf)); for (j = 0; j < DUMP_WIDTH; j++) { if (((i * DUMP_WIDTH) + j) >= len) apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf)); else { ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff; apr_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' '); apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf)); } } apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf)); for (j = 0; j < DUMP_WIDTH; j++) { if (((i * DUMP_WIDTH) + j) >= len) apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf)); else { ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff; apr_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.'); apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf)); } } apr_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf)); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr, "%s", buf); } if (trunc > 0) ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr, "| %04ld - <SPACES/NULS>", len + trunc); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr, "+-------------------------------------------------------------------------+"); return;}long ssl_io_data_cb(BIO *bio, int cmd, MODSSL_BIO_CB_ARG_TYPE *argp, int argi, long argl, long rc){ SSL *ssl; conn_rec *c; server_rec *s; if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL) return rc; if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL) return rc; s = c->base_server; if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN) || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) { if (rc >= 0) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "%s: %s %ld/%d bytes %s BIO#%pp [mem: %pp] %s", SSL_LIBRARY_NAME, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"), rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"), bio, argp, (argp != NULL ? "(BIO dump follows)" : "(Oops, no memory buffer?)")); if (argp != NULL) ssl_io_data_dump(s, argp, rc); } else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "%s: I/O error, %d bytes expected to %s on BIO#%pp [mem: %pp]", SSL_LIBRARY_NAME, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"), bio, argp); } } return rc;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -