Hello all
I hope this mailing list is active, and that this is the right place to post
to. In any case, I believe I have discovered a bug: get_selectable_fd() does
not work well when used with pcap_open_offline(). The symptom is that if you
use, as was the poet's intention, get_selectable_fd()'s return value in a call
to select() and then use pcap_next() to get a packet, you will fail to read the
last few packets if reading from a pipe (for example: reading from stdin when
it's piped).
The reason is that savefile.c implements the read_op using fread, which is
stdio and uses *buffering*, while select() is kernel IO and is unaware of the
buffers in stdlib. So while the last packets are inside stdlib's buffer,
select() will not see the fd as "ready for read", will never return, and you
will never see these packets.
(Assume that the pipe doesn't close -- it stays open but simply has no more
input. For example: it's the output of tcpdump -w)
In general, it's a bad thing to mix buffered IO (stdlib, such as
fread,fread,fseek, etc) with kernel io (read/write/seek/select, etc).
There are 2 obvious solutions, with pros and cons each, and possibly more
solutions as well:
1) Use setvbuf( fp, NULL, _IONBF, 0 ) inside pcap_fopen_offline. I tried this
and it works, on both Solaris and Red-Hat. There are several drawbacks which I
will detail later.
2) Stop using stdlib in savefile.c: use read instead of fread. There are only a
handful of places to change, and it seems easy enough. The "downsize" is that
you can't simply replace a call to fread with a call to read, because read()
may be interrupted, so you need to loop if you get EINTR. Another downside is
that you have to remove pcap_fopen_offline(), and replace it with something
like pcap_fdopen_offline(). Again: no stdio at all.
The downsides for (1) above:
a) setvbuf(), in glibc, does what you want:removes buffers *also from the
input, that is: if you use fread()*. It is *NOT* guaranteed to be the case in
the official stdio documentation. All the documentation I found only talks
about fwrite() and flushing. So it works for glibc, but maybe not for other
stdio libs.
b) The documentation states that you must call setvbuf() prior to doing any IO
on the FILE* object (but after having fopen'ed it, of course). In
pcap_fopen_offline() we don't know whether somebody has done any IO on the
FILE* object we are passed. It works well in glibc, again, because it zeros its
read pointer, write point, in buf, out buf, the works. It may not, again, work
well in other stdio libs, or may not work well if we've done some reading which
is still buffered (but not consumed) even in glibc. It *does* work well if you,
right on program start, try to pcap_open_offline( "-", errbuf ) without reading
from stdin, but that's the only case I checked.
... so: distinguished forum: what do you think? Is the description enough?
Should I provide a patch? Should I open a bug in sf.net?
Best,
Nitzan
-
This is the tcpdump-workers list.
Visit https://cod.sandelman.ca/ to unsubscribe.