Новости :

Имена Интернет (настройка DNS)

Имена Интернет

Подстрешный Артем,
программист
Radio-MSU Network,
art@radio-msu.net

Введение

Для человека, поработавшего даже небольшое время в сети, становится совершенно естественным, что у каждого компьютера, подключенного к интернет, есть свое название, имя, которое легко запомнить. Система, которая позволяет нам использовать эти привычные для человека имена, избегая других неудобных способов "маркировки" компьютеров, называется DNS (Domain Name System, доменная система имен).

В интернете существует, вообще говоря, два основных способа адресации компьютеров. Первый - численный (или IP-адрес; например, 193.124.134.101), второй - символьный (noc.radio-msu.net). DNS создана для того, чтобы поставить в соответствие один способ другому.

Чтобы облегчить упорядочивание наименований, вся структура компьютерных имен устроена таким образом:есть отдельные уровни (домены), которые могут включать в себя как другие поддомены, так и имена компьютеров. Все названия должны состоять только из латинских букв, цифр и, может быть, знака "минус". Отдельные уровни доменов разделяются точкой.

Типичное полное доменное имя компьютера может выглядеть так: computer3.otdel-5.firma.msk.ru В этом примере такой адрес мы присвоили компьютеру номер три, который стоит в отделе (номер сообразите сами) фирмы с изысканным английским названием "firma", которая находится в Москве ("msk"), в России ("ru"). Локальным именем компьютера (hostname) здесь является "computer3", а ".ru" обычно называется доменом верхнего уровня. Домен msk.ru, соответственно, является доменом второго уровня; firma.msk.ru - третьего...

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

Тот, кто имеет право администрировать домен, может делать изменения только в пределах этого домена. Например, системный администратор отдела N5 может, скажем, изменить имя "computer3" на "computer4" или на что-то более человеческое, например, назвать этот компьютер "julia" (Тогда полный его адрес станет julia.otdel-5.firma.msk.ru). Но для того, чтобы изменить имя домена 4-го уровня "otdel-5", администратору придется просить об этом у сисадмина фирмы (если, конечно, это не одно и тоже лицо). Процедура получения имени, например, в зоне .ru или .com называется регистрацией домена. Конечно, каждая компания, подключающаяся к интернет, стремится зарегистрировать как можно более естественное и легкое для запоминания имя. Так, для "Microsoft inc." логично зарезервировать домен microsoft.com

Доменов верхнего уровня очень немного - всего около 250.. Большая часть из них - так называемые, географические домены. Например, .de (Deutschland, Германия), .ru (Russia, Россия), .iq (Iraq, Ирак). Оставшиеся негеографические домены верхнего уровня - .com (для коммерческих компаний), .net (для сетевых ресурсов), .edu (образовательные учреждения), .mil (военные организации), .org (некоммерческие организации), .gov (правительственные ведомства), .int (интернациональные корпорации).

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

К началу 1998 года во всем интернете зарегистрировано около 30 миллионов хостов. Распределение по доменам верхнего уровня приведено в таблице:

домен  число хостов  описание
 com    8201511      Commercial
 net    5283568      Networks
 edu    3944967      Educational
 jp     1168956      Japan
 mil    1099186      US Military
 us     1076583      United States
 de      994926      Germany
 uk      987733      United Kingdom
 ca      839141      Canada
 au      665403      Australia
 org     519862      Organizations
 gov     497646      Government

Россия в этом списке находится на 28-м месте. Под доменом .ru зарегистрировано около 100 тысяч компьютеров. А в Антарктике (.aq), как оказывается, нет ни одного компьютера, подключенного к интернет..

DNS вашей компании

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

- Подключение к провайдеру. Физическое подключение может быть выполнено очень многими различными способами: от обычного модема до радиосетей и оптоволокна. Способ подключения и, соответственно, оплата оговариваются непосредственно с провайдером.

- Получение численных (IP) адресов. Для того, чтобы ваши компьютеры, подключенные к интернет, стали доступны, необходимо выделить для них уникальные численные адреса. Обычно в договоре на подключение к интернет указывается, сколько и каких адресов отдается в пользование вашей компании. Формат IP-адресов такой: четыре числа от 1 до 255, отделенных точками. Например, 193.124.134.101 - IP-адрес какого-то компьютера в сети.

- Настройка DNS. Допустим, вы получили блок IP-адресов. Теперь следует правильно сконфигурировать систему имен и корректно настроить работу серверов с этими именами.

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

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

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

Прежде всего, для полноценной работы DNS вам необходимо два или больше компьютеров, так называемых, name-серверов, которые независимо друг от друга подключенны к интернет (лучше, если они будут находиться в разных сетях или даже разных странах). Такая структура обеспечит неизменную работу системы преобразования символьного адреса в числовой и обратно, даже если какое-то время некоторые из этих компьютеров будут недоступны по сети. На таких компьютерах запускается специальная программа-демон named, которая обрабатывает запросы на преобразование адресов и отвечает на них. Настроить DNS - означает корректно написать конфигурационные файлы named.

Name-сервера бывают primary и secondary. Иногда их называют первичными и вторичными, а также master и slave. Primary name-сервер может быть только один. На нем хранится вся информация о доменах, и если происходят изменения, то конфигурация правится только на нем. Secondary name-серверов может быть несколько, но обычная практика - один secondary nameserver. Дополнительные вторичные name-сервера служат для повышения скорости расшифровывания вашего адреса и для повышения устойчивости такого преобразования. Для небольших сетей три и больше вторичных name-сервера - это уже излишество. Secondary name-сервера с заданной периодичностью в автоматическом режиме считывают текущую конфигурацию с primary-сервера. Заметим, что один и тот же компьютер может одновременно являться primary-сервером для одних доменов и secondary nameserver'ом для нескольких других. Конкретную реализацию таких DNS-серверов мы рассмотрим в следующих разделах.

Выбор и регистрация домена

Если вы имеете некоторый опыт работы в интернет, то вам, скорее всего, известны примеры доменных имен различных организаций и фирм. В качестве примера приведем случайно взятые адреса серверов:
www.playboy.com - web-сервер журнала "Playboy"
www.internic.net - основной web-сервер компании Network Solutions
rs.internic.net - регистрационный сервер Network Solutions
www.ripn.net - сервер организации RIPN


Как настроить DNS

Сама программа named в большинстве случаев входит в стандартный набор средств операционных систем UNIX. Если в вашем случае ее не оказалось, попробуйте сделать поиск программы named для вашей системы в поисковых серверах интернета (AltaVista, Lycos и т.д.). Обычно главный файл с настройками named называется named.boot и лежит в директории /etc. Запускать демон named обязательно с привилегиями пользователя root.

В данном случае рассматривается FreeBSD версии 2.2.2 со стандартной named. Сама программа лежит в /usr/sbin/named, а заготовки для конфигурации в директории /etc/namedb.

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

Скопируем named.boot в нужную директорию:

% cp /etc/namedb/named.boot /etc

Комментарии в named.boot начинаются с ";" на первом месте в строке.

Рассмотрим различные директивы в named.boot:


directory       /etc/namedb

Это та директория, в которой будут храниться конфигурационные файлы для каждого из доменов, которые этот name-сервер будет держать.


cache    .   named.root

Эта специальная опция задает имя файла named.root в директории /etc/namedb, в котором содержатся IP-адреса компьютеров, которые "знают все" о доменных именах, то есть, содержат домен "точка". В этих серверах хранится информация обо всех доменах верхнего уровня, и если на DNS-запрос ответ не был дан на более раннем этапе, то, добежав до одного из таких top-level серверов, запрос пойдет по необходимой ветке вниз. Вот кусок этого файла:


; formerly NS.INTERNIC.NET
.                        3600000  IN  NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4

; formerly NS1.ISI.EDU
. 3600000 NS B.ROOT-SERVERS.NET. B.ROOT-SERVERS.NET. 3600000 A 128.9.0.107

Файл named.root впоследствие нужно периодически забирать с FTP.RS.INTERNIC.NET, так как IP-адреса top-level серверов через несколько месяцев могут и поменяться.

Следующая директива в конфигурационном файле говорит о том, что наш name-сервер является primary name-сервером для какой-то зоны, то есть, содержит всю информацию о ней. Синтаксис ее таков:

primary [имя.домена] [имя_файла]

Пример:


primary radio-msu.net db.radio-msu.net
primary math.msu.su db.math.msu.su

Это означает, что мы установили primary nameserver для доменов radio-msu.net (домен второго уровня) и math.msu.su (третьего уровня), но мы проделали только небольшую часть работы. После этого нужно будет описать эти домены в текстовых файлах, называющихся db.radio-msu.net и db.msu.su в директории /etc/namedb. Мы специально выбрали имена файлов похожими на имена соответствующих им доменов из соображений удобства. Имена конфигурационных файлов больше нигде роли не играют, и вы вправе их называть как угодно. Создавать и редактировать их можно при помощи обычного текстового редактора, находясь в директории /etc/namedb. Подробное описание db.* файлов будет дано чуть ниже.

Для того, чтобы стать вторичным nameserver'ом для других доменов, в named.boot нужно написать строчку такого плана:


secondary npi.msu.su    158.250.2.232 db.npi.msu.su
secondary rector.msu.su 193.232.112.1 db.rector.msu.su

Общий вид директивы таков:

secondary [имя.домена] [IP-адрес primary nameserver'а] [имя_файла]

Первый параметр здесь - имя домена, для которого мы устанавливаем secondary. Для него обязательно должен существовать первичный name-сервер, с которого демон будет периодически считывать данные об этом домене. Вместо одного IP-адреса primary nameserver'а может идти и список адресов, откуда еще можно узнать информацию о домене. Это используется в том случае, если мы создаем многоуровневую систему nameserver'ов с различными приоритетами, и т.д. В большинстве случаев в это поле заносится только адрес primary. Последний параметр - имя временного файла, выбранное нами произвольным образом, в котором демон named будет хранить информацию о домене, полученную от primary name-сервера. Как и в первом случае, имена файлов лучше назначать созвучными с именем домена. Кстати, для того, чтобы установить вторичный nameserver для домена, нужно только добавить одну такую строчку named.boot, и все.

Чтобы правильно настроить primary name-сервер, нам осталось рассмотреть конфигурационные файлы db.* (эти файлы также называются файлами зоны). Общий вид записи в этом файле:

[domain] [opt_ttl] [opt_class] [type] [resource_record_data]

где domain есть "." для описания домена верхнего уровня, "@" для текущего домена или обычное доменное имя (в частности, просто имя машины (hostname)).

opt_ttl - необязательное поле, целое число, которое означает время жизни (time-to-live) этой записи в секундах. По истечении этого срока содержимое записи должно автоматически обновиться.

opt_class - тип адреса объекта. Такой тип существует только один, который, собственно, и указывается ("IN").

type - тип записи (рассмариваются ниже)

resource_record_data - данные этого типа

Рассмотрим пример primary nameserver'а и соответствующего ему файлу зоны для домена radio-msu.net. В случае, если вы будете использовать эти примеры как руководство, не забудьте, пожайлуста, поменять все записи соответственно структуре вашей сети.

В named.boot мы должны прописать такую строчку:


primary  radio-msu.net  db.radio-msu.net

И в директории /etc/namedb создаем файл с названием db.radio-msu.net примерно такого содержания:


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;     BIND configuration for the primary nameserver
;             Radio-MSU.NET host table
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@               IN      SOA  ns.radio-msu.net.  art.radio-msu.net. (
                        1998022300      ; Serial
                        28800           ; Refresh
                        7200            ; Retry
                        604800          ; Expire
                        86400 ) ; Minimum ttl
;----------------------------------------------------------------
                IN      NS              ns.radio-msu.net.
                        NS              mskws.desy.de.
localhost       IN      A               127.0.0.1
;----------------------------------------------------------------
@               IN      MX      20      mpr
                        MX      40      mskws.desy.de.
;----------------------------------------------------------------
ns              IN      A               193.124.134.1
                IN      MX      10      ns
                IN      MX      50      relay
;----------------------------------------------------------------
telion                  IN      A       193.124.134.2
                        MX      10      telion
                        MX      20      relay.radio-msu.net.
                        MX      50      mskws.desy.de.
www                     IN      CNAME   telion
;----------------------------------------------------------------
noc                     IN      A       193.124.134.101
                        IN      MX      10 noc
                        IN      MX      20 relay
;----------------------------------------------------------------
mpr                     IN      A       193.124.134.3
                        MX      10      mpr
                        MX      50      mskws.desy.de.
pop                     IN      A       193.124.134.3
uucp                    IN      A       193.124.134.3
relay                   IN      A       193.124.134.3
mail                    IN      A       193.124.134.3
vika                    IN      A       193.124.134.5
                        MX      10      vika
                        MX      50      relay
;----------------------------------------------------------------
brusun                  IN      A       193.124.134.20
diana                   IN      A       193.124.134.7
sftgames                IN      CNAME   telion
chat                    IN      CNAME   brusun
;------------------------  THE  END  ----------------------------

Заметим, что все строки, начинающиеся с ";", являются комментариями и используются для улучшения читабельности текста.

Теперь рассмотрим типы записей, встречающиеся в нашей конфигурации:

Самая первая запись в любом таком файле выглядит следующим образом:


@               IN      SOA  ns.radio-msu.net.  art.radio-msu.net. (

Символ "@" означает, что дальнейшие директивы относятся к текущему домену (то есть, к домену radio-msu.net). Поле "IN" можнно считать несущественым, а после него идет описание типа записи:

SOA - (Start of authorizing) - зона ответственности. Дальнейшие параметры определяют, кто отвечает за этот домен, как часто обновлять информацию об этой зоне на secondary-серверах, а также другую служебную информацию. Первое поле параметров этой записи - имя primary name-сервера, содержащего зону. После него следует адрес технического контактного лица, отвечающего за домен. Обратите особое внимание на точки в конце этих адресов! Точка в конце означает, что этот адрес является обычным доменным именем, и его не нужно дополнять справа доменом, которому соответствует наш конфигурационный файл. То есть, если вместо ns.radio-msu.net. написать ns.serv, то будет иметься ввиду компьютер ns.serv.radio-msu.net

Точку в конце символьных имен нужно не забывать ставить и в других полях зоны. Это одна из самых распространенных ошибок. Написать "ns.radio-msu.net" - значит, иметь ввиду сервер "ns.radio-msu.net.radio-msu.net", которого просто не существует.

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

Первое число ("1998022300" у нас) - сериальный номер (serial number) записи. По изменению сериального номера secondary name-сервера определяют, было ли изменено содержимое домена или нет, и соответственно, нужно ли считывать весь домен с primary-сервера. Сериальный номер должен состоять из десяти цифр и обязательно должен быть заменен вручную на какой-либо больший при любом измеении любой записи в файле домена. Для этих целей идеально подходит такой вариант: первые четыре цифры serial'а - год, затем две на месяц и число. Последние две - порядковый номер (начиная с 00) редакции в течение дня. Если мы меняем конфигурацию домена, то заодно мы должны увеличить и сериальный номер. А при таком задании serial'а увеличение происходит автоматом.

После сериального номера идет поле Refresh, которое указывает время в секундах, по истечении которого secondary name-сервер проверяет не изменилась ли данная зона на primary-сервере, и если изменения были, то происходит передача файла с зоной.

Если при этом у него не получилось по каким-либо причинам соединиться с сервером, то следующую попытку secondary сделает по истечении Retry секунд (третий параметр).

В том случае, если и последующие попытки подключиться к primary и узнать информацию о зоне оканчиваются неудачей, вторичный name-сервер по прошествии Expire секунд забудет всю информацию по этой зоне.

Последнее поле ('Minimum TTL') указывает на минимальное время жизни (Time To Live) записей в файле зоны, если только в какой-то записи не будет указано другое значение в необязательном поле opt_ttl.

Рекомендуемые значения для этих величин таковы:


 28800  ; Refresh     8 hours
 7200   ; Retry       2 hours
 604800 ; Expire      7 days
 86400  ; Minimum TTL 1 day

Однако вам никто не вправе помешать поставить свои значения. Только не ставьте слишком маленькие величины (меньше часа) - иначе вы просто забьете сеть бесполезной информацией, непрерывно пересылаемой от primary к secondary.

Теперь рассмотри записи следующего типа:

NS - (Name Server) - перечисляет name-сервера (и primary, и secondary), которые держат эту зону. Не забывайте про точку в конце имени!


                IN      NS              ns.radio-msu.net.
                        NS              mskws.desy.de.

Здесь мы для зоны .radio-msu.net указали два name-сервера (первый из них - primary, второй - secondary), на которых содержится вся информация о домене.

Далее идет одна из наиболее часто встречающихся записей DNS:

A - (Address) - адрес хоста. На первом месте в такой строке будет стоять символьное имя компьютера в текущем домене, после этого "IN A", а затем - числовой IP-адрес, соответствующий этой машине. Рекомендуется внести, например, такую строку в файл зоны:

 
localhost       IN      A               127.0.0.1

Это позволит обращаться с любого компьютера в сети к самому себе, используя зарезервированное имя localhost. Для того, чтобы как-нибудь назвать новый компьютер в сети, вам нужно просто добавить такую строчку в файл зоны (не забудьте только после этого подправить сериальный номер в записи SOA и после всех изменений перезапустить демон named (сделать ему kill -1)).

В рассмотренном примере мы видим такие строки:


ns              IN      A               193.124.134.1
telion          IN      A               193.124.134.2
diana           IN      A               193.124.134.7

Значит, мы назвали компьютеры с соответствующими IP-адресами именами ns,telion и diana. Очень часто используется имя www, которое позволяет обращаться к web-серверу домена. Если бы мы изменили здесь слово 'telion' на 'www', то по адресу http://www.radio-msu.net/ откликнулась машина с IP-адресом 193.124.134.2. В нашем случае, можно поступить более хитрым образом, используя запись типа CNAME.

CNAME - (Canonical name) - в транслитерации с латинского, на компьютерном жаргоне такая запись называется алиас (alias). Вы как бы добавляете компьютеру еще одно имя, которое при разрешении превратится в тот же IP-адрес, что и основное имя.


www             IN      CNAME   telion

Теперь имя www.radio-msu.net будет аналогично telion.radio-msu.net. Алиасов для одного IP-адреса может быть сколько угодно, но на них накладывается единственное условие: то имя (каноническое), которое стоит после CNAME, должно также где-либо быть описанным. Допускается использовать в качестве канонического имени и alias, лишь бы такая цепочка алиасов не замыкалась. В качестве cname может выступать любой именной адрес в сети, не обязательно из текущего домена, например:


other           IN      CNAME   www.sun.com.

И пойдя теперь по адресу other.radio-msu.net, мы попадем на сервер Sun Microsystems.

Заметим еще один момент. Второе имя компьютеру можно дать и так:


mail                    IN      A       193.124.134.3
relay                   IN      A       193.124.134.3

При этом у нас будет несколько записей типа 'A', соответствующих одному IP-адресу. Это ничему не противоречит, но так делать - плохой стиль.

Следующий важный тип записей - записи типа MX.

MX - (Mail eXchange) - пересылка почтовых сообщений. Они обычно следуют за записями типа 'A' или 'SOA'. Используются они обычно так:

[domain] IN MX [pref_value] [mail_server]

где domain - необязательное поле. Если оно есть, то запись онтосится к этому домену, если нет - то к предыдущему с типом 'A'. В том случае, если на месте [domain] стоит символ '@' или это поле отсутствует, но сама запись находится в самом начале файла зоны (сразу же после SOA), то такое поле MX будет относиться к домену, которому соответствует текущий db-файл.

'IN' также можно указывать, а можно опускать. mail_server - доменное имя почтового сервера, которому достанется вся почта, приходящая на этот домен. На этом сервере должны стоять программы, поддерживающие почтовый протокол SMTP. Для повышения надежности пересылки почты, вы можете указать подряд несколько почтовых серверов. В том случае, если один из них перестанет работать, вся почта на домен будет отправляться на эти "запасные" mail-сервера, которые при первой же возможности отправят все накопившиеся у них электронные письма на основной почтовый сервер. Чтобы регулировать такую систему, и вводится параметр pref_value (приоритет соответствующего почтового сервера), который может меняться от 0 (самый большой приоритет) до 32767 (почта на mail-сервер с таким значением pref_value будет приходить в совсем крайнем случае). Если у вас запись MX единственная для какого-то домена, то значение этого поля не играет роли. Но при использовании только одного mail-сервера в те моменты, когда он по каким-либо причинам недоступен, почта будет теряться. Поэтому обыкновенно ставят две-три MX-записи, а приоритеты у них устанавливают круглыми числами от 10 до 100. Пример:


mpr                     IN      A       193.124.134.3
                        MX      10      mpr
                        MX      50      mskws.desy.de.

Итак, почта, идущая на mpr (например, на john@mpr.radio-msu.net), прежде всего попытается лечь на сам mpr, а в том случае, если у нее это не получится, то будет использован второй вариант (50 больше, чем 10) - mskws.desy.de, где почта пролежит до того момента, пока mpr опять не заработает. Зачастую нужно использовать более короткие почтовые адреса, вроде alla@radio-msu.net. Делается это таким образом:


@               IN      MX      20      mpr
                        MX      40      mskws.desy.de.

Ситуация в точности та же. Почта на somebody@radio-msu.net перенаправляется на mpr (или на mskws), а на этих компьютерах уже должны быть корректно настроены почтовые программы, понимающие, на какой домен приходит почта и что с ней надо делать дальше. Для этого вам, к несчастью, придется ознакомиться с соответствующей документацией к почтовому серверу.

Следующие типы записей используются реже, тем не менее, приведем их тут:

NULL - пустая запись. Скорее всего, должна использоваться для резервирования доменного имени, но на практике применяется только в том случае, если необходимо убрать на некоторое время из DNS'а запись о какой-то машине. Хотя проще такие строчки просто закомментарить.

RP - (Responsible Person) - ответственное за этот домен лицо:


xerox		IN	RP	Ivanov Ivan

HINFO - (Host Information) - информация о компьютере (тип процессора, операционная система и т.д.). Используется крайне редко.

Нам осталось рассмотреть последний особый тип записи 'PTR' (domain name pointer), эта запись используется в db.* файлах так называемой "обратной зоны" (reverse-dns).
;

Настройка "обратной зоны"

В рассматриваемых до этого примерах мы видели, как установить взаимосвязь между символьными именами и числовыми IP-адресами, но только в одну сторону: от символьных имен к числовым. Доменная система имен должна также обеспечивать обратное преобразование: из числового адреса в строковый. Для этих целей, собственно и служат файлы обратной зоны (Reverse-DNS), структура которых во многом напоминает файлы зон, которые мы рассматривали выше. Для обратных зон также необходимы primary и secondary сервера. В конфигурационном файле named.boot для установки primary name-сервера может быть написана примерно такая строка:


primary  134.124.193.in-addr.arpa  db.193.124.134

Домен 'in-addr.arpa' - служебный. Он используется для обозначения числовых IP-адресов.

Будьте внимательны: при таком способе записи от вашего полного IP адреса из 4-х чисел остаются только первые три, общие для компьютеров внутри вашей локальной сети (или какого-то ее участка). Причем, эти числа меняются местами: так домен 80.67.194.in-addr.arpa будет соответствовать адресам сетки 194.67.80.*.

Secondary name-сервера обратных зон устанавливаются аналогично прямым зонам - в named.boot пишется строка


secondary  100.250.158.in-addr.arpa  158.250.100.1 db.158.250.100

Этой строкой мы установили secondary name-сервер для reverse-зоны IP-адресов сети 158.250.100, а primary name-сервером будет компьютер этой сети с адресом 158.250.100.1.

Обязательной строчкой в named.boot является обратная зона сетки 127.0.0, так как в ней находится IP-адрес 127.0.0.1 (localhost,loopback) - это специальный адрес, при обращении на который с любого компьютера, мы попадаем на него же. Итак, в named.boot должна быть строчка:


primary  0.0.127.IN-ADDR.ARPA  db.local

Большие и маленькие буквы здесь не различаются. А в директории /etc/namedb создадим файл db.local примерно такого содержания (имена доменов вам, естественно, придется поменять на свои):


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  db.local  -   Local domain configuration
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@        IN      SOA     ns.radio-msu.net.  art.radio-msu.net. (
                 1995101500      ; Serial
                 14400           ; Refresh
                 3600            ; Retry
                 3600000         ; Expire
                 259200 )        ; Minimum ttl
 
         IN      NS      ns.radio-msu.net.
1        IN      PTR     localhost.
;-----------------------------------------------

Итак, вы установили Reverse-DNS для 127.0.0.1. Рассмотрим теперь пример установки обратной зоны для того же домена ('radio-msu.net'). В named.boot primary name-сервера записывается строка:


primary  134.124.193.in-addr.arpa  db.193.124.134

В директории /etc/namedb создаем файл с именем db.193.124.134 для обратной зоны. Здесь, как и в предыдущих ситуациях, само по себе имя большой роли не играет, поэтому можно выбирать его легким для запоминания. Теперь посмотрим на то, что может содержаться в этом файле:


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;     BIND configuration for the primary nameserver  
;             Radio-MSU.NET reverse DNS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@               IN      SOA  ns.radio-msu.net. art.radio-msu.net. (
                        1998022300      ; Serial
                        14400           ; Refresh
                        3600            ; Retry
                        1209600         ; Expire
                        345600 )        ; Minimum ttl
 
                IN      NS      ns.radio-msu.net.
                        NS      mskws.desy.de.
 
0               IN      PTR     Radio-MSU-Ether.Radio-MSU.net.
                        A       255.255.255.0

1               IN      PTR     ns.radio-msu.net.
2               IN      PTR     telion.radio-msu.net.
3               IN      PTR     mpr.radio-msu.net.
5               IN      PTR     vika.radio-msu.net.
7               IN      PTR     diana.radio-msu.net.
20              IN      PTR     brusun.radio-msu.net.
101             IN      PTR     noc.radio-msu.net.
;----------------------------------------------------------------

Как мы видим, в обратной зоне в основном используются записи типа PTR. Структура записи такова:

[ip_address #4] IN PTR [имя.домена]

где ip_address #4 - последнее из 4-х чисел IP-адреса (его значения могут быть от 0 до 255). Первые три компоненты задаются для данного файла обратной зоны из named.boot. Запись PTR задает соответствие между IP-адресом и именем домена. Важно отметить, что для правильной работы необходимо соответствие числовых и символьных адресов в прямых и обратных зонах, иначе результат будет непредсказуем.

В файле обратной зоны первой записью стоит запись типа SOA, полностью аналогичная SOA прямой зоны. После этого идет перечисление name-серверов (записи типа NS). И особняком стоит строка для адреса 193.124.134.0, который обозначает весь Ethernet-участок сети. Прописывать нулевой адрес необязательно, но это считается правилом хорошего тона.

Наконец, после всех этих манипуляций ваш сервер готов к работе. Демон named должен запускаться при старте машины. Все изменения в конфигурации должен производить только пользователь root. После каждого изменения не забывайте менять серийные номера в db.* файлах, согласовывать прямые и обратные зоны, перезапускать (kill -1) named.

Комментарии: (0) | Интернет и Сети | 2006-06-03

Как слать письма в PHP с аттачами? Просто!

Как слать письма в PHP с аттачами? Просто!

© Antonio
из фоpума phpclub.net ;-)

Как послать письмо в HTML виде? Присоедините к письму аттач с названием message.html и письмо превратиться в HTML-письмо!


<?
// Функции. Можно вынести в дpугой файл.
 
class html_mime_mail {
  var $headers; 
  var $multipart; 
  var $mime; 
  var $html; 
  var $parts = array(); 

function html_mime_mail($headers="") { 
    $this->headers=$headers; 
} 

function add_html($html="") { 
    $this->html.=$html; 
} 

function build_html($orig_boundary,$kod) { 
    $this->multipart.="--$orig_boundary\n"; 
    if ($kod=='w' || $kod=='win' || $kod=='windows-1251') $kod='windows-1251';
    else $kod='koi8-r';
    $this->multipart.="Content-Type: text/html; charset=$kod\n"; 
    $this->multipart.="BCC: del@ipo.spb.ru\n";
    $this->multipart.="Content-Transfer-Encoding: Quot-Printed\n\n"; 
    $this->multipart.="$this->html\n\n"; 
} 


function add_attachment($path="", $name = "", $c_type="application/octet-stream") { 
    if (!file_exists($path.$name)) {
      print "File $path.$name dosn't exist.";
      return;
    }
    $fp=fopen($path.$name,"r");
    if (!$fp) {
      print "File $path.$name coudn't be read.";
      return;
    } 
    $file=fread($fp, filesize($path.$name));
    fclose($fp);
    $this->parts[]=array("body"=>$file, "name"=>$name,"c_type"=>$c_type); 
} 


function build_part($i) { 
    $message_part=""; 
    $message_part.="Content-Type: ".$this->parts[$i]["c_type"]; 
    if ($this->parts[$i]["name"]!="") 
       $message_part.="; name = \"".$this->parts[$i]["name"]."\"\n"; 
    else 
       $message_part.="\n"; 
    $message_part.="Content-Transfer-Encoding: base64\n"; 
    $message_part.="Content-Disposition: attachment; filename = \"".
       $this->parts[$i]["name"]."\"\n\n"; 
    $message_part.=chunk_split(base64_encode($this->parts[$i]["body"]))."\n";
    return $message_part; 
} 


function build_message($kod) { 
    $boundary="=_".md5(uniqid(time())); 
    $this->headers.="MIME-Version: 1.0\n"; 
    $this->headers.="Content-Type: multipart/mixed; boundary=\"$boundary\"\n"; 
    $this->multipart=""; 
    $this->multipart.="This is a MIME encoded message.\n\n"; 
    $this->build_html($boundary,$kod); 
    for ($i=(count($this->parts)-1); $i>=0; $i--)
      $this->multipart.="--$boundary\n".$this->build_part($i); 
    $this->mime = "$this->multipart--$boundary--\n"; 
} 


function send($server, $to, $from, $subject="", $headers="") { 

    $headers="To: $to\nFrom: $from\nSubject: $subject\nX-Mailer: The Mouse!\n$headers";
    $fp = fsockopen($server, 25, &$errno, &$errstr, 30);
    if (!$fp)
       die("Server $server. Connection failed: $errno, $errstr");
    fputs($fp,"HELO $server\n");
    fputs($fp,"MAIL FROM: $from\n");
    fputs($fp,"RCPT TO: $to\n");
    fputs($fp,"DATA\n");
    fputs($fp,$this->headers);
    if (strlen($headers))
      fputs($fp,"$headers\n");
    fputs($fp,$this->mime);
    fputs($fp,"\n.\nQUIT\n");
    while(!feof($fp))
      $resp.=fgets($fp,1024);
    fclose($fp);
  } 
}


// *************************************************************************
//
//   В качестве аттача пpисоединяем html-письмо (открывается автоматически).
//   Второй аттач - некоторый файл из каталога.
//   Вот так вызывать все то, что написано выше:
//
// *************************************************************************


  $mail=new html_mime_mail();
  $mail->add_html("<html><body><center><h2>Пpивет!<br><br>".
                  "<br>Посылаю двоичный файл [/bin/ls] ...".
                  "</h2></center></body></html>");
  $mail->add_attachment("/bin/","ls");
  $mail->build_message('win'); // если не "win", то кодиpовка koi8
  $mail->send('ПОЧТОВЫЙ_ХОСТ_ВАШЕГО_ПРОВАЙДЕРА',
              'КОМУ_(E-MAIL)',
              'ОТ_КОГО_(E-MAIL)',
              'ТЕМА ПИСЬМА');

//
// После прихода письма качаем по ФТП оригинальный /bin/ls и сравниваем с
// импортированным из письма: 
// 
//    X:\temp>fc /b ls ls2
//    Сравнение файлов ls и LS2
//    FC: различия не найдены
//
//
// Внимание! Если у вас нет файла /bin/ls, то просто закомментируйте строку
// $mail->add_attachment("/bin/","ls"), чтобы программа не пыталась присоединить
// к письму неcуществующие файлы.
//


?>

>

Комментаpии по пpосьбам тpудящихся. Будут добавляться до тех поp, пока всем все не станет понятно.

> ...объяснить поподробнее, то что написано
> на http://php.spb.ru/php/mail.html.
> Т.е. я не понял, вся та информация, она
> располагается все на одной странице или то,....

Пpогpамма состоит из 2х частей.

  1. необходимые функции
  2. как написать письмо с аттачем. Аттач -- это HTML-письмо, содеpжащее слово "пpивет"
> И второе: ("<html><body><center><h2>пpивет</h2>
> </center></body></html>") - это есть само тело послание,
> которое придет на mail ??? 

Да, это и есть аттач. Их может быть несколько.


> .. но у меня возникли кое-какие еще вопросы:
> "почтовый хост" - в этом случае прописывается тот адрес, где
> реально находиться почтовый ящик (т.е. в большинстве
> случаев у провайдера) или нет

Нет. Этот параметр не имеет никакого отношения к каким-либо почтовым ящикам... Если вы не можете запонить это поле, то не занимайтесь программированием вообще.

  • Открываем свою почтовую программу (для тех, кто не понял: Outlook, Thebat или др)
  • Смотрим, что указано в поле "исходящий (SMTP) сервер"
  • Пишем в параметре "ХОСТ" эти данные (без угловых скобок)

Пример: smtp.peterlink.ru (если я являюсь клиентом Петерлинка, что дает мне право пользоваться сервером почты). Любой человек как-то пишет письма в Инет. Это "как-то" он делает через почтовый сервер своего провайдера.


> "кому" - ???
> "от кого" - ???
> "тема" - ???

Нет, пожалуй эти поля комментировать не будем... Хотя:

  • Кому - пример: vasya@pupkin.ru
  • От кого - пример (от меня): dmitry@php...ru
  • Тема - пример: ...

Нет, все же лучше не комментировать...

Комментарии: (0) | PHP & MYSQL | 2006-06-03

Задачи связанные с календарем


Материал подготовил(и): klem4

1) Как вычислить конечную дату ?
Задача: У Васи Пупкина неожиданно сломался компьютер. Из-за отсутствия нужных материалов на ремонт понадобится N дней. Определите дату окончания ремонта, если известно, что компьютер сломался в текущем году, и ремонт должен закончиться тоже в этом году...
Код
Function GetInteger(s: String): Integer;
Var i, Err: Integer;
Begin
If s[1] = '0' Then Delete(s, 1, 1);
Val(s, i, Err); GetInteger := i
End;

Const
CurrYear = 2004;
DayInMonth: Array[1 .. 12] Of Byte =
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

(*
{ Тестировалось с этими данными }
Const
Repair: Integer = 50;
st: String = '21.06';
*)
Var
Day, Month, DaysBefore: Integer;
p, i: Byte;
Var
Repair: Integer;
st: String;
begin
Inc(DayInMonth[2], Byte((CurrYear mod 4) = 0));
Write('Дата поломки > '); ReadLn(st);
Write('Длительность ремонта > '); ReadLn(Repair);

p := Pos('.', st);
Day := GetInteger( Copy(st, 1, Pred(p)) );
Month := GetInteger( Copy(st, Succ(p), Length(st)-p) );

For i := 1 To Pred(Month) Do
Inc(DaysBefore, DayInMonth[i]);
Inc(DaysBefore, Day);

Inc(DaysBefore, Repair);
i := 1;
While DaysBefore > DayInMonth[i] Do
Begin
Dec(DaysBefore, DayInMonth[i]); Inc(i)
End;
WriteLn('Ремонт закончится: ', DaysBefore, '.', i);
end.

by Volvo

Еще один вариант
Код
program Lab8_02_2;
uses crt,dos;
var
q:char;
data:record
day,year,months:word;
end;
j:integer;
week:word;
m:integer;
g:word;

const
month:array[1..12] of string[7] =
('января','февраля','марта','апреля','мая','июня','июля',
'августа','сентябя','октября','ноября','декабря');
a:array[1..12] of integer =
(31,29,31,30,31,30,31,31,30,31,30,31);

begin
repeat
clrscr;
getdate(data.year,data.months,data.day,week);
g:=data.months;
writeln('Сегодняшняя дата: ',data.day,' ',month[g]);
writeln('введите число m через которое вы хотите узнать дату:');
read(m);
j:=m;
while j<>0 do begin
if j>a[data.months]-data.day then begin
j:=j-(a[data.months]-data.day);
inc(data.months);
data.day:=0;
end

else begin
data.day:=data.day+j; break;
end;
end;

g:=data.months;
writeln('Дата дня и месяц: ',data.day,' ',month[g] );
write('Вычислить еще ?(Y/N)');
q:=ReadKey;
until not (q in ['Y','y']);
end.

by Amro

Материал подготовил(и): klem4

2) Задача про старокитайский календарь
В старокитайском календаре был принят 60-летний цикл состоящий из 5 двенадцатилетних подциклов. Подциклы обозначались названиями цвета:
• Зеленый
• Красный
• Желтый
• Белый
• Черный
Внутри каждого подцикла годы получили название животных:
• Крыса
• Корова
• Тигр
• Заяц
• Дракон
• Змея
• Лошадь
• Овца
• Обезьяна
• Курица
• Собака
• Свинья
(К примеру 1984–год зеленой крысы - был началом очередного цикла). Написать программу, которая вводит номер некоторого года нашей эры и печатает его название по старояпонскому календарю.

var

y : array [0..11] of string =

(
'Rat',
'Cow',
'Tiger',
'Rabbit',
'Dragon',
'Snake',
'Horse',
'Sheep',
'Monkey',
'Chicken',
'Dog',
'Pig'
);

d : array[0..4] of string =

(
'Green',
'Red',
'Yellow',
'White',
'Black'
);


year:Integer;

begin

writeln('Input year'); readln(year);

year := (year mod 60)-4;

If year<0 then year := year+60;

writeln(d[year div 12],' ',y[year mod 12]);

end.

by Idea

Материал подготовил(и): klem4

Модуль для работы с календарем

{
Calendar.pas

Набор функций для работы с датами и вычислений по календарю.
Автор: Виктор Осташев
Fido: 2:5020/1194
E-mail: v_ostashev@chat.ru
WWW: _http://ostashev.newmail.ru
}

function datein(low, high, dt : tdate) : boolean;
{ Проверяет нахождение даты в промежутке между low и high }

procedure stringtodate(st : string; var dt : tdate);
{ Преобразует строку в дату }

procedure datetostring(dt : tdate; var st : string);
{ Преобразует дату в строку }

function compdate(d1, d2 : tdate) : integer;
{
Сравнивает две даты. Возвращает:
0, если даты равны;
-1, если первая дата меньше второй;
1, если вторая дата меньше первой
}

function numofday(dat : tdate; style : tstyle) : longint;
{ Вычисляет условный номер дня для даты dat
с учетом нового стиля при style=true }

function dayofweek(dat : tdate; style : tstyle) : byte;
{ Вычисляет день недели для даты dat с
учетом нового стиля при style=true }

function numinyear(dat : tdate; style : tstyle) : word;
{ Вычисляет номер дня от начала года с учетом стиля }

function lenofmonth(month: byte; year: word; style: tstyle): byte;
{ Вычисляет длину месяца с учетом стиля }

procedure numtodate(num: longint; style: tstyle; var dat: tdate);
{ Вычисляет дату по данному номеру дня }

function isleap(year : integer):boolean;
{ Является ли год високосным }
Комментарии: (0) | Pascal & Delphi | 2006-06-03

Создание безопасного высоко производительного почтового сервера на базе Solaris и Postfix

Создание безопасного высоко производительного почтового сервера на базе Solaris и Postfix

Автор:Zedis

Предисловие

Задание

АННОТАЦИЯ

ВВЕДЕНИЕ

1. Описание О.С “Solaris”

1.1 Быстродействие системы

1.2 SMF: Средства обслуживания сервисов

1.3 Надежность ПО

1.4 Зоны и Контейнеры (Solaris Containers)

1.5 Управление правами процессов

1.6 Ролевой доступ

1.7 Защита от атак, использующих переполнение буфера

2. Установка и настройка О.С. “Solaris”

2.1 Необходимые параметры Установки О.С. “Solaris”

2.2 Установка прикладного Soft’a

2.2.1 GCC компилятор

2.2.2 Файловый менеджер MC

2.3 Настройка О.С. “Solaris” после установки или выгребаем все не нужное

2.3.1 Огненная Стена

2.4 Оптимизация сетевого стека TCP/IP

3 почтовый сервер.

3.1 Выбор почтового сервера

3.2 Выбор дополнительного программного обеспечения

3.2.1 Выбора сервера баз данных

3.2.2 Выбор антивирусной защиты

3.2.3 Библиотека авторизация Cyrus SASL 2.x

3.2.4 POP3 Сервер

3.2.5 Ведение квот

3.3 Установка Серверов

3.4 Описание почтового сервера Postfix

3.4.1 Архитектура Работы Postfix

3.4.2 Описание процессов работы сервера Postfix

3.5 Защита сервера Postfix встроенными средствами

3.5.1 Защита от атак из вне

3.5.2 Защита от DOS, DDOS, СПАМА

3.7 Итог проделанной работы

3.8 Тестирование сервера Postfix

4. ВИРТУАЛИЗАЦИЯ Зон и Контейнеров (Solaris Containers)

4.1 Описание технологии:

4.2 Технолошия зон

4.2.1 Конфигурирования не глобальной зоны

4.2.2 Сборка зоны

4.2.3 Запуск зоны

4.3 Тестирование почтового сервера Postfix в не глобальной зоне z_postfix.

Global + z_postfix

4.4 Технология Контейнеров (Ресурс Менеджер).

4.5 Управление ресурсами CPU

4.5.1 Метод Fair Share Scheduler (FSS)

4.5.2 Настройка метода FAIR SHARE SCHEDULER (FSS).

4.6 Разделение ресурсов оперативной памяти

4.6.1 Установка ограничений на использования оперативной памяти

4.7 Тестирование почтового сервера Postfix в не глобальной зоне z_postfix с разграничением ресурсов.

5. Взлом системы


Предисловие


Немного истории. В декабре 2004 года я заканчивал 4 курс института транспорта и связи факультет программирования время неумолимо котилось к защите, и передо мной встал не лёгкий выбор темы дипломной работы. К этому времени я уже проработал системным администратором в двух крупнейших компаниях Латвии около 3х лет.За это время я заинтересовался Unix О.С. Долгое время проработал с FreeBSD, а так же с некоторыми дистрибутивами Linuxa.Ну и соответственно тему диплома хотел взять из этой области, что ни будь связанное с защитой сетевых сервисов от атак. При настройки серверов, к примеру apache, sendmail, mysql и.т.д. я всегда ставил их в родную chroot оболочку О.С.

грубо говоря псевдо виртуализация. Для темы диплома я хотел взять намного больше, чем просто FreeBSD+sendmail+chroot , мне требовалась виртуализация без слова псевдо. На тот момент было несколько вариантов:

  1. виртуальная машина XeN только для Linux или NetBSD

  2. Virtual Linux Server для Linux и сейчас портирован на FreeBSD

  3. Virtuozzo для большинства Unix

Я не буду попросту разводить канитель, что лучше Linux или *BSD, но лично мне линукс не очень нравился по этой причине я сразу отбросил 2 вариант. Ставить NetBSD и изучать XeN машину тоже не очень хотелось. По поводу viruozzo это мощная вещь, но только комерчиское использование делало не возможным нахождение бесплатной версии.

Ну и тут я наткнулся на статью, в которой описывались будущие возможности О.С. Solaris 10. В ряду с которыми была заметка о 1N grid Containers виртуализация. Я сразу заинтересовался этим вопросом, в конце концов, было решено тема диплома будет связана именно с Solaris 10 и технологией Containers.Тут встала другая задача как и чем тестировать данную технологию? Вариантов было масса, но я сразу решил что из той кучи преподов, что будет на защите дай боже чтоб вообще 2 человека поняли о чем будет работа (в конечном итоге понял только 1ч.), по этой причине было решено выдрать гланды через жопу, то есть сделать с нуля велосипед.

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

Поехали....

Задание


Цель работы заключается в следующем:


  • разработка почтового сервера с перечисленными возможностями:

  1. С высокой производительностью более 1000000 писем в сутки

  2. Интегрированным с базами данных для хранения информации

    • пользовательские данные: пароли доступа, имя доступа;

    • информации о почтовых ящиках: размер почтового ящика, время создания;

    • информация о доменах;

    • а также возможность нескольким администраторам системы управлять пользователями определенного домена.

  3. С активной защитой от вирусов, с пассивной защитой от спама.

  4. С защитой от атак на почтовый сервер средствами самого сервера, с защитой от DOS, DDOS атак.

  5. Возможностью пересылки почты только авторизовавшимся пользователям.


  • Создание многоуровневой системы защиты от атак из вне на основе изолированных виртуальных машин в операционной системе “Solaris 10”. Анализ целесообразности затрат ресурсов системы при работе почтового сервера в изолированной виртуальной среде.

АННОТАЦИЯ


“Создание безопасного высоко производительного почтового сервера с использованием технологии виртуализации для защиты сервера”


Скорость обработки сообщений почтовым сервером, затраты ресурсов системы при большой нагрузке почтового сервера, Настройка ИЗОЛИРОВАННОЙ виртуальной машины для работы почтового сервера, Анализ ЭФФЕКТИВНОСТИ работы почтового сервера как в ИЗОЛИРОВАННОЙ виртуальной машины так и без неё.


В данной работе создается высоко производительный почтовый сервер с интегрированной системой хранения информации в базе данных, проверкой на вирусы и с защитой от спама, а также создается многоуровневая система защиты сервера от атак из вне средствами самого почтового сервера и операционной системы. Проводится исследования ресурса ёмкости систем безопасности, производительности почтового сервера и операционной системы. С помощью новой технологии “1N Grid Containers” разрабатывается и создается виртуальная зона, в которой будет работать почтовый сервер с антивирусной защитой. Проводится тестирования почтового сервера при работе в оболочке виртуальной машины и при работе без виртуальных машин, анализируется затраты ресурсов на виртуальную машину. Производится анализ исходных показателей, и на основе выявленных результатов теста делается вывод об эффективности и защите сервера с помощью виртуальных машин.

ВВЕДЕНИЕ


В современном мире вопросы информационной, компьютерной или сетевой безопасности стоят, чуть ли не на первом месте в рейтинге основных проблем не только в высоко развитых странах, но и у большинства людей связанных с информационными технологиями. В связи с этим моя работа будет просвещена теме исследования технологии безопасности изолированных виртуальных машин на примере высоко производительного почтового сервера, а так же разработан и хорошо продуман сам почтовый сервер. В мире такой большой выбор операционных систем, что порой выбирать приходится не из положительных качеств О.С., а исходя из соображений дешевизны, доступности, тех. поддержки. По этому далеко не каждый себе может позволить оперировать 3 и более операционными системами. По этому я решил выбрать для своей дипломной работы О.С. “Solaris 10”. В этой О.С. были реализованы такие новаторские вещи как виртуализация Новый высокопроизводительный сетевой стек, Система управления сервисами и многое другое. В данной работе пошагово будет создан почтовый сервер, используя безопасность на основе изолированных виртуальных машин (технология “1N Grid Containers”). В первой части работы кратко рассмотрены некоторые новые возможности, а так же установка и конфигурирование О.С. “Solaris 10”.В этой же части рассматривается архитектура работы выбранного почтового сервера, после чего будет по создан почтовый сервер с описанными функциями, и проведены необходимые тесты производительности и затрат ресурсов почтового сервера и дополнительно используемого программного обеспечения при работе в общей среде О.С. Solaris. Все показатели будут зафиксированы в таблице и усреднены.

Во второй части работы рассматривается технология изолированных виртуальных машин (1N Grid Containers) их создание и конфигурирование. После чего в изолированной виртуальной машине будет запущен тот же почтовый сервер с некоторым набором дополнительного программного обеспечения, и проведены необходимые тесты производительности и затрат ресурсов почтового сервера и дополнительно используемого программного обеспечения при работе в разных изолированных средах О.С. Solaris. В конечном итоге будет сделан вывод о целесообразности использования технологии виртуальных машин для безопасности сервисов в О.С. Solaris.

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


1. Описание О.С “Solaris”


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

Solaris 10 работает не только на архитектуре SPARC, но и на x86 и AMD64. Корпорация Sun Microsystems готова предложить своему заказчику широкий спектр возможностей - на любой платформе. При создании этой версии Solaris уделялось особое внимание безопасности ПО, быстродействию системы и снижению стоимости эксплуатации.

Solaris 10 - это единственная ОС, оптимизированная для работы на нескольких основных платформах:SPARC, x86, AMD64. Это позволяет защитить инвестиции, сделанные заказчиком в свою инфраструктуру.

В Solaris 10 обеспечивается совместимость на уровне исходных кодов для Solaris SPARC и Solaris x86, а так же совместимость на уровне исполняемых кодов для Linux x86 и Solaris x86.


1.1 Быстродействие системы


Динамическая трассировка задач (Dynamic Tracing, DTrace) - это мощный инструмент для диагностики узких мест в режиме реального времени. Эта система позволяет отслеживать узкие места в производительности приложений, анализ проводить тюнинг, и диагностику системы. Динамическая трассировка может значительно повысить производительность отдельных приложений и упростить задачу настройки производительности разработчикам. Использование динамической трассировки задач позволяет повысить скорость выполнения приложений до 30 раз.

Операционная система Solaris 10, установившая на сегодняшний день 14 мировых рекордов производительности, обеспечивает отличное соотношение цена/производительность для всех индустрий - от финансов до телекоммуникаций. Технические усовершенствования, такие как усовершенствование сетевых стеков и оптимизация для различных процессорных архитектур, позволяют Solaris 10 обеспечивать рекордную скорость работы бизнес приложений на серверах Sun Fire и рабочих станциях Sun Java.



1.2 SMF: Средства обслуживания сервисов


Вкратце, SMF - это механизм отношений между сервисами посредством (взаимных) зависимостей. Это также инфраструктура для автоматического запуска и перезапуска сервисов. И, наконец, это репозиторий, где хранится конфигурация и правила для запуска сервисов.

1.3 Надежность ПО


Превентивная самодиагностика (Predictive Self Healing) - это новое слово в определении и автоматическом восстановлении. Solaris 10 автоматически перезапускает приложения и сервисы, в которых были обнаружены нарушения работы, причем весь процесс обнаружения и диагностики занимает миллисекунды, а не часы.


1.4 Зоны и Контейнеры (Solaris Containers)


Solaris 10 позволяет системному администратору организовывать виртуальные системные разделы, называемые зонами. Внутри каждой зоны существует собственное пространство имен и пространство процессов. Каждая зона является самостоятельной системой, со своим набором пользователей, своими каталогами, своими сетевыми адресами, своими процессами. Зоны изолированы друг от друга, так что процессы и пользователи, работающие в одной зоне, не имеют доступа к процессам и ресурсам другой. Важно отметить, что даже суперпользователь “root” зоны не имеет возможности увидеть, что делается в другой зоне. В случае “взлома” отдельной зоны злоумышленник не получает доступа ко всей системе, а остается в рамках этой зоны.

Технология контейнеров предназначена для распределения системных ресурсов между отдельными процессами, группами процессов, пользователями или зонами. Так, администратор имеет возможность выделить 40 частей (shares) процессорных ресурсов серверу баз данных, 30 частей - серверу приложений и 5 раз по 10 частей пяти веб-серверам, работающим в режиме горизонтального масштабирования.

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


1.5 Управление правами процессов


Одно важнейших нововведений в Solaris 10 - возможность ограничения прав процессов только самыми необходимыми потребностями. Прежде, если процессу нужно было обеспечить доступ к системному файлу или привилегированному сетевому порту, единственным выходом было присвоение этому процессу статуса суперпользовательского (root), что давало ему широчайший доступ ко всем системным ресурсам. В результате, если злоумышленник получал доступ к этому процессу, он получал доступ ко всей системе. В Solaris 10 администратор имеет возможность ограничить права доступа для конкретного процесса или группы процессов только теми привилегиями, которые ему реально необходимы. Таким образом, даже если процесс будет захвачен злоумышленником, он не сможет получить права суперпользователя.


1.6 Ролевой доступ


Система ролевого доступа (RBAC - Role Based Access Control) предназначена для решения "проблемы суперпользователя" - ситуации, когда один пользователь имеет доступ ко всем ресурсам системы. Такой подход нельзя назвать безопасным, поэтому в Solaris введена возможность разделения прав суперпользователя между несколькими пользователями. Одному пользователю может быть предоставлено только право чтения системных журналов (например, для аудита), другому - право добавления новых пользователей, третьему - право подключения и конфигурирования внешних устройств. Рекомендации экспертов по безопасности говорят о том, что в идеале в системе вообще не должно быть суперпользователя, а все его права должны быть разделены между различными ролями.


1.7 Защита от атак, использующих переполнение буфера


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

2. Установка и настройка О.С. “Solaris”


Установка Операционной системы Solaris 10 может производится в 3 режимах:

  1. В Интерактивном консольном режиме – когда пользователь в консольном режиме отвечает на вопросы системы установки, и заводит необходимые для работы О.С параметры, используя при этом только клавиатуру как устройство ввода информации.

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

  3. В ручном режиме – когда пользователь работает минимизированном режиме запуска О.С Solaris. Это требует определенных знаний от пользователя, команд О.С Solaris используя при этом только клавиатуру как устройство ввода информации.


2.1 Необходимые параметры Установки О.С. “Solaris”


Во всех трех режимах установки необходимо установить 3 параметра:

  1. Произвести разметку диска или другого устройства хранения информации, на который будет установлены О.С. “Solaris”, указать тип файловой системы каждого раздела диска. Для разметки по умолчанию в О.С. Solaris использует UFS (unix file system), но можно также дополнительно создавать и другие разделы с другой файловой системой (FAT32, NTFS, EXT2, EXT3,...) как дополнительные. Указать точки монтирования для каждого раздела диска.

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

  3. Установка сетевых настроек требует наличие сетевого адаптера (Ethernet, WAN,...). В операционной системе “Solaris” по умолчанию используется протокол TCP/IP. Основные параметры протокола TCP/IP:

    • IP адрес;

    • IP адрес шлюза по умолчанию;

    • Система идентификации доменных имен: DNS, LDAP, NIS, NIS+;

    • Хост имя системы с доменом;

2.2 Установка прикладного Soft’a

2.2.1 GCC компилятор

Настройка системы после окончания установки и загрузки полной рабочей версии О.С. Solaris, является компиляция и сборка GCC коллекции компиляторов, так как большинство сервисов, таких как MySQL, Postfix, Clamav и многие другие, рассчитаны на компиляцию именно пакетом GCC коллекции компиляторов, по умолчанию установленный пакет GCC собран на другой машине, возможно с другой процессорной архитектурой. По этому я просто удалил из системы GCC имевшийся по умолчанию скачал с http://www.sunfreeware.com компилятор gcc-2.95.3-sol 8-intel-local .gz, не смотрите на то что это компилятор для Solaris 8 он нам просто послужит временным компилятором для одной установки и всё. Потом распаковываем “gzip –d gcc-2.95.3-sol 8-intel-local .gz

И устанавливаем в систему “pkgadd –d gcc-2.95.3-sol 8-intel-local .gz”. Потом лезем на http://gcc.gnu.org/ и уже от туда забираем свежий GCC и с помощью временного GCC устанавливаем в систему только не забудьте удалить остатки временного компилятора. К стати не каких опций я не ставил при компиляции свежего GCC.

По чему вы спросите меня нужно было удалять старый компилятор ставить временный от Solaris 8, а дело вот в чем просто по умолчанию в Solaris 10 установлен компилятор GCC 3 которым в принципе сложно что либо скомпилировать к примеру MySQL, GCC постоянно на что то жалуется и даже если вы попробуете скомпилировать им свежий GCC с http://gcc.gnu.org то он не по-русски ругнётся и остановит процесс компиляции. Не знаю может в свежих ISO образах Solaris 10 этой проблемы уже нету, но когда я в феврале месяца работал с Solaris 10 то проблема была. Да к стати один из спецов по Солярке мне сказал что у Sun Microsystem есть свой Си компилятор который в ходит в пакет SDK который платный и возможно по этой причине GCC компилятор так криво установлен в Solaris’e (не знаю не проверял).К стати такая же проблема существует и с Perl модулями для того чтоб установить допустим Spamassasin с требуемым набором модулей необходимо стереть к черту существующий Perl скачать и скомпилить заново свежий Perl и только тогда вы сможете зафигачить необходимый набор модулей.

2.2.2 Файловый менеджер MC

Для более удобной и быстрой работы нам потребуется midnight commander. MC и дополнительные библиотеки необходимые для его работы забираем с http://www.sunfreeware.com так же как и gcc устанавливаем “gzip –d mc.gz” и “pkgadd –d mc”.Я не настаиваю на том что именно MC если кому нравится то и командная строка хороша.

2.3 Настройка О.С. “Solaris” после установки или выгребаем все не нужное


Отключение не нужных процессов и программ. Большинство сервисов, демонов, процессов в ранних версиях О.С. Solaris загружались при загрузки системы из загрузочных скриптов находящихся в директории /etc/rc2.d и /etc/rc3.d в зависимости от многопользовательского режима. Но в Solaris 10 была разработана система “SMF” это инфраструктура для автоматического запуска и перезапуска сервисов. По этой причине большая часть скриптов загружавшихся в /etc/rc2.d и /etc/rc3.d были перенесены в систему SMF. Для просмотра всех загружаемых через SMF программ можно воспользоваться командой “svcs –a”. Команда “svcs -a” показывает состояние сервиса, время запуска, и инстант сервиса – идентификатор по которому идёт управление сервисом из системы “SMF”. Для отключения сервиса используется команда вида “svcadm disable < инстант сервиса > ”.

Короче я приведу свой пример того что я оставил включоным в системе SMF (команда svcs -a) :

Состояние Инстант сервиса

online Aug_04 svc:/system/svc/restarter:default

online Aug_04 svc:/milestone/name-services:default

online Aug_04 svc:/network/pfil:default

online Aug_04 svc:/network/loopback:default

online Aug_04 svc:/network/physical:default

online Aug_04 svc:/milestone/network:default

online Aug_04 svc:/system/identity:node

online Aug_04 svc:/system/metainit:default

online Aug_04 svc:/system/filesystem/root:default

online Aug_04 svc:/network/mysqld:default

online Aug_04 svc:/system/filesystem/usr:default

online Aug_04 svc:/system/device/local:default

online Aug_04 svc:/milestone/devices:default

online Aug_04 svc:/system/keymap:default

online Aug_04 svc:/system/filesystem/minimal:default

online Aug_04 svc:/system/coreadm:default

online Aug_04 svc:/system/identity:domain

online Aug_04 svc:/system/mdmonitor:default

online Aug_04 svc:/system/power:default

online Aug_04 svc:/system/sar:default

online Aug_04 svc:/system/rmtmpfiles:default

online Aug_04 svc:/system/sysevent:default

online Aug_04 svc:/network/ipfilter:default

online Aug_04 svc:/system/picl:default

online Aug_04 svc:/system/device/fc-fabric:default

online Aug_04 svc:/system/cryptosvc:default

online Aug_04 svc:/network/initial:default

online Aug_04 svc:/network/service:default

online Aug_04 svc:/system/manifest-import:default

online Aug_04 svc:/milestone/single-user:default

online Aug_04 svc:/system/filesystem/local:default

online Aug_04 svc:/system/sysidtool:net

online Aug_04 svc:/system/sysidtool:system

online Aug_04 svc:/milestone/sysconfig:default

online Aug_04 svc:/system/sac:default

online Aug_04 svc:/system/utmp:default

online Aug_04 svc:/system/cron:default

online Aug_04 svc:/system/console-login:default

online Aug_04 svc:/system/system-log:default

online Aug_04 svc:/network/rarp:default

online Aug_04 svc:/system/dumpadm:default

online Aug_04 svc:/system/fmd:default

online Aug_04 svc:/network/ssh:default

online Aug_04 svc:/milestone/multi-user:default

online Aug_04 svc:/milestone/multi-user-server:default

online Aug_04 svc:/system/zones:default


Вкратце. Если состояние сервиса показывается, как legacy_run это означает что, сервис загружен с помощью rc скриптов. Если состояние сервиса maintenance это означает что произошла ошибка запуска сервиса. Сразу для пытливых умов хочу оговорить, что SMF это не просто иной способ запуска сервисов. Это система управления сервисами. К стати отключение одного из сервисов может повлечь за собой maintenance состояние другого сервиса. То есть сервисы в системе SMF имеют взаимные связи между друг другом командой svcs d <инстант>” можно посмотреть от каких инстантов зависит данный сервис. К стати командой “svcs D <инстант>“ можно посмотреть что зависит от данного истанта, то есть если мы вырубим данный инстант, то какие другие инстанты войдут в состояние maintenance.Да и еще если вы захотите убить сервис, который рулится из SMF, командой “kill” то он сразу же будет запущен заного. Более подробную инфу ищите на сайте http://docs.sun.com/app/docs/prod/solaris.10#hic . Да для самых пытливых умов, каким я сам не являюсь: наверно многие из вас сразу же спросят себя “а как создать свой собственный инстант?

1.Вариант

Создать строчку для запуска сервиса из inetd.conf и через команду “inetconv” преобразовать строчку запуска в файл формата xml для SMF. Да да все инстанты SMF описываются именно xml файлами и хранятся они в директории /var/svc /manifest. С разу хочу сказать, что я не очень люблю inetd по этому этот вариант отпадает.

2.Вариант

Я думаю, что вы уже догадались взять и вручную или создать или изменить существующий из файлов xml под необходимы сервис, и потом просто через команду svccfg import <путь к файлу xml> про импортировать этот файл в систему SMF.Мне этот вариант больше всего понравился.

3.Вариант

Не скажу.

2.3.1 Огненная Стена

Далеко ли уедет сервер, если на нем не будет файрвола не думаю.

Если сервер будет находиться в сети без пакетного фильтра (FIREWALL) то необходимо на самом сервере реализовать пакетную фильтрацию средствами О.С. “Solaris”. В О.С. “Solaris” доступны 2 пакетных фильтра:

  1. IP-FILTER – бесплатный, распространенный в UNIX мире пакетный фильтр.

  2. SUN SCREEN – платный, используется только в О.С. Solaris.

О великий ip-filter как же ты мне близок по духу.

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

  1. svcadm enable svc:/network/pfil:default

  2. svcadm enable svc:/network/ipfilter:default

Это запустит два сервиса pfil, ipfilter. После этого необходимо в директории /etc/ipf/pfil.ap указать сетевой интерфейс, на котором будет осуществляться фильтрация пакетов. Подробнее о настройки пакетного фильтра в разделе настройка пакетного фильтра.

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

2.4 Оптимизация сетевого стека TCP/IP


Сразу хочу сказать я не когда в своей жизни не видел еще такого быстрого сетевого стека.

Я запускал ftp сервер на Солярке и ftp выдавал мне по 11 мегабайт в секунду да да 11 мегабайт в секунду с устойчивой связью по Fast Ethernet.На FreeBSD я на максимум разгонял до 9,3 мегабайт в секунду.

Так как сервер рассчитывается на значительную сетевую нагрузку, необходимо изменить некоторые настройки TCP/IP протокола, так как по умолчанию в О.С. Solaris сетевой стек настроен для рабочей станции, но даже в этой ситуации сетевой стек имеет отличную производительность. Мы оптимизируем сетевой стек О.С. Solaris под предъявляемые требования к серверу.


Параметры настройки сетевого стека:

1. ndd –set /dev/tcp tcp_xmit_hiwat 65535

2. ndd –set /dev/tcp tcp_recv_hiwat 65535

3. ndd –set /dev/tcp tcp_conn_req_max_q 1024

4. ndd –set /dev/tcp tcp_conn_req_max_q0 4096

5. ndd –set /dev/tcp tcp_time_wait_interval 5000

6. ndd –set /dev/tcp tcp_max_buf 8388608

7. ndd –set /dev/tcp tcp_cwnd_max 8388608

1. tcp_xmit_hiwat - параметр, определяющий размер буфера посылки. По умолчанию равен 8192 (8K). Увеличив это значение до 65535 (65КB) в соответствие с стандартом Fast Ethernet 100Mbit/sec.

2. tcp_recv_hiwat - размер буфера при приеме, то есть размер который используется под информацию при ее приеме. По умолчанию равен 8192 (8K). Увеличим его до 65535 (65КB). в соответствие с стандартом Fast Ethernet 100Mbit/sec.

3. tcp_conn_req_max_q - Этот параметр определяет максимальное значение очереди запросов на каждое соединение.

4. tcp_conn_req_max_q0 - определяет при каком количестве полуоткрытых соединений, поступающие запросы не будут отклонены системой

5. tcp_time_wait_interval – устанавливает время в миллисекундах, в котором TCP соединение остается в состояние TIME-WAIT

6. tcp_max_buf – максимальный размер буфера в байтах, которое может использоваться.

7. tcp_cwnd_max - Максимальное значение окна переполнения. По умолчанию 32768.

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

3 почтовый сервер.


3.1 Выбор почтового сервера


Так как система ориентирована на свободно распространяемые программные продукты. С этой целью производился выбор из 4 самых популярных в мире Unix почтовых систем: sendmail, postfix, exim, qmail. Сразу необходимо рассмотреть достоинства и недостатки каждого из них, чтоб сделать объективный выбор будущей почтовой системы.

  1. Sendmail является самым старым MTA доступным практически на всех Unix подобных О.С. Несмотря на впечатляющий и мощный функционал он не подходит. В первую очередь из-за того, что программа представляет из себя один монолитный блок. По этой же причине за “Sendmail” тянется огромный шлейф уязвимостей и проблем с быстродействием. Вторым немаловажным недостатком является одна из самых сложных процедур конфигурирования. Перспективы программы для создании простейшей конфигурации, по моему мнению выглядят весьма грустно так как необходимо владеть компилятором m4 . Хотя сам Sendmail все еще не остаётся самым распространенным MTA в мире (до 60% всей отправляемой почты работа Sendmail по всему миру), по причине многочисленности своих приверженцев. Да и в дополнение sendmail не может быть интегрирован с базами данных, таких как MySQL для хранения пользовательских бюджетов.

  2. qmail является самым безопасным почтовым сервером. К сожалению, его недостаток заключается в том что qmail обладает слишком жесткой иерархией запуска служебных программ. Для выполнения каждого класса своих задач он порождает дочерние процессы специальных программ. После выполнения задачи процесс дочерней программы уничтожается. С одной стороны это обеспечивает безопасность и запас прочности программы, но с другой создает некоторые накладные расходы на постоянное создание дочерних процессов и межпроцессорное взаимодействие. К тому развитие проекта qmail двигается слишком медленно.

  3. Exim первый недостаток заключается в процессе установки. Процесс установки отличается от классической схемы установки программ в Unix, и не особенно подходит для начинающих пользователей мира Unix. Для включения тех или иных возможностей приходится редактировать Makеfile. Да и сам процесс последующей настройки показал необходимость обладать хорошими познаниями в программировании в командных интерпретаторах. Если не обращать внимания на описанные только что недостатки, то мощный функциональный потенциал, заложенный в эту программу, может сгладить все существующие недостатки.

  4. Postfix являющийся ближайшим конкурентом qmail делает упор на быстродействие и безопасность. Принципы деления выполняемой задачи очень похожи на те что применяются в qmail, но дизайн системы совершенно другой. Основой идеологии postfix является наличие независимых резидентных модулей. Ни один из модулей не является дочерним процессом другого. В тоже время за счет постоянного присутствия модулей в памяти каждый из них может независимо пользоваться услугами других. Проект postfix на данный момент является самым динамично развивающимся из всех перечисленных

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


3.2 Выбор дополнительного программного обеспечения

3.2.1 Выбора сервера баз данных


Сервера баз данных будет MySQL так как на сегодняшний день эта один из самых распостраненых серверов баз данных.


3.2.2 Выбор антивирусной защиты


В качестве антивирусной защиты был выбран ClamAV антивирус и Avcheck в качестве связующего звена почтового сервера “Postfix” и антивируса ClamAV.


Avcheck в качестве mail checkera сразу хочу оговорится я перепробовал целую кучу mail checker но не один не смог откомпилится по Solaris 10(Perl не всчет) и потом я наткнулся на проапгрейдную версию Avcheck c возможностью ClamAV. Написал Avcheck Michael Tokarev.


3.2.3 Библиотека авторизация Cyrus SASL 2.x


Так как по умолчанию в протоколе SMTP отсутствует методы авторизации пользователей то есть через сервер может слать почту любой желающий можно ограничить пересылку по IP адресу, но это не удобно так как если на сервере будет 1000 или более пользователей то слежение за IP-адресами и мобильность пользователей будет не возможно.К стати для Cyrus SASL я нашёл патч который даёт возможность хранения зашифрованных паролей в базе MySQL, я не знаю но по моему сейчас этот патч стал одним целым с Cyrus SASL.

Имя патча cyrus-patch-all.c.patch




3.2.4 POP3 Сервер


Courier-IMAP работает pop3 и imap сервером. Там, где сможет, проверяет пользователя сам, где не может – через SASL и отдает пользователю почту, полученную postfix'ом.

Главными плюсами Courier-IMAP является:

  • Возможность использовать пароли в зашифрованном виде в SQL базах данных

  • Возможность ведение квот на размер почтового ящика пользователя и совместимость с Postfix’ом

Главным минусом данного продукта является то, что Courier Imap может только работает с привилегиями суперпользователя (root).


3.2.5 Ведение квот


http://web.onda.com.br/nadal/ находится файл postfix-vda.patch, который дает возможность Postfix’u вести учет размера почтового ящика. Кстати этот патч на квоты в упор не хотел накладываться на postfix на Солярке. Я взял переписал sources postfix’a и данного патча на Linux (по-моему это был ШнэкWarezz), потом наложил патч на postfix и обратно переписал на Солярку.


3.3 Установка Серверов


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

2. После установки и настройки MySQL нам требуется установить Cyrus SASL. Я просто приведу опции установки для моей систему конечно у каждого пути будут разные

./configure --prefix=/usr/local --sysconfdir=/usr/local/lib/sasl2 --enable-ntlm –with-devrandom=/dev/random --with-openssl=/usr/local --enable-login --enable-plain --with-saslauthd=/usr/local/sbin --with-authdaemond=/usr/local/var/authdaemon --with-pwcheck=/usr/local/var/authdaemon --enable-sql --with-mysql=/usr/local/mysql

--with-rc4 --enable-digest --disable-otp -disable-cram --disable-gssapi --disable-anon --disable-pam --disable-java --without-javabase --without-dbpath --without-dblib --without-bdb-libdir --without-bdb-incdir --without-gdbm --without-ldap

--without-pgsql --without-sqlite --without-des CPPFLAGS="-I/usr/local/mysql/include/mysql"

LDFLAGS="-L/usr/local/mysql/lib/mysql -R/usr/local/mysql/lib/mysql -lmysqlclient -lcrypt"


Make


Make install


Настройка Cyrus SASL заключается в том что надо создать файл в /usr/local/lib/sasl2/smtpd.conf который будет читать сам постфикс.



Содержание файла:

pwcheck_method: auxprop

password_format: crypt

auxprop_plugin: sql

mech_list: DIGEST-MD5 CRAM-MD5 PLAIN LOGIN

auto_transition: yes

sql_engine: mysql

sql_user: mysql_user

sql_passwd: Mysql_pass

sql_hostnames: 127.0.0.1

sql_database: postfixdb

sql_select: SELECT password FROM mailbox WHERE username = '%u@%r'

sql_verbose: yes

log_level: 9

Подробно рассматривать каждую опцию я не буду, вы можете и так их найти в описании SASL.


4.Установка Courier IMAP

Сразу хочу сказать мне не нравится Courier IMAP как сервер POP3 протокола, так как уже не раз находили в нём дырки. Да и сам сервер к сожалению можно запустить только от roota, Но у меня не было времени да и желания изучать Дакота. Да установку и настройку Courier IMAP я рассматривать не буду.


3.Установка самого Postfix’a

Так как я уже говорил раньше для введения квот в постфиксе необходимо пропатчить систему на линуксе а потом переписать её на солярку.


Ниже приводится маленький скрипт для компиляции Postfixa, да кстати в процессе инсталляции будут заданы вопросы о том какой “prefix” дать Postfix’у я выбрал префикс /usr/local, и следующие пути к примеру путь очереди уже будет автоматически добовлятся

/usr/local.Так что будьте внимательны какие пути и куда вы ставите так как потом могут возникнуть проблемы.Да конечно надо не забыть добавить в систему пользователей postfix,postdrop и соответствующую группу.


#!/bin/sh

#make tidy

#make clean

set PATH=/usr:/usr/local:/usr/lib:/usr/include:/usr/libexec:/usr/local:/usr/local/lib:/usr/local/
include:/usr/local/lib/sasl2:/usr/local/mysql/lib/mysql:/bin:/sbin:/usr/bin:/usr/sbin

export PATH

make -f Makefile.init makefiles 'CCARGS=-DHAS_MYSQL -I/usr/local/mysql/include/mysql -DUSE_SASL_AUTH -I/usr/local/include/sasl -DUSE_SSL -I/usr/local/include/openssl' 'AUXLIBS=-L/usr/local/mysql/lib/mysql -lmysqlclient -lz -lm -L/usr/local/lib -lsasl2 -lssl -lcrypto'


Предворительно перед последней операцией, скопируйте все библиотеки(libsasl2.*) из директории /usr/local /lib в /usr/lib

Так же скопируйте все файлы из директории /usr/local /mysql/lib /mysql в /usr/lib


И потом последнее действие


#make install



Настройка Postfix’a

Я не буду объяснять каждую опцию конфига постфикса так как вы можете это найти на http://www.postfix.org

Файл Main.cf:


queue_directory = /usr/local/var/spool/postfix

command_directory = /usr/local/sbin/postfix


daemon_directory = /usr/local/libexec/postfix

sample_directory = /usr/local/etc/postfix


# opredeljaet uchotnuju zapisj kotoroja javljaetsja vladeljcem pochtovoj ocheredi

mail_owner = postfix

unknown_local_recipient_reject_code = 450

setgid_group = postdrop

html_directory = no

manpage_directory = /usr/local/man

smtpd_helo_required = yes

default_privs = mailnull


alias_database = mysql:/usr/local/etc/postfix/sql/alias.mysql

alias_maps = $alias_database


local_command_shell = /usr/lib/smrsh -c


# Vsegda slatj Ehlo pri nachale smtp sessiji

smtp_always_send_ehlo = yes

# Zapreshenije vrfy

disable_vrfy_command = yes

# Vikljuchaem zaderzhku na otvet v sluchae oshibki clienta

smtpd_error_sleep_time = 0

# Kogda kolichestvo oshibok previshaet eto kolichestvo to postfix rvet soedinenije

#smtpd_hard_error_limit = 8

# kolichestvo processov zapuskaemih postfixom (klientskih i servernih) naprjamuju svjazana s kazhdoj strochkoj iz master.cf (maxproc)

default_process_limit = 2000

# kolichestvo soedinenij s klientami ono dolzhno bitj rovno polovine default_process_limit

smtpd_client_connection_count_limit = 500

smtpd_proxy_timeout = 10s


# Ogranichivaet razmer pisjma pri oshibke

bounce_size_limit = 2000


# Chastota s kotoroj deffered mails pitajutsja zaitji v active queue uvilichenije etogo prarmetra

# videt k povisheniju proizvoditeljnosti tak kak rezhe process dlja peresilki pochti budet zapuskatsja

minimal_backoff_time = 1000s

maximal_backoff_time = 2000s

#Kak chasto manager ocheredi skaniruet ocheredj na "defered mail"

#queue_run_delay = 50s

#qmgr_message_recepient_limit = 9000


# Timeout dlja prinjatija ot klienta kakih ti bi ne bilo zaprosov po istecheniju etogo vremeni svjazj rvetsja

smtpd_timeout = 20s

#vremja dlja poluchenija helo/ehlo kommand dlja neotvechajushego udaljonogo SMTP servera

smtp_helo_timeout = 10s

smtp_mail_timeout = 10s

smtp_rcpt_timeout = 10s


virtual_alias_maps = mysql:/usr/local/etc/postfix/sql/alias.mysql

virtual_mailbox_base = /usr/local/var/spool/postfix/vmail

virtual_gid_maps = static:5001


virtual_minimum_uid = 5001

virtual_transport = virtual

virtual_uid_maps = static:5001



virtual_mailbox_domains = mysql:/usr/local/etc/postfix/sql/domains.mysql

virtual_mailbox_maps = mysql:/usr/local/etc/postfix/sql/mailbox.mysql


virtual_create_maildirsize = yes

virtual_mailbox_extended = yes

virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/sql/quota.mysql

virtual_mailbox_limit_override = yes

virtual_maildir_limit_messages = Sorry, the user's maildir has overdrawn his diskspace quota, please try again later.

virtual_overquota_bounce = yes


# Imja danogo kompa s postfixom vkljuchajushaja domenuju chastj (FQDN)

myhostname = mail.myserver.lv

# Domen dannogo kompjutera

mydomain = myserver.lv

# eti setki postfix schitaet lokaljnimi i komp iz etoj setki budet imetj vozmozhnostj mail relaya

mynetworks = 127.0.0.0/8 195.195.195.1/24


myorigin = $mydomain

# eta derektiva zadaet shto postfix dolzhen prinjatj pochtu dlja poljzovatelej dannogo domena

mydestination = $myhostname, localhost.$mydomain, localhost


debug_peer_level = 9


#========== CLAMAV VIRUS CHECK

content_filter = scan:127.0.0.1:10025

receive_override_options = no_address_mappings


empty_address_recipient = notrelay@myserver.lv


#==========Access

broken_sasl_auth_clients = yes

smtpd_sasl_auth_enable = yes

smtpd_sasl_security_options = noanonymous

smtpd_sasl_password_maps = mysql:/usr/local/etc/postfix/sql/sasl.mysql

smtpd_banner = ESMTP READY !!! ALL CONNECTION LOGGED IN WINDOWS STATION :( !!!


smtpd_delay_reject = no


# Proverka poljzovatelej

local_recipient_maps = $alias_maps, $virtual_mailbox_maps


#esli stoit to ljubije ogranichenija delajutsja posle vvoda commandi RCPT TO

smtpd_delay_reject = no

#Obichno eto ogranichenije srazu posle "HELO/EHLO" vvoda komandi

#Esli danije peresilajutsja ot clienta k e-mail(serveru) to client budet govoritj

#ustonovleniju u nego v hostname imja sootvetstvenno server ne smozhet ne chego opredelitj po etomu imenji

#NO esli potom e-mail(server)kogda poluchit e-mail ot klienta budet ego peresilatj daljshe to togda

#na zapros helo/ehlo on otvetit svoim imenem

#smtpd_helo_restrictions = reject_unknown_hostname


#smtpd_delay_reject = yes -esli stoit to ogranichenija delajutsja posle commandi RCPT TO

#proverka na etpe kogda poluchenajem "MAIL FROM" commandu

#reject_unknown_sender_domain reject when sender mail adress has no DNS A or MX record

smtpd_sender_restrictions = reject_unknown_sender_domain


# proverka na etpe poluchenija pisem

# Skoljko adressov e-mail'a mozhet soderzhatsja v odnom e-maile na dostavku (chislo CC v odnom pisjme)

smtpd_recipient_limit = 10000

# proverka na etape kogda polucheam commandu "RCPT TO:"

smtpd_recipient_restrictions = reject_unknown_sender_domain, reject_invalid_hostname, permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

readme_directory = no


Файл master.cf:

#

# Postfix master process configuration file. For details on the format

# of the file, see the Postfix master(5) manual page.

#

# ==========================================================================

# service type private unpriv chroot wakeup maxproc command + args

# (yes) (yes) (yes) (never) (100)

# ==========================================================================

smtp inet n y y - 500 smtpd -o content_filter=scan

127.0.0.1:1025 inet n y y - 500 smtpd -o content_filter=

pickup fifo n y y 60 1 pickup

cleanup unix n y y - 0 cleanup

qmgr fifo n y y 300 1 qmgr

rewrite unix - y y - 500 trivial-rewrite

bounce unix - y y - 0 bounce

defer unix - y y - 0 bounce

trace unix - y y - 0 bounce

flush unix n y y 1000? 0 flush

proxymap unix - y y - 500 proxymap

smtp unix - y y - 500 smtp

error unix - y y - 500 error

discard unix - y y - 500 discard

local unix - n y - 500 local

virtual unix - n n - 500 virtual

lmtp unix - y y - 500 lmtp

anvil unix - y y - 1 anvil

scache unix - y y - 1 scache

scan unix - n n - 500 pipe flags=q user=clamav:clamav argv=/usr/local/bin/avcheck -S 127.0.0.1:1025 -d /var/clamav/tmp -s clamav:127.0.0.1:3310 -f ${sender} -- ${recipient}

maildrop unix - n n - 500 pipe flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}


Как видно из файла master.cf я по самые помидоры затянул фичи безопастности да кстати путь к avcheck нужно указать правильный.


Теперь необходимо создать в директории с конфигами и директорию “sql” в которой будет хранится файлы обращения к базе MySQL.Кстати конфигурацию SQL части я взял из описания Postfix Admin 2.1 так как управление будет иметь WEB Interface

Все ниже перечисленые файлы должны хранится в директории sql.


Файл alias.mysql:

user = mysql_user

password = Mysql_pass

hosts = 127.0.0.1

dbname = postfixdb

table = alias

select_field = goto

where_field = address

Файл domains.mysql:

user = mysql_user

password = Mysql_pass

hosts = 127.0.0.1

dbname = postfixdb

table = domain

select_field = description

where_field = domain

#additional_conditions = and backupmx = '0' and active = '1'


Файл mailbox.mysql:

user = mysql_user

password = Mysql_pass

hosts = 127.0.0.1

dbname = postfixdb

table = mailbox

select_field = maildir

where_field = username

additional_conditions = and active = '1'


Файл quota.mysql:

user = mysql_user

password = Mysql_pass

hosts = 127.0.0.1

dbname = postfixdb

table = mailbox

select_field = quota

where_field = username

additional_conditions = and active = '1'


Файл relay.mysql:

user = mysql_user

password = Mysql_pass

hosts = 127.0.0.1

dbname = postfixdb

table = domain

select_field = domain

where_field = domain

additional_conditions = and backupmx = '1'


Файл sasl.mysql

user = mysql_user

password = Mysql_pass

hosts = 127.0.0.1

dbname = postfixdb

table = mailbox

select_field = password

where_field = username


файл transport:

myserver.lv


Установка ClamAV

На установке и настройки ClamAV я не буду останавливатся скажу только одно

В файле конфигурации clamd.conf необходимо установить две опции:

TCPSocket 3310

TCPAddr 127.0.0.1

Так как Postfix будет работать с ClamAV через AvCheck который в свою очередь работает с ClamAV через TCP порт даную схему мы расмотрим позже.

Установка AvCheck

Заключается в том чтобы в файле avcheck.c закоментировать или наоборот от коментировать несколько DEFINE. И после компиляции надо скопировать полученый бинарник “avcheck” в директорию /usr/local /bin можно конечно и в другую но тогда в файле master.cf надо изменить путь к avcheck.



MySQL запросы на создание базы данных для сервера Postfix


Postfixdb – база данных сервера Postfix должна содержать таблицы:

  1. Alias table - таблица пользовательских псевдонимов.

  2. Domain table - таблица доменных имен для виртуального сервера.

  3. Mailbox table - таблица пользовательских бюджетов.

  4. Domain admins table - таблица администраторов доменов.

  5. Admin table - таблица бюджетов администраторов доменов.

  6. Log table – таблица ведения логов web системы управления.


Необходимо создать доступ к базе данных postfixdb для адресов 127.0.0.1 и localhost, так как сервер Postfix будет связываться с базой через адрес 127.0.0.1 виртуального сетевого локального адаптера lo0.

GRANT SELECT ON postfixdb.* TO mysql_user@"127.0.0.1" IDENTIFIED BY 'Mysql_pass’ WITH GRANT OPTION;

GRANT SELECT ON postfixdb.* TO mysql_user@"localhost" IDENTIFIED BY 'Mysql_pass’ WITH GRANT OPTION;

Нижний запрос требуется для того чтоб когда postfix и mysql будут работать в разных зонах они имели связь между собой.

GRANT SELECT ON postfixdb.* TO mysql_user@"195.195.195.20 " IDENTIFIED BY 'Mysql_pass’ WITH GRANT OPTION;

FLUSH PRIVILEGES;


Создание Таблиц Базы данных Postfixsb

1) CREATE TABLE alias (

address varchar(255) NOT NULL default '',

goto text NOT NULL,

domain varchar(255) NOT NULL default '',

created datetime NOT NULL default '0000-00-00 00:00:00',

modified datetime NOT NULL default '0000-00-00 00:00:00',

active tinyint(1) NOT NULL default '1',

PRIMARY KEY (address),

KEY address (address)

) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Aliases';


2) CREATE TABLE domain (

domain varchar(255) NOT NULL default '',

description varchar(255) NOT NULL default '',

aliases int(10) NOT NULL default '0',

mailboxes int(10) NOT NULL default '0',

maxquota int(10) NOT NULL default '0',

transport varchar(255) default NULL,

backupmx tinyint(1) NOT NULL default '0',

created datetime NOT NULL default '0000-00-00 00:00:00',

modified datetime NOT NULL default '0000-00-00 00:00:00',

active tinyint(1) NOT NULL default '1',

PRIMARY KEY (domain),

KEY domain (domain)

) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Domains';


3) CREATE TABLE mailbox (

username varchar(255) NOT NULL default '',

password varchar(255) NOT NULL default '',

name varchar(255) NOT NULL default '',

maildir varchar(255) NOT NULL default '',

quota int(10) NOT NULL default '0',

domain varchar(255) NOT NULL default '',

created datetime NOT NULL default '0000-00-00 00:00:00',

modified datetime NOT NULL default '0000-00-00 00:00:00',

active tinyint(1) NOT NULL default '1',

PRIMARY KEY (username),

KEY username (username)

) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Mailboxes';


4) CREATE TABLE domain_admins (

username varchar(255) NOT NULL default '',

domain varchar(255) NOT NULL default '',

created datetime NOT NULL default '0000-00-00 00:00:00',

active tinyint(1) NOT NULL default '1',

KEY username (username)

) TYPE=MyISAM COMMENT='Postfix Admin - Domain Admins';

5) # Table structure for table admin

CREATE TABLE admin (

username varchar(255) NOT NULL default '',

password varchar(255) NOT NULL default '',

created datetime NOT NULL default '0000-00-00 00:00:00',

modified datetime NOT NULL default '0000-00-00 00:00:00',

active tinyint(1) NOT NULL default '1',

PRIMARY KEY (username),

KEY username (username)

) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Admins';


6) CREATE TABLE log (

timestamp datetime NOT NULL default '0000-00-00 00:00:00',

username varchar(255) NOT NULL default '',

domain varchar(255) NOT NULL default '',

action varchar(255) NOT NULL default '',

data varchar(255) NOT NULL default '',

KEY timestamp (timestamp)

) TYPE=MyISAM COMMENT='Postfix Admin - Log';



Описание сети:

2 сервера под управлением операционных систем “Solaris” и “Linux” находятся в одном сегменте сети Fast Ethernet и висят на одном Свитче. Сервер с О.С. “Solaris” будет главным объектам для исследования. Сервер с О.С “Linux” будет содержать программу тестирования “Postal” и Web систему управления пользовательскими бюджетами. Почтовый сервер будет тестироваться на прием почты, по этой причине необходим еще один сервер в том же сегменте локальной сети.


3.4 Описание почтового сервера Postfix


postfix – это один из самых популярный MTA (Mail Transfer Agent), позволяющий эффективно передавать письма адресатам. MTA это программа, которая лежит в основе передачи электронной почты. Когда посылается письмо с адресом user@mail.com, то делается это как правило посредством SMTP-клиента (например, Outlook, The Bat!, mutt), который передает письмо MTA. SMTP-клиент может делать это по протоколу SMTP или еще каким-нибудь способом, в любом случае он не выполняет доставку письма самостоятельно. MTA получает письмо и проверяет по своей конфигурации, что он должен с ним сделать: послать адресату напрямую (то есть, послать письмо MTA, который установлен на хосте, куда указывает MX-запись соответствующего домена). MTA должен уметь делать следующие действия:

* Обрабатывать входные соединения для приема почты по протоколу SMTP.

* Сохранять почту на диске в очереди.

* Отправлять почту адресату из очереди (это может быть локальный почтовый ящик, другой MTA или еще какой-то иной способ доставки почты).

* Гарантировать доставку почты получателю или нотификацию отправителю о невозможности доставки.

Этот список – только самое необходимое. Хороший MTA должен, кроме того, быть надежным, высокопроизводительным, гибок в настройках, поддерживать интеграцию с фильтрами (например, для борьбы со спамом) и т.д.

Долгое время фактически единственным MTA для операционных систем типа Unix был sendmail, отличающийся крайне сложным форматом конфигурации и, неудобством использования. Сейчас ситуация иная – есть еще несколько свободных почтовых систем, таких как QMail и postfix, которые обладают целым рядом преимуществ по сравнению с sendmail. Впрочем, sendmail все равно есть и, наверное, останется самым популярным MTA, так как традиционно входит в состав огромного количества дистрибутивов Unix. Кроме того, опять же, интерфейс с установленным MTA, все равно закрепился как вызов программ из установки sendmail и все остальные MTA поддерживают его. Общая организация postfix: модульность, управляющий процесс master.

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

Интересно, что модули постфикса, или сервисы, устроены в виде отдельных программ, каждая из которых запускается из управляющего сервиса master (порождается от master). Почему выбрана такая модель (то есть, множество однопоточных процессов) а не, к примеру, популярные потоки?

Многопоточные сервисы вполне могут существовать и создаваться, это мы все рассмотрим чуть позднее. Но все сервисы постфикса основаны либо на select, либо на pre-fork моделях. Связано это, с отлаженностью этих механизмов на различных операционных системах. Постфикс компилируется и успешно работает практически на всех более-менее популярных операционных системах типа Unix, это было бы попросту невозможно в том случае, если бы использовались потоки, так как на разных О.С. типа Unix потоки имеют разную реализацию. А pre-fork модель или select вполне отлажена в течение десятилетий использования. Для функционирования всей системы в целом необходимо запустить только процесс master. Он читает конфигурационный файл следующего вида:


Таблица 3.4.1.

Настройки демонов сервера Postfix


===================================================================

# service type private unpriv chroot wakeup maxproc command + args

# (yes) (yes) (yes) (never) (100)

===================================================================

smtp inet n y y - 500 smtpd -o content_filter=scan

127.0.0.1:1025 inet n y y - 500 smtpd -o content_filter=

pickup fifo n y y 60 1 pickup

cleanup unix n y y - 0 cleanup

qmgr fifo n y y 300 1 qmgr

rewrite unix - y y - 500 trivial-rewrite

bounce unix - y y - 0 bounce

defer unix - y y - 0 bounce

trace unix - y y - 0 bounce

flush unix n y y 1000? 0 flush

smtp unix - y y - 500 smtp

error unix - y y - 500 error

discard unix - y y - 500 discard

local unix - n y - 500 local

virtual unix - n n - 500 virtual

lmtp unix - y y - 500 lmtp

anvil unix - y y - 1 anvil

scache unix - y y - 1 scache

scan unix - n n - 500 pipe flags=q user=clamav:clamav argv=/usr/local/bin/avcheck -S 127.0.0.1:1025 -d /var/clamav/tmp -s clamav:127.0.0.1:3310 -f ${sender} -- ${recipient}


В таблице 3.4.1 приводится список всех доступных сервисов. Вкратце, о том, что значат все эти надписи, по столбцам:

  • service – название сервиса в случае использования сокетов unix-domain и прочих средств IPC (interprocess communication), порт и интерфейс в случае использования интернет-сокетов (smtp, аналог TCP порта 25).

  • type – тип сокета, который слушается сервисом.

  • private – признак того, что сервис может быть доступен снаружи MTA. Все сервисы с типом inet не могут быть private по понятным причинам – никак нельзя ограничить доступ процессов к интернет-сокету. В зависимости от этого флага отличается каталог и права доступа на файл, который связан с создаваемым сокетом. Доступные снаружи сервисы нужны по разным причинам, например сервис showq нужен для получения содержимого очереди сообщений для команды mailq.

  • unpriv – признак того, что сервис запускается от пользователя ограниченными привилегиями postfix, а не от супер-пользователя (root). Надо сказать, что смена пользователя целиком и полностью лежит на самом сервисе, а не управляющем сервисе master (единственное, чем отличается запуск непривилегированного процесса от запуска привилегированного – так это наличием ключа -u). Это можно объяснить тем, что сервисы (а это программы в каталоге /usr/local/libexec/postfix) просто так не появляются и они должны поддерживать такой интерфейс. Этот флаг нужен для того, чтобы обезопасить сервер от сбоев в работе. Допустим, в сервере Postfix найдена критическая ошибка в каком-либо из сервисов, позволяющая злоумышленнику выполнить любой код на вашем сервере; если бы сервисы работали от root'а то этот человек получил бы полный доступ к компьютеру, а если они работают от пользователя postfix, который даже не имеет возможности логина, то под ударом только ваша почта. Естественно, что основной процесс, master, работает с правами суперпользователя root, это значит, что master должен быть максимально простым для того, чтобы гарантировать отсутствие в нем грубых ошибок.

  • chroot – аналогично unpriv, флаг заставляет сервисы выполнить вызов chroot на /usr/local/var/spool/postfix. Тем самым для сервисов меняется положение каталога '/' и они не могут получить доступ к другим файлам, кроме очереди сообщений. Необходимость этого флага обусловлена теми же причинами, что и для unpriv.

  • wakeup – нотификация сервиса каждые n секунд. Это позволяет заставить сервисы, допустим, перечитать очередь. На самом деле, нотификация об изменениях в очереди может быть доставлена от других сервисов, но это все равно полезно: вдруг где-то что-то сломалось, или постфикс перезапустился, все равно очередь должна быть проверена.

  • maxproc – максимальное количество процессов сервиса, которые могут быть запущены. Это число очень полезно для настройки особенно тяжеловесных сервисов, запуск которых может привести к большой загрузке сервера.

  • command – это просто название программы и аргументы, которые должны быть ей переданы. Здесь никаких тонкостей нет.


Значения по умолчанию (например, maxproc) настраиваются через другой конфигурационный файл – main.cf. master: запуск сервисов, интерфейс с уже запущенными сервисами.

Теперь необходимо рассмотреть, как появляются новые процессы, выполняющие работу сервисов.

При запуске главного сервиса master считывает файл конфигурации master.cf, по которому он определяет, какие сервисы понадобятся. Он создает все указанные сокеты, и готовиться их "слушать", инициализирует внутри себя структуры, описывающие состояние каждого из сервисов (количество запущенных процессов и т.п.), после чего master заносит все дескрипторы сокетов в select и ждет какого-нибудь из событий на каждый из них. Теперь, допустим, на наш сервер должна прийти почта. Когда приходит почта, это значит, что некий почтовый релей установил соединение на 25-й порт нашего сервера. В этом случае, дескриптор в master'е, который связан с этим портом, будет готов к чтению и, тем самым, select завершится. Главный сервис master не умеет обрабатывать smtp-сессию, зато это умеет делать сервис smtpd, который и будет запущен при помощи fork. Естественно, что перед запуском производится некоторое количество действий, которые пока что не особенно интересны, важно то, что сразу же после запуска smtpd выполняет accept на этом дескрипторе и приступает к обработке smtp-сессии и пересылке письма дальше по сервисам до менеджера очереди.

Перед запуском master увеличивает счетчик процессов сервиса smtpd и запоминает его pid. Этот счетчик будет уменьшен тогда, когда master получит сигнал SIGCHLD, то есть smtpd завершится. Тем самым, master может контролировать количество запущенных процессов.

Теперь интересно как smtp-сессия обрабатывается, master может опять реагировать на изменения состояния дескриптора, связанного с 25-м портом, а что делать, когда эта сессия закончится? Глупо завершать smtpd сразу же после обработки одного письма если через секунду, возможно, придет еще одно письмо: тогда будет слишком много затрат связанных с fork. Тем самым, сервисы должны уметь обрабатывать новые соединения и при этом им не должен вмешаться master. Кроме того, master все равно должен следить за сервисами и если кто-то из них прекратил работу и умер, то это не должно сказаться на работоспобности MTA в целом.

Опять же, технология в этом случае совершенно простая, грубо говоря, это и есть pre-fork модель построения сетевых серверов, единственное отличие от, скажем так, классической реализации заключается в том, что обычно сервер выполняет только одну операцию и, тем самым, можно сделать так, чтобы главный сервер (который и выполняет fork) сам по себе тоже способен обрабатывать соединения. В случае с master, который запускает любые сервисы, реализовать такой подход попросту нереально.

Обычно, каждый свободный экземпляр сервера (то есть, отдельный процесс) выполняет accept (или сначала select, а потом accept) на нужный дескриптор. При этом реально accept будет выполнен только у одного экземпляра сервера (заранее неизвестно какого именно), остальные получат ошибку и могут опять запустить accept или select. Сейчас же, master должен уметь отличать ситуации, когда свободных экземпляров сервиса нет (тогда он должен слушать сокет сам и выполнить fork, когда придет новое соединение) и когда кто-то свободен (и тогда ему не надо запускать select на этот сокет, поскольку этот "кто-то" сам следит за своим сокетом). Естественно, что это делается опять же через IPC: между потомком (то есть, экземпляром сервиса) и master'ом есть канал, через который потомок уведомляет master о своей занятости или свободности. Когда потомок изменяет свое состояние, он передает master'у два значения: свой pid и флаг "занят" или "свободен".

Реализация многопроцессного однопотокового сервиса в этом случае простая: сервис все время делает accept, по успеху последнего он оповещает master о занятости, затем начинает обрабатывать соединение, после чего сообщает master'у о своей свободности и опять делает accept. Если в какой-то момент он решит закончить свое выполнение, он может это сделать без оповещений –– master получит сигнал от операционной системы и выполнит все необходимые действия.

Теперь небольшая ремарка о необходимости завершения работы процесса. Это общепринятая практика, когда сервер после выполнения определенного числа операций или простоя, прекращает свое выполнение. Такая логика очень полезно, потому что уменьшает зависимость от ошибок с неудалением выделенной ранее памяти (попросту, "разбухания" процесса), поэтому обычно любые сервера имеют какое-то ограничение на количество обработанных запросов и время простоя. В postfix каждый сервис имеет подобные ограничения, кроме, разумеется, master, так как последний некому перезапустить.

Когда некий сервис требует для своей работы другой сервис (например, для того чтобы передать почтовое сообщение от smtpd в cleanup), он всего-навсего обращается по нужному сетевому адресу (в сети Интернет или файловой системе), все остальное за него будет сделано master'ом или работающим целевым сервисом.


3.4.1 Архитектура Работы Postfix



3.4.2 Описание процессов работы сервера Postfix


  1. smtpd – демон SMTPD принимает соединение из сети и выполняет 0 или более транзакций на соединение, а также, если включены фильтры, проверяет доступ поступившего письма. Допустимо ли письмо и не попадает ли это письмо под запрещенные в директиве ACCESS и фильтр спама базы RBL. После проверок демон Smtpd передает письмо сервису scan, который запускает утилиту avcheck.

  2. Avcheck – утилита, предназначенная для связующего звена между smtpd демоном и ClamAV антивирусом. При поступлении письма avcheck передает копию письма по адресу 127.0.0.1 на порт 3310, на котором работает Clamav антивирус.

  • В случае, если в письме был найден вирус, то Clamav возвращает код ошибки и имя вируса, найденного в письме. Avcheck уничтожает письмо и передается уведомление о найденном вирусе и адресате пославшего его в master демон для занесения этой информации в логи.

  • В случае, если в письме Clamav не нашёл вирусов, тогда avcheck возвращается положительный код и avcheck возвращает письмо обратно через адрес 127.0.0.1 на порт 1025 smtpd демону, который в свою очередь передаёт письмо дальше по схеме.

  1. cleanup - проводит первоначальные проверки на правильность оформления и формат входящего сообщения, позволяя защитить остальную систему от многих атак, рассчитанных на переполнение буфера. В случае необходимости добавляет в письмо недостающие служебные заголовки и с помощью процесса по имени trivial-rewrite приводит адреса к виду: пользователь@поддомен.домен. В postfix пока нет полноценного языка, позволяющего гибко управлять процессом переписывания адресов, поэтому вместо него используются служебные таблицы canonical и virtual для хранения правил, управляющих перезаписью. После такой обработки письмо сохраняется на диск в формате файла почтовой очереди и кладется в директорию, где хранится очередь incoming, обычно это директория /usr/local/var/spool/postfix/incoming/. Эта очередь используется только для обработки входящих сообщений. Затем процесс cleanup уведомляет процесс queue manager (qmgr), управляющий всеми очередями о прибытии новой почты.

  2. Pickup – демон для доставки почты, отправленной локально из самой операционной системы. Во многих UNIX-системах в качестве почтовой программы и по сей день традиционно используется sendmail. Соответственно большинство скриптов, написанных ранее и используемых сейчас, используют именно sendmail, считает, что в системе установлена именно эта разновидность почтового сервера. Чтобы не разрушить совместимость с огромным количеством программного обеспечения при переходе к использованию Postfix, в систему вместе с прочими файлами устанавливается программа, заменяющая sendmail. Таким образом, получается, что команда mail передает письмо программе sendmail, установленной Postfix, а та в свою очередь вызывает привилегированную программу postdrop. Ну а последняя уже кладет файлы с письмами в служебную директорию maildrop, которая обычно находится в /var/spool/postfix/maildrop/. Затем письмо подхватывает процесс pickup и аналогично SMTP-демону проверяет соответствие послания установленным форматам, в результате чего письмо попадает в лапы свободному на данный момент процессу cleanup. В случае, если письмо не поддается коррекции, то оно немедленно уничтожается. Стоит отметить, что отправитель не получит никаких извещений о данном факте. Если же ничего аномального не было найдено, то сообщение беспрепятственно попадет в очередь incoming, а queue manager, как обычно, получит сигнал о появлении новых писем.

  3. Bounce, Defer. Демоны для извещения об ошибке доставки почты. Новая почта также может появиться в случае, если письмо невозможно доставить адресату и настройки системы диктуют в данном случае отправлять оповещения отправителю. При таком повороте событий процесс bounce или defer генерирует письмо с извещением о проблеме. Адресовано оно, конечно же, отправителю первоначального сообщения. Другим источником возникновения писем может быть настройка Postfix, заставляющая его отправлять администратору уведомления в случае проблем c SMTP или нарушения тех или иных политик, которые продиктованы настройками почтовой системы. Соответственно, в обоих случаях, чтобы доставить письмо адресату, приходится, как обычно, отдать его процессу cleanup и затем отправить в очередь incoming.

  4. Qmgr – Демон доставки почты ждёт пришедшую почту в очередь incoming и договаривается о доставке почты через Postfix delivery process. Получив от cleanup уведомление о поступлении новой почты, демон queue manager, управляющий очередями, перекладывает письмо в очередь active. Кстати, стоит заметить, что эта очередь очень маленького размера. В ней может находиться всего лишь несколько писем, которые в данный момент находятся в процессе доставки. Сделано это по двум причинам. Во-первых, для того чтобы при большой нагрузке менеджер очередей не потреблял слишком много памяти. Вторая причина состоит в том, что в случае, когда принимающая сторона не в состоянии получать письма с той скоростью, с которой Postfix старается их передать, приходится притормозить скорость отправки. С малой очередью это делать гораздо проще. Положив письмо в очередь active, демон queue manager создает запрос к процессу trivial-rewrite, с помощью которого удается определить точку назначения сообщения. В ответ можно будет лишь узнать, локальный ли получатель или на удаленной системе. Добавочную информацию о маршрутизации почты можно получить из файла transport. В зависимости от полученных данных queue manager входит в контакт с одним из следующих агентов доставки:

  • Local используется для доставки почты внутри локальной системы. Умеет работать со стандартными для UNIX почтовыми ящиками. В случае, если для данного пользователя не заведено системных псевдонимов, в файле псевдонимов обычно это /usr/local/etc/postfix/aliases и нет файлов локального перенаправления .forward, которые любой пользователь может создать в своей домашней директории, то письмо сразу же попадает в целевой почтовый ящик. В противном случае письмо будет передано туда, куда они указывают. Возможности локального агента доставки на этом не заканчиваются. Одновременно могут работать несколько агентов локальной доставки, но в то же время параллельная доставка нескольких писем в один почтовый ящик обычно не практикуется. Несмотря на то, что агент локальной доставки может самостоятельно справиться со всеми проблемами, по желанию администратор может задействовать механизмы, позволяющие передоверить доставку в почтовый ящик внешним программам. Примером такой программы может служить широко известный многим администраторам procmail.

  • Virtual агент виртуальной доставки представляет собой очень урезанную версию агента локальной доставки. Поэтому он может работать только с почтовыми ящиками в формате mailbox. В нем также отсутствует поддержка системной базы псевдонимов и файлов .forward. За счет таких ограничений данный агент доставки считается самым безопасным из всех. Благодаря своей природе он может легко работать с почтой, предназначенной для нескольких виртуальных доменов, располагающихся на одной и той же машине.

  • SMTP-клиент, являющийся еще одним агентом, вступает в действие в тот момент, когда нужно доставить письмо пользователю удаленной системы. Обычно queue manager передает ему следующие данные: имя файла очереди, адрес получателя, хост или домен, куда нужно доставить почту, адрес отправителя. Первым делом с помощью DNS-запроса нужно получить список MX-серверов для целевого домена и отсортировать его по приоритету. Следующим шагом мы начинаем пробовать каждый адрес до тех пор, пока не найдем тот, который находится в рабочем состоянии. Обычно доставка идет одновременно сразу для нескольких доменов, поэтому в системах, через которые проходит большой поток почты, можно увидеть несколько SMTP-клиентов, работающих параллельно. Если доставка завершилась удачно, то SMTP-клиент модифицирует файл почтовой очереди так, чтобы было понятно, что он обработан. В противном случае данный клиент уведомляет queue manager либо о фатальной ошибке, либо о временных затруднениях. Проблемы первого типа могут быть вызваны отсутствием нужного пользователя удаленной системы, а вторые, к примеру, неполадками в сети или неработоспособностью принимающего сервера. В первом случае процесс bounce посылает отправителю оповещение о неудаче предпринятого действия и делает запись в протокол работы почтовой системы с помощью syslogd. А во втором письмо помещается в специальную очередь deferred, где оно должно дожидаться следующей попытки доставки.

  • LMTP-клиент работает точно так же, как и SMTP-клиент, разве что протокол используется другой. LMTP специально создан для того, чтобы доставлять письмо на локальный или удаленный сервер, выделенный для хранения почтовых ящиков. В таком качестве могут выступать Courier- или Cyrus-сервер. Большим плюсом такого подхода к доставке писем является то, что один сервер Postfix может раздавать письма разным серверам почтовых ящиков. В то же время никто не мешает одному серверу почтовых ящиков получать почту от нескольких серверов postfix одновременно.

  1. incoming queue - эта очередь используется только для обработки входящих сообщений. Затем процесс cleanup уведомляет процесс queue manager, управляющий всеми очередями о прибытии новой почты.

  2. Deferred queue эта очередь для не доставленных сообщений. В ней складируются отложенные до лучших времен письма. На файл с письмом ставится временной штамп, находящийся в будущем, соответственно следующая обработка этого файла произойдет именно в тот момент, на который указывает штамп. Периодически менеджер очередей проверяет, не пришло ли время предпринять следующую попытку доставки отложенных сообщений. В случае если есть необходимость выполнить очередную попытку раньше, чем истечет тайм-аут, нужно воспользоваться командой postfix flush.

  3. Corrupt queue - Следующим интересным для нас понятием является очередь corrupt. В нее попадают все поврежденные, нечитаемые или неправильно отформатированные файлы почтовых очередей. Такая предосторожность позволяет изолировать подозрительные данные до тех пор, пока администратор системы не решит, что с ними делать. Впрочем, за несколько лет непрерывной работы моего сервера мне так и не удалось увидеть ни одного случая, чтобы сообщение попало в эту очередь.

  4. Hold queue - Последняя из существующих в системе очередей называется hold. Здесь хранятся письма, доставка которых приостановлена по тем или иным причинам. Они будут находиться в этой очереди, пока не поступит специальная команда, выводящая их из состояния паузы.


3.5 Защита сервера Postfix встроенными средствами

3.5.1 Защита от атак из вне


Так как постфикс разделён на модули, то к разным модулям предъявляется разного рода уровень защиты. Например, модуль smtpd работает на порту и принимает все входящие соединения и по этой причине к нему предъявляется повышенный уровень защиты. Он работает от прав доступа postfix, а не от root к тому же, чтобы усложнить использование эксплойтов новых уязвимостей в этом модуле мы можем его запустить в chroot оболочке, которая ограничит работу демона в пределах одной директории и не даст доступ к системным командам. Главной модуль master, который управляет системой демонов, должен запускаться с правами root, поэтому этот демон самый уязвимый в системе. Для защиты демона master он содержит минимальное количество строк кода и написан максимально просто.

А также для защиты других модулей применяются следующие меры:

  • Использование наименьших привилегий. Postfix может быть легко ограничен с помощью chroot и работать от имени самого бесправного пользователя.

  • Ни одна из служебных программ не использует set-uid бит.

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

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

  • Память для текстовых фрагментов и служебных буферов выделяется динамически, что позволяет значительно снизить вероятность успешного использования ошибок переполнения буфера. Слишком большие текстовые массивы обрабатываются после разрезания на фиксированные фрагменты. После завершения всех нужных действий они снова склеиваются воедино. Такой подход позволяет существенно уменьшить требования программы к наличию оперативной памяти.

  • Количество объектов, находящихся в памяти, строго ограничено. Соответственно даже под большой нагрузкой Postfix не будет потреблять слишком много системных ресурсов. Ведь скорость обработки почтовых сообщений вряд ли вырастет от того, что мы, к примеру, вместо десяти будем использовать двадцать буферов для обработки писем. Тут уже ограничителем выступает скорость аппаратных компонентов самой системы, а не количество объектов для обработки почты, хранящейся на диске.

  • Также выполнение команд самой операционной системы будет производится в специально созданном для этих целей командном интерпретаторе “smrsh”, в котором можно выполнить только определенные команды не угрожающие безопасности.

3.5.2 Защита от DOS, DDOS, СПАМА


Так как требуется защитить почтовый сервер Postfix от DDOS – атак, DOS – атак, сделать пересылку спама более сложным заданием для этих целей.

Для отправки почты необходимо разобраться, как же происходит вся smtp сессия.

Пример отправки почты при smtp сессии

    1. telnet myserver.lv 25

    2. HELO hostname

    3. MAIL FROM: email_adress_ot_kogo

    4. RCPT TO: email_adress_komu

    5. DATA

    6. .

    7. QUIT


Описание:

  1. соединяемся с почтовым сервером myserver.lv;

  2. указываем свое hostname имя;

  3. от кого будет послано письмо;

  4. кому будет послано письмо, если адресатов больше 1, то команда повторяется несколько раз с нужными адресами;

  5. команда, после которой идет данные, содержащиеся в письме (тело письма);

  6. конец письма. После этого мы можем отослать еще письма. Для этого нам надо проделать все еще раз с пункта 3;

  7. команда, говорящая об окончании smtp-сессии.

Теперь можно разработать эффективные методы борьбы с спамом и DOS атаками

    1. smtp_always_send_ehlo = yes

    2. disable_vrfy_command = yes

    3. smtpd_error_sleep_time = 0

    4. default_process_limit = 2000

    5. smtpd_client_connection_count_limit = 500

    6. bounce_size_limit = 2000

    7. smtpd_timeout = 20s

    8. smtp_helo_timeout = 10s

    9. smtp_mail_timeout = 10s

    10. smtp_rcpt_timeout = 10s

    11. local_recipient_maps = $alias_maps, $virtual_mailbox_maps

    12. smtpd_sender_restrictions = reject_unknown_sender_domain

    13. smtpd_recipient_limit = 10000

    14. smtpd_recipient_restrictions = reject_unknown_sender_domain, reject_invalid_hostname, permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination


Описание:

  1. всегда слать команду HELO/EHLO с HOST. До сих пор многие программы для спам рассылок не включают в себя набор этих команд;

  2. запрещаем использовать команду vrfy для определения пользователя на сервере;

  3. отключает задержку при ошибке;

  4. ограничивает число процессов, порождаемых сервером Postfix;

  5. ограничивает число соединений с сервером должно не превышать в половину default_process_limit;

  6. ограничивает размер письма при ошибке;

  7. завершение smtp сессии при истечении данного времени;

  8. завершение smtp сессии при истечении данного времени на этапе команды “HELO/EHLO”;

  9. завершение smtp сессии при истечении данного времени на этапе команды “MAIL FROM”;

  10. завершение smtp сессии при истечении данного времени на этапе команды ”RCPT TO”;

  11. проверка, существует ли такой адрес в базе почтовых адресов и пользовательских псевдонимов;

  12. запрещаем прием сообщения, если в DNS нет записей типа A или MX о посылающем сообщения;

  13. максимальное количество адресов, на которые надо отправить сообщение;

  14. запрещаем прием сообщения, если в DNS нет записей типа A или MX о посылающем сообщение, разрешаем пересылку почты только авторизовавшимся пользователям.

Данный набор опций в файле настроек main.cf существенно снижает издержки, связанные с некорректной отправкой почты.


3.7 Итог проделанной работы


В результате мы получаем систему, которая защищена от вирусов антивирусом Clamav. Пароли пользователей, зашифрованные по алгоритму MD5, имена пользователей, размер почтового ящика, пользовательские псевдонимы теперь хранятся в базе данных MySQL сервера. За счет того, что мы используем библиотеку SASL 2.x для авторизации пользователей пароли пользователей захешированы по MD5, запрещается пересылка через наш сервер почты не существующих пользователей или не авторизовавшихся, снижает риск рассылки через сервер спама. Ограничено число адресов, кому можно отправить одно письмо размером 1000 адресов, то есть одно письмо может иметь только до 1000 "RCPT TO:”.

Защита от DOS атак путем сокращения времени ожидания сокета: в полуоткрытом состоянии, в открытом состоянии, в состоянии ожидания “Time Out”. Путем сокращения времени smtp сессии после разных команд. Проверки на существования пользовательского почтового ящика и почтового псевдонима в базе MySQL. Проверки на существования домена, от адреса которого отсылается почта. Существенное ограничение и создание изолированной песочницы для демонов сервера Postfix и запуск их под привилегией ограниченного пользователя, исключение составляют local, virtual, master, scan.


3.8 Тестирование сервера Postfix


Оборудование сервера Солярки включает в себя: процессор P4-2.4 GHz, объём оперативной памяти 1024 Mb, скорость памяти 400 MHZ, жёсткий диск SATA 80 Gb. Сетевой адаптер 3Com 905 10/100 Fast Ethernet.


Программа Postal


Postal программа предназначена для тестирования почтовых серверов путем отсылки на них большого числа писем разного объема и сбор статистики.

Основные опции программы Postal:

postal <options> <smtp-server-ip> <user-list-filename>

-m <integer> – максимальный объём в килобайтах тела одного письма, выбирается случайным образом для каждого сообщения от 0 до этого установленного значения.

-с <integer> - максимальное число писем на одно соединение, выбирается случайным образом для каждого соединения от 0 до этого установленного значения.

-r <integer> - максимально количество сообщений, которое посылается за одну минуту.

smtp-server-ip – адрес почтового сервера

user-list-filename – файл с адресами почтовых ящиков пользователя.

Программа Postal, установленной на компьютере с операционной системой Linux, находящемся в одном сегменте локальной сети Fast Ethernet с сервером postfix.

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

Цель опытов увидеть нагрузку на программное обеспечение (загрузку процессора,загрузку памяти в процентах), количество переданных сообщений в минуту, размер сообщений выбирается случайным образом из диапазона от 20 килобайт до 100 килобайт в среднем 50-60 килобайт. Количество подключений за минуту. Одновременно сервер будет обрабатывать до 100 соединений.


3.8.1 Таблица

Производительность сервера Postfix.



Minutу

1

2

3

4

5

Messages

3141.4

2971.4

2694.6

2374.2

2432

data size (Kbyte)

157789.2

149398

135804.6

119583.4

119018.2

Total Connections

2105

1983.2

1790

1592.2

1582.4

HDD write (Kbyte/Sec.)

6105

5918

5655.6

5432.54

5473

Postfix

CPU %

28.04

26.34

25.72

27.36

26.04

Memory %

28.6

29.2

29.4

29.6

29.2

ClamAV

CPU %

36.8

38.4

36.6

36.8

36.4

Memory %

1.96

2.16

2.3

2.36

1.94

GLOBAL

CPU %

65.4

65.6

63.2

64.2

63.2

Memory %

34.4

35.2

36

36.2

35.6



Postfix CPU % - нагрузка процессора от всех процессов Postfix

Postfix Memmory % - нагрузка памяти от всех процессов Postfix

Clamav CPU % - нагрузка процессора от антивируса ClamAV

Clamav Memmory % - нагрузка памяти от антивируса ClamAV

Global – глобальная зона в данном случае суммарная нагрузка всей системы в целом.


Результат тестов как видно из тестов таблицы 3.8.1, на 1 минуте сервер очень хорошо справляется с потоком присланных писем, на второй минуте производительность обработки присланных писем начинает падать до 4 минуту на пятой ситуация нормализуется. Как видно из результатов, перегрузка ресурсов системы не происходит, так как нагрузка на процессор не превышает 70%, а использованная оперативная память занята не более чем на 40%. Виду этого узким для производительности почтовой системы, но защищенным от атак является qmgr сервис доставки почты. Сервис по умолчанию настроен так, чтобы в очереди active хранилось малое количество почтовых сообщений для уменьшения вероятности лавинного скачка нагрузки как на CPU, так и на оперативную память. Так же нельзя допустить, чтоб у оперативной системы не осталось свободных ресурсов, обязательно необходимо иметь запас ресурсов. Нагрузка на сервер баз данных MySQL остается не изменой на всем этапе тестирования и равна около 2% от памяти и 2% от CPU по этой причине сервер баз данных MySQL не присутствует в таблице тестов. Параметр Global в данном тесте показывает загрузку всей системы в целом

 

4. ВИРТУАЛИЗАЦИЯ Зон и Контейнеров (Solaris Containers)


4.1 Описание технологии:


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

Технология – Sun Dynamic System Domains, доступная на серверах среднего и высшего уровней, уже много лет как эффективно используется при организации современных высоко-доступных информационных систем. Суть ее заключается в разделении аппаратных ресурсов SPARC-серверов на несколько независимых подсистем, каждая из которых, содержит собственную версию операционной системы.

К сожалению, технология организации аппаратных динамических доменов жестко привязана к линейке серверов Sun Fire midrange- и high-end- уровней и недоступна на серверах начального уровня и системах на платформе х86.

Здесь на «помощь» приходит инновационная технология – Solaris Containers, которая является одной из наиболее значимых возможностей новой версии операционной системы – Solaris 10.

Имея обобщенное название – N1 Grid Containers, данная технология состоит из двух компонентов: контейнеров (Solaris Containers) и зон (Solaris Zones).

Технология контейнеров позволяет полностью изолировать работу процессов, запущенных в системе, и динамически распределять аппаратные ресурсы системы для их эффективной работы. Процессы запускаются и функционируют в изолированном адресном пространстве и не имеют прямого доступа друг к другу. Этот факт позволяет добиться максимальной степени безопасности.

Использование зон дает возможность создавать на одном сервере до 8192 виртуальных операционных систем - полностью изолированных, со своим пространством памяти, файловой системой и запущенными процессами. Пользователи в разных зонах могут отличаться друг от друга, и суперпользователь root из одной зоны может не иметь привилегий в другой зоне. Фактически это набор независимых виртуальных серверов Solaris 10, запущенных на одном аппаратном сервере.

Причем, важно отметить, данная технология не зависит от используемой аппаратной платформы и может применяться на всей линейке SPARC-серверов, 64-разрядных системах AMD Opteron и на платформе х86.

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

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

4.2 Технолошия зон

Технология зон внесла серьезные изменения в организационную структуру операционной системы Solaris 10. Если ранее установленная система Solaris единолично использовала все аппаратные ресурсы, то с применением технологии виртуализации ситуация кардинально изменилась.

Во-первых, аппаратные ресурсы теперь тоже представлены в виде неких виртуальных устройств, которые распределяются между существующими зонами, а также, и между самими процессами, разделенными механизмами контейнеров. В качестве примера можно посмотреть информацию об имеющихся в системе процессорах – они «виртуальные»:


# psrinfo -v

Status of virtual processor 0 as of: 05/31/2005 19:48:08

on-line since 05/23/2005 03:30:18.

The i386 processor operates at 2400 MHz,

and has an i387 compatible floating point processor.

Во-вторых, операционная система Solaris теперь разделена на два типа зон: глобальная зона (global zone) и не-глобальные зоны (non-global zone).

Глобальная зона – это именно та копия системы, которую вы устанавливаете непосредственно с дистрибутива на ваш сервер. Эта зона создается автоматически при инсталляции и неизменно присутствует в вашей системе. Фактически она несет на себе две функциональные обязанности:

  • Общесистемное администрирование

  • Является базовой зоной для создаваемых non-global зон

Только эта зона может быть загружена с аппаратных ресурсов системы, и все вопросы конфигурирования аппаратных устройств (добавление, динамическая реконфигурация и т.д.), настройка таблиц маршрутизации, а также, создание и управление non-global зонами – доступны только с нее.

Глобальная зона всегда имеет идентификатор ID=0 и имя global .

Non-global зоны – являются виртуальными копиями вашей системы. Создаются они только с глобальной зоны и унаследуют от нее ряд параметров и установленных продуктов. Каждая такая зона функционирует самостоятельно со своими сетевыми адресами, пользователями, сервисами и т.д. Все процессы, протекающие в любой из зон, полностью изолированы и от глобальной зоны и от остальных non-global зон. Зоны могут взаимодействовать между собой только через сетевые сервисы.

В каждой такой зоне можно устанавливать свои программные продукты, запускать необходимые сервисы и обслуживать своих пользователей.

Функциональные возможности non-global зон довольно широки:

  • Безопасность. Поскольку зоны полностью изолированы, то в случае «взлома» в одной из зон, злоумышленник не сможет получить доступа ко всем ресурсам системы, ограничившись лишь областью поврежденной им зоны.

  • Изоляция. Полная изоляция процессов и пользователей между зонами позволяет одновременно на одном сервере запускать несколько копий какого-нибудь сервиса для обработки запросов из разных доменов.

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

  • Универсальность. Используя ресурсы глобальной зоны, максимально упрощаются процедуры администрирования как системы в целом, так и отдельных зон в частности. Установка патчей и других программных продуктов в глобальной зоне, автоматически распространяется на все non-global зоны.

Создание, изменение, удаление и управление зонами осуществляет «глобальный» администратор, а вот администрирование внутри non-global зоны доступны только «локальному» администратору конкретной зоны.

Таблица 4.2.1

Таблица функциональных особенностей зон.



Глобальная зона (global zone)

Не глобальная зона (non-global zone)

ID всегда равен 0

ID назначается системой когда зона загружается

Одна копия Solaris ядра, с которой система загружается и функционирует

Не содержит собственного ядра, использует компоненты ядра глобальной зоны

Содержит полную инсталляцию системы Solaris и других продуктов

Может содержать только необходимую часть системы Solaris, взятую из глобальной зоны

Использует установленные в глобальной зоне продукты

Может содержать дополнительные программные продукты

Может содержать дополнительные программные продукты, даже не установленные в глобальной зоне

Содержит конфигурационную информацию для глобальной зоны

Содержит настройки только для данной зоны

Полный контроль над всеми устройствами и файловыми системами

Возможность только пользоваться предоставленными устройствами

Содержит информацию и параметры non-global зон

Не содержит информации о других зонах

Создание, изменение, удаление и управление non-global зонами

Не может создавать, изменять, удалять и управлять зонами, даже собственной


Любая non-global зона может находится в одном из шести функциональных состояний:

  • Configured – это состояние означает, что установлены все необходимые параметры для данной зоны.

  • Incomplete – это состояние устанавливается до тех пор, пока не будет корректно завершена процедура инсталляции (или удаления) зоны.

  • Installed – зона корректно инсталлирована в системе, необходимые продукты установлены в ее корневой путь. Виртуальная система в этом состоянии только установлена – не «запущена».

  • Ready – в этом состоянии «запускается» виртуальная система: ядро запускает необходимые процессы, «поднимаются» виртуальные сетевые интерфейсы, монтируются файловые системы и конфигурируются выделенные устройства. На этом этапе зоне назначается уникальный ID. Пользовательские процессы в зоне пока не запущены.

  • Running – в это состояние зона переходит, когда в ней запустится первый пользовательский процесс (init)

  • Shutting down или Down – состояние остановки функционирования данной зоны

Перевод зоны в перечисленные выше состояния осуществляются «глобальным» администратором с помощью команд администрирования виртуальных зон.

Как было сказано раньше, компонент зон – главная составляющая технологии N1 Grid Containers. Смысл данного компонента заключается в следующем: в изоляции некой виртуальной области операционной системы в отдельную зону с ограниченными правами. В этой области, так же как и в глобальной зоне, работают программы, сервисы и контролируются как из глобальной зоны, так и из самой не глобальной зоны. Ограничения накладываются при создании не глобальной зоны и могут ограничивать такие вещи как:

  • ограниченный доступ к аппаратным устройствам сервера;

  • ограниченный доступ к файловой системе;

  • ограниченный доступ к программам, установленных в операционной системе и доступных только в глобальной зоне;

  • ограниченный доступ к ядру операционной системы.

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

  • Virtual Private server – операционная система Linux.

  • Jail, Chroot - операционная система FreeBSD, OpenBSD, NetBSD, Linux.


4.2.1 Конфигурирования не глобальной зоны


1 Шаг создание не глобальной зоны


1) zonecfg -z z_postfix


2)
zonecfg:z_postfix> create

3)
zonecfg:z_postfix> set zonepath=/chroot/ZONI/postfix

4)
zonecfg:z_postfix> set autoboot=true

5) zonecfg:z_postfix> add fs
zonecfg:z_postfix:fs> set dir=/usr/local/etc/postfix/sql
zonecfg:z_postfix:fs> set special=/usr/local/etc/postfix/sql.non
zonecfg:z_postfix:fs> set type=lofs
zonecfg:z_postfix:fs> end

zonecfg:z_postfix> add fs
zonecfg:z_postfix:fs> set dir=/usr/local/var
zonecfg:z_postfix:fs> set special=/chroot/ZONI/var
zonecfg:z_postfix:fs> set type=lofs
zonecfg:z_postfix:fs> end


6)
zonecfg:z_postfix> add net
zonecfg:z_postfix:net> set address=195.195.195.20

zonecfg:z_postfix:net> set physical=elxl0
zonecfg:z_postfix:net> end

7)
zonecfg:z_postfix> add device

zonecfg:z_postfix:device> set match=/dev/pts*
zonecfg:z_postfix:device> end


zonecfg:z_postfix> add device

zonecfg:z_postfix:device> set match=/dev/tcp
zonecfg:z_postfix:device> end

zonecfg:z_postfix> add device

zonecfg:z_postfix:device> set match=/dev/ip
zonecfg:z_postfix:device> end


8)
zonecfg:z_postfix> add attr
zonecfg:z_postfix:attr> set name=Postfix
zonecfg:z_postfix:attr> set type=string
zonecfg:z_postfix:attr> set value=”test_zone1”
zonecfg:z_postfix:attr> end

9) zonecfg:z_postfix> verify

10)
zonecfg:z_postfix> commit

11)
zonecfg:z_postfix> exit

Описание:

1) Запускаем команду zonecfg с указанием имени нашей будущей зоны. Поскольку зона еще не существует, рекомендуется ее создать.

2) Создаем зону.

3) Указываем, где будем размещать зону.

4) Зона должна автоматически загружаться, когда загружается глобальная зона.

5) Задаем дополнительно монтируемые в виртуальной зоне файловые системы в режиме запись, чтение. В данном примере указывается, что каталог /usr/local/etc/postfix/sql.non из глобальной зоны будет смонтирован виртуальной зоне z_postfix в каталог /usr/local/etc/postfix/sql. Так как сервисы из не глобальной зоны могут общаться с сервисами в глобальной зоне только через сетевые сервисы, а для работы сервера Postfix необходима связь с сервером баз данных MySQL сервером, тогда в директории /usr/local/etc/postfix/sql.non содержится связь не через 127.0.0.1, как это было раньше, а через 195.195.195.10(IP адрес глобальной зоны). У каждой зоны своя таблица маршрутизации и своя локальная сетевая петля, не доступная между сервисами разных зон (рисунок 3.3.1). Так же монтируется в режиме чтения, запись в рабочую директорию с очередями для сервера Postfix из глобальной зоны /chroot/ZONI/var в не глобальную зону /usr/local/var. Это необходимо потому, что директория из глобальной зоны /usr монтируется в не глобальную зону по умолчанию с правами только чтения. По этой причине необходимо это действие. В дополнение можно отметить, что, так как используется защита модулей сервера Postfix, в chroot это требует наличие копии таких файлов (именно той зоны, в которой он работает) как:

  1. /etc/resolv.conf содержащий адреса DNS серверов.

  2. /etc/hosts содержащий хост имена и IP-адреса.

  3. /etc/services содержащий сервисы соотношения сервиса к сетевому порту.

Если не перемонтировали директорию из глобальной зоны в не глобальную в режиме запись, чтение, то сервер Postfix не смог бы складывать почту в такие директории как incoming, hold, defered, active, а так же не смогли бы работать модули сервера Postfix в режиме chroot, так как в не существовала файлов resolv.conf, hosts.

6) Устанавливаем сетевые устройства. Указываем для них сетевой адрес и «реальный» сетевой интерфейс, на который будет конструироваться виртуальная не глобальная зона. Для каждой виртуальной не глобальной зоны должен быть свой уникальный адрес. Для зоны z_postfix устанавливаем IP адрес 195.195.195.20.

7) Добавляем доступ к устройствам. Добавляем все имеющиеся псевдо-терминальные устройства. По умолчанию виртуальной не глобальной зоне предоставляется минимальный набор доступных устройств и необходимо вручную добавить доступ к тем устройствам, которые необходимы процессам в виртуальной зоне. Доступ к устройствам предоставляется ограниченным – нет возможности не удалить устройство, не переконфигурировать его.

8) Задаем комментарии для данной зоны.

9) Проверяем правильность установленных параметров.

10) Сохраняем внесенные конфигурационные параметры зоны.

11) Выходим из интерактивного режима конфигурирования виртуальной зоны.

Из глобальной зоны по умолчанию при создании не глобальной зоны монтируются следующие каталоги в режиме только чтение /usr,/lib,/platform,/sbin.

И если изобразить графически наша зона будет выглядеть следующим образом (Рисунок 4.2.2).


Рисунок 4.2.2

Топология сети с учётом виртуальной машины


Рисунок 4.2.3

Составная схема глобальной и не глобальной зоны по дереву файловой системы:


Если же рассматривать внутри сервера составную схему глобальной и не глобальной зоны по дереву файловой системы (Рисунок 4.2.3).

Так как не глобальная зона z_postfix достижима из глобальной по этой причине, она входит в состав глобальной зоны, но в свою очередь глобальная зона не входит в состав не глобальной z_postfix. Пунктирными линиями серого цвета соединены директории глобальной зоны, монтируемые в директории не глобальной зоны z_postfix с правами только чтение, зелёной пунктирной линией соединены директории глобальной зоны, монтируемые в не глобальную зону z_postfix с правами чтение, запись.


4.2.2 Сборка зоны


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

# zoneadm -z z_postfix install

Preparing to install zone <z_postfix>.

Creating list of files to copy from the global zone.

Copying <3368> files to the zone.

Initializing zone product registry.

Determining zone package initialization order.

Preparing to initialize <839> packages on the zone.

Initialized <839> packages on zone.

Successfully initialized zone <z_postfix>.


4.2.3 Запуск зоны


Для запуска зоны необходимо вести команду:


# zoneadm - z zone1 boot


Так как при конфигурации зоны мы ввели параметр “set autoboot=true”, что означает, что не глобальная зона z_postfix будет грузится автоматически с загрузкой глобальной зоны, то есть каждый раз при загрузки самой операционной системы Solaris 10 необходимости вводить каждый раз команду для загрузки не глобальной зоны z_postfix нет необходимости.


Теперь необходимо зайти и отконфигурировать не глобальную зону, так же как и глобальную зону в пункте 10. По умолчанию при первой загрузки не глобальной зоны z_postfix будет грузится множество сервисов и программ, которые нам не нужны. Для входа в зону вводим команду:


#zlogin -C -e\@ z_postfix


Система попросит вести требуемые параметры для не глобальной зоны как и при установки самой операционной системы:

  1. Язык и используемую локальную кодировку символов.

  2. Тип терминала (VT100,XTERM,DTTERM,....).

  3. Хост имя зоны в месте с доменом.

  4. Адрес DNS сервера.

Да кстати можно сразу изменить конфиг SSH сервера чтоб root или кто нибудь другой имел доступ по протоколу ssh к зоне z_postfix для удобства.

Перед запуском Postfixa надо изменить IP адресс обращения к базе MySQL в файлах находящихся в директории “sql” на IP адрес 195.195.195.10 и также открыть дырку в файрволе с IP 195.195.195.20 на 195.195.195.10 порт 3306 если файрвол конечно установлен на этом же компьютере.

Теперь можно запустить сервер Postfix и антивирус ClamAV внутри не глобальной зоны z_postfix.

Для того чтобы сервер Postfix и антивирус ClamAV загружались в месте с загрузкой не глобальной зоны z_postfix, можно воспользоваться системой SMF внутри самой не глобальной зоны z_postfix, она будет абсолютно не зависима от системы SMF в глобальной зоне также, как и любая программа или сервис, запущенный в не глобальной зоне z_postfix.Да кстати вычищать зону z_postfix так же как и глобальную необходимо так как не глобальные зоны наследуют все то что было в первоначальном варианте глобальной зоны то есть в первый за груз Солярки.


4.3 Тестирование почтового сервера Postfix в не глобальной зоне z_postfix.


Задача протестировать сервер Postfix и антивирус ClamAV в не глобальной зоне z_postfix и определить затраты аппаратных ресурсов при работе сервера Postfix и антивируса ClamAV в не глобальной зоне z_postfix. Цель эффективности работы не глобальной зоны. Тест будет проходить по одинаковой также как и в разделе 3.8.

Таблица 4.3.1

Таблица производительности системы.


Minutе

1

2

3

4

5

Messages

3092

2919.5

2753.25

2552.5

2440

data size

156032.5

146663.5

138050.5

128103.5

124461

Total Connections

2081

1946

1839.25

1698

1622

M(write)/sec-M(read)/sec

6056.5

5557.75

5785.25

5655.5

5391.5

Postfix

CPU

31.375

29.925

30.575

31.875

32.5

Memory

22.625

22.975

23.075

23.2

23.925

ClamAV

CPU

35.75

34

36.25

34.5

32.5

Memory

2.225

1.875

2.175

2.1

2.25

GLOBAL

CPU

1.775

1.7

1.75

1.725

1.65

Memory

14

14

14

14

14

z_postfix

CPU

67.5

63.75

67

67.25

65

Memory

24.75

25.5

25.25

26

26

Global + z_postfix

CPU

69.275

65.45

68.75

68.975

66.65

Memory

38.75

39.5

39.25

40

40


Если проанализировать таблицу 3.8.1 и таблицу 4.3.1 при работе в глобальной зоне сервер Postfix расходует меньше на 2%-5% процессорного времени, но при этом в глобальной зоне сервер Postfix расходует больше оперативной памяти на 2%-5%. Затраты ресурсов на антивирус ClamAV остается не измененным как для глобальной зоны, так и для не глобальной зоны.

Сравнение затрат ресурсов на работу только глобальной зоны (Таблица 3.8.1) и суммы не глобальной и глобальной зоны (Таблица 4.3.1) показывает, что в случае с двумя работающими зонами затраты на процессорное время выше 3%-4%, затраты оперативной памяти выше на 4%. Также надо учитывать что в не глобальной зоне для удобства, работал такой сервис как SSH который требует 1%-3%, как процессорного времени так и оперативной памяти. Виду выше перечисленного можно сделать вывод что общие затраты на работу дополнительно одной не глобальной зоны с работающими сервисами и программами не превышает 2% как памяти так и процессорного времени.


4.4 Технология Контейнеров (Ресурс Менеджер).

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



Использование технологии управления ресурсами позволяет:

  • Распределять аппаратные ресурсы системы между различными задачами

  • Контролировать эффективность работы процессов и динамически выделять им необходимые ресурсы

  • Вести статистику работы процессов

4.5 Управление ресурсами CPU


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

 

4.5.1 Метод Fair Share Scheduler (FSS)


При распределении ресурсов процессора, в Sun Solaris, по умолчанию используется механизм временного разделения (TS – timesharing scheduling). Согласно этому механизму, система пытается распределить ресурсы процессора приблизительно равными долями между всеми запущенными процессами. С использованием ряда параметров *.max-cpu-time можно устанавливать время использования процессора для необходимых задач и процессов. Но время использования процессора не позволяет четко задать, в процентном отношении, ресурс его использования.

Если стоит задача гарантированного выделения необходимой части вычислительной мощности процессоров, то в этом случае, применяется технология долевого распределения (FSS – Fair Share Scheduler). С помощью механизмов FSS можно выдавать определенное количество частей (shares) использования процессорных ресурсов системы для необходимых вам проектов.

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

Главным отличием данного методы является то, что, если проект, на который выделено определенная часть ресурсов, не активен в данную минуту, то его ресурсы могут использовать другие проекты нуждающейся в этом, то есть динамическое управление ресурсами. Так как мы провели тесты в прошлом, в котором было видно, что около 70% процессорного времени уходит для не глобальной зоны с этой целью мы будем делать разделение процессорного времени на глобальную зону = 15%, на не глобальную зону z_postfix = 85%.


4.5.2 Настройка метода FAIR SHARE SCHEDULER (FSS).

1) dispadmin –d FSS


2) pooladm –e

init 6


3)pooladm -x

pooladm -s

poolcfg -f create pool work-zpool ( string pool.scheduler = "FSS" )

pooladm -c


4)zoneadm –z z_postfix halt

zonecfg –z z_postfix

set pool=work-zpool

add rctl

set name=zone.cpu-shares

add value (priv=privileged,limit=85,action=none)

end

zoneadm –z z_postfix boot


5)prctl –n zone.cpu-shares –v 15 –r –i zone global


Сразу хочу сказать я забыл команду с помощью корой можно посмотреть сколько кому выделено ресурсов CPU, но вы её можете найти по MAN’у


Описание:


1) Переводим всю систему под метод управления FSS по умолчанию.

2) Активизируем контроль над пулами устройств. Перезапускаем систему для вступления в силу новых изменений.

3) Создаем пул ресурсов для не глобальной зоны и прописываем туда метод по умолчанию FSS, сохраняем изменения в файл /etc/pooladm.conf.

4) Останавливаем работу не глобальной зоны z_postfix для внесения изменений в работу. Входим в режим конфигурирования зоны. Устанавливаем пул устройств, созданных специально для не глобальной зоны z_postfix. Устанавливаем переменную зоны zone.cpu-shares и значение 85, что обозначает 85 долей использования ресурса процессора между проектами. Выходим из режима конфигурации. Загружаем зону.

5) По умолчанию для глобальной зоны выделено всего 1 доля использования. По этой причине мы должны повысить это значение до 15. И желательно занести эту команду в загрузочный скрипт /lib/svc/method/svc-zone для того, чтобы после каждой загрузки системы для глобальной зоны выставлялась это значение.


Если пытливым умам захотелось проверить как это работает на практике то ниже преведёный скрипт загружает CPU по самые возможности. Даный скрипт запускаете в глобальной зоне и не в глобальной зоне и из глобальной зоны запускаете команду

“prstat Z” она покажет использованое CPU в процентах между зонами.


#!/usr/bin/perl


print "eating the CPUs\n";


foreach $i (1..16) {

$pid = fork();

last if $pid == 0;

print "created PID $pid\n";

}


while (1) {

$x++;

}

4.6 Разделение ресурсов оперативной памяти


Технология разделение используемого объёма оперативной памяти на момент написания данной работы, описывалась только для отдельных запусков приложений, но не для ограничения оперативной памяти, для какой-нибудь не глобальной или глобальной зоны. Соответственно есть существенный недостаток, так как для ограничения работы приложения или сервиса внутри зоны нельзя ограничить использования оперативной памяти для этой зоны и ограничения будут распространятся на приложения, работающие в этой зоне. Ограничения на оперативную память накладываются только при запуске приложения той зоны, в которой данное приложение будет работать.


4.6.1 Установка ограничений на использования оперативной памяти


1) rcapadm –E


2) projadd –K ‘rcap.max-rss=536870912’ workproj1


3) /usr/bin/newtask –p workproj1 /usr/local/bin/postfix/postfix


Описание:

1) Активизация rcap демона отвечающего за разграничения объёма используемой памяти.

2) Создания системного проекта, в переменой которого мы указываем максимальный размер оперативной памяти (512 мегабайт), которой может оперировать приложение или сервис, запущенный в рамках данного проекта.

3) запуск сервера Postfix в рамках проекта ограниченного максимальным объёмом оперативной памяти.


4.7 Тестирование почтового сервера Postfix в не глобальной зоне z_postfix с разграничением ресурсов.


Так как уже тестировалась производительность работы почтового сервера при работе в не глобальной зоне z_postfix необходимо протестировать еще раз только при этом будут разграничены ресурсы и замерить на сколько сильно будут отличатся результаты от двух первых тестов.

Разграничения использования ресурса процессора по 85% на глобальную зону и 15% для не глобальной зоны, а так же что максимальный объём памяти, используемый сервером Postfix.


Таблица 4.7.1

Тест производительности


Minut е

1

2

3

4

5

Messages

3049.6

2911.6

2634.8

2586.4

2265.2

data size

153565.6

151841

132414.6

130113

114217.4

Total Connection

2045.6

2009.6

1764.8

1717.6

1494.4

M(write)/sec-M(read)/sec

5809.2

5720.7

5727.2

5680.98

5195.96

Postfix

CPU

28.98

32.76

31.86

32.36

32.1

Memory

24.68

25.78

25.2

24.9

25.72

ClamAV

CPU

32.2

30

33.2

29.4

30

Memory

1.94

1.54

1.94

2.22

2.08

GLOBAL

CPU

1.4

1.32

1.24

1.3

1.22

Memory

16.8

16.8

16.8

16.8

16.8

z_postfix

CPU

61.6

63.2

65

60.6

62.8

Memory

26.6

27.6

27.2

26.6

27.4

Global + z_postfix

CPU

63

64.52

66.24

61.9

64.02

Memory

43.4

44.4

44

43.4

44.2


Если сравнить таблицу 4.3.1 и таблицу 4.7.1, то можно заметить что 2%-3% снизилась нагрузка на процессор в таблице 5 по сравнению с таблицей 4, связано это с тем, что метод разделения и управления ресурсами процессора FSS более совершенный, чем метод TS используемый по умолчанию в системе при том улучшение связаны с антивирусом ClamAV. Если сравнивать таблицу 3.8.1 и таблицу 4.7.1, то улучшения показателей процессора отсутствует при сравнительно этих же нагрузках. Если брать Оперативную Память и сравнивать таблицу 3.8.1 и таблицу 4.7.1, видно, что нагрузка на память в таблице 4.7.1 выше от 3%-8%. Остальные показатели остаются приблизительно одинаковыми.


5. Взлом системы


Как видно из решения в Интернете будут доступны два порта 25 почтовый сервер Postfix и 110 pop3 сервер Courier imap. Так как я рассматриваю только почтовый сервер Postfix, то я беру за главную цель взлома только почтовый сервер Postfix. Я хочу рассмотреть два вида атак самых популярных на сегодняшний день:

      1. Проникновение в систему с целью выполнения произвольной команды или же подготовка плацдарма с целью дальнейшего использования системы.

      2. DOS, DDOS атаки с целью вывода сервера из строя и недостижимости его в дальнейшем


1. Проникновение в систему и выполнения произвольной команды.

Допустим ситуацию, что в одном из модулей Postfix’a допустим в модуле smtpd (так как этот модуль работает на TCP порту 25, соответственно он достижим из Интернета) найдена уязвимость, позволяющая выполнить произвольный код. Первая ступень обороны эта chroot оболочка самого сервера Postfix:

  • взломщик попадет в директорию /usr/local/var/spool/postfix, которая будет считаться для него корнем файловой системы “”/”. Соответственно, там не будет находится стандартных программ операционной системы Solaris, которые могли бы дать взломщику выполнять команды (например команды passwd root для смены пароля суперпользователя root) взломщик сможет только использовать права доступа модуля smtpd. Модуль smtpd имеет права доступа “postfix”. Главная проблема на этом уровне защиты для взломщика заключается в том, чтобы перенести в chroot оболочку необходимые файлы для дальнейших действий и выполнять их с правами суперпользователя. В эту директорию можно записывать файлы, так как сюда складывается сообщения модулем qmgr, но они имеют формат Mime соответственно, если взломщик пришлёт нужный ему файл, он будет в формате Mime, и не будет выполняем. Как видно даже на 1 рубеже защиты уже становится понятно, что данный вид атаки не приведет к большим последствиям.

  • Если взломщик обойдет первый уровень защиты, то тогда он сможет выполнять допустимые команды только специальным командным интерпретатором smrsh, который существенно ограничивает выполнение команд и их аргументы.

  • Если первых два уровня защиты будут пройдены, то взломщик получит доступ суперпользователя “root” в системе он будет изолирован областью не глобальной зоны z_postfix. В этом случае он не сможет получить доступ к глобальной зоне операционной системы Solaris, в которой работают такие сервисы как MySQL, SSH соответственно и получить доступ к этим сервисам будет не возможно. То есть взломщик будет изолирован от главной области операционной системы. Так же все события из не глобальной зоны z_postfix будут регистрироваться в syslogd демоне, который через сеть будет выводить регистрацию событий из не глобальной зоны z_postfix в глобальную зону, то есть любое проникновение в систему не останется не замеченным и не будет возможным вычистить журнал событий.

Как видно из фактов, проникновение и выполнение на стороне сервера команд является практически не выполнимой операцией.


2. DOS, DDOS атаки с целью вывода сервера из строя и недостижимости его в дальнейшем.

Этот вид атаки самый сложно решаемый. Главная цель сделать сервер не достижимым путем затраты самим сервером всех ресурсов системы памяти, процессора, сетевых буферов. В операционной системе Solaris был разработан новый высоко производительный сетевой стек, который частично решает проблему атак отказ в обслуживания по крайне мере увеличивает время, необходимое для успешного выполнения данной атаки. Так же учитывая то, что ресурсы системы разделены между зонами решает проблему затраты всех ресурсов системы на почтовый сервер Postfix. В случае, если такая атака будет проведена, то у главной зоны останется резерв ресурсов системы для работы.

 

Выводы


В конечном результате мне удалось решить требуемые задачи:

  1. Удалось достигнуть более лучших результатов, чем 1000000 писем в сутки со среднем размером письма 50 килобайт.

  2. Удалось добиться гибкости системы для администрирования и интегрировать почтовый сервер с MySQL базой данных.

  3. Создать активную систему защиты от вирусов на базе бесплатного программного продукта ClamAV, который не уступает своим коммерческим аналогам.

  4. Сделать пассивную систему защиты от спама и пересылки спам писем через почтовый сервер.

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

  6. Проанализировать эффективность работы виртуальной машины для укрепления защиты сервера.

  7. Создать полнофункциональный рабочий сервер с многоуровневой системой защиты от большинства существующих атак.

  8. Создать хорошую базу для дальнейшего использования данной модели почтового сервера для решения большого круга проблем.

В ходе проделанной работы я доказал, что операционная система Solaris может служить не только как платформа для высоко производительных баз данных, но и высоко производительный почтовый сервер с многоуровневой системой безопасности по технологии виртуальных машин. Считаю, что создать данное решение на аналогичных платформах (Linux, FreeBSD, HP-UX) было бы на много сложнее или даже невозможно.

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

Список использованной литературы и других информационных источников


Дж.Уинзор, SOLARIS. Руководство системного администратора. 3-е издание – Москва: Издание ПИТЕР,2003 – 443 стр.


Технология виртуализации Solaris Containers – Resource Management and Solaris Zones //http://docs.sun.com/app/docs/doc/817-1592, (5.10.2005)

Internet Protocol Suite Tunable Parameters

http://docs.sun.com/app/docs/doc/817-0404/6mg74vsad?a=view, (5.15.2005)


System Administration Guide: Devices and File Systems

// http://docs.sun.com/app/docs/doc/817-5093 , (5.15.2005)


System Administration Guide: Basic Administration

//http://docs.sun.com/app/docs/doc/817-1985, (5.15.2005)


System Administration Guide: Advanced Administration

//http://docs.sun.com/app/docs/doc/817-0403, (5.15.2005)


Postfix Documentation //http://www.postfix.org/documentation.html (5.15.2005)


MySQL Documentation //http://dev.mysql.com/doc/ (5.15.2005)


ClamAV Documentation //http://www.clamav.net/doc/0.85 (5.15.2005)


Simple Authentication and Security Layer (SASL) //http://asg.web.cmu.edu/sasl/sasl-ietf-docs.html (5.15.2005)


Комментарии: (0) | Безопасность | 2006-06-02

Перевод руководства по PHP/FI 2.0

Перевод руководства по PHP/FI 2.0

Перевод выполнен Юрием Плетневым, все замечания, предложения по качеству перевода направляйте ему :-) Для некоторых терминов ни стилусу, ни автору не удалось подобрать эквивалента, такие термины оставлялись либо в английском варианте, либо транслитерировались. По поводу перевода автор цитирует комментарий к оригинальному документу:

" Документация - как секс: если она хороша, то это очень, очень хорошо; а если она плоха, то это лучше чем ничего. "

                                       Дик Брэндон


Оглавление

  1. Краткая Хронология
  2. Инструкции по установке
  3. Итак, что я могу сделать с помощью PHP/FI?
  4. Переадресация CGI
  5. Проблемы безопасности
  6. Установление подлинности HTTP
  7. Замечания по модулю Apache
  8. Директивы конфигурации модуля Apache
  9. Поддержка FastCGI
  10. Управление доступом
  11. Регистрация Доступа
  12. Относительный URL против Абсолютного URL - или, Почему не выводятся мои имиджи?
  13. Как PHP/FI обрабатывают данные методов GET и PUT
  14. GD (графическая библиотека для создания GIF) Поддержка в PHP
  15. PHP/FI и виртуальные хосты
  16. Поддержка закачки файлов
  17. Поддержка Cookie
  18. Поддержка mSQL
  19. Поддержка Postgres95
  20. Регулярные выражения
  21. Скрипт язык PHP/FI
  22. Добавление ваших собственных функций PHP/FI
  23. Примечания к хакания кода

Краткая Хронология

PHP начал жизнь как простая небольшая cgi оболочка, написанная на Perl. Я написал это в полдне в течение периода между контрактами, когда я нуждался в быстром инструменте, чтобы понять того, кто читал мое интерактивное резюме. Никогда не было предназначено идти вне моего собственного частного использования. Станция сети, где я имел мое резюме, была чрезвычайно перегружена и имела постоянные проблемы с "форканьем" процессов. Я переписал Perl оболочку на C, чтобы избавиться от значительных непроизводительных затрат из-за необходимости запуска Perl, при каждом обращении к моему резюме.

В конечном счете другие люди на том же самом web сервере натолкнулись на мою оболочку и спросили, могут ли они использовать её. Затем, как неизбежно случается, они начали просить о большем количестве особенностей. Я добавил большее количество особенностей и в заключение собрал наполовину дистрибуцию наряду с документацией, списком адресатов и FAQ. Имя этого первого пакета было Персональные Инструментальные средства для домашней страницы (PHP), которые позже стали Персональным комплектом создания домашней страницы.

В то же самое время я начал играть с базами данных и написал инструмент, чтобы легко включить запросы SQL в web страницы. Это было в основном другая оболочка CGI, которая анализировала запросы SQL и облегчала, создание форм и таблиц, основанных на этих запросах. Этот инструмент был именован Интерпретатор Форм (Form Interpreter ).

PHP/FI версии 2.0 - полная перезапись из этих двух пакетов, объединенных в одиночную программу. Это теперь развилось по сути в простой язык программирования, внедренный внутри HTML файлов. Первоначальный акроним, PHP, прижился. Он теперь не соответствует действительности. PHP/FI используется больше для создания целых web серверов сегодня чем для малых домашних страниц. Под любым именем, это устраняет потребность в многочисленных малых cgi программах на Perl, позволяя Вам поместить простые скрипт программы непосредственно в ваши HTML файлы. Это увеличивает общую производительность ваших web страниц, хотя бы потому что нет непроизводительных затрат на запуск Perl-а. Пакет также упрощает управление большими web серверами, помещая все компоненты web страницы в одиночном файле html. Включением поддержки для различных баз данных, пакет также делает тривиальным разработку web страниц с доступом к базам данных. Многие людей находят, что с внедренным характер намного проще иметь дело чем пытаться создать отдельные HTML и CGI файлы . В этой документации любые ссылки PHP, FI или PHP/FI все относятся к одному и тому же. Различие между PHP и FI - только концептуальное. И PHP и FI созданы из одного и того же исходного текста. Когда я формирую пакет без любой регистрации доступа или поддержки ограничения доступа, я вызываю мой выполняемый модуль FI. Когда я формирую с этими опциями, я называю его PHP.


Инструкции по установке

Прежде, чем Вы начнете

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

Что вам необходимо знать перед установкой

- Можете ли Вы выполнять оба метода GET и POST из программы cgi на вашем сервере?
Это не важно если Вы, устанавливаете пакет в виде модуля Apache. Если нет, Вы не можете использовать этот пакет. У многих ISP CGI скрипты также запрещены или строго ограничены. Если на вашей системе дело обстоит именно так, поговорите с вашим системным администратором, и попросите, чтобы он / она взглянул на этот пакет, и посмотрите, установят ли они его для Вас.

- Если на вашей системе установлен mSQL, то вам нужно знать основной каталог этой установки.

- Если на вашей системе установлен Postgres95, то вам нужно знать основной каталог этой установки.

- Если Вы собираетесь хранить лог-файл и файлы конфигурации доступа в каталоге смонтированном по NFS и ваша система не обеспечивают блокирование файлов по NFS, Вам нужно будет определить NFS_HACK переменную вручную в файле src/Makefile, и возможно предется использовать немного измененную версию библиотеки gdbm. См. файл nfs_hack.txt в каталоге doc для более подробной информации относительно этого.

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

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

Шаги Установки
Шаг 1.

Выполните программу установки: ./install

Вам будет задан ряд вопросов. Если Вы их не понимаете те, то просто нажимайте "Return". Заданный по умолчанию выбор должен удовлетворять требования для большинства систем. Это однако не имеет отношения к вопросам, определяющим каталог для ваших конфигурационных и лог файлов. Выберите любой каталог, к которому httpd (обычно пользователь "nobody") имеет доступ по записи. Вы можете создать этот каталог вручную где-нибудь и просто сменить владельца с помощью команды chown nobody catalog .

Шаг 2.

Войдите в каталог src: cd src

Шаг 3.

Введите команду: make

По умолчанию будет создан фактический выполнимый программный файл, именованный php.cgi , или если Вы устанавливаете пакет в виде модуля Apache, будет создан libphp.a файл.

Шаг 4. (Если Вы не устанавливаете пакет в виде модуля Apache)

Скoпируйте файл php.cgi в каталог cgi-bin вашей системы. Если у вас нет прав сделать это и вы желаете установить пакет в ваш собственный персональный каталог, вы можете сделать это, но в таком случае нужно установить setuid бит для выполняемой программы командой: chmod u+s /path/php.cgi

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

Шаг 4. (Если Вы устанавливаете программу в виде модуля Apache)

Перейдите в каталог src Apache, в который должны быть скопированы файлы mod_php.c и mod_php.h. Если они еще не были скопированы, что может произойти из-за проблем с правами доступа, скопируйте эти два файла вручную. Подредактируйте ваш файл Конфигурации Apache(Configuration), и добавьте в строку EXTRA_LIBS libphp.a, которая была создана в конце Шага 3. А также добавьте строку:

Module php_module mod_php.o

в самом конце файла. Затем введите: ./Configure и затем make , чтобы перекомпилировать ваш файл httpd Apache. Инсталлируйте этот файл.

Затем Вам нужно подредактировать ваш Apache conf/srm.conf файл и добавить строку :

AddType application/x-httpd-php .phtml

Это определяет новый MIME тип, application/x-httpd-php, который будет вызывать модуль PHP, для обработки любого файла с расширением .phtml . Вы можете выбрать любое расширение, которое вам по душе.

Вы можете захотеть чтобы не все могли выполнить PHP файлы. Для этого вы можете поместить вышеупомянутую AddType строку внутри <Location/path > .... < /Location > директивы в access.conf файле, чтобы только в определенных директориях вашего сервера PHP мог обрабатывать документы.

Теперь Вы готовы перезапустить ваш httpd сервер. Для более подробной информации см. примечания по конфигурированию модуля Apache.

Тестирование программного обеспечения

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

http://your.site.domain/cgi-bin/php.cgi

При этом должна показаться страница, которая содержит номер версии наряду с другой полезной информацией.

Чтобы проверить работу модуля Apache , создайте любой файл с .phtml расширением, поместите в него тэг подобно: <?phpinfo () > и посмотрите будет ли он анализироваться.

Использование программного обеспечения
Чтобы фактически использовать программное обеспечение на существующем HTML файле, Вы можете просто добавить путь к вашему файлу вышеупомянутому URL. То есть.

http://your.site.domain/cgi-bin/php.cgi/path/file.html

Вам нужно взглянуть на раздел Переадресация CGI этой документации. При выполнении PHP/FI с переадресацией вы можете автоматически задавать чтобы URL подобно http:/your.site.domain/file.phtml мог анализироваться PHP/FI.

Это не относится к пользователям программы, выполненной в виде модуля Apache.


Итак, что же я могу делать с PHP/FI?

Во первых обратите внимание, если страница обрабатывается PHP/FI - то, добавляется нижний колонтитул с информацией относительно количества обращений к вашей странице ( в том случае если Вы скомпилировали программу с опцией регистрации доступа). Это - всего лишь малая часть того что PHP/FI может сделать для Вас. Пргорамма (модуль) играет другую очень важную роль - интерпретатор форм cgi (часть имени FI). Например, если Вы создаете форму на одной из ваших web страниц, то вам нужно что-нибудь для обработатки информации связанной с этой формой. Даже если вы хотите только передать информацию в другую web страницу, вам нужно будет иметь программу cgi, которая это сделает. PHP/FI делает чрезвычайно простым делом получение данных из форм и производить их обработку.

Простой пример

Предположим, что у вас есть форма:

<FORM ACTION="/cgi-bin/php.cgi/~userid/display.html" METHOD=POST>
<INPUT TYPE="text" name="name">
<INPUT TYPE="text" name="age"> <INPUT TYPE="submit">
</FORM>

Ваш display.html файл мог бы в этом случае содержать что - нибудь вроде:

<?echo "Hi $name, you are $age years old!<p>">

Это так просто! PHP/FI автоматически создает переменную для каждого поля ввода в вашей форме. Вы можете впоследствии использовать эти переменные в ACTION URL файле.

Следующий шаг, если только Вы определили, как использовать переменные, это начать играться с некоторыми тэгами, определяющими логический ход выполнения в ваших страницах. Например, если Вы хотели отобразить различные сообщения, основанные на том что вводит пользователь, Вы используете if/else конструкцию. В нашем примере выше, мы можем отображать различные сообщения, основанные на возрасте, который ввел пользователь, изменив наш display.html:

<?
if($age>50);
echo "Hi $name, you are ancient!<p>"; elseif($age>30);
echo "Hi $name, you are very old!<p>"; else;
echo "Hi $name."; endif;
>

PHP/FI обеспечивает очень мощный язык, который будет делать намного больше чем то, что показывает этот простой пример . См. раздел по Script язык PHP/FI для подробной информации.

Вы можете также использовать PHP/FI, чтобы конфигурировать, кому разрешено обращаться к вашим страницам. Это может быть выполнено, с использованием встроенного экрана конфигурации. В этом случае Вы могли бы например определять, что только людям из некоторых доменов позволено обращаться к вашим страницам, или Вы могли бы создать правило, которое будет защищать некоторые страницы с помощью пароля. См. раздел Управления доступом для более подробной информации.

PHP/FI также имеет возможность принимать файл, загружаемый из любого, отвечающего требованиям RFC-1867, web броузера. Эта возможность позволяет передавать как текстовые так и двоичные файлы. С управлением доступом PHP/FI и его логическими функциями, у вас есть полный контроль над тем, кому позволено передавать файлы и что с этим файлом должно быть сделано , если он передан. См., что раздел Передача Файлов.

PHP/FI имеет поддержку для пакета базы данных называемого mSQL. Это позволяет Вам помещать информацию в базу данных или обращаться за этой информацией через простые встроенные прямо в ваши .HTML файлы SQL запросы. Обработка базы данных через web страницу никогда не была проще. См. раздел по Поддержке mSQL.

PHP/FI также имеет поддержку для пакета базы данных Postgres95. Это поддерживает встроенные SQL запросы в ваших .HTML файлах. См. раздел по Поддержке Postgres95 для подробной информации.


Переадресация CGI

Apache

Хороший способ выполнять PHP/FI - используя модуль переадресации cgi с http сервером Apache. Пожалуйста обратите внимание, что вам не нужно волноваться относительно модулей переадресации, если PHP/FI используется в виде модуля Apache. Существует два таких модуля переадресации. Один из них разработан Дэйвом Андерсеном <angio@aros.net>, и доступен по ftp://ftp.aros.net/pub/util/apache/mod_cgi_redirect.c, и другой идет вместе с исходными текстами Apache и называется mod_actions.c. Эти модули очень похожи. Есть лишь небольшая разница в их использовании. Оба были протестированы и оба работают с PHP/FI.

Одно большое "но" на момент написания документа (апрель. 20/96) - то, что текущий официальный выпуск Apache (1.0.5) имеет серьезное ограничение, которое препятствует получению данных в переадресованных cgi запросах метода POST. Я разрешил эту проблему и устранил этот недостаток в моей версии Apache, есть также официальный патч, доступный в файловом архиве на домашней странице PHP.

Вторая довольно большая проблема с Apache, 1.0.x - то, что он не выравнивает тип double правильно на большинстве архитектур. Вы получаете странные ошибки типа BUSERROR от вашего httpd при использовании mod_php, для решения этой проблемы нужно или обновление Apache до 1.1 или подредактировать файл alloc.c Apache. В этом файле найдите следующий фрагмент кода:

union align {
/* Types which are likely to have the longest RELEVANT alignment 
*  restrictions... we don't do much with doubles.
*/

char *cp; void (*f)();
long l;
FILE *fp;
};

Нужно добавить double к этой строке и перетранслировать вашу Apache сервер. Правильный блок кода:

union align {
/* Types which are likely to have the longest RELEVANT alignment 
*  restrictions... we don't do much with doubles.
*/

char *cp; void (*f)();
long l;
FILE *fp; double d;
};

Проверьте документацию к Apache по тому, как добавлять модуль. Вообще Вы добавляете имя модуля к файлу называемому Configuration. Если вы хотите использовать модуль mod_action, то вам нужно добавить следующую строку:

Module action_module mod_actions.o

Если хотите использовать модуль mod_cgi_redirect.c, добавьте строку:

Module cgi_redirect_module mod_cgi_redirect.o

Затем скомпилируйте ваш httpd, и инсталлируйте его. Чтобы разрешить cgi переадресацию, нужно или создать новый MIME тип, в файле mime.types , либо можно использовать команду AddType в вашем srm.conf файле, чтобы добавить тип MIME. Тип MIME, который будет добавлен должен быть что - нибудь вроде этого:

application/x-httpd-php phtml

Если Вы используете модуль mod_actions.c, Вам нужно добавить следующую строку к вашему файлу srm.conf:

Action application/x-httpd-php /cgi-bin/php.cgi

Если Вы используете mod_cgi_redirect.c, нужно добавить эту строку к srm.conf:

CgiRedirect application/x-httpd-php /cgi-bin/php.cgi

Не пробуйте одновременно использовать, и mod_actions.c и mod_cgi_redirect.c.

Если только Вы имеете один из этих cgi модулей переадресации, установленных и правильно сконфигурированных , Вы захотите чтобы файл анализировался php/fi просто дав ему расширение файла .phtml. Кроме того, если Вы добавляете index.phtml к вашей строке конфигурации DirectoryIndex в файле srm.conf, то верхне-уровневая страница в каталоге будет автоматически анализироваться php, в том случае если ваш индексный файл называется index.phtml.

Netscape HTTPD

Вы можете автоматически переадресовывать запросы к файлам с данным расширением, которые будут обрабатываться PHP/FI, с использованием модуля переадресации CGI сервера Netscape. Этот модуль доступен в файловом архиве на домашней странице PHP/FI . Файл README в пакете подробно объясняет, как конфигурировать модуль для использования с PHP/FI.

NCSA HTTPD

NCSA в настоящее время не поддерживает модули, таким образом чтобы использовать cgi переадресацию с этим сервером, нужно изменить исходный текст сервера. Патч, для сервера NCSA 1.5 доступен в архиве PHP/FI.


Проблемы защиты

PHP/FI не читает никакие .htaccess файлы, которые могут присутствовать в каталоге. Это означает что, если у вас есть файлы, которые защищены, с использованием стандартного, основанного на особенности сервера .htaccess контроля доступа, люди могли бы потенциально обойти эту защиту, загружая страницу через PHP/FI.

Имеется пара различных решений для этой проблемы. Самое простое, возможно, это использовать особенность PATTERN_RESTRICT, находящуюся в php.h. Это позволяет вам, определить расширение (или шаблон расширений) файлов, которые можно передавать для анализа PHP/FI. Если у файла другое расширения, и кто-либо пытается загружать через PHP/FI, появится сообщение: в доступе отказано.

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

Проблема может также быть решена, используя установку прав доступа к файлу. PHP/FI может быть установлен, чтобы выполнить setuid как любой пользователь, от которого Вы пожелаете. Затем файлам, которые должны читаться PHP/FI, могут быть установлены, соответствующие биты доступа и для файлов, которые не должны читаться PHP/FI должен быть установлены владелец с другим идентификатором пользователя и соответственно измененные права доступа.


Установление подлинности HTTP

Ловушки HTTP аутентикации в PHP/FI доступны только в случае, если пакет выполняется как модуль Apache. В программе для PHP/FI в виде модуля Apache , возможно использование команда Header() , чтобы послать сообщение "Authentication Required" к броузеру пользователя, которое приведет к появлению окна ввода с запросом Пользователь/Пароль (Username/Password). Как только пользователь ввел свое имя и пароль, URL, содержащий PHP/FI скрипт будет вызыван снова с переменными, $PHP_AUTH_USER, $PHP_AUTH_PW и $PHP_AUTH_TYPE установленными соответственно имени пользователя, его паролю и типу аутентикации. Только На текущий момент поддерживается только "Базовая"("Basic") аутентикация.

Фрагмент примерa скрипта , который запросит аутентикацию пользователя при обращении к странице:

<?
if(!$PHP_AUTH_USER) {
Header("HTTP/1.0 401 Unauthorized"); 
Header("WWW-authenticate: basic realm=\"My Realm\""); 
exit;
} else { 
     echo "Hello $PHP_AUTH_USER.<P>"; 
     echo "You entered $PHP_AUTH_PW as your password.<P>"; 
}
>

Вместо простого вывода $PHP_AUTH_USER и $PHP_AUTH_PW, вам возможно захочется проверить правильность имени пользователя и пароля. Возможно, посылая запрос к базе данных, или, осуществляя поиск пользователя в dbm файле.

Чтобы предотвратить вариант что кто-либо напишет скрипт, который показывает пароль для страницы, аутентикация которой была через традиционный внешний механизм, переменные PHP _AUTH не будут установлены в том случае, если рарешена внешняя аутентикация для этой конкретной страницы.

Обратите внимание однако, что вышесказанное не мешает кому-либо, кто контролировал не-аутентифицированные URL украсть пароль от аутентифицированных URL на том же самом сервере. PHP _AUTH_VARS определяется в php.h, может быть установлена в "неопределена", для того чтобы быть уверенным, что эти переменные никогда не будут установлены и таким образом отключить возможность использования mod_php для того чтобы пытаться украсть пароли.


Примечания к модулю Apache

Выполнение PHP/FI в виде модуля Apache это наиболее эффективный способ использования пакета. В случае если пакет выполняется в виде модуля, то это означает что функциональные возможности PHP/FI объединены с функциональными возможностями сервера Apache в одной программе. Имеется ряд преимуществ для пакета в виде модуля:

Эффективность

Эффективность - намного быстрее чем традиционные программы CGI. Фактически, при выполнении PHP/FI в виде модуля, никакой CGI, не запускается. Скрипт код в HTML файлах выполняется непосредственно процессом web сервера Apache.

Защита

При выполнении модулем модулем, сначала применяются нормальные основанные на httpd правила ограничения доступа определяемые в конфигурационных файлах Apache или в частных .htaccess файлах прежде чем модулю позволяются анализировать файл. В качестве альтернативы, Вы можете также создавать скрипты PHP/FI, которые управляют нормальной httpd-основанной аутентикацией. См. Аутентикация HTTP

.

Конфигурируемость

Так как синтаксический анализатор всегда активный внутренний процесс httpd, он может быть сконфигурирован при запуске, с использованием тех же самых файлов конфигурации, которые используются для конфигурации сервера httpd. Модуль может даже быть сконфигурирован на "по-каталожной" основе, путем помещения директив конфигурации PHP в .htaccess файлах.

Директивы конфигурации Модуля Apache

Следующие директивы могут быть помещены или в файл srm.conf , или внутри тэгов <Directory> ... </Directory> в файле access.conf или внутри тэгов <Location /path> ... <Location> в файле access.conf или в индивидуальных .htaccess файлах:

phpShowInfo on|off
Включает или выключает нижние информационные колонтитулы PHP.
phpLogging on|off
Включает или выключает регистрация.
phpUploadTmpDir directory
Установка каталога, куда будут помещены переданные с помощью форм файлы.
phpDbmLogDir directory
Установка каталога, где будут размещаться файлы регистрации на базе dbm.
phpMsqlLogDB database
Установка имени базы данных mSQL, которая будет использоваться для регистрации.
phpAccessDir directory
Установка каталога, где хранятся внутренние файлы PHP для управления доступа.
phpMaxDataSpace KiloBytes
Максимальный размер , до которого может расти внутренний подпул модуля PHP. Уменьшение этого значения минимизирует использование ресурсов, которое mod_php будет занимать на вашей системе, но это может также ограничить людей при написании сложных скриптов.
Все эти директивы необязательные. Если директива не определена где-нибудь, будет использоваться значение по умолчанию, заданное во время компиляции .

Поддержка FastCGI

PHP/FI может быть скомпилирован с поддержкой FastCGI. Но прежде вам нужно будет выбрать и скомпилироватьFCGI Development Kit для вашей платформы. А также рабочий модуль переадресации CGI. Затем следуйте инструкциям, изложенным в документации по FastCGI, для конфигурирования пакета FastCGI применительно к вашей платформе. Если Вы используете модуль mod_fastcgi с сервером Apache, ниже приводятся пошаговые инструкции:

  • Подредактируйте ваш файл конфигурации Apache(Configuration), и добвьте модуль mod_fastcgi, затем перетранслируйте Apache.
  • Подредактируйте файл конфигупации srm.conf и добавьте строки:
    AddType application/x-httpd-fcgi .fcgi
    AppClass /usr/local/etc/httpd/fcgi-bin/php.fcgi -processes 4
    AddType application/x-httpd-fphp .fhtml
    Action application/x-httpd-fphp /fcgi-bin/php.fcgi
  • Скопируйте php.cgi в /usr/local/etc/httpd/fcgi-bin/php.fcgi

Теперь, любые страницы с расширением .fhtml будут переданы для обработки процессу FastCGI - php.fcgi , который уже выполняется. Php.fcgi все еще будет работать как обычный CGI, так что можно просто создать символический линк от php.cgi к php.fcgi.

Если Вы не используете Apache, вышеупомянутые шаги будут подобны, но не идентичны. Механизмы переадресации CGI доступны для NCSA и Netscape серверов на файловом архиве PHP/FI.


Контроль доступа

Если вы решили включить контроль доступа, при компиляции пакета, Вы можете добавить "?config" к любому URL, чтобы редактировать файл управления доступом. То есть.

http://your.machine.domain/cgi-bin/php.cgi/path/file.html?config

Ваш пароль конфигурации будет первоначально установлен вашему идентификатору пользователя. Если ваш идентификатор пользователя не работает как ваш пароль, это возможно означает что PHP, не смог читать файл /etc/passwd, чтобы определить ваш пользовательский идентификатор. Если дело обстоит таким образом, начальный пароль будет установлен "php". Будет совсем не глупо изменить этот пароль. Обратите внимание, что разные пользователи могут поддерживать их собственные персональные файлы конфигурации с помощью одного выполняемого PHP/FI.

Управление доступом может совершенно запутать поначалу. Экран "?config" разделен на несколько разделов. Верхний раздел - для изменения пароля, используемого для того чтобы удостовериться, что только знающие этот пароль могут изменять характеристики управления доступом. При системной установке, каждый пользователь имеет его или ее собственный экран конфигурации с его или ее собственным паролем.

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

Чтобы добавить набор правил для конкретного файла, введите URL файла в вашем броузере, и добавьте ?config к концу URL. В появившемся окне конфигурации вы увидете, что для этой страницы был добавлен набор правил , если его еще не было там. При добавлении нового набора правил, первоначально установливается равным набором правил по умолчанию. Следующая иллюстрация показывает два простых набора правил. Сначала набору правил заданный по умолчанию, который указывает только, что доступ изо всех доменов должны регистрироваться, и второй, для файла /~rasmus/test.html, и только для этого файла всем пользователям, приходящим из домена ".edu", будет отказано в доступе.

[Image of ?config screen]

Для редактирования набора правил изменяют поля, пока не будет достигнута желаемая конфигурация внутри набора правил и затем нажмите кнопку "Submit Changes". Если нужно большее количество правил, нажмите кнопку "Add Rule", и затем подредактируйте добавленное правило.

Чтобы удалить правило, выберите переключатель справа от правила, и нажмите кнопку "Submit Changes". Экран будет, перерисован и правило должно исчезнуть.

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


Регистрация Доступа

Регистрация Доступа - другая опциональная особенность, которую можно разрешить во время компиляции, отвечая "Yes" на вопрос в скрипте установки. Данные регистрации доступа могут быть сохранены в dbm файле или в базе данных mSQL. Последний более мощен, но он также немного более труден в установке.

Чтобы использовать dbm файлы для сохранения данных о регистрации доступа, нужно определить каталог, в котором могут быть записаны файлы с данными о регистрации. PHP попытается создать этот каталог, если он не существует, но для того чтобы быть уверенным, что каталог имеет соответствующие права на доступ, вам воможно захочется создать этот каталог самостоятельно перед первым запуском PHP. Права доступа к каталогу должны быть такими, чтобы пользователь с идентификатором, под которым выполняется программа cgi PHP , имел право на запись.

Для использования базы данных mSQL для регистрирации доступа, нужно сначала удостовериться, что в системе установлен mSQL и он выполняется. Затем необходимо создать базу данных. Заданное по умолчанию имя - "phpfi", хотя оно может быть изменено в src/php.h. Для создания базы данных, введите:

msqladmin create phpfi

Затем подредактируйте ваш msql.acl файл, и удостоверитесь, что разрешения на доступ к базе данных правильны. Что - нибудь вроде следующего должно удовлетворить:

database=phpfi
read=*
write=nobody,<your user id>
access=local

Для каждого пользователя, для которого Вы хотите сохранять данные регистрации, нужно создать две таблицы. msqllog шелл скрипт в каталоге scripts сделает это для Вас. Просто введите:

msqllog <user id>

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

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

Информация о регистрации доступ сохраняется при каждом обращении к странице. Эта информация впоследствии может суммироваться, просмотром этих данных регистрации. Пример скрипта для суммирования данных по регистрации приводится в составе пакета. Это - log.html файл в каталоге examples. Это - анализатор dbm файла регистрации данных. Анализатор msql файла регистрации называется mlog.html. Чтобы выполнять эти скрипты, скопируйте их в каталогу, который доступен с вашего web сервера, и введите:

http://your.machine.domain/cgi-bin/php.cgi/path/log.html

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

<?setshowinfo(0)>


Относительный против Абсолютного URL - или, почему не отображаются мои изображения?

Общая проблема для всех программ CGI состоит в том, что программа HTTPD изменяет текущий каталог на тот, в котором находится загружаемый документ. В случае программы CGI, текущий каталог установлен на каталог, где находится программа CGI. Обычно это не проблема, за исключением тех случаев, когда это приводит к относительному URL.

Относительный URL это тот, который полагает что текущий каталог, является также и каталогом, в котором размещен HTML файл . Так, например, если у меня есть URL:

http://my.machine/~rasmus/file.html

Фактический HTML файл мог бы быть:

~rasmus/public_html/file.html

Если внутри файла file.html у меня есть тэг:

<IMG SRC="pic.gif">

В случае если file.html загружен обычным образом то gif файл, как и ожидается, будет в ~rasmus/public_html/pic.gif. Однако, если он загружен через программу CGI с URL подобно:

http://my.machine/cgi-bin/php.cgi/~rasmus/file.html

то HTTPD устанавливает текущий каталог на /cgi-bin (или на тот, на который указывает директива ScriptAlias) и впоследствии, когда страница загружена, pic.gif файл будет ожидаеться, в каталоге: /cgi-bin/pic.gif, что обычно является не желательным эффектом.

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

<IMG SRC="/~rasmus/pic.gif">

то не было бы никакой проблемы. Но дело в том что использование абсолютного URL не всегда желательно, так как это делает страницы менее переносимыми. Очевидный вопрос, который Вы можете сейчас задать: " Почему бы PHP просто не изменяет текущий каталог на правильный? ". Ответ - PHP фактически изменяет текущий каталог на каталог, в котором расположен отображаемый HTML файл. Любые пути файлов, используемые внутри скрипта PHP могут быть относительны. Проблема состоит в том, что тэги, находящиеся вне области действия PHP типа <img> и <href> не будут обрабатываться PHP. Когда они анализируются, PHP уже не активен, и текущий рабочий каталог установлен на каталог, определенный HTTPD.

Решение - состоит в компромиссе. PHP обеспечивает переменную, называемую PATH_DIR. Она всегда содержит часть каталога из имени текущего HTML файла. Если эта переменная PATH_DIR используется в <img> и <href> тэгах, то может быть достигнут эффект относительного URL , хотя для сервера при анализе они будут выглядеть как абсолютный URL. Для нашего вышеприведенного примера , единственое изменение, которое нужно сделать это изменить тэг img на:

<IMG SRC= "<? ECHO $PATH_DIR>/pic.gif">

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

Другой способ решения состоит в том, чтобы использовать традиционный <BASE HREF= ... > тэг в HTML файле.


Как программа PHP обрабатывает методы данных GET и POST

PHP обнаруживает оба метода GET и POST , исходящие из HTML формы. Одна важная деталь, для понимания - это то что метод, POST всегда обрабатывается раньше, если присутствуют оба из них. Если переменная PHP определяется методом POST, или если переменная определена дэймоном HTTP в среде окружения Unix, то метод GET не может перезаписать ее. Это должно предотвратить ситуацию когда кто-либо добавит строку ?REMOTE_HOST=some.bogus.host к его URL и таким образом, подчунуть механизму регистрации PHP, эти альтернативные данные. Однако POST методу, разрешено перезаписывать эти переменные.

Любая компонента данных GET (данные после '?' в URL) которая имеет форму, word=something, определит переменную $word, содержащую значение something. Даже если данные не определены в этой форме, к ним все равно можно обратиться через массив $argv. Например, в URL подобно:

/cgi-bin/php.cgi/file.html?abc+def+EMAIL_ADDR=rasmus@vex.net&var==value

Соответствующие компоненты таблицы идентификаторов PHP будут:

$argc       = 4
$argv[0]    = abc
$argv[1]    = def
$argv[2]    = EMAIL_ADDR=rasmus@vex.net&var==value $EMAIL_ADDR = rasmus@vex.net
$var        = value

Обратите внимание, как EMAIL_ADDR часть данных присутствует и в переменной $argv [2], в которой она не анализируется, и создается переменная $EMAIL_ADDR, содержащая значение rasmus@vex.net.

$EMAIL_ADDR переменная использовалась в вышеупомянутом примере, потому что это - полезная переменная, если Вы используете особенности регистрации PHP. Добавляя:

?EMAIL_ADDR=

К любым линкам на странице, где известен email адрес пользователя, Вы можете передавать это значение следующей странице. Система регистрации PHP, будет автоматически просматривать значение этой переменной и записывать его в качестве адреса электронной почты пользователя в файлах регистрации. Для любых пользователей PHP, вышеупомянутое выполняет ту же самую функцию что и добавление ?<!--$email--> к используемому URL. Это выглядит немного сложным сейчас, но сложным является и решение самому сформировать свою собственную страницу.

В вышеприведенном примере Вы также видели, как несколько переменных в методе GET могут быть правильно определены, отделением их друг от друга символом "&". Этот список переменных, разделенный символом "&" должен быть последней (или единственной) компонентой метода GET.

SELECT MULTIPLE и PHP

Тэг SELECT MULTIPLE в конструкции HTML позволяет пользователям сделать множественный выбор из списка. Выбранные объекты впоследствии передаются обработчику формы. Проблема состоит в том, что все они будут переданы с одним и тем же именем объекта. То есть.

<SELECT NAME="var" MULTIPLE>

Каждая выбранная опция будет передана обработчику формы в виде:

var=option1
var=option2
var=option3

Каждая опция затрет предыдущее содержимое переменной $var. Решение состоит в том, чтобы использовать особенность PHP/FI - не-индексированные массивы. Так нужно использовать:

<SELECT NAME="var[]" MULTIPLE>

Это сообщает PHP/FI, чтобы он обработывал переменную var как массив, каждое присвоение значения переменной var[] добавляет элемент к массиву. Первый элемент становится $var[0], следующий $var[1], и т.д. Для опеределения количества выбранных элементов может использоваться функция count(), и в случае необходимости функция sort() для сортировки массива.


IMAGE SUBMIT и PHP

При передаче формы на рассмотрение, можно использовать картинку, вместо стандартного представления кнопки. Это можно сделать, указав тэг:

<input type=image src=image.gif name=sub>

Когда пользователь нажимает где-нибудь на изображение, сопровождающая форма будет передана серверу с двумя дополнительными переменными, sub_x и sub_y. Они содержат координаты точки внутри изображения, на которой пользователь щелкнул мышью. Опытный может обратить внимание, что фактические имена переменных , посланных броузером содержат точку вместо подчеркивания, но PHP автоматически преобразовывает точку в подчеркивание.


GD (графическая библиотека для создания GIF) Поддержка в PHP

PHP поддерживает библиотеку GD версии 1.2 написанной Томасом Бутеллом. Непосредственно в PHP нет никакого кода GD . Если Вы хоттите использовать поддержку GD в PHP/FI, Вам нужно получить библиотеку GD с http://www.boutell.com/gd/gd1.2.tar.Z, инсталлировать ее, и затем переустановить PHP.

Не все возможности GD поддерживаются PHP. Для списка поддерживаемых функций см. Алфавитный Список функций. Все функции GD начинаются со слова, Image.

Подробная информация относительно пакета GD доступна по: http://www.boutell.com/gd/.

GD 1.2 - copyright 1994, 1995 Quest Protein Database Center, Cold Springs Harbor Labs.


PHP/FI и виртуальные сервера

PHP прекрасно работает на виртуальных хостах, которые поддерживаются некоторыми дэймонами http. Одна проблема, которая может произойти при такой установке, связана с различиями в способе, которым httpd устанавливает переменную окружения SCRIPT_NAME. Обычно ее значение установливается равным пути выполняемой в текущий момент программы CGI относительно директории ROOT_DIR сервера httpd. Однако, при использовании виртуального домена , некоторые httpd не устанавливают правильно значение переменной SCRIPT_NAME - относительно самого верхнего каталога виртуалного домена, как это должно быть. Если добавление ?config к URL дает Вам сообщение об ошибке: "invalid URL", значит эта проблема у вас существует. Вам нужно будет подредактировать файл php.h и устанавить значение переменной VIRTUAL_PATH, чтобы указать путь к вашему выполняемому файлу php.cgi относительно вашего каталога верхнего уровня .


Поддержка закачки файлов

PHP/FI автоматически обнаруживает попытку закачки файла, из броузера, который поддерживает закачку файлов основанную на базе форм, как это предложено Е. Небелем и Л. Мазинтером из Xerox и описано в RFC 1867.

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

<FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=POST> <
INPUT TYPE="hidden" name="MAX_FILE_SIZE" value="1000">
Send this file: <INPUT NAME="userfile" TYPE="file">
<INPUT TYPE="submit" VALUE="Send File">
</FORM>

_URL_ должен указывать на php html файл. Скрытое поле MAX_FILE_SIZE должно предшествовать полю ввода имени файла и его значением является максимальный размер принимаемого файла. Значение задается в байтах. Для этого файла назначения, следующие переменные будут определены, при успешной закачке:

$userfile

Временное имя файла, в котором загруженный файл был сохранен на машине сервере.

$userfile_name

Первоначальное имя файла на машине отправителя.

$userfile_size

Размер загруженного файла в байтах.

$userfile_type

MIME тип файла, в том случае если броузер предоставляет эту информацию. например это может быть "image/gif".

$userfile - основная переменная из вышеупомянутых, будет соответствовать полю NAME в форме закачки.

По умолчанию файлы будут сохранены в заданном для сервера временном каталоге по умолчанию. Это значение может быть изменено путем установки переменной окружения среды TMPDIR для PHP/FI. Хотя при установке ее,с использованием вызова PutEnv () из скрипта PHP/FI, работать это не будет. В качестве альтернативы, Вы можете установить временный каталог, редактируя php.h и определив переменную UPLOAD_TMPDIR.

Скрипт PHP/FI, который принимает закачиваемый файл, должен выполнить любые логические действия, необходимые для определения, что должно быть выполнено с закачаннным файлом. Вы можете например использовать $file_size переменную, для того чтобы отбросить любые файлы, которые являются либо слишком маленькими либо слишком большими. Вы можете использовать $file_type переменную, для того чтобы отбросить любые файлы, которые не соответствуют каким-либо критериям типа. Какие бы ни были действия, вам нужно будет удалить файл из временного каталога или, переместить его в другое месте.

Пожалуйста обратите внимание, что CERN httpd , кажется, удаляет все начиная с первого пробеле в заголовке MIME content-type, который он получает от пользователя. Если дело обстоит таким образом, то CERN httpd не будет поддерживать возможность закачки файлов.


Поддержка Cookie

PHP/FI поддерживает HTTP cookies как определено Спецификациями Netscape. Cookies - механизм для сохранения данных в удаленном броузере и таким образом трэкинг или идентификация возвращаемая пользователем . Вы можете устанавливать cookies, используя функцию SetCookie() . Cookies - часть HTTP заголовка, таким образом функция SetCookie() должна вызваться до того как какие-либо данные будут посланы для вывода удаленному броузеру. Это - то же самое ограничение что касается функции Header() .

Любое значени cookies посланного Вам от пользователя, превратится автоматически в переменную PHP/FI точно так же как методах GET и POST.

Если Вы желаете привоить несколько значений к одиночному cookie, просто добавьте [] к имени cookie. Например:

SetCookie("MyCookie[]","Rasmus Lerdorf", time()+3600);

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

$Count++;
SetCookie("Count",$Count, time()+3600); 
SetCookie("Cart[$Count]",$item, time()+3600);

Поддержка mSQL

mSQL это мини-SQL и это небольшой и простой SQL сервер баз данных, написанный Дэвидом Хьюджесом. Он доступен по ftp://ftp.bond.edu.au/pub/Minerva/msql

PHP/FI обладает богатым набором функций поддержки mSQL:

msql()
msql_Connect()
msql_CreateDB()
msql_dbName()
msql_DropDB()
msql_FieldFlags()
msql_FieldLen()
msql_FieldName()
msql_FieldType()
msql_FreeResult()
msql_ListDBs()
msql_Listfields()
msql_ListTables()
msql_NumFields()
msql_NumRows()
msql_RegCase()
msql_Result()
msql_TableName()

В дополнение к этим функциям, PHP/FI может быть также скомпилирован таким образом, чтобы автоматически пропускать, любые прямые одиночные кавычки ( ' ), полученные из методов GET или POST. Если в файле php.h определена переменная MAGIC_QUOTES, то эти кавычки будут автоматически пропускаться, упрощая, передачу данных из форм непосредственно к запросам msql.


Поддержка Postgres95

Postgres95 - мощная база данных являющаяся паблик домен, которая реализует значительное подмножество языка SQL . Она поддерживает множество типов данных и команды, которые не доступны в mSQL. Подробная информация относительно Postgres95 и непосредственно программного обеспечения может быть найдена по URL: http://epoch.cs.berkeley.edu: 8000/postgres95/.

Следующие функции PHP доступны для Postgres95:

pg_Connect()
pg_Close()
pg_Host()
pg_Port()
pg_Options()
pg_tty()
pg_DBname()
pg_Exec()
pg_Result()
pg_FreeResult()
pg_GetLastOid()
pg_NumRows()
pg_NumFields()
pg_FieldNum()
pg_FieldName()
pg_FieldType()
pg_FieldSize()
pg_FieldPrtLen()
pg_errorMessage()

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

В дополнение к этим функциям, PHP/FI может быть скомпилирован, чтобы пропускать любые прямые одиночные символы ( ' ) кавычки, найденные в данных методов GET или POST . Если в файле php.h определена переменная MAGIC_QUOTES, то эти кавычки автоматически будут пропускаться, упрощая, передачу данных из форм непосредственно к запросам Postgres95.

Вот простой скрипт , который соединяется с базой данных Postgres95 на локальном сервере, называющейся 'mydb' и извлеает имена и возрасты людей из таблицы:

<?
   $conn = pg_Connect("localhost", "5432", "", "", "mydb"); 
   if (!$conn) { 
       echo "An error occured.\n"; 
       exit;
   }

   $result = pg_Exec($conn, "select * from table1"); 
   if (!$result) { 
       echo "An error occured.\n"; 
       exit;
   }

   $num = pg_NumRows($result);
   $i = 0;

   while ($i < $num) { 
      echo "name: "; 
      echo pg_Result($result, $i, "name"); 
      echo "  age: "; 
      echo pg_Result($result, $i, "age"); 
      echo "<BR>";
      $i++;
   }

   pg_FreeResult($result);
   pg_Close($conn);
>

Регулярные Выражения

Регулярные выражения используются для сложного манипулирования строками в PHP/FI. Поддержка интерфейса между скриптом и регулярными выражениями осуществляется через следующие функции: Reg_Match(), Reg_Replace(), и Reg_Search(). Первым аргументом для всех трех функций - это строка, задающая регулярное выражение. Этот строка состоит из регулярных и специальных символов. Регулярные символы имеют то же значение что и при в других командах Unix, в то время как специальные символы имеют специальное значение. Далее следуюет - полный список специальных символов и их значения как это воспринимает синтаксический анализатор PHP/FI:

`.'

Является специальным символом, который соответствует любому символу, за исключением символа новой строки. Используя конкатенацию, мы можем задавать регулярные выражения подобно 'a.b', которое соответствует любой трех-символьной строке, которая начинается с 'a' и заканчивается 'b'.

`*'

Сам по себе это не конструкция; это - суффикс, который означает, что предшествующее регулярное выражение, может быть повторено сколь угодно много раз. В строке "fo*", символ "*" применяется к символу "o', так "fo*" задает "f" с последующим любым количеством символов "o".

В случае нулевого количества символов "o" строка "fo*" будет также соответствовать "f".

Символ "*" Всегда применяет к *наименьшему* возможному предшествующему выражению. Таким образом, "fo*" задает повторение "o", а не повторение "fo".

Процесс сравненияr обрабатывает конструкцию "*", пытаясь согласовать настолько много повторений насколько много их их может быть найдено. Затем он продолжает обработку остальной части шаблона. Если, впоследствии, появится несоответсвтие с шаблогам, происходит возврат, путем отбрасывания некоторых повторений "*", в случае, если это делает возможным совпадение остальной части шаблона. Например, шаблон "c[ad]*ar" для строки "caddaar", "[ad]*" сначала совпадает с "addaa", но это не позволяет совпасть следующему символу "a" в шаблоне. Так последнее совпадение "[ad]" отменяутся, и следующий символ "a" пробуется вновь. Теперь шаблон сооветствует.

`+'

"+" Подобен "*" за исключением того, что требуется по крайней мере одно соответствие для предшествующего образца. Таким образом, "c[ad]+r" не совпадает с "cr", но совпадет с чем либо еще что может быть задано шаблоном "c[ad]*r".

`?'

"?" Подобен "*" за исключением того, что позволяет задать нуль или более соответствий для заданного шаблона. Таким образом, шаблон "c[ad]?r" будет задавать строки "cr" или "car" или "cdr", и ничего больше.

`[ ... ]'

"[" начинает "множество символов", которое завершается символом "]". В самом простом случае, символы между этими двумя скобками формируют множество. Таким образом, "[ad]" задает символы "a" или "d", и "[ad]*" задает любоую п оследовательность символов "a" и "d" (включая и пустую строку), из чего следует, что шаблон "c[ad]*r" задает "car", и т.д.

Диапазон символов также может быть включен в множество символов, с помощью символа "-", помещенного между двумя другими. Таким образом, шаблон "[a-z]" задает любой символ нижнего регистра. Диапазоны могут свободно перемежаться с одиночными символами, как в шаблоне "[a-z$%.]", который задает любой символ нижнего регистра или символы "$", "%" или точку.

Обратите внимание, что символы, обычно являющиеся специальными, внутри множества символов больше не являются таковыми. Внутри множества символов существует полностью отличный набор специальных символов : "]", "-" и "^".

Для того чтобы включить "]" в множество символов, нужно сделать его первым символом. Например, шаблон "[]a]" задает символ "]" или "a". Чтобы включить символ "-", нужно использовать его в таком контексте, где он не может указывать диапазон: то есть или первым символом, или сразу после диапазона.

`[^ ... ]'

"[^" начинает "исключающее множество символов", который задает любой символ за исключением заданных. Таким образом, шаблон "[^a-z0-9A-Z]" задает любой символ *за исключением* букв и цифр.

"^" не является специальным символом в множестве, если только это не первый символ. Символ следующий после "^" обрабатывается так, как будто он является первым (это может быть "-" или "]").

`^'

Является специальным символом, который задает пустую строку - но только в случае если он стоит в начале строки шаблона. Иначе шаблон не будет соответствовать. Таким образом, шаблон "^foo" задает "foo" в начале строки.

`$'

Подобен "^", но только задает конец строки. Так шаблон, "xx*$" задает строку с одним или более символом "x" в конце строки.

`\'

Имеет два значения: экранирует вышеперечисленные специальные символы (включая "\"), и задает дополнительные специальные конструкции.

Так как "\" экранирует специальные символы, "$" является регулярным выражением, задающим только символ "$", а "\[" является регулярным выражением, задающим только "[", и так далее.

В основном, "\" с последующим любым символом соответствует только этому символу. Однако, есть некоторые исключения: символы, который, когда "\" предшествует специальная конструкция. Такие символы обычно всегда задают их собственное значение.

Никакие новые специальные символы не определены. Все расширения к синтаксису регулярных выражений сделаны, определением новые двух-символьных конструкций, которые начинаются с "\".

`\|'

Задает альтернативу. Два регулярных выражения A и B с "\|" между ними формируют выражение, которое задает что-либо чему соответствует или А или B.

Так выражение, "foo\|bar" или "foo" или "bar", но никакую другую строку.

"\|" применяется к максимально большим окружающим выражениям. Только "\(...\)" вокруг выражений могут ограничивать мощность "\|".

Существует полная возможность перебора с возвратами , когда задано множество "\|".

`\( ... \)'

является конструкцией группирования , которая служит трем целям: 1. Заключать в себя множество "\|" альтернатив для других операций. Так, шаблон "\(foo\|bar\)x" соответствует или "foox" или "barx".

2. Включать сложное выражение для постфиксного "*". Так шаблон "ba\(na\)*" задает "bananana", и т.д., с любым (ноль или болеее ) количеством "na".

3. Отметить искомую подстроку для последующего обращения.

Эта последняя функция - не следствие идеи относительно группировки выражений скобками; это - отдельная особенность, которая задает второе значение для той-же самой конструкции "\(...\)" , так как нет практически никакого конфликта между этими двумя значениями. Вот объяснение этой особенности:

`\DIGIT'

После окончания конструкции "\(...\)" , анализатор запоминает начало и конец текста, совпавшего с этой конструкцией. Затем, позднее в регулярном выражении можно использовать "\" с поледующей цифрой (DIGIT), что означает "задать тот же самый текст, который соответствовует DIGIT нахождению в конструкции '\(...\)'". "\(...\)" конструкции пронумерованы в порядке возрастания в регулярном выражении.

Строкам задающим первые девять конструкций "\(...\)" , появляющимся в регулярном выражении - соответствуют числа от 1 до 9. "" до "" может быть использовано для обращения к тексту, соответствующей "\(...\)" конструкции. Эти 9 сохраненных конструкций известны также как регистры.

Например, шаблон "\(.*\)" задает любую строку, который состоит из двух идентичных частей. "\(.*\)" задает первую часть, которая может быть всем чем угодно, но последующая "" задает точно тот же тексту.

Сохраненные конструкции или регистры могут использоваться внутри одиночных выражений, или, они могут быть извлечены и использоваться где-либо еще. Добавление третьего параметра к reg_match() или reg_search() определит массив, в который будут записаны 9 регистров. При этом записывается дополнительный регистр (нулевой элемент) в котором задана строка совпавшая со всем выражением. Например:

<?$string = "This is a test";
      $cnt = reg_match("\(\w*\).*\(\")
      echo $cnt;
      echo $regs[0];
      echo $regs[1];
      echo $regs[2];
    >

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

`\b'

Задает пустую строку, но только, если она находится в начале или в конце слова. Таким образом, "\bfoo\b" соответствует любому местонахождению "foo" в виде отдельного слова. "\bball\(s\|\)\b" соответствует "ball" или "balls" в виде отдельных слов.

`\B'

Задает пустую строку, если она не в начале или не в конце слова.

`\<'

Задает пустую строку, но только, если она - в начале слова.

`\>'

Задает пустую строку, но только, если она в конце слова.

`\w'

Задает любой символ, являющийся составной частью слова.

`\W'

Задает любой символ, который - не является составной частью слова.


Скрипт язык PHP/FI

Скрипт язык PHP подобен по синтаксису языку C по многим показателям. Он поддерживает переменные, массивы, обращения к функциям, различные типы переменных и множество других вещей, которые вам могут потребоваться для написания сложных cgi программы .

В последующих разделах описана каждая функция PHP/FI и он может быть быстро найдена, простым добавлением #function_name к url этого документа, так как каждое функциональное описание снабжено тэгом имени.

Синтаксис

Каждая команда PHP начинается с тэга <?и оконивается >. Или, команды могут быть сгруппированы внутри одной пары <? >и отделяться друг от друга символом ;.

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

       <?$a = 5> 
       <?echo $a>

Это можно записать, также в виде:

  <; $a = 5; echo $a >

Или даже:

  <
       $a = 5; 
       echo $a;
       >

Лишние символы пробела, табуляции и новой строки игнорируются. Это предложение должно использовать, чтобы форматировать блоки программы PHP, чтобы сделать их более удобочитаемыми. Регистр написания имеет значение для имен переменных, но не для имен к функций. Позже в этой документации, при обзоре функций , регистр используется только для того, чтобы сделать имена функций более читабельными. В фактической программе Вы можете использовать любой регистр, который пожелаете. Комментарии поддерживаются. Комментарии записываются точно так же как комментарии в Языке C. /* начинает комментарий, и */ заканчивает комментарий. Комментарии могут быть помещены в любом месте внутри блока <? ... >.


Переменные

Поддерживаются три типа переменных . Длинные целые (long int) , двойной точности с плавающей запятой (double) и символьные строки (strings). Тип переменных обнаруживается автоматически. Например:

  <?$a = 5>

Заставляет $a стать переменной типа INTEGER.

  <?$a = 5.0>

Заставляет $a стать переменной типа DOUBLE.

  <?$a = "5">

Заставляет $a стать переменной типа STRING.

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

Все три типа переменных могут также рассматриваться как массивы, если к их именам добавляется [значение]. В отличие от C, массивы в PHP фактически представляют собой ассоциативные массивы, подобные тем, которые используются в Perl. Следующая запись корректна:

       <?
       $a[0] = 5;
       $a["hello"] = 6; 
       echo $a[0]; 
       echo $a["hello"];
       >

Обратите внимание, что, если имя переменной используется, и как массив и как обычная переменная, то имя обычной переменной является синонимом элементу массива с индексом "0". То есть.

  $a = 1;

Это тоже самое что:

  $a[0] = 1;

PHP/FI также поддерживает не-индексированные массивы. Не-индексированный массив генерирует собственный индекс, по мере добавления элементов к нему. Например:

  $a[] = "Hello";
       $a[] = "There";

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

  echo $a[0]; 
       echo $a[1];

Вы можете использовать функцию count(), для того чтобы определить количество элементов для любого массива.

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

  $a = $b + $c;

Может вести себя двояко. Если $b это число, то числовое значение $c добавляется к $b, и сумма сохраняется в $a. В этом случае тип $c не важен. Операция управляется типом первой переменной. Если $b строка, то значение строки $c сонкатенируется с $b, и результирующая строка помещается в $a. Это также приводит к некоторым недоразумениям. Вам нужно прочесть раздел по перегруженным операторам, чтобы получить лучше понимание этого факта.


Ассоциативные массивы

В предыдущем разделе мы познакомились с ассоциативными массивами. Ассоциативный массив - это массив, в котором индекс не обязательно должен быть последовательным рядом числовых значений. Индексом массива может быть любое число или строка. PHP/FI обеспечивает набор функций, чтобы манипулирования этими ассоциативными массивами. Это: Next(), Prev(), Reset(), End(), и Key().


Ссылки на переменные

Иногда удобно иметь способ ссылаться на имена переменных . То есть имя переменной может установливаться и использоваться динамически. Нормальной переменной присваивается значение оператором типа:

  $a = "hello";

При использовании ссылки на переменную берется значение переменной и обрабатывается как имя переменной. В вышеупомянутом примере, hello, может использоваться как имя переменной, используя два знака $. То есть.

  $$a = "world";

На этот момент определены две переменных и сохранены в дереве символов PHP/FI:

       Имя переменной  Содержимое переменной
       a        hello
       hello            hello world

Следовательно, оператор:

  echo "$a $$a";

Произведет точно такой же вывод как и:

echo "$a $hello";

То есть. Они выведут строку: hello world


Конструкции Языка

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

  • if(условие)
  • else
  • elseif(условие)
  • endif
  • switch(выражение)
  • case выражение
  • default
  • break
  • endswitch
  • while
  • endwhile
  • include
  • exit
Синтаксис условных выражений подобен синтаксису в языке C. ==проверка на равенство.!=означает не равно. Также поддерживаются: >, <, >=, <=. Условное И - &&, условное ИЛИ - ||.

Примеры:

       <?
       if($a==5 &&  $b!=0 );
       $c = 100 + $a / $b; 
       endif;
       >

Этот пример может быть записан в стандартном синтаксисе C:

       <?
       if($a==5 && $b!=0) {
       $c = 100 + $a / $b;
       }
>

Нет никакого различия между двумя синтаксисами. Мне лично больше нравится использовать endif, endswitch и endwhile, так что я точно знаю, какую конструкцию я заканчиваю. Однако, эти конструкции окончания могут быть всегда заменены на закрывающую фигурную скобку.

Важно обратить внимание, что управление не зависит от организации блоков скриптов внутри кода. Вы можете начать выражение if в одном блоке и закончить выражение в другом. Например:

       <?if($a==5 &&  $b!=0)>
               <b>Normal html text</b> 
       <?endif>

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

       <?if($a==5 &&  $b!=0) {>
       <b>Normal html text</b> 
       <? } >

Обе версии верны, и они будут делать одно и тоже.


Функции определяемые пользователем

Вы можете определять функции внутри программы PHP со следующим синтаксисом:

       <?
       Function Test ( 
         echo "This is a test\n";
       );
       >

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

       <?
         Test();
       >

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

       <?
         Function Sum $a,$b,$c (
         return($a+$b+$c);
       );

       echo Sum($a,$b,$c);
       >

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


Область видимости переменных

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

       $a=1; /* глобальная переменная */
       Function Test ( 
          echo $a;/* ссылка на локальную переменную */
       );
       Test();

Эта программа ничего не выведет, так как оператор echo работает с локальной версией переменной $a , и ей не было присвоено значение внутри этой области видимости. Вы можете обратить внимание, что это немного отличается от языка C, где глобальные переменные автоматически доступны функциям, если только специально не отменяются локальным определением. Это может вызвать некоторые проблемы, в случае когда люди могут неосторожно изменять глобальную переменную. В PHP/FI глобальная переменная должна быть объявлена глобальной внутри функции, если предполагается что она будет использоваться в этой функции. Пример:

       $a=1;
       $b=2;
       Function Sum $first,$second ( 
          global $a,$b;

          $b = $a + $b;
       );
       Sum();
       echo $b;

Эта прграмма выведет "3". После объявления $a и $b глобальными внутри функции, все ссылки к любой из них будут относится к их глобальной версии. Нет никакого ограничения на число глобальных переменных, которые могут использоваться функцией. Однако, переменная должна существовать в глобальной области видимости до вызова функции. Нельзя создать новые глобальные переменные изнутри функции.

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

       Function Test (
         $a=0;
         echo $a;
         $a++;
       );

Эта функция совершенно бесполезна, так как, каждый раз при вызове, она устанавливает $a в 0 и печатает "0". Оператор $a++, который увеличивает переменную, не дает никакого результата, так как, как только проиходит выход из функции переменная $a исчезает. Чтобы сделать полезную функцию подсчета, которая не будет терять значения текущего счетчика, переменная $a объявляется статической:

       Function Test ( 
         static $a=0; 
         echo $a;
         $a++;
       );

Теперь, каждый раз при вызове функции Test(), она будет печатать значение $a и затем увеличивать его.

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

       Function Test ( 
         static $count=0;

         $count++;
         echo $count;
         if($count < 10 {
           Test();
         }
       );        

Математические Выражения

PHP поддерживает полный набор математических операций, в выражениях. Учитывается порядок операций. Следующие операторы допустимы:

       <? $a = 2 + 1 > Сложение
       <? $a = 2 - 1 > Bычитание
       <? $a = 2 * 1 > Умножение
       <? $a = 2 / 1 > Деление
       <? $a = 2 % 1 > Деление по модулю

Поддерживаются и скобки и порядок операций, так что следующая запись верна:

       <?$a = (2"+1")*3+6/3>

Поддерживаются C-подобные операторы увеличения += и уменьшения -= . То есть.

       <? $a += $b>

Это эквивалентно:

       <? $a = $a + $b>

Поддерживаются C-подобные поразрядные операторы =& и |=. То есть.

       <? $a &== 4>

Это эквивалентно:

<? $a = $a &  4>

Циклы While

Вы можете зацикливать управление внутри программы PHP, используя конструкцию while(); endwhile;.

       <?
         $a=0;
         while($a <100) {
               $a++;
               echo $list[$a];
         }
       >

Вышеупомянутый пример показывает использование цикла while, чтобы отобразить содержимое массива. ПРЕДУПРЕЖДЕНИЕ, хотя язык PHP поддерживает операторы типа ++ и -- для увеличения или уменьшения переменной, они не обрабатываются точно так же как в языке C . В PHP нет концепции пре- и пост- инкрементирования как это есть в С. Как объяснянолось в разделе Конструкции Языка, выше, тоже может быть получено и с циклом while(); endwhile;.


Конструкция Switch

PHP поддерживает switch конструкцию очень похожую на эквивалентную в C.

<? $a=0; switch($a) { case 1; echo "a is 1"; break; case "hello"; echo "a is hello"; break; default; echo "a is unknown"; break; } >

Выше - пример конструкции switch. Она подобна последовательности конструкций if/elseif/else, но более легко читаема. Единственое различие между конструкцией switch PHP и подобным ему в C - это, что в конце каждой строки используется точка с запятой. Нет никаких двоеточий. Как объяснялось в раздел Конструкции Языка, выше, тот же самое может быть получено с switch(); endswitch;.

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


Безопасные Переменные - Победа над взломами метода GET

В предыдущих разделах говорилось относительно методво GET и POST и переменных. Если Вы задумаетесь об этом, Вы можете увидеть проблемы защиты. Например, если я на web странице получаю некоторые данные из базы данных и передаю эти данные дальше в переменной с именем "data" через форму методом POST. В следующей странице я могу обращаться к этой переменной и что-то с ней делать. Однако, если кто-либо обратится к этой второй странице непосредственно и поместит "?data=something" прямо после URL, то переменная устанавливается методом GET, они действительно обошли оригинальную форму POST.

PHP предоставляет функцию SecureVar(), которая используется для того, чтобы отметить имена переменных как безопасные. Эти безопасные переменные могут быть установлены только непосредственно в программе PHP, либо получены из формы методом POST. Они не могут быть установлены с использованием механизма определения переменной методом GET. Из нашего сценария выше, если мы поместим строку:

       <?SecureVar("data")>

Где-нибудь в начале нашей второй страницы, то прием с методом GET не cработает. Переменная "data", появилась бы пустой, если бы не была получена непосредственно из формы методом POST на первой странице.

SecureVar() фактически принимает в качестве аргумента регулярное выражение, так что можно задавать образцы имен переменных, которые должны обработываться этим безопасным способом. Например,

       <?SecureVar(".*data.*")>
Отметит любую переменную со словом "data" где-нибудь в имени, как являющуюся безопасной.

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


Перегруженные Операторы и работа с типами данных

Перегруженный оператор - это оператор подобный например '+' , который может делать различные, основываясь на типах выражений, к которым он применяется.

Проблема состоит в том, что PHP понимает 3 типа переменных. Integer, Double и String. Когда переменной впервые присваивается значение, PHP автоматически оперделяет тип этой переменной.

То есть.

       $a = 1;         Тип будет integer
       $b = 1.5;       Тип будет double
       $c = "1";     Тип будет string

Теперь, что произойдет, если Вы сделаете что-нибудь вроде:

       $d = $a + $c;

Синтаксический анализатор рассматривает первую часть арифметического выражения и использует его для результата а также характер действия, которое должно быть выполнено. В вышеупомянутом прмере, так как $a - integer, $d будет тоже integer, и выполнение целочисленного сложения , даст результат:

       $d = 2  Тип integer

Следовательно:

$d = $c + $a

Результаты в:

       $d = "11"     Тип - string

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

Решение состоит в механизм простого приведения типа .

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

       $a = 1;

Внутри в таблице идентификаторов я сохраняю 3 версии.

       Integer:  1  <--- flag
       Double: 1.0
       String: "1"

Функция SetType() может перемещать флажок, указывающий тип переменной.

       SetType($a,"double");

Это вынудит рассматривать $a, как double в дальнейшем.

Функция GetType() возвращает тип.

GetType($a) вернет, в этом случае, "double" .

Также существуют функции для того, чтобы возвратить 3 различных типа без перемещения флажка типа.

       IntVal($a)      вернет  1
       DoubleVal($a)   вернет   1.0
       StrVal($a)      вернет   "1"

Перегруженный оператор не изменяет характер переменных PHP, но дает Вам некоторые инструментальные средства, для лучшей работы с ними. PHP - не нечто, что напоминало бы не вполне развитый Perl. PHP должен быть малым и быстрым. Perl имеет дело ловушкой перегруженных операторов, заставляя оператор вроде '+' работать только с числами. Если Вы суммировать строки, нужно использовать оператор '.'. Как только появляются отдельные операторы для каждого типа, это делает язык намного более сложным. То есть. Вы не можете использовать '==' для строк, Вам придется использовать 'eq'. Я не вижу смысла, особенно для чего-то вроде PHP, где большинство скриптов будут довольно простыми и в большинстве случаев, написанными непрограммистами, которые хотят язык с базовым логичным синтаксисом, который не требует больших навыков.


Подавление Ошибок при обращении к функциям

Иногда бывает желательно игнорировать фатальные ошибки, о которых могут сообщать специфические функции PHP. Например, Вы захотите игнорировать ошибки от вызова dbmopen() и просто проверять возвращаемое значение обращения, без того, чтобы сообщение об ошибке появлялось на экране web. Это может быть сделано, помещая символ "@" перед именем функции. То есть.

       $err_code = @dbmopen($filename,"w");

Реальное сообщение об ошибке, которое должно было бы быть выведено, может быть проверено во внутренней переменной PHP, $phperrmsg.

Более общий подход, для подаления вывода сообщений об ошибках - это использование функции SetErrorReporting(). С помощью этой функции вывод сообщений об ошибках может быть заблокирован для всех блоков программы, помещением вызова типа:

       SetErrorReporting(0);

Это выключает все сообщения об ошибках. Им можно затем разрешить снова с помощью вызова:

       SetErrorReporting(1);

Внутренние функции

PHP имеет целый ряд встроенных функций. Функции точно также как и в языке C. Некоторые из них имеют один или более аргументов, некоторые возвращают значения, которые затем могут быть присвоены переменной или использоваться как аргумент для другой функции. Например:

  <?$t=time()>

Эта запись присваивает значение, возвращаемое функцией time(), переменной t.

Алфавитный Список функций

Abs(arg)

Abs возвращает абсолютное значение аргумента.

BinDec(binary_string)

BinDec возвращает десятичный эквивалент двоичного числа, представленного параметром binary_string. Самый большое число, которое может быть преобразовано - длиной 31 бит или 4294967295 в десятичном выражении. См. также функцию DecBin().

ChDir(dir)

ChDir изменяет текущий рабочий каталог на каталог, заданный аргументом.

ChGrp(file,group)

ChGrp изменяет идентификатор группы заданного файла.

ChMod(file,perms)

Функция ChMod изменяет права доступа к файлу, заданному аргументом. Параметр perms задается в восьмеричном виде.

ChOwn(file,owner)

ChOwn изменяет владельца заданного файла. Обратите внимание, что это будет работать только в том случае, если PHP/FI выполняется от имени суперпользователя (что вообще то не очень хорошая идея).

Chr(arg)

Chr возвращает символ ASCII, заданный целочисленным аргументом.

closeDir()

CloseDir закрывает каталог, открытый функцией openDir.

Cos(arg)

Cos возвращает косинус аргумента, заданного в радианах. См. также Sin() и Tan()

Count(array)

Count возвращает число элементов в переменной типа массив. Если переменная не является массивом, возвращаемое значение будет 1 (потому что нормальная переменная подобна массиву с одним элементом). Если переменная не определена, возвращаемое значение будет 0.

Crypt(string,[salt])

Функция Crypt шифрует строку, используя стандартный в Unix метод шифрования - DES . Аргументами являются строка, которую нужно зашифровать и необязательная двух-символьная строка - затравка, служащая базой для шифрования. См. справку по функции Crypt для вашей системы Unix, для более полной информации. Если на вашей системе функция crypt не поддерживается, то Вы можете использовать пакет UFC-crypt Глэда Майкла, который является паблик домен; пакет был разработан в Дании и следовательно на него не распространяются ограничения, накладываемые экспортными законами США, так как Вы получаете его по ftp с сервера за пределами США.

Date(format,time)

Функция Date используется для отображения времена и даты различными способами. Функция принимает, в качестве аргументов, строку формата и время. Если параметр, задающий время, опущен, будут использоваться текущее время и дата . Параметр time задается в виде целого числа, в секундах начиная с Unix эпохи - 1 января 1970. Строка форматирования используется для указания, какие компоненты даты / времени нужно отображать и как они должны быть отформатированы. Следующие символы распознаются внутри строки формата. Любой неопознанный символ будет выводиться точно, как задан:

  • Y - Год напр. 1995
  • y - год напр. 95
  • M - Месяц напр. Oct
  • m - месяц напр. 10
  • M - Месяц напр. October
  • D - День напр. Fri
  • l - День напр. Friday
  • d - день напр. 27
  • z - День года напр. 299
  • H - Час в 24-м формате напр. 13
  • h - Час в 12-м формате напр. 1
  • i - Минуты напр. 5
  • s - Секунды напр. 40
  • U - Секунды с начала эпохи напр. 814807830 + A - AM/PM
  • a - am/pm
dbList()

dbList выводит информацию относительно поддержки db, скомпилированной в PHP.

dbmClose(filename)

dbmClose просто закрывает заданный dbm файл. Это также разблокирует все файлы блокировок, так что важно закрыть все открытые dbm файлы.

dbmDelete(filename,key)

dbmdelete удаляет пару ключ/содержимое, задаваемую заданным параметром key.

dbmFetch(filename,key)

dbmFetch возвратит содержимое строки, связанной с данным ключом.

dbmFirstKey(filename)

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

dbmInsert(filename,key,content)

dbmInsert вставляет новую пару данных ключ/содержимое в dbm файл. Если ключ уже существует, вставка потерпит неудачу.

dbmNextKey(filename,key)

dbmNextKey возвращает следующий, после заданного, ключ. Вызывая dbmfirstkey(), и сопровождая этот вызов последовательными обращениями к dbmnextkey() можно просмотреть каждую пару ключ/содержимое dbm файла.

dbmOpen(filename,mode)

dbmOpen() открывает dbm файл. Первый аргументпредставляет собой полное имя файла dbm, с указанием пути, а второй - режим, в котором открывается файл; режим может быть одним из: "r", "n" или "w" толко для чтения, создание нового (подразумевается запись) и для записи соответственно. Если используется поддержка ndbm , ndbm фактически создаст файлы filename.dir и filename.pag. Gdbm использует только один файл, поддерживается как обычный ascii файл, и Berkeley libdb создает файл filename.db. Обратите внимание, что PHP организует свою собственную блокировку файла, в дополнение к любым блокировкам, которые могут быть выполнены непосредственно библиотекой dbm. PHP не удаляет файлы блокировок с расширением .lck, которые он создает. Это просто для того, чтобы использовать для файлов блокировок фиксированные иноды. Для более подробной информации относительно dbm файлов, можно обратиться к справочной системе Unix, или получить GNU gdbm с ftp://prep.ai.mit.edu/pub/gnu.

dbmReplace(filename,key,content) dbmReplace похожа на функцию dbminsert(); единственое различие состоит в том что, если ключ уже существует, старое содержимое строки будет заменено на новое.

DecBin(number)

DecBin возвращает строку, содержащую двоичное представление заданного, в качестве аргумента, числа. Самое большое число, которое может быть преобразовано ограничено длиной 31 бит или 4294967295 в десятичном представлении. См. также функцию BinDec().

DecHex(number)

DecHex преобразовывает десятичное число в шестнадцатеричный строку. См. также функцию HexDec().

DecOct(number)

DecOct преобразует десятичное число в восьмиричное число. См. также OctDec().

doubleval(variable) Doubleval возвращает значение переменной типа double (с плавающей запятой) . См. также функции strval() и intval().
Echo [format_string] expression [, expression [,...]]

Echo не является функцией. То есть, Вы не помещаете скобки вокруг передаваемых параметров. Используется, для отображения результатов вычисления функций или переменных PHP. Специальные эскейп символы , \n, \r и \t могут использоваться для вывода символов новая строка, возврат каретки и табуляции соответственно. Format_string необязательный аргумент, и если он не задан, не будет выполняться никакого форматирования вывода. Строка формата подобна строке форматирования функции printf в C . См. справку printf для более полной информации. Одно командой echo может быть выведено до 5 выражений. Если Вы попробуете вывести больше, то получите от синтаксического анализатора сообщение об ошибке. Обратите внимание, что тип выражений не важен. Выражения "автомагически" преобразуются к нужному типу в соответствии с типамт, определенными строкой форматирования, если таковая присутствует. Если Вы желаете отформатировать что-либо и присвоить отформатированную строку переменной, то вместо того чтобы выводить это, используйте функцию sprintf().

Следующие преобразования допустимы,

%d %i
Выводит десятичное число со знаком.
%o
Выводит восьмеричное число.
%u
Выводит десятичное число без знака.
%x %X
Выводит шестнадцатеричное число.
%f
Выводит число с плавающей запятой.
%e %E
Выводит число с плавающей запятой в экспоненциальной форме.
%g %G
Выводит число с плавающей запятой в экспоненциальном формате или нормальной записи.
%c
Выводит одиночный символ.
%s
Выводит строку символов.
%%
Выводит знак процента.

Следующие флаги поддерживаются.

'-'
Выровнивание по левому краю поля.
'+'
Гарантирует, что целые числа имеют знак (со знаком плюс/минус).
' '
Тоже что и '+', но вместо знака "плюс" используется пробел .
'#'
Выводит префиксы для шестнадцатеричных и восьмеричных чисел.
'''
Разделяет цифры на группы (обычно группы отделяются запятой по три).
'0'
Заполнение поля нулями.
Все эти флажки зависят от того, поддерживает ли их ваша функция printf библиотеки C (''', например, является расширением GNU).

Для большинства преобразований можно задавать ширину поля и точность, как показано в файле demo_echo.html в каталоге /examples. Задавать модификаторы типа не обязательно, и, фактически, PHP будет жаловаться, если модификатор типа не имеет смысла (который почти всегда имеет место). PHP будет жаловаться относительно (и отказываться воспринимать) чего-либо, что не распознать. Любые дополнительные параметры, которые не требуются строкой форматирования, игнорируются.

End(variable)

End перемещает внутренний указатель массива для данной переменной к последнему элементу массива и возвращает значение этого элемента. Это бывает полезно для перебора элементов ассоциативного массива в обратном порядке. См. также Reset() и Prev(). Следующий пример перебирает ассоциативный массив в обратном порядке:

       <?
         Reset($array);
         $first_key = key($array);
         End($array);
         $k = key($array);
         while($k != $first_key); 
            echo $array[$k];
            prev($array);
            $k = key($array); 
         endwhile;
         echo $array[$k];
       >
EscapeShellCmd(string)

EscapeShellCmd экранирует любые символы в строке, который могут использоваться специальным образом при задании выполняющихся команд шелл-оболочки. Эта функция должна использоваться, чтобы удостовериться, что любые специальные символы, получаемые из ввода пользователя экранируются прежде чем будут переданы функциям Exec() или System(). Стандартное использование этой функции может быть:

       <?system(EscapeShellCmd($cmd))>
Eval(string)

Eval берет содержимое строки аргумента и, обрабатывает, это подобно мини PHP/FI скрипту. Выполняется как отдельный скрипт PHP/FI. Любые установки или обращения к переменным внутри eval будут из глобальной области видимости текущего контекста оператора eval. Для строковых аргументов выполняется подстановка переменных замена выполнена на параметрах ряда, таким образом если в выражении нужно использовать переменные , то их нужно экранировать. Несколко примеров:

       $a = "echo phpversion();";
       eval($a);

       eval("echo phpversion();");

       eval("$a=1; echo $a;");
Exec(command_string [, array [,return_var]])

Exec выполняет заданную команду unix, однако ничего при этом не выводит. Функция просто возвращает последнюю строку из результатов выполнения команды. Если нужно выполнить команду и получить все данные, переданные непосредственно, без какого-либо вмешательства обратно, используйте функцию PassThru(). Если в качестве аргумента задан массив , то этот массив будет заполнен каждой строкой вывода команды unix, начиная с элемента [0]. Если наряду с параметром массива присутствует параметр return_var, то в эту переменную будет записан код возврата выполненной команды unix. Обратите внимание, что, если Вы собираетесь позволить чтобы данные, поступающие из ввода пользователя, были переданы этой функции, то нужно использовать функцию EscapeShellCmd(), чтобы удостовериться, что пользователи не смогут передать для выполнения в системе произвольных команд. См. также функцию System().

Exit

Команда Exit используется, для завершения синтаксического анализа сразу же, как только будет проанализирован этот тэг.

Exp(arg)

Exp возвращает e, с степени arg.

fclose($fd)

fclose() закрывает файл, открытый fopen(). Аргумент - на файловый дескриптор, который возвращается вызовом fopen().

feof($fd)

Feof возвращает истину, если достигнут конец файла, заданного параметром указателя на файловый дескриптор.

fgets($fd,bytes)

fgets() считаетыват строку из файла, открытого fopen(). Аргументы - файловый дескриптор, возвращаемый fopen() и максимальное число байт для считывания. Чтение оканчивается, когда считано максимальное число байтов, или на конце строки. Эта функция подобна вызову fgets() в C. См. также fputs().

fgetss($fd,bytes)

Идентична fgets(), за исключением того, что эта функция пытается удалить любые тэги HTML или тэги PHP/FI, при чтении файла.

$array = File(filename)

File читает весь файл и возвращает массив, каждый элемент которого, содержит строку файла, индекс в массиве начинается с 0.

fileAtime(filename)

FileAtime возвращает время последнего доступа к данным. Если файл не существует, или если к нему нельзя было обратиться, эта функция возвращает -1.

fileCtime(filename)

FileCtime возвращает время последнего изменения. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1.

fileGroup(filename)

FileGroup возвращает идентификатор группы владельца файла. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1.

fileInode(filename)

FileInode возвращает inode файла. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1.

fileMtime(filename)

FileMtime возвращает время последнего изменения данных. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1

fileOwner(filename)

FileOwner возвращает универсальный идентификатор владельца файла. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1.

filePerms(filename)

FilePerms возвращает биты доступа файла. Это - st_mode поле структуры stat Unix. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1.

fileSize(filename)

fileSize возвращает размер файла в байтах. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1.

$fp = fopen(filename,mode)

Fopen() открывает файл и возвращает указатель на дескриптор файла. Если файл не существует, или если к нему нельзя было обратиться по какой-либо другой причине, эта функция возвращает -1. Функция подобна вызову C fopen(). Параметр filename - относительный или абсолютный путь к файлу, который нужно открыть, и параметр режима - один из, "r", "r+", "w", "w+", "a", "a+". См. справку Unix по вызову fopen() для более полной информации. См. также описание функции popen(). См. также описание функции fclose().

Пример:

       $fp = fopen("/home/rasmus/file.txt","r");
fputs(fp,string)
Fputs() записывает строку в файл, открытый функцией fopen(). Параметры - указателя дескриптора файла, который возвращается fopen() и строка для записи. Обратите внимание, что аргумент строка может содержать специальные символы, \n, \r и \t, для вывода новой строки, возвраты каретки и табуляции соответственно. См. также fgets().
fseek(fp,pos)

Fseek() устанавливает указатель файла, заданный параметром $fd; Значением fp является значение возвращаемое вызовом fopen(). Указатель файла установливается от начала файла плюс смещение, определяемое параметром pos. См. также ftell() и rewind().

fp = fsockopen(hostname,port)

Fsockopen() открывает cокет соединение и возвращает указателя дескриптора файла. Этот указатель дескриптора файла может использоваться функциями fgets, fputs и fclose. Параметры - адрес сервера и номер порта. Возвращаемые значения: -3, если нелзя было создать сокет, -4, если потерпела неудачу попытка поиска адреса соответствующего имени сервера hostname , -5 если, в соединение отказано, или закончилось тайм-аутом, -6 если потерпело неудачу вызов фактический вызов fdopen() или -7, если потерпел неудачу вызов setvbuf(). Если номер порта равен 0, то параметр hostname будет рассматриваться как имя файла для сокета файлового пространства имен (File NameSpace), если ваша операционная система их поддерживает.

pos = ftell(fp)

Ftell() возвращает позицию указателя файла, заданного параметром fp, возвращаемого обращением к fopen(). Позиция может быть использована в качестве параметра для fseek(). См. также fseek() и rewind().

getAccDir()

GetAccDir возвращает имя каталога, где хранятся файлы PHP конфигурации доступа. Имена файлов конфигурации доступа исходят из числового идентификатора пользователя, чьи обращения к конфигурации они представляют.

GetEnv(string)

GetEnv возвращает значение значения переменной среды, заданной строкой. Обычно эта функция не используется, так как переменные окружения доступны PHP/FI непосредственно. Если сделанна ссылка на переменную, которая не найдена во внутренней таблице идентификаторов, то автоматически просматривается среда окружения. GetEnv нужно использовать, когда необходимо гарантировать, что переменная среды окружения не будет перезаписана поверх нормальной переменной PHP/FI. Механизмы защиты, которые полагаются на переменные, определяемые http-сервером, таких как REMOTE_ADDR и REMOTE_HOST, должны получать значения этих переменных, используюя GetEnv вместо того, стобы непосредственно обращаться к ним (напр. $REMOTE_ADDR), чтобы избежать, что кто-либо, составив фальшивую форму и передав данные на ваш сервер, сможет обойти какой-либо механизм защиты, который мог-бы быть у вас.

getHostByName(domain_name)

GetHostByName преобразует переданное имя домена в IP адрес в формате nnn.nnn.nnn.nnn.

getHostByAddr(ip_address)

GetHostByAddr преобразует данный IP адрес в формате nnn.nnn.nnn.nnn в полное имя домена.

getLastAccess()

GetLastAccess возвращает дату и время последнего обращения к странице, в формате unix. Это значение может быть передано, впоследствии функции Date() для форматирования.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

getLastbrowser()

GetLastBrowser возвращает строку идентификации броузера, который использовался пользователем, при последнем обращении к текущей странице.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

getLastEmail()

GetLastEmail возвращает адрес Электронной почты пользователя, который последним оброщался к текущей странице.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

getLastHost()

GetLastHost возвращает имя серверас которого было последнее обращени к текущей странице.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

getLastMod()

GetLastMod возвращает дату и время, в формате unix, времени последней модификации страницы. Это значение может быть передано функции Date() для форматирования.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

getLastref()

GetLastRef возвращает URL, с которого было последнее обращение к странице.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

getLogDir()

GetLogDir возвращает каталог, в котором могут быть найдены журналы регистрации PHP . Фактические файлы журналов регистрации находятся внутри этого каталога. Каждый подкаталог представляет собой числовой идентификатор пользователя пользователя, которому принадлежат файлы журнала регистрации . Затем внутри каждого каталога находится ряд файлов в виде dbm, каждый с числовым inode файла, который они представляют как первичная компонента в имени файла.

getMyInode()

GetMyInode возвращает числовой inode текущего HTML файла.

getMyPid()

GetMyPid() возвращает текущий идентификатор процесса php.

getMyUid()

GetMyUid возвращает числовой идентификатор пользователя владельца текущего HTML файла.

getRandMax()

GetRandMax возвращает максимально возможное случайное число, которое может вернуть функция Rand. Если возвращенное значение кажется, не совсем точным, смотрите файл php.h в дистрибуции PHP для более подробной информации.

getStartLogging()

GetStartLogging возвращает время и дату в, формате Unix, времени начала регистрации на текущей странице. Более точные значения получаются при использовании регистрации на основе mSQL, так как маркер времени хранится в каждом из файла регистрации. Для dbm-регистрации возвращается время создания каталог файлов регистрации пользователем.

getToday()

GetToday возвращает общее число обращений к текущая странице с 12 пополуночи локального времени.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

getTotal()

GetTotal возвращает общее число обращений к текущая странице , с тех пор как была начата регистрация доступа для страницы.
Эта функция доступна, только если PHP компилировался с опцией регистрации доступа.

GetType(variable)

GetType возвращает тип переменной. Возвращаемое значение это строка, содержащая одно из значений: "integer", "double" или "string". См. также функци. SetType().

gmDate(format,time)

GmDate идентична функции Date если, за исключением того факта, что используется время по Гринвичу.

Header "header_string"

Команда Header используется в начале HTML файла, для того чтобы послать необработанные строку HTTP заголовка. См. Спецификацию HTTP для более полной информации относительно необработанных заголовков http.

HexDec(hex_string)

HexDec преобразовывает шестнадцатеричный строку в десятичное число. См. также функцию DecHex().

HtmlSpecialChars(string)

HtmlSpecialChars преобразовывает любые символы c ascii кодами со 160 по 255 в параметре string к их соответствующему HTML представлению. Функция возвращает преобразованную строку.

ImageArc (im, cx, cy, w, h, s, e, col)

ImageArc выводит частичный эллипс, с центром в cx, cy (верхний левый угол имеет координаты - 0,0) в изображение, представленное im. W и h определяют ширину и высоту эллипса соответственно, в то время как начальная и конечная точки задаются в градусах, параметрами s и e.
Эта функция доступна только, если в PHP включена поддержка GD .

ImageChar(im, size, x, y, c, col)

ImageChar выводит символ c в изображение, заданное переменной im с координатами x, y (верхний левый угол - 0,0) цветом col. Параметр размера может быть 1, 2, 3, 4 или 5 задавая размер шрифта, который нужно использовать. 1 самый маленький, и 5 самый большой.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageCharUp(im, size, x, y, c, col)

ImageCharUp выводит символ c вертикально, в изображение, заданное im с координатами x, y (верхний левый угол - 0,0) цветом col . Параметр размера может быть 1, 2, 3, 4 или 5 задавая размер шрифта, который нужно использовать. 1 самый маленький, и 5 самый большой.
Эта функция доступна только, если в PHP включена поддержка GD.

col = ImageColorAllocate(im, red, green, blue)

ImageColorAllocate возвращает идентификатор цвета, представленного RGB составляющими компонентами цвета. Параметр im - значееие возвращаемое функцией ImageCreate. ImageColorAllocate должна вызываться для создания каждого цвета, который используется в изображении, представляемом im.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageColorTransparent(im, col)

ImageColorTransparent устанавливает прозрачный цвет для изображения im в значение col. Im - идентификатор изображения, возвращаемый фнкцией ImageCreate, и col - идентификатор цвета, возвращаемого ImageColorAllocate.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageCopyResized(dst_im, src_im, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH)

ImageCopyResized копирует прямоугольную область одного изображения в другое изображение. Dst_im - изображение назначения, src_im - идентификатор исходного изображения. Если координаты, ширина и высоты исходного изображения и изображения получателя различаются, то будет выполненно соответственно растяжение, или стягивание фрагмента. Координаты задаются относительно верхнего левого угла. Эта функция может быть использована для копирования области внутри одного и того же изображения (если dst_im тоже что и src_im) но если регионы накладываются, то результаты будут непредсказуемы.
Эта функция доступна только, если в PHP включена поддержка GD.

im = ImageCreate(x_size, y_size)

ImageCreate возвращает идентификатор изображения, представляющий пустое изображение с размерами x_size и y_size.
Эта функция доступна только, если в PHP включена поддержка GD.

im = ImageCreateFromGif(filename)

ImageCreateFromGif возвращает идентификатор изображения, представленный изображением, полученным из заданного файла filename.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageDestroy(im) ImageDestroy освобождает всю память, связанную с изображением im. Im - идентификатор изображения, возвращенный функцией ImageCreate.
Эта функция доступна только, если в PHP включена поддержка GD.
ImageFill(im, x, y, col) ImageFill выполняет заливку изображения, заливка начинается точки с координатами x, y (верхний левый угол - 0,0), цветом col в изображении im.
Эта функция доступна только, если в PHP включена поддержка GD.
ImageFilledPolygon(im, points, num_points, col)

ImageFilledPolygon создает заполненный многоугольник в изображении im. Points - массив PHP, содержащий вершины многоугольника. То есть. Points[0] = x0, points[1] = y0, points[2] = x1, points[3] = y1, и т.д. num_points - общее количество вершины.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageFilledRectangle(im, x1, y1, x2, y2, col)

ImageFilledRectangle создает заполненный цветом col прямоугольник в изображении im, заданный верхней левой координатой x1, y1 и заканчивающийся правой нижней координинатой x2, y2. 0,0 - верхний левый угол изображения.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageFillToBorder(im, x, y, border, col)

ImageFillToBorder выполняет заливку, причем цвет у границы, изображения определяется этой границей. Отправная точка для заливки - x, y (верхний левый угол- 0,0) и область заполняется цветом col.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageGif(im [,filename])

ImageGif создает файл GIF, с именем filenam из изображения im. Параметр im - значение возвращенное функцией ImageCreate. Параметр имени файла необязательный, и если он опущен, будет возвращен непосредственно необработанный поток изображения. Посылая content-type image/gif, с использованием функции Header(), Вы можете создать программу PHP/FI, которая возвращает GIF изображение непосредственно используя эту функцию.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageInterlace(im, interlace)

ImageInterlace включает, либо выключает бит чередования (interlace). Если interlace равен 1, изображение im будет отображено в режиме interlace, и если interlace - 0, бит чередования выключается. Эта функция доступна только, если в PHP включена поддержка GD.

ImageLine(im, x1, y1, x2, y2, col)

ImageLine рисует линию из точки x1, y1 до точки x2, y2 (верхний левый угол - 0,0) в изображении im цветом col.
Эта функция доступна только, если в PHP включена поддержка GD.

ImagePolygon (im, points, num_points, col)

ImagePolygon создает многоугольник в изображении im. points - массив PHP, содержащий вершины многоугольника. То есть. points[0] = x0, points[1] = y0, points[2] = x1, points[3] = y1, и т.д. Num_points - общее число вершин.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageRectangle (im, x1, y1, x2, y2, col)

ImageRectangle создает прямоугольник цвета col в изображении im начиная с верхнего левого угла с координатой x1, y1 и заканчивая правым нижним углом с координатами x2, y2. 0,0 - верхний левый угол изображения.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageSetPixel(im, x, y, col)

ImageSetPixel выводит пиксель в точке x, y (верхний левый угол - 0,0) в изображении im цветом col.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageString (im, size, x, y, s, col)

ImageString выводит строку s в изображение, заданное аргументом im, координатами x, y (верхний левый угол - 0,0) и цветом col. Параметр size может быть 1, 2, 3, 4 или 5, задавая размер шрифта, который нужно использовать. 1 самый маленький, и 5 самый большой.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageStringUp (im, size, x, y, s, col)

ImageStringUp выводит строку s вертикально в изображение, заданное im, координатами x, y (верхний левый угол - 0,0) и цветом col. Параметр size может быть 1, 2, 3, 4 или 5, задавая размер шрифта, который нужно использовать. 1 самый маленький, и 5 самый большой.
Эта функция доступна только, если в PHP включена поддержка GD.

ImageSX(im)

ImageSX возвращает ширину изображения, заданного im.

ImageSY(im)

ImageSY возвращает высоту изображения, заданного im.

Include filename Команда Include может использоваться для того, чтобы вставить другие файлы в текущий html файл. Это чрезвычайно удобно для заголовков и нижних колонтитулов, которые возможно должны быть включены в сотни HTML файлов. При использовании команды include если потребуется изменить заголовок или колонтитул, то вам нужно будет изменить заголовок или файл с нижними колонтитулами только одном месте. Так как полный синтаксический анализ PHP выполняется и для включенного файле, то вы можете также использовать команду include, чтобы включить общие для всех фрагменты программ PHP. Это что-то вроде примитивной общедоступной библиотеки программ, которые могут быть вызваны из вашего HTML файла.
intval(variable)

Intval возвращает значение переменной, приведенное к типу long integer. См. также функции strval() и doubleval().

IsSet(variable)

Функция IsSet возвращается 1, если данная переменная определена, и 0, если нет.

Key(variable)

Key возвращает ключ текущего элемента массива. Текущий элемент определяется позицией указателя массива для данной переменной. Этим указателем массива можно управлять с помощью функций Reset(), End(), Next(), и Prev(). Эта функция в основном используется для определения значения ключа для элемента ассоциативного массива, хотя она также будет работать и для нормального массива .

Link(target,link)

Link() создает жесткую связь. См. функцию Symlink() для создания символических связей. См. также функции ReadLink и LinkInfo.

LinkInfo(path)

LinkInfo возвращает st_dev поле структуры stat в Unix, возвращаемой системным вызовом lstat . Эта функция используется, чтобы проверить, существует ли действительно связь (указанная аргументом path), (используется тот же самый метод, что и в макрокоманде S_ISLNK, определенной в stat.h). Возвращает -1 в случае ошибки.

Log(arg)

Файл регистрации возвращает натуральный логарифм аргумента.

Log10(arg)

Log10 возвращает логарифм аргумента по основанию 10.

Max(array)

Max возвращает максимальное значение массива PHP. То есть. Эта функция просмотрит весь массив для поиска максимального элемента. Если это массив строк, возвращаемая строка это строка, которая будет последней в алфавитном порядке в массиве, если он был сортирован.

Microtime() Microtime() возвращает строку "msec sec" где sec - число секунд, отсчитанных от 00:00 , 1 января, 1970 по Гринвичу, а msec - микросекундная часть (как доля секунды). Напр. "0.87633900 825010464".
Эта функция доступна только на операционных системах, которые поддерживают системный вызов gettimeofday() .
Min(array) Min возвращает минимальное значение массива PHP. То есть она просмотрит весь массив, для поиска минимального элемента. Если это массив строк, возвращаемая строка представляет собой строку, которая была бы первой в массиве по алфавиту, если этот массив сортирован.
MkDir(dir,mode)

MkDir создает каталог. Параметр mode должен быть задан в восьмеричном представлении.

MkTime(hour,min,sec,mon,day,year)

MkTime возвращает время в представлении Unix (long integer) формат, которое соответствует дате и времени, заданными аргументами. Параметры могут быть опущены, в этом случае данная компонента установливается к текущему значению согласно текущему местному времени и дате. Эти параметры могут опускаться только справа налево. То есть допутсима запись MkTime(hour, min, sec), а MkTime(mon, day, year) нет.

$result = msql($database,$query)

Msql посылает запрос mSQL. Параметры - имя базы данных, строка запроса. То есть. <? Msql ("MyDatabase", "select * from table") >. Возвращаемое значение из этой функции - идентификатор результата, который используется для того, чтобы можно было обратиться к результатам из других функций msql. Идентификатор результата - положительное целое число. Функция возвращает 0, в случае если не был создан никакой идентификатор результата. Дело обстоит таким образом для любых запросов, которые ничего не возвращают, что - нибудь, типа create, update, drop, insert и delete. Если происходит ошибка функция возвращает -1. Строка, описывающая ошибку будет помещена в переменную $phperrmsg, и если функция не была вызвана как @msql() то эта строка ошибки будет также выведена.
Эта функция доступна только, если в PHP разрешена поддержка mSQL .

msql_connect($hostname)

Msql_Connect задает имя сервера или его IP адрес, на котором постоянно находится сервер базы данных mSQL . Эта функция эквивалентна функции msqlConnect() в mSQL C API. Одно различие между этой функцией и ее эквивалентом С API - то, что, если функция не вызывается, то по умолчанию устанавливается соединение с локальным серверм при первом обращении к функции msql(). И, не имеется никакой потребности в функции msql_close, так как в любой момент может быть активно только одно соединение. Если в файле сделано второе обращение к msql_connect (), то соединение с первым сервером втоматически закрывается. Чтобы явно соединиться с msql дэймоном на локальном сервере, используйте: <? Msql_connect ("localhost") >
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_CreateDB($database)

Msql_CreateDB создает заданную базу данных.
Эта функция доступна только, если в PHP разрешена поддержка mSQL .

msql_dbName($result,$i)

Msql_dbName возвращает имя базы данных, сохраненное в позиции $i результата, возвращенного функцией msql_ListDbs(). Msql_NumRows() функция может быть использована, для того чтобы определить, сколько доступно имен баз данных.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_DropDB($database)

Msql_DropDB удаляет заданную базу данных mSQL. Используйте эту функцию с осторожностью, поскольку все данные в базе данных будут потеряны.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_FieldFlags($result,$i)

Msql_FieldFlags возвращает флаги для заданного поля. В настоящее время это может быть - "not null", "primery key", комбинация из этих двух или "" (пустая строка).
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_FieldLen($result,$i)

Msql_FieldLen возвращает длину заданного поля.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_FieldName($result,$i)

Msql_FieldName возвращает имя заданного поля. Параметры функции - идентификатор результата и индекс поля. То есть. Msql_FieldName($result, 2); возвратит имя второго поля в идентификаторе результата result.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_FieldType($result,$i)

Msql_FieldType подобна функции msql_FieldName() . Параметры идентичны, но возвращается тип поля. Это будет один из "int", "char" или "real".
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_FreeResult($result)

Msql_FreeResult должна вызваться только, если вы волнуетесь, что ваша программа привыполнении занимает слишком много памяти. Вся занимаемая память будет автоматически освобождена по окончании выполнения программы. Но, если Вы уверены, что не данные результата нигде в программе более не понадобятся, Вы можете вызвать msql_freeresult с идентификатором результата, в качестве аргумента, и ассоциированная с ним память будет освобождена.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

$result = msql_ListDBs()

msql_ListDBs возвратит указатель результата, содержащий список доступных баз данных, из текущего дэймона mSQL. Используйте функцию msql_dbName(), чтобы перебрать этот массив указателя результата.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

$result = msql_Listfields ($database,$tablename)

msql_listfields отыскивает информацию относительно заданного имени таблиц tablename. Параметры - имя базы данных, и имя таблицы. Возвращается указатель результата, который может использоваться с msql_fieldflags, msql_fieldlen, msql_fieldname, msql_fieldtype. Идентификатор результата - положительное целое число. Функция возвращает -1, если произошла ошибка. Строка, описывающая ошибку будет помещена в $phperrmsg, и если функция вызывалась не как @msql(), будет также выведена.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

$result = msql_ListTables($database)

msql_ListTables берет имя базы данных в качестве аргумента, и возвращает указатель результата очень похожий на тот что вовращает msql(). msql_TableName() может быть использована для того, чтобы извлечь фактические имена таблиц из указателя результата.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_NumFields($result)

Msql_NumFields возвращает количество полей в результате result. Параметр - идентификатор результата, возвращенный функцией msql().
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_NumRows($result)

Msql_NumRows просто возвращает количество строк в результате result. Параметр - идентификатор результата, возвращенный функцией msql().
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_RegCase(string)

Msql_RegCase берет строку в качестве аргумента и преобразовывает ее в регулярное выражение, необходимое, чтобы послать запрос mSQL, для получения результат независимого от регистра. Она преобразует строку типа "abc" в "[Aa][Bb][Cc]".
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_Result($result,$i,field)

Msql_Result отображает поле из возвращенной записи. Параметры - идентификатор результата, возвращенный функцией msql(), целое число, которое является индексом записи, которую нужно просмотреть и имя поля. Параметр поля поддерживает "table.field" синтаксис для обработки обьединения.Эту функцию возможно лучше всего рассмотреть на примере:

<?
  $name = "bob";
  $result = msql($database,"select * from table where firstname='$name'"); 
  $num = msql_numrows($result); 
  echo "$num records found!<p>";
  $i=0;
  while($i < $num);
     echo msql_result($result,$i,"fullname");
     echo "<br>";
     echo msql_result($result,$i,"address");
     echo "<br>";
     $i++;
  endwhile;
>

Эта программа подключается к серверу mSQL на локальной машине, устанавливает переменную name в значение bob и посылает запрос, который запрашивает информацию о всех полях из таблицы, в которых поле firstname установлено в значение bob. Затем программа отображает количество найденных записей, и затем в цикле проходит по всем найденным записям и отображает значения полей fullname и address для каждой записи. Как можно увидеть, было бы тривиально добавить, что HTML тэги вокруг напечатанных полей, для форматирования результатов в таблицу или любым желаемым способом. Обратите внимание, что нет вызова connect(). Msql_connect должна быть вызвана только если желательно подключиться к базе данных на удаленном сервере.
Эта функция доступна только, если в PHP разрешена поддержка mSQL.

msql_TableName($result,$i)

Msql_TableName берет указатель результата, возвращенный функцией msql_ListTables(), также как целочисленный индекс и возвращает имя таблицы. Функция Msql_NumRows() может использоваться для определения количества таблиц в указателе результата. Пример:

<?
  $result = msql_listtables("dbname");
  $i=0;
  while($i <  msql_numrows($result));
     $tb_names[$i]=msql_tablename($result, $i);
     echo $tb_names[$i];
     echo "<BR>";
     $i++;
  endwhile;
>

Эта функция доступна только, если в PHP разрешена поддержка mSQL.
Next(variable)

Next перемещает внутренний указатель массива к следующему элементу массива. Это происходит автоматически, когда к массиву обращаются, используя не-индексированный метод ($array []). Функция возвращает значение нового элемента. Эта функция может использоваться, чтобы переместить указатель, вперед, без необходимости явного обращения к массиву. Можно использовать ее, чтобы просмотреть ассоциативный массив и только выводить значения ключей массива а не фактическое содержимое.

<?
  Reset($array);
  $i=0;
  while($i < count($array)); echo key($array);
    next($array);
    $i++;
  endwhile;
>
OctDec(octal_number)

OctDec преобразовывает восьмиричное число в десятичное число. См. также DecOct().

openDir(directory)

OpenDir открывает заданный каталог и перемещает внутренний указатель на начало каталога. Элементы каталога могут быть считаны, используя функцию readDir, и каждый открытый каталог должен быть закрыт функцией closeDir.

Ord(arg)

Ord возвращает ASCII значение первого символа параметра.

PassThru(command_string [,return_var]) PassThru() похожа на функцию Exec(), которая выполняет команду Unix. Если параметр return_var присутствует, то в него будет помещен код возврата команды Unix. Эта команда должна использоваться вместо Exec или System, когда вывод команды Unix - представляет собой двоичные данные, которые должны быть переданы непосредственно обратно броузеру.Общее применение для этой функции может быть, если нужно выполнить что-либо вроде pbmplus утилит, которые могут непосредственно выводить поток изображения. Устанавливая content-type в image/gif и вызывая затем программу pbmplus, для того чтобы вывести gif, Вы можете создавать программы PHP/FI, которые непосредственно выводят изображения .
pclose(fp)

Pclose закрывает канал открытый с использованием функции popen().

pg_Close(connection_id)

Pg_Close закрывает соединение с базой данных Postgres95 , связанной с данным идентификатором соединения.
Эта функция доступна только если в PHP включена поддержка Postgres95.

$connection = pg_Connect(host, port, options, tty, dbname)

Pg_Connect открывает соединение с базой данных Postgres95. Каждый из параметров представляет собой строку в кавычках, включая и номер порта. Параметры options и tty необязательны и могут быть пустыми строками. Эта функция возвращает идентификатор соединения connection. Этот идентификатор необходим другим функциям Postgres95. Можно иметь множество открытых соединений сразу. Эта функция вернет 0 при ошибке.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_DBname(connection_id)

Pg_DBname возвратит имя базы данных Postgres95, с которой связан данный идентификатор соединения.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_ErrorMessage(connection_id)

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

$result = pg_Exec(connection_id, query_string)

Pg_Exec пошлет оператор SQL к базе данных Postgres95, определенной параметром connection_id. Connection_id должен быть легальный идентификатор, который был возвращен pg_Connect. Значение, возвращаемое этой функции, - идентификатор, который нужно использовать, для обращения к результатам других функций Postgres95. Эта функция возвращает 0 при ошибке, 1 когда команда выполненна правильно, но не ожидается возвращения данных (например команды insert или update). Обратите внимание, что select, который тоже не возвращают никаких данных, будет возвращать верный результат больше 1.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_FieldName(result_id, field_number)

Pg_FieldName возвращает имя поля, занимающего данный номер столбца в заданном идентификаторе результата Postgres95. Поля, нумеруются начиная с 0.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_FieldPrtLen(result_id, row_number, field_name)

Pg_FieldPrtLen возвращает фактическую, печатаемую, длину (число символов) заданного значения в результате Postgres95. Строки нумеруются начиная с 0. Этот функция возвращает -1 при ошибке.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_FieldNum(result_id, field_name)

Pg_FieldNum возвращает номер слота столбца, который соответствует к field_name в данном идентификаторе результата Postgres95. Поля нумеруюются с 0. Функция возвращает -1 при ошибке.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_FieldSize(result_id, field_name)

Pg_FieldSize возвращает размер памяти (в байтах) поля field_name в данном идентификаторе результата Postgres95. Размер поля 0 указывает поле переменной длины. Эта функция возвращает -1 при ошибке.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_FieldType(result_id, field_number)

Pg_FieldType возвращает строку, содержащую имя типа данных поля для заданного идентификатора результата Postgres95. Поля нумеруются с 0.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_FreeResult(result_id)

Pg_FreeResult должна вызваться только, если вы волнуетесь что программа занимает слишком много памяти во время выполнения. Вся память занимаемая результатами будет автоматически освобождена по окончании программы. Но, если Вы уверены, данные результаты нигде в программе больше не потребуются, Вы можете вызвать pg_freeresult с идентификатором результата, в качестве параметра и связанная с результатом память будет освобождена.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_GetLastOid()

Pg_GetLastOid может быть использована для того, чтобы отыскать Oid, присвоенный вставляемому кортежу, если последняя команда, посланная через pg_Exec была SQL insert. Эта функция возвращает положительное целое число, если есть справедливый Oid, и -1, если произошла ошибка, или последняя команда, посланная через pg_Exec была не insert.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_Host(connection_id)

Pg_Host возвращает имя сервера, с которым соединен данный идентификатор соединения Postgres95
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_NumFields(result_id)

Pg_NumFields возвращает количество полей (столбцов) в результате Postgres95. Параметр - идентификатор результата, возвращенный pg_Exec. Эта функция возвращает -1 при ошибке.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_NumRows(result_id)

Pg_NumRows возвращает число строк в результате Postgres95. Параметр - идентификатор результата, возвращенный pg_Exec. Эта функция возвращает -1 при ошибке.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_Options(connection_id)

Pg_Options возвращает строку, содержащую опции, определенные заданные для данного идентификатора соединения Postgres95.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_Port(connection_id)

Pg_Port возвращает номер порта, с которым соединен данный идентификатор соединения Postgres95.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_Result(result_id, row_number, field name/index)

Pg_Result будет возвращать значения из идентификатора результата, полученного pg_Exec. Row_number и name_field определяют ячейку в таблице, которая будет возвращена в качестве результата. Строки нумеруюются с 0. Вместо того, задавать имя поля, можно задавать индекс поля в виде числа без кавычек. Индексы полей начинаются с 0.

Postgres95 имеет множество встроенных типов и здесь непосредственно поддерживаются только основные. Все формы integer, boolean и oid типов возвращаются как integer. Все виды типов float и real возвращаются как double. Все другие типы, включая массивы возвращаются в виде строк, форматированных также как они форматированы Postgres95 программах 'psql' или 'monitor'.

Поддержка для возврата массивов PHP числовых и строковых данных из результата Postgres95 запланирована на более позднюю дату.
Эта функция доступна только если в PHP включена поддержка Postgres95.

pg_tty(connection_id)

Pg_tty возвращает, имя tty на который выводится отладочная информация Postgres95, для заданного идентификатора соединения.
Эта функция доступна только если в PHP включена поддержка Postgres95.

phpInfo()

PhpInfo выводит ту же самую страницу, которую Вы получите при добавлении "?info" к URL, анализируемый PHP/FI или при выполнении непосредственно php.cgi. Это особенно полезно для отладки программ, в случае когда PHP/FI собран в виде модуля Apache, так как отображается ряд полезных внутренних данных.

phpVersion()

PhpVersion возвращает номер версии выполняющегося в данный момент PHP/FI.

fp = popen(command,mode)

Popen открывает канал к команде и возвращает указатель файла. Этот указатель файла может использоваться функциями fgets, fputs и fclose. Параметры - команда для выполнения и режим открытия файла. Режим может быть или "r" для чтения или "w" для записи. См. справку по библиотечной функции popen в UNIX C для большего количества деталей. Любой файл, открытый с popen () должен быть закрыт, функцией pclose().

Prev(variable)

Prev перемещает внутренний указатель массива для заданной переменной variable к предыдущему элументу массива. Если указатель уже находится в начале списка, указатель установится на первый элемент. Функция возвращает значение нового элемента. Эта функция полезна для просмотра ассоциативных массивов в обратном порядке. См. пример при описании End(), а также см. Next().

PutEnv(string)

PutEnv помещает заданную строку в среду окружения. Не особо полезная, так как локальные переменные окружения затираются снаружи, когда PHP заканчивает работу со страницей, но в некоторых случаях полезна, если где-либо в программе PHP проверяются переменные окружения. Например, если Вы хотите выполнять несколько процессов дэймона mSQL, нужно будет использовать PutEnv, чтобы переключаться между различными соединениями.

QuoteMeta(arg)

QuoteMeta возвращает строку, составленную из arg, в которой любые специальные символы, используемые в регулярных выражениях, экранированы наклонной чертой влево.

Rand()

Rand возвращает случайное число между 0 и RANDMAX. RANDMAX может быть определен функцией getRandMax. Обычно специфический диапазон выбирается, просто применяя оператор модуля к результату.

readDir()

ReadDir читает следующий элемент из текущего открытого каталога. Как только элемент считан, указатель продвигается к следующему элементу в каталоге иследующее обращение к этой функции возвратит следующий элемент каталога. Перед вызовом этой функции используйте openDir, для того чтобы открыть каталог .

ReadLink(path)

ReadLink делает тоже что и функция C readlink и возвращает содержимое символического связи path или -1 в случае ошибки. См. также LinkInfo.

reg_Match(expr,arg[,regs])

Reg_Match возвращает не нуль, если регулярное выражение нашло соответсвтия в строке аргумента. Например, условие, <?if (reg_match (" ^This. * ", "This is an example string")>, будет истинно, так как выражение "^This. *" говорит, что нужно найти слово This в начале строки и затем могут следовать любые символы. Если параметр присутствует regs , то match-регистры, заполняют позиции 0-10 в массиве, заданном параметром regs. Регистр 0 будет всегда содержать полную совпавшую строку. Для подробной информации относительно регулярных выражений, см. раздел регулярные выражения, этого документа.

reg_replace(expr,replace,arg)

Reg_Replace просматривает весь строку параметра и заменяет любые части строки, совпавшие с данным выражением, строкой для замены. Например, в строке, "This is an example string" мы могли бы очень легко заменять каждый пробел на черточку с помощью команды: reg_replace(" ", "-", "This is an example string"). Для более полной информации относительно регулярных выражений, см. раздел регулярные выражения, этого документа.

reg_Search(expr,arg[,regs])

Reg_Search просмотрит всю строку аргумента для любых пар в поисках любых соответствий для заданного регулярного выражения. Если соответствие найдено, она возвратит часть строки, начиная с места соответствия. Если не найдено никаких соответствий , то возвращается строка нулевой длины. Если параметр regs присутствует, то match регистры, заполняются в позициях 0-10 массива, заданного параметром regs. В регистре 0 всегда будет полная строка. Для более полной информации относительно регулярных выражений, см. раздел регулярные выражения, этого документа.

Rename(old,new)

Переименовывает файл из old в new. Подобна функции C rename в Unix.

Reset(variable)

Reset перемещает внутренний указатель массива для заданной переменной типа массив к первому элементу массива и возвращает значение этого элемента. Это полезно для просмотра ассоциативных и не-индексированных массивов. См. также End() и Next(). Следующий пример перебирает ассоциативный массив:

       <?
         Reset($array);
         $i=0;
         while($i < count($array)); 
           echo $array[]; /* pointer automatically moves ahead one */ 
           $i++;
         endwhile;
       >
return(value)

Return возвращает управление из текущего вызова функции и возвращает определенное значение обратно к вызывающему оператору. См. раздел определяемые пользователем функциям для более полной информации.

rewind($fd)

rewind() сбрасывает указатель файла, заданный параметром $fd, который является значением, возвращенным вызовом fopen(). Указатель файла установливается в начало файла. См. также ftell() и fseek().

rewindDir()

RewindDir перемещает указатель текущего каталога назад к началу каталога. Используйте функцию openDir, чтобы открыть каталог перед вызовом этой функции.

RmDir(dir)

RmDir() удаляет данный каталог. См. функцию unlink() для удаления регулярных файлов.

SetCookie(name,value,expire,path,domain,secure)

SetCookie() определяет cookie, который будет послан наряду с остальной частью информации заголовка. Все параметры за исключением name необязательны. Если задан только параметр name, cookie с этим именем name будет удалено от удаленного пользователя. Вы можете также заменять любой параметр на пустую строку ("") чтобы пропустить этот параметр. Параметры expire и secure - целые числа и не могут быть пропущены, указанием пустой строки. Вместо этого используйте нуль (0). Параметр expire - регулярное целое число Unix, задающее время, в таком-же виде, в каком возвращают его функции time() или mktime(). ниже приводятся некоторые примеры:

       SetCookie("TestCookie","Test Value"); 
       SetCookie("TestCookie",$value,time()+3600);  /* expire in 1 hour */ 
       SetCookie("TestCookie",$value,time()+3600,"/~rasmus/",".utoronto.ca",1);

Обратите внимание, что value часть cookie будет автоматически url кодирована, когда Вы посылаете cookie, а когда получаете, автоматически декодируется и присваивается переменной с тем же самым именем что и имя cookie. То есть. Чтобы просмотреть содержимое нашего теста cookie в программе, просто сделайте:

       echo $TestCookie;
SetErrorReporting(arg)

SetErrorReporting устанавливает текущее значение состояния выдачи ошибок, по значению параметра arg. Если ненулевое, ошибки будут выводится, и если 0 не будут. Функция возвращает предыдущую состояние о выводе ошибок . Это - более общий способ отключения вывода сообщений об ошибках, чем, предшествующий функциям символ '@'. См. раздел Подавление вывода сообщений об ошибках при обращении к функциям для более полной информации.

SetLogging(arg)

SetLogging() разрешает или запрещает регистрацию статистики доступа для страницы. Если параметр arg ненулевой, регистрация включена, если нуль выключена.

SetShowInfo(arg)

SetShowInfo() разрешает или запрещает вывод информационных нижних колонтитулов внизу всех страниц, загружаемых PHP. Если параметр arg ненулевой, нижние колонтитулы выводятся, если нуль - нет.

SetType(variable,type)

SetType устанавливает тип переменной. Параметр type - один из, "integer", "double" или "string". См. также функцию GetType().

Sin(arg)

Sin возвращает синус аргумента в радианах. См. также Cos() и Tan().

Sleep(secs)

Sleep выполняет задержку на secs секунд. Подобна функции C sleep() в Unix. См. также функцию USleep().

Sort(array)

Sort используется, чтобы сортировать массив PHP в порядке возрастания. Функция понимает три типа переменных; если массив содержит строки - сортирует в алфавитном порядке, и численно, если массив содержит числа. В случае массива, который содержит, смесь типов, первый тип в массиве определит метод сортировки.

Sprintf(format,arg)

Sprintf возвращает строку, созданную форматированным выводом, определяемого параметрами format и arg. Функция подобна версии команды echo, за исключением того что эта функция только возвращает строку, в то время как echo отображает ее. Она подобна, также, одноименной функции C. Различием является то, что эта версия не воспринимает множество параметров arg. Если нужно форматировать множество параметров в одну строку, просто вызовите sprintf() один раз для каждого параметра. Обратите внимание, что тип параметра не влияет на вывод. Тип параметра - "автомагически" преобразуется, чтобы соответствовать типу, определенному в строке формата.

Sqrt(arg)

Sqrt возвращает квадратный корень параметра.

Srand(integer)

Srand инициализирует генератор случайных чисел. Эта функция принимает любое целое число в качестве аргумента. Можно для выбора инициализационного число, использовать функцию date, чтобы задать текущее количество прошедших секунд, по окончании минуты. Обратите внимание, что эта функция не возвращает значение! Эта функция просто инициализирует генератор случайных чисел для последующих обращений к функции rand(). Напр.

       <?srand(date("s")>
strchr(string,arg)

Strchr и strstr - фактически идентичные функции. Они могут взаимозаменямы при использовании и включены обе в целях завершенности. Они возвращают часть строки параметра, начиная с места, где найдена данная подстрока . Например, в строке, "This is an example string" , вызов: <echo strstr($string, "an ") > возвратил бы строку: "an example string".

strlen(string)

Strlen возвращает длину строки.

strrchr(string, arg)

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

strstr(string,arg)

Strstr и strchr - фактически идентичные функции. Они могут использоваться взаимозаменяемо и включены обе толко для законченности. Они возвращают часть параметра string, начиная с места, где найдена данная подстрока . Например, в строке, "This is an example string" выше, вызов: <? echo strstr ($string, "an")> возвратил бы строку: "an example string".

strtok(string,arg)

Strtok используется для разбивки строки. То есть если есть строка подобно "This is an example string" Вы могли бы разбить эту строку на индивидуальные слова, используя пробел как маркер. Вы использовали бы следующую программу:

       <?
         $string = "This is an example string"; 
         $tok = strtok($string," "); 
         while($tok);
            echo "Word=$tok<br>";
            $tok = strtok(" "); 
          endwhile;
       >

Обратите внимание, что только первое обращение к strtok использует параметр string. Каждое последующее обращение к strtok нуждается только в маркере, поскольку функция отслеживает, где находится в текущей строке. Чтобы начать сначала, или приступить к разбивке новой строки, просто вызовите strtok с параметром string снова, для инициализации.

strtolower(string)

Strtolower преобразует все символы, аргумента string, в символы строчных букв.

strtoupper(string)

Strtoupper преобразует все символы строки в символы верхнего регистра.

strval(variable)

Strval возвращает строковое значение переменной. См. также функции intval() и doubleval().

substr(string, start, length)

Substr возвращает часть заданной строки. Позиция начала задается параметром start. Первая позиция в строке - позиция 0. А параметр length определяет количество символов от позиции начала.

Symlink(target,link)

Symlink() создает символическую связь. См. функцию Link(), для создания жестких связей.

System(command_string [,return_var])

System - подобна команде system() C, в которой выполняется заданная команда unix и выводится результат. Если в качестве второго аргумента задается переменная , то в эту переменную будет записан код возврата выполненной команды unix . Обратите внимание, что, если Вы собираетесь позволять данные, вводимые пользователем были переданны этой функции System, то нужно использовать функцию EscapeShellCmd(), чтобы удостовериться, что пользователи не смогут таким приемом передать на выполнение системе произвольную команду. Если нужно выполнить команду и получить все данные, выводимые командой обратно без любого вмешательства, использйте функцию PassThru(). См. также функцию Exec.

Tan(arg)

Tan возвращает тангенс параметра в радианах. См. также Sin() и Cos()

TempNam(path, prefix)

TempNam возвращает уникальное имя файла, размещенное в каталоге, заданном аргументом path с префиксом имени файла, заданным аргументом prefix. Идентична функции C tempnam() в Unix.

Time()

Time просто возвращает текущее местное время в секундах начиная с периода Unix (00:00:00 январь. 1 1970). Эквивалентна вызову Date("U"). Если нужна степень детализации выше чем по секундам, используйте функцию Microtime.

Unlink(filename)

Unlink удаляет файл с именем filename. Подобна функции C unlink() в Unix. См. функцию RmDir() для удаления каталогов.

UnSet($var)

UnSet сбрасывает значение заданной переменной. В случае массива, очищается весь массив. Обратите внимание, что индивидуальные элементы массива не могут быть сброшены этой командой.

UrlDecode(arg) UrlDecode декодирует строку, закодированную функцией UrlEncode. При типичном использовании, не нужно декодировать URL кодированные строки, так как они автоматически декодируются, когда строки передаются между страницами. Однако, эта функция была включена, для законченности, .
UrlEncode(arg)

UrlEncode кодирует любые символы параметра arg, которые - не входят в множество символов "a-zA-Z0-9_-". Заменяя их %xx, где xx - ASCII значение этих символов в шестнадцатеричном представлении. Возвращается кодированная строка.

USleep(microsecs)

USleep задерживет выполнение на заданное число микросекунд. Подобна функции C usleep() в Unix . См. также функцию Sleep().

Virtual(filename)

Virtual - Apache -специфическая функция, которая является эквивалентом <!-- #include virtual ... --> в mod_include. Она выполняет под-запрос Apache. Это полезно для включения CGI программ или .shtml файлов, или еще чего-либо, что должно быть передано для разбора Apache (для .phtml файлов, лучше использовать директиву <?Include>.


Добавление ваших собственных внутренних функций к PHP/FI

Может случиться так, что набор функций, обеспечиваемых PHP/FI не включает в себя специфическую функцию, в которая может вам потребоваться. Тщательно следуя пунктам, описанным ниже, вы сможете добавить ваши собственные функции PHP/FI.

Прежде, чем Вы начнете хачить внутреннюю организацию PHP/FI, нужно найти копию последней версии Bison. Bison - GNU реализация YACC (Yet Another Compiler Compiler). YACC, который шел с вашей операционной системой, может оказаться, а может и не оказаться достаточно приемлимым, но просто чтобы удостовериться, лучше добыть Bison. Вы можете найти его в ftp://prep.ai.mit.edu/pub/gnu.

Нужно также просмотреть Makefile и включить отладку. Просто разкомментируйте строку DEBUG в файле Makefile. Выходной файл информации отладки определяется переменной DEBUG_FILE в php.h. По умолчанию установлен в /tmp/php.err. Вы можете изменять его, согласно вашим потребностям.

Заключительная вещь которую нужно иметь в виду - то, что php выполняется с тем же идентификатор пользователя что и httpd на вашей системе, если конечно Вы не выполняете, его с установленным битом setuid, и этот пользователь httpd вообще не имеет доступа для записи к различным каталогам. Это означает это, если Вы делаете что-либо, что вызывает php к дампу памяти, Вы можете не получить файл дампа. Простой способ решения состоит в том что нужно сделать каталог, где Вы храните ваш тестовые .html файлы, доступным всем по записи. PHP изменяет текущий каталог на каталог .html файла, который считаетывается, и таким образом отбрасывать корку туда, если сможет.

В последующих шагах мы будем использовать функцию Time(), для иллюстрирации, как добавить функцию.

Шаг 1 - Определение грамматики вашей Функции

если ваша функция принимает от 0 до 6 аргументов, то доступны - предопределенные грамматики. Вы можете пропустить этот шаг.

Грамматика вашей функции определяется в файле parse.raw. Первым делом нужно добавить лексему. Лексема - ключевое слово, из букв верхнего регистра, которое обычно совпадает с именем вашей функции. Все лексемы определены вначале файла parse.raw. Порядок не имеет значения. Затем нужно сформировать фактическое правило грамматики YACC. Рассмотрите существующие правила, и найдите функцию, которая похожа на добавляемую. Имейте в виду, что большинство нормальных функции - стандартные функции, которые считывают параметры из стека выражений. Ваша функция скорее всего будет отнесена к этой группе, в этом случае вам не нужно будет трогать файл parse.raw.

Шаг 2 - Добавление вашей функции к хэш-таблице лексического анализатора

чтобы сделать это, подредактируйте lex.c, и найдите хэш-таблицу вблизи верхней части файла. Найдите строку, static cmd_table_t cmd_table[22][30] = {, которая определяет начало хэш-таблицы. [22] [30] определяет размер 2 мерного массива, который содержит хэш-таблицу. 22 это на единицу большая максимальная длина имени функции, и 30 относится к максимальному числу функций в любом хэш списке. Если Вы превышаете любое из этих ограничений, просто увеличьте их прямо здесь.

Эта хэш-таблица завоевала бы в соревнованиях абсолютное звание самой простой хэш-таблицы во всем мире. Хэш значение - это длина строки имени функции, которую нужно хэшировать. Так для нашего примера Time(), нужно добавить вход для значения хэша 4 . Таким образом мы добавляем следующую строку к хэш-списку для 4:

       { "time",INTFUNC0,UnixTime },

Этот запись отображает строку на лексему INTFUNC0. Вы можете поискать грамматику для лексемы INTFUNC0 в parse.raw, и увидете, что это - общая грамматика для внутреннего вызова функции без параметров. Строка в кавычках, является фактической строкой, которая будет использоваться в .html файлах, для вызова функцию. Имейте в виду, что имена функции PHP/FI регистронезависимы. И заключительный элемент - UnixTime, это реально вызываемая функция.

Шаг 3 - Написание вашей реалбной функции Вы можете фактически писать вашу функцию на любом языке, который вам нравится, лишь бы только он поддерживал соглашение о вызовах для нормальных функций C, и у вас есть выбор создать ли ее объектным файлом или библиотечным файлом, совместимым с компоновщиком на вашей системе. Вообще, мы будем предполагать, что функцию написана на C. Все функции, идущие с PHP/FI, были написаны на C. Функция Time() или UnixTime(), как она вызывается внутри PHP, может быть найдена в файле date.c и выглядит:
       void UnixTime(void) { 
           char temp[32];

           sprintf(temp,"%ld",(long)time(NULL)); 
           Push(temp,LNUMBER);
       }

Обратите внимание, что функция является фунцией void.Это указывает, что она не возвращает ничего. Это может показаться путанным, потому что очевидно функция так или иначе должна возвратить время. Время возвращается, но не как значение возвращаемое функцией. Оно помещается в то, что называется стеком выражений. Стек выражений - просто стек строк и связанных с ними типов. PHP/FI понимает только 3 основных типа переменных: STRING, LNUMBER и DNUMBER. STRING - символьная строка, LNUMBER - длинное целое число, и DNUMBER - значение double или float. В этом примере Time() , значение, которое будет возвращено - время, выраженное в формате Unix (число секунд начиная с января. 1 1970) и - таким образом целое число. Стек выражения принимает только строки, таким образом мы преобразуем, с помощью sprintf, длинное целое число в строку и помещаем это значение в стек, указывая, что это фактически является длинным целым числом с помощью строки: Push(temp,LNUMBER);

Шаг 4 - Добавление прототипа вашей функции в php.h

В нижней половине файла php.h Вы найдете полный список прототипов всех функций в php. Они сгруппированы по файлам, в которых они появляются. Просто добавьте ваш прототип к соответствующему месту в этом файле. В нашего примера Time() будет добавлена следующая строка:

       void UnixTime(void);
Шаг 5 - Компилирование

Вы должны помнить о том что нужно переделывать синтаксический анализатор всякий раз, когда измененяется файл parse.raw. Введите: make parser, чтобы сделать это. Затем сделайте нормальную компиляцию, введя: make, если только это выполнено.

Шаг 6 - Вышлите мне ваши добавления! Если Вы хотите, чтобы ваши функции, были добавленны к следующему выпуску PHP/FI, пошлите их мне. Возможно самый лучший способ это сделать - это с помощью контекстно-зависимого diff. Чтобы сделать это, вам нужна будет копия немодифицированной дистрибуции. Просто сделайте, diff -c, для файлов, которые были изменены, сравнивая их с первоначальными файлми. Пожалуйста не высылайте мне изменения в файле parse.c, так как тот файл генерируется автоматически. Вместо этого высылайте мне различия в файле parse.raw.

Time() - это пример, иллюстрирующий шаги, при добавлении функции. Возможно, что функция, которую Вы захотите добавить будет немного более сложной чем этот пример. Возможно вы захотите передавать параметры вашей функции и манипулировать этими параметрами каким-либо способом. Возможно вы даже захотите чтобы она вызывалась различными способами. Эти понятия будут проиллюстрированы PHP/FI функцией Crypt(). См. также раздел, озаглавленный Замечания по хаканию Кода для несколько большего числа технических деталей относительно написания кода для PHP/FI.

Грамматика Crypt() в parse.raw:

%token CRYPT
        .
        .
        .
    | CRYPT '(' expr ',' expr ')'
        {
            if(GetCurrentState(NULL) || inCase || inElseIf) Crypt(1);
        }
    | CRYPT '(' expr ')'
        {
            if(GetCurrentState(NULL) || inCase || inElseIf) Crypt(0);
        }

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

Другой иллюстрируемый аспект - как фактически представить параметры функции . В большинстве случаев Вы захотите использовать идентификатор expr. Этот идентификатор означает, что параметр - выражение. Выражение может быть литеральное значение, обращение к функции или комбинация многих выражений. См. parse.raw для полного определения грамматики yacc для выражений для большего количества деталей.

Запись Хэш-Таблицы в lex.c:

       { "crypt",CRYPT,NULL },

Обратите внимание, что последний элемент - NULL, в этом случае обращение к функции обрабатывается прямо в parse.raw. Если Вы использовали INTFUNC грамматику, то Вы поместите имя вашей функции вместо NULL. Фактическая функция Crypt находится в crypt.c:

/*
* If mode is non-zero, a salt is expected.
* If mode is zero, a pseudo-random salt will be selected. 
*/
void Crypt(int mode) {
#if HAVE_CRYPT
       Stack *s;
       char salt[8];
       char *enc;
       
       salt[0] = '{PAGE_ROW_TEXT}';
       if(mode) {
               s = Pop();
               if(!s) {
                       Error("Stack error in crypt");
                       return;
               }
               if(s->strval) strncpy(salt,s->strval,2);
       }
       s = Pop();
       if(!s) {
               Error("Stack error in crypt");
               return;
       }
       if(!salt[0]) {
               salt[0] = 'A' + (time(NULL) % 26);
               salt[1] = 'a' + (time(NULL) % 26);
               salt[2] = '{PAGE_ROW_TEXT}';
       }
       enc = (char *)crypt(s->strval,salt);
#if DEBUG
       Debug("Crypt returned [%s]\n",enc);
#endif
       Push(enc,STRING);       

#else
       Error("No crypt support compiled into this version");
#endif
}

Наиболее важный аспект этой функции - это вызов s = Pop(). Параметры для функции должны быть вытолкнуты из стека выражений один за другим. Когда Вы пишите функцию, которая принимает несколько аргументов, не забывайте, что стек - это структура данных "последним пришел", "первым вышел" . Это означает это, параметры будут выталкиваться из стека в обратном порядке. Последний параметр выталкивается первым. В вышеупомянутом примере мы выясняем, вызвана ли функция с 2 параметрами. Если да, параметр выталкивается из стека и сохраняется. Затем из стека выталкивается следующий параметр. Pop() возвращает указатель на структуру Stack (s). Структура Stack похожа на (из php.h):

/* Expression Stack */
typedef struct Stack {
    short type;
    unsigned char *strval;
    long intval;
    double douval;
    VarTree *var;
    struct Stack *next;
} Stack;

Тип type будет один из STRING, LNUMBER или DNUMBER. Strval, intval и douval компоненты - строки, integer и double представления значения соответственно. Если выражение - фактически определенная переменная, компонента var содержит указатель на переменную структуру, которая определяет эту переменную.

В нашей функции Crypt() нас интересует только строковое значение параметра, так что мы используем s->strval. Много функций PHP/FI могут делать различные вещи в зависимости от типа переменной просто проверяя s->type и используя s->strval, s->intval и/или s->douval соответственно.

После вызова реальной функции Crypt() и получения шифрованной строки, наша функции Crypt() вызывает Push(enc, STRING); помещая возвращаемое значение в стек выражений. Нужно отметить, что стек выражений очищается после каждой строки PHP/FI, так что, если Вы помещаете выражения в стек, которые никогда не выталкиваются чем-либо, это не будет иметь значения.

Вызов Debug() в примере Crypt() показывает, как добавить вывод отладочной информации к вашей функции. Debug() - это функция с переменным списком параметров, точно так же как printf.


Примечания для хакания кода

Управление памятью внутри PHP/FI - сложная вещь. Так как пакет может выполняться как модуль сервера, мы должны быть очень осторожны относительно ресурсов памяти. Программа должна быть не только реентерабельной, но также нужно учитывать тот факт, что мы можем получить в любое время сигнал по тайм-ауту, по которому управление выбывает из модуля. Мы не получим никакого предупреждения, и не будет времени, чтобы освободить память, которая может быть распределена. И эта память должна быть освобождена, или область данных процесса httpd, с которым мы связаны, может расти неопределенно. Это также применимо, когда PHP выполняется в режиме CGI, так как это может быть установлено, чтобы выполниться как постоянный процесс FastCGI .

Решение состоит в том, чтобы использовать подпулы памяти. Эти пулы автоматически очищаются Apache, по завершению сеанса, или в случае FastCGI, эти пулы очищаются в main.c, каждый раз, когда выполняется цикл оболочки FastCGI. В настоящий момент используются три таких пула. Они пронумерованы 0,1 и 2. Номер подпула является первым аргументом для функций emalloc и estrdup.

Пул 0 - Сеансовое время жизни
Любая память, распределенная из этого пула будет существовать весь сеанс. Это - хорошая идея использовать этот настолько мало насколько возможно. Например, если кто-то сделает цикл while, который выполняет итерации 1000 раз и внутри этого цикла while, он вызывают что-либо, что распределяет память из пула 0, то эта память будет распределена 1000 раз. Это - быстрый способ исчерпать всю распределенную область данных.
Пул 1 - Временное хранение (самое короткое время жизни)
Если для чего-либо внутри функции необходим временный рабочий буфер, память для него, должна исходить из этого пула. Этот пул очищается при каждом обращении к yylex(). т.е. память теряется примерно, как только Вы оставляете функцию.
Пул 2 - Область выражений (среднее время жизни)

Использование подпулов полностью устраняет потребность явно освобождать память где-либо в программе, с одним исключением - памятью распределенной, с использованием регулярного вызова malloc различными библиотеками, которые могут быть слинкованы в PHP. Библиотека gdbm - один из таких примеров.


Комментарии: (0) | PHP & MYSQL | 2006-06-02


Страница 11 из 51Первая«891011121314 »Последняя