Wednesday 16 February 2011

I never heard of Agnitas EMM until today

One of our servers has been blowing up occasionally. After a few failed attempts, I managed to use gdb to write out the file our process was trying to parse:


(gdb) call open("/tmp/x", 0101, 0777);
$17 = 3
(gdb) call write(3, body_data._M_dataplus._M_p, strlen(body_data._M_dataplus._M_p))
[wait... then hit ^C]
$18 = 1782948729

So clearly there's a missing NUL termination. Hardly surprising for this kind of bug. I just used dd to reduce the file to what seemed like the relevant data, exluding random junk from the process' memory.

And... bingo!

X-Mailer: Agnitas EMM 7.0
...
X-Barracuda-BRTS-Evidence: 5367d33c72fcbdfe74e38a30b0711cd7-9667-unk
X-Barracuda-BRTS-Evidence: c57ea21d3b9fd34cfd1e59d35e55f1c9-4515-unk
X-OriginalArrivalTime: 16 Feb 2011 13:34:07.0165 (UTC) FILETIME=[2F631ED0:01CBCDDE]
X-Recipient-Count: 1
X-Incoming-Message-Id: 1PphWB-0003Zl-Bu
X-Sender-Host-Name: mail.example.com
X-Sender-Host-Address: 192.168.17.42
X-Sender-Auth:

This is a multi-part message in MIME format.

---==AGNITASOUTER164240059B290156CA==
Content-Type: multipart/alternative;

boundary="-==AGNITASINNERB164240059B290156CA=="

The last five of those X- headers are ones we add. But look at that blank line between the MIME part's Content-Type: header and the boundary=... parameter. Just look at it! That blank line caused our process to not know what message part boundary to look for, so it just went into infinite loop apoplexy, gobbling up memory until the OOM killer zapped it.

Oh wow, they offer some presumably older version of EMM for download from sourceforge. Let's take a quick dive into their source code:


else if (n == 1) // online HTML
buf.append ("HContent-Type: multipart/alternative;" + data.eol +
"\tboundary=\"" + xmlStr (outerBoundary) + "\"" + data.eol);
...
if (n == 2)
buf.append ("Content-Type: multipart/alternative;" + data.eol +
" boundary=\"" + xmlStr (innerBoundary) + "\"" + data.eol +
data.eol +
"--" + xmlStr (innerBoundary) + data.eol);
...
else if (n == 1)
buf.append ("Content-Type: multipart/alternative;" + data.eol +
"\tboundary=\"" + xmlStr (outerBoundary) + "\"" + data.eol +
data.eol);
...
if (n == 2)
buf.append ("Content-Type: multipart/alternative;" + data.eol +
" boundary=\"" + xmlStr (innerBoundary) + "\"" + data.eol +
data.eol +
"--" + xmlStr (innerBoundary) + data.eol);

Nope, doesn't look like EMM is likely to be adding the extra newline. So I don't know who is. Maybe Barracuda Network are doing something nasty? I can't tell, because there's nothing relevant I can (easily) find on their website.

Tuesday 1 February 2011

Grokking C declarations.

What does it mean when some bit of code declares

int (*foo(char))(double); // ?

Forget all those C-to-English recipes - if you're anything like me, shoehorning your thinking into natural language just confuses things.

Instead, chop off the atomic type specifier (the "int"), and think what the remaining expression would mean:

x = (*foo('x'))(3.1416);

Immediately it's clear that if you call foo with a char argument, you get a pointer, that you can dereference (not really necessary to do explicitly for function pointers), to get a function that you can call with a double argument, that returns an int.

If you really still need to know what foo "is": it's a function taking char, returning a pointer to a function taking double, returning int. Rather wordy, and now you still have to translate the English version into some more fundamental / more abstract prelinguistic mental structure.

Thanks to Lee for pointing this trick out to me a few years ago. I'm sure it's all over the Internet for those with whom the search is strong.