Let's fix this thing. Attached is a patch that does discard_client_input
() without using any memory (2 words of stack+stdio overhead), alerts us
if we're seeing bare LF, and alerts us separately if we see a stdio-
driven EOF.
We also clear the error status beforehand. This generally make stdio
behave better (esp. on SUN)
I also do error checking [on the socket]. This'll work on UNIX systems,
but quite obviously, not on others. Someone will have to test to see if
fileno() can actually return -1 when feof() doesn't go off.
This is 80/90%... Can't test it right now...
On Mon, 2005-05-09 at 17:36 +0000, Aaron Stone wrote:
> OK -- so read discard_client_input() in pipe.c and tell me if you think
> that it's hanging there waiting for ".\r\n" and nothing else can get out
> of that loop. AFAICT, that's got to be what's happening after the error in
> lmtp.c:
>
> mime_readheader()...
> "main(): fatal error from mime_readheader()");
>
> Aaron
>
>
> On Mon, May 9, 2005, Geo Carncross <[EMAIL PROTECTED]>
> said:
>
> > On Mon, 2005-05-09 at 17:12 +0000, Aaron Stone wrote:
> >> I wonder if the bug reporter's LMTP is sitting in a loop waiting for
> >> feof() on the socket and not reading anything. That would certainly cause
> >> LMTP to hang following the last error message he reported. I thought that
> >> sockets do report EOF when they are open, but no more data to be read.
> >> Right?
> >
> > Saying EOF is confusing. EOF is a concept of POSIX.1 and not unix.
> >
> > This is all confusing.
> >
> > In an attempt to clarify:
> >
> > * read() returns 0 bytes when a pipe has no writers
> >
> > * read() returns 0 bytes from a tty when VEOF was sent to the tty driver
> >
> > * read() returns 0 bytes when at the end of a ifile (not a FILE*)
> >
> > * read/recv() returns 0 bytes from a STREAM SOCKET when SHUT_WR occurs
> > on far end.
> >
> >
> > the "eof" flag of a FILE* is set, _ON_UNIX_ when the underlying read()
> > returned 0. Obviously, this can happen for lots of reasons, none of
> > which (except maybe the kernel ifile) mean no more data is coming...
> >
> > feof() is true on POSIX.1 when the next fread() or fgets() will be
> > unable to return any bytes. Some C libraries will anticipate this with a
> > fstat() or similar, but quite obviously, with sockets, it can't be done
> > this way.
> >
> > The difference is critical, because feof() won't fire until after fread
> > () or fgets() fails when the FILE* is actually on a socket. This is in
> > direct opposition to what POSIX.1 says should happen.
> >
> > Unless the far side is deliberately doing a shutdown(fileno(f),SHUT_WR)
> > you simply are never going to get your "EOF report" on a socket.
> >
> > .... whether it's in the UNIX domain or in another domain...
> >
> > And to make matters worse, NOBODY uses shutdown()*. Not that'd matter if
> > they did...
> >
> >
> > * I mean, _I_ do, but I'm a pedantic bastard :)
> >
> > --
> > Internet Connection High Quality Web Hosting
> > http://www.internetconnection.net/
> >
> > _______________________________________________
> > Dbmail-dev mailing list
> > [email protected]
> > http://twister.fastxs.net/mailman/listinfo/dbmail-dev
> >
--
Internet Connection High Quality Web Hosting
http://www.internetconnection.net/
Index: pipe.c
===================================================================
--- pipe.c (revision 1772)
+++ pipe.c (working copy)
@@ -328,25 +328,45 @@
/* read from instream, but simply discard all input! */
int discard_client_input(FILE * instream)
{
- char *tmpline;
+ int ch, ns, l;
- tmpline = (char *) dm_malloc(MAX_LINE_SIZE + 1);
- if (tmpline == NULL) {
- trace(TRACE_ERROR, "%s,%s: unable to allocate memory.",
- __FILE__, __func__);
- return -1;
+ clearerr(instream);
+ for (ns = 0; (ch = fgetc(instream)) != EOF;) {
+ if (ch == '\r') {
+ if (ns == 4) {
+ /* \r\n.\r */
+ ns = 5;
+ } else {
+ /* \r */
+ ns = 1;
+ }
+ } else if (ch == '\n') {
+ if (ns == 1) {
+ /* \r\n */
+ ns = 2;
+ } else if (ns == 5) {
+ /* complete: \r\n.\r\n */
+ return 0;
+ } else {
+ /* .\n ? */
+ trace(TRACE_ERROR, "%s,%s: bare LF.",
+ __FILE__, __func__);
+ }
+ } else if (ch == '.' && ns == 3) {
+ /* \r\n. */
+ ns = 4;
+ }
+ if ((ch = fileno(instream)) != -1) {
+ /* okay, look for error slippage */
+ l = 0;
+ if (getpeername(ns,(struct sockaddr *)"",&l) == -1 && errno != ENOTSOCK) {
+ trace(TRACE_ERROR, "%s,%s: unexpected failure from socket layer (client hangup?)",
+ __FILE__, __func__);
+ }
+ }
}
-
- while (!feof(instream)) {
- if (fgets(tmpline, MAX_LINE_SIZE, instream) == NULL)
- break;
-
- trace(TRACE_DEBUG, "%s,%s: tmpline = [%s]", __FILE__,
- __func__, tmpline);
- if (strcmp(tmpline, ".\r\n") == 0)
- break;
- }
- dm_free(tmpline);
+ trace(TRACE_ERROR, "%s,%s: unexpected EOF from stdio (client hangup?)",
+ __FILE__, __func__);
return 0;
}