Subject: Winsock recv/WSARecv(..., MSG_PEEK) is unreliable (was Re: Transient fetchmail ...)
From: Jason Tishler <[email-masked]>
To: "Gerrit P. Haase" <[email-masked]>
Cc: Cygwin <[email-masked]>
Date: Tue, 14 Aug 2001 12:53:50 -0400

On Wed, Aug 08, 2001 at 11:01:00PM -0400, Jason Tishler wrote:
> Thanks for trying to help me, it is much appreciated.  Unfortunately,
> your problem seems to be similar to (or the same as) Olaf's.  Your
> problem seems to be on the transmit side -- mine seems to be on the
> receive side.

I believe that I have finally found the root cause to my transient
fetchmail corruption problems under Windows 2000.  From the following
MSDN articles, it appears the Winsock's recv/WSARecv(..., MSG_PEEK)
is inherently unreliable and strongly discouraged:
 
    http://support.microsoft.com/support/kb/articles/Q192/5/99.ASP
    http://support.microsoft.com/support/kb/articles/Q140/2/63.asp

The following fetchmail code is affected by the above:

    int SockRead(int sock, char *buf, int len)
    {
        char *bp = buf;
        ...
        do
        {
            ...
            if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0)
                return (-1);
            if ((newline = memchr(bp, '\n', n)) != NULL)
                n = newline - bp + 1;           // *** [1] ***
            if ((n = read(sock, bp, n)) == -1)  // *** [2] ***
                return (-1);
            bp += n;
            ...
        }
        while (!newline && len);
        *bp = '\0';
        ...
    }

The number of bytes read in [2] is occasionally less than the number of
bytes requested as determined by [1].  This causes fetchmail to put null
characters in the wrong place and to read the same bytes more than once.
This is the source of the "corruption" that I have been experiencing
under 2000.  For some reason, this problem has not occurred (yet) under
NT 4.0 SP5/SP6a.

Is there any way to fix this in Cygwin?  Otherwise, other applications
that use MSG_PEEK may fail in similar ways.

The attached *ugly* patch to fetchmail works around this problem.  If
the MSG_PEEK problem cannot be fixed in Cygwin, then what is the best
way to solve this issue in fetchmail?  I see the following options:

1. Leverage off of existing BeOS specific code that does not use MSG_PEEK
but only reads one byte at a time.

2. Rework the ugly patch with __CYGWIN__ #ifdefs.

3. Rework SockRead() to not use MSG_PEEK, but instead "drain data
immediately upon arrival into application-allocated buffer space" as
suggested by the MSDN.

I guess that have I ordered the above in what I think is decreasing
chance of acceptance into fetchmail CVS.

Does anyone have any other, hopefully better, ideas?

Thanks,
Jason
--- socket.c.orig	Tue Aug 14 10:36:19 2001
+++ socket.c	Tue Aug 14 10:38:06 2001
@@ -493,7 +493,7 @@ int SockWrite(int sock, char *buf, int l
 int SockRead(int sock, char *buf, int len)
 {
     char *newline, *bp = buf;
-    int n;
+    int n, n2, n3;
 #ifdef	SSL_ENABLE
     SSL *ssl;
 #endif
@@ -578,8 +578,15 @@ int SockRead(int sock, char *buf, int le
 	if ((newline = memchr(bp, '\n', n)) != NULL)
 	    n = newline - bp + 1;
 #ifndef __BEOS__
-	if ((n = fm_read(sock, bp, n)) == -1)
+	if ((n2 = fm_read(sock, bp, n)) == -1)
 	    return(-1);
+	
+	if (n2 != n)
+	{
+	    if ((n3 = fm_read(sock, bp + n2, n - n2)) == -1)
+		return(-1);
+	    printf("n = %d, n2 = %d, n3 = %d\n", n, n2, n3);
+	}
 #endif /* __BEOS__ */
 #endif
 	bp += n;

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/