5 messages in net.sourceforge.lists.courier-usersRe: [courier-users] hosteddomains and...
FromSent OnAttachments
Gordon MessmerOct 24, 2006 10:21 am 
Sam VarshavchikOct 24, 2006 3:24 pm 
charles uchuOct 26, 2006 8:23 pm 
David GomillionOct 27, 2006 6:48 am 
Alessandro VeselyOct 27, 2006 11:53 am 
Actions with this message:
Paste this link in email or IM:
Paste this link in email or IM:
Atom feed for this thread
Paste this URL into your reader:
Subject:Re: [courier-users] hosteddomains and aliases capitalizationActions...
From:Alessandro Vesely (ves@tana.it)
Date:Oct 27, 2006 11:53:06 am
List:net.sourceforge.lists.courier-users

David Gomillion wrote:

charles uchu wrote:

I think it is understood that based on the current code in courier that, even if there is an aliased domain set in hosteddomains, that you must again explicitly state this aliasing in the aliases file for addresses that are not mailboxes.

To cut down on the confusion, I always recommend creating a hosted domain, and then create actual mailboxes for each alias I want to create. In it I create a .courier file to forward the mail where ever I want it to go.

That way one can trace which alias has been matched. Although technically it is a full blown mailbox, it would be unfair to count it as such for administrative purposes.

The ONLY aliases I create in the global alias file are those required by the system (like root, postmaster, etc).

However,it is annoying to maintain a bunch of aliases for each domain. I have a script structured more or less like so:

LCLOCAL=`echo $LOCAL | tr '[A-Z]' '[a-z]'` case $LCLOCAL in webmaster|postmaster|admin|abuse|operator|sys|system|root) NEWADDR="me@my.domain";; *) NEWADDR="";; esac if [ "$NEWADDR" = "" ]; then case $HOST in domain1*) # per domain catchall policy...;; # ... esac fi

if [ "$NEWADDR" = "" ]; then exit 64 fi

printf "$NEWADDR\n" exit 0

It's a little extra work, but in the end, I opt for simplicity and maintainability. And it's very easy to script with bash...

Yes. After some years spent trying to match rcptfilters with a dynamic delivery instruction calling the above script from .courier-default, I finally coded the attached replacement for maildropfilter to run the same script in either case.

I'd propose to drop it in a miscellanea "contrib" directory, if Courier had one...

/* * rcptfilter.c - written by vesely in milan on 8 aug 2006 * * Run rcptfilter.sh * gcc -W -O -DNDEBUG -o /usr/local/sbin/rcptfilter rcptfilter.c * gcc -W -Wall -Wno-parentheses -O0 -g -o rcptfilter rcptfilter.c */

#define SCRIPTFILE "rcptfilter.sh"

static char const usage[] = "usage:\n" "\trcptfilter -h\n" "\trcptfilter -D uid/gid -M rcptfilter[-ext] ...\n" "The first format is only useful to print this note.\n" "The second format is used by Courier's local output module if the\n" "maildropfilter configuration file contains the path of this executable.\n" "In this case, rcptfilter changes directory to HOME and looks for file\n" "\"" SCRIPTFILE "\" and runs it (with null input and output, logged error)\n" "and then returns an exit code of 0 if the script exits rc < 50, 1 otherwise.\n" "The script will have argument $1 set to \"RCPT\" and the other arguments\n" "set to whatever was passed to rcptfilter as ellipsis (...). The variables\n" "LOCAL and HOST will be set from ext. The rest of the environment remains.";

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #include <syslog.h> #include <ctype.h> #include <errno.h> #include <assert.h>

static volatile int signal_child = 0, signal_timed_out = 0, signal_break = 0;

static void sig_catcher(int sig) { #if !defined(NDEBUG) char buf[80]; unsigned s = snprintf(buf, sizeof buf, "rcptfilter[%d]: received signal %d\n", (int)getpid(), sig); if (s >= sizeof buf) { buf[sizeof buf - 1] = '\n'; s = sizeof buf; } write(2, buf, s); #endif switch(sig) { case SIGALRM: signal_timed_out = 1; break;

case SIGHUP: case SIGPIPE: case SIGINT: case SIGQUIT: case SIGTERM: signal_break = 1; break;

case SIGCHLD: signal_child = 1; break;

default: break; } }

#if 0 static void reset_signal(void) { struct sigaction act; memset(&act, 0, sizeof act); sigemptyset(&act.sa_mask); act.sa_handler = SIG_DFL;

sigaction(SIGALRM, &act, NULL); sigaction(SIGPIPE, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGCHLD, &act, NULL); } #endif

static void set_signal(void) { struct sigaction act; memset(&act, 0, sizeof act); sigemptyset(&act.sa_mask);

act.sa_handler = sig_catcher; sigaction(SIGALRM, &act, NULL); sigaction(SIGPIPE, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGCHLD, &act, NULL); }

static void addtoenv(char const *name, char const *value) { char *freeonexit = (char*)malloc(strlen(name) + strlen(value) + 1); if (freeonexit) putenv(strcat(strcpy(freeonexit, name), value)); }

static int run_script(char const *ext, char *argv[], char const *home) { int rtc = 0; char *local = strdup(ext); if (local) { int err[2]; char *host = strchr(local, '@');

if (host) *host++ = 0; else host = ""; addtoenv("HOST=", host); addtoenv("LOCAL=", local); free(local);

if (pipe(err) < 0) syslog(LOG_CRIT, "Cannot open pipe: %s\n", strerror(errno)); else { pid_t const pid = fork(); if (pid < 0) syslog(LOG_CRIT, "Cannot fork: %s\n", strerror(errno)); else if (pid) { char buf[2048], *next = &buf[0]; char *const first = &buf[0], *const last = &buf[sizeof buf - 2];

close(err[1]); alarm(30); last[1] = 0; // terminator on forced newline while (signal_timed_out == 0 && signal_break == 0 && signal_child == 0) { int rd = read(err[0], next, last - next); #if !defined NDEBUG printf("rd=%2d, next=%2d\n", rd, next - first); #endif if (rd > 0) { char *p = first, *br; next += rd;

*next = next == last? '\n': 0; // force newline if full

while ((br = strchr(p, '\n')) != NULL) { int level = LOG_CRIT; *br = 0;

/* * e.g., "##This is a warning\n" */ while (*p == '#' && level < LOG_DEBUG) ++p, ++level;

if (*p) syslog(level, "script: %s\n", p); p = br + (br < last); // +1 if not forced newline assert(first <= p && p <= next); }

memmove(first, p, next - p); next -= p - first; assert(first <= next && next < last); } else if (rd == 0 || errno != EINTR && errno != EAGAIN) { if (rd) syslog(LOG_CRIT, "Pipe broken: %s\n", strerror(errno)); break; } } alarm(0); if (signal_timed_out || signal_break) { kill(pid, SIGTERM); } close(err[0]);

for (;;) { int status; pid_t wpid = wait(&status); if (wpid < 0 && errno != EAGAIN && errno != EINTR) { syslog(LOG_CRIT, "Cannot wait %s/" SCRIPTFILE "[%u]: %s\n", home, (unsigned)wpid, strerror(errno)); break; } else if (wpid == pid) { if (WIFEXITED(status)) { int level, s_rtc = WEXITSTATUS(status); switch (s_rtc) { case 0: case 64: case 99: level = LOG_DEBUG; break; default: level = LOG_CRIT; break; } rtc = s_rtc > 50; syslog(level, "%s/" SCRIPTFILE " with %s exited %d, rtc=%d\n", home, ext, s_rtc, rtc); } else if (WIFSIGNALED(status)) { syslog(LOG_CRIT, "%s/" SCRIPTFILE " terminated with signal %d, rtc=%d\n", home, WTERMSIG(status), rtc); } else continue; // stopped?

break; } } } else // child process { close(0); open("/dev/null", O_RDONLY); close(1); open("/dev/null", O_WRONLY); close(2); dup(err[1]); close(err[0]); close(err[1]); closelog(); execv(SCRIPTFILE, argv); syslog(LOG_MAIL|LOG_CRIT, "rcptfilter: cannot execv: %s\n", strerror(errno)); exit(0); } }

} return rtc; }

int main(int argc, char *argv[]) { int rtc = 0; int i, uid = 0, gid = 0, err = 0; char *ext = NULL, *home = getenv("HOME"); static char const argerror[] = "invoked with wrong argument: ";

char *xargv[32]; size_t xargc = 0;

openlog("rcptfilter", LOG_PID, LOG_MAIL); xargv[xargc++] = SCRIPTFILE; xargv[xargc++] = "RCPT";

for (i = 1; i < argc; ++i) { char *a = argv[i]; int pass_it = 1;

if (*a == '-') { pass_it = 0; switch (*++a) { case 'D': if (i + 1 >= argc) { syslog(LOG_CRIT, "%s-D requires a value\n", argerror); ++err; } else { char *t = NULL; a = argv[++i]; uid = strtoul(a, &t, 10); if (t && *t == '/') { gid = strtoul(t + 1, &t, 10); if (t && *t) t = NULL; } else t = NULL; if (t == NULL) { syslog(LOG_CRIT, "%suidgid is %s\n", argerror, a); ++err; } } break;

case 'M': if (i + 1 >= argc) { syslog(LOG_CRIT, "%s-M requires a value\n", argerror); ++err; } else if (strncmp(a = argv[++i], "rcptfilter" , sizeof "rcptfilter" - 1) != 0) { syslog(LOG_CRIT, "%s-M with value %s\n", argerror, a); ++err; } else { ext = strchr(a, '-'); if (ext) ++ext; else ext = ""; } break;

case 'h': puts(usage); return 0;

default: pass_it = 1; break; } }

if (pass_it && xargc + 1 < sizeof xargv/ sizeof xargv[0]) { xargv[xargc++] = a; } }

xargv[xargc] = NULL;

if (geteuid() == 0 && (uid || gid)) { if (gid) setgid(gid); setuid(uid); }

set_signal(); if (home && ext) { struct stat buf; uid_t const me = geteuid(); gid_t const myg = getegid();

if (chdir(home)) { syslog(LOG_CRIT, "Cannot chdir to %s: %s\n", home, strerror(errno)); } else if (stat(SCRIPTFILE, &buf) != 0) { if (errno != ENOENT) syslog(LOG_CRIT, "Cannot stat %s/" SCRIPTFILE ": %s\n", home, strerror(errno)); } else if (!S_ISREG(buf.st_mode) || !( ((S_IXUSR & buf.st_mode) && (me == 0 || me == buf.st_uid)) || ((S_IXGRP & buf.st_mode) && (me == 0 || myg == buf.st_gid)) || (S_IXOTH & buf.st_mode))) { syslog(LOG_INFO, "%s/" SCRIPTFILE " not executable by %d/%d\n", home, (int)me, (int)myg); } else { rtc = run_script(ext, xargv, home); } } else { syslog(LOG_CRIT, "%smissing %s%s%s\n", argerror, home ? "" : "HOME env variable", home || ext ? "" : " and ", ext ? "" : "-M argument"); }

return rtc; }