Sync my recent stuff to HEAD. Easy Rule, Log parsing tweaks, Firewall Log filter, Firewall Log Summary graphs. Also add ProtoChart.

This commit is contained in:
jim-p 2009-06-06 17:59:08 -04:00
parent a87afa7cb8
commit c0b6fddeec
12 changed files with 4217 additions and 31 deletions

View File

@ -5,7 +5,8 @@
part of pfSesne by Scott Ullrich
originally based on m0n0wall (http://m0n0.ch/wall)
Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
Copyright (C) 2003-2009 Manuel Kasper <mk@neon1.net>,
Jim Pingle <myfirstname>@<mylastname>.org
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -51,13 +52,34 @@ if($_GET['getrulenum'] or $_POST['getrulenum']) {
exit;
}
if($_GET['dnsip'] or $_POST['dnsip']) {
if($_GET['dnsip'])
$dnsip = $_GET['dnsip'];
if($_POST['dnsip'])
$dnsip = $_POST['dnsip'];
$host = get_reverse_dns($dnsip);
if ($host == $ip) {
$host = "No PTR Record";
}
echo "IP: {$dnsip}\nHost: {$host}";
exit;
}
$filtertext = "";
if($_GET['filtertext'] or $_POST['filtertext']) {
if($_GET['filtertext'])
$filtertext = $_GET['filtertext'];
if($_POST['filtertext'])
$filtertext = $_POST['filtertext'];
}
$filter_logfile = "{$g['varlog_path']}/filter.log";
$nentries = $config['syslog']['nentries'];
if (!$nentries)
$nentries = 50;
if ($_POST['clear'])
if ($_POST['clear'])
clear_log_file($filter_logfile);
$pgtitle = array("Status","System logs","Firewall");
@ -88,12 +110,20 @@ include("head.inc");
<td>
<div id="mainarea">
<table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td colspan="6" align="left">
Normal View | <a href="diag_logs_filter_dynamic.php">Dynamic View</a> | <a href="diag_logs_filter_summary.php">Summary View</a><br/><br/>
</td></tr>
<?php if (!isset($config['syslog']['rawfilter'])):
$filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 100);
$filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 100, $filtertext);
?>
<tr>
<td colspan="6" class="listtopic">
Last <?php echo $nentries;?> firewall log entries &nbsp;&nbsp;&nbsp;(<a href='/diag_logs_filter_dynamic.php'>switch</a> to dynamic view)</td>
<?php if (!$filtertext) { ?>
Last <?php echo count($filterlog);?> firewall log entries.
<?php } else { ?>
<?php echo count($filterlog);?> matched log entries.
<?php } ?>
(Max <?php echo $nentries;?>)
</tr>
<tr>
<td width="10%" class="listhdrr">Act</td>
@ -111,8 +141,23 @@ include("head.inc");
<?php if ($filterent['count']) echo $filterent['count'];?></td>
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['time']);?></td>
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['interface']);?></td>
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['src']);?></td>
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['dst']);?></td>
<?php
$int = strtolower($filterent['interface']);
$proto = strtolower($filterent['proto']);
$srcstr = $filterent['srcip'] . get_port_with_service($filterent['srcport'], $proto);
$dststr = $filterent['dstip'] . get_port_with_service($filterent['dstport'], $proto);
?>
<td class="listr" nowrap>
<a href="diag_dns.php?host=<?php echo $filterent['srcip']; ?>" title="Reverse Resolve with DNS"><img border="0" src="/themes/nervecenter/images/icons/icon_log.gif"></a>
<a href="easyrule.php?<?php echo "action=block&int={$int}&src={$filterent['srcip']}"; ?>" title="Easy Rule: Add to Block List" onclick="return confirm('Do you really want to add this BLOCK rule?\n\nEasy Rule is still experimental.\nContinue at risk of your own peril.\nBackups are also nice.')"><img border="0" src="/themes/nervecenter/images/icons/icon_block_add.gif"></a>
<?php echo $srcstr;?>
</td>
<td class="listr" nowrap>
<a href="diag_dns.php?host=<?php echo $filterent['dstip']; ?>" title="Reverse Resolve with DNS"><img border="0" src="/themes/nervecenter/images/icons/icon_log.gif"></a>
<a href="easyrule.php?<?php echo "action=pass&int={$int}&proto={$proto}&src={$filterent['srcip']}&dst={$filterent['dstip']}&dstport={$filterent['dstport']}"; ?>" title="Easy Rule: Pass this traffic" onclick="return confirm('Do you really want to add this PASS rule?\n\nEasy Rule is still experimental.\nContinue at risk of your own peril.\nBackups are also nice.')"><img border="0" src="/themes/nervecenter/images/icons/icon_pass_add.gif"></a>
<?php echo $dststr;?>
</td>
<?php
if ($filterent['proto'] == "TCP")
$filterent['proto'] .= ":{$filterent['tcpflags']}";
@ -124,11 +169,27 @@ include("head.inc");
<td colspan="2" class="listtopic">
Last <?php echo $nentries;?> firewall log entries</td>
</tr>
<?php dump_clog($filter_logfile, $nentries); ?>
<?php
if($filtertext)
dump_clog($filter_logfile, $nentries, true, array("$filtertext"));
else
dump_clog($filter_logfile, $nentries);
?>
<?php endif; ?>
<tr><td><br /><form action="diag_logs_filter.php" method="post">
<input name="clear" type="submit" class="formbtn" value="Clear log" /></td></tr>
</form>
<tr>
<td align="left" valign="top" colspan="3">
<form id="clearform" name="clearform" action="diag_logs_filter.php" method="post" style="margin-top: 14px;">
<input id="submit" name="clear" type="submit" class="formbtn" value="<?=gettext("Clear log");?>" />
</form>
</td>
<td align="right" valign="top" colspan="3">
<form id="filterform" name="filterform" action="diag_logs_filter.php" method="post" style="margin-top: 14px;">
<input id="filtertext" name="filtertext" value="<?=gettext($filtertext);?>" />
<input id="filtersubmit" name="filtersubmit" type="submit" class="formbtn" value="<?=gettext("Filter");?>" />
</form>
</td>
</tr>
</table>
</div>
</td>

View File

@ -108,8 +108,11 @@ include("head.inc");
<tr>
<td>
<div id="mainarea">
<div class="tabcont">
<a href="diag_logs_filter.php">Normal View</a> | Dynamic View | <a href="diag_logs_filter_summary.php">Summary View</a>
</div>
<div class="listtopic">
Last <?php echo $nentries; ?> records; (Switch to <a href="diag_logs_filter.php">regular</a> view) Pause:<input valign="middle" type="checkbox" onClick="javascript:toggle_pause();">
Last <?php echo $nentries; ?> records; Pause:<input valign="middle" type="checkbox" onClick="javascript:toggle_pause();">
</div>
<div id="log">
<div class="log-header">

View File

@ -0,0 +1,227 @@
<?php
/*
diag_logs_filter_summary.php
Copyright (C) 2009 Jim Pingle (jpingle@gmail.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
$pgtitle = "Diagnostics: System logs: Firewall Log Summary";
require_once("guiconfig.inc");
include_once("includes/log.inc.php");
$filter_logfile = "{$g['varlog_path']}/filter.log";
$lines = 5000;
$entriesperblock = 5;
$filterlog = conv_clog_filter($filter_logfile, $lines, $lines);
$gotlines = count($filterlog);
$descr = array();
$descr['actions'] = "Actions";
$descr['ints'] = "Interfaces";
$descr['protos'] = "Protocols";
$descr['srcips'] = "Source IPs";
$descr['dstips'] = "Destination IPs";
$descr['srcports'] = "Source Ports";
$descr['dstports'] = "Destination Ports";
$summary['actions'] = array();
$summary['ints'] = array();
$summary['protos'] = array();
$summary['srcips'] = array();
$summary['dstips'] = array();
$summary['srcports'] = array();
$summary['dstports'] = array();
$totals = array();
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? 1 : -1;
}
function stat_block($summary, $stat, $num) {
global $gotlines, $descr;
uasort($summary[$stat] , 'cmp');
print '<table width="200px" cellpadding="3" cellspacing="0" border="1">';
print "<tr><th colspan='2'>{$descr[$stat]} data</th></tr>";
$k = array_keys($summary[$stat]);
$total = 0;
$numentries = 0;
for ($i=0; $i < $num; $i++) {
if ($k[$i]) {
$total += $summary[$stat][$k[$i]];
$numentries++;
$outstr = $k[$i];
if (is_ipaddr($outstr)) {
$outstr = "<a href=\"diag_dns.php?host={$outstr}\" title=\"Reverse Resolve with DNS\"><img border=\"0\" src=\"/themes/nervecenter/images/icons/icon_log.gif\"></a> {$outstr}";
} elseif (substr_count($outstr, '/') == 1) {
list($proto, $port) = explode('/', $outstr);
$service = getservbyport($port, strtolower($proto));
if ($service)
$outstr .= ": {$service}";
}
print "<tr><td>{$outstr}</td><td width='50px' align='right'>{$summary[$stat][$k[$i]]}</td></tr>\n";
}
}
$leftover = $gotlines - $total;
if ($leftover > 0) {
print "<tr><td>Other</td><td width='50px' align='right'>{$leftover}</td></tr>\n";
}
print '</table>';
}
function pie_block($summary, $stat, $num) {
global $gotlines, $descr;
uasort($summary[$stat] , 'cmp');
$k = array_keys($summary[$stat]);
$total = 0;
$numentries = 0;
print "\n<script language=\"javascript\" type=\"text/javascript\">\n";
print "function lblfmt(lbl) {\n";
print " return '<font size=\"-2\">' + lbl + '</font>'\n";
print "}\n";
for ($i=0; $i < $num; $i++) {
if ($k[$i]) {
$total += $summary[$stat][$k[$i]];
$numentries++;
print "var d{$i} = [];\n";
print "d{$i}.push([1, {$summary[$stat][$k[$i]]}]);\n";
}
}
$leftover = $gotlines - $total;
if ($leftover > 0) {
print "var d{$num} = [];\n";
print "d{$num}.push([1, {$leftover}]);\n";
}
print "Event.observe(window, 'load', function() {\n";
print " new Proto.Chart($('piechart{$stat}'), \n";
print " [\n";
for ($i=0; $i < $num; $i++) {
if ($k[$i]) {
print " { data: d{$i}, label: \"{$k[$i]}\"}";
if (!(($i == ($numentries - 1)) && ($leftover <= 0)))
print ",\n";
else
print "\n";
}
}
if ($leftover > 0)
print " { data: d{$i}, label: \"Other\"}\n";
print " ],\n";
print " {\n";
print " pies: {show: true, autoScale: true},\n";
print " legend: {show: true, labelFormatter: lblfmt}\n";
print " });\n";
print "});\n";
print "</script>";
print "<table cellpadding=\"3\" cellspacing=\"0\" border=\"0\">";
print "<tr><th><font size=\"+1\">{$descr[$stat]}</font></th></tr>";
print "<tr><td><div id=\"piechart{$stat}\" style=\"width:450px;height:300px\"></div>\n";
print "</table>";
}
foreach ($filterlog as $fe) {
$summary['actions'][$fe['act']]++;
$summary['ints'][$fe['interface']]++;
$summary['protos'][$fe['proto']]++;
$summary['srcips'][$fe['srcip']]++;
$summary['dstips'][$fe['dstip']]++;
if ($fe['srcport'])
$summary['srcports'][$fe['proto'].'/'.$fe['srcport']]++;
else
$summary['srcports'][$fe['srcport']]++;
if ($fe['dstport'])
$summary['dstports'][$fe['proto'].'/'.$fe['dstport']]++;
else
$summary['dstports'][$fe['dstport']]++;
}
include("head.inc"); ?>
<body link="#000000" vlink="#000000" alink="#000000">
<script src="/javascript/filter_log.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript" src="/protochart/prototype.js"></script>
<script language="javascript" type="text/javascript" src="/protochart/ProtoChart.js"></script>
<!--[if IE]>
<script language="javascript" type="text/javascript" src="/protochart/excanvas.js">
</script>
<![endif]-->
<? include("fbegin.inc"); ?>
<p class="pgtitle"><?=$pgtitle?></p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td>
<?php
$tab_array = array();
$tab_array[] = array("System", false, "diag_logs.php");
$tab_array[] = array("Firewall", true, "diag_logs_filter.php");
$tab_array[] = array("DHCP", false, "diag_logs_dhcp.php");
$tab_array[] = array("Portal Auth", false, "diag_logs_auth.php");
$tab_array[] = array("IPSEC VPN", false, "diag_logs_ipsec.php");
$tab_array[] = array("PPTP VPN", false, "diag_logs_vpn.php");
$tab_array[] = array("Load Balancer", false, "diag_logs_slbd.php");
$tab_array[] = array("OpenVPN", false, "diag_logs_openvpn.php");
$tab_array[] = array("OpenNTPD", false, "diag_logs_ntpd.php");
$tab_array[] = array("Settings", false, "diag_logs_settings.php");
display_top_tabs($tab_array);
?>
</td></tr>
<tr>
<td>
<div id="mainarea">
<table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0" align="center">
<tr><td colspan="6" align="left">
<a href="diag_logs_filter.php">Normal View</a> | <a href="diag_logs_filter_dynamic.php">Dynamic View</a> | Summary View<br/><br/>
</td></tr>
<tr><td align="center">
This is a firewall log summary, of the last <?php echo $gotlines; ?> lines of the firewall log (Max <?php echo $lines; ?>).<br />
NOTE: IE8 users must enable compatibility view.
<?php pie_block($summary, 'actions' , $entriesperblock); ?><br /><br />
<?php stat_block($summary, 'actions' , $entriesperblock); ?><br /><br />
<?php pie_block($summary, 'ints' , $entriesperblock); ?><br /><br />
<?php stat_block($summary, 'ints' , $entriesperblock); ?><br /><br />
<?php pie_block($summary, 'protos' , $entriesperblock); ?><br /><br />
<?php stat_block($summary, 'protos' , $entriesperblock); ?><br /><br />
<?php pie_block($summary, 'srcips' , $entriesperblock); ?><br /><br />
<?php stat_block($summary, 'srcips' , $entriesperblock); ?><br /><br />
<?php pie_block($summary, 'dstips' , $entriesperblock); ?><br /><br />
<?php stat_block($summary, 'dstips' , $entriesperblock); ?><br /><br />
<?php pie_block($summary, 'srcports', $entriesperblock); ?><br /><br />
<?php stat_block($summary, 'srcports', $entriesperblock); ?><br /><br />
<?php pie_block($summary, 'dstports', $entriesperblock); ?><br /><br />
<?php stat_block($summary, 'dstports', $entriesperblock); ?><br /><br />
</td></tr></table>
</div>
</td>
</tr>
</table>
<?php include("fend.inc"); ?>

260
usr/local/www/easyrule.inc Normal file
View File

@ -0,0 +1,260 @@
<?php
/*
easyrule.inc.php
Copyright (C) 2009 Jim Pingle (jpingle@gmail.com)
Sponsored By Anathematic @ pfSense Forums
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
$blockaliasname = 'EasyRuleBlockHosts';
$specialsrcdst = explode(" ", "any wanip lanip lan pptp pppoe");
function easyrule_find_rule_interface($int) {
global $config;
/* Borrowed from firewall_rules.php */
$iflist = array("lan" => "LAN", "wan" => "WAN");
for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
$iflist['opt' . $i] = $config['interfaces']['opt' . $i]['descr'];
}
if ($config['pptpd']['mode'] == "server")
$iflist['pptp'] = "PPTP VPN";
if ($config['pppoe']['mode'] == "server")
$iflist['pppoe'] = "PPPoE VPN";
/* add ipsec interfaces */
if (isset($config['ipsec']['enable']) || isset($config['ipsec']['mobileclients']['enable'])){
$iflist["enc0"] = "IPSEC";
}
for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
$iflist['opt' . $i] = $config['interfaces']['opt' . $i]['descr'];
}
if (isset($iflist[$int]))
return $int;
foreach ($iflist as $if => $ifd) {
if (strtolower($int) == strtolower($ifd))
return $if;
}
return false;
}
function easyrule_block_rule_exists($int = 'wan') {
global $blockaliasname, $config;
/* No rules, we we know it doesn't exist */
if (!is_array($config['filter']['rule'])) {
return false;
}
/* Search through the rules for one referencing our alias */
foreach ($config['filter']['rule'] as $rule)
if ($rule['source']['address'] == $blockaliasname . strtoupper($int) && ($rule['interface'] == $int))
return true;
return false;
}
function easyrule_block_rule_create($int = 'wan') {
global $blockaliasname, $config;
/* If the alias doesn't exist, exit.
* Can't create an empty alias, and we don't know a host */
if (easyrule_block_alias_getid($int) === false)
return false;
/* If the rule already exists, no need to do it again */
if (easyrule_block_rule_exists($int))
return true;
/* No rules, start a new array */
if (!is_array($config['filter']['rule'])) {
$config['filter']['rule'] = array();
}
filter_rules_sort();
$a_filter = &$config['filter']['rule'];
/* Make up a new rule */
$filterent = array();
$filterent['type'] = 'block';
$filterent['interface'] = $int;
$filterent['source']['address'] = $blockaliasname . strtoupper($int);
$filterent['destination']['any'] = '';
$filterent['descr'] = "Easy Rule: Blocked from Firewall Log View";
$a_filter[] = $filterent;
return true;
}
function easyrule_block_alias_getid($int = 'wan') {
global $blockaliasname, $config;
if (!is_array($config['aliases']))
return false;
/* Hunt down an alias with the name we want, return its id */
foreach ($config['aliases']['alias'] as $aliasid => $alias)
if ($alias['name'] == $blockaliasname . strtoupper($int))
return $aliasid;
return false;
}
function easyrule_block_alias_add($host, $int = 'wan') {
global $blockaliasname, $config;
/* If the host isn't a valid IP address, bail */
if (!is_ipaddr($host))
return false;
/* If there are no aliases, start an array */
if (!is_array($config['aliases']['alias']))
$config['aliases']['alias'] = array();
aliases_sort();
$a_aliases = &$config['aliases']['alias'];
/* Try to get the ID if the alias already exists */
$id = easyrule_block_alias_getid($int);
if ($id === false)
unset($id);
$alias = array();
if (isset($id) && $a_aliases[$id]) {
/* Make sure this IP isn't already in the list. */
if (in_array($host.'/32', explode(" ", $a_aliases[$id]['address'])))
return true;
/* Since the alias already exists, just add to it. */
$alias['name'] = $a_aliases[$id]['name'];
$alias['type'] = $a_aliases[$id]['type'];
$alias['descr'] = $a_aliases[$id]['descr'];
$alias['address'] = $a_aliases[$id]['address'] . ' ' . $host . '/32';
$alias['detail'] = $a_aliases[$id]['detail'] . 'Entry added ' . date('r') . '||';
} else {
/* Create a new alias with all the proper information */
$alias['name'] = $blockaliasname . strtoupper($int);
$alias['type'] = 'network';
$alias['descr'] = mb_convert_encoding("Hosts blocked from Firewall Log view","HTML-ENTITIES","auto");
$alias['address'] = $host . '/32';
$alias['detail'] = 'Entry added ' . date('r') . '||';
}
/* Replace the old alias if needed, otherwise tack it on the end */
if (isset($id) && $a_aliases[$id])
$a_aliases[$id] = $alias;
else
$a_aliases[] = $alias;
return true;
}
function easyrule_block_host_add($host, $int = 'wan') {
global $retval;
/* Bail if the supplied host is not a valid IP address */
if (!is_ipaddr($host))
return false;
/* Flag whether or not we need to reload the filter */
$dirty = false;
/* Attempt to add this host to the alias */
if (easyrule_block_alias_add($host, $int)) {
$dirty = true;
} else {
/* Couldn't add the alias, or adding the host failed. */
return false;
}
/* Attempt to add the firewall rule if it doesn't exist.
* Failing to add the rule isn't necessarily an error, it may
* have been modified by the user in some way. Adding to the
* Alias is what's important.
*/
if (!easyrule_block_rule_exists($int)) {
if (easyrule_block_rule_create($int)) {
$dirty = true;
} else {
return false;
}
}
/* If needed, write the config and reload the filter */
if ($dirty) {
write_config();
config_lock();
$retval = filter_configure();
config_unlock();
header("Location: firewall_aliases.php");
exit;
} else {
return false;
}
}
function easyrule_pass_rule_add($int, $proto, $srchost, $dsthost, $dstport) {
global $config;
/* No rules, start a new array */
if (!is_array($config['filter']['rule'])) {
$config['filter']['rule'] = array();
}
filter_rules_sort();
$a_filter = &$config['filter']['rule'];
/* Make up a new rule */
$filterent = array();
$filterent['type'] = 'pass';
$filterent['interface'] = $int;
$filterent['descr'] = "Easy Rule: Passed from Firewall Log View";
if ($proto != "any")
$filterent['protocol'] = $proto;
else
unset($filterent['protocol']);
/* Default to only allow echo requests, since that's what most people want and
* it should be a safe choice. */
if ($proto == "icmp")
$filterent['icmptype'] = 'echoreq';
pconfig_to_address($filterent['source'], $srchost, 32);
pconfig_to_address($filterent['destination'], $dsthost, 32, '', $dstport, $dstport);
$a_filter[] = $filterent;
write_config();
config_lock();
$retval = filter_configure();
config_unlock();
header("Location: firewall_rules.php?if={$int}");
exit;
}
?>

132
usr/local/www/easyrule.php Normal file
View File

@ -0,0 +1,132 @@
<?php
/*
easyrule.php
Copyright (C) 2009 Jim Pingle (jpingle@gmail.com)
Sponsored By Anathematic @ pfSense Forums
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
$pgtitle = "Status : EasyRule";
require_once("guiconfig.inc");
require_once("easyrule.inc");
$retval = 0;
$message = "";
if ($_GET && isset($_GET['action'])) {
switch ($_GET['action']) {
case 'block':
/* Check that we have a valid host */
if (isset($_GET['src']) && isset($_GET['int'])) {
if (!is_ipaddr($_GET['src'])) {
$message .= "Tried to block invalid IP: " . htmlspecialchars($_GET['src']) . "<br/>";
break;
}
$_GET['int'] = easyrule_find_rule_interface($_GET['int']);
if ($_GET['int'] === false) {
$message .= "Invalid interface for block rule: " . htmlspecialchars($_GET['int']) . "<br/>";
break;
}
if (easyrule_block_host_add($_GET['src'], $_GET['int'])) {
/* shouldn't get here, the function will redirect */
$message .= "Host added successfully" . "<br/>";
} else {
$message .= "Failed to create block rule, alias, or add host." . "<br/>";
}
} else {
$message .= "Tried to block but had no host IP or interface<br/>";
}
break;
case 'pass':
/* Check for valid int, srchost, dsthost, dstport, and proto */
if (isset($_GET['int']) && isset($_GET['proto']) && isset($_GET['src']) && isset($_GET['dst'])) {
$_GET['int'] = easyrule_find_rule_interface($_GET['int']);
if ($_GET['int'] === false) {
$message .= "Invalid interface for pass rule: " . htmlspecialchars($_GET['int']) . "<br/>";
break;
}
if (getprotobyname($_GET['proto']) == -1) {
$message .= "Invalid protocol for pass rule: " . htmlspecialchars($_GET['proto']) . "<br/>";
break;
}
if (!is_ipaddr($_GET['src'])) {
$message .= "Tried to pass invalid source IP: " . htmlspecialchars($_GET['src']) . "<br/>";
break;
}
if (!is_ipaddr($_GET['dst'])) {
$message .= "Tried to pass invalid destination IP: " . htmlspecialchars($_GET['dst']) . "<br/>";
break;
}
if (($_GET['proto'] != 'icmp') && !isset($_GET['dstport'])) {
$message .= "Missing destination port: " . htmlspecialchars($_GET['dstport']) . "<br/>";
break;
}
if ($_GET['proto'] == 'icmp') {
$_GET['dstport'] = 0;
}
if (!is_numeric($_GET['dstport']) || ($_GET['dstport'] < 0) || ($_GET['dstport'] > 65536)) {
$message .= "Tried to pass invalid destination port: " . htmlspecialchars($_GET['dstport']) . "<br/>";
break;
}
/* Should have valid input... */
if (easyrule_pass_rule_add($_GET['int'], $_GET['proto'], $_GET['src'], $_GET['dst'], $_GET['dstport'])) {
/* Shouldn't get here, the function should redirect. */
$message .= "Successfully added pass rule!" . "<br/>";
} else {
$message .= "Failed to add pass rule." . "<br/>";
}
} else {
$message = "Missing parameters for pass rule";
break;
}
break;
}
}
if(stristr($retval, "error") == true)
$message = $retval;
include("head.inc"); ?>
<body link="#000000" vlink="#000000" alink="#000000">
<? include("fbegin.inc"); ?>
<p class="pgtitle"><?=$pgtitle?></p>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<?php if ($input_errors) print_input_errors($input_errors); ?>
<?php if ($message) { ?>
<br/>
Message: <?php echo $message; ?>
<br/>
<? } else { ?>
This is the Easy Rule status page, mainly used to display errors when adding rules.
If you are seeing this, there apparently was not an error, and you navigated to the
page directly without telling it what to do.<br/><br/>
This page is meant to be called from the block/pass buttons on the Firewall Logs page, <a href="http://192.168.56.101/diag_logs_filter.php">Status &gt; System Logs,
Firewall Tab</a>.
<br />
<? } ?>
</td></tr></table>
<?php include("fend.inc"); ?>

View File

@ -1,7 +1,7 @@
<?php
/* $Id$ */
/*
log.inc.php
filter_log.inc
part of pfSesne by Scott Ullrich
originally based on m0n0wall (http://m0n0.ch/wall)
@ -31,13 +31,16 @@
*/
/* format filter logs */
function conv_log_filter($logfile, $nentries, $tail = 50) {
function conv_log_filter($logfile, $nentries, $tail = 50, $filtertext = "") {
global $config, $g;
/* Make sure this is a number before using it in a system call */
if (!(is_numeric($tail)))
return;
if ($filtertext)
$tail = 5000;
/* FreeBSD 8 splits pf log lines into two lines, so we need to at least
* tail twice as many, plus some extra to account for unparseable lines */
$tail = $tail * 2 + 50;
@ -45,7 +48,7 @@ function conv_log_filter($logfile, $nentries, $tail = 50) {
/* Always do a reverse tail, to be sure we're grabbing the 'end' of the log. */
$logarr = "";
if(isset($config['system']['usefifolog']))
if(isset($config['system']['usefifolog']))
exec("/usr/sbin/fifolog_reader {$logfile} | /usr/bin/tail -r -n {$tail}", $logarr);
else
exec("/usr/sbin/clog {$logfile} | grep -v \"CLOG\" | grep -v \"\033\" | /usr/bin/tail -r -n {$tail}", $logarr);
@ -60,7 +63,7 @@ function conv_log_filter($logfile, $nentries, $tail = 50) {
break;
$flent = parse_filter_line($logent);
if ($flent != "") {
if (($flent != "") && (match_filter_line($flent, $filtertext))) {
$counter++;
$filterlog[] = $flent;
}
@ -69,6 +72,13 @@ function conv_log_filter($logfile, $nentries, $tail = 50) {
return isset($config['syslog']['reverse']) ? $filterlog : array_reverse($filterlog);
}
function match_filter_line($flent, $filtertext = "") {
if (!$filtertext)
return true;
$filtertext = str_replace(' ', '\s+', $filtertext);
return preg_match("/{$filtertext}/i", implode(" ", array_values($flent)));
}
function collapse_filter_lines($logarr) {
$lastline = "";
$collapsed = array();
@ -95,8 +105,17 @@ function parse_filter_line($line) {
list($all, $flent['time'], $host, $rule, $flent['act'], $flent['realint'], $details, $src, $dst, $leftovers) = $log_split;
$flent['src'] = convert_port_period_to_colon($src);
$flent['dst'] = convert_port_period_to_colon($dst);
list($flent['srcip'], $flent['srcport']) = parse_ipport($src);
list($flent['dstip'], $flent['dstport']) = parse_ipport($dst);
$flent['src'] = $flent['srcip'];
$flent['dst'] = $flent['dstip'];
if ($flent['srcport'])
$flent['src'] .= ':' . $flent['srcport'];
if ($flent['dstport'])
$flent['dst'] .= ':' . $flent['dstport'];
$flent['interface'] = convert_log_interface_to_friendly_interface_name($flent['realint']);
$tmp = split("/", $rule);
@ -104,7 +123,7 @@ function parse_filter_line($line) {
$proto = array(" ", "(?)");
/* Attempt to determine the protocol, based on several possible patterns.
* The value returned by strpos() must be strictly checkeded against the
* The value returned by strpos() must be strictly checkeded against the
* boolean FALSE because it could return a valid answer of 0 upon success. */
if (!(strpos($details, 'proto ') === FALSE)) {
preg_match("/.*\sproto\s(.*)\s\(/", $details, $proto);
@ -152,26 +171,49 @@ function convert_log_interface_to_friendly_interface_name($int) {
return $int;
}
function convert_port_period_to_colon($addr) {
function parse_ipport($addr) {
$addr = rtrim($addr, ":");
$port = '';
if (substr_count($addr, '.') > 1) {
/* IPv4 - Change the port delimiter to : */
/* IPv4 */
$addr_split = split("\.", $addr);
if($addr_split[4] == "") {
$newvar = "{$addr_split[0]}.{$addr_split[1]}.{$addr_split[2]}.{$addr_split[3]}";
$newvar = rtrim($newvar, ":");
} else {
$ip = "{$addr_split[0]}.{$addr_split[1]}.{$addr_split[2]}.{$addr_split[3]}";
if ($ip == "...")
return array($addr, '');
if($addr_split[4] != "") {
$port_split = split("\:", $addr_split[4]);
$newvar = "{$addr_split[0]}.{$addr_split[1]}.{$addr_split[2]}.{$addr_split[3]}:{$port_split[0]}";
$newvar = rtrim($newvar, ":");
$port = $port_split[0];
}
if($newvar == "...")
return $addr;
return $newvar;
} else {
/* IPv6 - Leave it alone */
/* IPv6 */
$addr = split(" ", $addr);
return rtrim($addr[0], ":");
$addr = rtrim($addr[0], ":");
$addr_split = split("\.", $addr);
if (count($addr_split) > 1) {
$ip = $addr_split[0];
$port = $addr_split[1];
} else {
$ip = $addr;
}
}
return array($ip, $port);
}
function get_port_with_service($port, $proto) {
if (!$port)
return '';
$service = getservbyport($port, $proto);
$portstr = "";
if ($service) {
$portstr = "<span title=\"Service {$port}/{$proto}: {$service}\">" . htmlspecialchars($port) . "</span>";
} else {
$portstr = htmlspecialchars($port);
}
return ':' . $portstr;
}
function find_rule_by_number($rulenum, $type="rules") {

View File

@ -63,7 +63,11 @@ while(!feof($log)) {
proto - Protocol (e.g. TCP, UDP, ICMP, etc)
tcpflags - TCP flags/control bits
src - Source address with port
srcip - Source IP
srcport - Source Port
dst - Destination address with port
dstip - Destination IP
dstport - Destination Port
*/
if ($flent != "")
echo "{$flent['time']} {$flent['act']} {$flent['realint']} {$flent['proto']} {$flent['src']} {$flent['dst']}\n";

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
if(!window.CanvasRenderingContext2D){(function(){var I=Math,i=I.round,L=I.sin,M=I.cos,m=10,A=m/2,Q={init:function(a){var b=a||document;if(/MSIE/.test(navigator.userAgent)&&!window.opera){var c=this;b.attachEvent("onreadystatechange",function(){c.r(b)})}},r:function(a){if(a.readyState=="complete"){if(!a.namespaces["s"]){a.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml")}var b=a.createStyleSheet();b.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}";
var c=a.getElementsByTagName("canvas");for(var d=0;d<c.length;d++){if(!c[d].getContext){this.initElement(c[d])}}}},q:function(a){var b=a.outerHTML,c=a.ownerDocument.createElement(b);if(b.slice(-2)!="/>"){var d="/"+a.tagName,e;while((e=a.nextSibling)&&e.tagName!=d){e.removeNode()}if(e){e.removeNode()}}a.parentNode.replaceChild(c,a);return c},initElement:function(a){a=this.q(a);a.getContext=function(){if(this.l){return this.l}return this.l=new K(this)};a.attachEvent("onpropertychange",V);a.attachEvent("onresize",
W);var b=a.attributes;if(b.width&&b.width.specified){a.style.width=b.width.nodeValue+"px"}else{a.width=a.clientWidth}if(b.height&&b.height.specified){a.style.height=b.height.nodeValue+"px"}else{a.height=a.clientHeight}return a}};function V(a){var b=a.srcElement;switch(a.propertyName){case "width":b.style.width=b.attributes.width.nodeValue+"px";b.getContext().clearRect();break;case "height":b.style.height=b.attributes.height.nodeValue+"px";b.getContext().clearRect();break}}function W(a){var b=a.srcElement;
if(b.firstChild){b.firstChild.style.width=b.clientWidth+"px";b.firstChild.style.height=b.clientHeight+"px"}}Q.init();var R=[];for(var E=0;E<16;E++){for(var F=0;F<16;F++){R[E*16+F]=E.toString(16)+F.toString(16)}}function J(){return[[1,0,0],[0,1,0],[0,0,1]]}function G(a,b){var c=J();for(var d=0;d<3;d++){for(var e=0;e<3;e++){var g=0;for(var h=0;h<3;h++){g+=a[d][h]*b[h][e]}c[d][e]=g}}return c}function N(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=
a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX=a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.d=a.d;b.e=a.e}function O(a){var b,c=1;a=String(a);if(a.substring(0,3)=="rgb"){var d=a.indexOf("(",3),e=a.indexOf(")",d+1),g=a.substring(d+1,e).split(",");b="#";for(var h=0;h<3;h++){b+=R[Number(g[h])]}if(g.length==4&&a.substr(3,1)=="a"){c=g[3]}}else{b=a}return[b,c]}function S(a){switch(a){case "butt":return"flat";case "round":return"round";
case "square":default:return"square"}}function K(a){this.a=J();this.m=[];this.k=[];this.c=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=m*1;this.globalAlpha=1;this.canvas=a;var b=a.ownerDocument.createElement("div");b.style.width=a.clientWidth+"px";b.style.height=a.clientHeight+"px";b.style.overflow="hidden";b.style.position="absolute";a.appendChild(b);this.j=b;this.d=1;this.e=1}var j=K.prototype;j.clearRect=function(){this.j.innerHTML=
"";this.c=[]};j.beginPath=function(){this.c=[]};j.moveTo=function(a,b){this.c.push({type:"moveTo",x:a,y:b});this.f=a;this.g=b};j.lineTo=function(a,b){this.c.push({type:"lineTo",x:a,y:b});this.f=a;this.g=b};j.bezierCurveTo=function(a,b,c,d,e,g){this.c.push({type:"bezierCurveTo",cp1x:a,cp1y:b,cp2x:c,cp2y:d,x:e,y:g});this.f=e;this.g=g};j.quadraticCurveTo=function(a,b,c,d){var e=this.f+0.6666666666666666*(a-this.f),g=this.g+0.6666666666666666*(b-this.g),h=e+(c-this.f)/3,l=g+(d-this.g)/3;this.bezierCurveTo(e,
g,h,l,c,d)};j.arc=function(a,b,c,d,e,g){c*=m;var h=g?"at":"wa",l=a+M(d)*c-A,n=b+L(d)*c-A,o=a+M(e)*c-A,f=b+L(e)*c-A;if(l==o&&!g){l+=0.125}this.c.push({type:h,x:a,y:b,radius:c,xStart:l,yStart:n,xEnd:o,yEnd:f})};j.rect=function(a,b,c,d){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath()};j.strokeRect=function(a,b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.stroke()};j.fillRect=function(a,
b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.fill()};j.createLinearGradient=function(a,b,c,d){var e=new H("gradient");return e};j.createRadialGradient=function(a,b,c,d,e,g){var h=new H("gradientradial");h.n=c;h.o=g;h.i.x=a;h.i.y=b;return h};j.drawImage=function(a,b){var c,d,e,g,h,l,n,o,f=a.runtimeStyle.width,k=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var q=a.width,r=a.height;a.runtimeStyle.width=
f;a.runtimeStyle.height=k;if(arguments.length==3){c=arguments[1];d=arguments[2];h=(l=0);n=(e=q);o=(g=r)}else if(arguments.length==5){c=arguments[1];d=arguments[2];e=arguments[3];g=arguments[4];h=(l=0);n=q;o=r}else if(arguments.length==9){h=arguments[1];l=arguments[2];n=arguments[3];o=arguments[4];c=arguments[5];d=arguments[6];e=arguments[7];g=arguments[8]}else{throw"Invalid number of arguments";}var s=this.b(c,d),t=[],v=10,w=10;t.push(" <g_vml_:group",' coordsize="',m*v,",",m*w,'"',' coordorigin="0,0"',
' style="width:',v,";height:",w,";position:absolute;");if(this.a[0][0]!=1||this.a[0][1]){var x=[];x.push("M11='",this.a[0][0],"',","M12='",this.a[1][0],"',","M21='",this.a[0][1],"',","M22='",this.a[1][1],"',","Dx='",i(s.x/m),"',","Dy='",i(s.y/m),"'");var p=s,y=this.b(c+e,d),z=this.b(c,d+g),B=this.b(c+e,d+g);p.x=Math.max(p.x,y.x,z.x,B.x);p.y=Math.max(p.y,y.y,z.y,B.y);t.push("padding:0 ",i(p.x/m),"px ",i(p.y/m),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",x.join(""),", sizingmethod='clip');")}else{t.push("top:",
i(s.y/m),"px;left:",i(s.x/m),"px;")}t.push(' ">','<g_vml_:image src="',a.src,'"',' style="width:',m*e,";"," height:",m*g,';"',' cropleft="',h/q,'"',' croptop="',l/r,'"',' cropright="',(q-h-n)/q,'"',' cropbottom="',(r-l-o)/r,'"'," />","</g_vml_:group>");this.j.insertAdjacentHTML("BeforeEnd",t.join(""))};j.stroke=function(a){var b=[],c=O(a?this.fillStyle:this.strokeStyle),d=c[0],e=c[1]*this.globalAlpha,g=10,h=10;b.push("<g_vml_:shape",' fillcolor="',d,'"',' filled="',Boolean(a),'"',' style="position:absolute;width:',
g,";height:",h,';"',' coordorigin="0 0" coordsize="',m*g," ",m*h,'"',' stroked="',!a,'"',' strokeweight="',this.lineWidth,'"',' strokecolor="',d,'"',' path="');var l={x:null,y:null},n={x:null,y:null};for(var o=0;o<this.c.length;o++){var f=this.c[o];if(f.type=="moveTo"){b.push(" m ");var k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else if(f.type=="lineTo"){b.push(" l ");var k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else if(f.type=="close"){b.push(" x ")}else if(f.type=="bezierCurveTo"){b.push(" c ");
var k=this.b(f.x,f.y),q=this.b(f.cp1x,f.cp1y),r=this.b(f.cp2x,f.cp2y);b.push(i(q.x),",",i(q.y),",",i(r.x),",",i(r.y),",",i(k.x),",",i(k.y))}else if(f.type=="at"||f.type=="wa"){b.push(" ",f.type," ");var k=this.b(f.x,f.y),s=this.b(f.xStart,f.yStart),t=this.b(f.xEnd,f.yEnd);b.push(i(k.x-this.d*f.radius),",",i(k.y-this.e*f.radius)," ",i(k.x+this.d*f.radius),",",i(k.y+this.e*f.radius)," ",i(s.x),",",i(s.y)," ",i(t.x),",",i(t.y))}if(k){if(l.x==null||k.x<l.x){l.x=k.x}if(n.x==null||k.x>n.x){n.x=k.x}if(l.y==
null||k.y<l.y){l.y=k.y}if(n.y==null||k.y>n.y){n.y=k.y}}}b.push(' ">');if(typeof this.fillStyle=="object"){var v={x:"50%",y:"50%"},w=n.x-l.x,x=n.y-l.y,p=w>x?w:x;v.x=i(this.fillStyle.i.x/w*100+50)+"%";v.y=i(this.fillStyle.i.y/x*100+50)+"%";var y=[];if(this.fillStyle.p=="gradientradial"){var z=this.fillStyle.n/p*100,B=this.fillStyle.o/p*100-z}else{var z=0,B=100}var C={offset:null,color:null},D={offset:null,color:null};this.fillStyle.h.sort(function(T,U){return T.offset-U.offset});for(var o=0;o<this.fillStyle.h.length;o++){var u=
this.fillStyle.h[o];y.push(u.offset*B+z,"% ",u.color,",");if(u.offset>C.offset||C.offset==null){C.offset=u.offset;C.color=u.color}if(u.offset<D.offset||D.offset==null){D.offset=u.offset;D.color=u.color}}y.pop();b.push("<g_vml_:fill",' color="',D.color,'"',' color2="',C.color,'"',' type="',this.fillStyle.p,'"',' focusposition="',v.x,", ",v.y,'"',' colors="',y.join(""),'"',' opacity="',e,'" />')}else if(a){b.push('<g_vml_:fill color="',d,'" opacity="',e,'" />')}else{b.push("<g_vml_:stroke",' opacity="',
e,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',S(this.lineCap),'"',' weight="',this.lineWidth,'px"',' color="',d,'" />')}b.push("</g_vml_:shape>");this.j.insertAdjacentHTML("beforeEnd",b.join(""));this.c=[]};j.fill=function(){this.stroke(true)};j.closePath=function(){this.c.push({type:"close"})};j.b=function(a,b){return{x:m*(a*this.a[0][0]+b*this.a[1][0]+this.a[2][0])-A,y:m*(a*this.a[0][1]+b*this.a[1][1]+this.a[2][1])-A}};j.save=function(){var a={};N(this,a);
this.k.push(a);this.m.push(this.a);this.a=G(J(),this.a)};j.restore=function(){N(this.k.pop(),this);this.a=this.m.pop()};j.translate=function(a,b){var c=[[1,0,0],[0,1,0],[a,b,1]];this.a=G(c,this.a)};j.rotate=function(a){var b=M(a),c=L(a),d=[[b,c,0],[-c,b,0],[0,0,1]];this.a=G(d,this.a)};j.scale=function(a,b){this.d*=a;this.e*=b;var c=[[a,0,0],[0,b,0],[0,0,1]];this.a=G(c,this.a)};j.clip=function(){};j.arcTo=function(){};j.createPattern=function(){return new P};function H(a){this.p=a;this.n=0;this.o=
0;this.h=[];this.i={x:0,y:0}}H.prototype.addColorStop=function(a,b){b=O(b);this.h.push({offset:1-a,color:b})};function P(){}G_vmlCanvasManager=Q;CanvasRenderingContext2D=K;CanvasGradient=H;CanvasPattern=P})()};

View File

@ -0,0 +1,785 @@
// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Known Issues:
//
// * Patterns are not implemented.
// * Radial gradient are not implemented. The VML version of these look very
// different from the canvas one.
// * Clipping paths are not implemented.
// * Coordsize. The width and height attribute have higher priority than the
// width and height style values which isn't correct.
// * Painting mode isn't implemented.
// * Canvas width/height should is using content-box by default. IE in
// Quirks mode will draw the canvas using border-box. Either change your
// doctype to HTML5
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
// or use Box Sizing Behavior from WebFX
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
// * Optimize. There is always room for speed improvements.
// only add this code if we do not already have a canvas implementation
if (!window.CanvasRenderingContext2D) {
(function () {
// alias some functions to make (compiled) code shorter
var m = Math;
var mr = m.round;
var ms = m.sin;
var mc = m.cos;
// this is used for sub pixel precision
var Z = 10;
var Z2 = Z / 2;
var G_vmlCanvasManager_ = {
init: function (opt_doc) {
var doc = opt_doc || document;
if (/MSIE/.test(navigator.userAgent) && !window.opera) {
var self = this;
doc.attachEvent("onreadystatechange", function () {
self.init_(doc);
});
}
},
init_: function (doc) {
if (doc.readyState == "complete") {
// create xmlns
if (!doc.namespaces["g_vml_"]) {
doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
}
// setup default css
var ss = doc.createStyleSheet();
ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
// default size is 300x150 in Gecko and Opera
"text-align:left;width:300px;height:150px}" +
"g_vml_\\:*{behavior:url(#default#VML)}";
// find all canvas elements
var els = doc.getElementsByTagName("canvas");
for (var i = 0; i < els.length; i++) {
if (!els[i].getContext) {
this.initElement(els[i]);
}
}
}
},
fixElement_: function (el) {
// in IE before version 5.5 we would need to add HTML: to the tag name
// but we do not care about IE before version 6
var outerHTML = el.outerHTML;
var newEl = el.ownerDocument.createElement(outerHTML);
// if the tag is still open IE has created the children as siblings and
// it has also created a tag with the name "/FOO"
if (outerHTML.slice(-2) != "/>") {
var tagName = "/" + el.tagName;
var ns;
// remove content
while ((ns = el.nextSibling) && ns.tagName != tagName) {
ns.removeNode();
}
// remove the incorrect closing tag
if (ns) {
ns.removeNode();
}
}
el.parentNode.replaceChild(newEl, el);
return newEl;
},
/**
* Public initializes a canvas element so that it can be used as canvas
* element from now on. This is called automatically before the page is
* loaded but if you are creating elements using createElement you need to
* make sure this is called on the element.
* @param {HTMLElement} el The canvas element to initialize.
* @return {HTMLElement} the element that was created.
*/
initElement: function (el) {
el = this.fixElement_(el);
el.getContext = function () {
if (this.context_) {
return this.context_;
}
return this.context_ = new CanvasRenderingContext2D_(this);
};
// do not use inline function because that will leak memory
el.attachEvent('onpropertychange', onPropertyChange);
el.attachEvent('onresize', onResize);
var attrs = el.attributes;
if (attrs.width && attrs.width.specified) {
// TODO: use runtimeStyle and coordsize
// el.getContext().setWidth_(attrs.width.nodeValue);
el.style.width = attrs.width.nodeValue + "px";
} else {
el.width = el.clientWidth;
}
if (attrs.height && attrs.height.specified) {
// TODO: use runtimeStyle and coordsize
// el.getContext().setHeight_(attrs.height.nodeValue);
el.style.height = attrs.height.nodeValue + "px";
} else {
el.height = el.clientHeight;
}
//el.getContext().setCoordsize_()
return el;
}
};
function onPropertyChange(e) {
var el = e.srcElement;
switch (e.propertyName) {
case 'width':
el.style.width = el.attributes.width.nodeValue + "px";
el.getContext().clearRect();
break;
case 'height':
el.style.height = el.attributes.height.nodeValue + "px";
el.getContext().clearRect();
break;
}
}
function onResize(e) {
var el = e.srcElement;
if (el.firstChild) {
el.firstChild.style.width = el.clientWidth + 'px';
el.firstChild.style.height = el.clientHeight + 'px';
}
}
G_vmlCanvasManager_.init();
// precompute "00" to "FF"
var dec2hex = [];
for (var i = 0; i < 16; i++) {
for (var j = 0; j < 16; j++) {
dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
}
}
function createMatrixIdentity() {
return [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
}
function matrixMultiply(m1, m2) {
var result = createMatrixIdentity();
for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
var sum = 0;
for (var z = 0; z < 3; z++) {
sum += m1[x][z] * m2[z][y];
}
result[x][y] = sum;
}
}
return result;
}
function copyState(o1, o2) {
o2.fillStyle = o1.fillStyle;
o2.lineCap = o1.lineCap;
o2.lineJoin = o1.lineJoin;
o2.lineWidth = o1.lineWidth;
o2.miterLimit = o1.miterLimit;
o2.shadowBlur = o1.shadowBlur;
o2.shadowColor = o1.shadowColor;
o2.shadowOffsetX = o1.shadowOffsetX;
o2.shadowOffsetY = o1.shadowOffsetY;
o2.strokeStyle = o1.strokeStyle;
o2.arcScaleX_ = o1.arcScaleX_;
o2.arcScaleY_ = o1.arcScaleY_;
}
function processStyle(styleString) {
var str, alpha = 1;
styleString = String(styleString);
if (styleString.substring(0, 3) == "rgb") {
var start = styleString.indexOf("(", 3);
var end = styleString.indexOf(")", start + 1);
var guts = styleString.substring(start + 1, end).split(",");
str = "#";
for (var i = 0; i < 3; i++) {
str += dec2hex[Number(guts[i])];
}
if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
alpha = guts[3];
}
} else {
str = styleString;
}
return [str, alpha];
}
function processLineCap(lineCap) {
switch (lineCap) {
case "butt":
return "flat";
case "round":
return "round";
case "square":
default:
return "square";
}
}
/**
* This class implements CanvasRenderingContext2D interface as described by
* the WHATWG.
* @param {HTMLElement} surfaceElement The element that the 2D context should
* be associated with
*/
function CanvasRenderingContext2D_(surfaceElement) {
this.m_ = createMatrixIdentity();
this.mStack_ = [];
this.aStack_ = [];
this.currentPath_ = [];
// Canvas context properties
this.strokeStyle = "#000";
this.fillStyle = "#000";
this.lineWidth = 1;
this.lineJoin = "miter";
this.lineCap = "butt";
this.miterLimit = Z * 1;
this.globalAlpha = 1;
this.canvas = surfaceElement;
var el = surfaceElement.ownerDocument.createElement('div');
el.style.width = surfaceElement.clientWidth + 'px';
el.style.height = surfaceElement.clientHeight + 'px';
el.style.overflow = 'hidden';
el.style.position = 'absolute';
surfaceElement.appendChild(el);
this.element_ = el;
this.arcScaleX_ = 1;
this.arcScaleY_ = 1;
}
var contextPrototype = CanvasRenderingContext2D_.prototype;
contextPrototype.clearRect = function() {
this.element_.innerHTML = "";
this.currentPath_ = [];
};
contextPrototype.beginPath = function() {
// TODO: Branch current matrix so that save/restore has no effect
// as per safari docs.
this.currentPath_ = [];
};
contextPrototype.moveTo = function(aX, aY) {
this.currentPath_.push({type: "moveTo", x: aX, y: aY});
this.currentX_ = aX;
this.currentY_ = aY;
};
contextPrototype.lineTo = function(aX, aY) {
this.currentPath_.push({type: "lineTo", x: aX, y: aY});
this.currentX_ = aX;
this.currentY_ = aY;
};
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
aCP2x, aCP2y,
aX, aY) {
this.currentPath_.push({type: "bezierCurveTo",
cp1x: aCP1x,
cp1y: aCP1y,
cp2x: aCP2x,
cp2y: aCP2y,
x: aX,
y: aY});
this.currentX_ = aX;
this.currentY_ = aY;
};
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
// the following is lifted almost directly from
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
var cp2x = cp1x + (aX - this.currentX_) / 3.0;
var cp2y = cp1y + (aY - this.currentY_) / 3.0;
this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
};
contextPrototype.arc = function(aX, aY, aRadius,
aStartAngle, aEndAngle, aClockwise) {
aRadius *= Z;
var arcType = aClockwise ? "at" : "wa";
var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
// IE won't render arches drawn counter clockwise if xStart == xEnd.
if (xStart == xEnd && !aClockwise) {
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
// that can be represented in binary
}
this.currentPath_.push({type: arcType,
x: aX,
y: aY,
radius: aRadius,
xStart: xStart,
yStart: yStart,
xEnd: xEnd,
yEnd: yEnd});
};
contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
};
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
// Will destroy any existing path (same as FF behaviour)
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.stroke();
};
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
// Will destroy any existing path (same as FF behaviour)
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.fill();
};
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
var gradient = new CanvasGradient_("gradient");
return gradient;
};
contextPrototype.createRadialGradient = function(aX0, aY0,
aR0, aX1,
aY1, aR1) {
var gradient = new CanvasGradient_("gradientradial");
gradient.radius1_ = aR0;
gradient.radius2_ = aR1;
gradient.focus_.x = aX0;
gradient.focus_.y = aY0;
return gradient;
};
contextPrototype.drawImage = function (image, var_args) {
var dx, dy, dw, dh, sx, sy, sw, sh;
// to find the original width we overide the width and height
var oldRuntimeWidth = image.runtimeStyle.width;
var oldRuntimeHeight = image.runtimeStyle.height;
image.runtimeStyle.width = 'auto';
image.runtimeStyle.height = 'auto';
// get the original size
var w = image.width;
var h = image.height;
// and remove overides
image.runtimeStyle.width = oldRuntimeWidth;
image.runtimeStyle.height = oldRuntimeHeight;
if (arguments.length == 3) {
dx = arguments[1];
dy = arguments[2];
sx = sy = 0;
sw = dw = w;
sh = dh = h;
} else if (arguments.length == 5) {
dx = arguments[1];
dy = arguments[2];
dw = arguments[3];
dh = arguments[4];
sx = sy = 0;
sw = w;
sh = h;
} else if (arguments.length == 9) {
sx = arguments[1];
sy = arguments[2];
sw = arguments[3];
sh = arguments[4];
dx = arguments[5];
dy = arguments[6];
dw = arguments[7];
dh = arguments[8];
} else {
throw "Invalid number of arguments";
}
var d = this.getCoords_(dx, dy);
var w2 = sw / 2;
var h2 = sh / 2;
var vmlStr = [];
var W = 10;
var H = 10;
// For some reason that I've now forgotten, using divs didn't work
vmlStr.push(' <g_vml_:group',
' coordsize="', Z * W, ',', Z * H, '"',
' coordorigin="0,0"' ,
' style="width:', W, ';height:', H, ';position:absolute;');
// If filters are necessary (rotation exists), create them
// filters are bog-slow, so only create them if abbsolutely necessary
// The following check doesn't account for skews (which don't exist
// in the canvas spec (yet) anyway.
if (this.m_[0][0] != 1 || this.m_[0][1]) {
var filter = [];
// Note the 12/21 reversal
filter.push("M11='", this.m_[0][0], "',",
"M12='", this.m_[1][0], "',",
"M21='", this.m_[0][1], "',",
"M22='", this.m_[1][1], "',",
"Dx='", mr(d.x / Z), "',",
"Dy='", mr(d.y / Z), "'");
// Bounding box calculation (need to minimize displayed area so that
// filters don't waste time on unused pixels.
var max = d;
var c2 = this.getCoords_(dx + dw, dy);
var c3 = this.getCoords_(dx, dy + dh);
var c4 = this.getCoords_(dx + dw, dy + dh);
max.x = Math.max(max.x, c2.x, c3.x, c4.x);
max.y = Math.max(max.y, c2.y, c3.y, c4.y);
vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
filter.join(""), ", sizingmethod='clip');");
} else {
vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;");
}
vmlStr.push(' ">' ,
'<g_vml_:image src="', image.src, '"',
' style="width:', Z * dw, ';',
' height:', Z * dh, ';"',
' cropleft="', sx / w, '"',
' croptop="', sy / h, '"',
' cropright="', (w - sx - sw) / w, '"',
' cropbottom="', (h - sy - sh) / h, '"',
' />',
'</g_vml_:group>');
this.element_.insertAdjacentHTML("BeforeEnd",
vmlStr.join(""));
};
contextPrototype.stroke = function(aFill) {
var lineStr = [];
var lineOpen = false;
var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
var color = a[0];
var opacity = a[1] * this.globalAlpha;
var W = 10;
var H = 10;
lineStr.push('<g_vml_:shape',
' fillcolor="', color, '"',
' filled="', Boolean(aFill), '"',
' style="position:absolute;width:', W, ';height:', H, ';"',
' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
' stroked="', !aFill, '"',
' strokeweight="', this.lineWidth, '"',
' strokecolor="', color, '"',
' path="');
var newSeq = false;
var min = {x: null, y: null};
var max = {x: null, y: null};
for (var i = 0; i < this.currentPath_.length; i++) {
var p = this.currentPath_[i];
if (p.type == "moveTo") {
lineStr.push(" m ");
var c = this.getCoords_(p.x, p.y);
lineStr.push(mr(c.x), ",", mr(c.y));
} else if (p.type == "lineTo") {
lineStr.push(" l ");
var c = this.getCoords_(p.x, p.y);
lineStr.push(mr(c.x), ",", mr(c.y));
} else if (p.type == "close") {
lineStr.push(" x ");
} else if (p.type == "bezierCurveTo") {
lineStr.push(" c ");
var c = this.getCoords_(p.x, p.y);
var c1 = this.getCoords_(p.cp1x, p.cp1y);
var c2 = this.getCoords_(p.cp2x, p.cp2y);
lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
mr(c2.x), ",", mr(c2.y), ",",
mr(c.x), ",", mr(c.y));
} else if (p.type == "at" || p.type == "wa") {
lineStr.push(" ", p.type, " ");
var c = this.getCoords_(p.x, p.y);
var cStart = this.getCoords_(p.xStart, p.yStart);
var cEnd = this.getCoords_(p.xEnd, p.yEnd);
lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
mr(c.y - this.arcScaleY_ * p.radius), " ",
mr(c.x + this.arcScaleX_ * p.radius), ",",
mr(c.y + this.arcScaleY_ * p.radius), " ",
mr(cStart.x), ",", mr(cStart.y), " ",
mr(cEnd.x), ",", mr(cEnd.y));
}
// TODO: Following is broken for curves due to
// move to proper paths.
// Figure out dimensions so we can do gradient fills
// properly
if(c) {
if (min.x == null || c.x < min.x) {
min.x = c.x;
}
if (max.x == null || c.x > max.x) {
max.x = c.x;
}
if (min.y == null || c.y < min.y) {
min.y = c.y;
}
if (max.y == null || c.y > max.y) {
max.y = c.y;
}
}
}
lineStr.push(' ">');
if (typeof this.fillStyle == "object") {
var focus = {x: "50%", y: "50%"};
var width = (max.x - min.x);
var height = (max.y - min.y);
var dimension = (width > height) ? width : height;
focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
var colors = [];
// inside radius (%)
if (this.fillStyle.type_ == "gradientradial") {
var inside = (this.fillStyle.radius1_ / dimension * 100);
// percentage that outside radius exceeds inside radius
var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
} else {
var inside = 0;
var expansion = 100;
}
var insidecolor = {offset: null, color: null};
var outsidecolor = {offset: null, color: null};
// We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
// won't interpret it correctly
this.fillStyle.colors_.sort(function (cs1, cs2) {
return cs1.offset - cs2.offset;
});
for (var i = 0; i < this.fillStyle.colors_.length; i++) {
var fs = this.fillStyle.colors_[i];
colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
insidecolor.offset = fs.offset;
insidecolor.color = fs.color;
}
if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
outsidecolor.offset = fs.offset;
outsidecolor.color = fs.color;
}
}
colors.pop();
lineStr.push('<g_vml_:fill',
' color="', outsidecolor.color, '"',
' color2="', insidecolor.color, '"',
' type="', this.fillStyle.type_, '"',
' focusposition="', focus.x, ', ', focus.y, '"',
' colors="', colors.join(""), '"',
' opacity="', opacity, '" />');
} else if (aFill) {
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
} else {
lineStr.push(
'<g_vml_:stroke',
' opacity="', opacity,'"',
' joinstyle="', this.lineJoin, '"',
' miterlimit="', this.miterLimit, '"',
' endcap="', processLineCap(this.lineCap) ,'"',
' weight="', this.lineWidth, 'px"',
' color="', color,'" />'
);
}
lineStr.push("</g_vml_:shape>");
this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
//this.currentPath_ = [];
};
contextPrototype.fill = function() {
this.stroke(true);
};
contextPrototype.closePath = function() {
this.currentPath_.push({type: "close"});
};
/**
* @private
*/
contextPrototype.getCoords_ = function(aX, aY) {
return {
x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
}
};
contextPrototype.save = function() {
var o = {};
copyState(this, o);
this.aStack_.push(o);
this.mStack_.push(this.m_);
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
};
contextPrototype.restore = function() {
copyState(this.aStack_.pop(), this);
this.m_ = this.mStack_.pop();
};
contextPrototype.translate = function(aX, aY) {
var m1 = [
[1, 0, 0],
[0, 1, 0],
[aX, aY, 1]
];
this.m_ = matrixMultiply(m1, this.m_);
};
contextPrototype.rotate = function(aRot) {
var c = mc(aRot);
var s = ms(aRot);
var m1 = [
[c, s, 0],
[-s, c, 0],
[0, 0, 1]
];
this.m_ = matrixMultiply(m1, this.m_);
};
contextPrototype.scale = function(aX, aY) {
this.arcScaleX_ *= aX;
this.arcScaleY_ *= aY;
var m1 = [
[aX, 0, 0],
[0, aY, 0],
[0, 0, 1]
];
this.m_ = matrixMultiply(m1, this.m_);
};
/******** STUBS ********/
contextPrototype.clip = function() {
// TODO: Implement
};
contextPrototype.arcTo = function() {
// TODO: Implement
};
contextPrototype.createPattern = function() {
return new CanvasPattern_;
};
// Gradient / Pattern Stubs
function CanvasGradient_(aType) {
this.type_ = aType;
this.radius1_ = 0;
this.radius2_ = 0;
this.colors_ = [];
this.focus_ = {x: 0, y: 0};
}
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
aColor = processStyle(aColor);
this.colors_.push({offset: 1-aOffset, color: aColor});
};
function CanvasPattern_() {}
// set up externs
G_vmlCanvasManager = G_vmlCanvasManager_;
CanvasRenderingContext2D = CanvasRenderingContext2D_;
CanvasGradient = CanvasGradient_;
CanvasPattern = CanvasPattern_;
})();
} // if

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B