[pkg-fetchmail-maint] Bug#416812: fetchmail: fails to write bsmtp file and aborts with socket error

Reto Schuettel reto at schuettel.ch
Fri Mar 30 13:48:27 UTC 2007


Package: fetchmail
Version: 6.3.6-1
Severity: normal
Tags: patch

fetchmail aborts all attempts to download messages from a mailserver
with a 'socket error'.

I was able to reproduce the error on two different systems (Debian Linux
and Solaris) using two different mail servers (MS Exchange and Courier
Imap). 

| $ cat config 
| poll localhost protocol imap user testuser password testpass folder
| INBOX bsmtp /tmp/D3RvtNRoDx.bsmtp keep no rewrite
| 
| $ fetchmail -f config 
| fetchmail: Server CommonName mismatch: bounty.schuettel.ch != localhost
| fetchmail: Server certificate verification error: self signed
| certificate
| 46 messages for testuser at localhost (folder INBOX).
| reading message testuser at localhost.localdomain:1 of 46 (2397 header
| octets)..fetchmail: socket error while fetching from testuser at localhost
| fetchmail: Query status=2 (SOCKET)


| $ fetchmail -v -v -v -f config 
| ... 
| fetchmail: IMAP< * 41 FETCH (RFC822.SIZE 2760)
| fetchmail: IMAP< * 42 FETCH (RFC822.SIZE 1887)
| fetchmail: IMAP< * 43 FETCH (RFC822.SIZE 1927)
| fetchmail: IMAP< * 44 FETCH (RFC822.SIZE 3663)
| fetchmail: IMAP< * 45 FETCH (RFC822.SIZE 3035)
| fetchmail: IMAP< * 46 FETCH (RFC822.SIZE 2530)
| fetchmail: IMAP< A0007 OK FETCH completed.
| fetchmail: IMAP> A0008 FETCH 1 RFC822.HEADER
| fetchmail: IMAP< * 1 FETCH (RFC822.HEADER {2397}
| reading message testuser at localhost.localdomain:1 of 46 (2397 header octets)
| fetchmail: socket error while fetching from testuser at localhost
| #fetchmail: 6.3.6 querying localhost (protocol IMAP) at Fri Mar 30
| 13:52:55 2007: poll completed
| fetchmail: Query status=2 (SOCKET)
| fetchmail: Deleting fetchids file.
| fetchmail: normal termination, status 2
| fetchmail: Deleting fetchids file.


I tried to analyse the problem using gdb and I found out that the
following call to fetch_messages returns 2 (PS_SOCKET) and causes this 'socket
error'.  

* driver.c
| 1412     /* fetch in lockstep mode */
| 1413     err = fetch_messages(mailserver_socket, ctl,
| 1414                          count, &msgsizes,
| 1415                          maxfetch,
| 1416                          &fetches, &dispatches, &deletions);
| 1417     if (err != PS_SUCCESS && err != PS_MAXFETCH)
| 1418         goto cleanUp;


It turns out the the following if-else statement returns the error-code
'PS_STOCKET' because n is not equal two strlen(buf).  

* transact.c
| 1327     *cp++ = '\n';
| 1328     *cp++ = '\0';
| 1329     n = stuffline(ctl, buf);
| 1330 
| 1331     if (n == strlen(buf))
| 1332         return PS_SUCCESS;
| 1333     else
| 1334         return PS_SOCKET;
| 1335 }

* backtrace at 1334:
| #0  readheaders (sock=7, fetchlen=1571, reallen=2716, ctl=0x8095688, num=356, suppress_readbody=0x0) at transact.c:1334
| #1  0x08055726 in fetch_messages (mailserver_socket=7, ctl=0x8095688, count=3606, msgsizes=0x80869a8, maxfetch=0, fetches=0xbfc5bcb4, dispatches=0xbfc5bcb0, 
|     deletions=0xbfc5bcc4) at driver.c:621
| #2  0x08056f1c in do_session (ctl=0x8095688, proto=0x8081ca0, maxfetch=0) at driver.c:1413
| #3  0x0805751c in do_protocol (ctl=0x8095688, proto=0x8081ca0) at driver.c:1624
| #4  0x0806cdc5 in doIMAP (ctl=0x8095688) at imap.c:1302
| #5  0x080505e9 in query_host (ctl=0x8095688) at fetchmail.c:1480
| #6  0x0804e0ed in main (argc=3, argv=0xbfc5bf54) at fetchmail.c:739

And interestingly stuffline always returns 1, well.. after looking at the code it even made sense...:

* sink.c 
|  687     n = 0;
|  688     if (ctl->mda || ctl->bsmtp) {
|  689         n = fwrite(buf, last - buf, 1, sinkfp);
|  690         if (ferror(sinkfp)) n = -1;
|  691     } else if (ctl->smtp_socket != -1)
|  692         n = SockWrite(ctl->smtp_socket, buf, last - buf);
|  693 
|  694     phase = oldphase;
|  695 
|  696     return(n);

* man fwrite
|        size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
| The function fwrite() writes nmemb elements of data, each size bytes long, to
| the stream pointed to by stream, obtaining them from the location given by ptr.

... I'm not sure, but it seems like author swaped the two args size and nmemb
by accident, so instead of writing N elements with the size 1 he writes
ONE elment with size N :). Of course that works as well, but then you
can't compare the returned value (which is always 1) against the strlen
of the buf... and therefore it always aborts. transact.c::1331 was the
only place I've seen where the return value is actually checked against
another value, usually it is only checked if it is -1 or not, so I'm not
sure what the intented handling was. 

Anyway, it turns out that swaping the two parameters in the call to
fwrite() fixes my problem pretty well (because then strlen(buf) ==
fwrite(buf)), but I don't know if this fix causes problems anywhere
else. I've attached a small patch which does that.

Regards,
Reto Schuettel


-- System Information:
Debian Release: 4.0
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.18-4-k7
Locale: LANG=C, LC_CTYPE=de_CH (charmap=ISO-8859-1)

Versions of packages fetchmail depends on:
ii  adduser                     3.102        Add and remove users and groups
ii  debianutils                 2.17         Miscellaneous utilities specific t
ii  libc6                       2.3.6.ds1-13 GNU C Library: Shared libraries
ii  libssl0.9.8                 0.9.8c-4     SSL shared libraries
ii  lsb-base                    3.1-23.1     Linux Standard Base 3.1 init scrip

Versions of packages fetchmail recommends:
ii  ca-certificates               20070303   Common CA Certificates PEM files

-- no debconf information
-------------- next part --------------
--- fetchmail-6.3.6.orig/sink.c
+++ fetchmail-6.3.6/sink.c
@@ -686,7 +686,7 @@
 
     n = 0;
     if (ctl->mda || ctl->bsmtp) {
-	n = fwrite(buf, last - buf, 1, sinkfp);
+	n = fwrite(buf, 1, last - buf, sinkfp);
 	if (ferror(sinkfp)) n = -1;
     } else if (ctl->smtp_socket != -1)
 	n = SockWrite(ctl->smtp_socket, buf, last - buf);


More information about the pkg-fetchmail-maint mailing list