--- CHANGES.spamthrottle Tue May 5 16:32:27 1998 +++ CHANGES.spamthrottle Tue Feb 26 12:46:48 2002 @@ -0,0 +1,54 @@ +20020226 documentation says spamthrottlercpt defaults to 0, but it wasn't + (fixed code to match documentation) + obviously the non-libtai time code hasn't been tested since + it was rewritten: + time_sub() calculated t2-t1 (should be t1-t2) + time_pack() didn't preserve usec's leading zeroes (added fmt_ulong0()) + reading times from an empty file resulted in a crash + added spam.h dependencies in Makefile + public release 1.03 + +20020219 forgot to flush continuation lines in teergrube code + public release 1.02 + +20020219 fix time(tai)-related bugs in reading/writing taia values + (thanks to Roland Chan) + add entry to qmail-spamthrottle(9) man page with a simple perl + expression to convert packed taia values to tai64n timestamps + public release 1.01 + +20020218 add teergrube support (now 1.01 release) {dw} + remove all strerr_warn... calls as they cause problems + with qmail's sendmail and the -bs switch (as used by pine + for instance) + remove dead/redundant code {dw} + +20020213 public release 1.00 {dw,jl,mk} + +20011224 fix fpe error + +20011213 release 1.00 candidate complete + +20010923 new formulation, code restructure, man pages updated + +20010319 spam wrap open/lock calls + +20010313 wrap tai handlers so tai isn't required + +20010311 remove an extra CRLF in EHLO response + short circuit when sleep time would be zero + +20010309 RFC 2920 compliance (don't advertise PIPELINING with stflush on) + add fcntl() style lock module for compatibility + make new control functions return values + lock file is separate from time/wait files + +20010227 update man pages with spamthrottle info + add spamthrottleflush/spamthrottlemax support + add warning in readme about libtai dependency + +20010221 remove testing code + add spamthrottledir support (so IPs can be grouped) + (the '.' means new subdir semantic still enforced though) + +20010220 alpha implementation --- FILES 2001/02/19 20:16:31 +++ FILES 2002/02/26 17:46:46 @@ -431,3 +431,15 @@ tcp-environ.5 constmap.h constmap.c +README.spamthrottle +fmt_ulong0.c +time.c +time.h +control_time.c +lock_exfcntl.c +lock_unfcntl.c +spam.c +tryltai.c +trytai.c +open_rw.c +qmail-spamthrottle.9 --- Makefile 2001/02/19 20:16:31 +++ Makefile 2002/02/26 17:45:58 @@ -358,9 +358,14 @@ control.o: \ compile control.c readwrite.h open.h getln.h stralloc.h gen_alloc.h \ -substdio.h error.h control.h alloc.h scan.h +substdio.h error.h control.h alloc.h scan.h fmt.h ./compile control.c +control_time.o: \ +compile control_time.c readwrite.h open.h stralloc.h gen_alloc.h \ +substdio.h error.h control.h alloc.h time.h hastai.h + ./compile control_time.c + date822fmt.o: \ compile date822fmt.c datetime.h fmt.h date822fmt.h ./compile date822fmt.c @@ -577,6 +582,10 @@ compile fmt_ulong.c fmt.h ./compile fmt_ulong.c +fmt_ulong0.o: \ +compile fmt_ulong0.c fmt.h + ./compile fmt_ulong0.c + fmtqfn.o: \ compile fmtqfn.c fmtqfn.h fmt.h auto_split.h ./compile fmtqfn.c @@ -608,10 +617,10 @@ ./compile forward.c fs.a: \ -makelib fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o fmt_ulong.o \ +makelib fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o fmt_ulong.o fmt_ulong0.o \ scan_ulong.o scan_8long.o ./makelib fs.a fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o \ - fmt_ulong.o scan_ulong.o scan_8long.o + fmt_ulong.o fmt_ulong0.o scan_ulong.o scan_8long.o getln.a: \ makelib getln.o getln2.o @@ -685,6 +694,13 @@ hasshsgr.h rm -f tryshsgr.o tryshsgr +hastai.h: \ +trytai.c compile load + ( ( ./compile trytai.c && ./load trytai -ltai ) >/dev/null \ + 2>&1 \ + && echo \#define HASTAI 1 || exit 0 ) > hastai.h + rm -f trytai.o trytai + haswaitp.h: \ trywaitp.c compile load ( ( ./compile trywaitp.c && ./load trywaitp ) >/dev/null \ @@ -816,8 +832,8 @@ chmod 755 load lock.a: \ -makelib lock_ex.o lock_exnb.o lock_un.o - ./makelib lock.a lock_ex.o lock_exnb.o lock_un.o +makelib lock_ex.o lock_exnb.o lock_un.o lock_exfcntl.o lock_unfcntl.o + ./makelib lock.a lock_ex.o lock_exnb.o lock_un.o lock_exfcntl.o lock_unfcntl.o lock_ex.o: \ compile lock_ex.c hasflock.h lock.h @@ -831,6 +847,14 @@ compile lock_un.c hasflock.h lock.h ./compile lock_un.c +lock_exfcntl.o: \ +compile lock_exfcntl.c lock.h + ./compile lock_exfcntl.c + +lock_unfcntl.o: \ +compile lock_unfcntl.c lock.h + ./compile lock_unfcntl.c + maildir.0: \ maildir.5 nroff -man maildir.5 > maildir.0 @@ -935,7 +959,7 @@ maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \ qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \ qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \ -envelopes.0 forgeries.0 +envelopes.0 forgeries.0 qmail-spamthrottle.0 mbox.0: \ mbox.5 @@ -968,9 +992,9 @@ open.a: \ makelib open_append.o open_excl.o open_read.o open_trunc.o \ -open_write.o +open_write.o open_rw.o ./makelib open.a open_append.o open_excl.o open_read.o \ - open_trunc.o open_write.o + open_trunc.o open_write.o open_rw.o open_append.o: \ compile open_append.c open.h @@ -984,6 +1008,10 @@ compile open_read.c open.h ./compile open_read.c +open_rw.o: \ +compile open_rw.c open.h + ./compile open_rw.c + open_trunc.o: \ compile open_trunc.c open.h ./compile open_trunc.c @@ -1144,7 +1172,7 @@ quote.o now.o control.o date822fmt.o constmap.o qmail.o \ case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \ token822.o env.a stralloc.a alloc.a substdio.a error.a \ - str.a fs.a auto_qmail.o + str.a fs.a auto_qmail.o qmail-inject.0: \ qmail-inject.8 @@ -1533,16 +1561,16 @@ qmail-smtpd: \ load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ -timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ -date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ -open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o socket.lib +timeoutwrite.o ip.o ipme.o ipalloc.o control.o control_time.o time.o constmap.o \ +received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ +spam.o open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ +fs.a auto_qmail.o auto_uids.o socket.lib tai.lib lock.a ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ - timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ - received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ - datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + timeoutwrite.o ip.o ipme.o ipalloc.o control.o control_time.o time.o \ + constmap.o received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ + datetime.a getln.a spam.o open.a sig.a case.a env.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_uids.o \ + `cat socket.lib tai.lib` lock.a qmail-smtpd.0: \ qmail-smtpd.8 @@ -1553,9 +1581,21 @@ substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ -exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spam.h ./compile qmail-smtpd.c +qmail-spamthrottle.0: \ +qmail-spamthrottle.5 + nroff -man qmail-spamthrottle.5 > qmail-spamthrottle.0 + +qmail-spamthrottle.5: \ +qmail-spamthrottle.9 conf-break conf-spawn + cat qmail-spamthrottle.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-spamthrottle.5 + qmail-start: \ load qmail-start.o prot.o fd.a auto_uids.o ./load qmail-start prot.o fd.a auto_uids.o @@ -1815,19 +1855,22 @@ trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \ byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \ str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \ -str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \ +str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c lock_exfcntl.c \ +lock_unfcntl.c tryflock.c getln.3 \ getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \ subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \ error.h error.c error_str.c error_temp.c fmt.h fmt_str.c fmt_strn.c \ -fmt_uint.c fmt_uint0.c fmt_ulong.c scan.h scan_ulong.c scan_8long.c \ -slurpclose.h slurpclose.c quote.h quote.c hfield.h hfield.c \ +fmt_uint.c fmt_uint0.c fmt_ulong.c fmt_ulong0.c scan.h scan_ulong.c \ +scan_8long.c slurpclose.h slurpclose.c quote.h quote.c hfield.h hfield.c \ headerbody.h headerbody.c token822.h token822.c control.h control.c \ datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \ date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \ ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ -maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c +maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \ +README.spamthrottle time.c time.h control_time.c lock_exfcntl.c \ +lock_unfcntl.c spam.c tryltai.c trytai.c open_rw.c qmail-spamthrottle.9 shar -m `cat FILES` > shar chmod 400 shar @@ -1890,6 +1933,10 @@ && echo -lsocket -lnsl || exit 0 ) > socket.lib rm -f trylsock.o trylsock +spam.o: \ +compile spam.c auto_uids.h control.h stralloc.h lock.h spam.h + ./compile spam.c + spawn.o: \ compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \ stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \ @@ -2064,6 +2111,13 @@ find-systype trycpp.c ./find-systype > systype +tai.lib: \ +tryltai.c compile load + ( ( ./compile tryltai.c && \ + ./load tryltai -ltai ) >/dev/null 2>&1 \ + && echo -ltai || exit 0 ) > tai.lib + rm -f tryltai.o tryltai + tcp-env: \ load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \ timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \ @@ -2094,6 +2148,10 @@ tcpto_clean.o: \ compile tcpto_clean.c tcpto.h open.h substdio.h readwrite.h ./compile tcpto_clean.c + +time.o: \ +compile time.c time.h stralloc.h fmt.h scan.h hastai.h + ./compile time.c timeoutconn.o: \ compile timeoutconn.c ndelay.h select.h error.h readwrite.h ip.h \ --- README.spamthrottle Tue May 5 16:32:27 1998 +++ README.spamthrottle Tue Feb 26 12:46:48 2002 @@ -0,0 +1,17 @@ +qmail-spamthrottle 1.03 +20020219 +Copyright 2001,2002 +D. Woolridge, dale-qmail-spamthrottle@woolridge.ca +J. Law, jtlaw@arctic.org +M. Kawasaki, kawasaki@kawasaki3.org + +Man pages have been updated with spamthrottle documentation: + qmail-smtpd(8) (file qmail-smtpd.8) + qmail-control(5) (file qmail-control.9) + qmail-spamthrottle(5) (file qmail-spamthrottle.9) ** new ** + +This patch uses subsecond precision calculations. Use of libtai +(http://cr.yp.to/libtai.html) is recommended, but not required. +If you choose to use libtai, we suggest version 0.60 since this patch +has been tested with it. Use of libtai is automatic when it is +available (for inclusion and linking). --- TARGETS 2001/02/19 20:16:31 +++ TARGETS 2001/12/19 17:31:40 @@ -45,6 +45,7 @@ open_read.o open_trunc.o open_write.o +open_rw.o open.a seek_cur.o seek_end.o @@ -55,6 +56,8 @@ lock_ex.o lock_exnb.o lock_un.o +lock_exfcntl.o +lock_unfcntl.o lock.a fd_copy.o fd_move.o @@ -165,6 +168,10 @@ qmail-getpw qmail-remote.o control.o +control_time.o +hastai.h +time.o +tai.lib constmap.o timeoutread.o timeoutwrite.o @@ -252,6 +259,7 @@ qmail-qmtpd qmail-smtpd.o qmail-smtpd +spam.o sendmail.o sendmail tcp-env.o @@ -382,6 +390,8 @@ addresses.0 envelopes.0 forgeries.0 +qmail-spamthrottle.5 +qmail-spamthrottle.0 man setup check --- control.c 2001/02/19 20:16:33 +++ control.c 2001/03/09 03:54:13 @@ -7,8 +7,10 @@ #include "control.h" #include "alloc.h" #include "scan.h" +#include "fmt.h" static char inbuf[64]; +static char outbuf[64]; static stralloc line = {0}; static stralloc me = {0}; static int meok = 0; @@ -127,4 +129,26 @@ } close(fd); return -1; +} + + +int control_writeint(i,fn) +int *i; +char *fn; +{ + substdio ss; + int fd; + char num[FMT_ULONG]; + + fd = open_write(fn); + if (fd == -1) { if (errno == error_noent) return 0; return -1; } + + substdio_fdbuf(&ss,write,fd,outbuf,sizeof(outbuf)); + + substdio_putflush(&ss,num,fmt_ulong(num,*i)); + substdio_putsflush(&ss,"\n"); + + close(fd); + + return 1; } --- control.h 2001/02/19 20:16:33 +++ control.h 2001/03/13 08:59:40 @@ -6,5 +6,9 @@ extern int control_rldef(); extern int control_readint(); extern int control_readfile(); +extern int control_writeint(); + +extern int control_readtime(); +extern int control_writetime(); #endif --- control_time.c Tue May 5 16:32:27 1998 +++ control_time.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,62 @@ +#include "readwrite.h" +#include "open.h" +#include "stralloc.h" +#include "substdio.h" +#include "error.h" +#include "control.h" +#include "alloc.h" +#include "time.h" + +static char inbuf[64]; +static char outbuf[64]; + + +int control_readtime(t,fn) +struct q_time_t *t; +char *fn; +{ + stralloc sa = { 0 }; + substdio ss; + int fd; + char tb; + + fd = open_read(fn); + if (fd == -1) { if (errno == error_noent) return 0; return -1; } + + substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf)); + + for (;;) + { + if (substdio_get(&ss,&tb,1) <= 0) break; + if (!stralloc_append(&sa,&tb)) return -1; + } + + if ((!Q_TIME_PACK && sa.len) || (Q_TIME_PACK && sa.len == Q_TIME_PACK)) + { + time_unpack(sa.s,t); + } + + close(fd); + return 1; +} + + +int control_writetime(t,fn) +struct q_time_t *t; +char *fn; +{ + stralloc sa = { 0 }; + substdio ss; + int fd; + + fd = open_write(fn); + if (fd == -1) { if (errno == error_noent) return 0; return -1; } + + substdio_fdbuf(&ss,write,fd,outbuf,sizeof(outbuf)); + + time_pack(&sa,t); + substdio_putflush(&ss,sa.s,sa.len); + + close(fd); + return 1; +} --- fmt.h 2001/02/19 20:16:33 +++ fmt.h 2002/02/26 17:45:58 @@ -12,6 +12,7 @@ extern unsigned int fmt_xshort(); extern unsigned int fmt_nbbshort(); extern unsigned int fmt_ulong(); +extern unsigned int fmt_ulong0(); extern unsigned int fmt_xlong(); extern unsigned int fmt_nbblong(); --- fmt_ulong0.c Tue May 5 16:32:27 1998 +++ fmt_ulong0.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,10 @@ +#include "fmt.h" + +unsigned int fmt_ulong0(s,u,n) char *s; unsigned long u; unsigned int n; +{ + unsigned int len; + len = fmt_ulong(FMT_LEN,u); + while (len < n) { if (s) *s++ = '0'; ++len; } + if (s) fmt_ulong(s,u); + return len; +} --- hier.c 2001/02/19 20:16:32 +++ hier.c 2001/02/27 09:28:19 @@ -47,6 +47,7 @@ d(auto_qmail,"man/man8",auto_uido,auto_gidq,0755); d(auto_qmail,"alias",auto_uida,auto_gidq,02755); + d(auto_qmail,"spam",auto_uidd,auto_gidn,02700); d(auto_qmail,"queue",auto_uidq,auto_gidq,0750); d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700); @@ -164,6 +165,8 @@ c(auto_qmail,"man/cat5","qmail-users.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man5","tcp-environ.5",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat5","tcp-environ.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-spamthrottle.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-spamthrottle.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man7","forgeries.7",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat7","forgeries.0",auto_uido,auto_gidq,0644); --- lock.h 2001/02/19 20:16:33 +++ lock.h 2001/03/09 03:54:13 @@ -5,4 +5,7 @@ extern int lock_un(); extern int lock_exnb(); +extern int lock_exfcntl(); +extern int lock_unfcntl(); + #endif --- lock_exfcntl.c Tue May 5 16:32:27 1998 +++ lock_exfcntl.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,16 @@ +#include +#include +#include "lock.h" + +int lock_exfcntl(fd) +int fd; +{ + struct flock fl; + + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + return fcntl(fd,F_SETLKW,&fl); +} --- lock_unfcntl.c Tue May 5 16:32:27 1998 +++ lock_unfcntl.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,16 @@ +#include +#include +#include "lock.h" + +int lock_unfcntl(fd) +int fd; +{ + struct flock fl; + + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + return fcntl(fd,F_SETLKW,&fl); +} --- open.h 2001/02/19 20:16:32 +++ open.h 2001/02/20 06:00:02 @@ -6,5 +6,6 @@ extern int open_append(); extern int open_trunc(); extern int open_write(); +extern int open_rw(); #endif --- open_rw.c Tue May 5 16:32:27 1998 +++ open_rw.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,6 @@ +#include +#include +#include "open.h" + +int open_rw(fn) char *fn; +{ return open(fn,O_RDWR | O_CREAT); } --- qmail-control.9 2001/02/19 20:16:32 +++ qmail-control.9 2002/02/19 08:42:30 @@ -63,6 +63,12 @@ .I rcpthosts \fR(none) \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd .I smtproutes \fR(none) \fRqmail-remote +.I spamthrottle \fR0 \fRqmail-smtpd +.I spamthrottlemax \fR0 \fRqmail-smtpd +.I spamthrottlercpt \fR0 \fRqmail-smtpd +.I spamthrottleflush \fR0 \fRqmail-smtpd +.I spamthrottletg \fR0 \fRqmail-smtpd +.I spamthrottletgresp \fRplease wait \fRqmail-smtpd .I timeoutconnect \fR60 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote .I timeoutsmtpd \fR1200 \fRqmail-smtpd --- qmail-showctl.c 2001/02/19 20:16:32 +++ qmail-showctl.c 2002/02/19 08:42:30 @@ -257,6 +257,12 @@ do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_int("spamthrottle","0","Spam throttle delay is "," milliseconds"); + do_int("spamthrottlemax","0","Spam throttle maximum delay is "," milliseconds"); + do_int("spamthrottlercpt","0","Spam throttle reasonable recipient count is ",""); + do_int("spamthrottleflush","0","Spam throttle flush before DATA is ",""); + do_int("spamthrottletg","0","Spam throttle teergrube periodicity is "," seconds"); + do_str("spamthrottletgresp",0,"please wait","Sapm throttle teergrube response is "); do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds"); do_int("timeoutremote","1200","SMTP client data timeout is "," seconds"); do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds"); @@ -292,6 +298,12 @@ if (str_equal(d->d_name,"rcpthosts")) continue; if (str_equal(d->d_name,"smtpgreeting")) continue; if (str_equal(d->d_name,"smtproutes")) continue; + if (str_equal(d->d_name,"spamthrottle")) continue; + if (str_equal(d->d_name,"spamthrottlemax")) continue; + if (str_equal(d->d_name,"spamthrottlercpt")) continue; + if (str_equal(d->d_name,"spamthrottleflush")) continue; + if (str_equal(d->d_name,"spamthrottletg")) continue; + if (str_equal(d->d_name,"spamthrottletgresp")) continue; if (str_equal(d->d_name,"timeoutconnect")) continue; if (str_equal(d->d_name,"timeoutremote")) continue; if (str_equal(d->d_name,"timeoutsmtpd")) continue; --- qmail-smtpd.8 2001/02/19 20:16:32 +++ qmail-smtpd.8 2002/02/19 08:42:30 @@ -169,10 +169,103 @@ .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. +.TP 5 +.I spamthrottle +Approximately, the number of milliseconds allowed between delivery +of messages from the same IP address. Default: 0. A typical value +would be 2000. See +.B qmail-spamthrottle(5) +for details. + +If the environment variable +.B SPAMTHROTTLE +is set, it overrides +.IR spamthrottle . + +.TP 5 +.I spamthrottlemax +The maximum delay, in milliseconds, between delivery of messages from +the same IP address. Default: 0. A recommended value would be 240000. +See +.B qmail-spamthrottle(5) +for details. + +If the environment variable +.B SPAMTHROTTLEMAX +is set, it overrides +.IR spamthrottlemax . + +.TP 5 +.I spamthrottlercpt +The number of recipients allowed per SMTP connection before additional +delay penalties are applied. Default: 0. A recommended value would be 10. +See +.B qmail-spamthrottle(5) +for details. + +If the environment variable +.B SPAMTHROTTLERCPT +is set, it overrides +.IR spamthrottlercpt . + +.TP 5 +.I spamthrottleflush +If non-zero, then the input buffer is flushed after receiving the DATA +command and prior to sending a response to the DATA command. This is +in direct violation of RFC 2920 (STD 60) when PIPELINING is supported. +Thus, PIPELINING is not supported when this flush mechanism is enabled. +See +.B qmail-spamthrottle(5) +for details. + +If the environment variable +.B SPAMTHROTTLEFLUSH +is set, it overrides +.IR spamthrottleflush . + +.TP 5 +.I spamthrottletg +If non-zero, continuation lines (for the DATA command response) are sent +to the remote SMTP client every +.I spamthrottletg +seconds, but only when there would be a call to +.B sleep(3) +as per normal spam throttle behaviour. Default: 0. +See +.B qmail-spamthrottle(5) +for details. + +If the environment variable +.B SPAMTHROTTLETG +is set, it overrides +.IR spamthrottletg . + +.TP 5 +.I spamthrottletgresp +A string appended to the continuation line sent as a result of +.I spamthrottletg +being set. Default: +.BR "please wait" . +For example, + +.EX + ... + DATA + 354-please wait + 354 go ahead + ... +.EE + +If the environment variable +.B SPAMTHROTTLETGRESP +is set, it overrides +.IR spamthrottletgresp . + .SH "SEE ALSO" tcp-env(1), tcp-environ(5), qmail-control(5), +qmail-spamthrottle(5), qmail-inject(8), qmail-newmrh(8), qmail-queue(8), --- qmail-smtpd.c 2001/02/19 20:16:32 +++ qmail-smtpd.c 2002/02/19 08:42:31 @@ -23,6 +23,7 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "spam.h" #define MAXHOPS 100 unsigned int databytes = 0; @@ -85,7 +86,9 @@ stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ -void dohelo(arg) char *arg; { +void dohelo(arg) +char *arg; +{ if (!stralloc_copys(&helohost,arg)) die_nomem(); if (!stralloc_0(&helohost)) die_nomem(); fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; @@ -96,6 +99,7 @@ int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; +struct spam_t spamt = spam_t_init(); void setup() { @@ -110,6 +114,34 @@ if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (timeout <= 0) timeout = 1; + if (control_readint(&spamt.throttle,"control/spamthrottle") == -1) die_control(); + x = env_get("SPAMTHROTTLE"); + if (x) { scan_ulong(x,&u); spamt.throttle = u; } + + if (spamt.throttle) { + spamt.out = &ssout; + + if (control_readint(&spamt.max,"control/spamthrottlemax") == -1) die_control(); + x = env_get("SPAMTHROTTLEMAX"); + if (x) { scan_ulong(x,&u); spamt.max = u; } + + if (control_readint(&spamt.flush,"control/spamthrottleflush") == -1) die_control(); + x = env_get("SPAMTHROTTLEFLUSH"); + if (x) { scan_ulong(x,&u); spamt.flush = u; } + + if (control_readint(&spamt.reasonablercpt,"control/spamthrottlercpt") == -1) die_control(); + x = env_get("SPAMTHROTTLERCPT"); + if (x) { scan_ulong(x,&u); spamt.reasonablercpt = u; } + + if (control_readint(&spamt.tg,"control/spamthrottletg") == -1) die_control(); + x = env_get("SPAMTHROTTLETG"); + if (x) { scan_ulong(x,&u); spamt.tg = u; } + + if (control_rldef(&spamt.tg_resp,"control/spamthrottletgresp",0,"please wait") == -1) die_control(); + x = env_get("SPAMTHROTTLETGRESP"); + if (x) { stralloc_copys(&spamt.tg_resp,x); } + } + if (rcpthosts_init() == -1) die_control(); bmfok = control_readfile(&bmf,"control/badmailfrom",0); @@ -131,6 +163,13 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + spamt.dir = env_get("SPAMTHROTTLEDIR"); + if (spamt.dir) { + if (*spamt.dir == '\0') spamt.dir = 0; + } + else { + spamt.dir = remoteip; + } dohelo(remotehost); } @@ -219,6 +258,7 @@ int seenmail = 0; int flagbarf; /* defined if seenmail */ +int rcptcount; stralloc mailfrom = {0}; stralloc rcptto = {0}; @@ -229,7 +269,11 @@ } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); + if (!spamt.flush || !spamt.throttle) { + out("\r\n250-PIPELINING"); + } + out("\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() @@ -245,6 +289,7 @@ if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem(); + rcptcount = 0; out("250 ok\r\n"); } void smtp_rcpt(arg) char *arg; { @@ -261,6 +306,7 @@ if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); + ++rcptcount; out("250 ok\r\n"); } @@ -365,7 +411,8 @@ out("\r\n"); } -void smtp_data() { +void smtp_data() +{ int hops; unsigned long qp; char *qqx; @@ -376,6 +423,11 @@ if (databytes) bytestooverflow = databytes + 1; if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); + spamt.rcptcount = rcptcount; + if (spam_wait(&spamt) && spamt.flush) + { + spam_flush(ssin.fd); + } out("354 go ahead\r\n"); received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); @@ -384,7 +436,7 @@ if (hops) qmail_fail(&qqt); qmail_from(&qqt,mailfrom.s); qmail_put(&qqt,rcptto.s,rcptto.len); - + qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } --- qmail-spamthrottle.9 Tue May 5 16:32:27 1998 +++ qmail-spamthrottle.9 Tue Feb 26 12:46:49 2002 @@ -0,0 +1,235 @@ +.TH qmail-spamthrottle 5 + +.SH NAME +qmail-spamthrottle \- the qmail spam throttle mechanism + +.SH INTRODUCTION +The use of spam throttling was motivated by the ease with which +tarpitting is countered by would-be spammers. A reasonable +tarpitcount is one which does not adversely affect acceptable +mail usage, but any would-be spammer can simply create separate +SMTP connections with a recipient count lower than tarpitcount. +Spam throttling addresses these issues by parameterizing SMTP +usage and imposing a wait (via +.BR sleep (3)) +following the +.B DATA +command. SMTP usage parameters include: remote IP address; +previous SMTP connection timestamp; and previous wait time. + +.SH FILES +Two files, +.I wait +and +.IR time , +store the previous wait time and SMTP connection timestamp, +respectively. Both files are found in +.B QMAILHOME/spam/\fIdir\fB/\fR. +Where +.I dir +is +.BR SPAMTHROTTLEDIR , +if that is set, and +.B TCPREMOTEIP +otherwise. If you are using libtai for your time calculations, +then the format for the +.I time +file is a packed TAI64NA label. If you have perl and the tai64nlocal +program, you can use the following perl expression to convert from +a packed TAI64NA label to a TAI64N timestamp: + +.EX + print join("","@",unpack("H24",<>)), "\n"; +.EE + +Note: Every dot (\fB.\fR) is interpreted as a slash (\fB/\fR) +for the purposes of constructing \fIdir\fR. For example, if +.B TCPREMOTEIP +is +.I a\fR.\fIb\fR.\fIc\fR.\fId +then the two spam throttle state files are stored in +.B QMAILHOME/spam/\fIa\fB/\fIb\fB/\fIc\fB/\fId\fB/\fR. + +Message throughput is controlled using the +.B QMAILHOME/control/spamthrottle +file. The delays imposed (by calling +.BR sleep (3)) +depend on: the contents of this file (\fIS\fR); number of recipients +for the current SMTP session (\fIR\fR); the number of reasonable +recipients per connection (\fIRM\fR); how much time has +passed (\fIT\fR) since the last SMTP request from +.B TCPREMOTEIP +(or in +.BR SPAMTHROTTLEDIR ); +and the last imposed delay (\fIW\fR). The latter two correspond +to the state files mentioned above. The new delay is approximately +.EX + + (1 + \fIR\fR - \fIR\fR / 2^(\fIR\fR/\fIRM\fR)) * ((\fIW\fR * \fIS\fR * \fIR\fR) / \fIT\fR) + +.EE +when \fIRM\fR is greater than 0, and +.EX + + (1 + \fIR\fR) * ((\fIW\fR * \fIS\fR * \fIR\fR) / \fIT\fR) + +.EE +otherwise. The unit of time is milliseconds. + +If +.B QMAILHOME/control/spamthrottlemax +exists, then it is used as a maximum (in milliseconds) for the +delay calculated above. The environment variable, +.BR SPAMTHROTTLEMAX , +may also contain the maximum delay. The environment variable +always takes precedence. + +.SH ENVIRONMENT +The environment variable, +.BR SPAMTHROTTLE , +will be used, if present, instead of the contents of +.BR QMAILHOME/control/spamthrottle . +This is particularly useful when one wishes to disable the spamthrottle +mechanism in select cases and is easily done in conjunction with a program +like +.BR tcpserver . +An ISP may wish to enable this mechanism for its customers to prevent +them from using the mail servers as a convenient location from which +to send spam. However, in some or all other cases (other originating +IP addresses) this mechanism might be disabled to allow for legitimate +high-volume mail traffic such as mailing lists. + +As indicated above, +.B SPAMTHROTTLEDIR +will be used in the construction of the state files' storage path. +Otherwise, +.B TCPREMOTEIP +is used in its place. +Typically, this environment variable would be used to collect groups +of connections (IP addresses) logically to avoid the proliferation of +files/directories (for each IP address). This is easily done in +conjunction with a program like +.BR tcpserver . +As a home user, operating a small mail server, one might use the +directory 'external', 'all', 'public', or 'default' (as examples) +to use a shared state file by default, but support exceptions for +specific IP addresses such as a local subnet or an external set of +trusted mail servers. +See +.B EXAMPLES +below for specific examples of the spamthrottle mechanism used in +conjunction with +.BR tcpserver . + +Despite efforts to impose a waiting period on would-be +spammers, it is still possible for the client to circumvent +the call to +.BR sleep (3). +That is, they may not wait for the response from +the DATA command, continuing to write their message, assuming +success, then closing the socket, again without waiting for a +response from the server; the message will be delivered at no +(time) cost to them. Adherence to standards should not be +assumed for clients acting as agents for unsolicited bulk email. +As such, an additional variable, +.BR SPAMTHROTTLEFLUSH , +can be set to indicate that all input will be flushed after +calling +.BR sleep (3) +and prior to sending a response to the DATA command. The contents of +.B QMAILHOME/control/spamthrottleflush +may also be used for the same purpose, but the environment +variable still takes precedence. RFC 2920 (STD 60) prohibits +flushing of the input buffer if PIPELINING is supported. +As such, EHLO responses will not advertise PIPELINING while +SPAMTHROTTLEFLUSH is set. + +Another method, teergrubing, involves issuing continuation lines +periodically to keep the client connected while they wait for the +go ahead from the DATA command. By setting the environment variable +.BR SPAMTHROTTLETG , +you can specify the frequency of continuation lines in response to the +DATA command. If the argument to +.BR sleep (3) +would have been 11 (seconds) and +.B SPAMTHROTTLETG +is set to 2, then the response to the DATA command would result in +several calls to sleep(2) (and one sleep(1)) with each accompanied +by a continuation line. A continuation line consist of a 3-digit code, +a dash, and an arbitrary string. The default string is "please wait", +but can be changed using the environment variable +.BR SPAMTHROTTLETGRESP . +The teergrube variables, +.B SPAMTHROTTLETG +and +.B SPAMTHROTTLETGRESP +may also be specified in the two files +.B QMAILHOME/control/spamthrottletg +and +.BR QMAILHOME/control/spamthrottletgresp , +respectively. The environment variables always take precedence. + +.SH EXAMPLES +These examples assume that +.B QMAILHOME/control/spamthrottle +contains a non-zero value. + +Here is a sample rule file for a home user: +.EX + + # private (trusted) network does not adhere to + # spamthrottle mechanism + 192.168.0.:allow,RELAYCLIENT="",SPAMTHROTTLE="" + # + # some external network which we would like to + # throttle collectively + 10.0.0.:allow,SPAMTHROTTLEDIR="collected" + # + # an external network which is throttled based on + # individual IP address + # - we don't specify SPAMTHROTTLEDIR and the default + # behaviour of storing state files in directories + # based on IP address is used) + # - we also allow relaying from this semi-trusted + # network + 10.1.:allow,RELAYCLIENT="" + # + # allow other connections and treat them as one + # large throttle group + :allow,SPAMTHROTTLEDIR="public" + +.EE + +Here is a sample rule file for a high-volume mail server (or servers) +for some arbitrary ISP (with customer network 10.0.0.0/16 and internal/ +employee network 10.1.0.0/24): +.EX + + # customer network uses default behaviour + # (IP-based throttle files) + 10.0.:allow,RELAYCLIENT="" + # + # employee network doesn't adhere to throttling + 10.1.0.:allow,RELAYCLIENT="",SPAMTHROTTLE="" + # + # external trusted network which legitimately + # provides high volume mail traffic + 10.1.1.:allow,SPAMTHROTTLE="" + # + # a collection of addresses/networks which we + # might have gathered from past abuse experience + # - we allow the mail, but we're aggressive + # about throttling it + 10.1.2.1:allow,SPAMTHROTTLE="5000",SPAMTHROTTLEDIR="abuse" + 10.1.2.2:allow,SPAMTHROTTLE="5000",SPAMTHROTTLEDIR="abuse" + 10.1.2.3:allow,SPAMTHROTTLE="5000",SPAMTHROTTLEDIR="abuse" + 10.1.3.:allow,SPAMTHROTTLE="5000",SPAMTHROTTLEDIR="abuse" + # + # allow other connections and assume throttling + # isn't necessary + :allow,SPAMTHROTTLE="" + +.EE + +.SH "SEE ALSO" +qmail-smtpd(8) --- spam.c Tue May 5 16:32:27 1998 +++ spam.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,258 @@ +#include "auto_uids.h" +#include "lock.h" +#include "stralloc.h" +#include "control.h" +#include "open.h" +#include "fmt.h" +#include "timeoutread.h" +#include "error.h" +#include "time.h" +#include "spam.h" + + +int spam_create_directory(dn) +char *dn; +{ + if (mkdir(dn,0700) == -1 && errno != error_exist) + { + /* can't create dir */ + return 0; + } + if (chown(dn,auto_uidd,auto_gidn) == -1) + { + /* can't chown dir */ + return 0; + } + return 1; +} + + +int spam_open_rw(fn) +char *fn; +{ + int fd; + + fd = open_rw(fn); + + if (fd == -1) + { + /* can't open file for read/write */ + close(fn); + return -1; + } + + if (chmod(fn,0600) == -1) + { + /* can't chmod file */ + close(fd); + return -1; + } + return fd; +} + + +int spam_stralloc_path(to,path,pa1,pa2) +stralloc *to; +char *path; +char *pa1; +char *pa2; +{ + int rc = 0; + + if (!stralloc_copys(to,path) || !stralloc_cats(to,pa1) || (pa2 && !stralloc_cats(to,pa2)) || !stralloc_0(to)) + { + /* can't alloc */ + rc = -1; + } + return rc; +} + + +static unsigned int normalize_recipients( r, r_m ) +unsigned int r; +unsigned int r_m; +{ + static const unsigned int r_min = 1; + unsigned int r_new = r_min; + + if( r_m ) + { + r_new = r - (r >> (r/r_m)); + if( r_new < r_min ) r_new = r_min; + } + + return r_new; +} + + +static unsigned int normalize_wait( w, dt, s_t, s_m, r, r_m ) +unsigned int w; +unsigned int dt; +unsigned int s_t; +unsigned int s_m; +unsigned int r; +unsigned int r_m; +{ + static const unsigned int min_wait = 100; + unsigned int w_new = 0; + + /* + * the maximum throttle times the "reasonable" # recipients + * is approximately the longest wait we want/expect so if + * we've waited that long, then we'll return the minimum + */ + if( r_m && s_m && dt > (s_m * r_m) ) return min_wait; + + /* + * then apply a normalizing factor to the previous wait (w) of + * (s_t * r) / dt, where s_t is our throttle constant (time per msg), + * r is actual # recipients, and dt is time since last imposed wait + */ + w_new = (w * (s_t * r)) / (dt ? dt : 1); + if( w_new < min_wait ) w_new = min_wait; + + return w_new; +} + + +static void spam_tg( spamt, wait ) +struct spam_t * spamt; +unsigned int wait; +{ + unsigned int tg = 0; + /* + * output continuation lines (up to) every "tg" seconds + */ + while (wait) + { + tg = (wait < spamt->tg) ? wait : spamt->tg; + wait -= tg; + substdio_puts(spamt->out,spamt->code); + substdio_put(spamt->out,spamt->tg_resp.s,spamt->tg_resp.len); + substdio_puts(spamt->out,"\r\n"); + substdio_flush(spamt->out); + sleep(tg); + } +} + + +int spam_wait( spamt ) +struct spam_t * spamt; +{ + stralloc sd = {0}; + stralloc sw = {0}; + stralloc st = {0}; + stralloc sl = {0}; + unsigned int pd = 0; + int fdw = 0; + int fdt = 0; + int fdl = 0; + struct q_time_t tn; + struct q_time_t ts; + unsigned int lw = 0; + unsigned long it = 0; + unsigned long sr = 0; + int rc = 1; + + if (!spamt) return 0; + if (!spamt->throttle) return 0; + + if (spam_stralloc_path(&sd,"spam/",spamt->dir,".") == -1) return 0; + + while (1) + { + pd += str_chr(&sd.s[pd],'.'); + if (!sd.s[pd]) break; + + sd.s[pd] = '\0'; + if (!spam_create_directory(sd.s)) return 0; + sd.s[pd] = '/'; + } + + if (spam_stralloc_path(&sl,sd.s,"lock",0) == -1) return 0; + if (spam_stralloc_path(&st,sd.s,"time",0) == -1) return 0; + if (spam_stralloc_path(&sw,sd.s,"wait",0) == -1) return 0; + + if ((fdl = spam_open_rw(sl.s)) == -1) return 0; + + if (lock_exfcntl(fdl) == -1) + { + close(fdl); + return 0; + } + + if ((fdt = spam_open_rw(st.s)) == -1) + { + lock_unfcntl(fdl); + close(fdl); + return 0; + } + + if ((fdw = spam_open_rw(sw.s)) == -1) + { + lock_unfcntl(fdl); + close(fdl); + close(fdt); + return 0; + } + + close(fdw); + close(fdt); + + time_now(&tn); + ts = tn; + control_readtime(&ts,st.s); + control_readint(&lw,sw.s); + + time_sub(&ts,&tn,&ts); + + it = time_ms(&ts); + lw = normalize_recipients( spamt->rcptcount, spamt->reasonablercpt ) + * normalize_wait( lw, it, spamt->throttle, spamt->max, spamt->rcptcount, spamt->reasonablercpt ); + + if (control_writetime(&tn,st.s) < 1) + { + lock_unfcntl(fdl); + close(fdl); + return 0; + } + + rc = (control_writeint(&lw,sw.s) > 0); + rc &= (lock_unfcntl(fdl) != -1); + + close(fdl); + + if (!rc) return 0; + + if (lw < 1000) return 0; + + if (spamt->max && lw > spamt->max) lw = spamt->max; + lw /= 1000; + + if (spamt->tg && spamt->out) + { + spam_tg(spamt,lw); + } + else + { + sleep(lw); + } + + return 1; +} + + +void spam_flush(fd) +int fd; +{ + char ch; + + /* + * since this is a potential spammer, we'll see if we should + * flush all input (we want to force them to wait for the + * response from their DATA command before we accept input + * from them) + */ + while (timeoutread(1,fd,&ch,1) > 0) ; + /* timeout/error...doesn't matter */ +} --- spam.h Tue May 5 16:32:27 1998 +++ spam.h Tue Feb 26 12:46:49 2002 @@ -0,0 +1,25 @@ +#ifndef SPAM_H +#define SPAM_H + +#include "stralloc.h" +#include "substdio.h" + +extern int spam_wait(); +extern void spam_flush(); + +typedef struct spam_t { + unsigned int throttle; + unsigned int max; + unsigned int reasonablercpt; + unsigned int flush; + unsigned int tg; /* teergrube */ + stralloc tg_resp; /* continuation line response: - */ + substdio * out; + char * code; + char * dir; + unsigned int rcptcount; +} spam_t; + +#define spam_t_init() { 0, 0, 0, 0, 0, { 0, 0, 0 }, 0, "354-", 0, 0 } + +#endif --- time.c Tue May 5 16:32:27 1998 +++ time.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,82 @@ +#include "time.h" +#include "stralloc.h" +#include "fmt.h" +#include "scan.h" + +void time_unpack(s,t) +char *s; +struct q_time_t *t; +{ +#ifdef HASTAI + taia_unpack(s,t); +#else + int i; + + i = scan_ulong(s,&(t->tv_sec)); + if (s[i] == ':') + scan_ulong(s+i+1,&(t->tv_usec)); + else + t->tv_usec = 0; +#endif +} + + +void time_pack(sa,t) +stralloc *sa; +struct q_time_t *t; +{ +#ifdef HASTAI + stralloc_ready(sa,TAIA_PACK); + taia_pack(sa->s,t); + sa->len = TAIA_PACK; +#else + char strnum[FMT_ULONG]; + + stralloc_catb(sa,strnum,fmt_ulong(strnum,(unsigned long)t->tv_sec)); + stralloc_cats(sa,":"); + stralloc_catb(sa,strnum,fmt_ulong0(strnum,(unsigned long)t->tv_usec,6)); + stralloc_0(sa); +#endif +} + +void time_now(t) +struct q_time_t *t; +{ +#ifdef HASTAI + taia_now(t); +#else + gettimeofday(t,0); +#endif +} + + +void time_sub(td,t1,t2) +struct q_time_t *td; +struct q_time_t *t1; +struct q_time_t *t2; +{ +#ifdef HASTAI + taia_sub(td,t1,t2); +#else + unsigned long usec = t1->tv_usec; + + td->tv_sec = t1->tv_sec - t2->tv_sec; + td->tv_usec = usec - t2->tv_usec; + if (td->tv_usec > usec) + { + td->tv_usec += 1000000UL; + --td->tv_sec; + } +#endif +} + + +unsigned long time_ms(t) +struct q_time_t *t; +{ +#ifdef HASTAI + return (unsigned long)((taia_approx(t) + taia_frac(t)) * (double)1000.0); +#else + return (unsigned long)((double)1000.0 * t->tv_sec + t->tv_usec / (double)1000.0); +#endif +} --- time.h Tue May 5 16:32:27 1998 +++ time.h Tue Feb 26 12:46:49 2002 @@ -0,0 +1,22 @@ +#ifndef TIME_H +#define TIME_H + +#include "hastai.h" + +#ifdef HASTAI +# include +# define q_time_t taia +# define Q_TIME_PACK TAIA_PACK +#else +# include +# define q_time_t timeval +# define Q_TIME_PACK 0 +#endif + +void time_pack(); +void time_unpack(); +void time_now(); +void time_sub(); +unsigned long time_ms(); + +#endif --- tryltai.c Tue May 5 16:32:27 1998 +++ tryltai.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,4 @@ +main() +{ + ; +} --- trytai.c Tue May 5 16:32:27 1998 +++ trytai.c Tue Feb 26 12:46:49 2002 @@ -0,0 +1,7 @@ +#include + +void main() +{ + struct taia t; + taia_now(&t); +}