On Sun, Aug 06, 2006 at 09:21:04AM -0700, Chris Frost wrote:
> For this reason I have written mswatch. mswatch listens for mailbox changes
> from two sets of mailboxes and runs runs a mailbox synchronizer
> (I've developed mswatch using mbsync) when there are mailbox changes.
> This allows mailbox changes to propagate quickly and more efficiently than
> periodically synchronizing the mailboxes.
>
heh ... see attachements (.forward entries & procmail rules not
included). i'm using this for years, but never got around to making
something publicable of it. oh, right, i even published a precursor of it
years ago: http://developer.kde.org/~ossi/sw/fastmail.html
i'll have a look at mswatch when i find time.
> mswatch currently includes one mailbox watcher which watches Maildirs
> - mailbox deletions are not propagated (the deleted mailbox is recreated)
> when "mbsync <DELETED_MAILBOX>" is ran
>
possible todo. it's pretty dangerous, though.
> - maildir mailbox creations are not propagated when "mbsync <NEW_MAILBOX>"
> is ran (mbsync errors)
>
mbsync -C
--
Hi! I'm a .signature virus! Copy me into your ~/.signature, please!
--
Chaos, panic, and disorder - my work here is done.
PROGS := tunneld tunnelk tunnelm
prefix := /usr/local
all: debug
debug:
$(MAKE) CFLAGS+=-DDEBUG ndebug
ndebug: $(PROGS)
clean:
rm -f $(PROGS) tunneld-*.sock tunnelk.log
%: %.o
# $(CC) -s -o $@ $<
$(CC) -o $@ $<
%.o: %.c tunnel.h tunnel.c
$(CC) -O2 $(CFLAGS) -W -Wall -Wmissing-prototypes -Wwrite-strings
-Wshadow -o $@ -c $<
# $(CC) -g3 -W -Wall -Wmissing-prototypes -Wwrite-strings -Wshadow -o $@
-c $<
%.s: %.c tunnel.h tunnel.c
$(CC) -g3 -W -Wall -Wmissing-prototypes -Wwrite-strings -Wshadow -S $<
tunnelm: tunnelk
ln -sf $^ $@
install: tunneld
cp tunneld $(prefix)/sbin
strip $(prefix)/sbin/tunneld
upload ul:
scp Makefile *.c *.h mail:src/mail/tunnel
.SUFFIXES:
static void
Signal (int sig, void (*sh)(void), int flags)
{
struct sigaction sa;
sa.sa_handler = (void (*)(int))sh;
sigemptyset (&sa.sa_mask);
sa.sa_flags = flags;
(void) sigaction (sig, &sa, 0);
}
int now, lping;
static void
reader (void *buf, int count)
{
int ret, rlen;
for (rlen = 0; rlen < count; rlen += ret) {
ret = read(fd_in, (void *)((char *)buf + rlen), count - rlen);
if (ret <= 0)
erraction("Tunnel read error\n");
}
if (rlen != count)
erraction("Tunnel broken\n");
alarm(pongtime);
#ifdef DUMP_COMM
loggen("r:");
for (rlen = 0; rlen < count; rlen++)
printf(" %02x", ((unsigned char *)buf)[rlen]);
printf("\n");
#endif
}
static void
writer (const void *buf, int count)
{
#ifdef DUMP_COMM
int rlen;
#endif
if (write(fd_out, buf, count) != count)
erraction("Tunnel broken\n");
lping = now;
#ifdef DUMP_COMM
loggen("w:");
for (rlen = 0; rlen < count; rlen++)
printf(" %02x", ((unsigned char *)buf)[rlen]);
printf("\n");
#endif
}
typedef enum {
tComm, // tunnel socket
tLst, // listeners
tPreConn, // dns lookup
tConn, // connections
tNot, // notifier socket
} fdType;
static struct pollfd *pollfds;
static struct {
ushort type:4; // fdType
ushort ctype:4; // connType
ushort conn:8; // int
} *fdparms;
static int npolls, rpolls;
typedef enum { cUnused = -1, cClosing = -2 } connState;
static int *conns;
static int nconns;
static int
add_fd(int fd, fdType type, connType ctype, int conn, int ev)
{
int n = npolls++;
if (rpolls < npolls) {
rpolls = npolls;
if (!(pollfds = realloc(pollfds, npolls * sizeof(*pollfds))) ||
!(fdparms = realloc(fdparms, npolls * sizeof(*fdparms))))
die("Out of memory\n");
}
pollfds[n].fd = fd;
pollfds[n].events = ev; /* POLLERR & POLLHUP implicit */
fdparms[n].type = type;
fdparms[n].ctype = ctype;
fdparms[n].conn = conn;
dbg("added fd %d, type %d, ctype %d, conn %d, ev %#x\n",
fd, type, ctype, conn, ev);
return n;
}
static void
close_poll(int n)
{
int i;
if (n >= npolls)
die("Internal error: closing unknown poll\n");
dbg("closing fd %d\n", pollfds[n].fd);
close(pollfds[n].fd);
npolls--;
memcpy(pollfds + n, pollfds + n + 1, (npolls - n) * sizeof(*pollfds));
memcpy(fdparms + n, fdparms + n + 1, (npolls - n) * sizeof(*fdparms));
for (i = 0; i < nconns; i++)
if (conns[i] > n)
conns[i]--;
}
static void
is_valid(int n)
{
if (n >= nconns || conns[n] == cUnused)
die("Internal error: unknown connection\n");
}
static int
is_open(int n)
{
is_valid(n);
return (conns[n] >= 0) && (pollfds[conns[n]].events & POLLIN);
}
//#define DEBUG_PING
typedef enum { pPing, pNotify,
pOpenReq, pOpenOk, pData, pClose } packType;
typedef enum { cDaemon, cForward } connType;
#ifdef DEBUG_PING
# define pingtime 5
# define pongtime 30
#else
# define pingtime 60
# define pongtime 100
#endif
#define MAX_BLOCK_LEN 1024
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
#ifndef DEBUG
# define D_ATTR_UNUSED ATTR_UNUSED
#else
# define D_ATTR_UNUSED
#endif
//#define VALGRIND 1
//#define DUMP_COMM
//#define SPY_SOCKS
//#define NO_DAEMON
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <grp.h>
#include <pwd.h>
#include "tunnel.h"
static inline void idie(const char *msg, ...) ATTR_PRINTFLIKE(1,2) ATTR_NORETURN;
static inline void die(const char *msg, ...) ATTR_PRINTFLIKE(1,2) ATTR_NORETURN;
static inline void loggen(const char *msg, ...) ATTR_PRINTFLIKE(1,2);
static inline void dbg(const char *msg D_ATTR_UNUSED, ...) ATTR_PRINTFLIKE(1,2);
static void
logit(const char *msg, va_list va)
{
time_t tim;
char fmt[256], dbuf[32];
(void) time(&tim);
strftime(dbuf, sizeof(dbuf), "%Y-%m-%d %H:%M:%S", localtime(&tim));
snprintf(fmt, sizeof(fmt), "%s %s", dbuf, msg);
vprintf(fmt, va);
fflush(stdout);
}
static inline void
dbg(const char *msg D_ATTR_UNUSED, ...)
{
#ifdef DEBUG
va_list va;
va_start(va, msg);
logit(msg, va);
va_end(va);
#endif
}
static inline void
loggen(const char *msg, ...)
{
va_list va;
va_start(va, msg);
logit(msg, va);
va_end(va);
}
static void sigterm(void) ATTR_NORETURN;
static inline void
die(const char *msg, ...)
{
va_list va;
va_start(va, msg);
logit(msg, va);
sigterm();
}
static inline void
idie(const char *msg, ...)
{
va_list va;
va_start(va, msg);
vfprintf(stderr, msg, va);
exit(1);
}
#include <setjmp.h>
static sigjmp_buf borked;
#define erraction(msg) do { loggen(msg); siglongjmp(borked, 1); } while(0)
static int fds[2];
#define fd_in fds[1]
#define fd_out fd_in
#include "tunnel.c"
static const char *pidfile = "/var/run/tunneld.pid";
static const char *post_conn, *post_disconn;
static int tun, nodelay, broken;
static char *
makeEnv(const char *name, const char *value)
{
char *result;
if (!(result = malloc((unsigned) (strlen(name) + strlen(value) + 2))))
idie("Out of memory\n");
sprintf(result, "%s=%s", name, value);
return result;
}
static const char *notifier = ".mailinfo";
static struct {
const char *remote, *local;
} *maps;
static int nmaps;
static void
notify_user(const char *user, const char *sig, const char *mbx)
{
char *env[4];
struct passwd *pwd;
struct stat st;
int i;
char mailinfo[128];
for (i = 0; i < nmaps; i++)
if (!strcmp(user, maps[i].remote)) {
user = maps[i].local;
break;
}
if (*user) {
dbg("Running user notification for %s: %s %s\n", user, mbx, sig);
if (!(pwd = getpwnam(user))) {
loggen("Unknown user %s\n", user);
return;
}
} else {
dbg("Running user notification: %s %s\n", mbx, sig);
if (!(pwd = getpwuid(getuid()))) {
loggen("Whoops - current user does not exist\n");
return;
}
}
snprintf(mailinfo, sizeof(mailinfo), "%s/%s", pwd->pw_dir, notifier);
if (stat(mailinfo, &st))
return;
if (!fork()) {
endpwent();
close(0); open("/dev/null", O_RDONLY);
if ((!*user ||
((!initgroups(pwd->pw_name, pwd->pw_gid) &&
!setgid(pwd->pw_gid) && !setuid(pwd->pw_uid)))) &&
!chdir(pwd->pw_dir))
{
env[0] = makeEnv("HOME", pwd->pw_dir);
env[1] = makeEnv("USER", pwd->pw_name);
env[2] = makeEnv("LOGNAME", pwd->pw_name);
env[3] = 0;
execle(mailinfo, mailinfo, sig, mbx, (char *)0, env);
loggen("Cannot exec %s.\n", mailinfo);
}
exit(1);
}
}
static void
killem(void)
{
if (tun) {
kill(tun, SIGTERM);
while (tun)
pause();
}
}
static void
sigterm(void)
{
killem();
unlink(pidfile);
loggen("tunnel daemon exiting ...\n");
exit(0);
}
static void
sigchld(void)
{
pid_t pid;
dbg("SIGCHLD\n");
while ((pid = waitpid(-1, 0, WNOHANG)) > 0) {
dbg("wait returns %d\n", pid);
if (pid == tun) {
dbg("Tunnel command exited.\n");
tun = 0;
broken++;
}
}
}
static void
sighup(void)
{
dbg("SIGHUP received.\n");
nodelay++;
broken++;
}
static const char *waitfile;
static void
sigalrm(void)
{
struct stat st;
if (!waitfile || stat(waitfile, &st))
erraction("Ping timeout.\n");
else
alarm(pongtime);
}
static void
accept_conn(int fd, connType ctype, unsigned char *data, int dlen)
{
int n;
unsigned char buff[256];
if ((fd = accept(fd, 0, 0)) < 0) {
dbg("Accept failed: %s\n", strerror(errno));
return;
}
fcntl(fd, F_SETFD, FD_CLOEXEC);
for (n = 0; n < nconns; n++)
if (conns[n] == cUnused)
goto haven;
if (!(conns = realloc(conns, ++nconns * sizeof(*conns))))
die("Out of memory\n");
haven:
conns[n] = add_fd(fd, tConn, ctype, n, 0);
buff[0] = pOpenReq;
buff[1] = n;
buff[2] = ctype;
memcpy(buff + 3, data, dlen);
writer(buff, 3 + dlen);
}
static void
start_listen (int port, connType ctype, int idx)
{
struct sockaddr_in inaddr;
int fd;
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
idie("Cannot open socket\n");
fcntl(fd, F_SETFL, O_NONBLOCK);
fcntl(fd, F_SETFD, FD_CLOEXEC);
#ifdef VALGRIND
memset(&inaddr, 0, sizeof(inaddr));
#endif
inaddr.sin_family = AF_INET;
inaddr.sin_addr.s_addr = htonl(0x7f000001);
inaddr.sin_port = htons(port);
if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0)
idie("Cannot bind socket to port %d\n", port);
if (listen(fd, 5) < 0)
idie("Cannot listen on port %d\n", port);
(void) add_fd(fd, tLst, ctype, idx, POLLIN);
}
typedef struct {
ushort lport;
const char *rcmd;
} rdaemon;
static rdaemon *rdms;
static int nrdms;
typedef struct {
ushort lport, rport;
const char *rhost;
} forward;
static forward *fwds;
static int nfwds;
#ifdef SPY_SOCKS
# define qput(s) printf(s)
# define qblock(b,l) \
do { \
int i; \
for (i = 0; i < l; i++) { \
char c = (b)[i]; \
if (c == '\r' || c == '\n' || (c >= 32 && c < 127)) \
putchar(c); \
else \
printf("\\%#02ux", c); \
} \
} while(0)
#else
# define qput(s)
# define qblock(b,l)
#endif
static int
serve(void)
{
forward *fwd;
rdaemon *rdm;
register int j, p, c;
int i, connected = 0;
volatile int in_poll;
unsigned char buffer[MAX_BLOCK_LEN];
Signal(SIGALRM, sigalrm, 0);
in_poll = add_fd(fd_in, tComm, 0, 0, POLLIN);
broken = 0;
alarm(pongtime);
lping = 0;
now = time(0);
if (sigsetjmp(borked, 1)) {
bork:
broken++;
} else
for (;;) {
if (lping + pingtime <= now) {
buffer[0] = pPing;
writer(buffer, 1);
}
i = poll(pollfds, npolls, (lping - now + pingtime) * 1000);
if (broken)
break;
now = time(0);
if (!i)
continue;
if (i < 0) {
if (errno == EINTR)
continue;
else
die("Poll failed: %s\n", strerror(errno));
}
for (i = 0; i < npolls; i++) {
if (pollfds[i].revents & POLLNVAL)
die("Internal error: polling invalid fd\n");
else if (pollfds[i].revents & (POLLERR | POLLHUP)) {
dbg("error on fd %d, type %d, ctype %d\n", pollfds[i].fd, fdparms[i].type, fdparms[i].ctype);
switch (fdparms[i].type) {
case tComm:
goto bork;
case tLst:
die("Internal error: listening socked broken\n");
case tConn:
p = i;
c = fdparms[p].conn;
reqclose:
buffer[0] = pClose;
buffer[1] = c;
writer(buffer, 2);
close_poll(p);
conns[c] = cClosing;
goto next;
}
} else if (pollfds[i].revents & POLLIN) {
dbg("input on fd %d, type %d, ctype %d\n", pollfds[i].fd, fdparms[i].type, fdparms[i].ctype);
switch (fdparms[i].type) {
case tComm:
reader(buffer, 1);
switch(buffer[0]) {
case pData:
reader(buffer + 1, 3);
j = buffer[2] | (buffer[3] << 8);
reader(buffer + 4, j);
c = buffer[1];
if (is_open(c)) {
qput("> ");
qblock(buffer + 4, j);
p = conns[c];
if (write(pollfds[p].fd, buffer + 4, j) != j)
goto reqclose;
}
break;
case pOpenOk:
reader(buffer + 1, 1);
c = buffer[1];
is_valid(c);
p = conns[c];
pollfds[p].events = POLLIN;
break;
case pClose: // req, ack & openfail
reader(buffer + 1, 1);
c = buffer[1];
is_valid(c);
if (conns[c] >= 0) {
writer(buffer, 2);
close_poll(conns[c]);
}
conns[c] = cUnused;
goto next;
case pNotify:
reader(buffer + 1, 3);
j = (int)buffer[1] + (int)buffer[2] + (int)buffer[3];
reader(buffer + 4, j);
notify_user((char *)buffer + 4,
(char *)buffer + 4 + buffer[1],
(char *)buffer + 4 + buffer[1] + buffer[2]);
break;
case pPing:
if (!connected) {
connected = 1;
if (post_conn) {
dbg("running post-connect command\n");
system(post_conn);
}
}
break;
} // switch(buffer[0])
break;
case tLst:
switch(fdparms[i].ctype) {
case cDaemon:
rdm = rdms + fdparms[i].conn;
j = strlen(rdm->rcmd) + 1;
buffer[0] = j;
memcpy(buffer + 1, rdm->rcmd, j);
j++;
break;
case cForward:
fwd = fwds + fdparms[i].conn;
buffer[0] = fwd->rport & 255;
buffer[1] = fwd->rport >> 8;
j = strlen(fwd->rhost) + 1;
buffer[2] = j;
memcpy(buffer + 3, fwd->rhost, j);
j += 3;
break;
default:
die("Internal error: unknown connection type %d\n", fdparms[i].ctype);
}
accept_conn(pollfds[i].fd, fdparms[i].ctype, buffer, j);
goto next;
case tConn:
p = i;
c = fdparms[i].conn;
j = read(pollfds[p].fd, buffer + 4, sizeof(buffer) - 4);
if (!j)
goto reqclose;
if (j > 0) {
if (is_open(c)) {
qput("< ");
qblock(buffer + 4, j);
buffer[0] = pData;
buffer[1] = c;
buffer[2] = j & 255;
buffer[3] = j >> 8;
writer(buffer, j + 4);
}
}
break;
} // switch(type)
} // if(readable)
} // i++
next: ;
} // for(;;)
alarm(0);
for (i = 0; i < nconns; i++) {
if (conns[i] >= 0)
close_poll(conns[i]);
}
nconns = 0;
close_poll(in_poll);
if (connected && post_disconn) {
dbg("running post-disconnect command\n");
system(post_disconn);
}
return connected;
}
static char *
istrdup(const char *str)
{
char *es;
if (!(es = strdup(str)))
idie("Out of memory\n");
return es;
}
static void
parse_lforward(const char *str)
{
char *s, *es;
int n = nfwds++;
if (!(fwds = realloc(fwds, nfwds * sizeof(*fwds))))
idie("Out of memory\n");
fwds[n].rhost = "";
es = istrdup(str);
if ((s = strsep(&es, ":"))) {
if ((fwds[n].lport = atoi(s)) > 0) {
if ((s = strsep(&es, ":"))) {
if (*s)
fwds[n].rhost = istrdup(s);
if ((s = strsep(&es, ":"))) {
if ((fwds[n].rport = atoi(s)) > 0) {
free(es);
return;
}
}
}
}
}
idie("Invalid port forward specification %s\n", str);
}
static void
parse_daemon(const char *str)
{
char *s, *es;
int n = nrdms++;
if (!(rdms = realloc(rdms, nrdms * sizeof(*rdms))))
idie("Out of memory\n");
es = istrdup(str);
if ((s = strsep(&es, ":"))) {
if ((rdms[n].lport = atoi(s)) > 0) {
if ((s = strsep(&es, ":"))) {
rdms[n].rcmd = istrdup(s);
free(es);
return;
}
}
}
idie("Invalid remote daemon specification %s\n", str);
}
static void
parse_mapping(const char *str)
{
char *s, *es;
int n = nmaps++;
if (!(maps = realloc(maps, nmaps * sizeof(*maps))))
idie("Out of memory\n");
es = istrdup(str);
if ((s = strsep(&es, ":"))) {
if ((maps[n].remote = istrdup(s))) {
if ((s = strsep(&es, ":"))) {
maps[n].local = istrdup(s);
free(es);
return;
}
}
}
idie("Invalid user mapping specification %s\n", str);
}
static char **
parseCmd(const char *ostr)
{
char **arr, *sp, *nstr, *str, *dstr;
int nst;
if (!(arr = malloc (sizeof(char *))) ||
!(nstr = str = strdup(ostr)))
idie("Out of memory\n");
dbg("parsing '%s'\n", str);
for (nst = 0, sp = dstr = 0;;) {
char c = *str++;
if (!c || isspace(c)) {
if (sp) {
dbg(" word is '%.*s'\n", dstr - sp, sp);
if (!(arr = realloc (arr, (nst + 2) * sizeof(char *))) ||
!(arr[nst] = strndup(sp, dstr - sp)))
idie("Out of memory\n");
arr[++nst] = 0;
sp = 0;
}
if (!c) {
free (nstr);
return arr;
}
} else {
if (!sp)
dstr = sp = str - 1;
if (c == '\'') {
for (;;) {
c = *str++;
if (!c)
idie("Unexpected EOS in '-quote: '%s'.\n", ostr);
if (c == '\'')
break;
*dstr++ = c;
}
} else if (c == '"') {
for (;;) {
c = *str++;
if (!c)
idie("Unexpected EOS in \"-quote: '%s'.\n", ostr);
if (c == '"')
break;
if (c == '\\') {
c = *str++;
if (!c)
idie("Unexpected EOS in \\-quote: '%s'\n", ostr);
}
*dstr++ = c;
}
} else {
if (c == '\\') {
c = *str++;
if (!c)
idie("Unexpected EOS in \\-quote: '%s'\n", ostr);
}
*dstr++ = c;
}
}
}
}
static char **
parse_tunnel(const char *tunnel, const char *client)
{
char tunbuf[1024];
if (sizeof(tunbuf) <=
(unsigned)snprintf(tunbuf, sizeof(tunbuf), tunnel, client))
idie("Tunnel command too long\n");
return parseCmd (tunbuf);
}
int
main(int argc ATTR_UNUSED, char **argv)
{
char **argp;
const char *logfile = "/dev/tty11";
const char *client = "bin/tunnelk";
const char *tunnel = 0;
#ifndef NO_DAEMON
FILE *f;
#endif
int i;
for (argp = argv + 1; *argp; argp++) {
if (!strcmp(*argp, "-l")) // log file
logfile = *++argp;
else if (!strcmp(*argp, "-p")) // pid file
pidfile = *++argp;
else if (!strcmp(*argp, "-L")) // local port -> remote port
parse_lforward(*++argp);
else if (!strcmp(*argp, "-D")) // local port -> remote daemon
parse_daemon(*++argp);
else if (!strcmp(*argp, "-e")) // remote side tunnel executable
client = *++argp;
else if (!strcmp(*argp, "-t")) // tunnel command
tunnel = *++argp;
else if (!strcmp(*argp, "-n")) // mail arrived notification command
notifier = *++argp;
else if (!strcmp(*argp, "-m")) // map remote:local user
parse_mapping(*++argp);
else if (!strcmp(*argp, "-c")) // post-connect program
post_conn = *++argp;
else if (!strcmp(*argp, "-d")) // post-disconnect program
post_disconn = *++argp;
else if (!strcmp(*argp, "-w")) // ssh is waiting for user input indicator
waitfile = *++argp;
else
idie("Unknown command line option '%s'\n", *argp);
}
if (!tunnel)
idie("You must specify a tunnel command (-t \"cmd\")\n");
if (!strstr(tunnel, "%s"))
idie("Tunnel command must contain '%%s'\n");
argp = parse_tunnel (tunnel, client);
for (i = 0; i < nfwds; i++)
start_listen(fwds[i].lport, cForward, i);
for (i = 0; i < nrdms; i++)
start_listen(rdms[i].lport, cDaemon, i);
#ifndef NO_DAEMON
close(0);
open("/dev/null", O_RDONLY);
close(1);
if (open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0666) != 1)
idie("Cannot open log file\n");
dup2(1, 2);
if (fork())
exit(0);
setsid();
if ((f = fopen(pidfile, "w"))) {
fprintf(f, "%d\n", getpid());
fclose(f);
}
#else
Signal(SIGINT, sigterm, 0);
#endif
Signal(SIGTERM, sigterm, 0);
Signal(SIGHUP, sighup, 0);
Signal(SIGCHLD, sigchld, SA_RESTART);
Signal(SIGPIPE, (void (*)(void))SIG_IGN, 0);
dbg("tunnel daemon started\n");
for (;;) {
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds) < 0) {
loggen("socketpair() failed\n");
exit(1);
}
if ((tun = fork()) < 0) {
loggen("fork() failed\n");
exit(1);
} else if (!tun) {
dbg("Running tunnel command.\n");
dup2(fds[0], 1); dup2(fds[0], 0); close(fds[0]); close(fds[1]);
execvp(*argp, argp);
loggen("Cannot exec '%s'.\n", *argp);
exit(1);
}
close(fds[0]);
fcntl(fds[1], F_SETFD, FD_CLOEXEC);
if (serve())
nodelay++;
killem();
if (nodelay)
nodelay = 0;
else {
if (!broken)
loggen("Tunnel command exited unexpectedly.\n");
loggen("Sleeping 10 minutes ...\n");
sleep(600);
loggen("Woken up.\n");
}
}
return 0;
}
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <glob.h>
#include "tunnel.h"
#ifdef DEBUG
FILE *errfile;
#endif
static inline void dbg(const char *msg D_ATTR_UNUSED, ...) ATTR_PRINTFLIKE(1,2);
static inline void die(const char *msg, ...) ATTR_PRINTFLIKE(1,2) ATTR_NORETURN;
static inline void
dbg(const char *msg D_ATTR_UNUSED, ...)
{
#ifdef DEBUG
va_list va;
va_start(va, msg);
vfprintf(errfile, msg, va);
va_end(va);
if (strchr(msg, '\n'))
fflush(errfile);
#endif
}
static inline void
die(const char *msg D_ATTR_UNUSED, ...)
{
#ifdef DEBUG
va_list va;
va_start(va, msg);
vfprintf(errfile, msg, va);
#endif
exit(1);
}
#define erraction(msg) die(msg)
#define fd_in 0
#define fd_out 1
#include "tunnel.c"
static void
sigchld(void)
{
while (waitpid(-1, 0, WNOHANG) > 0);
}
static void
sigalrm(void)
{
die("Ping timeout\n");
}
static int
tcp_connect(struct in_addr addr, u_short port)
{
struct sockaddr_in server_in;
int s;
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
return -1;
fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
server_in.sin_family = AF_INET;
server_in.sin_addr = addr;
server_in.sin_port = port;
connect(s, (struct sockaddr *)&server_in, sizeof(server_in));
return s;
}
static void
set_conn(int conn, int p)
{
if (conn > nconns)
die("Invalid connection index\n");
if (conn == nconns)
if (!(conns = realloc(conns, ++nconns * sizeof(*conns))))
die("Out of memory\n");
conns[conn] = p;
}
static char **
splitargs(const char *string)
{
const char *word;
char **argv;
int nstrs;
char ch;
if (!(argv = malloc (sizeof(char *))))
die("Out of memory\n");
nstrs = 0;
for (word = string; ; ++string) {
ch = *string;
if (!ch || ch == ' ' || ch == '\t') {
if (word != string) {
if (!(argv = realloc(argv, (nstrs + 2) * sizeof(char *))) ||
!(argv[nstrs++] = strndup(word, string - word)))
die("Out of memory\n");
}
if (!ch) {
argv[nstrs] = 0;
return argv;
}
word = string + 1;
}
}
}
static inline int
init_not(void)
{
int s;
if ((s = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0)
die("Cannot create notifier socket\n");
return s;
}
static int notsock, orgpid;
static struct sockaddr_un notsa;
static void
close_not(void)
{
if (orgpid == getpid()) {
unlink(notsa.sun_path);
dbg("unlinked %s at exit\n", notsa.sun_path);
}
}
static inline int
creat_not(void)
{
notsock = init_not();
notsa.sun_family = AF_UNIX;
snprintf(notsa.sun_path, sizeof(notsa.sun_path), "notify-%d.sock", getpid());
unlink(notsa.sun_path);
dbg("unlinked %s at init\n", notsa.sun_path);
if (bind(notsock, ¬sa, sizeof(notsa)))
die("Cannot bind notifier socket\n");
orgpid = getpid();
atexit(close_not);
return notsock;
}
static void
serve(const char *pnam)
{
char **argv;
register int j, p, c;
int i, sfds[2];
struct {
struct in_addr addr;
ushort port;
} adbuf;
unsigned char buffer[MAX_BLOCK_LEN];
Signal(SIGCHLD, sigchld, SA_RESTART);
Signal(SIGALRM, sigalrm, 0);
Signal(SIGPIPE, (void (*)(void))SIG_IGN, 0);
add_fd(fd_in, tComm, 0, 0, POLLIN);
add_fd(fd_out, tComm, 0, 0, 0);
add_fd(creat_not(), tNot, 0, 0, POLLIN);
if (!fork()) {
int pnl = strlen(pnam);
char *buf = malloc(pnl + 6);
if (buf) {
memcpy(buf, pnam, pnl);
memcpy(buf + pnl, ".init", 6);
execl(buf, buf, 0);
}
}
alarm(pongtime);
lping = 0;
now = time(0);
for (;;) {
if (lping + pingtime <= now) {
buffer[0] = pPing;
writer(buffer, 1);
}
i = poll(pollfds, npolls, (lping + pingtime - now) * 1000);
now = time(0);
if (!i)
continue;
if (i < 0) {
if (errno == EINTR)
continue;
else
die("Poll failed: %s\n", strerror(errno));
}
for (i = 0; i < npolls; i++) {
if (pollfds[i].revents & POLLNVAL)
die("Internal error: polling invalid fd\n");
else if (pollfds[i].revents & (POLLERR/* | POLLHUP*/)) {
switch (fdparms[i].type) {
case tComm:
die("Tunnel broken\n");
case tPreConn:
case tConn:
p = i;
c = fdparms[p].conn;
dbg("error on connection %d\n", c);
reqclose:
dbg("requesting close of connection %d\n", c);
buffer[0] = pClose;
buffer[1] = c;
writer(buffer, 2);
close_poll(p);
conns[c] = cClosing;
goto next;
}
} else if (pollfds[i].revents & POLLIN) {
switch (fdparms[i].type) {
case tComm:
reader(buffer, 1);
switch(buffer[0]) {
case pData:
reader(buffer + 1, 3);
j = buffer[2] | (buffer[3] << 8);
reader(buffer + 4, j);
c = buffer[1];
if (is_open(c)) {
p = conns[c];
dbg("writing %d bytes to connection %d\n", j, fdparms[i].conn);
if (write(pollfds[p].fd, buffer + 4, j) != j)
goto reqclose;
}
break;
case pOpenReq:
reader(buffer + 1, 2);
c = buffer[1];
switch(buffer[2]) {
case cForward:
reader(buffer + 3, 3);
reader(buffer + 6, buffer[5]);
p = buffer[3] | (buffer[4] << 8);
dbg("forward request %s:%hd, connection %d\n", buffer + 6, p, c);
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sfds) < 0 ||
((j = fork()) < 0 && (close(sfds[0]), close(sfds[1]), 1))) {
dbg("failed\n");
buffer[0] = pClose;
//buffer[1] = c;
writer(buffer, 2);
set_conn(c, cClosing);
} else {
if (!j) {
struct hostent *he;
dbg("looking up '%s'\n", buffer + 6);
if (!buffer[6]) {
dbg("empty host name\n");
adbuf.addr.s_addr = htonl(0x7f000001);
adbuf.port = htons(p);
send(sfds[1], &adbuf, sizeof(adbuf), 0);
} else if ((he = gethostbyname(buffer + 6)) &&
he->h_length == 4)
{
dbg("lookup ok\n");
adbuf.addr = *(struct in_addr *)he->h_addr;
adbuf.port = htons(p);
send(sfds[1], &adbuf, sizeof(adbuf), 0);
}
dbg("lookup done\n");
exit(0);
}
close(sfds[1]);
set_conn(c, add_fd(sfds[0], tPreConn, buffer[2], c, POLLIN));
goto next;
}
break;
case cDaemon:
reader(buffer + 3, 1);
reader(buffer + 4, buffer[3]);
dbg("remode daemon request \"%s\", connection %d\n", buffer + 4, c);
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sfds) < 0 ||
((j = fork()) < 0 && (close(sfds[0]), close(sfds[1]), 1))) {
dbg("failed\n");
buffer[0] = pClose;
//buffer[1] = c;
writer(buffer, 2);
set_conn(c, cClosing);
} else {
if (!j) {
dup2(sfds[1], 0); dup2(0, 1); dup2(0, 2);
close(sfds[0]); close(sfds[1]);
argv = splitargs(buffer + 4);
execv(argv[0], argv);
die("Cannot execute %s\n", argv[0]);
}
close(sfds[1]);
set_conn(c, add_fd(sfds[0], tConn, buffer[2], c, POLLOUT));
goto next;
}
break;
}
break;
case pClose: // req, ack
reader(buffer + 1, 1);
c = buffer[1];
dbg("close request for connection %d\n", c);
is_valid(c);
if (conns[c] >= 0) {
writer(buffer, 2);
close_poll(conns[c]);
}
conns[c] = cUnused;
goto next;
case pPing:
break;
} // switch(buffer[0])
break;
case tPreConn:
c = fdparms[i].conn;
dbg("connecting %d\n", c);
if (recv(pollfds[i].fd, &adbuf, sizeof(adbuf), 0) != sizeof(adbuf) ||
(j = tcp_connect(adbuf.addr, adbuf.port)) < 0)
{
dbg("connect %d failed\n", c);
buffer[0] = pClose;
buffer[1] = c;
writer(buffer, 2);
set_conn(c, cClosing);
close_poll(i);
goto next;
}
else
{
close(pollfds[i].fd);
fdparms[i].type = tConn;
pollfds[i].fd = j;
pollfds[i].events = POLLOUT;
}
break;
case tConn:
dbg("reading connection %d\n", fdparms[i].conn);
p = i;
c = fdparms[i].conn;
j = read(pollfds[p].fd, buffer + 4, sizeof(buffer) - 4);
dbg("%d bytes\n", j);
if (!j)
goto reqclose;
if (j > 0) {
if (is_open(c)) {
buffer[0] = pData;
buffer[1] = c;
buffer[2] = j & 255;
buffer[3] = j >> 8;
writer(buffer, j + 4);
}
}
break;
case tNot:
c = recv(pollfds[i].fd, buffer + 4, sizeof(buffer) - 4, 0);
if (c > 0)
{
buffer[0] = pNotify;
buffer[1] = strlen(buffer + 4) + 1;
buffer[2] = strlen(buffer + 4 + buffer[1]) + 1;
buffer[3] = strlen(buffer + 4 + buffer[1] + buffer[2]) + 1;
writer(buffer, c + 4);
}
break;
} // switch(type)
} else if (pollfds[i].revents & POLLOUT) {
switch (fdparms[i].type) {
case tConn:
dbg("connection %d established\n", fdparms[i].conn);
buffer[0] = pOpenOk;
buffer[1] = fdparms[i].conn;
writer(buffer, 2);
pollfds[i].events = POLLIN;
break;
} // switch(type)
} // if(writeable)
} // i++
next: ;
} // for(;;)
}
static void
notify(const char *user, const char *sig, const char *mbox)
{
int s, ul, sl, ml;
unsigned i;
glob_t globbuf;
struct sockaddr_un sa;
char buff[758];
dbg("notifying '%s' about mail in '%s' with signal '%s'\n", user, mbox, sig);
if ((ul = strlen(user) + 1) > 255 ||
(sl = strlen(sig) + 1) > 255 ||
(ml = strlen(mbox) + 1) > 255)
die("Invalid notification\n");
memcpy(buff, user, ul);
memcpy(buff + ul, sig, sl);
memcpy(buff + ul + sl, mbox, ml);
s = init_not();
sa.sun_family = AF_UNIX;
if (glob("notify-*.sock", GLOB_ERR | GLOB_NOSORT, 0, &globbuf))
dbg("Nobody to notify\n");
else
for (i = 0; i < globbuf.gl_pathc; i++) {
sprintf(sa.sun_path,
"%.*s", (int)sizeof(sa.sun_path) - 1, globbuf.gl_pathv[i]);
if (sendto(s, buff, ul + sl + ml, MSG_DONTWAIT, &sa, sizeof(sa)) < 0)
dbg("Send to notifier socket %s failed\n", sa.sun_path);
}
}
int main(int argc, char **argv)
{
const char *prog;
#ifdef DEBUG
errfile = fopen("tunnelk.log", "a");
#endif
prog = strrchr(argv[0], '/');
if (prog)
prog++;
else
prog = argv[0];
if (!strcmp(prog, "tunnelk")) {
if (argc == 1)
serve(argv[0]);
else
fprintf(stderr, "Tunnel daemon login-server-side part. Don't invoke manually!\n");
} else if (!strcmp(prog, "tunnelm")) {
if (argc == 4)
notify(argv[1], argv[2], argv[3]);
else
fprintf(stderr, "Usage: tunnelm <user> <signal> <mailbox>\n");
} else
fprintf(stderr, "Must be named tunnelm or tunnelk!\n");
return 0;
}
#! /bin/bash
# -x
#exec </dev/null >>$(me).log 2>&1;echo;date
ISYNC=/usr/local/bin/isync
# pattern throttle maxthrottle sleep
mailclasses="
inbox 3 10 0
*-null 60 180 300
* 20 120 240
"
maxfetchtime=320
maildir=`sed -ne 's, ~/, '"$HOME"'/,;s/^Maildir *\([^ ]\+\) *$/\1/p' ~/.isyncrc`
getpid()
{
# bash bug: $$ won't be updated in a forked process [()&]
bash -c 'echo $PPID'
}
mysleep()
{
sleep $1 & pid=$!
trap 'kill $pid; exit' 15
wait $pid
trap - 15
}
getlock()
{
if ! lockfile -s 0 -r 0 -l $maxfetchtime ${lf[$1]}.lock 2>/dev/null
then
if test $force; then
pid=$(cat <${lf[$1]}.pid 2>/dev/null)
test -z "$pid" -o "$pid" = 0 && return 1
kill $pid
rm ${lf[$1]}.pid
else
return 1
fi
fi
}
freelock()
{
force=
cont=$((${delay2[i]}+$now-$(date '+%s')))
if ((cont > 0)); then
getpid > ${lf[i]}.pid
mysleep $cont ${lf[i]}.pid
rm ${lf[$i]}.pid
fi
rm -f ${lf[i]}.lock
test -f ${lf[i]}.need # must be last!
}
throttle()
{
if test -z "$force" -a ${delay1[$1]} != 0; then
getpid > ${lf[$1]}.pid
expire=$(($now+${maxdelay[$1]}))
while :; do
test $(date -r ${lf[$1]}.need '+%s') -lt $(($now-${delay1[$1]})) && break
test $now -lt $expire || break
mysleep ${delay1[$1]}
touch ${lf[$1]}.lock
now=$(date '+%s')
done
rm ${lf[$1]}.pid
mysleep 1
fi
}
getonly=
force=
bgnd=
info=
unset boxes
while test "$1"; do
case $1 in
-f) force=1; shift;;
-g) getonly=-f; shift;;
-b) bgnd=1; shift;;
-i) info=$2; shift 2;;
-a) IFS=$'\n'; boxes=($($ISYNC -ql)); IFS=$' \t\n'; shift;;
-*) echo "unknown option '$1'"; exit 1;;
*) break;;
esac
done
test -z "$1" && set -- "[EMAIL PROTECTED]"
test -z "$1" && echo "no mailbox specified" && exit 1
n=0
while test -n "$1"; do
mbox[n]=$1
lf[n]=~/tmp/getmail/$1
mbe[n]=$maildir/$1/isyncerror
eval `echo "${mailclasses:1:$((${#mailclasses}-2))}" |
while read pat tdelay1 tmaxdelay tdelay2; do
case $1 in
$pat)
echo "delay1[n]=$tdelay1 maxdelay[n]=$tmaxdelay delay2[n]=$tdelay2"
break
;;
esac
done`
shift
echo "$info" >> ${lf[n]}.need
test -n "$getonly" || touch ${lf[n]}.need.full
getlock $n || continue
((n++))
done
test $n = 0 && exit
now=$(date '+%s')
if test -z "$bgnd"; then
for i in "[EMAIL PROTECTED]"; do
mv $i.need $i.doing
test -z "$getonly" && rm -f $i.need.full
done
$ISYNC -C $getonly "[EMAIL PROTECTED]"
for i in "[EMAIL PROTECTED]"; do
. $i.doing
rm $i.doing
done
fi
for ((i=0; i<n; i++)); do
(
test -n "$bgnd" || { freelock && getlock $i; } || exit
while :; do
if test -f ${mbe[i]}; then
freelock
exit
fi
now=$(date '+%s')
throttle $i
mv ${lf[i]}.need ${lf[i]}.doing
if test -f ${lf[i]}.need.full; then
rm ${lf[i]}.need.full
getonly=
else
getonly=-f
fi
if ! log=$($ISYNC -Cq $getonly ${mbox[i]} 2>&1); then
if ! echo "$log" | egrep -q -i "(unexpected EOF|Connection
refused|Connection reset by peer|Connection timed out|channel .* is locked\$)";
then
echo "$log" > ${mbe[i]}
echo "$log" | mail -s "getmail failed for '${mbox[i]}'" $(id -nu)
fi
cat ${lf[i]}.doing >> ${lf[i]}.need
rm ${lf[i]}.doing
test -z "$getonly" && touch ${lf[i]}.need.full
freelock
exit
fi
. ${lf[i]}.doing
rm ${lf[i]}.doing
freelock || exit
getlock $i || exit
done
) &
done
#! /bin/bash
# -x
#exec </dev/null >>$(me).log 2>&1;echo;date
exec <>/dev/null >&0 2>&1
export PATH=$HOME/bin:$PATH
if [ $2 = .cabot-mail ]; then
exec fetchmail -f ~/.fetchmailrc.cabot -r $2
fi
if test ${2:0:5} = /var/; then
mbox=inbox
else
mbox=${2##*/}
fi
if test "x$1" = "x<delete>"; then
exec getmail -b "$mbox"
else
exec getmail -g -b -i "~/.mailinfo '$2 (remote)' '$1' &" "$mbox"
fi
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel