Настройка VPN-сервера FreeBSD с ведением статистики, шейпингом и фильтрацией
Обновлено: 01.05.2025
Для начала: данная статья не претендует на роль полноценного руководства или панацеи на все случаи жизни, посему все вопросы и пожелания по исправлению и дополнению статьи шлем на <shulik at ua.fm>.
Итак - приступим. В работе нам понадобится: FreeBSD 6.0-RELEASE (можно и другую :) ), mpd, freeradius, netams, mysql, поддержка в ядре или в виде модулей IPFW, IPDIVERT, PF, NETGRAPH. Рекомендуется включить поддержку этих компонентов в ядро. Поэтому открываем конфигурационный файл вашего ядра и добавляем, если не хватает, туда следующее:
options NETGRAPH # собственно поддержка netgraph options NETGRAPH_PPP # поддержка PPP в netgraph options NETGRAPH_PPTPGRE # поддержка gre-туннелирования options IPFIREWALL # включаем поддержку ipfw options IPFIREWALL_DEFAULT_TO_ACCEPT # политика по умолчанию - ACCEPT options IPDIVERT # включаем divert-сокеты (понадобится для статистики)
Что касается pf - то я оставил его в виде модуля. Если хотите включить в ядро - /sys/conf/NOTES вам поможет.
Теперь установим такие пакеты, как freeradius, mpd, mysql и netams. Коротко:
cd /usr/ports/net-mgmt/netams make clean make make install make clean
При сборке freeradius обязательно указываем поддержку mysql.
cd /usr/ports/databases/mysql50-server make clean make make install make clean cd /usr/ports/net/freeradius make clean make make install make clean cd /usr/ports/net/mpd make clean make make install make clean
Теперь необходимо отконфигурировать установленный софт. MySQL конфигурим по вкусу - задавая пароли рута и прослушку портов или ее отсутствие. Затем нужно настроить freeradius. Для начала идем в /usr/local/etc/raddb/ и переименовываем все файлики из ‘*.samples’ в ‘*’. Открываем radiusd.conf. В нем правим следующие настройки:
log_stripped_names=yes # Логируем атрибут User-Name если он найден в запросе log_auth=yes # Логируем запросы аутентификации log_auth_badpass=yes # Если пароль неверен - пишем его в Лог log_auth_goodpass=no # верные пароли незачем писать в лог
Проверяем чтобы была раскоментарена строчка:
$INCLUDE ${confdir}/sql.conf
В секции authorize комментарим
files
и удаляем комментарий с
sql
В секции accounting также снимаем комментарий с
sql
Далее смотрим в sql.conf и правим:
driver="rlm_sql_mysql" server="localhost" login="login_k_mysql" password="pass_k_mysql"
Осталось поправить clients.conf, задав для клиента 127.0.0.1 secret:
secret=MyOwnSecret
Кроме того нужно создать в mysql базу radius’a следующего содержания: таблица со словарями:
CREATE TABLE `dictionary` ( `id` int(10) NOT NULL AUTO_INCREMENT, `Type` varchar(30) DEFAULT NULL, `Attribute` varchar(64) DEFAULT NULL, `Value` varchar(64) DEFAULT NULL, `Format` varchar(20) DEFAULT NULL, `Vendor` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
таблица с клиентами самого радиуса (вместо нее пользуем файлы)
CREATE TABLE `nas` ( `id` int(10) NOT NULL AUTO_INCREMENT, `nasname` varchar(128) DEFAULT NULL, `shortname` varchar(32) DEFAULT NULL, `ipaddr` varchar(15) DEFAULT NULL, `type` varchar(30) DEFAULT NULL, `ports` int(5) DEFAULT NULL, `secret` varchar(60) DEFAULT NULL, `community` varchar(50) DEFAULT NULL, `snmp` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
таблица для клиентских сессий:
CREATE TABLE `radacct` ( `RadAcctId` bigint(21) NOT NULL AUTO_INCREMENT, `AcctSessionId` varchar(32) NOT NULL DEFAULT '', `AcctUniqueId` varchar(32) NOT NULL DEFAULT '', `UserName` varchar(64) NOT NULL DEFAULT '', `Realm` varchar(64) DEFAULT '', `NASIPAddress` varchar(15) NOT NULL DEFAULT '', `NASPortId` int(12) DEFAULT NULL, `NASPortType` varchar(32) DEFAULT NULL, `AcctStartTime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `AcctStopTime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `AcctSessionTime` int(12) DEFAULT NULL, `AcctAuthentic` varchar(32) DEFAULT NULL, `ConnectInfo_start` varchar(32) DEFAULT NULL, `ConnectInfo_stop` varchar(32) DEFAULT NULL, `AcctInputOctets` int(12) DEFAULT NULL, `AcctOutputOctets` int(12) DEFAULT NULL, `CalledStationId` varchar(10) NOT NULL DEFAULT '', `CallingStationId` varchar(10) NOT NULL DEFAULT '', `AcctTerminateCause` varchar(32) NOT NULL DEFAULT '', `ServiceType` varchar(32) DEFAULT NULL, `FramedProtocol` varchar(32) DEFAULT NULL, `FramedIPAddress` varchar(15) NOT NULL DEFAULT '', `AcctStartDelay` int(12) DEFAULT NULL, `AcctStopDelay` int(12) DEFAULT NULL, PRIMARY KEY (`RadAcctId`), KEY `UserName` (`UserName`), KEY `FramedIPAddress` (`FramedIPAddress`), KEY `AcctSessionId` (`AcctSessionId`), KEY `AcctUniqueId` (`AcctUniqueId`), KEY `AcctStartTime` (`AcctStartTime`), KEY `AcctStopTime` (`AcctStopTime`), KEY `NASIPAddress` (`NASIPAddress`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
таблица для проверяемых параметров:
CREATE TABLE `radcheck` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `UserName` varchar(64) NOT NULL DEFAULT '', `Attribute` varchar(32) NOT NULL DEFAULT '', `op` char(2) NOT NULL DEFAULT '', `Value` varchar(253) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `UserName` (`UserName`(32)) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
таблица для проверяемых параметров групп:
CREATE TABLE `radgroupcheck` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `GroupName` varchar(64) NOT NULL DEFAULT '', `Attribute` varchar(32) NOT NULL DEFAULT '', `op` char(2) NOT NULL DEFAULT '', `Value` varchar(253) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `GroupName` (`GroupName`(32)) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
таблица для возвращаемых параметров групп:
CREATE TABLE `radgroupreply` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `GroupName` varchar(64) NOT NULL DEFAULT '', `Attribute` varchar(32) NOT NULL DEFAULT '', `op` char(2) NOT NULL DEFAULT '', `Value` varchar(253) NOT NULL DEFAULT '', `prio` int(10) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `GroupName` (`GroupName`(32)) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
таблица для возвращаемых параметров:
CREATE TABLE `radreply` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `UserName` varchar(64) NOT NULL DEFAULT '', `Attribute` varchar(32) NOT NULL DEFAULT '', `op` char(2) NOT NULL DEFAULT '', `Value` varchar(253) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `UserName` (`UserName`(32)) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
таблица соответствия пользователей и групп:
CREATE TABLE `usergroup` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `UserName` varchar(64) NOT NULL DEFAULT '', `GroupName` varchar(64) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `UserName` (`UserName`(32)) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
И сразу же заведем тестового пользователя:
INSERT INTO `radcheck` VALUES (1,'user_test','Password','==','qwerty'); INSERT INTO `radreply` VALUES (1,'user_test','Framed-IP-Address',':=','192.168.1.3'); INSERT INTO `radreply` VALUES (1,'user_test','Framed-IP-Netmask',':=','255.255.255.255'); INSERT INTO `radreply` VALUES (1,'user_test','Framed-Protocol',':=','PPP');
По сути ААА готова к использованию. Добавляем в /etc/rc.conf строки:
radiusd_enable="YES" mysql_enable="YES"
и стартуем скриптами из /usr/local/etc/rc.d/
Проверим радиус на работоспособность:
radtest user_test qwerty 127.0.0.1 0 MyOwnSecret
В результате должны получить что-то вида:
Sending Access-Request of id 189 to 127.0.0.1:1812 User-Name = "user_test" User-Password = "qwerty" NAS-IP-Address = localhost NAS-Port = 0 rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=189, length=38 Framed-IP-Address = 192.168.1.3 Framed-IP-Netmask = 255.255.255.255 Framed-Protocol = PPP
Это говорит о том, что радиус работает верно. Если нет - смотрим что он кричит в /var/log/radiusd.log
Пришло время настроить mpd. Смело идем в /usr/local/etc/mpd/ и убираем из имен конфигов samples. Открываем mpd.conf и пишем туда:
default: load pptp0 pptp0: new -i ng0 pptp0 pptp0 load pptp_all pptp_all: set ipcp ranges 192.168.1.1/32 10.0.0.0/24 set iface disable on-demand set bundle disable multilink set link yes acfcomp protocomp set link no pap chap set link enable chap set link keep-alive 60 180 set ipcp yes vjcomp set ipcp dns 192.168.1.1 # или любой другой доступный DNS # set bundle enable compression # обязательно проверяем, чтобы это было выключено. # иначе будем долго искать, почему пинги идут, но ничего не грузится set pptp enable incoming set pptp disable originate # если нужно - задаем ip для открытия 1723 порта. по умолчанию - все # set pptp self 172.16.101.2 set radius timeout 10 set radius config /usr/local/etc/mpd/radius.conf set radius retries 3 set bundle enable radius-acct set bundle enable radius-auth set ipcp yes radius-ip
Нужно заметить, что для примера создан всего лишь один pptp линк. В реале их нужно создать столько, сколько вам необходимо одновременных pptp-туннелей. Создаем mpd.links:
pptp0: set link type pptp
И radius.conf:
acct 127.0.0.1 MyOwnSecret auth 127.0.0.1 MyOwnSecret
Добавляем в rc.conf строку:
mpd_enable="YES"
и запускаем mpd.
Теперь нужно его проверить. Чтобы проще было вести отладку - добавим в /etc/syslog.conf:
!mpd *.* /var/log/mpd.log
И передергиваем syslog. Идем за клиентскую машинку и проверяем подключение по впн.
Если все прошло удачно - идем дальше, иначе ищем ошибку.
Далее нужно клиентов из внутренней сети с внутренними ip-адресами выпустить в инет. Для этого у нас есть несколько путей: воспользоваться связками ipfw+natd или ipfw+ng_nat, но мы пойдем по пути использования pf.
В /etc/rc.conf добавляем:
pf_enable="YES" pf_rules="/etc/pf.rules" pf_flags="" pflog_enable="YES" pflog_logfile="/var/log/pflog" pflog_flags=""
Файлик pf.rules:
ext_ip="19х.1хх.хх.хх" # внешний ip-адрес сервера int_if="rl1" # интерфейс смотрящий в локалку int_net_vpn="xx.yy.zz.ff" # внутренняя VPN-сеть local_net="..." # внутрення локалка int_ip="..." # внутренний ип в локалке nat on rl0 from $int_net_vpn to any -> $ext_ip # собсно NAT pass quick on lo0 all # разрешаем все на lo0 pass quick from $ext_ip to any # Разрешаем траф pass quick from any to $ext_ip # на внешнем ip pass quick from $int_net_vpn to any # разрешаем любой траф pass quick from any to $int_net_vpn # по впн-сети # блокируем платные сервисы из локалки block in quick on $int_if proto { tcp udp } from $local_net to $int_ip port { 25 110 143 3128 3306 } pass in quick on $int_if from $local_net to $int_ip block in all # все осальное блокируем по умолчанию
Все. Теперь трафик должен ходить в инет. Осталось организовать логирование трафика через netams. Приведу лишь коротки отрывки конфигов - так как на сайте netams.com есть хорошая документация на русском языке с примерами настройки netams. В rc.conf привычно добавляем:
firewall_enable="YES" firewall_script="/etc/ipfw.sh"
ipfw.sh:
#!/bin/sh fwcmd="/sbin/ipfw" ${fwcmd} -f flush ${fwcmd} -f flush pipe ${fwcmd} -f flush queue ${fwcmd} pipe 1 config bw 512Kbit/s queue 50 # для обжимки трафа пользуемся pipe'ами ${fwcmd} pipe 2 config bw 512Kbit/s queue 50 ${fwcmd} add pipe 1 ip from <vpn_net> to any in # вгоняем траф в пайпы ${fwcmd} add pipe 2 ip from any to <vpn_net> out ${fwcmd} add tee 100 ip from <vpn_net> to any in # копируем траф в divert-socket для netams. ${fwcmd} add tee 100 ip from any to <vpn_net> out
В netams для нас важны разделы:
service data-source 1 type ip-traffic source tee 100 service storage 1 type mysql user mysql_login # с правами создания БД, хотя бы на момент первого запуска password mysql_pass host localhost service monitor 1 monitor to storage 1 monitor unit VPN unit net name VPN ip <vpn-net> acct-policy ip
Теперь видимо все. Жду отзывы и пожелания на указанное выше мыло.
Copyright by Alexander Shulikov <shulik at ua.fm>
Хакеры: Приёмы взлома.
Обновлено: 01.05.2025Согласно недавнему опросу, проведённому в США, обычные граждане боятся хакеров больше, чем обычных преступников. Интересно, что высокие технологии входят в нашу жизнь очень быстро и становятся её неотъемлемой частью. Каждый день наполнен множеством взломов, которые, как правило, не бывают зарегистрированы. Атакуют всех - от обычных пользователей до крупных правительственных организаций. Цели бывают разные, машины обычных пользователей, как правило, становятся «зомби», с которых проводятся атаки или рассылается спам, а у организаций похищается информация.
Введение в Основные моменты взлома и защиты сервера.
Обновлено: 01.05.2025Человек, при любых обстоятельствах помни, что попытки (даже безуспешные) взлома чужих серверов - дело уголовно наказуемое. Даже если ты считаешь, что действуешь с благими намерениями (например, убедить кого-то, что ему следует обновиться). Все нижеизложенное приведено в целях ознакомления с тем, как могут действовать атакующие и от чего и как следует защищать инфраструктуру.
Источники бесперебойного питания. Общие понятия и термины
Обновлено: 01.05.2025Система бесперебойного питания (СБП)
Автоматическое устройство, обеспечивающее нормальное питание нагрузки при полном обесточивании внешней электросети в результате аварии или недопустимо высоком отклонении параметров сетевого напряжения от номинальных значений (см. "Неполадки в сети"). Различают два основных типа СБП: источники бесперебойного питания и генераторные установки.
Удаление стандартных общих ресурсов C$, ADMIN$, IPC$
Обновлено: 07.04.2025Большинсто людей, работающих в локальных сетях, даже не подозревают о том, что можно обратиться к их диску C: и посмотреть их личные документы. Большинство людей считают: раз я не ставил общий доступ к ресурсам на моем компьютере, то другие не смогут туда залезть. Как бы не так :). Достаточно вам ввести к командной строке ("Пуск -> Выполнить -> cmd")
Как создать голосование на PHP
Обновлено: 07.04.2025Ниже приведена простая для понимания и реализации функция создания интернет-голосования на сайте.
Вызывается функция следующим образом:
// Создаем новую функцию С 3-мя параметрами:
// $name - название опроса;
// $variants - массив вариантов ответа;
// $dir - имя директории для файлов опроса (папка создается скриптом).
function voting($name, $variants, $dir){
GLOBAL $REQUEST_URI;
// Если пользователь уже проголосовал
if(isset($_POST['vote'])){
// Массив ошибок
$errors = array();
// Массив содержимого директории
$indir = array();
$dh = opendir('./');
while($file = readdir($dh)){
if($file == '.' || $file == '..') continue;
$indir[] = $file;
}
closedir($dh);
// --------------------------------------------------------
// Если у вас PHP5, вместо получения содержимого дериктории
// вручную, вы можете воспльзоваться функцией scandir():
// $indir = scandir('./');
// --------------------------------------------------------
// Если директория для файлов опроса отсутсвует,
// Значит еще никто не голосовал, следовательно,
// Нужно создать эту директорию и необходимые файлы,
// Чтобы избежать ошибки
if(in_array($dir, $indir) == FALSE){
mkdir($dir, 0770);
$cr1 = fopen($dir.'/base.txt', 'w');
for($i=0; $i<sizeof($variants); $i++){
fputs($cr1, '0');
}
fclose($cr1);
$cr2 = fopen($dir.'/ip_list.txt', 'w');
fclose($cr2);
}
// Понеслась... Проверяем, голосовал ли пользователь
// Массив уже проголосовавших IP-адресов
$ip_list = file($dir.'/ip_list.txt');
// Если IP юзера совпал с одним из базы, значит юзер
// Уже голосовал
if(in_array($_SERVER['REMOTE_ADDR']." ", $ip_list) == TRUE){
$errors[] = "Вы уже голосовали!";
}else{
$answers = file($dir."/base.txt");
$fh = fopen($dir."/base.txt", "w");
flock($fh, LOCK_EX);
for($i=0; $i<sizeof($answers); $i++){
// Удаляем переводы строк
$answers[$i] = trim($answers[$i]);
// Пишем
if($i == $_POST['vote']){
fputs($fh, ($answers[$i]+1)." ");
}else{
fputs($fh, $answers[$i]." ");
}
}
flock($fh, LOCK_UN);
fclose($fh);
// Записываем IP юзера в базу проголосовавших
$fb = fopen($dir."/ip_list.txt", "a+");
fputs($fb, $_SERVER['REMOTE_ADDR']." ");
fclose($fb);
}
if(isset($errors[0])){
echo "Ваш голос не был учтен, т.к. были обнаружены следующие ошибки: <br>";
for($i=0; $i<sizeof($errors); $i++) echo $errors[$i]."<br>";
}else{
echo "Ваш голос учтен! Результаты:<br>";
// Обновляем базу
$answers = file($dir."/base.txt");
// Выводим результаты, и заодно считаем общее количество
// Для этого складываем существующие голоса
$summ = 0;
for($i=0; $i<sizeof($answers); $i++){
echo $variants[$i].": ".$answers[$i]."<br>";
// Пересчет голосов
$summ += $answers[$i];
}
// Вывод общего количества голосов
echo "Всего: ".$summ;
}
}else{
echo "<b>".$name."</b><br>";
echo "<form action=./ method=POST>";
for($i=0; $i<sizeof($variants); $i++){
echo "<input type=radio name=vote value=".$i;
if($i==0) echo " checked";
echo ">".$variants[$i]."<br>";
}
echo "<input type=submit value=Голосовать>";
echo "</form>";
}
}
?>
Вызывается функция следующим образом:
voting("Выбираем самый популярный антивирус", Array("Kaspersky", "Eset NOD32", "Symantec", "DrWeb", "McAfee"), "vote2");
?>
Ну вот и все.
Автор: Mostom, http://cngroup.ru/
Парсер web страниц
Обновлено: 01.05.2025При написании достаточно большого сайта с единым дизайном появляется желание отделить дизайн от содержимого страниц. Как правило над крупным проектом работает несколько человек, минимум двое: программист и дизайнер. Дело программиста - программировать, а дизайнера - работать над дизайном. Как нетрудно заметить, дизайн состоит из нескольких файлов, к тому же в дизайн приходится включать код PHP. Естественно дизайнеру будет неудобно проводить изменение дизайна, а если он не разбирается в PHP, то может что-то испортить. Для программиста возникают аналогичные трудности при написании скриптов - в *.php файлах приходится держать все содержимое страницы.
Решить данную проблему, то есть полностью отделить код от дизайна, позволяет так называемый парсер. Парсер - это некоторый скрипт, который берет готовый дизайн и в специальным способом помеченные места подставляет некоторые значения. Сейчас мы напишем простейший парсер. Его я писал, когда у меня возникло желание сделать на своем сайте несколько разных дизайнов, причем каждый пользователь мог выбрать тот дизайн, который ему больше нравился.
Итак, приступим. Нам необходимо, чтобы на машине (или там где вы собираетесь размещать сайт) были установлены и правильно настроены веб-сервер (например Apache) и интерпритатор языка PHP.
Создайте в директории сайта следующие папки:
- site_funcs - здесь мы будем размещать общие модули сайта
- designs - здесь мы будем держать дизайны
В папке designs создайте файл design.htm будущего дизайна со следующим содержанием:
<title>Наш сайт :: {*title*}</title>
</head><body>
<table cellspacing="0" border="1" cellpadding="1" width="100%" height="100%">
<tr><td height="70" colspan="3">Тут всякие картинки будут</td></tr>
<tr><td height="20" align="center" colspan="3">{*menu*}</td></tr>
<tr valign="top">
<td width="160" align="center">{*left*}</td>
<td style="color:#888888; font-size: 8pt; font-weight: bold;">
::: {*title*}<br><br>{*main*}<br><br></td>
<td width="160" align="center">{*right*}</td>
</tr>
</table>
</body></html>
Наш парсер будет заменять выражения {*имя_переменной*} на некоторые значения, которые будут определяться во время выполнения скрипта.
Теперь приступим к написанию собственно парсера. Создайте в папке site_funcs файл parse_funcs.php следующего содержания:
$Designs=array(); // Элементы подстановки в дизайн
$DesignOut=''; // То, что будем выводить
function LoadDesign ($CurDesign='design.htm'){ // Загружаем выбранный дизайн
global $DesignOut;
//echo 'Start design loading';
$FN='designs/'.$CurDesign;
if (!file_exists($FN)) return false;
$DesignOut=join('',File($FN));
//echo 'Design loaded';
return true;
}
function Parse ($n=1){ // Выполняет глобальную замену переменных {*...*} в дизайне на их значения, парсер одним словом. При необходимости делает замену нужное число раз (Если передать 0, то ничего не делает :) )
global $Designs, $DesignOut;
//echo 'Start parse';
for ($i=0;$i<$n;$i++){
foreach($Designs as $k=>$v){
$DesignOut=str_replace("{*$k*}","$v",$DesignOut);
}
}
}
?>
Разберем работу данного скрипта. Сначала нам понадобятся несколько переменных. В переменной $DesignOut мы будем держать содержимое нашего файла дизайна, а в массиве $Designs будем держать имена переменных, используемых в файле дизайна и их значения. Имена переменных используются в качестве ключей массива, а в значениях массива хранятся значения наших переменных.
Функция LoadDesign загружает в переменную $DesignOut содержимое файла дизайна, по умолчанию это design.htm, но при вызове функции можно указать любой другой.
Теперь самое интересное, функция Parse. Сердце функции состоит всего из одного цикла:
$DesignOut=str_replace("{*$k*}","$v",$DesignOut);
}
Этот цикл заменяет все вхождения переменных {*имя_переменной*} в строке $DesignOut на их значения из массива $Designs, причем имя_переменной это один из ключей массива $Designs. В самой функции есть еще один цикл, который выполняет описанный выше цикл n раз. Это нужно в том случае, если мы захотим в одной из переменных написать "Некоторый текст: {*text*}", а потом на место {*text*} вставить что-то еще. В этом случае нам надо сказать парсеру, чтобы он выполнил подстановку два раза.
Теперь нам надо написать простенький интерфейс для работы с парсером. Интерфейс будет состоять из набора функций, которые будут передавать значение в массив $Designs. Создайте в папке site_funcs файл design_funcs.php следующего содержания:
$CurDes='main'; // Указатель дизайна по умолчанию
function With ($newdes){ // Выбирает дизайн по умолчанию
global$CurDes;
$CurDes=$newdes;
}
function PrClr ($text){ // Пишет в дизайн по умолчанию, предварительно очистив его
global $CurDes,$Designs;
$Designs[$CurDes]=$text;
}
function Pr ($text){ // Пишет в дизайн по умолчанию
global $CurDes,$Designs;
@$Designs[$CurDes].=$text;
}
function PrToClr ($des,$text){ // Пишет в указанный дизайн, предварительно очистив его
global$Designs;
$Designs[$des]=$text;
}
function PrTo ($des,$text){ // Пишет в указанный дизайн
global $Designs;
@$Designs[$des].=$text;
}
function Clr ($des){ // Очищает указанный дизайн
global $Designs;
$Designs[$des]='';
}
?>
В принципе в этом модуле нет ничего хитрого, это всего лишь набор функций облегчающих работу. Все это нужно, чтобы писать не $Designs['main'].='некоторый текст', а просто PrTo('main', 'некоторый текст').
Если мы в элемент дизайна ничего выводить не собираемся, то его нужно просто очистить, т.е. записать в него пустую строку. Если этого не сделать, то парсер эту переменную не заменит и в результате у вас на страничке выскочит {*переменная*}. Функция Clr() как раз и очищает дизайн. Функции PrTo() и PrToClr() пишут в указанный дизайн, отличие в том, что первая просто дописывает текст к тому, что есть, а вторая предварительно очищает выбранный дизайн. Pr() и PrClr() работают аналогично двум последним, с тем лишь отличием, что они пишут в предварительно выбранный элемент дизайна, который можно выбрать с помощью функции With(), в нашем случае по умолчанию это main.
Теперь можно приступить к созданию первой странички. В папке сайта создайте файл index.php следующего содержания:
require_once 'site_funcs/parse_funcs.php';
require_once 'site_funcs/design_funcs.php';
LoadDesign();
PrToClr('menu','Меню, которого нет');
PrToClr('left','Левая часть');
Clr('right'); // В right ничего не пишем
PrToClr('title','Главная страничка');
PrToClr('text','некоторый текст');
Pr('Текст нашей страницы, который содержит в себе подтекст "{*text*}"');
With('left'); //Теперь функция Pr() будет писать в left
Pr('Пишем что-нибудь еще в left');
Parse(2);
echo $DesignOut;
?>
Как это работает думаю пояснять не стоит. Это примитивный пример. Поэкспериментируйте со значением, передаваемым парсеру. По хорошему первые 6 строк скрипта нужно отделить в отдельный файл инициализации, ведь по сути там ничего не меняется, и писать в каждом скрипте все это неразумно. Последние 2 строки нужно поместить в отдельную функцию, которая будет строить дизайн по частям, выводить его и завершать работу скрипта, у нас это будет DoDesign(). Эту функцию тоже не помешало бы положить в отдельный модуль, например в тот же parse_funcs.php. Таким образом последний файл примет вид:
require 'init.php';
PrToClr('title','Главная страничка');
PrToClr('text','некоторый текст');
Pr('Текст нашей страницы, который содержит в себе подтекст "{*text*}"');
With('left'); //Теперь функция Pr() будет писать в left
Pr('Пишем что-нибудь еще в left');
DoDesign(2);
?>
Файл init.php будет содержать в себе:
require_once 'site_funcs/parse_funcs.php';
require_once 'site_funcs/design_funcs.php';
LoadDesign();
PrToClr('menu','Меню, которого нет');
PrToClr('left','Левая часть');
Clr('right'); // В right ничего не пишем
?>
Функция DoDesign будет выглядеть примерно так
Parse($n);
echo $DesignOut;
exit;
}
Вот и все. Для полного разделения содержимого сайта, программного кода и дизайна нужно будет написать функцию загрузки содержимого из некоторого внешнего файла и вывода его в дизайн.
Автор: Gorinich, http://cngroup.ru/
Обжим витой пары
Обновлено: 01.05.2025На рисунке представлены два варианта обжима витой пары - прямое соединение двух компьютеров через сетевые платы (первая часть рисунка), и соединение компьютера и свитча (switch) (на второй, нижней, части рисунка). Соединение компьютера и свитча характерно для локальных сетей офисов, когда несколько компьютеров подключаются к общему коммутатору - свитчу (иначе switch).


Принимаю заказы на настройку серверов, mikrotik и других роутеров, точек доступа, nginx и т.п. В пределах Санкт-Петербурга возможен выезд к заказчику. См. контакты.
Последние комментарии
Популярно:
Разделы статей:
Подскажите. подключение с ПК все работает все ок. Делал по вашей мурзилке.
Но при подключе...