Created
December 9, 2021 15:32
-
-
Save mechanicalnull/4044b2f08e7f8bfa38f5666c3f758b4e to your computer and use it in GitHub Desktop.
Test harness for crackaddr from sendmail 8.12.7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.