Skip to content

Commit

Permalink
WebUI: add host information to the report view (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
liuch committed Jan 23, 2025
1 parent 71bf0c8 commit 4c932e7
Show file tree
Hide file tree
Showing 10 changed files with 595 additions and 19 deletions.
1 change: 1 addition & 0 deletions classes/Database/DatabaseConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ abstract class DatabaseConnector
{
protected static $names = [
'domain' => 'DomainMapper',
'host' => 'HostMapper',
'report' => 'ReportMapper',
'report-log' => 'ReportLogMapper',
'setting' => 'SettingMapper',
Expand Down
44 changes: 44 additions & 0 deletions classes/Database/HostMapperInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
* Copyright (C) 2025 Aleksey Andreev (liuch)
*
* Available at:
* https://github.com/liuch/dmarc-srg
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* =========================
*
* This file contains the HostMapperInterface
*
* @category API
* @package DmarcSrg
* @author Aleksey Andreev (liuch)
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3
*/

namespace Liuch\DmarcSrg\Database;

interface HostMapperInterface
{
/**
* Return an array with statistics
*
* @param array $data Array with filter parameters
*
* @return array
*/
public function statistics(array &$data, int $user_id): array;
}
110 changes: 110 additions & 0 deletions classes/Database/Mariadb/HostMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

/**
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
* Copyright (C) 2025 Aleksey Andreev (liuch)
*
* Available at:
* https://github.com/liuch/dmarc-srg
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* =========================
*
* This file contains the HostMapper
*
* @category API
* @package DmarcSrg
* @author Aleksey Andreev (liuch)
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3
*/

namespace Liuch\DmarcSrg\Database\Mariadb;

use Liuch\DmarcSrg\DateTime;
use Liuch\DmarcSrg\Database\HostMapperInterface;
use Liuch\DmarcSrg\Exception\DatabaseFatalException;

/**
* HostMapper class implementation for MariaDB
*/
class HostMapper implements HostMapperInterface
{
/** @var \Liuch\DmarcSrg\Database\DatabaseConnector */
private $connector = null;

/**
* The constructor
*
* @param \Liuch\DmarcSrg\Database\DatabaseConnector $connector DatabaseConnector instance of the current database
*/
public function __construct(object $connector)
{
$this->connector = $connector;
}

/**
* Return an array with statistics
*
* @param array $data Array with filter parameters
* @param int $user_id User ID
*
* @return array
*/
public function statistics(array &$data, int $user_id): array
{
$res = [];
if (!$user_id) {
$user_domains1 = '';
$user_domains2 = '';
} else {
$user_domains1 = ' INNER JOIN `' . $this->connector->tablePrefix('userdomains')
. '`AS `ud` ON `rp`.`domain_id` = `ud`.`domain_id`';
$user_domains2 = ' AND `user_id` = ' . $user_id;
}
$db = $this->connector->dbh();
$db->beginTransaction();
try {
$st = $db->prepare(
'SELECT COUNT(*), SUM(`rc`) FROM (SELECT COUNT(`report_id`), SUM(`rcount`) AS `rc` FROM `'
. $this->connector->tablePrefix('rptrecords') . '` AS `rr`'
. ' INNER JOIN `reports` AS `rp` ON `rr`.`report_id` = `rp`.`id`' . $user_domains1
. ' WHERE `rr`.`ip` = ?' . $user_domains2 . ' GROUP BY `report_id`) as t'
);
$st->bindValue(1, inet_pton($data['ip']), \PDO::PARAM_STR);
$st->execute();
$row = $st->fetch(\PDO::FETCH_NUM);
$res['reports'] = intval($row[0]);
$res['messages'] = intval($row[1]);
$st->closeCursor();
$st = $db->prepare(
'SELECT `rp`.`id`, `begin_time` FROM `' . $this->connector->tablePrefix('rptrecords') . '` AS `rr`'
. ' INNER JOIN `reports` AS `rp` ON `rr`.`report_id` = `rp`.`id`' . $user_domains1
. ' WHERE `rr`.`ip` = ?' . $user_domains2 . ' GROUP BY `report_id` ORDER BY `begin_time` DESC LIMIT 2'
);
$st->bindValue(1, inet_pton($data['ip']), \PDO::PARAM_STR);
$st->execute();
$last_report = [];
while ($row = $st->fetch(\PDO::FETCH_NUM)) {
$last_report[] = new DateTime($row[1]);
}
$res['last_report'] = $last_report;
$st->closeCursor();
$db->commit();
} catch (\PDOException $e) {
$db->rollBack();
throw new DatabaseFatalException('Failed to get host data', -1, $e);
}
return $res;
}
}
144 changes: 144 additions & 0 deletions classes/Hosts/Host.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

/**
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
* Copyright (C) 2025 Aleksey Andreev (liuch)
*
* Available at:
* https://github.com/liuch/dmarc-srg
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* =========================
*
* This file contains the class Host
*
* @category API
* @package DmarcSrg
* @author Aleksey Andreev (liuch)
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3
*/

namespace Liuch\DmarcSrg\Hosts;

use Liuch\DmarcSrg\Core;
use Liuch\DmarcSrg\Exception\SoftException;
use Liuch\DmarcSrg\Exception\LogicException;
use Liuch\DmarcSrg\Exception\RuntimeException;

/**
* This class is designed for storing and manipulating hosts data and utilites.
*/
class Host
{
private $db = null;
private $rip = null;
private $rdns = null;
private $data = [
'ip' => null
];

/**
* The constructor
*
* @param string $ip IP address
* @param \Liuch\DmarcSrg\Database\DatabaseController $db The database controller
*
* @return void
*/
public function __construct(string $ip, $db = null)
{
$this->db = $db ?? Core::instance()->database();
if (gettype($ip) != 'string' || empty($ip)) {
throw new LogicException('Incorrect host data');
}
if (!filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE
)) {
throw new SoftException('Incorrect IP address');
}
$this->data['ip'] = $ip;
}

/**
* Returns the reverse DNS hostname or an empty string if lookup fails
*
* @return string
*/
public function rdnsName(): string
{
if (is_null($this->rdns)) {
$rdns = gethostbyaddr($this->data['ip']);
if ($rdns === false) {
throw new RuntimeException('Failed to get the reverse DNS hostname');
}
if ($rdns === $this->data['ip']) {
$rdns = '';
}
$this->rdns = $rdns;
}
return $this->rdns;
}

/**
* Checks if the IP address resolved from the reverse DNS hostname matches the source IP address
*
* @return bool
*/
public function checkReverseIP(): bool
{
if (!is_null($this->rip)) {
return $this->rip;
}

$rname = $this->rdnsName();
if (empty($rname)) {
return false;
}

if (filter_var($this->data['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$type = DNS_A;
} else {
$type = DNS_AAAA;
}
$ip = inet_ntop(inet_pton($this->data['ip']));
$ip_res = dns_get_record($rname, $type);
if ($ip_res === false) {
throw new RuntimeException('Failed to resolve IP address');
}
$this->rip = false;
foreach ($ip_res as &$rec) {
$r = ($type === DNS_A) ? $rec['ip'] : $rec['ipv6'];
if ($ip === $r) {
$this->rip = true;
break;
}
}
unset($rec);
return $this->rip;
}

/*
* Returns an array with statistics of the host
*
* @param \Liuch\DmarcSrg\Users\User $user User for whom to get statistic
*
* @return array
*/
public function statistics($user): array
{
return $this->db->getMapper('host')->statistics($this->data, $user->id());
}
}
7 changes: 6 additions & 1 deletion public/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,11 @@ p.wait-message, p.error-message {
cursor: auto;
}
.report-record > .header {
position: relative;
display: flex;
flex-flow: row wrap;
gap: .5em;
justify-content: center;
padding: 0.4em 0;
border-bottom: 1px solid var(--color-br-default);
border-radius: 7px 7px 0 0;
Expand Down Expand Up @@ -723,7 +728,7 @@ p.wait-message, p.error-message {
text-align: left;
margin: auto 0;
}
.table-cell.fqdn, .table-cell.orgname, .table-cell.report-id, .table-cell.setting-value {
.table-cell.fqdn, .table-cell.orgname, .table-cell.report-id, .table-cell.setting-value, .hint-content li>span:nth-child(2) {
max-width: 15em;
overflow: hidden;
text-overflow: ellipsis;
Expand Down
44 changes: 42 additions & 2 deletions public/css/widgets.css
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,17 @@
border-color: var(--color-br-strong) transparent;
}
.hint-content h4 {
text-align: center;
font-size: 85%;
margin-bottom: 1em;
padding-bottom: 1em;
border-bottom: 1px dotted var(--color-br-strong);
}
.hint-content h5 {
margin: .5em 0;
}
.hint-content h4, .hint-content h5 {
font-size: 85%;
text-align: center;
}
.hint-content ul {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -396,3 +401,38 @@ multi-select[disabled] .multiselect-tags *, multi-select[disabled] .multiselect-
.multiselect-options li[aria-selected="true"].focused {
background-color: var(--color-bg-deleted);
}
.spinner {
display: inline-block;
}
.spinner > div {
display: inline-block;
background-color: currentColor;
width: 4px;
height: 1em;
margin: 0 2px;
animation: line-scale 1.1s infinite ease;
text-align: center;
}
.spinner > div:nth-child(1) {
animation-delay: -1.2s;
}
.spinner > div:nth-child(2) {
animation-delay: -1.1s;
}
.spinner > div:nth-child(3) {
animation-delay: -1s;
}
.spinner > div:nth-child(4) {
animation-delay: -.9s;
}
.spinner > div:nth-child(5) {
animation-delay: -.8s;
}
@keyframes line-scale {
0%, 40%, 100% {
transform: scaleY(.4);
}
20% {
transform: scaleY(1);
}
}
Loading

0 comments on commit 4c932e7

Please sign in to comment.