Protocol Plug-in Programming Guide:
Previous Next Contents Index



Anti-Relay Plug-in Code
/*
** If you want to see this protocol level plug-in in action,
** you can save, build and run the Protocol Level plug-in
** code in this file. To download, see the
Note at the
** beginning of this chapter.
*/
/*
** NETSCAPE COMMUNICATIONS CORPORATION
**
** Copyright (c) 1998 Netscape Communications Corporation.
** All Rights Reserved.
**
** Use of this Source Code is subject to the terms of
** the applicable license agreement from
** Netscape Communications Corporation.
**
** The copyright notice(s) in this Source Code does not
** indicate actual or intended publication of this Source Code.
**
** Filename
** --------
** antirelay.c
**
** Description
** -----------
** Antirelay sample plug-in
**
*/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "protplug.h"
#define MAX_LINE_LENGTH 1024
#define CONFIG_FILE "/config/antirelay.conf"
/* response strings we send over SMTP */
#define NONLOCAL_RESPONSE_STRING \
"551 delivery not allowed to non-local recipient\r\n"
#define NONLOCAL_ARESPONSE_STRING \
"530 delivery not allowed to non-local recipient, try
authenticating\r\n"
/* turn on or off debug logging based on compile time */
/* flag _DEBUG */
#ifdef _DEBUG
#define LOCAL_DEBUG
#endif
/* UNIX and NT have different routines for doing simple */
/* case insensitive string comparisons */
#ifdef XP_UNIX
#define STRCASECMP strcasecmp
#define STRNCASECMP strncasecmp
#else
#define STRCASECMP _stricmp
#define STRNCASECMP _strnicmp
#endif
/* structure used to store domain patterns for */
/* our submission and delivery lists */
typedef struct HostEntryStruct {
char *pszPattern;
struct HostEntryStruct *pNext;
} HostEntry;
/* session-persistent data that should be passed to */
/* the plug-in each time it is called. */
struct ProtPlugStruct {
int iPassed;
PPSession *pMySession;
char *pszMyPeerAddress;
char *pszResp;
};
/* local function prototypes */
static int PatternMatch(const char *text, const char *p);
static char *ARStrdup(char *s);
/* pointer to the plug-in system routines */
static ProtPlugSystem *gpMySys;
/* list of domains allowed to relay from */
static HostEntry *gpSubmissionDomains;
/* list of domains we handle delivery for */
static HostEntry *gpDeliveryDomains;
/* commonly used response strings from rejecting relays */
static char *gpNonLocalResponse = NULL;
static char *gpNonLocalAuthResponse = NULL;
/* global flag to turn on/off DNS lookups */
static int giResolveHostnames = 1;
/* global flag to turn on/off using authinfo */
static int giUseAuthinfo = 1;
/* global flag to turn on/off stripping AUTH extensions */
static int giStripAuthCapability = 1;
/* global flag to turn on/off advertising of authinfo */
static int giAdvertiseAuthinfo = 0;
/*
** Function
** -------
** int PPModule_Init(ProtPlugSystem *pSys);
**
** Description
** -----------
** initialization entry point for a protocol plug-in module library
**
** Parameters
** ----------
** ProtPlgSystem *pSys: structure with function pointers for
** thread synch functions, memory object, etc.
**
** Returns
** -------
** 0 if successful, non-zero otherwise
** Remarks
** -------
** if Init() fails, Exit() will not be called
**
*/
__export int PPModule_Init(ProtPlugSystem *pSys) 
{ FILE *hConfigFile;
char *pConfigFilePath = NULL;
char *pszBuffer = NULL;
char *pszString;
char *p = NULL;
HostEntry *pNewEntry= NULL;
#ifdef LOCAL_DEBUG
    HostEntry  *pThisEntry= NULL;
#endif
    /* set the global pointer to the system information */
/* so plug-in instances can find it later */
gpMySys = pSys;
    /* announce ourselves in the server log */
gpMySys->PPLogPrintf("Antirelay: installing plugin");
    /* initialize our config lists */
gpSubmissionDomains = NULL;
gpDeliveryDomains = NULL;
    /* initialize a buffer containing the string we will */
/* use when denying relays. doing it once now will save */
/* us time later on */
    if ((gpNonLocalResponse = (char*)
ARStrdup(NONLOCAL_RESPONSE_STRING)) == NULL) {
gpMySys->PPLogPrintf("Antirelay: unable to allocate memory");
gpMySys->PPLogPrintf("Antirelay: plugin not installed");
return -1;
}
    if ((gpNonLocalAuthResponse = (char*)ARStrdup(
NONLOCAL_ARESPONSE_STRING)) == NULL) {
gpMySys->PPLogPrintf("Antirelay: unable to allocate memory");
gpMySys->PPLogPrintf("Antirelay: plugin not installed");
return -1;
}
    if ((pConfigFilePath = gpMySys->PPRealloc(
pConfigFilePath, MAX_LINE_LENGTH))== NULL) {
gpMySys->PPLogPrintf("Antirelay: unable to allocate
%d bytes of memory",
MAX_LINE_LENGTH);
gpMySys->PPLogPrintf("Antirelay: plugin not installed");
return -1;
}
    /* identify the server instance dir so the plug-in */
/* can find the config file */
if ((pConfigFilePath =
gpMySys->PPGetIni("local.instancedir", NULL, pConfigFilePath,
MAX_LINE_LENGTH - 24)) == NULL) {
gpMySys->PPLogPrintf("Antirelay: could not find path to
config file");
gpMySys->PPLogPrintf("Antirelay: plugin not installed");
return -1;
}
    /* build up the path to the config file */
strcat(pConfigFilePath, CONFIG_FILE);
if ( (hConfigFile = fopen(pConfigFilePath, "r")) == NULL) {
gpMySys->PPLogPrintf("Antirelay: unable to open config
file %s (%d)",
pConfigFilePath, errno);
gpMySys->PPLogPrintf("Antirelay: antirelay plugin
not installed");
gpMySys->PPRealloc(pConfigFilePath,0);
return -1;
}
    /* allocate a buffer to read the config file in with */
if ( (pszBuffer =
(char*)(gpMySys->PPRealloc(pszBuffer, MAX_LINE_LENGTH)))
== NULL) {
gpMySys->PPLogPrintf("Antirelay: unable to allocate
%d bytes of memory",
MAX_LINE_LENGTH);
gpMySys->PPLogPrintf("Antirelay: plugin not installed");
return -1;
}
    /* read through the config file */
while ( fgets(pszBuffer, MAX_LINE_LENGTH, hConfigFile)
!= NULL ) {
/* trim the new line */
if ( (p = strchr(pszBuffer, '\n')) != NULL)
*p = '\0';
                /* trim the carriage return */
if ( (p = strchr(pszBuffer, '\r')) != NULL)
*p = '\0';
        /* if the line looks like "delivery:*.netscape.com" */
if (STRNCASECMP(pszBuffer, "delivery:", 9) == 0) {
/* add an entry to the delivery list */
if ( (pszString = (char*)strchr(pszBuffer, ':'))
!= NULL ) {
/* allocate and fill in a new record */
if ( (pNewEntry = (HostEntry*)(
gpMySys->PPRealloc(pNewEntry,
sizeof(HostEntry)))) == NULL ) {
gpMySys->PPLogPrintf("Antirelay:
unable to allocate %d ",
"bytes of memory", sizeof(HostEntry));
gpMySys->PPLogPrintf("Antirelay:
plugin not installed");
gpMySys->PPRealloc(pszBuffer,0);
fclose(hConfigFile);
return -1;
}
                pNewEntry->pNext = gpDeliveryDomains;
pNewEntry->pszPattern = ARStrdup(++pszString);
gpDeliveryDomains = pNewEntry;
                /* be sure to NULL the ptr or else Realloc */
/* will stomp the new buffer */
pNewEntry = NULL;
}
            continue;
}
        /* if the line looks like "submission:*.netscape.com" */
if (STRNCASECMP(pszBuffer, "submission:", 11) == 0) {
/* add an entry to the submission list */
if ( (pszString = (char*)strchr(pszBuffer, ':'))
!= NULL ) {
/* allocate and fill in a new record */
                if ( (pNewEntry = (HostEntry*)
(gpMySys->PPRealloc(pNewEntry,
sizeof(HostEntry)))) == NULL ) {
gpMySys->PPLogPrintf("Antirelay: unable to
allocate %d ",
"bytes of memory",
sizeof(HostEntry));
gpMySys->PPLogPrintf("Antirelay:
plugin not installed");
gpMySys->PPRealloc(pszBuffer,0);
fclose(hConfigFile);
return -1;
}
                pNewEntry->pNext = gpSubmissionDomains;
pNewEntry->pszPattern = ARStrdup(++pszString);
gpSubmissionDomains = pNewEntry;
                /* be sure to NULL the ptr or else Realloc */
/* will stomp the new buffer */
pNewEntry = NULL;
}
            continue;
}
        /* if the line looks like "resolvehostnames:0 */
if (STRNCASECMP(pszBuffer, "resolvehostnames:", 16) == 0) {
if ( (pszString = (char*)strchr(pszBuffer, ':')) != NULL ) {
if (*(++pszString) == '0') {
giResolveHostnames = 0;
gpMySys->PPLogPrintf("Antirelay: turning off DNS ",
"resolution");
}
}
}
/* if the line looks like "stripauthcapability:0 */
if (STRNCASECMP(pszBuffer, "stripauthcapability:", 19) == 0) {
if ( (pszString = (char*)strchr(pszBuffer, ':')) != NULL ) {
if (*(++pszString) == '0') {
giStripAuthCapability = 0;
gpMySys->PPLogPrintf("Antirelay:
not stripping AUTH ",
"extensions for local hosts");
}
}
}
        /* if the line looks like "advertiseauthinfo:0 */
if (STRNCASECMP(pszBuffer, "advertiseauthinfo:", 18) == 0) {
if ( (pszString = (char*)strchr(pszBuffer, ':')) != NULL ){
if (*(++pszString) == '1') {
giAdvertiseAuthinfo = 1;
gpMySys->PPLogPrintf("Antirelay: not advertising ",
"authentication on failure");
}
}
}
/* comment line or bad value, just ignore it */
}
    gpMySys->PPRealloc(pszBuffer,0);
fclose(hConfigFile);
#ifdef LOCAL_DEBUG
    /* turn this on if you want the plug-in to log its config */
/* tables when it starts up ... good for problem tracing */
pThisEntry = gpSubmissionDomains;
while (pThisEntry != NULL) {
gpMySys->PPLogPrintf("Antirelay:
submissions allowed from [%s]",
pThisEntry->pszPattern);
pThisEntry = pThisEntry->pNext;
}
    pThisEntry = gpDeliveryDomains;
while (pThisEntry != NULL) {
gpMySys->PPLogPrintf("Antirelay: delivery allowed to [%s]",
pThisEntry->pszPattern);
pThisEntry = pThisEntry->pNext;
}
#endif
    /* make a note in the logs that the plug-in is live and running */
gpMySys->PPLogPrintf("Antirelay: installation successful");
/* return success */
return 0;
}
/*
** Function
** --------
** void PPModule_Exit(void);
**
** Description
** -----------
** For resource cleanup in a PPModule, called after all
** ProtPlugs have been deleted, before the server exits.
**
** Parameters
** ----------
** none
**
** Returns
** -------
** nothing
**
** Remarks
** -------
**
*/
__export void PPModule_Exit(void) {
HostEntry *pCurrent;
HostEntry *pNext;
    /* free out reject strings */
gpMySys->PPRealloc(gpNonLocalResponse, 0);
gpMySys->PPRealloc(gpNonLocalAuthResponse, 0);
/* free up our submission domain list */
pCurrent = gpSubmissionDomains
    while( pCurrent != NULL ) {
pNext = pCurrent->pNext;
gpMySys->PPRealloc(pCurrent->pszPattern,0);
gpMySys->PPRealloc(pCurrent,0);
pCurrent = pNext;
}
    /* free up our delivery domain list */
    pCurrent = gpDeliveryDomains;
while( pCurrent != NULL ) {
pNext = pCurrent->pNext;
gpMySys->PPRealloc(pCurrent->pszPattern,0);
gpMySys->PPRealloc(pCurrent,0);
pCurrent = pNext;
}
return;
}
/*
** Function*
** --------*
** ProtPlug *PPModule_NewProtPlug(void);*
** *
** Description*
** -----------*
** creates a new ProtPlug, the session analogue of a PPModule*
** *
** Parameters*
** ----------*
** *
** Returns*
** -------*
** a new ProtPlug*
** *
** Remarks*
** -------*
**
*/
__export ProtPlug *PPModule_NewProtPlug(PPSession *ps) {
HostEntry *pThisEntry;
ProtPlug *pNewPlug = NULL;
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: allocating new plugin");
#endif
    /* allocate a new ProtPlug object */
    if ((pNewPlug = 
(ProtPlug*)gpMySys->PPRealloc(pNewPlug, sizeof(ProtPlug)))
== NULL ) {
gpMySys->PPLogPrintf("Antirelay:
unable to allocate %d bytes of memory",
sizeof(ProtPlug));
gpMySys->PPLogPrintf("Antirelay: new plugin instance
not created");
return NULL;
}
    pNewPlug->pMySession = ps;
pNewPlug->iPassed = 0;
pNewPlug->pszResp = NULL;
    /* if the user wants they can force the use of ip-address */
if (giResolveHostnames) {
pNewPlug->pszMyPeerAddress =
(char*)PPSESSION_GETPROPERTY(ps, "PeerName", NULL);
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: using peer hostname [%s]",
pNewPlug->pszMyPeerAddress);
#endif
    } else {
pNewPlug->pszMyPeerAddress =
(char*)PPSESSION_GETPROPERTY(ps,"PeerIP",NULL);
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: using ip-address [%s]",
pNewPlug->pszMyPeerAddress);
#endif
}
    /* if we fail to find the peer address, there isn't much point */
if (pNewPlug->pszMyPeerAddress == NULL) {
gpMySys->PPLogPrintf("Antirelay: unable to
discover peer address");
gpMySys->PPLogPrintf("Antirelay: new plugin
instance not created");
gpMySys->PPRealloc(pNewPlug,0);
return NULL;
}

/* if there are no domains, the default is "*", so we match */
if (gpSubmissionDomains != NULL) {

#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: comparing peer address %s",
pNewPlug->pszMyPeerAddress);
#endif

/* search the submission list for a pattern that matches our */
/* peer's address or hostname */

pThisEntry = gpSubmissionDomains;

while (pThisEntry != NULL) {

#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: vs %s",
pThisEntry->pszPattern);
#endif

if (PatternMatch((const char*)pNewPlug->pszMyPeerAddress,
(const char*)pThisEntry->pszPattern)) {

#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: allowing relay");
#endif
                /* mark peer as trusted */

pNewPlug->iPassed = 1;
return pNewPlug;
}
pThisEntry = pThisEntry->pNext;
}
}
return pNewPlug;
}

/*
** Function
** --------
** int ProtPlug_ProcessLine(ProtPlug *ppp,
** char *pszLine,char **ppszResp);
**
** Description
** -----------
** function called on a ProtPlug when a line has been received
** in the protocol
**
** Parameters
** ----------
** ProtPlug *ppp: the session pointer
** char *pszLine: the line to be processed, null terminated,
** includes "\r\n"
** char *pszPrevResp: the response given by the previous ProtPlug
** char **ppszResp: (input/output)a pointer a pointer to the desired
** response, must be null-terminated and include
** "\r\n"
**
** Returns
** -------
** PP_SKIP the command is not handled
** PP_CLOSE to terminate the connection after sending the response
**
** Remarks
** -------
** The memory pointed to by pszLine and pszPrevResp is owned by the
** server and may not be modified or freed by the ProtPlug
**
** The memory pointed to by *ppszResp belongs to the ProtPlug
** and need only be valid until the next call to ProcessLine
** or Delete, the server will not modify or free it.
**
** NULL for pszLine is a request for a protocol banner
**
*/
__export int ProtPlug_ProcessLine(ProtPlug *ppp, 
char *pszLine,
char **ppszResp) {
char *pszUser;
char *p;
char *q;
char *r;
char *pAddress;
char *pStartOfString;
HostEntry *pThisEntry;
int iRv = PP_SKIP;
int iLength = 0;
int iThisLength = 0;
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: processing line");
#endif
    /* if pszLine is NULL, this is the banner */
if (pszLine == NULL)
return PP_SKIP;
    /* first see if this is a RCPT TO line */
if (((*pszLine == 'r') || (*pszLine == 'R')) &&
(! STRNCASECMP(pszLine, "rcpt to:", 8))) {
/* if peer is already validated as trusted, just return */
if (ppp->iPassed == 1)
return PP_SKIP;
        /* if authentcation is in use as a criteria and */
/* the client has authenticated, trust them */
if ((giUseAuthinfo == 1) &&
((pszUser =
(char*)PPSESSION_GETPROPERTY(
ppp->pMySession,"AuthSender",NULL))
!= NULL) &&
(*pszUser != '\0')) {
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay:
authenticated user [%s], ",
"skipping", pszUser);
#endif
ppp->iPassed = 1;
return PP_SKIP;
}
        /* if there are no domains, the default is "*", so we match */
if (gpDeliveryDomains == NULL)
return PP_SKIP;
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: comparing recipients
to delivery domains");
#endif
        /* duplicate the line since the memory does not belong */
/* to us and we'll need to muck with it below */
if ( (p = ARStrdup( (char*)(&pszLine[8]))) == NULL) {
gpMySys->PPLogPrintf("Antirelay: failed to copy buffer,
skipping");
return PP_SKIP;
}
        /* walk the list, breaking it up into addresses */
/* on whitespace and commas */
pStartOfString = p;
pAddress = NULL;
    /* the address will look like one of the following:
**
** foo@moof.com\r\n
** <foo@moof.com>\r\n
** <@HOSTA.ARPA,@HOSTB.ARPA:foo@moof.com>\r\n
**
*/
    /* skip to the next non-whitespace, non '<' character */
while((*p != '\0') && ((*p == '<') || (isspace(*p)))) {
p++;
}
        /* skip over the relay host stuff */
if ((q = strchr(p, ':')) != NULL)
p = ++q;
    /* mark this... this is where the address starts */
pAddress = p;
        /* now find the end... a null char, a comma or whitespace */
while( (*p != '\0') && (*p != '>') && (! isspace(*p))) {
p++;
}
        /* make a substring out of this piece */
*p = '\0';
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay:
verifying recipient [%s]", pAddress);
#endif
        /* see if address matches any domains in our list */
pThisEntry = gpDeliveryDomains;
        while (pThisEntry != NULL) {
#ifdef LOCAL_DEBUG
            gpMySys->PPLogPrintf("Antirelay:  vs [%s]",
pThisEntry->pszPattern);
#endif
if (PatternMatch((const char*)pAddress,
(const char*)pThisEntry->pszPattern)) {
/* free up that copy we made of the line */
gpMySys->PPRealloc(pStartOfString,0);
return PP_SKIP;
}
pThisEntry = pThisEntry->pNext;
}
        /* if it falls through to here, they didn't match any pattern */
gpMySys->PPLogPrintf("Antirelay: refusing delivery to ",
"recpient [%s]", pAddress);
        gpMySys->PPRealloc(pStartOfString,0);
        /* if peer is not authenticated and we might trust */
/* peer if it was, say so */
if ((giAdvertiseAuthinfo == 1) &&
(PPSESSION_GETPROPERTY(ppp->pMySession,"AuthName",NULL)
== NULL))
*ppszResp = gpNonLocalAuthResponse;
else
*ppszResp = gpNonLocalResponse;
        return PP_DONE;
}
    /* now see if this is a EHLO line */
if (((*pszLine == 'e') || (*pszLine == 'E')) &&
(! STRNCASECMP(pszLine, "EHLO", 4))) {
if (giStripAuthCapability == 0)
return PP_SKIP;
if (ppp->iPassed == 0)
return PP_SKIP;
#ifdef LOCAL_DEBUG
gpMySys->PPLogPrintf("Antirelay: stripping AUTH extensions");
#endif
        iRv = PPSESSION_DEFERLINE(ppp->pMySession,pszLine,ppszResp);
ppp->pszResp = (char *)gpMySys->PPRealloc(ppp->pszResp,
strlen(*ppszResp));
        /* remove the AUTH stuff we don't want to show */
p = *ppszResp;
while ((p != NULL) && ((q = strstr(p, "\r\n")) != NULL)) {
q+=2; /* move past the CRLF */
            /* if it's not an AUTH line, copy it */
if ((strncmp(p+4, "AUTH=LOGIN", 10)) &&
(strncmp(p+4, "AUTH ", 5))) {
iThisLength = q-p;
iLength += iThisLength;
strncat(ppp->pszResp, p, iThisLength);
}
p = q;
}
        /* now, fix up in case we eliminated the last 250 response */
/* to turn a '250-' into a '250 ' */
r = ppp->pszResp + (iLength-iThisLength);
if ((r != NULL) && (! strncmp(r, "250-", 4)))
r[3] = ' ';
*ppszResp = ppp->pszResp;
return iRv;
}
return PP_SKIP;
}
/*
** Function
** --------
** void ProtPlug_Delete(ProtPlug *ppp);
**
** Description
** -----------
** fries a ProtPlug
**
** Parameters
** ----------
** ProtPlug *ppp: the session to be deleted
**
** Returns
** -------
** nothing
**
** Remarks
** -------
** this function must free all memory associated with the ProtPlug
**
*/
__export void ProtPlug_Delete(ProtPlug *ppp) {
/* remove anything plug-in specific we created */
gpMySys->PPRealloc(ppp->pszResp,0);
gpMySys->PPRealloc(ppp,0);
return;
}
/* 
** Function
** --------
** static int DoMatch(register const char *text,
** register const char *p);
**
** Description
** -----------
** internal pattern matching routine
**
** Parameters
** ----------
** const char *text: the text to match against
** const chap *p: the pattern to match to
**
** Returns
** -------
** a weight indicating the match or 0 indicating no match
** or -1 indicating an abort
**
** Remarks
** -------
** Based on the wildmat code written by Rich $alz <rsalz@bbn.com>.
**
** Match text and p, return match weight, FALSE, or ABORT.
**
** The match "weight" is two times the count of literally matched
** characters plus one times the count of class matched characters
** plus one. Matches on * and ? * do not count towards the weight.
**
** Therefore, when matching "test.foo.bar", the weights would be:
**
** Matching Pattern Weight
**
** * 1
** test.* 11
** test.[abcdef]??.* 14
** test.f??.* 15
** *.foo.bar 17
** test.foo.bar 25
**
*/
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#define ABORT -1
#define NEGATE_CLASS '^'
static int DoMatch(register const char *text, register const char *p)
{
register int last;
register int matched;
register int reverse;
register int weight;
    /* Weight must be non-zero to begin with so that matches */
/* on just "*" will work. */
    weight = TRUE;
for ( ; *p; text++, p++) {
if (*text == '\0' && *p != '*')
return ABORT;
switch (*p) {
case '\\':
/* Literal match with following character. */
p++;
/* FALLTHROUGH */
default:
if (tolower(*text) != tolower(*p))
return FALSE;
weight += 2; /* extra credit for exact matches */
continue;
case '?':
/* Match anything. */
continue;
case '*':
while (*++p == '*')
/* Consecutive stars act just like one. */
continue;
if (*p == '\0')
/* Trailing star matches everything. */
return weight;
while (*text)
if ((matched = DoMatch(text++, p)) > 0) {
matched += weight-1;
return matched;
}
return ABORT;
case '[':
reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
if (reverse)
/* Inverted character class. */
p++;
matched = FALSE;
if (p[1] == ']' || p[1] == '-')
if (*++p == *text) {
weight++;
matched = TRUE;
}
for (last = *p; *++p && *p != ']'; last = *p)
/* This next line requires a good C compiler. */
if (*p == '-' && p[1] != ']'
? *text <= *++p && *text >= last : *text == *p)
matched = TRUE;
if (matched == reverse)
return FALSE;
weight++;
continue;
}
}
if (*text == '\0')
return weight;
return FALSE;
}
/*
** Function
** --------
** static int PatternMatch(const char *text, const char *p)
**
** Description
** -----------
** simple wrapper for pattern patching routine
**
** Parameters
** ----------
** const char *text: the text to match against
** const chap *p: the pattern to match to
**
** Returns
** -------
** an int indicating TRUE(1) or FALSE(0)
**
** Remarks
** -------
** Based on the wildmat code written by Rich $alz <rsalz@bbn.com>.
*/
static int PatternMatch(const char *text, const char *p)
{
int i;
    if ((i = DoMatch(text, p)) > 0)
return i;
return FALSE;
}
/*
** Function
** --------
** static char *ARStrdup(char *s) {
**
** Description
** -----------
** a plug-in-safe version of strdup
**
** Parameters
** ----------
** char *s: the null-terminated string to be duplicated
**
** Returns
** -------
** a pointer to a newly allocated copy of the string s
**
** Remarks
** -------
** this function uses the plug-in system's memory allocation
** routines
**
*/
static char *ARStrdup(char *s) {
char *r;
char *pszNewString = NULL;
int iLength = 1;
r = s;
while (*r != '\0') {
iLength++;
r++;
}
    pszNewString = (char*)(gpMySys->PPRealloc(
pszNewString, iLength));
if (pszNewString == NULL)
return NULL;
strcpy(pszNewString, s);
return(pszNewString);
}

Tracer Plug-in Code
/*
** If you want to see this protocol level plug-in in action,
** you can save, build and run the Protocol Level plug-in
** code in this file. To download, see the
Note at the
** beginning of this chapter.
*/
/*
** NETSCAPE COMMUNICATIONS CORPORATION
**
** Copyright (c) 1998 Netscape Communications Corporation.
** All Rights Reserved.
**
** Use of this Source Code is subject to the terms of the
** applicable license agreement from Netscape
** Communications Corporation.
**
** The copyright notice(s) in this Source Code does not
** indicate actual or intended publication of this Source Code.
**
** Filename
** --------
** tracer.c
**
** Description
** -----------
** SMTP tracing plugin
**
*/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "protplug.h"
#undef SHOWARTDATA
#define MAX_LINE_LENGTH 1024
/* pointer to the plug-in system routines */
static ProtPlugSystem *gpMySys;
/* session-persistent data to pass to */
/* the plug-in each time it is called. */
struct ProtPlugStruct {
PPSession *pMySession;
char *pszMyPeerAddress;
char *pszResp;
};
/*
** Function
** --------
** int PPModule_Init(ProtPlugSystem *pSys);
**
** Description
** -----------
** initialization entry point for a protocol plug-in module library
**
** Parameters
** ----------
** ProtPlgSystem *pSys: structure with function pointers for
** thread synch functions, memory object, etc.
**
** Returns
** -------
** 0 if successful, non-zero otherwise
**
** Remarks
** -------
** if Init() fails, Exit() will not be called.
**
*/
__export int PPModule_Init(ProtPlugSystem *pSys) {
    /* set global pointer to the system information */
/* so plug-in instances can find it later */
    gpMySys = pSys;
    /* announce the plug-in in the server log */
gpMySys->PPLogPrintf("TRC: installing plugin");
    /* return success */
return 0;
}
/*
** Function
** --------
** void PPModule_Exit(void);
**
** Description
** -----------
** for resource cleanup in a PPModule, called after all
** ProtPlugs have been deleted, before the server exits
**
** Parameters
** ----------
** none
**
** Returns
** -------
** nothing
**
** Remarks
** -------
**
*/
__export void PPModule_Exit(void) {
return;
}
/*
** Function
** --------
** ProtPlug *PPModule_NewProtPlug(void);
**
** Description
** -----------
** creates a new ProtPlug, the session analogue of a PPModule
**
** Parameters
** ----------
**
** Returns
** ------
** a new ProtPlug
**
** Remarks
** -------
**
*/
__export ProtPlug *PPModule_NewProtPlug(PPSession *ps) {
ProtPlug *pNewPlug = NULL;
    /* allocate a new ProtPlug object */
    if ( (pNewPlug = 
(ProtPlug*)gpMySys->PPRealloc(pNewPlug,
sizeof(ProtPlug))) == NULL ) {
        gpMySys->PPLogPrintf("TRC: unable to allocate %d bytes 
of memory",
sizeof(ProtPlug));
        gpMySys->PPLogPrintf("TRC: new plugin instance not created");
return NULL;
}
    pNewPlug->pMySession = ps;
    pNewPlug->pszMyPeerAddress =
(char*)PPSESSION_GETPROPERTY(ps,"PeerName",NULL);
    gpMySys->PPLogPrintf("TRC: host [%s] connected", 
pNewPlug->pszMyPeerAddress);
    return pNewPlug;
}
/*
** Function
** --------
** int ProtPlug_ProcessLine(ProtPlug *ppp,
** char *pszLine,char **ppszResp);
**
** Description
** -----------
** function called on a ProtPlug when a line has been received
** in the protocol
**
** Parameters
** ----------
** ProtPlug *ppp: the session pointer
** char *pszLine: the line to be processed, null terminated,
** includes "\r\n"
** char *pszPrevResp: the response given by the previous ProtPlug
** char **ppszResp: (input/output)a pointer a pointer to the desired
** response, must be null-terminated and include
** "\r\n"
**
** Returns
** -------
** PP_SKIP the command is not handled
** PP_CLOSE to terminate the connection after sending
** the response
**
** Remarks
** -------
** The memory pointed to by pszLine and pszPrevResp is owned by
** the server and may not be modified or freed by the ProtPlug.
**
** The memory pointed to by *ppszResp belongs to the ProtPlug
** and need only be valid until the next call to ProcessLine
** or Delete, the server will not modify or free it.
**
** NULL for pszLine is a request for a protocol banner.
**
*/
__export int ProtPlug_ProcessLine(ProtPlug *ppp, 
char *pszLine,
char **ppszResp) {
char acBuffer[MAX_LINE_LENGTH];
char *p;
char *q;
#ifdef SHOWARTDATA
int iRv;
#endif
(void)ppp;
if (pszLine == NULL)
return PP_SKIP;
    q = &acBuffer[0];
strcpy(q, pszLine);
    if ((p = strchr(q, '\r')) != NULL) {
*p = '\0';
}
if ((p = strchr(q, '\n')) != NULL) {
*p = '\0';
}
    gpMySys->PPLogPrintf("TRC: C:[%s]", q);
#ifdef SHOWARTDATA
iRv = PPSESSION_DEFERLINE(ppp->pMySession,pszLine,ppszResp);
    if (*ppszResp != NULL)
gpMySys->PPLogPrintf("TRC: S:[%s]", *ppszResp);
    return iRv;;
#else
if (*ppszResp != NULL)
gpMySys->PPLogPrintf("TRC: S:[%s]", *ppszResp);
    return PP_SKIP;
#endif
}
/*
** Function
** --------
** void ProtPlug_Delete(ProtPlug *ppp);
**
** Description
** -----------
** fries a ProtPlug
**
** Parameters
** ----------
** ProtPlug *ppp: the session to be deleted
**
** Returns
** -------
** nothing
**
** Remarks
** -------
** this function must free all memory associated with the ProtPlug
**
*/
__export void ProtPlug_Delete(ProtPlug *ppp) {
/* remove anything plug-in specific we created */
gpMySys->PPRealloc(ppp,0);
   return;
}
 

© Copyright 1999 Netscape Communications Corp., a subsidiary of America Online, Inc. All rights reserved.