Skip to content

Instantly share code, notes, and snippets.

@mechanicalnull
Created December 9, 2021 15:32
Show Gist options
  • Save mechanicalnull/4044b2f08e7f8bfa38f5666c3f758b4e to your computer and use it in GitHub Desktop.
Save mechanicalnull/4044b2f08e7f8bfa38f5666c3f758b4e to your computer and use it in GitHub Desktop.
Test harness for crackaddr from sendmail 8.12.7
// Test harness for crackaddr from sendmail 8.12.7 by @mechanicalnull
// sendmail license: https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf
#include <string.h>
#include <ctype.h>
#define MAXNAME 256
typedef int bool;
#define false 0
#define true 1
#define ColonOkInAddr 1
#define MACROEXPAND ((unsigned char)0201) /* macro expansion */
char *MustQuoteChars = "@,;:\\()[].'";
/*
** CRACKADDR -- parse an address and turn it into a macro
**
** This doesn't actually parse the address -- it just extracts
** it and replaces it with "$g". The parse is totally ad hoc
** and isn't even guaranteed to leave something syntactically
** identical to what it started with. However, it does leave
** something semantically identical.
**
** This algorithm has been cleaned up to handle a wider range
** of cases -- notably quoted and backslash escaped strings.
** This modification makes it substantially better at preserving
** the original syntax.
**
** Parameters:
** addr -- the address to be cracked.
**
** Returns:
** a pointer to the new version.
**
** Side Effects:
** none.
**
** Warning:
** The return value is saved in local storage and should
** be copied if it is to be reused.
*/
char *
crackaddr(addr)
register char *addr;
{
register char *p;
register char c;
int cmtlev;
int realcmtlev;
int anglelev, realanglelev;
int copylev;
int bracklev;
bool qmode;
bool realqmode;
bool skipping;
bool putgmac = false;
bool quoteit = false;
bool gotangle = false;
bool gotcolon = false;
register char *bp;
char *buflim;
char *bufhead;
char *addrhead;
static char buf[MAXNAME + 1];
// Omit debug prints for testing
/*
if (tTd(33, 1))
sm_dprintf("crackaddr(%s)\n", addr);
*/
/* strip leading spaces */
while (*addr != '\0' && isascii(*addr) && isspace(*addr))
addr++;
/*
** Start by assuming we have no angle brackets. This will be
** adjusted later if we find them.
*/
bp = bufhead = buf;
buflim = &buf[sizeof buf - 7];
p = addrhead = addr;
copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0;
bracklev = 0;
qmode = realqmode = false;
while ((c = *p++) != '\0')
{
/*
** If the buffer is overful, go into a special "skipping"
** mode that tries to keep legal syntax but doesn't actually
** output things.
*/
skipping = bp >= buflim;
if (copylev > 0 && !skipping)
*bp++ = c;
/* check for backslash escapes */
if (c == '\\')
{
/* arrange to quote the address */
if (cmtlev <= 0 && !qmode)
quoteit = true;
if ((c = *p++) == '\0')
{
/* too far */
p--;
goto putg;
}
if (copylev > 0 && !skipping)
*bp++ = c;
goto putg;
}
/* check for quoted strings */
if (c == '"' && cmtlev <= 0)
{
qmode = !qmode;
if (copylev > 0 && !skipping)
realqmode = !realqmode;
continue;
}
if (qmode)
goto putg;
/* check for comments */
if (c == '(')
{
cmtlev++;
/* allow space for closing paren */
if (!skipping)
{
buflim--;
realcmtlev++;
if (copylev++ <= 0)
{
if (bp != bufhead)
*bp++ = ' ';
*bp++ = c;
}
}
}
if (cmtlev > 0)
{
if (c == ')')
{
cmtlev--;
copylev--;
if (!skipping)
{
realcmtlev--;
buflim++;
}
}
continue;
}
else if (c == ')')
{
/* syntax error: unmatched ) */
if (copylev > 0 && !skipping)
bp--;
}
/* count nesting on [ ... ] (for IPv6 domain literals) */
if (c == '[')
bracklev++;
else if (c == ']')
bracklev--;
/* check for group: list; syntax */
if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
!gotcolon && !ColonOkInAddr)
{
register char *q;
/*
** Check for DECnet phase IV ``::'' (host::user)
** or ** DECnet phase V ``:.'' syntaxes. The latter
** covers ``user@DEC:.tay.myhost'' and
** ``DEC:.tay.myhost::user'' syntaxes (bletch).
*/
if (*p == ':' || *p == '.')
{
if (cmtlev <= 0 && !qmode)
quoteit = true;
if (copylev > 0 && !skipping)
{
*bp++ = c;
*bp++ = *p;
}
p++;
goto putg;
}
gotcolon = true;
bp = bufhead;
if (quoteit)
{
*bp++ = '"';
/* back up over the ':' and any spaces */
--p;
while (isascii(*--p) && isspace(*p))
continue;
p++;
}
for (q = addrhead; q < p; )
{
c = *q++;
if (bp < buflim)
{
if (quoteit && c == '"')
*bp++ = '\\';
*bp++ = c;
}
}
if (quoteit)
{
if (bp == &bufhead[1])
bp--;
else
*bp++ = '"';
while ((c = *p++) != ':')
{
if (bp < buflim)
*bp++ = c;
}
*bp++ = c;
}
/* any trailing white space is part of group: */
while (isascii(*p) && isspace(*p) && bp < buflim)
*bp++ = *p++;
copylev = 0;
putgmac = quoteit = false;
bufhead = bp;
addrhead = p;
continue;
}
if (c == ';' && copylev <= 0 && !ColonOkInAddr)
{
if (bp < buflim)
*bp++ = c;
}
/* check for characters that may have to be quoted */
if (strchr(MustQuoteChars, c) != NULL)
{
/*
** If these occur as the phrase part of a <>
** construct, but are not inside of () or already
** quoted, they will have to be quoted. Note that
** now (but don't actually do the quoting).
*/
if (cmtlev <= 0 && !qmode)
quoteit = true;
}
/* check for angle brackets */
if (c == '<')
{
register char *q;
/* assume first of two angles is bogus */
if (gotangle)
quoteit = true;
gotangle = true;
/* oops -- have to change our mind */
anglelev = 1;
if (!skipping)
realanglelev = 1;
bp = bufhead;
if (quoteit)
{
*bp++ = '"';
/* back up over the '<' and any spaces */
--p;
while (isascii(*--p) && isspace(*p))
continue;
p++;
}
for (q = addrhead; q < p; )
{
c = *q++;
if (bp < buflim)
{
if (quoteit && c == '"')
*bp++ = '\\';
*bp++ = c;
}
}
if (quoteit)
{
if (bp == &buf[1])
bp--;
else
*bp++ = '"';
while ((c = *p++) != '<')
{
if (bp < buflim)
*bp++ = c;
}
*bp++ = c;
}
copylev = 0;
putgmac = quoteit = false;
continue;
}
if (c == '>')
{
if (anglelev > 0)
{
anglelev--;
if (!skipping)
{
realanglelev--;
buflim++;
}
}
else if (!skipping)
{
/* syntax error: unmatched > */
if (copylev > 0)
bp--;
quoteit = true;
continue;
}
if (copylev++ <= 0)
*bp++ = c;
continue;
}
/* must be a real address character */
putg:
if (copylev <= 0 && !putgmac)
{
if (bp > bufhead && bp[-1] == ')')
*bp++ = ' ';
*bp++ = MACROEXPAND;
*bp++ = 'g';
putgmac = true;
}
}
/* repair any syntactic damage */
if (realqmode)
*bp++ = '"';
while (realcmtlev-- > 0)
*bp++ = ')';
while (realanglelev-- > 0)
*bp++ = '>';
*bp++ = '\0';
// Omitting debug prints for testing
/*
if (tTd(33, 1))
{
sm_dprintf("crackaddr=>`");
xputs(buf);
sm_dprintf("'\n");
}
*/
return buf;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main( int argc, char **argv ){
ssize_t num_bytes_read;
char input_buf[1003] = {0};
if( argc == 1 ) {
num_bytes_read = read(STDIN_FILENO, input_buf, sizeof(input_buf)-1);
if (num_bytes_read > 0)
crackaddr( input_buf );
}
else
printf("Please supply input via stdin.\n");
}
@mechanicalnull
Copy link
Author

Note this is trivial to turn into a persistent-mode or Libfuzzer-style harness. I used a standard main function for a more apples-to-apples comparison with the CGC version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment