Exim 9 Поиски в файлах и базах данных

Материал из wiki.lissyara.su
Перейти к: навигация, поиск

Содержание

Поиски в файлах и базах данных

Exim может быть сконфигурирован для поиска данных в файлах или базах данных, когда он обрабатывает сообщение. Могут использоваться два различных синтаксиса:

  • 1. Строка, которая будет раскрыта, может содержать явный запрос поиска. По этой причине, часть строки будет заменена данными, найденными поиском. Поиски этого типа - условия в элементах раскрытия. Различные результаты могут быть заданы для случаев успешного и неудачного поисков. Смотрите раздел 11, где в деталях описано раскрытие строк.
  • 2. Списки доменов, хостов, и адресов e-mail могут содержать запрос поиска, как способ избежать слишком длинного линейного списка. В этом случае, данные возвращённые запросом, обычно (но не всегда) отбрасываются (короче, не нужны они - прим. lissyara); реально засчитывается - успешен ли поиск, или нет. Эта разновидность списков описана в разделе 10.

Раскрытие строк, списков, и поисков взаимодействуют друг с другом, в каждом случае, нет порядка, в котором описан любой из них и нет ссылок к другим. Каждая из этих трёх частей даст намного больше, если вы прочтёте вначале две другие. Если вы читаете это первым, то знайте, что поймёте больше, после прочтения глав 10 и 11.

Примеры различных синтаксисов поиска

Очень легко перепутать два разных способа поиска, тем более, что списки, которые могут содержать вторую разновидность, всегда раскрываются, прежде чем быть обработанными как списки. Поэтому, они также могут содержать поиски первого вида. Будьте точны в различии между следующими двумя примерами:

domains = ${lookup{$sender_host_address}lsearch{/some/file}}
domains = lsearch;/some/file

Первый использует раскрытие строки, результат которого должен быть списком доменов. Строки, для успешного или безуспешного поиска не заданы; значение по-умолчанию, в этом случае, найденные данные и пустая строка, соответственно. Раскрытие помещается прежде чем строка обрабатывается как список, и файл, по которому ведётся поиск, может содержать строки как эти:

192.168.3.4: domain1:domain2:...
192.168.1.9: domain3:domain4:...

Когда поиск успешен, результат раскрытия - список доменов (и, возможно, другие типы элементов, разрешённые в списке доменов).

Во втором примере, ищется один элемент в списке доменов. Это вынуждает exim к использованию поиска, для того чтобы узнать, может ли обрабатываемый домен быть найден в файле. Файл может содержать строки как эти:

domain1:
domain2:

Любые данные, сопровождаемые ключами, не уместны при проверке что домен совпадает с элементом списка.

Их можно спутать, при использовании обоих видов поиска сразу. Рассмотрите файл, содержащий, например, такие строки:

192.168.5.6: lsearch;/another/file

Если значение “$sender_host_address” - 192.168.5.6, раскрытие первой установки “domains” генерит вторую установку, которая вызывает второй поиск.

Оставшаяся часть этой главы описывает различные доступные типы поиска. Любой из них может использоваться в любой части конфигурации, где разрешены поиски.


Типы поиска

Реализованы два различных типа поиска:

  • Одноключевой” (“single-key”) тип поиска, требует задания файла, в котором будет происходить поиск, и одного ключа для поиска. Ключ должен быть непустой строкой, чтобы поиск был успешен. Тип поиска определяет, как найден файл.
  • Поиск в “стиле запроса” (“query-style”) - принимает обобщённый запрос базы данных. Exim не предполагает никакого специфического ключевого значения для поисков в стиле запроса. Вы можете использовать любые переменные exim`a для необходимого вам запроса к БД.

Код для каждого типа поиска находится в отдельном файле исходных текстов, и включается в бинарник exim`a лишь если при компиляции установлена соответствующая опция. Дефолтовые настройки в “src/EDITME” таковы:

LOOKUP_DBM=yes
LOOKUP_LSEARCH=yes

и означают, что лишь линейный поиск и поиск в DBM включены по дефолту. Для некоторых типов поисков (например, БД SQL), вам необходимо инсталлировать библиотеки и файлы заголовков, до сборки exim`a.

Одноключевые типы поиска

Реализованы следующие одноключевые типы поиска:

  • cdb”: По данному файлу производится поиск как по файлу Статической БД (Constant DataBase), используя ключевую строку без завершающего двоичного нуля. Формат “cdb” спроектирован для индексных файлов, которые часто читаются, и никогда не обновляются, исключая полное пересоздание. Такде он является наиболее подходящим для больших файлов, содержащих альясы, или другие индексированные данные, на которые ссылается MTA. Информация о “cdb” может быть найдена в нескольких местах:
http://www.pobox.com/~djb/cdb.html
ftp://ftp.corpit.ru/pub/tinycdb/
http://packages.debian.org/stable/utils/freecdb.html

Дистрибутив “cdb” не нужен для сборки exim`a с поддержкой “cdb”, поскольку код для чтения “cdb”-файлов непосредственно включён в exim. Однако, с exim`ом не предоставляется никаких средств для сборки или тестирования “cdb”-файлов, таким образом вам необходимо получить дистрибутив “cdb” для этого.

  • dbm”: Вызовы к библиотечным функциям “dbm” используются для извлечения данных из файлов DBM, путём поиска записей с данным ключом. Завершающий бинарный ноль включён в ключ, который передаётся библиотеке DBM. Смотрите раздел 4.3, для обсуждения библиотек DBM.

Для всех версий Berkeley DB, exim использует стиль DB_HASH базы данных, когда собирает DBM-файлы используя утилиту “exim_dbmbuild”. Однако, когда используется Berkeley DB версий 3 и 4, он открывает для чтения, существующие базы данных, с опцией DB_UNKNOWN. Это позволяет ему обработать любой из типов БД поддерживаемых библиотекой, и может быть полезным для доступа к DBM-файлам созданным другими приложениями. (Для более ранних версий DB, всегда используется DB_HASH.)

  • dbmnz”: Это тоже самое, что и “dbm”, за исключением, что, завершающий бинарный ноль не включен в ключ передаваемый библиотеке DBM. Вам может понадобиться использовать это, если вы хотите искать данные в файлах, которые созданы или расшарены с каким-либо иным приложением, которое не использует завершающий ноль. Например, вы должны использовать “dbmnz” а не “dbm”, если вам необходимо аутентифицировать входящие SMTP-подключения, используя пароли из файла “/etc/userdbshadow.dat” Сourier`a. Утилита exim`a для создания файлов DBM (“exim_dbmbuild”) по дефолту включает нули, но у неё есть опция для их исключения (смотрите раздел 49.9).
  • dsearch”: Данный файл должен быть директорией; ищется файл, имя которого равно ключу. Ключ не должен содержать символов слэша. Результат успешного поиска - имя файла. Пример, как этот поиск может использоваться для поддержки виртуальных доменов, дан в разделе 46.7.
  • iplsearch”: Данный файл - текстовый файл, содержащий ключи и данные. Ключ завершается двоеточием, или пробелом, или концом строки. Ключи, в файле, должны быть IP-адресами, или IP-адресами с CIDR масками. Ключи включающие в себя адреса IPv6, должны быть заключены в кавычки, для предотвращения интерпретации первого внутреннего двоеточия как завершение ключа. Например:
1.2.3.4:           data for 1.2.3.4
192.168.0.0/16     data for 192.168.0.0/16
"abcd::cdab":      data for abcd::cdab
"abcd:abcd::/32"   data for abcd:abcd::/32

Ключ для “iplsearch” поиска должен быть IP-адресом (без маски). Поиск по файлу линейный, с использованием масок CIDR, где они заданы, до нахождения соответствия ключу. Используется первый совпадающий ключ; дальнейших попыток найти “лучшее” совпадение, не предпринимается. Кроме совпадения ключей, обработка “iplsearch” - такая же как у “lsearch”.

Предупреждение 1: В отличие от большинства других одноключевых поисков, файл данных для “iplsearchне может быть превращён в DBM или cdb-файл, поскольку эти типы поиска поддерживают только
буквальные ключи.
Предупреждение 2: В списке хостов,вы всегда должны использовать “net-iplsearch”, таким 
образом, чтобы неявный ключ был IP-адресом, а не именем (смотрите раздел 10.12).
  • lsearch”: Данный файл - текстовый файл, по которому линейно ищется строка, начинающаяся с искомого ключа, законченную двоеточием, или пробелом, или концом строки. Используется первое найденное совпадение. пустое место между ключом и двоеточием разрешается. Остаток строки, после удаления начального и конечного пустого пространства является данными. Они могут быть продолжены на последующие строки путём начала их с любого количества пустого пространства, но только один символ пробела включается в данные при таком соединении. Если данные начинаются с двоеточия, ключ должен быть завершён двоеточием, например:
baduser:  :fail:

Пустые строки, и строки начинающиеся с “#” игнорируются, даже если они встречаются в середине строки. Это - традиционный текстовый формат файла альясов. Обратите внимание, что ключи в файле “lsearch” - литеральные строки. Тут нету подстановки (“wildcarding”) какого бы то ни было вида.

В большинстве “lsearch” файлов, ключи не могут содержать двоеточия, или символы “#”, или пустые пробелы. Однако, если вам необходима эта возможность, она доступна. Если ключ начинается с символа двойной кавычки, она завершается только соответствующей кавычкой (или концом строки), и, к её содержимому, применяются обычные правила экранирования (смотрите раздел 6.16). Опционально, двоеточие разрешено после ключа в кавычках (также как и для ключей без кавычек). Специальная обработка кавычек для части данных строки “lsearch” отсутствует.

  • nis”: Данный файл - имя карты NIS, и поиск NIS производится с данным ключом, без завершающего двоичного нуля. Есть вариант, называемый “nis0”, который включает двоичный нуль в ключ. По сведениям, это необходимо для файла альясов в стиле SUN. Exim не понимает NIS-альясы; должны использоваться полные имена карт.
  • wildlsearch” или
  • nwildlsearch”: Поиск по файлу линейный, как “lsearch”, но вместо того, чтобы интепретировать как литеральную строку, каждый ключ в файле может быть подстановочным. Различие между этими двумя типами поиска в том, что для “wildlsearch” каждый ключ в файле раскрывается до начала использования, тогда как для “nwildlsearch” нет раскрытия на месте.

Как и “lsearch”, тестирование производится без учёта регистра. Признаются следующие формы подстановочных знаков:

  • 1. Строка может начинаться со звёздочки, для обозначения “кончается на”. Например:
*.a.b.c       data for anything.a.b.c
*fish         data for anythingfish
  • 2. Строка может начинаться с крышки (“^”), для обозначения регулярного выражения. Например, для “wildlsearch”:
^\N\d+\.a\.b\N    data for <digits>.a.

Примечание - использование “\N” отключает раскрытие содержимого регулярного выражения. Если вы используете “nwildlsearch” там, где ключи не раскрываются, это эквивалентно:

^\d+\.a\.b        data for <digits>.a.b

Если регулярное выражение содержит пустое место, или символы двоеточия, вы должны поместить его в кавычки (смотрите “lsearch”, выше), или представить эти символы другим образом. Например, “\s” может быть использовано для обозначения пробела и “\x3A” - для двоеточия. Это может оказаться легче, чем использовать кавычки, поскольку при использовании кавычек, вы должны экранировать все обратные слэши внутри кавычек.

Примечание: Невозможно зафиксировать подстроки в совпадении регулярного выражения, для 
дальнейшего использования, поскольку результаты всех поисков кэшируются. Если поиск повторяется, 
результат берётся из кэша, и нет фактического сопоставления с образцом. Значения всех цифровых 
переменных сбрасываются после совпадения “(n)wildlsearch”.
  • 3. Хотя я не вижу много применений, общая функция соответствия, используемая для реализации “(n)wildlsearch”, означает, что строка может начинаться с имени поиска, завершаемого двоеточием, и сопровождаться данными поиска. Например:
cdb;/some/file  data for keys that match the file

Данные, полученные из вложенного поиска, отвергаются.

Ключи, которые не соответствуют ни одному из этих паттернов, интепретируются буквально. Правила продолжения для данных - точно такие же как для “lsearch”, и ключи могут сопровождаться опциональными двоеточиями.

  Предупреждение: В отличие от большинства других одноключевых поисков, файл данных для 
“(n)wildlsearchне может быть превращён в DBM или cdb-файл, поскольку эти типы поиска поддерживают 
только буквальное соответствие.

Типы поиска в стиле запроса

Поддерживаемые типы поиска в стиле запроса перечислены ниже. Дальнейшие детали, о многих из них, даны в дальнейших разделах.

  • dnsdb”: Этот производит поиск одной или более записей, чьи доменные имена даны в предоставленном запросе. Результирующие данные - содержимое записей. Смотрите раздел 9.10.
  • ibase”: Этот производит поиск по БД Interbase.
  • ldap”: Этот производит поиск по LDAP, используя запрос в форме URL, и возвращает атрибуты единственного элемента. Есть вариант, вызывающий “ldapm”, который разрешает возврат значений от нескольких элементов. Третий вариант, называемый “ldapdn”, возвращает Distinguished Name (отличительное имя) одного элемента, вместо любых значений атрибутов. Смотрите раздел 9.13.
  • mysql”: Формат запроса - SQL-выражение, передаваемое БД MySQL. Смотрите раздел 9.20.
  • nisplus”: Этот производит поиск в NIS+, используя запрос, который может задать имя поля для возврата. Смотрите раздел 9.19.
  • oracle”: Формат запроса - SQL-выражение, передаваемое БД Oracle. Смотрите раздел 9.20.
  • passwd”: Поиск в стеле запросов, с запросами, которые содержат лишь имя пользователя. Поиск вызывает “getpwnam()”, для запроса данных системного пароля, и при успехе, строка результата - то же самое, что вы бы получили из поиска “lsearch” в традиционном файле паролей “/etc/passwd file”, со значением “*” в качестве значения пароля. Например:
*:42:42:King Rat:/home/kr:/bin/bash
  • pgsql”: Формат запроса - SQL-выражение, передаваемое БД PostgreSQL. Смотрите раздел 9.20.
  • sqlite”: Формат запроса - имя файла, сопровождаемое SQL-выражением, передаваемым БД SQLite. Смотрите раздел 9.24.
  • testdb”: Это тип поиска, используемый для тестирования exim. Он врятли будет полезен в обчной ситуации.
  • whoson”: “Whoson” (http://whoson.sourceforge.net) - предложенный протокол Интернета, который разрешает программам серверов Интернета проверять, какой из двух специфических (динамически выделенных) IP-адресов, в данный момент выделен известному (доверенному) пользователю, и опционально, получить идентификацию упомянутого пользователя. В exim, это может использоваться для реализации проверки условия ACL “POP перед SMTP”, например:
require condition = \
  ${lookup whoson {$sender_host_address}{yes}{no}}

Запрос состоит из единственного IP-адреса. Возвращённое значение - имя аутентифицированного пользователя, который сохранён в переменной “$value”. Однако, в этом примере, данные “$value” не используются; результат поиска - одна из фиксированных строк - “yes” or “no”.

Временные ошибки в поисках

Функции поиска могут вернуть коды временных ошибок, если поиск не может быть завершён. Например, БД SQL или LDAP могут быть недоступны. Поэтому не желательно использовать поиск, которые мог бы сделать такое для критичных опций, например, списка локальных доменов.

Когда поиск не может быть завершён в роутере или транспорте, доставка сообщения (к релевантному адресу) задерживается, как и для других временных ошибок. При других обстоятельствах, exim может предположить, что поиск был неудачен, или может вообще всё бросить.

Дефолтовые значения в одноключевых поисках

В этом контексте, “дефолтовые значения” - это значения заданные администратором, которое должно использоваться, если поиск неудачен.

Если “*” добавляется к одноключевому типу поиска (например “lsearch*”) и начальный поиск неудачен, ключ “*” ищется в файле, для нахождения значения по-умолчанию. Также, смотрите раздел о частичном соответствии, ниже.

Альтернативно, если “*” добавляется к одноключевому типу поиска (например “dbm*@”), тогда, если начальный поиск неудачен и ключ содержит символ “@”? второй поиск производится заменив все на “*”, до последней “@”. Это позволяет предоставить значения по умолчанию на домен, в файлах альясов, включающих домены в ключи. Если воторой поиск неудачен (или его нет, потому что в ключе нет “@”), ищется “*”. Например, роутер “redirect” мог бы содержать:

data = ${lookup{$local_part@$domain}lsearch*@{/etc/mix-aliases}}

Предположим, обрабатываемый адрес - “jane@eyre.example”. Exim ищет эти ключи в таком порядке:

jane@eyre.example
*@eyre.example
*

Данные берутся из любого ключа найденного первым.

Примечание: В файле “lsearch”, это не означает первый из этих ключей в файле. Полное 
сканирование производится для каждого ключа, и лишь если он не найден, exim пробует следующий ключ.

Частичное совпадение в одноключевых поисках

Нормальная операция одноключевого поиска - поиск в файле, точного соответствия заданному ключу. Однако, во множестве ситуаций в которых ищутся домены, было бы полезным частичное соответствие. В этом случае, информация в файле, которая начинается с “*.”, совпадает с любым доменом заканчивающимся компоненами, следующими за точкой. Например, если ключ в DBM-файле такой

*.dates.fict.example

тогда, когда частичное соответствие включено, это совпадает (в том числе) “2001.dates.fict.example” и “1984.dates.fict.example”. Также совпадает с “dates.fict.example”, если эта строка не появляется как отдельный ключ в файле.

Примечание: Частичное соответствие не доступно для поисков в стиле запроса. Также оно недоступно 
для поиска любых элементов в списках адресов (смотрите раздел 10.18).

Частичное соответствие реализовано путём отдельных поисков с использованием ключей сконструированных путём модификации оригинального ключа. Это означает, что он может использоваться с любым типом одноключевого поиска, при условии, что частично совпадающие ключи, начинающиеся со специального префикса (по умолчанию - “*.”), включены в файл данных. Ключи в файле, которые не начинаются с префикса, совпадают только с немодифицированными ключами, когда используется частичное соответствие.

Частичное соответствие вызывают путём добавления строки “partial-” к началу имени одноключевого типа поиска, например, “partial-dbm”. Когда это происходит, вначале ищется немодифицированный объект ключа; если поиск неудачен, “*.” добавляется вначале ключа, и снова производится поиск. Если он неудачен, будущие поиски пробуют удалять разделённые точками компоненты, он начала ключа, один за одним, и добавляя “*.” к началу того, что осталось.

Требуемое минимальное число не-* компонентов - два. Это может быть скорректировано включением числа до дефиса, в типе поиска. Например, “partial3-lsearch” задаёт минимум три не-* компонента в измененённых ключах. Отсутствие числа эквивалентно “partial2-”. Если ключ “2250.dates.fict.example”, тогда следующие ключи ищутся, когда минимальное число не-* компонентов - два:

2250.dates.fict.example
*.2250.dates.fict.example
*.dates.fict.example
*.fict.example

Как только один ключ, в последовательности, успешно найден, поиск завершён.

Использование “*.”, как дефолтового префикса, может быть изменено. Мотивацией для этой возможности является разрешение exim`y работать с форматами файлов используемыми другими MTA. Иной префикс может быть предоставлен в круглых скобках, вместо дефиса, после “partial”. Например:

domains = partial(.)lsearch;/some/file

В этом примере, если домен - “a.b.c”, последовательность поисков - “a.b.c”, “.a.b.c” и “.b.c” (при неизменённом дефолтовом минимуме в 2 компонента). Префикс может состоять из любых символов пунктуации, кроме закрывающей круглой скобки. Он может быть пустым, например:

domains = partial1()cdb;/some/file

Для этого примера, если домен “a.b.c”, последовательность поиска будет “a.b.c”, “b.c” и “c”.

Если задан “partial0”, что случается в конце (когда поиск, с лишь одним неподстановочным компонентом, неудачен и оригинальный ключ укорачивается вправо на нулевую строку) зависимостей от префикса:

  • Если префикс имеет нулевую длину, весь поиск неудачен.
  • Если длинна префикса равна 1, поиск производится лишь для префикса. Например, заключительный поиск для “partial0(.)” является единственным для “.”.
  • Иначе, если префикс заканчивается точкой, точка удаляется, и ищется оставшаяся часть. Поэтому, с дефолтовым префиксом, финальный поиск для “*” самостоятелен.
  • Иначе, ищется полный префикс.

Если тип поиска заканчивается на “*” или “*@” (смотрите выше, раздел 9.6), поиск окончательного дефолтового значения, подразумевающего эти последовательности, происходит после неудачи всех поисков. Однако, тут можно использовать поиск типа “partial0(.)lsearch*”.

Использование “*”, в частично соответствующем поиске, отличается от её использования как подстановочного символа в списках доменов и тому подобном. Частичное соответствие работает только в виде компонентов разделённых точкой; ключ, например “*fict.example” бесполезен в БД, поскольку звёздочка в частично совпадающем ключе всегда сопровождается точкой.

Кэширование поиска

Exim кэширует все результаты поисков, для избежания бесполезных повторений поисков. Однако, поскольку (кроме даемона) exim работает как коллекция независимых, короткоживущих процессов, это кэширование применяется только в пределах одного процесса exim`a. Средства для межпроцессного кэширования отсутствуют.

Для одноключевого поиска, exim оставляет релевантные файлы открытыми в случае, если есть другой поиск, нуждающийся в них. В некоторых типах конфигураций, это может привести к большому числу открытых файлов, сохраняемых открытыми, оставляемых открытыми для сообщений со многими получателями. Для избежания попаданий под системные ограничения на число открытых файлов, exim закрывает последний использованный файл, когда необходимо открыть больше файлов чем позволяют его внутренние ограничения, которое можно изменить через опцию “lookup_open_max”.

Файлы одноключевого поиска закрываются и сбрасывается кэш поиска в стратегических точках доставки - например, после завершения всех роутингов.

Квотирование (помещение в двойные кавычки) данных поиска

Когда данные из входящего сообщения включаются в поиск типа запросов, возможно появление специальных символов в данных, нарушающих синтаксис запроса. Например, запрос NIS+ содержащий

[name=$local_part]

будет прерван, если локальная часть будет содержать правую квадратную скобку. Для данных NIS+, данные должны быть помещены в двойные кавычки, как в примере:

[name="$local_part"]

но это оставляет проблемы с кавычками в данных. Правила для NIS+ состоит в том, что двойные кавычки должны быть удвоены. Другие типы поиска имеют иные правила, и для решения этих требований существет оператор раскрытия такой формы:

${quote_<lookup-type>:<string>}

Например, самый безопасный способ написания NIS+ запроса:

[name="${quote_nisplus:$local_part}"]

Смотрите раздел 11 для полного обзора раскрытия строк. Оператор кавычек может использоваться для всех типов поисков, но он не имеет эффекта в одноключевых поисках, т.к. кавычки в них никогда не бывают необходимы.

Дополнительные сведения о dnsdb

Тип поиска “dnsdb” использует DNS как базу данных. Простой запрос содержит тип записи и имя домена, разделённые знаком равно (“=”). Например, строка раскрытия может содержать:

${lookup dnsdb{mx=a.b.example}{$value}fail}

Если поиск успешен, результат помещается в “$value”, которая, в этом случае, используется как результат. Если поиск успешен, ключевое слово “fail” вызывает принудительную ошибку раскрытия (“forced expansion failure”) - смотрите раздел 11.4 для понимания, что это означает.

Поддерживаемые типы DNS-записей - A, CNAME, MX, NS, PTR, SRV, and TXT, и когда exim скомпилирован с поддержкой IPv6 - AAAA (и A6, если это тоже сконфигурировано). Если тип не задан, предполагается TXT. Когда тип PTR, данные могут быть нормально записанным IP-адресом; инверсия и добавление “in-addr.arpa” или “ip6.arpa” происходят автоматически. Например:

${lookup dnsdb{ptr=192.168.4.5}{$value}fail}

Если данные для PTR-записи не являются синтаксически допустимым IP-адресом, он не изменяется и ничего не добавляется.

Для поиска MX, для каждой записи возвращаются оба привилегированных значения, и имя хоста, разделённые пробелом. Для поиска SRV, приоритет, вес, порт и имя хоста возвращаются для каждой записи, разделённые пробелами.

Для любых типов записей, если найдено много записей (или, для поиска A6, если одна запись ведёт ко многим адресам), данные возвращаются как объединение, с символом новой строки, как дефолтовым разделителем. Порядок, разумеется, определяется DNS-резольвером. Вы можете задать иной разделитель символов, между несколькими записями, путём помещения в начале запроса правой угловой скобки, сопровождаемой (без пробелов) новым разделителем. Например:

${lookup dnsdb{>: a=host1.example}}

Разрешается задать пробел, как символ разделителя. дальнейшее пустое пространство игнорируется.

Псевдо-“dnsdb” типы записей

По-умолчанию, и предпочтительное значение, и имя хоста, возвращаются для каждой MX-записи, разделённые пробелами. Если вам нужны только имена хостов, вы можете использовать псевдо-тип MXH:

${lookup dnsdb{mxh=a.b.example}}

В этом случае, предпочтительное значение опущено, и возвращаются только имена хостов.

Другой псевдо-тип - ZNS (расшифровывается “zone NS”). Он выполняет поиск NS-записи для данного домена, но если она не найдена, он удаляет первый компонент имени домена, и пробует снова. Этот процесс продолжается пока не найдена NS-запись, или не останется компонентов имени (или произойдёт ошибка DNS). Другими словами, он может вернуть сервер имён домена верхнего уровня, но никогда не вернёт корневой сервер имён. Если нет NS-записей домена верхнего уровня, поиск неудачен. Рассмотрите эти примеры:

${lookup dnsdb{zns=xxx.quercite.com}}
${lookup dnsdb{zns=xxx.edu}}

Предполагается, что в каждом случае тут нет NS-записей для полного доменного имени, в первом случае сервером имён возвращается значение для “quercite.com”, и во втором случае сервером имён возвращается значение для “edu”.

Вы должны быть внимательны при использовании этого типа поиска, поскольку, если домен верхнего уровня не существует, поиск всегда вернёт какое-то имя домена. Это могло бы использоваться для того, чтобы видеть, находится ли сервер имён данного домена в чёрном списке. Вероятно, вы можете предполагать, что сервера имён для доменов верхнего уровня, таких как “su” или “co.uk” не собираются находиться в таких списках.

Третий псевдо-тип - CSA (Client SMTP Authorization). Он ищет SRV-записи для правил CSA, которые описаны в разделе 39.37. Хотя “dnsdb” непосредственно поддерживает поиски SRV, этого недостаточно, из-за дополнительного режима поиска родительских доменов CSA. Результат успешного поиска, например:

${lookup dnsdb {csa=$sender_helo_name}}

имеет два разделённых пробелами поля: код авторизации и имя целевого хоста. Авторизационный код может быть “Y” для yes, “N” для no, “X” для явно требуемой, но отсутствующей авторизации, или “?” для неизвестного.

Множественные поиски “dnsdb”

В предыдущих разделах описаны поиски для одиночного домена. Однако, вы можете задать список доменов или адресов в отдельном “dnsdb” поиске. Список задаётся в нормальном виде exim`a, с двоеточием в качестве дефолтового разделителя, но с возможностью изменить его. Например:

${lookup dnsdb{one.domain.com:two.domain.com}}
${lookup dnsdb{a=one.host.com:two.host.com}}
${lookup dnsdb{ptr = <; 1.2.3.4 ; 4.5.6.8}}

Для сохранения обратной совместимости, есть один специальный случай: если тип поиска PTR и не указано изменение разделителя, exim смотрит, не является ли остаток строки одним IPv6 адресом. В этом случае, он не обрабатывает её как список.

Данные каждого поиска объединены, с символом новой строки в качестве дефолтового разделителя, таким образом обрабатываются множественные DNS-записи для одного элемента. Может быть задан иной разделитель, как указано выше.

Поиск “dnsdb” неудачен, лишь если неудачны все все DNS-поиски. Если для любого из них происходит временная ошибка DNS, то поведением управляет опциональное ключевой слово, с последующей запятой, могущей появиться перед типом записи. Возможные ключевые слова - “defer_strict”, “defer_never”, и “defer_lax”. С “strict” поведением, любая временная ошибка DNS вызывает задержку всего поиска. С “never” поведением, временные ошибки DNS игнорируются, и поведение такое, будто поиск в DNS не привёл ни к чему. С “lax” поведением, предпринимаются все запросы, но временные ошибки DNS вызывают задержку лишь в случае, если если остальные поиски были безуспешны. Дефолт - “lax”, таким образом, следующие поиски эквивалентны:

${lookup dnsdb{defer_lax,a=one.host.com:two.host.com}}
${lookup dnsdb{a=one.host.com:two.host.com}}

Следовательно, в дефолтовом случае, поиск успешен до тех пор, пока хоть один поиск в DNS привёл к каким-то данным.

Дополнительные сведения о LDAP

Оригинальная реализация LDAP была сделана в University of Michigan; она стала “Open LDAP”, и сейчас существует два различных релиза. Другая реализация происходит из Netscape, Solaris 7 и последующие релизы содержат встроенную поддержку LDAP. К сожалению, хотя все они совместимы на уровне функционирования запросов, обработка их ошибок различна. По этой причине необходимо установить переменную, во время компиляции exim`a с LDAP, для указания, какая библиотека LDAP используется. Одна из следующих строк должна быть в вашем “Local/Makefile”:

LDAP_LIB_TYPE=UMICHIGAN
LDAP_LIB_TYPE=OPENLDAP1
LDAP_LIB_TYPE=OPENLDAP2
LDAP_LIB_TYPE=NETSCAPE
LDAP_LIB_TYPE=SOLARIS

Если “LDAP_LIB_TYPE” не задана, exim предполагает “OPENLDAP1”, имеющий такой же интерфейс, как и версия University of Michigan.

Есть три типа поиска LDAP в exim. Они ведут себя по-разному, когда обрабатывают результаты запроса:

  • ldap” - требует, чтобы результат содержал только один элемент; если их больше - он выдаёт ошибку.
  • ldapdn” - также требует, чтобы результат содержал только один элемент, но запросом должно быть возвращено Distinguished Name, а не любые атрибуты со значением.
  • ldapm” - разрешает результату содержать более одного элемента; все их атрибуты возвращаются запросом.

Для “ldap” и “ldapm”, если запрос находит лишь входы без атрибутов, exim ведёт себя, как будто вхождения не найдены, и поиск неудачен. Формат данных, возвращаемых успешным поиском описаны в следующей секции. Сначала мы объясняем, как кодируются LDAP-запросы.

Формат запросов LDAP

Запрос к LDAP имеет форму URL, как определено в RFC 2255. Например, в конфигурации роутера “redirect”, могла бы быть такая установка:

data = ${lookup ldap \
  {ldap:///cn=$local_part,o=University%20of%20Cambridge,\
  c=UK?mailbox?base?}}

URL может начинаться с “ldap” или “ldaps”, если ваша библиотека LDAP поддерживает безопасные (шифрованные) LDAP-соединения. Второй из них гарантирует, что используются шифрованные подключения TLS.

Квотирование (использование двойных кавычек и спецсимволов) в LDAP

В запросах LDAP требуются два уровня квотирования, первый - непосредвственно для LDAP, и второй, поскольку запрос LDAP представлен как URL. Кроме того, внутри LDAP-запроса, требуются два различных вида квотирования. Поэтому есть два различных, LDAP-специфичных, оператора квотирования.

Оператор “quote_ldap” спроектирован для использования на строках, являющихся частью спецификации фильтра. Концептуально, он, вначале, производит следующие преобразования строки:

*   =>   \2A
(   =>   \28
)   =>   \29
\   =>   \5C

в соответствии с RFC 2254. Результирующая строка квотируется согласно правилам для URL, т.е. все не алфавитно-цифровые символы, кроме:

! $ ' - . _ ( ) * +

конвертируются в их шестнадцатеричные значения, с предшествующим им символом процента. Например:

${quote_ldap: a(bc)*, a<yz>; }

превращается в

%20a%5C28bc%5C29%5C2A%2C%20a%3Cyz%3E%3B%20

Удалив квотирование URL, это (с начальным и конечным пустым пространством):

a\28bc\29\2A, a<yz>;

Оператор “quote_ldap_dn” спроектирован для использования на строках, являющихся частью базовых спецификаций DN, в запросех. Концептуально, вначале он конвертирует строку, вставляя обратный слэш перед любым из следующих символов:

, + " \ < > ;

Он также вставляет обратный слэш перед любыми пробелами или символом “#”, и перед конечными пробелами. (Правила находятся в RFC 2253.) Тогда результирующая строка квотирована согласно правилам для URL. Например:

${quote_ldap_dn: a(bc)*, a<yz>; }

будет

%5C%20a(bc)*%5C%2C%20a%5C%3Cyz%5C%3E%5C%3B%5C%20

Удалив квотирование URL, получится (с конечными пробелами)

\ a(bc)*\, a\<yz\>\;\ 

Есть некоторые дальнейшие комментарии о квотировании в секции о аутентификации LDAP, ниже.

Соединения LDAP

Подключение к серверу LDAP может быть через TCP/IP, или, когда используется OpenLDAP, через сокет UNIX. Пример, данный выше, не определяет сервер LDAP. Сервер, который доступен по TCP/IP, может быть задан в запросе, запуская его так:

ldap://<hostname>:<port>/...

Если порт (и предыдущее двоеточие) опущены, используется стандартный порт LDAP (389). Если в запросе не указан сервер, список дефолтовых серверов берётся из конфигурационной опции “ldap_default_servers”. Он предоставляет список серверов, разделённых двоеточиями, пробуемых по очереди, пока запрос не будет успешно обработан, или не произойдёт серьёзная ошибка. Успешная обработка или вернёт запрошенные данные, или укажет, что они не существуют. Серьёзные ошибки - синтаксические, или много значений, когда ожидается только одно. Ошибки, приводящие к пробе следующего сервера - сбои подключения, привязки, и таймауты.

Для каждого имени сервера, в списке, можно задать номер порта. Стандартный способ задания хоста и порта - использование двоеточия, как разделителя (RFC 1738). Поскольку “ldap_default_servers” - список значений разделённых двоеточиями, такие двоеточия должны быть удвоены. Например:

ldap_default_servers = ldap1.example.com::145:ldap2.example.com

Если “ldap_default_servers” не задана, библиотеке LDAP передаётся URL без имени сервера, и используется дефолтовое значение библиотеки (обычно - локальный компьютер).

Если вы используете библиотеку OpenLDAP, вы можете соединится с LDAP-сервером используя сокет UNIX, вместо подключения через TCP/IP. Это задаётся использованием “ldapi” вместо “ldap” в LDAP-запросах. Нижеследующее (имеется ввиду - про “ldapi” - прим. lissyara), применяется только в OpenLDAP. Если exim скомпилирован с поддержкой различных LDAP-библиотек, эта возможность недоступна.

Для этого типа соединения, вместо имени хоста, требуется имя-путь сокета, и номер порта неуместен. Имя-путь может быть указано как элемент в “ldap_default_servers”, или встроено в запрос. В первом случае, вы будете иметь настройки типа таких:

ldap_default_servers = /tmp/ldap.sock : backup.ldap.your.domain

Когда путь с именем указываются в запросе, вы должны заменить прямые слэши последовательностью “%2F” для соблюдения синтаксиса LDAP URL. Например:

${lookup ldap {ldapi://%2Ftmp%2Fldap.sock/o=...

Когда exim производит поиск LDAP, и находит, что “имя хоста” (“hostname”) - реальный путь к сокету, он использует код сокета UNIX, даже если запрос задаёт использование “ldap” или “ldaps”. В частности, для соединения с сокетом не используется шифрование. Это поведение означает, что вы можете использовать настройки, например, “ldap_default_servers”, в примере выше, с традиционными “ldap” или “ldaps”, и эо будет работать. Вначале, exim пробует соединиться через через сокет UNIX; если это не удаётся, он пробует подключиться по TCP/IP к резервному хосту.

Если в запросе задаётся явный тип “ldapi”, при указанном имени хоста, диагностируется ошибка. Однако, если есть другие элементы в “ldap_default_servers”, пробуются они. Другими словами:

  • Использование пути к сокету с “ldap” или “ldaps” вызывает использование интерфейса сокета UNIX.
  • Использование “ldapi” с именем хоста вызывает ошибку.

Использование “ldapi” без хоста или пути в запросе, и без установки “ldap_default_servers”, делает то, что библиотека делает по умолчанию.

Аутентификация LDAP и управляющая информация

Синтаксис LDAP URL не предоставляет пути передачи аутентификационной и иной управляющей информации на сервер. Чтобы сделать это возможным, URL в запросе LDAP может предшествоваться любым числом установок “<name>=<value>”, разделённых пробелами. Если значение содержит пробелы, они должны быть помещены в двойные кавычки, и, когда используются двойные кавычки, надо использовать обратный слэш, как обычно. Распознаются следующие имена:

имя значение
DEREFERENCE установить параметр разименования
NETTIME установить таймаут сетевой операции
USER установить DN для аутентификации связи LDAP
PASS установить пароль для аутентификации связи LDAP
SIZE установить ограничение числа возвращаемых входов
TIME установить таймаут запроса

Значение параметра “DEREFERENCE” должно быть одним из слов “never”, “searching”, “finding”, или “always”.

Имя “CONNECT” - устаревшее имя “NETTIME”, сохраненноё для обратной совместимости. Этот таймаут (заданный как число секунд) устанавливатся с клиентской стороны, для операций, который могут быть выполнены по сети. Специально, это применяется к сетевым соединениям и вызовам функции “ldap_result()”. Если значение больше чем ноль, используется LDAP_OPT_NETWORK_TIMEOUT, если задано в заголовках LDAP (OpenLDAP), или, если в заголовках LDAP (Netscape SDK 4.1) задано LDAP_X_OPT_CONNECT_TIMEOUT. Нулевое значение вызывает явную установку “no timeout” для Netscape SDK; для OpenLDAP никакого действия не происходит.

Параметр TIME (также число секунд) передаётся на сервер для установки серверных ограничений на время потраченное на поиск.

Вот пример запроса LDAP в поиске exim`a, использующем некоторые из этих значений. Это - одна строка, перенесённая, чтобы поместиться на странице:

${lookup ldap
  {user="cn=manager,o=University of Cambridge,c=UK" pass=secret
  ldap:///o=University%20of%20Cambridge,c=UK?sn?sub?(cn=foo)}
  {$value}fail}

Кодирование пробелов, как “%20” - из URL, его нелььзя делать для каких-либо вспомогательных данных. Конфигурационные настройки exim`a, включающие поиски содержащие информацию о пароле, необходимо предварять “hide”, чтобы предотвратить возможность увидеть эти значения не-административными пользователями, при использовании опции “-bP”.

Вспомогательные данные могут быть даны в любом порядке. По умолчанию - таймаут отсутсвует (используется системный таймаут), нет пользователя или пароля, нет ограничений на число возвращённых значений, и нет ограничений по времени запроса.

Когда DN квотирован в USER= setting для LDAP аутентификации, exim удаляет любое URL-квотирование, которое может быть до LDAP. Очевидно, некоторые библиотеки делают это для себя, но некоторые нет. Удаление URL-квотирование даёт два преимущества:

  • Это позволяет использовать тоже самое раскрытие “quote_ldap_dn” для USER= DNs, что и для DNs внутри фактических запросов.
  • Это разрешает пробелы внутри USER= DNs.

Например, настройка типа

USER=cn=${quote_ldap_dn:$1}

должна работать, даже если “$1” содержит пробелы.

Раскрытые данные для PASS= value должны быть квотированы с использованием оператора раскрытия “quote”, а не оператора квотирования LDAP. Единственная причина, по которой это поле нуждается в квотировании, состоит в том, чтобы гарантировать его соответствие синтаксису exim`a, который не разрешает пробелы вне кавычек. Например:

PASS=${quote:$3}

Аутентификационный механизм LDAP может использоваться для проверки паролей, как часть SMTP-аутентификации. Смотрите условие раскрытия строки “ldapauth” в разделе 11.

Формат данных возвращённых LDAP

Типы поиска “ldapdn” возвращают Distinguished Name (отличительное имя) из единственного элемента, как последовательность значений, например:

cn=manager, o=University of Cambridge, c=UK

Тип поиска “ldap” генерит ошибку, если более одного элемента соответствует фильтру поиска, тогда как “ldapm” разрешает этот случай, и вставляет новую строку в результат, до данных от различных входов. Это возможно для многочисленных значений возвращённых для обоих “ldap” и “ldapm”, но в первом случае вы знаете, что независимо от возвращённого значения, исходили из одиночного вхождения в директории.

В общем случае, где вы задаёте один атрибут в вашем LDAP-запросе, результат не квотируется, и не содержит имя атрибута. Если атрибут имеет множественные значения, они разделяются запятыми.

Если вы определяете множественные атрибуты, результат содержит разделённые пробелами, квотированные строки, каждая с предшествующим именем атрибута и символом равно. В пределах кавычек, символ двойной кавычки, обратного слэша и новой строки экранируется обратным слэшем, и запятые используются для разделения многочисленных значений атрибута. В части для экранирования, строка внутри кавычек принимает такую же форму как вывод, когда запрашивается единственный атрибут. Если никакие атрибуты не заданы, это тоже самое, что и задание всех атрибутов.

Это - некоторые примеры формата вывода. Первая строка каждой пары - запрос LDAP, и вторая - возвращённые данные. Атрибут называемый “attr1” имеет два значения, тогда как “attr2” - лишь одно:

ldap:///o=base?attr1?sub?(uid=fred)
value1.1, value1.2
ldap:///o=base?attr2?sub?(uid=fred) value two
ldap:///o=base?attr1,attr2?sub?(uid=fred) attr1="value1.1, value1.2" attr2="value two"
ldap:///o=base??sub?(uid=fred) objectClass="top" attr1="value1.1, value1.2" attr2="value two"

Оператор “extract” в раскытиях строки может быть использован для выбора индивидуальных полей из данных, состоящих из пар “key=value”. Вы модете использовать опцию exim`a “-be”, для хапуска теста раскрытия и таким образом проверить результаты поиска в LDAP.

Дополнительные сведения о NIS

Запросы NIS+ состоят из “индексного имени” (“indexed name”) NIS+, сопровождаемого опциональным двоеточием и именем поля. Если это дано, разультат успешного запроса - содержимое именованного поля; иначе - результат состоит из объединённых пар “field-name=field-value”, разделённых пробелами. пустые значения и значения содержащие пробелы помещаются в двойные кавычки. Например, запрос

[name=mg1456],passwd.org_dir

мог бы вернуть строку

name=mg1456 passwd="" uid=999 gid=999 gcos="Martin Guerre"
home=/home/mg1456 shell=/bin/bash shadow=""

(разбито на две строки чтобы пометиться на странице), тогда как

[name=mg1456],passwd.org_dir:gcos

вернул бы лишь

Martin Guerre

без кавычек. Поиск NIS+ неудачен если NIS+ возвращает больше одного элемента таблицы для данного индексного ключа. Эффект оператора раскрытия “quote_nisplus” удваивает любые символы кавычек внутри текста.

Поиски SQL

Exim может поддерживать поиски в Interbase, MySQL, Oracle, PostgreSQL, и SQLite базах данных. Запросы для этих БД содержат SQL-выражения, таким образом, пример мог бы быть таким

${lookup mysql{select mailbox from users where id='userx'}{$value}fail}

Если результат запроса содержит более одного поля, данные возвращаются для каждого поля, предшествующие его именем, таким образом, результат

${lookup pgsql{select home,name from users where id='userx'}{$value}}

мог бы быть

home=/home/userx name="Mister X"

Пустые значения, и значения содержащие пробелы помещаются в двойные кавычки, внутренние кавычки экранируются обратным слэшем. Если результат запроса содержит лишь одно поле, значение возвращается дословно, без имени поля, например:

Mister X

Если результат запроса приводит более чем к одной строке, они все объединяются, с новой строкой между данными для каждой строки.

Дополнительные сведения о MySQL, PostgreSQL, Oracle, и Interbase

Если используются какие-либо поиски в MySQL, PostgreSQL, Oracle, или Interbase, должна быть установлена опция “mysql_servers”, “pgsql_servers”, “oracle_servers” или “ibase_servers” (соответственно) в виде списка информации о сервере, разделённого двоеточиями. Каждый элемент в списке - разделённый слэшами список четырёх пунктов: имя хоста, имя БД, имя пользователя и пароль. В случае Oracle, поле имени хоста используется для “имени сервиса” (“service name”), поле имени базы данных не используется, и должно быть пустым. Например:

hide oracle_servers = oracle.plc.example//userx/abcdwxyz

Поскольку данные пароля секретны, вы всегда должны предшествовать настройку словом “hide”, для предотвращения просмотра установки не административными пользователями при использовании опции “-bP”. Вот пример, где перечислены два сервера MySQL:

hide mysql_servers = localhost/users/root/secret:\
                     otherhost/users/root/othersecret 

Для MySQL и PostgreSQL, хост может быть задан как “<name>:<port>”, но т.к. это список значений разделённых двоеточиями, то оно должно быть удвоено. Для каждого запроса, эти параметры групп проверяются, в порядке успешности соединений и запросов.

Операторы раскрытия “quote_mysql”, “quote_pgsql” и “quote_oracle” конвертируют новую строку, таб, возврат каретки и обратный слэш в “\n”, “\t”, “\r” и “\b” соответственно, и символы одиночной кавычки, двойной кавычки и обратного слэша экранируются обратным слэшем. Оператор раскрытия “quote_pgsql”, кроме того, экранирует символы процента и подчёркивания. Это нельзя делать для MySQL, поскольку эти символы экранирования не распознаются в контексте, где они они не экранируют специальные символы.

Специальные возможности MySQL

Для MySQL, пустое имя хоста, или использование “localhost” в “mysql_servers” вызывает соединение с сервером на локальном хосте через сокет UNIX. Альтернативный сокет может быть указан в круглых скобках. полный синтаксис каждого элемента в “mysql_servers” таков:

<hostname>::<port>(<socket name>)/<database>/<user>/<password>

Любая из трёх частей первого поля может быть опущена. Для нормального использования на локальном хосте можно оставить пробел, или установить лишь “localhost”.

Нет необходимости в указании БД, - если она тут отсутствует, то должна быть дана в запросах.

Если запрос MySQL не возвращает никаких данных (команды insert, update, или delete), результат поиска - число затронутых строк.

Внимание: Это может ввести в заблуждение. Если обновление ничего, фактически, не меняет (например, 
устанавливая поле на то же самое значение), результат - ноль, поскольку нет затронутых строк.

Специальные возможности PostgreSQL

Поиски в PostgreSQL также могут использовать сокет UNIX для соединения с БД. Обычно, это быстрей, и стоит меньше процессорного времени, чем подключение по TCP/IP. Однако он может использоваться лишь в случае, если сервер БД работает на той же самой машине, что и почтовый сервер. Конфигурационная строка для PostgreSQL, через сокет UNIX, выглядит так: hide pgsql_servers = (/tmp/.s.PGSQL.5432)/db/user/password : ...

Другими словами, вместо имени хоста даётся путь к сокету. путь заключён в круглые скобки так, чтобы его прямые слэши не были визуально перепутаны с разделителями других параметров сервера.

Если запрос PostgreSQL не возвращает никаких данных (команды insert, update, или delete), результат поиска - число затронутых строк.

Дополнительные сведения о SQLite

SQLite отличается от других поисков SQL, поскольку требуется имя файла, в дополнение к SQL-запросу. БД SQLite - один файл, и нет демона, как в других БД. Интерфейс exim`a требует чтобы имя файла, как абсолютный путь, было задано в начале запроса. Оно отделяется от запроса пустым пространством. Это означает что путь и имя файла не могут содержать пустые символы. Вот пример раскрытия поиска:

${lookup sqlite {/some/thing/sqlitedb \
  select name from aliases where id='userx';}}

В списке, похожий синтаксис. Например:

domainlist relay_domains = sqlite;/some/thing/sqlitedb \
   select * from relays where ip='$sender_host_address';

Единственный символ, затрагиваемый оператором “quote_sqlite” - символ одиночной кавычки, которую он удваивает.

Библиотека SQLite обрабатывает множественные одновременные доступы к БД внутренне. Множественные чтения разрешены, но лишь один процесс может производить обновление. Попытки обращения к БД, во время обновления, отклоняются после таймаута ожидания, в течение которого библиотека SQLite ждёт освобождения блокировки. В exim, дефолтовый таймаут установлен в 5 секунд, но это может быть изменено с помощью опции “sqlite_lock_timeout”.


translated by lissyara