"the stream output chain is empty" while writing a stream filter module
Maxim Dounin
mdounin at mdounin.ru
Mon Jan 10 18:28:42 UTC 2022
Hello!
On Mon, Jan 10, 2022 at 10:10:37AM +0000, Max Dymond wrote:
> I'm trying to use NGINX as an SSL "man in the middle" to (a)
> terminate an SSL stream, (b) parse a binary protocol inside that
> stream to accept or reject messages, (c) connect over SSL to
> another target and send all the accepted messages.
>
> After a bit of investigation I'm trying my hand at a stream
> filter module, and I'm trying to do a very simple module which:
> - splits incoming messages into individual buffers (the protocol
> has well-defined packet sizes)
> - creates chain links for each of these messages.
>
> // Create a new buffer and chain link.
> link = ngx_alloc_chain_link(c->pool);
> if (link == NULL) {
> return NGX_ERROR;
> }
>
> b = ngx_create_temp_buf(c->pool, frame_length);
> link->buf = b;
>
> // Copy the frame into the buffer.
> ngx_memcpy(link->buf->start, pos, frame_length);
> link->buf->last += frame_length;
> link->buf->tag = cl->buf->tag;
> link->buf->flush = cl->buf->flush;
>
>
> This process seems to work but I'm getting an error message when
> I pass my chain to the next filter:
>
> return ngx_stream_next_filter(s, out, from_upstream);
>
> gives
>
> the stream output chain is empty while proxying and sending to upstream, client: 172.20.0.5, server: 0.0.0.0:9043, upstream: "172.20.0.2:9042", bytes from/to client:9/61, bytes from/to upstream:61/9
>
> I've got diagnostics which verify the (single) chainlink I'm
> passing to the next filter:
>
> 2022/01/10 09:26:14 [info] 24#24: *3 MD: OUT chainlink ....3FB0: buf: ....ADE0 pos:....3FC0 last:....3FC9 file_pos:0 file_last:0 start:....3FC0 end:....3FC9 tag:000055D260A4A880 file:0000000000000000 temporary:1 memory:0 mmap:0 recycled:0 in_file:0 flush:1 last_buf:0 last_in_chain:0 temp_file:0
>
> (I'm copying the tag and the flush bit from the original chain
> in the hopes of it doing something, but no dice).
Note that copying tags is generally wrong. Tags are used to
identify buffers owned by a module, and wrong tag may result in
incorrect behaviour if a module will try to modify/reuse such
buffer. I don't think this can be a problem in your particular
case, yet it's something to avoid.
> When I turn debug mode on logs are a little confusing as well;
> for the incoming chain:
>
> 2022/01/10 10:03:30 [info] 24#24: *5 MD: IN chainlink ....7C00: buf: ....7C10 pos:....91E0 last:....91E9 file_pos:0 file_last:0 start:0000000000000000 end:0000000000000000 tag:000055928B63E880 file:0000000000000000 temporary:1 memory:0 mmap:0 recycled:0 in_file:0 flush:1 last_buf:0 last_in_chain:0 temp_file:0
> <my processing>
> 2022/01/10 10:03:30 [info] 24#24: *5 MD: OUT chainlink ....7C60: buf: ....EA70 pos:....7C70 last:....7C79 file_pos:0 file_last:0 start:.....7C70 end:.....7C79 tag:000055928B63E880 file:0000000000000000 temporary:1 memory:0 mmap:0 recycled:0 in_file:0 flush:1 last_buf:0 last_in_chain:0 temp_file:0
> 2022/01/10 10:03:30 [debug] 24#24: *5 write new buf t:1 f:0 00007FFB71B57C70, pos 00007FFB71B57C70, size: 9 file: 0, size: 0
> 2022/01/10 10:03:30 [debug] 24#24: *5 stream write filter: l:0 f:1 s:9
> 2022/01/10 10:03:30 [debug] 24#24: *5 writev: 9 of 9
> 2022/01/10 10:03:30 [debug] 24#24: *5 stream write filter 0000000000000000
> 2022/01/10 10:03:30 [debug] 24#24: *5 event timer: 4, old: 515169565, new: 515169565
Writing of your buffer is done here. Following lines correspond
to the next chunk of data:
> 2022/01/10 10:03:30 [debug] 24#24: *5 recv: eof:0, avail:-1
> 2022/01/10 10:03:30 [debug] 24#24: *5 recv: fd:12 61 of 16384
> 2022/01/10 10:03:30 [debug] 24#24: *5 write new buf t:1 f:0 0000000000000000, pos 00007FFB70AADC10, size: 61 file: 0, size: 0
> 2022/01/10 10:03:30 [debug] 24#24: *5 stream write filter: l:0 f:1 s:61
> 2022/01/10 10:03:30 [debug] 24#24: *5 SSL to write: 61
> 2022/01/10 10:03:30 [debug] 24#24: *5 SSL_write: 61
> 2022/01/10 10:03:30 [debug] 24#24: *5 stream write filter 0000000000000000
> 2022/01/10 10:03:30 [debug] 24#24: *5 event timer: 4, old: 515169565, new: 515169665
(As far as I understand, this is a chunk of data in the opposite
direction: note it uses SSL_write() instead of writev().)
And here is another call of the stream write filter, which results
in the error:
> 2022/01/10 10:03:30 [debug] 24#24: *5 stream write filter: l:0 f:0 s:0
> 2022/01/10 10:03:30 [alert] 24#24: *5 the stream output chain is empty
Given no read operations before the call, most likely it's called
again by nginx because you haven't correctly marked original
buffers as sent.
> Am I missing some step somewhere that's causing this to fail?
> Does anyone have an example of a simple stream filter module
> which repackages buffers?
Some example of a stream filter module can be found in njs code,
see here:
http://hg.nginx.org/njs/file/9b112a44e540/nginx/ngx_stream_js_module.c#l585
Note the "ctx->buf->pos = ctx->buf->last;" line, which marks the
original buffer as fully sent:
http://hg.nginx.org/njs/file/9b112a44e540/nginx/ngx_stream_js_module.c#l648
Hope this helps.
--
Maxim Dounin
http://mdounin.ru/
More information about the nginx-devel
mailing list