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
> > Dbmail-dev@dbmail.org
> > 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;
 }
 

Reply via email to