Фильтры regex в sendmail
Часто для борьбы со спамом используют procmail, просто поиском находя подозрительные сочетания букв. Однако, если Вы хотите проверять не только почту, которая ложится в почтовые ящики, но и проходящую транзитом, все становится не так просто. Хотя и существует способ задействовать procmail и для транзитной почты, см. Milter, его здесь рассматривать не будем.
Рассмотрим здесь поиск регулярных выражений в заголовках сообщений.
Вам, вероятно, понадобится перекомпилировать sendmail с поддержкой regexp
Проверить, есть ли такая поддержка, можно командой

#echo a|sendmail -d|grep REGEX

и найдя строку Сompiled with, которая должна содержать MAP_REGEX
Если строки нет, добавьте в файл /devtools/OS/Linux строку

APPENDDEF(`conf_sendmail_ENVDEF', `-DMAP_REGEX')

После этого перекомпилируйте файлы

#./Build
#./Build install

Теперь Вы можете внести фильтры в .mc файл в директории /cf/cf
Вот пример файла sendmail.mc

:
MAILER(smtp)
MAILER(local)
:
LOCAL_CONFIG
HSubject: $>CheckSubject
Kspamsubj regex -aCONTAINSSHIT get|dvd|sell|cool

LOCAL_RULESETS
SCheckSubject
R$*(tab)$: $(spamsubj $1 $)
R$*CONTAINSSHIT(tab)$#error $: "550 5.7.1 Subject denied"
R$*(tab)$@ OK

Как оно работает?
Каждая строчка заголовка, которая начинается с Subject: попадает на правило HSubject:
Оттуда на рулесет CheckSubject
Первая строка вызывает поиск регулярного выражения Kspamsubj
Если выражения найдены, то к строке subject дописывается слово CONTAINSSHIT
Вторая строка рулесета проверяет, кончается ли строка subject словом CONTAINSSHIT
И если содержит, то обработка правил рулесета прекращается и отправителю выдается ошибка 550 5.7.1 Subject denied
Если не содержит, то третья строка безусловно заканчивает проверку строки subject

Замечания
1. обязательно необходимо использовать (табуляцию) а не пробелы в описании рулесета
2. по умолчанию regex не делает разницы между большими и малыми буквами
3. ответ, что сообщение не принято, выдается только после того как сервер принял все сообщение целиком


О вызове проверок во время входящей SMTP-сессии см. также
При начале SMTP сессии от удаленного компютера может быть вызван рулесет SCheck_relay
После команды MAIL FROM может быть вызван рулесет SCheck_mail
После каждой команды RCPT TO может быть вызван рулесет SLocal_check_rcpt
Рулесеты, связаные с отдельными полями заголовка, а также SCheck_eoh (вызывается после последнего поля заголовка) обрабатываются только после получения всего письма, поэтому по возможности лучше вынести проверки в SCheck_mail и SLocal_check_rcpt
Пример
...
LOCAL_RULESETS
SLocal_check_rcpt
R$*(tab)$: $&{verify} 
#присваивает переменной значение &{verify} , которое есть OK 
#если TLS сертификат клиента разрешен
ROK(tab)$# OK
#Такому человеку разрешен релеинг(то есть отменяются все дальнейшие проверки)
...
Полный список доступных переменных можно посмотреть например здесь
Например, ${client_addr},${mail_addr},${msg_size},${rcpt_addr} и даже такие экзотические как ${load_avg}
Отладка
В любом месте обработки рулесетов можно заставить sendmail записать в maillog значение переменной. Для этого необходимо включить логгинг:
...
Ksyslog syslog
LOCAL_RULESETS
SLocal_check_rcpt
R$*(tab)$: $&{verify}
ROK(tab)$# OK
R$*(tab)$: $&{rcpt_addr}
R$*(tab)$: $(syslog RCPT: $1 $)
# Здесь вместо RCPT: можно поставить любой текст, он попадает в лог
# а $1 есть первый параметр, он тоже запишется в лог
...
Сохранение переменной
Допустим, мы хотим проверить было ли в заголовке поле To: Примечание: поле To: в заголовке письма и &{Rcpt_addr} могут различаться, значение To: видно в почтовом клиенте как адрес получателя, а &{Rcpt_addr} используется для доставки почты; Они могут быть различны, например, если письмо отправляестя списку рассылки, или, при спаме. Отсутствие поля To: в заголовке может также служить признаком спама
Необходимо предварительно включить макрос сохранения переменных
...
HTo: $>To
...
Kstorage macro
Ksyslog syslog
LOCAL_RULESETS
STo
R$*(tab)$: $(storage {TO} $@ $1 $) $1
#Сохраняем нашу переменную под именем &{TO}
R$*(tab)$@ OK
#На этом заканчиваем проверку поля To:
...
Scheck_eoh
R$*(tab)$: < $&{TO} >
#Загружаем переменную из &{TO}
R$*(tab)$: $(syslog $&{TO} $1 $) $1
R$*(tab)$: $(storage {TO} $) $1
#Сохраняем в &{TO} пустоту (для проверки следующих писем)
R< $+ >(tab)$@ OK
#Если наша переменная не пуста, заканчиваем проверку с ОК
R$*(tab)$#error $@ 4.7.1 $: 450 letter to nobody
#Иначе, выдаем временную ошибку

Сравнение переменных
При включении соответствующего arith map нам становятся доступны простейшие арифметические операции такие как +, -, *, /, =, а так же сравнение. Результат первых пяти операций - целое число, а сравнения - FALSE или TRUE
пример применения - будем откладывать принятие больших писем при высокой загрузке сервера
...
MAILER(smtp)dnl
MAILER(local)dnl
Kcomp arith
Ksyslog syslog
...
Kmyself regex -aOK 127.0.0.1
LOCAL_RULESETS
Scheck_mail
R$*(tab)$: $&{client_addr}
#загружаем адрес подключившегося клиента
R$*(tab)$: $(myself $1 $)
#Если локальный пользователь
R$*OK(tab)$@ OK
#То может передавать сообщения без ограничений
R$*(tab)$: $(comp l $@ 3 $@ $&{load_avg} $) $1
#Сравниваем (функция l(маленькая L)) максимально допустимую среднюю загрузку 
#(здесь 3) и текущую $&{load_avg}
#Результат присваиваем текущей переменной $1
RFALSE $*(tab)$@ OK
#Если текущая загрузка меньше предельно допустимой то заканчиваем проверку
R$*(tab)$: $&{msg_size}
#Загружаем размер сообщения
R$*(tab)$: $(syslog size: $1 load: $&{load_avg} $)              
#Syslog (для отладки)
R$*(tab)$: $(comp l $@ $&{msg_size} $@ 100000 $) $1
#Аналогично, сравниваем размер сообщения с 
#предельно допустимым при высокой нагрузке, 
#то есть 100000байт
RFALSE $*(tab)$#error $: "450 4.7.1 Too busy (${load_avg}) to process big messages"
#Если сообщение больше, даем временный отлуп
R$*(tab)$@ OK
#Иначе, все ОК
...
Примечание: данный метод не является 100% - надежным, поскольку мы не можем знать наверняка размер сообщения во время команды MAIL FROM, однако, обычно клиент сообщает серверу размер сообщения в виде
MAIL FROM:<a@b.com> SIZE=12345

Еще пример - разрешить отправку для авторизованых пользователей только если они отправляют с адреса соответсвующего SMTP AUTH логину и только при шифровании не менее 128 бит
...
LOCAL_CONFIG
...
Kislogin regex -aOK login
Kstorage macro
Ksyslog syslog
Kcomp arith
LOCAL_RULESETS
...
SLocal_check_rcpt
R$*(tab)$: $&t
#Заменяем переменную AU (временем) чтобы она была непустая
R$*(tab)$: $(storage {AU} $@ $1 $) $1
R$*(tab)$: $&{auth_authen}
#Загружаем имя под которым залогинился пользователь (используя SMTP AUTH)
R$*(tab)$: $(storage {AU} $@ $1 $) $1
#и сохраняем его как AU
R$*(tab)$: $&{mail_addr}
#Загружаем отправителя
R$+@$+(tab)$: $1@
#Оставляем только то что было до собаки и приписываем собаку
R$=AU@(tab)$: $1
#Ищем в переменной(то есть левой стороне отправителя с собакой) 
#имя под которым он аутентифицировался (переменная AU), 
#если найдено, результату присваивается перменная AU (в ней нету собачки)
R$+@(tab)$: NOK
#А теперь ищем собачку и если она есть присваиваем кодовое слово NOK
R$*(tab)$: $(storage {SEQA} $@ $1 $) $1 
#Сохраняем результат как переменную SEQA
R$*(tab)$: $&{cipher_bits}
#Загружаем уровень шифрования TLS
R$*(tab)$: $(comp l $@ $&{cipher_bits} $@ 127 $) $1
#Сравниваем его с числом 127 (это для минимум 128-битного шифрования)
R$*FALSE$*(tab)$: $&{auth_type}!$&{SEQA}
#Если уровень шифрования не меньше 127 (FALSE) 
#Составляем нашу переменную из типа Аутентификации (слово login или пусто)
#восклицательного знака и результата сравнения отправителя и логина (SEQA)
Rlogin!NOK$*(tab)$#error $@ 4.7.1 $: 450 FROM must match auth user $&{auth_authen}
#Отлуп, если уровень шифрования ок, метод login, 
#но имя отправителя не соответствует логину
Rlogin$*(tab)$@ OK
#иначе - ОК, разрешен RELAY
R$*(tab)$: $(syslog WHATCOMPARED: $1 $)
# Это - для отладки
R$*(tab)$: $&{auth_type}
#Опять загружаем метод аутентификации
R$*(tab)$: $(islogin $1 $)
#Ищеи в нем слово login и если есть дописываем ОК
#(см. Kislogin regex выше)
R$*OK(tab)$#error $@ 4.7.1 $: 450 You MUST use minimum 128 bit encription
#И если использован логин то даем отлуп
#(Если же шифрование было включено, то отлуп был дан выше)

Хорошие ссылки

Буду благодарен за любые комментарии на адрес vt@bykoff.com
Прикручиваем spamassassin через MILTER -->
Конфигурация: пример из жизни -->
На главную страницу -->

очередность обработки
MARLENUS14.03.2005
RE: очередность обработки
Victor14.03.2005
неправильная работа regexp на длинных хедерах
Rodin Vasiliy30.01.2004
RE: неправильная работа regexp на длинных хедерах
Виктор11.02.2005
regex vs access.db ?
Vasiliy28.01.2004
RE: regex vs access.db ?
Виктор29.01.2004
HELO
Dmitry Karpov23.09.2003
RE: HELO
Victor23.09.2003
RE: RE: HELO
Dmitry Karpov23.09.2003
Вопрос
Rodin Vasiliy Stepanovitch22.09.2003
EX_TEMPFAIL
Victor26.11.2003
Новое сообщение


  В начало
       Фильтр Regex sendmail
            Пример фильтра
       Зарегистрироваться
       Видео клипы
       Книга жалоб/предложенй
       Экономика просто
            Амортизация
            НДС
       spamassassin-milter
       Всякая фигня
       Конвертер юникод
       Сбор данных
       SRS в sendmail
       my soft
            lpt daemon (RUS)
            lpt daemon (ENG)
       Из LJ
            О детях
                 Основы ГВ
                 Прикорм
                 Игрушки. Родственники
                 Гардероб(1)
                 Гардероб(2)
                 Гардероб(3)