Merge branch 'translations' into patch-1

This commit is contained in:
Giuliano Bellini
2025-09-03 23:09:46 +02:00
committed by GitHub
100 changed files with 3748 additions and 3764 deletions

View File

@@ -601,7 +601,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/171437458?v=4",
"profile": "https://github.com/shu-kitamura",
"contributions": [
"code"
"code",
"translation"
]
},
{
@@ -721,6 +722,60 @@
"contributions": [
"platform"
]
},
{
"login": "islameehassan",
"name": "islameehassan",
"avatar_url": "https://avatars.githubusercontent.com/u/98806155?v=4",
"profile": "https://github.com/islameehassan",
"contributions": [
"code"
]
},
{
"login": "lionrayonnant",
"name": "Lion Rayonnant",
"avatar_url": "https://avatars.githubusercontent.com/u/106342136?v=4",
"profile": "https://github.com/lionrayonnant",
"contributions": [
"translation"
]
},
{
"login": "18601673727",
"name": "18601673727",
"avatar_url": "https://avatars.githubusercontent.com/u/3302620?v=4",
"profile": "https://github.com/18601673727",
"contributions": [
"translation"
]
},
{
"login": "AlleM43",
"name": "AlleM43",
"avatar_url": "https://avatars.githubusercontent.com/u/16104173?v=4",
"profile": "https://github.com/AlleM43",
"contributions": [
"platform"
]
},
{
"login": "aris1009",
"name": "Aris Konstantoulas",
"avatar_url": "https://avatars.githubusercontent.com/u/25184469?v=4",
"profile": "https://github.com/aris1009",
"contributions": [
"translation"
]
},
{
"login": "mmseng",
"name": "Matt Seng",
"avatar_url": "https://avatars.githubusercontent.com/u/8218085?v=4",
"profile": "https://github.com/mmseng",
"contributions": [
"financial"
]
}
],
"contributorsPerLine": 7,

View File

@@ -1,9 +1,6 @@
name: Docker
on:
# push:
# tags:
# - 'v[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
jobs:
@@ -12,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Extract version from Cargo.toml
id: cargo-version
@@ -21,6 +18,13 @@ jobs:
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Check there is no existing image with the same version
run: |
if docker manifest inspect ghcr.io/gyulyvgc/sniffnet:${{ env.VERSION }}; then
echo "Image with version ${{ env.VERSION }} already exists"
exit 1
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -43,4 +47,3 @@ jobs:
tags: |
ghcr.io/gyulyvgc/sniffnet:latest
ghcr.io/gyulyvgc/sniffnet:${{ env.VERSION }}
# ghcr.io/gyulyvgc/sniffnet:${{ github.ref_name }}

View File

@@ -38,7 +38,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Linux dependencies
if: matrix.os == 'ubuntu'
@@ -126,7 +126,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install dependencies
run: apt-get update -y && apt-get install -y curl build-essential
@@ -139,7 +139,7 @@ jobs:
- name: Install packaging tools
run: cargo install cargo-deb
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
name: build-ubuntu-${{ matrix.target }}
path: target/
@@ -158,6 +158,63 @@ jobs:
path: artifacts/
if-no-files-found: error
appimage:
runs-on: ubuntu-latest
container:
image: debian:latest
options: --privileged
needs: deb
strategy:
fail-fast: true
matrix:
include:
- arch: amd64
appimgarch: x86_64
- arch: arm64
appimgarch: aarch64
- arch: i386
appimgarch: i686
- arch: armhf
appimgarch: armhf
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Install dependencies
run: apt-get update -y && apt-get install -y git build-essential graphicsmagick-imagemagick-compat wget file desktop-file-utils libfuse2
- name: Download Debian package
uses: actions/download-artifact@v5
with:
name: deb-${{ matrix.arch }}
path: /target/
- name: Download pkg2appimage
shell: bash
run: git clone https://github.com/AppImageCommunity/pkg2appimage.git
- name: Replace placeholders
shell: bash
run: |
sed -i -e 's/REPLACE_TAG/Sniffnet_LinuxDEB_${{ matrix.arch }}.deb/g' resources/packaging/linux/AppImage/sniffnet.yml
sed -i -e 's/binary-${arch}/binary-${{ matrix.arch }}/g' pkg2appimage/functions.sh
- name: Repackage for AppImage
shell: bash
run: |
cd pkg2appimage
ARCH=${{ matrix.appimgarch }} FUNCTIONS_SH=$(pwd)/functions.sh ./pkg2appimage ../resources/packaging/linux/AppImage/sniffnet.yml
mkdir /out
mv out/*.AppImage /out/Sniffnet_LinuxAppImage_${{ matrix.arch }}.AppImage
- name: Upload package artifacts
uses: actions/upload-artifact@v4
with:
name: appimage-${{ matrix.arch }}
path: /out/
if-no-files-found: error
rpm:
runs-on: ubuntu-latest
container:
@@ -174,7 +231,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install dependencies
run: dnf update -y && dnf install -y @development-tools patchelf
@@ -187,7 +244,7 @@ jobs:
- name: Install packaging tools
run: cargo install cargo-generate-rpm
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
name: build-ubuntu-${{ matrix.target }}
path: target/
@@ -221,7 +278,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
@@ -231,7 +288,7 @@ jobs:
cargo install toml-cli
brew install create-dmg
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
name: build-macos-${{ matrix.target }}
path: target/
@@ -285,7 +342,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install dependencies
shell: powershell
@@ -306,7 +363,7 @@ jobs:
- name: Install packaging tools
run: cargo install cargo-wix
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
name: build-windows-${{ matrix.target }}
path: target/
@@ -318,9 +375,29 @@ jobs:
cargo wix --no-build --nocapture --target ${{ matrix.target }}
Move-Item -Path target\wix\sniffnet*.msi -Destination .\artifacts\Sniffnet_Windows_${{ matrix.arch }}.msi
- name: Upload package artifacts
- name: Upload unsigned package artifacts
id: upload-unsigned-artifact
uses: actions/upload-artifact@v4
with:
name: msi-${{ matrix.arch }}
path: artifacts/
if-no-files-found: error
- name: Sign package artifacts
uses: signpath/github-action-submit-signing-request@v1.3
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '3b533e02-73c3-4908-a018-d09a34498a6a'
project-slug: 'sniffnet'
signing-policy-slug: 'release-signing'
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: './artifacts'
- name: Upload signed package artifacts (overwrite unsigned)
uses: actions/upload-artifact@v4
with:
name: msi-${{ matrix.arch }}
path: artifacts/
if-no-files-found: error
overwrite: true

View File

@@ -32,7 +32,7 @@ jobs:
- os: windows
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

3
.gitignore vendored
View File

@@ -57,3 +57,6 @@ $RECYCLE.BIN/
### Custom... ###
lcov.info
*.pcap
node_modules
package.json
yarn.lock

View File

@@ -3,10 +3,21 @@ # Changelog
All Sniffnet releases with the relative changes are documented in this file.
## [UNRELEASED]
- Enhanced traffic filtering capabilities: Berkeley Packet Filter ([#937](https://github.com/GyulyVGC/sniffnet/pull/937) — fixes [#810](https://github.com/GyulyVGC/sniffnet/issues/810))
- Added support for `Linux SLL` link type, enabling to monitor the `any` interface on Linux ([#945](https://github.com/GyulyVGC/sniffnet/pull/945))
- Added _bits_ data representation ([#936](https://github.com/GyulyVGC/sniffnet/pull/936) — fixes [#506](https://github.com/GyulyVGC/sniffnet/issues/506))
- An AppImage of Sniffnet is now available ([#859](https://github.com/GyulyVGC/sniffnet/pull/859) — fixes [#900](https://github.com/GyulyVGC/sniffnet/issues/900))
- Added Dutch translation 🇳🇱 ([#854](https://github.com/GyulyVGC/sniffnet/pull/854))
- Improved configurations persistence across different runs of the app ([#938](https://github.com/GyulyVGC/sniffnet/pull/938) — fixes [#507](https://github.com/GyulyVGC/sniffnet/issues/507))
- The Windows Installer is now signed with a code signing certificate provided by the [SignPath Foundation](https://signpath.org/) ([#897](https://github.com/GyulyVGC/sniffnet/pull/897) — fixes [#894](https://github.com/GyulyVGC/sniffnet/issues/894))
- Updated some of the existing translations to v1.4:
- German ([#833](https://github.com/GyulyVGC/sniffnet/pull/833))
- Uzbek ([#834](https://github.com/GyulyVGC/sniffnet/pull/834))
- Simplified Chinese ([#838](https://github.com/GyulyVGC/sniffnet/pull/838))
- Japanese ([#849](https://github.com/GyulyVGC/sniffnet/pull/849))
- French ([#864](https://github.com/GyulyVGC/sniffnet/pull/864))
- Greek ([#879](https://github.com/GyulyVGC/sniffnet/pull/879))
- Fix support for IPinfo's databases (the most recent version renamed the `country` field to `country_code`)
## [1.4.0] - 2025-06-27
- Import PCAP files ([#795](https://github.com/GyulyVGC/sniffnet/pull/795) — fixes [#283](https://github.com/GyulyVGC/sniffnet/issues/283))

View File

@@ -6,105 +6,111 @@
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/18601673727"><img src="https://avatars.githubusercontent.com/u/3302620?v=4?s=100" width="100px;" alt="18601673727"/><br /><sub><b>18601673727</b></sub></a><br /><a href="#translation-18601673727" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://ads.fund"><img src="https://avatars.githubusercontent.com/u/202042116?v=4?s=100" width="100px;" alt="ADS Fund"/><br /><sub><b>ADS Fund</b></sub></a><br /><a href="#financial-ADS-Fund" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abdullahdevrel"><img src="https://avatars.githubusercontent.com/u/111275753?v=4?s=100" width="100px;" alt="Abdullah"/><br /><sub><b>Abdullah</b></sub></a><br /><a href="#ideas-abdullahdevrel" title="Ideas, Planning, & Feedback">🤔</a> <a href="#content-abdullahdevrel" title="Content">🖋</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nitefood"><img src="https://avatars.githubusercontent.com/u/24555810?v=4?s=100" width="100px;" alt="Adriano"/><br /><sub><b>Adriano</b></sub></a><br /><a href="#ideas-nitefood" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aguacero7"><img src="https://avatars.githubusercontent.com/u/72492241?v=4?s=100" width="100px;" alt="Aguacero 🌧️"/><br /><sub><b>Aguacero 🌧️</b></sub></a><br /><a href="#translation-aguacero7" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://tahinli.com"><img src="https://avatars.githubusercontent.com/u/96421894?v=4?s=100" width="100px;" alt="Ahmet Kaan GÜMÜŞ"/><br /><sub><b>Ahmet Kaan GÜMÜŞ</b></sub></a><br /><a href="#translation-Tahinli" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dutyrok"><img src="https://avatars.githubusercontent.com/u/68692503?v=4?s=100" width="100px;" alt="Alexandr Shashkin"/><br /><sub><b>Alexandr Shashkin</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/issues?q=author%3Adutyrok" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AmadeusGraves"><img src="https://avatars.githubusercontent.com/u/18572939?v=4?s=100" width="100px;" alt="AmadeusGraves"/><br /><sub><b>AmadeusGraves</b></sub></a><br /><a href="#translation-AmadeusGraves" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AlleM43"><img src="https://avatars.githubusercontent.com/u/16104173?v=4?s=100" width="100px;" alt="AlleM43"/><br /><sub><b>AlleM43</b></sub></a><br /><a href="#platform-AlleM43" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AmadeusGraves"><img src="https://avatars.githubusercontent.com/u/18572939?v=4?s=100" width="100px;" alt="AmadeusGraves"/><br /><sub><b>AmadeusGraves</b></sub></a><br /><a href="#translation-AmadeusGraves" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abousis"><img src="https://avatars.githubusercontent.com/u/25039645?v=4?s=100" width="100px;" alt="Angelos Bousis"/><br /><sub><b>Angelos Bousis</b></sub></a><br /><a href="#translation-abousis" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/acolombier"><img src="https://avatars.githubusercontent.com/u/7086688?v=4?s=100" width="100px;" alt="Antoine Colombier"/><br /><sub><b>Antoine Colombier</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=acolombier" title="Tests">⚠️</a> <a href="#translation-acolombier" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aris1009"><img src="https://avatars.githubusercontent.com/u/25184469?v=4?s=100" width="100px;" alt="Aris Konstantoulas"/><br /><sub><b>Aris Konstantoulas</b></sub></a><br /><a href="#translation-aris1009" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BugsQuanti"><img src="https://avatars.githubusercontent.com/u/168212217?v=4?s=100" width="100px;" alt="BugsQuanti"/><br /><sub><b>BugsQuanti</b></sub></a><br /><a href="#translation-BugsQuanti" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Charpy"><img src="https://avatars.githubusercontent.com/u/4827568?v=4?s=100" width="100px;" alt="Charpy"/><br /><sub><b>Charpy</b></sub></a><br /><a href="#translation-Charpy" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://toto.io"><img src="https://avatars.githubusercontent.com/u/2256579?v=4?s=100" width="100px;" alt="Christoph Wanja"/><br /><sub><b>Christoph Wanja</b></sub></a><br /><a href="#financial-iwan-uschka" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/colin99d"><img src="https://avatars.githubusercontent.com/u/72827203?v=4?s=100" width="100px;" alt="Colin Delahunty"/><br /><sub><b>Colin Delahunty</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=colin99d" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/corneliusroemer"><img src="https://avatars.githubusercontent.com/u/25161793?v=4?s=100" width="100px;" alt="Cornelius Roemer"/><br /><sub><b>Cornelius Roemer</b></sub></a><br /><a href="#ideas-corneliusroemer" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://cosminperram.com"><img src="https://avatars.githubusercontent.com/u/7972857?v=4?s=100" width="100px;" alt="CosminPerRam"/><br /><sub><b>CosminPerRam</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=CosminPerRam" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Crirock"><img src="https://avatars.githubusercontent.com/u/101053125?v=4?s=100" width="100px;" alt="Cristiano"/><br /><sub><b>Cristiano</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=Crirock" title="Code">💻</a> <a href="#ideas-Crirock" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cthulu201"><img src="https://avatars.githubusercontent.com/u/9865418?v=4?s=100" width="100px;" alt="Cthulu201"/><br /><sub><b>Cthulu201</b></sub></a><br /><a href="#financial-Cthulu201" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Dinozavvvr"><img src="https://avatars.githubusercontent.com/u/45518871?v=4?s=100" width="100px;" alt="Dinar Shagaliev"/><br /><sub><b>Dinar Shagaliev</b></sub></a><br /><a href="#translation-Dinozavvvr" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/eatingdm"><img src="https://avatars.githubusercontent.com/u/44078909?v=4?s=100" width="100px;" alt="Dominic Kim"/><br /><sub><b>Dominic Kim</b></sub></a><br /><a href="#translation-eatingdm" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://3kh0.net"><img src="https://avatars.githubusercontent.com/u/58097612?v=4?s=100" width="100px;" alt="Echo"/><br /><sub><b>Echo</b></sub></a><br /><a href="#financial-3kh0" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Embers-of-the-Fire"><img src="https://avatars.githubusercontent.com/u/106362488?v=4?s=100" width="100px;" alt="Embers-of-the-Fire"/><br /><sub><b>Embers-of-the-Fire</b></sub></a><br /><a href="#translation-Embers-of-the-Fire" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/franciscoBSalgueiro"><img src="https://avatars.githubusercontent.com/u/92053465?v=4?s=100" width="100px;" alt="Francisco Salgueiro"/><br /><sub><b>Francisco Salgueiro</b></sub></a><br /><a href="#translation-franciscoBSalgueiro" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bdantas"><img src="https://avatars.githubusercontent.com/u/5084088?v=4?s=100" width="100px;" alt="GNUser"/><br /><sub><b>GNUser</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=bdantas" title="Documentation">📖</a> <a href="#platform-bdantas" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amarao"><img src="https://avatars.githubusercontent.com/u/652496?v=4?s=100" width="100px;" alt="George Shuklin"/><br /><sub><b>George Shuklin</b></sub></a><br /><a href="#translation-amarao" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Digitalone1"><img src="https://avatars.githubusercontent.com/u/25790525?v=4?s=100" width="100px;" alt="Giusy Digital"/><br /><sub><b>Giusy Digital</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/issues?q=author%3ADigitalone1" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://blog.c6h12o6.org/"><img src="https://avatars.githubusercontent.com/u/16320859?v=4?s=100" width="100px;" alt="Hiroki Tagato"/><br /><sub><b>Hiroki Tagato</b></sub></a><br /><a href="#platform-tagattie" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Kiruyuto"><img src="https://avatars.githubusercontent.com/u/80201134?v=4?s=100" width="100px;" alt="Hubert"/><br /><sub><b>Hubert</b></sub></a><br /><a href="#translation-Kiruyuto" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fuzunspm"><img src="https://avatars.githubusercontent.com/u/12132746?v=4?s=100" width="100px;" alt="Hüseyin Fahri Uzun"/><br /><sub><b>Hüseyin Fahri Uzun</b></sub></a><br /><a href="#translation-fuzunspm" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://ipinfo.io"><img src="https://avatars.githubusercontent.com/u/15721521?v=4?s=100" width="100px;" alt="IPinfo"/><br /><sub><b>IPinfo</b></sub></a><br /><a href="#financial-ipinfo" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ilmi2"><img src="https://avatars.githubusercontent.com/u/72260726?v=4?s=100" width="100px;" alt="Ilmi2"/><br /><sub><b>Ilmi2</b></sub></a><br /><a href="#financial-Ilmi2" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.janwalter.org"><img src="https://avatars.githubusercontent.com/u/1074865?v=4?s=100" width="100px;" alt="Jan Walter"/><br /><sub><b>Jan Walter</b></sub></a><br /><a href="#financial-wahn" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jauderho"><img src="https://avatars.githubusercontent.com/u/13562?v=4?s=100" width="100px;" alt="Jauder Ho"/><br /><sub><b>Jauder Ho</b></sub></a><br /><a href="#infra-jauderho" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://joshuamegnauth54.github.io/"><img src="https://avatars.githubusercontent.com/u/48846352?v=4?s=100" width="100px;" alt="Joshua Megnauth"/><br /><sub><b>Joshua Megnauth</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=joshuamegnauth54" title="Code">💻</a> <a href="#design-joshuamegnauth54" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JulianSchmid"><img src="https://avatars.githubusercontent.com/u/1327472?v=4?s=100" width="100px;" alt="Julian Schmid"/><br /><sub><b>Julian Schmid</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=JulianSchmid" title="Code">💻</a> <a href="#ideas-JulianSchmid" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LiChenG-P"><img src="https://avatars.githubusercontent.com/u/54274109?v=4?s=100" width="100px;" alt="LiChenG-P"/><br /><sub><b>LiChenG-P</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=LiChenG-P" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/celtic-coder"><img src="https://avatars.githubusercontent.com/u/11787866?v=4?s=100" width="100px;" alt="Liam OBrien"/><br /><sub><b>Liam OBrien</b></sub></a><br /><a href="#ideas-celtic-coder" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DocRAID"><img src="https://avatars.githubusercontent.com/u/69478178?v=4?s=100" width="100px;" alt="Limdongju"/><br /><sub><b>Limdongju</b></sub></a><br /><a href="#translation-DocRAID" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lionrayonnant"><img src="https://avatars.githubusercontent.com/u/106342136?v=4?s=100" width="100px;" alt="Lion Rayonnant"/><br /><sub><b>Lion Rayonnant</b></sub></a><br /><a href="#translation-lionrayonnant" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://aloso.github.io/"><img src="https://avatars.githubusercontent.com/u/15658558?v=4?s=100" width="100px;" alt="Ludwig Stecher"/><br /><sub><b>Ludwig Stecher</b></sub></a><br /><a href="#ideas-Aloso" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/GyulyVGC/sniffnet/commits?author=Aloso" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/marcgavilangil/"><img src="https://avatars.githubusercontent.com/u/45849876?v=4?s=100" width="100px;" alt="Marc Gavilán"/><br /><sub><b>Marc Gavilán</b></sub></a><br /><a href="#translation-marc-gav" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/domcyrus"><img src="https://avatars.githubusercontent.com/u/884083?v=4?s=100" width="100px;" alt="Marco Cadetg"/><br /><sub><b>Marco Cadetg</b></sub></a><br /><a href="#platform-domcyrus" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mb720"><img src="https://avatars.githubusercontent.com/u/4164548?v=4?s=100" width="100px;" alt="Matthias Braun"/><br /><sub><b>Matthias Braun</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=mb720" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mhansma96"><img src="https://avatars.githubusercontent.com/u/84068842?v=4?s=100" width="100px;" alt="Michel Hansma"/><br /><sub><b>Michel Hansma</b></sub></a><br /><a href="#design-mhansma96" title="Design">🎨</a> <a href="#a11y-mhansma96" title="Accessibility">️️️️♿️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/marcgavilangil/"><img src="https://avatars.githubusercontent.com/u/45849876?v=4?s=100" width="100px;" alt="Marc Gavilán"/><br /><sub><b>Marc Gavilán</b></sub></a><br /><a href="#translation-marc-gav" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/domcyrus"><img src="https://avatars.githubusercontent.com/u/884083?v=4?s=100" width="100px;" alt="Marco Cadetg"/><br /><sub><b>Marco Cadetg</b></sub></a><br /><a href="#platform-domcyrus" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mmseng"><img src="https://avatars.githubusercontent.com/u/8218085?v=4?s=100" width="100px;" alt="Matt Seng"/><br /><sub><b>Matt Seng</b></sub></a><br /><a href="#financial-mmseng" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mb720"><img src="https://avatars.githubusercontent.com/u/4164548?v=4?s=100" width="100px;" alt="Matthias Braun"/><br /><sub><b>Matthias Braun</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=mb720" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mhansma96"><img src="https://avatars.githubusercontent.com/u/84068842?v=4?s=100" width="100px;" alt="Michel Hansma"/><br /><sub><b>Michel Hansma</b></sub></a><br /><a href="#design-mhansma96" title="Design">🎨</a> <a href="#a11y-mhansma96" title="Accessibility">️️️️♿️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://pcwizz.net"><img src="https://avatars.githubusercontent.com/u/1645637?v=4?s=100" width="100px;" alt="Morgan Hill"/><br /><sub><b>Morgan Hill</b></sub></a><br /><a href="#security-pcwizz" title="Security">🛡️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Mkadir"><img src="https://avatars.githubusercontent.com/u/69618539?v=4?s=100" width="100px;" alt="Muhammadali Hakimov"/><br /><sub><b>Muhammadali Hakimov</b></sub></a><br /><a href="#translation-Mkadir" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://nubikripto.my.id"><img src="https://avatars.githubusercontent.com/u/35731885?v=4?s=100" width="100px;" alt="Nubi"/><br /><sub><b>Nubi</b></sub></a><br /><a href="#translation-nubikripto" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://blog.brightone.cloud"><img src="https://avatars.githubusercontent.com/u/12615679?v=4?s=100" width="100px;" alt="Oleksii Filonenko"/><br /><sub><b>Oleksii Filonenko</b></sub></a><br /><a href="#translation-Br1ght0ne" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://orhun.dev"><img src="https://avatars.githubusercontent.com/u/24392180?v=4?s=100" width="100px;" alt="Orhun Parmaksız"/><br /><sub><b>Orhun Parmaksız</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=orhun" title="Documentation">📖</a> <a href="#platform-orhun" title="Packaging/porting to new platform">📦</a> <a href="#financial-orhun" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.peterdavehello.org/"><img src="https://avatars.githubusercontent.com/u/3691490?v=4?s=100" width="100px;" alt="Peter Dave Hello"/><br /><sub><b>Peter Dave Hello</b></sub></a><br /><a href="#translation-PeterDaveHello" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://hachyderm.io/@phil8o"><img src="https://avatars.githubusercontent.com/u/8797027?v=4?s=100" width="100px;" alt="Phil Clifford"/><br /><sub><b>Phil Clifford</b></sub></a><br /><a href="#platform-philclifford" title="Packaging/porting to new platform">📦</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Quetzal-coalt"><img src="https://avatars.githubusercontent.com/u/62941885?v=4?s=100" width="100px;" alt="Quetzal-coalt"/><br /><sub><b>Quetzal-coalt</b></sub></a><br /><a href="#translation-Quetzal-coalt" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.signl4.com"><img src="https://avatars.githubusercontent.com/u/43244104?v=4?s=100" width="100px;" alt="Ron"/><br /><sub><b>Ron</b></sub></a><br /><a href="#ideas-rons4" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/msaf94"><img src="https://avatars.githubusercontent.com/u/27451267?v=4?s=100" width="100px;" alt="Safaraliev Maxim"/><br /><sub><b>Safaraliev Maxim</b></sub></a><br /><a href="#translation-msaf94" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://shawnyeager.com"><img src="https://avatars.githubusercontent.com/u/980297?v=4?s=100" width="100px;" alt="Shawn Yeager"/><br /><sub><b>Shawn Yeager</b></sub></a><br /><a href="#financial-shawnyeager" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://signpath.io"><img src="https://avatars.githubusercontent.com/u/34448643?v=4?s=100" width="100px;" alt="SignPath GmbH"/><br /><sub><b>SignPath GmbH</b></sub></a><br /><a href="#platform-SignPath" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/4r7if3x"><img src="https://avatars.githubusercontent.com/u/8606282?v=4?s=100" width="100px;" alt="The Artifex"/><br /><sub><b>The Artifex</b></sub></a><br /><a href="#translation-4r7if3x" title="Translation">🌍</a> <a href="#platform-4r7if3x" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/HUNG-rushb"><img src="https://avatars.githubusercontent.com/u/57101685?v=4?s=100" width="100px;" alt="Trịnh Duy Hưng"/><br /><sub><b>Trịnh Duy Hưng</b></sub></a><br /><a href="#translation-HUNG-rushb" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TyseEX"><img src="https://avatars.githubusercontent.com/u/102823177?v=4?s=100" width="100px;" alt="TyseEX"/><br /><sub><b>TyseEX</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/issues?q=author%3ATyseEX" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://index45.com"><img src="https://avatars.githubusercontent.com/u/79985743?v=4?s=100" width="100px;" alt="Victor Nilsson"/><br /><sub><b>Victor Nilsson</b></sub></a><br /><a href="#translation-vcrn" title="Translation">🌍</a> <a href="https://github.com/GyulyVGC/sniffnet/commits?author=vcrn" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wangzishi"><img src="https://avatars.githubusercontent.com/u/8288105?v=4?s=100" width="100px;" alt="Wang Zishi"/><br /><sub><b>Wang Zishi</b></sub></a><br /><a href="#translation-wangzishi" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Kapelianovych"><img src="https://avatars.githubusercontent.com/u/28602579?v=4?s=100" width="100px;" alt="Yevhen"/><br /><sub><b>Yevhen</b></sub></a><br /><a href="#translation-Kapelianovych" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vanharen07"><img src="https://avatars.githubusercontent.com/u/91621548?v=4?s=100" width="100px;" alt="Ylva"/><br /><sub><b>Ylva</b></sub></a><br /><a href="#translation-vanharen07" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.backbox.org"><img src="https://avatars.githubusercontent.com/u/2687905?v=4?s=100" width="100px;" alt="ZEROF"/><br /><sub><b>ZEROF</b></sub></a><br /><a href="#financial-ZEROF" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://bit.ly/cBWeb"><img src="https://avatars.githubusercontent.com/u/28985171?v=4?s=100" width="100px;" alt="ZeroDot1"/><br /><sub><b>ZeroDot1</b></sub></a><br /><a href="#design-ZeroDot1" title="Design">🎨</a> <a href="#a11y-ZeroDot1" title="Accessibility">️️️️♿️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/clr-cera"><img src="https://avatars.githubusercontent.com/u/93736542?v=4?s=100" width="100px;" alt="clr"/><br /><sub><b>clr</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=clr-cera" title="Documentation">📖</a> <a href="#translation-clr-cera" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ervinpopescu"><img src="https://avatars.githubusercontent.com/u/84532402?v=4?s=100" width="100px;" alt="ervinpopescu"/><br /><sub><b>ervinpopescu</b></sub></a><br /><a href="#translation-ervinpopescu" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/glitsj16"><img src="https://avatars.githubusercontent.com/u/959378?v=4?s=100" width="100px;" alt="glitsj16"/><br /><sub><b>glitsj16</b></sub></a><br /><a href="#platform-glitsj16" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/guilherme-demarchi"><img src="https://avatars.githubusercontent.com/u/196574579?v=4?s=100" width="100px;" alt="guilherme-demarchi"/><br /><sub><b>guilherme-demarchi</b></sub></a><br /><a href="#translation-guilherme-demarchi" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hirotake111"><img src="https://avatars.githubusercontent.com/u/6666092?v=4?s=100" width="100px;" alt="hirotake111"/><br /><sub><b>hirotake111</b></sub></a><br /><a href="#translation-hirotake111" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/islameehassan"><img src="https://avatars.githubusercontent.com/u/98806155?v=4?s=100" width="100px;" alt="islameehassan"/><br /><sub><b>islameehassan</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=islameehassan" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/louis-ym4"><img src="https://avatars.githubusercontent.com/u/200361621?v=4?s=100" width="100px;" alt="louis-ym4"/><br /><sub><b>louis-ym4</b></sub></a><br /><a href="#design-louis-ym4" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/luca3s"><img src="https://avatars.githubusercontent.com/u/41015240?v=4?s=100" width="100px;" alt="luca3s"/><br /><sub><b>luca3s</b></sub></a><br /><a href="#translation-luca3s" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://piapark.me"><img src="https://avatars.githubusercontent.com/u/76558220?v=4?s=100" width="100px;" alt="pia"/><br /><sub><b>pia</b></sub></a><br /><a href="#translation-rkdud007" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://piapark.me"><img src="https://avatars.githubusercontent.com/u/76558220?v=4?s=100" width="100px;" alt="pia"/><br /><sub><b>pia</b></sub></a><br /><a href="#translation-rkdud007" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/0323pin"><img src="https://avatars.githubusercontent.com/u/90570748?v=4?s=100" width="100px;" alt="pin"/><br /><sub><b>pin</b></sub></a><br /><a href="#platform-0323pin" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shu-kitamura"><img src="https://avatars.githubusercontent.com/u/171437458?v=4?s=100" width="100px;" alt="shu-kitamura"/><br /><sub><b>shu-kitamura</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=shu-kitamura" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shu-kitamura"><img src="https://avatars.githubusercontent.com/u/171437458?v=4?s=100" width="100px;" alt="shu-kitamura"/><br /><sub><b>shu-kitamura</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=shu-kitamura" title="Code">💻</a> <a href="#translation-shu-kitamura" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starccy"><img src="https://avatars.githubusercontent.com/u/17541742?v=4?s=100" width="100px;" alt="starccy"/><br /><sub><b>starccy</b></sub></a><br /><a href="https://github.com/GyulyVGC/sniffnet/commits?author=starccy" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/0x0177b11f"><img src="https://avatars.githubusercontent.com/u/2305166?v=4?s=100" width="100px;" alt="tiansheng li"/><br /><sub><b>tiansheng li</b></sub></a><br /><a href="#financial-0x0177b11f" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vtiinanen"><img src="https://avatars.githubusercontent.com/u/49340148?v=4?s=100" width="100px;" alt="vtiinanen"/><br /><sub><b>vtiinanen</b></sub></a><br /><a href="#translation-vtiinanen" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://catchts.com"><img src="https://avatars.githubusercontent.com/u/16646733?v=4?s=100" width="100px;" alt="yossarian"/><br /><sub><b>yossarian</b></sub></a><br /><a href="#translation-captain-yossarian" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cxw620"><img src="https://avatars.githubusercontent.com/u/70561268?v=4?s=100" width="100px;" alt="陈寒彤"/><br /><sub><b>陈寒彤</b></sub></a><br /><a href="#translation-cxw620" title="Translation">🌍</a></td>
</tr>
</tbody>

703
Cargo.lock generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@ strip = true
[dependencies]
pcap = "2.3.0"
etherparse = "0.18.0"
etherparse = "0.19.0"
chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
plotters = { version = "0.3.7", default-features = false, features = ["area_series", "line_series"] }
iced = { version = "0.13.1", features = ["tokio", "svg", "advanced", "lazy", "image"] }
@@ -47,39 +47,39 @@ maxminddb = "0.26.0"
confy = "1.0.0"
serde = { version = "1.0.219", default-features = false, features = ["derive"] }
rodio = { version = "0.21.1", default-features = false, features = ["mp3", "playback"] }
dns-lookup = "2.0.4"
toml = "0.9.2"
dns-lookup = "3.0.0"
toml = "0.9.5"
ctrlc = { version = "3.4.7", features = ["termination"] }
rfd = "0.15.4"
phf = "0.12.1"
phf_shared = "0.12.1"
phf = "0.13.1"
phf_shared = "0.13.1"
splines = "5.0.0"
clap = { version = "4.5.41", features = ["derive"] }
tokio = { version = "1.46.1", features = ["macros"] }
clap = { version = "4.5.46", features = ["derive"] }
tokio = { version = "1.47.1", features = ["macros"] }
async-channel = "2.5.0"
[target.'cfg(windows)'.dependencies]
gag = "1.0.0"
[target.'cfg(not(target_arch = "powerpc64"))'.dependencies]
reqwest = { version = "0.12.22", default-features = false, features = ["json", "rustls-tls"] }
reqwest = { version = "0.12.23", default-features = false, features = ["json", "rustls-tls"] }
[target.'cfg(target_arch = "powerpc64")'.dependencies]
reqwest = { version = "0.12.22", features = ["json"] }
reqwest = { version = "0.12.23", features = ["json"] }
#───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[dev-dependencies]
serde_test = "1.0.177"
rstest = "0.25.0"
rstest = "0.26.1"
serial_test = { version = "3.2.0", default-features = false }
#───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[build-dependencies]
phf_codegen = "0.12.1"
phf_shared = "0.12.1"
rustrict = { version = "0.7.35", default-features = false, features = ["censor"] }
phf_codegen = "0.13.1"
phf_shared = "0.13.1"
rustrict = { version = "0.7.36", default-features = false, features = ["censor"] }
[target."cfg(windows)".build-dependencies]
winres = "0.1.12"

View File

@@ -59,9 +59,33 @@ ## _Support Sniffnet's development_ 💖
## Download
| <a href="#download"><img alt="Windows" title="Windows" height="35px" src="https://raw.githubusercontent.com/GyulyVGC/sniffnet/main/resources/repository/badges/windows.svg"/></a> | <a href="#download"><img alt="macOS" title="macOS" height="35px" src="https://raw.githubusercontent.com/GyulyVGC/sniffnet/main/resources/repository/badges/macos.svg"/></a> | <a href="#download"><img alt="Linux (.deb)" title="Linux (.deb)" height="35px" src="https://raw.githubusercontent.com/GyulyVGC/sniffnet/main/resources/repository/badges/linux_deb.svg"/></a> | <a href="#download"><img alt="Linux (.rpm)" title="Linux (.rpm)" height="35px" src="https://raw.githubusercontent.com/GyulyVGC/sniffnet/main/resources/repository/badges/linux_rpm.svg"/></a> |
|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[64&#8209;bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_64-bit.msi)&nbsp;\|&nbsp;[32&#8209;bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_32-bit.msi)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | [Intel](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_Intel.dmg)&nbsp;\|&nbsp;[Apple&nbsp;silicon](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_AppleSilicon.dmg) | [amd64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_amd64.deb)&nbsp;\|&nbsp;[arm64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_arm64.deb)&nbsp;\|&nbsp;[i386](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_i386.deb)&nbsp;\|&nbsp;[armhf](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_armhf.deb) | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[x86_64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_x86_64.rpm)&nbsp;\|&nbsp;[aarch64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_aarch64.rpm)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |
<table>
<tr>
<td>
<picture><img alt="Windows" title="Windows" height="85px" src="https://raw.githubusercontent.com/GyulyVGC/sniffnet/main/resources/repository/badges/windows.svg"/></picture>
</td>
<td>
<a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_64-bit.msi">64-bit</a> | <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_32-bit.msi">32-bit</a>
</td>
</tr>
<tr>
<td>
<picture><img alt="macOS" title="macOS" height="85px" src="https://raw.githubusercontent.com/GyulyVGC/sniffnet/main/resources/repository/badges/macos.svg"/></picture>
</td>
<td>
<a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_Intel.dmg">Intel</a> | <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_AppleSilicon.dmg">Apple silicon</a>
</td>
</tr>
<tr>
<td>
<picture><img alt="Linux" title="Linux" height="85px" src="https://raw.githubusercontent.com/GyulyVGC/sniffnet/main/resources/repository/badges/linux.svg"/></picture>
</td>
<td>
DEB: <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_amd64.deb">amd64</a> | <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_arm64.deb">arm64</a> | <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_i386.deb">i386</a> | <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_armhf.deb">armhf</a> <br>
RPM: <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_x86_64.rpm">x86_64</a> | <a href="https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_aarch64.rpm">aarch64</a>
</td>
</tr>
</table>
Links in the table above will download the latest version of Sniffnet directly from [GitHub releases](https://github.com/GyulyVGC/sniffnet/releases). <br>
Not what you're looking for? Check out [alternative installation methods](https://github.com/GyulyVGC/sniffnet/wiki/Alternative-installation-methods).
@@ -136,6 +160,7 @@ ## Acknowledgements
- A big shout-out to [all the contributors](https://github.com/GyulyVGC/sniffnet/blob/main/CONTRIBUTORS.md) of Sniffnet!
- The graphical user interface has been realized with [iced](https://github.com/iced-rs/iced), a cross-platform GUI library for Rust focused on simplicity and type-safety
- IP geolocation and ASN data are provided by [MaxMind](https://www.maxmind.com)
- Free code signing for Windows Installer is provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
- [Sniffnet](https://ads.fund/token/0xadfc251f8ef00ceaeca2b5c1882dabe5db0833df) project is supported by ADS.FUND
- Last but not least, thanks to [every single stargazer](https://github.com/GyulyVGC/sniffnet/stargazers): all forms of support made it possible to keep improving Sniffnet!

5
giscus.json Normal file
View File

@@ -0,0 +1,5 @@
{
"origins": ["https://sniffnet.net"],
"originsRegex": ["http://localhost:[0-9]+"],
"defaultCommentOrder": "oldest"
}

View File

@@ -150,6 +150,7 @@ z
Ț
ț
ʼ
Ά
Έ
Ή
Ό
@@ -158,15 +159,20 @@ z
Γ
Δ
Ε
Θ
Κ
Λ
Μ
Ν
Ξ
Ο
Π
Ρ
Σ
Τ
Υ
Φ
Χ
ά
έ
ή
@@ -333,10 +339,8 @@ z
@@ -352,6 +356,7 @@ z
@@ -454,6 +459,7 @@ z
@@ -486,6 +492,7 @@ z
@@ -498,6 +505,7 @@ z
@@ -525,6 +533,7 @@ z
@@ -564,6 +573,7 @@ z
@@ -608,6 +618,7 @@ z
@@ -695,9 +706,11 @@ z
@@ -711,6 +724,7 @@ z
@@ -721,6 +735,7 @@ z
@@ -754,6 +769,7 @@ z
@@ -785,6 +801,7 @@ z
@@ -805,6 +822,7 @@ z
調
@@ -815,6 +833,7 @@ z
@@ -823,6 +842,7 @@ z
@@ -873,8 +893,10 @@ z

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1,9 @@
app: Sniffnet
ingredients:
dist: bookworm
packages:
- libpcap0.8
sources:
- deb https://deb.debian.org/debian stable main
debs:
- /target/REPLACE_TAG

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.7 KiB

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -1 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90.25" height="28" role="img" aria-label="MACOS"><title>MACOS</title><g shape-rendering="crispEdges"><rect width="90.25" height="28" fill="#0f1e3c"/></g><g fill="#a0e8f8" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjYTBlOGY4IiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+QXBwbGU8L3RpdGxlPjxwYXRoIGQ9Ik0xMi4xNTIgNi44OTZjLS45NDggMC0yLjQxNS0xLjA3OC0zLjk2LTEuMDQtMi4wNC4wMjctMy45MSAxLjE4My00Ljk2MSAzLjAxNC0yLjExNyAzLjY3NS0uNTQ2IDkuMTAzIDEuNTE5IDEyLjA5IDEuMDEzIDEuNDU0IDIuMjA4IDMuMDkgMy43OTIgMy4wMzkgMS41Mi0uMDY1IDIuMDktLjk4NyAzLjkzNS0uOTg3IDEuODMxIDAgMi4zNS45ODcgMy45Ni45NDggMS42MzctLjAyNiAyLjY3Ni0xLjQ4IDMuNjc2LTIuOTQ4IDEuMTU2LTEuNjg4IDEuNjM2LTMuMzI1IDEuNjYyLTMuNDE1LS4wMzktLjAxMy0zLjE4Mi0xLjIyMS0zLjIyLTQuODU3LS4wMjYtMy4wNCAyLjQ4LTQuNDk0IDIuNTk3LTQuNTU5LTEuNDI5LTIuMDktMy42MjMtMi4zMjQtNC4zOS0yLjM3Ni0yLS4xNTYtMy42NzUgMS4wOS00LjYxIDEuMDl6TTE1LjUzIDMuODNjLjg0My0xLjAxMiAxLjQtMi40MjcgMS4yNDUtMy44My0xLjIwNy4wNTItMi42NjIuODA1LTMuNTMyIDEuODE4LS43OC44OTYtMS40NTQgMi4zMzgtMS4yNzMgMy43MTQgMS4zMzguMTA0IDIuNzE1LS42ODggMy41NTktMS43MDEiLz48L3N2Zz4="/><text transform="scale(.1)" x="551.25" y="175" textLength="462.5" fill="#a0e8f8" font-weight="bold">MACOS</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="48" role="img"
aria-label="macOS">
<title>macOS</title>
<g shape-rendering="crispEdges">
<rect stroke-width="2px" stroke="#a0e8f8" width="60" height="48" fill="#0f1e3c"/>
</g>
<g fill="#a0e8f8" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision" font-size="10">
<image x="19" y="7" width="22" height="22"
xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjYTBlOGY4IiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+QXBwbGU8L3RpdGxlPjxwYXRoIGQ9Ik0xMi4xNTIgNi44OTZjLS45NDggMC0yLjQxNS0xLjA3OC0zLjk2LTEuMDQtMi4wNC4wMjctMy45MSAxLjE4My00Ljk2MSAzLjAxNC0yLjExNyAzLjY3NS0uNTQ2IDkuMTAzIDEuNTE5IDEyLjA5IDEuMDEzIDEuNDU0IDIuMjA4IDMuMDkgMy43OTIgMy4wMzkgMS41Mi0uMDY1IDIuMDktLjk4NyAzLjkzNS0uOTg3IDEuODMxIDAgMi4zNS45ODcgMy45Ni45NDggMS42MzctLjAyNiAyLjY3Ni0xLjQ4IDMuNjc2LTIuOTQ4IDEuMTU2LTEuNjg4IDEuNjM2LTMuMzI1IDEuNjYyLTMuNDE1LS4wMzktLjAxMy0zLjE4Mi0xLjIyMS0zLjIyLTQuODU3LS4wMjYtMy4wNCAyLjQ4LTQuNDk0IDIuNTk3LTQuNTU5LTEuNDI5LTIuMDktMy42MjMtMi4zMjQtNC4zOS0yLjM3Ni0yLS4xNTYtMy42NzUgMS4wOS00LjYxIDEuMDl6TTE1LjUzIDMuODNjLjg0My0xLjAxMiAxLjQtMi40MjcgMS4yNDUtMy44My0xLjIwNy4wNTItMi42NjIuODA1LTMuNTMyIDEuODE4LS43OC44OTYtMS40NTQgMi4zMzgtMS4yNzMgMy43MTQgMS4zMzguMTA0IDIuNzE1LS42ODggMy41NTktMS43MDEiLz48L3N2Zz4="/>
<text text-anchor="start" x="10" y="41" fill="#a0e8f8" font-weight="bold">macOS</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="112.75" height="28" role="img" aria-label="WINDOWS"><title>WINDOWS</title><g shape-rendering="crispEdges"><rect width="112.75" height="28" fill="#0f1e3c"/></g><g fill="#a0e8f8" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjYTBlOGY4IiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+V2luZG93czwvdGl0bGU+PHBhdGggZD0iTTAsMEgxMS4zNzdWMTEuMzcySDBaTTEyLjYyMywwSDI0VjExLjM3MkgxMi42MjNaTTAsMTIuNjIzSDExLjM3N1YyNEgwWm0xMi42MjMsMEgyNFYyNEgxMi42MjMiLz48L3N2Zz4="/><text transform="scale(.1)" x="663.75" y="175" textLength="687.5" fill="#a0e8f8" font-weight="bold">WINDOWS</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="48" role="img"
aria-label="Windows">
<title>Windows</title>
<g shape-rendering="crispEdges">
<rect stroke-width="2px" stroke="#a0e8f8" width="60" height="48" fill="#0f1e3c"/>
</g>
<g fill="#a0e8f8" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision" font-size="10">
<image x="19" y="7" width="22" height="22"
xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPgo8c3ZnIGZpbGw9IiNhMGU4ZjgiIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHRpdGxlPmlvbmljb25zLXY1X2xvZ29zPC90aXRsZT48cGF0aCBkPSJNNDgwLDI2NUgyMzJWNDQ0bDI0OCwzNlYyNjVaIi8+PHBhdGggZD0iTTIxNiwyNjVIMzJWNDE1bDE4NCwyNi43VjI2NVoiLz48cGF0aCBkPSJNNDgwLDMyLDIzMiw2Ny40VjI0OUg0ODBWMzJaIi8+PHBhdGggZD0iTTIxNiw2OS43LDMyLDk2VjI0OUgyMTZWNjkuN1oiLz48L3N2Zz4="/>
<text text-anchor="start" x="4" y="41" fill="#a0e8f8" font-weight="bold">Windows</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 891 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,7 +1,7 @@
use splines::{Interpolation, Key, Spline};
use crate::TrafficChart;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::info_traffic::InfoTraffic;
use splines::{Interpolation, Key, Spline};
impl TrafficChart {
pub fn update_charts_data(&mut self, info_traffic_msg: &InfoTraffic, no_more_packets: bool) {
@@ -16,13 +16,21 @@ pub fn update_charts_data(&mut self, info_traffic_msg: &InfoTraffic, no_more_pac
self.ticks += 1;
#[allow(clippy::cast_precision_loss)]
let out_bytes_entry = -(info_traffic_msg.tot_data_info.outgoing_bytes() as f32);
let out_bytes_entry = -(info_traffic_msg
.tot_data_info
.outgoing_data(DataRepr::Bytes) as f32);
#[allow(clippy::cast_precision_loss)]
let in_bytes_entry = info_traffic_msg.tot_data_info.incoming_bytes() as f32;
let in_bytes_entry = info_traffic_msg
.tot_data_info
.incoming_data(DataRepr::Bytes) as f32;
#[allow(clippy::cast_precision_loss)]
let out_packets_entry = -(info_traffic_msg.tot_data_info.outgoing_packets() as f32);
let out_packets_entry = -(info_traffic_msg
.tot_data_info
.outgoing_data(DataRepr::Packets) as f32);
#[allow(clippy::cast_precision_loss)]
let in_packets_entry = info_traffic_msg.tot_data_info.incoming_packets() as f32;
let in_packets_entry = info_traffic_msg
.tot_data_info
.incoming_data(DataRepr::Packets) as f32;
let out_bytes_point = (tot_seconds, out_bytes_entry);
let in_bytes_point = (tot_seconds, in_bytes_entry);
@@ -145,10 +153,10 @@ fn reduce_all_time_data(all_time: &mut Vec<(f32, f32)>) {
while all_time.len() > 150 {
let mut new_vec = Vec::new();
all_time.iter().enumerate().for_each(|(i, (x, y))| {
if i % 2 == 0 {
if let Some(next) = all_time.get(i + 1) {
new_vec.push((*x, (y + next.1) / 2.0));
}
if i % 2 == 0
&& let Some(next) = all_time.get(i + 1)
{
new_vec.push((*x, (y + next.1) / 2.0));
}
});
*all_time = new_vec;
@@ -213,8 +221,9 @@ mod tests {
use crate::chart::manage_chart_data::{ChartSeries, get_max, get_min};
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_representation::DataRepr;
use crate::utils::types::timestamp::Timestamp;
use crate::{ChartType, InfoTraffic, Language, StyleType, TrafficChart};
use crate::{InfoTraffic, Language, StyleType, TrafficChart};
fn spline_from_vec(vec: Vec<(i32, i32)>) -> Spline<f32, f32> {
Spline::from_vec(
@@ -310,7 +319,7 @@ fn test_chart_data_updates() {
min_packets: -1000.0,
max_packets: 21000.0,
language: Language::default(),
chart_type: ChartType::Packets,
data_repr: DataRepr::Packets,
style: StyleType::default(),
thumbnail: false,
is_live_capture: true,
@@ -318,8 +327,6 @@ fn test_chart_data_updates() {
no_more_packets: false,
};
let mut info_traffic = InfoTraffic {
all_bytes: 0,
all_packets: 0,
tot_data_info,
dropped_packets: 0,
..Default::default()

View File

@@ -1,21 +0,0 @@
use crate::Language;
use crate::translations::translations::{bytes_translation, packets_translation};
use serde::{Deserialize, Serialize};
/// Enum representing the possible kind of chart displayed.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ChartType {
Packets,
Bytes,
}
impl ChartType {
pub(crate) const ALL: [ChartType; 2] = [ChartType::Bytes, ChartType::Packets];
pub fn get_label(&self, language: Language) -> &str {
match self {
ChartType::Packets => packets_translation(language),
ChartType::Bytes => bytes_translation(language),
}
}
}

View File

@@ -1,7 +1,6 @@
use crate::chart::types::chart_type::ChartType;
use crate::gui::styles::donut::Catalog;
use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_SUBTITLE};
use crate::networking::types::byte_multiple::ByteMultiple;
use crate::networking::types::data_representation::DataRepr;
use iced::alignment::{Horizontal, Vertical};
use iced::widget::canvas::path::Arc;
use iced::widget::canvas::{Frame, Text};
@@ -10,10 +9,9 @@
use std::f32::consts;
pub struct DonutChart {
chart_type: ChartType,
data_repr: DataRepr,
incoming: u128,
outgoing: u128,
filtered_out: u128,
dropped: u128,
font: Font,
thumbnail: bool,
@@ -21,19 +19,17 @@ pub struct DonutChart {
impl DonutChart {
fn new(
chart_type: ChartType,
data_repr: DataRepr,
incoming: u128,
outgoing: u128,
filtered_out: u128,
dropped: u128,
font: Font,
thumbnail: bool,
) -> Self {
Self {
chart_type,
data_repr,
incoming,
outgoing,
filtered_out,
dropped,
font,
thumbnail,
@@ -41,24 +37,19 @@ fn new(
}
fn total(&self) -> u128 {
self.incoming + self.outgoing + self.filtered_out + self.dropped
self.incoming + self.outgoing + self.dropped
}
fn title(&self) -> String {
let total = self.total();
if self.chart_type.eq(&ChartType::Bytes) {
ByteMultiple::formatted_string(total)
} else {
total.to_string()
}
self.data_repr.formatted_string(total)
}
fn angles(&self) -> [(Radians, Radians); 4] {
fn angles(&self) -> [(Radians, Radians); 3] {
#[allow(clippy::cast_precision_loss)]
let mut values = [
self.incoming as f32,
self.outgoing as f32,
self.filtered_out as f32,
self.dropped as f32,
];
let total: f32 = values.iter().sum();
@@ -105,12 +96,7 @@ fn draw(
let radius = (frame.width().min(frame.height()) / 2.0) * 0.9;
let style = <Theme as Catalog>::style(theme, &<Theme as Catalog>::default());
let colors = [
style.incoming,
style.outgoing,
style.filtered_out,
style.dropped,
];
let colors = [style.incoming, style.outgoing, style.dropped];
for ((start_angle, end_angle), color) in self.angles().into_iter().zip(colors) {
let path = canvas::Path::new(|builder| {
@@ -150,10 +136,9 @@ fn draw(
}
pub fn donut_chart<Message, Theme: Catalog>(
chart_type: ChartType,
data_repr: DataRepr,
incoming: u128,
outgoing: u128,
filtered_out: u128,
dropped: u128,
font: Font,
thumbnail: bool,
@@ -164,13 +149,7 @@ pub fn donut_chart<Message, Theme: Catalog>(
Length::Fixed(110.0)
};
iced::widget::canvas(DonutChart::new(
chart_type,
incoming,
outgoing,
filtered_out,
dropped,
font,
thumbnail,
data_repr, incoming, outgoing, dropped, font, thumbnail,
))
.width(size)
.height(size)

View File

@@ -1,3 +1,2 @@
pub mod chart_type;
pub mod donut_chart;
pub mod traffic_chart;

View File

@@ -15,24 +15,25 @@
use crate::gui::styles::style_constants::CHARTS_LINE_BORDER;
use crate::gui::styles::types::palette::to_rgb_color;
use crate::gui::types::message::Message;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::translations::translations::{incoming_translation, outgoing_translation};
use crate::utils::error_logger::{ErrorLogger, Location};
use crate::utils::formatted_strings::{get_formatted_num_seconds, get_formatted_timestamp};
use crate::utils::types::timestamp::Timestamp;
use crate::{ByteMultiple, ChartType, Language, StyleType, location};
use crate::{Language, StyleType, location};
/// Struct defining the chart to be displayed in gui run page
pub struct TrafficChart {
/// Current time interval number
pub ticks: u32,
/// Sent bytes filtered and their time occurrence
/// Sent bytes and their time occurrence
pub out_bytes: ChartSeries,
/// Received bytes filtered and their time occurrence
/// Received bytes and their time occurrence
pub in_bytes: ChartSeries,
/// Sent packets filtered and their time occurrence
/// Sent packets and their time occurrence
pub out_packets: ChartSeries,
/// Received packets filtered and their time occurrence
/// Received packets and their time occurrence
pub in_packets: ChartSeries,
/// Minimum number of bytes per time interval (computed on last 30 intervals)
pub min_bytes: f32,
@@ -45,7 +46,7 @@ pub struct TrafficChart {
/// Language used for the chart legend
pub language: Language,
/// Packets or bytes
pub chart_type: ChartType,
pub data_repr: DataRepr,
/// Style of the chart
pub style: StyleType,
/// Whether the chart is for the thumbnail page
@@ -71,7 +72,7 @@ pub fn new(style: StyleType, language: Language) -> Self {
min_packets: 0.0,
max_packets: 0.0,
language,
chart_type: ChartType::Bytes,
data_repr: DataRepr::Bytes,
style,
thumbnail: false,
is_live_capture: true,
@@ -80,7 +81,7 @@ pub fn new(style: StyleType, language: Language) -> Self {
}
}
pub fn view(&self) -> Element<Message, StyleType> {
pub fn view(&self) -> Element<'_, Message, StyleType> {
let x_labels = if self.is_live_capture || self.thumbnail {
None
} else {
@@ -115,8 +116,8 @@ pub fn view(&self) -> Element<Message, StyleType> {
.into()
}
pub fn change_kind(&mut self, kind: ChartType) {
self.chart_type = kind;
pub fn change_kind(&mut self, kind: DataRepr) {
self.data_repr = kind;
}
pub fn change_language(&mut self, language: Language) {
@@ -169,9 +170,10 @@ fn x_axis_range(&self) -> Range<f32> {
}
fn y_axis_range(&self) -> Range<f32> {
let (min, max) = match self.chart_type {
ChartType::Packets => (self.min_packets, self.max_packets),
ChartType::Bytes => (self.min_bytes, self.max_bytes),
let (min, max) = match self.data_repr {
DataRepr::Packets => (self.min_packets, self.max_packets),
DataRepr::Bytes => (self.min_bytes, self.max_bytes),
DataRepr::Bits => (self.min_bytes * 8.0, self.max_bytes * 8.0),
};
let fs = max - min;
let gap = fs * 0.05;
@@ -186,12 +188,12 @@ fn font<'a>(&self, size: f64) -> TextStyle<'a> {
}
fn spline_to_plot(&self, direction: TrafficDirection) -> &Spline<f32, f32> {
match self.chart_type {
ChartType::Packets => match direction {
match self.data_repr {
DataRepr::Packets => match direction {
TrafficDirection::Incoming => &self.in_packets.spline,
TrafficDirection::Outgoing => &self.out_packets.spline,
},
ChartType::Bytes => match direction {
DataRepr::Bytes | DataRepr::Bits => match direction {
TrafficDirection::Incoming => &self.in_bytes.spline,
TrafficDirection::Outgoing => &self.out_bytes.spline,
},
@@ -219,11 +221,16 @@ fn area_series<DB: DrawingBackend>(
let color = self.series_color(direction);
let alpha = self.style.get_extension().alpha_chart_badge;
let spline = self.spline_to_plot(direction);
let multiplier = if self.data_repr == DataRepr::Bits {
8.0
} else {
1.0
};
let data = match spline.keys() {
// if we have only one tick, we need to add a second point to draw the area
[k] => vec![(0.0, k.value), (0.1, k.value)],
_ => sample_spline(spline),
[k] => vec![(0.0, k.value * multiplier), (0.1, k.value * multiplier)],
_ => sample_spline(spline, multiplier),
};
AreaSeries::new(data, 0.0, color.mix(alpha.into()))
@@ -283,12 +290,10 @@ fn build_chart<DB: DrawingBackend>(
.max_light_lines(0)
.label_style(self.font(12.5))
.y_labels(min(5, y_labels))
.y_label_formatter(if self.chart_type.eq(&ChartType::Packets) {
&|packets| packets.abs().to_string()
} else {
.y_label_formatter(
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
&|bytes| ByteMultiple::formatted_string(bytes.abs() as u128)
})
&|amount| self.data_repr.formatted_string(amount.abs() as u128),
)
.x_labels(min(6, x_labels))
.x_label_formatter(
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
@@ -331,7 +336,7 @@ fn build_chart<DB: DrawingBackend>(
}
}
fn sample_spline(spline: &Spline<f32, f32>) -> Vec<(f32, f32)> {
fn sample_spline(spline: &Spline<f32, f32>, multiplier: f32) -> Vec<(f32, f32)> {
let pts = spline.len() * 10; // 10 samples per key
let mut ret_val = Vec::new();
let len = spline.len();
@@ -348,7 +353,7 @@ fn sample_spline(spline: &Spline<f32, f32>) -> Vec<(f32, f32)> {
for i in 0..pts {
#[allow(clippy::cast_precision_loss)]
let x = first_x + delta * i as f32;
let p = spline.clamped_sample(x).unwrap_or_default();
let p = spline.clamped_sample(x).unwrap_or_default() * multiplier;
ret_val.push((x, p));
}
ret_val
@@ -402,7 +407,7 @@ fn test_spline_samples() {
let eps = 0.001;
let pts = spline.len() * 10;
let samples = sample_spline(&spline);
let samples = sample_spline(&spline, 1.0);
assert_eq!(samples.len(), pts);
let delta = samples[1].0 - samples[0].0;

View File

@@ -1,12 +1,11 @@
use crate::SNIFFNET_LOWERCASE;
use crate::gui::types::conf::{CONF, Conf};
use crate::gui::types::message::Message;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::utils::formatted_strings::APP_VERSION;
use clap::Parser;
use iced::{Task, window};
use crate::CONFIGS;
use crate::Configs;
use crate::SNIFFNET_LOWERCASE;
use crate::gui::types::message::Message;
use crate::utils::formatted_strings::APP_VERSION;
#[derive(Parser, Debug)]
#[command(
name = SNIFFNET_LOWERCASE,
@@ -16,7 +15,7 @@
)]
struct Args {
/// Start sniffing packets from the supplied network adapter
#[arg(short, long, value_name = "NAME", default_missing_value = CONFIGS.device.device_name.as_str(), num_args = 0..=1)]
#[arg(short, long, value_name = "NAME", default_missing_value = CONF.device.device_name.as_str(), num_args = 0..=1)]
adapter: Option<String>,
#[cfg(all(windows, not(debug_assertions)))]
/// Show the logs (stdout and stderr) of the most recent application run
@@ -50,7 +49,7 @@ pub fn handle_cli_args() -> Task<Message> {
}
if args.restore_default {
if Configs::default().store().is_ok() {
if Conf::default().store().is_ok() {
println!("Restored default settings");
}
std::process::exit(0);
@@ -60,7 +59,12 @@ pub fn handle_cli_args() -> Task<Message> {
.map(Message::StartApp)
.chain(Task::done(Message::Periodic));
if let Some(adapter) = args.adapter {
// TODO: check if this works once #653 is fixed
// currently the link type and device name aren't displayed properly when starting from CLI
boot_task_chain = boot_task_chain
.chain(Task::done(Message::SetCaptureSource(
CaptureSourcePicklist::Device,
)))
.chain(Task::done(Message::DeviceSelection(adapter)))
.chain(Task::done(Message::Start));
}
@@ -72,21 +76,28 @@ pub fn handle_cli_args() -> Task<Message> {
mod tests {
use serial_test::serial;
use crate::configs::types::config_window::{PositionTuple, SizeTuple};
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::types::custom_palette::ExtraStyles;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::conf::Conf;
use crate::gui::types::config_window::{PositionTuple, SizeTuple};
use crate::gui::types::export_pcap::ExportPcap;
use crate::gui::types::filters::Filters;
use crate::gui::types::settings::Settings;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::networking::types::config_device::ConfigDevice;
use crate::notifications::types::notifications::Notifications;
use crate::{ConfigDevice, ConfigSettings, ConfigWindow, Language, Sniffer, StyleType};
use super::*;
use crate::report::types::sort_type::SortType;
use crate::{ConfigWindow, Language, Sniffer, StyleType};
#[test]
#[serial]
fn test_restore_default_configs() {
// initial configs stored are the default ones
assert_eq!(Configs::load(), Configs::default());
let modified_configs = Configs {
settings: ConfigSettings {
assert_eq!(Conf::load(), Conf::default());
let modified_conf = Conf {
settings: Settings {
color_gradient: GradientType::Wild,
language: Language::ZH,
scale_factor: 0.65,
@@ -111,19 +122,35 @@ fn test_restore_default_configs() {
size: SizeTuple(452.0, 870.0),
thumbnail_position: PositionTuple(20.0, 20.0),
},
capture_source_picklist: CaptureSourcePicklist::File,
report_sort_type: SortType::Ascending,
host_sort_type: SortType::Descending,
service_sort_type: SortType::Neutral,
filters: Filters {
bpf: "tcp".to_string(),
expanded: true,
},
import_pcap_path: "whole_day.pcapng".to_string(),
export_pcap: ExportPcap {
enabled: true,
file_name: "sniffnet.pcap".to_string(),
directory: "home".to_string(),
},
last_opened_setting: SettingsPage::General,
last_opened_page: RunningPage::Inspect,
};
// we want to be sure that modified config is different from defaults
assert_ne!(Configs::default(), modified_configs);
assert_ne!(Conf::default(), modified_conf);
//store modified configs
modified_configs.clone().store().unwrap();
modified_conf.clone().store().unwrap();
// assert they've been stored
assert_eq!(Configs::load(), modified_configs);
assert_eq!(Conf::load(), modified_conf);
// restore defaults
Configs::default().store().unwrap();
Conf::default().store().unwrap();
// assert that defaults are stored
assert_eq!(Configs::load(), Configs::default());
assert_eq!(Conf::load(), Conf::default());
// only needed because it will delete config files via its Drop implementation
Sniffer::new(Configs::default());
Sniffer::new(Conf::default());
}
}

View File

@@ -1 +0,0 @@
pub mod types;

View File

@@ -1,86 +0,0 @@
//! Module defining the `ConfigDevice` struct, which allows to save and reload
//! the application default configuration.
use crate::networking::types::my_device::MyDevice;
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
use pcap::{Device, DeviceFlags};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct ConfigDevice {
pub device_name: String,
}
impl Default for ConfigDevice {
fn default() -> Self {
Self {
device_name: Device::lookup()
.unwrap_or(None)
.unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
})
.name,
}
}
}
impl ConfigDevice {
const FILE_NAME: &'static str = "device";
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(device) = confy::load::<ConfigDevice>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
device
} else {
let _ = confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, ConfigDevice::default())
.log_err(location!());
ConfigDevice::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
pub fn to_my_device(&self) -> MyDevice {
for device in Device::list().unwrap_or_default() {
if device.name.eq(&self.device_name) {
return MyDevice::from_pcap_device(device);
}
}
let standard_device = Device::lookup().unwrap_or(None).unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
});
MyDevice::from_pcap_device(standard_device)
}
}
#[cfg(test)]
mod tests {
use crate::ConfigDevice;
impl ConfigDevice {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<ConfigDevice>(ConfigDevice::test_path())
.unwrap_or_else(|_| ConfigDevice::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(ConfigDevice::test_path(), self)
}
}
}

View File

@@ -1,84 +0,0 @@
//! Module defining the `ConfigSettings` struct, which allows to save and reload
//! the application default configuration.
use serde::{Deserialize, Serialize};
use crate::gui::styles::types::gradient_type::GradientType;
use crate::notifications::types::notifications::Notifications;
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
use crate::{Language, StyleType};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct ConfigSettings {
pub color_gradient: GradientType,
pub language: Language,
pub scale_factor: f64,
pub mmdb_country: String,
pub mmdb_asn: String,
pub style_path: String,
pub notifications: Notifications,
// StyleType should be last in order to deserialize as a table properly
pub style: StyleType,
}
impl ConfigSettings {
const FILE_NAME: &'static str = "settings";
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(settings) = confy::load::<ConfigSettings>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
settings
} else {
let _ = confy::store(
SNIFFNET_LOWERCASE,
Self::FILE_NAME,
ConfigSettings::default(),
)
.log_err(location!());
ConfigSettings::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
}
impl Default for ConfigSettings {
fn default() -> Self {
ConfigSettings {
color_gradient: GradientType::default(),
language: Language::default(),
scale_factor: 1.0,
mmdb_country: String::new(),
mmdb_asn: String::new(),
style_path: String::new(),
notifications: Notifications::default(),
style: StyleType::default(),
}
}
}
#[cfg(test)]
mod tests {
use crate::ConfigSettings;
impl ConfigSettings {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<ConfigSettings>(ConfigSettings::test_path())
.unwrap_or_else(|_| ConfigSettings::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(ConfigSettings::test_path(), self)
}
}
}

View File

@@ -1,30 +0,0 @@
use crate::{ConfigDevice, ConfigSettings, ConfigWindow};
use confy::ConfyError;
pub static CONFIGS: std::sync::LazyLock<Configs> = std::sync::LazyLock::new(Configs::load);
#[derive(Default, Clone, PartialEq, Debug)]
pub struct Configs {
pub settings: ConfigSettings,
pub device: ConfigDevice,
pub window: ConfigWindow,
}
impl Configs {
/// This should only be used directly to load fresh configs;
/// use `CONFIGS` instead to access the initial instance
pub fn load() -> Self {
Configs {
settings: ConfigSettings::load(),
device: ConfigDevice::load(),
window: ConfigWindow::load(),
}
}
pub fn store(self) -> Result<(), ConfyError> {
self.settings.store()?;
self.device.store()?;
self.window.store()?;
Ok(())
}
}

View File

@@ -1,4 +0,0 @@
pub mod config_device;
pub mod config_settings;
pub mod config_window;
pub mod configs;

View File

@@ -5,28 +5,27 @@
use iced::widget::{Container, Row, Space, Text, Tooltip, button, horizontal_space};
use iced::{Alignment, Font, Length};
use crate::configs::types::config_settings::ConfigSettings;
use crate::gui::components::tab::notifications_badge;
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::sniffer::Sniffer;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::container::ContainerType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::translations::translations::{quit_analysis_translation, settings_translation};
use crate::translations::translations_3::thumbnail_mode_translation;
use crate::utils::types::icon::Icon;
use crate::{Language, SNIFFNET_TITLECASE, StyleType};
pub fn header(sniffer: &Sniffer) -> Container<Message, StyleType> {
pub fn header(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let thumbnail = sniffer.thumbnail;
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
if thumbnail {
@@ -41,8 +40,8 @@ pub fn header(sniffer: &Sniffer) -> Container<Message, StyleType> {
);
}
let last_opened_setting = sniffer.last_opened_setting;
let is_running = sniffer.running_page.ne(&RunningPage::Init);
let last_opened_setting = sniffer.conf.last_opened_setting;
let is_running = sniffer.running_page.is_some();
let logo = Icon::Sniffnet
.to_text()

View File

@@ -99,12 +99,12 @@ fn new_page_tab<'a>(
.align_y(alignment::Alignment::Center),
);
if let Some(num) = unread {
if num > 0 {
content = content
.push(Space::with_width(7))
.push(notifications_badge(font_headers, num));
}
if let Some(num) = unread
&& num > 0
{
content = content
.push(Space::with_width(7))
.push(notifications_badge(font_headers, num));
}
content = content.push(horizontal_space());

View File

@@ -8,6 +8,7 @@
use crate::gui::styles::text::TextType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::gui::types::timing_events::TimingEvents;
use crate::networking::manage_packets::{
get_address_to_lookup, get_traffic_type, is_local_connection, is_my_address,
@@ -15,6 +16,7 @@
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::arp_type::ArpType;
use crate::networking::types::bogon::is_bogon;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::icmp_type::IcmpType;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
@@ -33,7 +35,7 @@
};
use crate::utils::formatted_strings::{get_formatted_timestamp, get_socket_address};
use crate::utils::types::icon::Icon;
use crate::{ByteMultiple, ConfigSettings, Language, Protocol, Sniffer, StyleType};
use crate::{Language, Protocol, Sniffer, StyleType};
use iced::alignment::Vertical;
use iced::widget::scrollable::Direction;
use iced::widget::tooltip::Position;
@@ -44,17 +46,18 @@
pub fn connection_details_page(
sniffer: &Sniffer,
key: AddressPortPair,
) -> Container<Message, StyleType> {
) -> Container<'_, Message, StyleType> {
Container::new(page_content(sniffer, &key))
}
fn page_content<'a>(sniffer: &Sniffer, key: &AddressPortPair) -> Container<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let data_repr = sniffer.traffic_chart.data_repr;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -130,7 +133,7 @@ fn page_content<'a>(sniffer: &Sniffer, key: &AddressPortPair) -> Container<'a, M
dest_col = dest_col.push(host_info_col);
}
let col_info = col_info(key, &val, font, language);
let col_info = col_info(key, &val, data_repr, font, language);
let content = assemble_widgets(col_info, source_col, dest_col);
@@ -172,6 +175,7 @@ fn page_header<'a>(
fn col_info<'a>(
key: &AddressPortPair,
val: &InfoAddressPortPair,
data_repr: DataRepr,
font: Font,
language: Language,
) -> Column<'a, Message, StyleType> {
@@ -221,12 +225,13 @@ fn col_info<'a>(
incoming_translation(language).to_lowercase()
}
),
&format!(
"{}\n {} {}",
ByteMultiple::formatted_string(val.transmitted_bytes),
val.transmitted_packets,
packets_translation(language)
),
&(data_repr.formatted_string(val.transmitted_data(data_repr))
+ if data_repr == DataRepr::Packets {
format!(" {}", packets_translation(language))
} else {
String::new()
}
.as_ref()),
font,
));
@@ -295,9 +300,9 @@ fn get_local_tooltip<'a>(
address_to_lookup: &IpAddr,
key: &AddressPortPair,
) -> Tooltip<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let local_address = if address_to_lookup.eq(&key.address1) {
&key.address2

View File

@@ -2,18 +2,8 @@
//!
//! It contains elements to select network adapter and traffic filters.
use std::collections::HashSet;
use std::fmt::Write;
use iced::Length::FillPortion;
use iced::widget::scrollable::Direction;
use iced::widget::tooltip::Position;
use iced::widget::{
Button, Checkbox, Column, Container, Row, Rule, Scrollable, Space, Text, TextInput, Tooltip,
button, center,
};
use iced::{Alignment, Font, Length, Padding, alignment};
use crate::gui::components::button::button_open_file;
use crate::gui::sniffer::Sniffer;
use crate::gui::styles::button::ButtonType;
@@ -21,287 +11,171 @@
use crate::gui::styles::scrollbar::ScrollbarType;
use crate::gui::styles::style_constants::{FONT_SIZE_SUBTITLE, FONT_SIZE_TITLE};
use crate::gui::styles::text::TextType;
use crate::gui::styles::text_input::TextInputType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::export_pcap::ExportPcap;
use crate::gui::types::filters::Filters;
use crate::gui::types::message::Message;
use crate::networking::types::capture_context::CaptureSource;
use crate::networking::types::filters::Filters;
use crate::networking::types::ip_collection::AddressCollection;
use crate::networking::types::port_collection::PortCollection;
use crate::gui::types::settings::Settings;
use crate::networking::types::capture_context::{CaptureSource, CaptureSourcePicklist};
use crate::translations::translations::{
address_translation, addresses_translation, choose_adapters_translation,
ip_version_translation, protocol_translation, select_filters_translation, start_translation,
address_translation, addresses_translation, network_adapter_translation, start_translation,
};
use crate::translations::translations_3::{
directory_translation, export_capture_translation, file_name_translation, port_translation,
directory_translation, export_capture_translation, file_name_translation,
};
use crate::translations::translations_4::import_capture_translation;
use crate::utils::formatted_strings::{get_invalid_filters_string, get_path_termination_string};
use crate::translations::translations_4::capture_file_translation;
use crate::translations::translations_5::{filter_traffic_translation, traffic_source_translation};
use crate::utils::formatted_strings::get_path_termination_string;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, IpVersion, Language, Protocol, StyleType};
use crate::{Language, StyleType};
use iced::Length::FillPortion;
use iced::widget::scrollable::Direction;
use iced::widget::{
Button, Checkbox, Column, Container, PickList, Row, Scrollable, Space, Text, TextInput, button,
center, vertical_space,
};
use iced::{Alignment, Font, Length, Padding, alignment};
/// Computes the body of gui initial page
pub fn initial_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
let ConfigSettings {
pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
let col_adapter = get_col_adapter(sniffer, font);
let col_import_pcap = get_col_import_pcap(
language,
font,
&sniffer.capture_source,
&sniffer.import_pcap_path,
);
let col_capture_source = Column::new().push(col_adapter).push(col_import_pcap);
let col_data_source = get_col_data_source(sniffer, font, language);
let ip_active = &sniffer.filters.ip_versions;
let col_ip_buttons = col_ip_buttons(ip_active, font, language);
let protocol_active = &sniffer.filters.protocols;
let col_protocol_buttons = col_protocol_buttons(protocol_active, font, language);
let address_active = &sniffer.filters.address_str;
let col_address_filter = col_address_input(address_active, font, language);
let port_active = &sniffer.filters.port_str;
let col_port_filter = col_port_input(port_active, font, language);
let filters_pane = Column::new()
.width(FillPortion(6))
.padding(10)
.spacing(15)
.push(
select_filters_translation(language)
.font(font)
.class(TextType::Title)
.size(FONT_SIZE_TITLE),
)
.push(
Row::new()
.spacing(20)
.push(col_ip_buttons)
.push(col_protocol_buttons),
)
.push(
Row::new()
.spacing(20)
.push(col_address_filter)
.push(col_port_filter),
)
.push(Rule::horizontal(40))
.push(get_export_pcap_group(
&sniffer.capture_source,
&sniffer.export_pcap,
let col_checkboxes = Column::new()
.spacing(10)
.push(get_filters_group(&sniffer.conf.filters, font, language))
.push_maybe(get_export_pcap_group_maybe(
sniffer.conf.capture_source_picklist,
&sniffer.conf.export_pcap,
language,
font,
));
let is_capture_source_consistent = sniffer.is_capture_source_consistent();
let right_col = Column::new()
.width(FillPortion(1))
.padding(10)
.push(Space::with_height(76))
.push(col_checkboxes)
.push(vertical_space())
.push(button_start(
font_headers,
language,
color_gradient,
is_capture_source_consistent,
))
.push(
Container::new(button_start(
font,
language,
color_gradient,
&sniffer.filters,
))
.width(Length::Fill)
.height(Length::Fill)
.align_y(Alignment::Start)
.align_x(Alignment::Center),
);
.push(vertical_space());
let body = Column::new().push(Space::with_height(5)).push(
Row::new()
.push(col_capture_source)
.push(Space::with_width(30))
.push(filters_pane),
.push(col_data_source)
.push(Space::with_width(15))
.push(right_col),
);
Container::new(body).height(Length::Fill)
}
fn col_ip_buttons(
active_ip_filters: &HashSet<IpVersion>,
font: Font,
language: Language,
) -> Column<Message, StyleType> {
let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5));
for option in IpVersion::ALL {
let is_active = active_ip_filters.contains(&option);
let check_symbol = if is_active { "" } else { "" };
buttons_row = buttons_row.push(
Button::new(
Text::new(format!("{option} {check_symbol}"))
.align_x(Alignment::Center)
.align_y(Alignment::Center)
.font(font),
)
.width(80)
.height(35)
.class(if is_active {
ButtonType::BorderedRoundSelected
} else {
ButtonType::BorderedRound
})
.on_press(Message::IpVersionSelection(option, !is_active)),
);
}
Column::new()
.width(Length::Fill)
.spacing(7)
.push(
Text::new(ip_version_translation(language))
.font(font)
.class(TextType::Subtitle)
.size(FONT_SIZE_SUBTITLE),
)
.push(buttons_row)
}
fn col_protocol_buttons(
active_protocol_filters: &HashSet<Protocol>,
font: Font,
language: Language,
) -> Column<Message, StyleType> {
let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5));
for option in Protocol::ALL {
let is_active = active_protocol_filters.contains(&option);
let check_symbol = if is_active { "" } else { "" };
buttons_row = buttons_row.push(
Button::new(
Text::new(format!("{option} {check_symbol}"))
.align_x(Alignment::Center)
.align_y(Alignment::Center)
.font(font),
)
.width(80)
.height(35)
.class(if is_active {
ButtonType::BorderedRoundSelected
} else {
ButtonType::BorderedRound
})
.on_press(Message::ProtocolSelection(option, !is_active)),
);
}
Column::new()
.width(Length::Fill)
.spacing(7)
.push(
Text::new(protocol_translation(language))
.font(font)
.class(TextType::Subtitle)
.size(FONT_SIZE_SUBTITLE),
)
.push(buttons_row)
}
fn col_address_input(value: &str, font: Font, language: Language) -> Column<Message, StyleType> {
let is_error = if value.is_empty() {
false
} else {
AddressCollection::new(value).is_none()
};
let input_row = Row::new().padding(Padding::ZERO.left(5)).push(
TextInput::new(AddressCollection::PLACEHOLDER_STR, value)
.padding([3, 5])
.on_input(Message::AddressFilter)
.font(font)
.width(310)
.class(if is_error {
TextInputType::Error
} else {
TextInputType::Standard
}),
);
Column::new()
.width(Length::Fill)
.spacing(7)
.push(
Text::new(address_translation(language))
.font(font)
.class(TextType::Subtitle)
.size(FONT_SIZE_SUBTITLE),
)
.push(input_row)
}
fn col_port_input(value: &str, font: Font, language: Language) -> Column<Message, StyleType> {
let is_error = if value.is_empty() {
false
} else {
PortCollection::new(value).is_none()
};
let input_row = Row::new().padding(Padding::ZERO.left(5)).push(
TextInput::new(PortCollection::PLACEHOLDER_STR, value)
.padding([3, 5])
.on_input(Message::PortFilter)
.font(font)
.width(180)
.class(if is_error {
TextInputType::Error
} else {
TextInputType::Standard
}),
);
Column::new()
.width(Length::Fill)
.spacing(7)
.push(
Text::new(port_translation(language))
.font(font)
.class(TextType::Subtitle)
.size(FONT_SIZE_SUBTITLE),
)
.push(input_row)
}
fn button_start(
font: Font,
fn button_start<'a>(
font_headers: Font,
language: Language,
color_gradient: GradientType,
filters: &Filters,
) -> Tooltip<Message, StyleType> {
let mut content = button(
Icon::Rocket
.to_text()
.size(25)
is_capture_source_consistent: bool,
) -> Button<'a, Message, StyleType> {
button(
Text::new(start_translation(language))
.font(font_headers)
.size(FONT_SIZE_TITLE)
.width(Length::Fill)
.align_x(alignment::Alignment::Center)
.align_y(alignment::Alignment::Center),
)
.padding(10)
.height(80)
.width(160)
.class(ButtonType::Gradient(color_gradient));
let mut tooltip = start_translation(language).to_string();
//tooltip.push_str(" [⏎]");
let mut position = Position::Top;
if filters.are_valid() {
content = content.on_press(Message::Start);
.padding(20)
.width(Length::Fill)
.class(ButtonType::Gradient(color_gradient))
.on_press_maybe(if is_capture_source_consistent {
Some(Message::Start)
} else {
tooltip = get_invalid_filters_string(filters, language);
position = Position::FollowCursor;
}
Tooltip::new(content, Text::new(tooltip).font(font), position)
.gap(5)
.class(ContainerType::Tooltip)
None
})
}
fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column<Message, StyleType> {
let ConfigSettings { language, .. } = sniffer.configs.settings;
fn get_col_data_source(
sniffer: &Sniffer,
font: Font,
language: Language,
) -> Column<'_, Message, StyleType> {
let current_option = if sniffer.conf.capture_source_picklist == CaptureSourcePicklist::Device {
network_adapter_translation(language)
} else {
capture_file_translation(language)
};
let picklist = PickList::new(
[
network_adapter_translation(language),
capture_file_translation(language),
],
Some(current_option),
move |option| {
if option == network_adapter_translation(language) {
Message::SetCaptureSource(CaptureSourcePicklist::Device)
} else {
Message::SetCaptureSource(CaptureSourcePicklist::File)
}
},
)
.padding([2, 7])
.font(font);
let mut col = Column::new()
.align_x(Alignment::Center)
.padding(Padding::new(10.0).top(30))
.spacing(30)
.height(Length::Fill)
.width(FillPortion(1))
.push(
Row::new()
.spacing(10)
.push(
Text::new(traffic_source_translation(language))
.font(font)
.class(TextType::Title)
.size(FONT_SIZE_TITLE),
)
.push(picklist),
);
match &sniffer.conf.capture_source_picklist {
CaptureSourcePicklist::Device => {
col = col.push(get_col_adapter(sniffer, font, language));
}
CaptureSourcePicklist::File => {
col = col.push(get_col_import_pcap(
language,
font,
&sniffer.capture_source,
&sniffer.conf.import_pcap_path,
));
}
}
col
}
fn get_col_adapter(
sniffer: &Sniffer,
font: Font,
language: Language,
) -> Column<'_, Message, StyleType> {
let mut dev_str_list = vec![];
for my_dev in &sniffer.my_devices {
let mut title = String::new();
@@ -342,16 +216,8 @@ fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column<Message, StyleType>
}
Column::new()
.padding(10)
.spacing(5)
.height(Length::Fill)
.width(FillPortion(4))
.push(
choose_adapters_translation(language)
.font(font)
.class(TextType::Title)
.size(FONT_SIZE_TITLE),
)
.push(if dev_str_list.is_empty() {
Into::<iced::Element<Message, StyleType>>::into(center(
Icon::get_hourglass(sniffer.dots_pulse.0.len()).size(60),
@@ -359,7 +225,7 @@ fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column<Message, StyleType>
} else {
Scrollable::with_direction(
dev_str_list.into_iter().fold(
Column::new().padding(13).spacing(5),
Column::new().padding(Padding::ZERO.right(13)).spacing(5),
|scroll_adapters, (name, title, subtitle, addrs)| {
let addrs_text = if addrs.is_empty() {
None
@@ -424,7 +290,7 @@ fn get_col_import_pcap<'a>(
let content = Column::new()
.width(Length::Fill)
.align_x(Alignment::Center)
.align_x(alignment::Alignment::Center)
.spacing(5)
.push(button_row);
@@ -439,29 +305,59 @@ fn get_col_import_pcap<'a>(
})
.on_press(Message::SetPcapImport(path.to_string())),
)
.padding(13);
.padding(Padding::ZERO.right(13));
Column::new()
.padding(10)
.spacing(5)
.width(FillPortion(4))
.push(
Text::new(import_capture_translation(language))
.font(font)
.class(TextType::Title)
.size(FONT_SIZE_TITLE),
)
.push(button)
Column::new().spacing(5).push(button)
}
fn get_export_pcap_group<'a>(
cs: &CaptureSource,
fn get_filters_group<'a>(
filters: &Filters,
font: Font,
language: Language,
) -> Container<'a, Message, StyleType> {
let expanded = filters.expanded();
let bpf = filters.bpf();
let caption = filter_traffic_translation(language);
let checkbox = Checkbox::new(caption, expanded)
.on_toggle(move |_| Message::ToggleFilters)
.size(18)
.font(font);
let mut ret_val = Column::new().spacing(10).push(checkbox);
if expanded {
let input = TextInput::new("", bpf)
.on_input(Message::BpfFilter)
.padding([2, 5])
.font(font);
let inner_col = Column::new()
.spacing(10)
.padding(Padding::ZERO.left(26))
.push(
Row::new()
.align_y(Alignment::Center)
.spacing(5)
.push(Text::new("BPF:").font(font))
.push(input),
);
ret_val = ret_val.push(inner_col);
}
Container::new(ret_val)
.padding(15)
.width(Length::Fill)
.class(ContainerType::BorderedRound)
}
fn get_export_pcap_group_maybe<'a>(
cs_pick: CaptureSourcePicklist,
export_pcap: &ExportPcap,
language: Language,
font: Font,
) -> Container<'a, Message, StyleType> {
if matches!(cs, CaptureSource::File(_)) {
return Container::new(Space::with_height(Length::Fill));
) -> Option<Container<'a, Message, StyleType>> {
if cs_pick == CaptureSourcePicklist::File {
return None;
}
let enabled = export_pcap.enabled();
@@ -479,7 +375,7 @@ fn get_export_pcap_group<'a>(
if enabled {
let inner_col = Column::new()
.spacing(10)
.padding(Padding::ZERO.left(45))
.padding(Padding::ZERO.left(26))
.push(
Row::new()
.align_y(Alignment::Center)
@@ -489,8 +385,7 @@ fn get_export_pcap_group<'a>(
TextInput::new(ExportPcap::DEFAULT_FILE_NAME, file_name)
.on_input(Message::OutputPcapFile)
.padding([2, 5])
.font(font)
.width(200),
.font(font),
),
)
.push(
@@ -511,12 +406,10 @@ fn get_export_pcap_group<'a>(
ret_val = ret_val.push(inner_col);
}
Container::new(
Some(
Container::new(ret_val)
.padding(10)
.padding(15)
.width(Length::Fill)
.class(ContainerType::BorderedRound),
)
.height(Length::Fill)
.align_y(Alignment::Start)
}

View File

@@ -11,7 +11,6 @@
};
use iced::{Alignment, Font, Length, Padding, Pixels, alignment};
use crate::chart::types::chart_type::ChartType;
use crate::gui::components::tab::get_pages_tabs;
use crate::gui::components::types::my_modal::MyModal;
use crate::gui::pages::overview_page::{get_bars, get_bars_length};
@@ -22,28 +21,30 @@
use crate::gui::styles::text::TextType;
use crate::gui::styles::text_input::TextInputType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::byte_multiple::ByteMultiple;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host_data_states::HostStates;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::report::get_report_entries::get_searched_entries;
use crate::report::types::report_col::ReportCol;
use crate::report::types::search_parameters::{FilterInputType, SearchParameters};
use crate::report::types::sort_type::SortType;
use crate::translations::translations_2::{
administrative_entity_translation, country_translation, domain_name_translation,
no_search_results_translation, only_show_favorites_translation, showing_results_translation,
};
use crate::translations::translations_3::filter_by_host_translation;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, ReportSortType, RunningPage, Sniffer, StyleType};
use crate::{Language, RunningPage, Sniffer, StyleType};
/// Computes the body of gui inspect page
pub fn inspect_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
let ConfigSettings {
pub fn inspect_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -75,7 +76,8 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
language,
&sniffer.search,
font,
sniffer.report_sort_type,
sniffer.conf.report_sort_type,
sniffer.traffic_chart.data_repr,
))
.push(Space::with_height(4))
.push(Rule::horizontal(5))
@@ -97,7 +99,7 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
.align_y(Alignment::Center)
.align_x(Alignment::Center)
.padding(Padding::new(7.0).top(10).bottom(3))
.width(1042)
.width(947)
.class(ContainerType::BorderedRound),
);
@@ -105,9 +107,10 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
}
fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let data_repr = sniffer.traffic_chart.data_repr;
let font = style.get_extension().font;
let (search_results, results_number, agglomerate) = get_searched_entries(sniffer);
@@ -122,12 +125,17 @@ fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let end_entry_num = start_entry_num + search_results.len() - 1;
for report_entry in search_results {
scroll_report = scroll_report.push(
button(row_report_entry(&report_entry.0, &report_entry.1, font))
.padding(2)
.on_press(Message::ShowModal(MyModal::ConnectionDetails(
report_entry.0,
)))
.class(ButtonType::Neutral),
button(row_report_entry(
&report_entry.0,
&report_entry.1,
data_repr,
font,
))
.padding(2)
.on_press(Message::ShowModal(MyModal::ConnectionDetails(
report_entry.0,
)))
.class(ButtonType::Neutral),
);
}
if results_number > 0 {
@@ -144,7 +152,7 @@ fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
.push(get_agglomerates_row(
font,
agglomerate,
sniffer.traffic_chart.chart_type,
sniffer.traffic_chart.data_repr,
))
.push(Rule::horizontal(5))
.push(get_change_page_row(
@@ -177,12 +185,13 @@ fn report_header_row(
language: Language,
search_params: &SearchParameters,
font: Font,
sort_type: ReportSortType,
) -> Row<Message, StyleType> {
sort_type: SortType,
data_repr: DataRepr,
) -> Row<'_, Message, StyleType> {
let mut ret_val = Row::new().padding([0, 2]).align_y(Alignment::Center);
for report_col in ReportCol::ALL {
let (title_display, title_small_display, tooltip_val) =
title_report_col_display(&report_col, language);
title_report_col_display(&report_col, data_repr, language);
let title_row = Row::new()
.align_y(Alignment::End)
.push(Text::new(title_display).font(font))
@@ -208,7 +217,9 @@ fn report_header_row(
.width(report_col.get_width())
.height(56)
.push(title_tooltip);
if report_col != ReportCol::Packets && report_col != ReportCol::Bytes {
if report_col == ReportCol::Data {
col_header = col_header.push(sort_arrows(sort_type));
} else {
col_header = col_header.push(
Container::new(filter_input(
report_col.get_filter_input_type(),
@@ -218,8 +229,6 @@ fn report_header_row(
.height(Length::Fill)
.align_y(Alignment::Center),
);
} else {
col_header = col_header.push(sort_arrows(sort_type, &report_col));
}
ret_val = ret_val.push(col_header);
}
@@ -228,10 +237,11 @@ fn report_header_row(
fn title_report_col_display(
report_col: &ReportCol,
data_repr: DataRepr,
language: Language,
) -> (String, String, String) {
let max_chars = report_col.get_max_chars(Some(language));
let title = report_col.get_title(language);
let title = report_col.get_title(language, data_repr);
let title_direction_info = report_col.get_title_direction_info(language);
let chars_title = title.chars().collect::<Vec<char>>();
let chars_title_direction_info = title_direction_info.chars().collect::<Vec<char>>();
@@ -261,21 +271,16 @@ fn title_report_col_display(
}
}
fn sort_arrows<'a>(
active_sort_type: ReportSortType,
report_col: &ReportCol,
) -> Container<'a, Message, StyleType> {
fn sort_arrows<'a>(active_sort_type: SortType) -> Container<'a, Message, StyleType> {
Container::new(
button(
active_sort_type
.icon(report_col)
.icon()
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.class(active_sort_type.button_type(report_col))
.on_press(Message::ReportSortSelection(
active_sort_type.next_sort(report_col),
)),
.class(active_sort_type.button_type())
.on_press(Message::ReportSortSelection(active_sort_type.next_sort())),
)
.align_y(Alignment::Center)
.height(Length::Fill)
@@ -284,6 +289,7 @@ fn sort_arrows<'a>(
fn row_report_entry<'a>(
key: &AddressPortPair,
val: &InfoAddressPortPair,
data_repr: DataRepr,
font: Font,
) -> Row<'a, Message, StyleType> {
let text_type = if val.traffic_direction == TrafficDirection::Outgoing {
@@ -296,7 +302,7 @@ fn row_report_entry<'a>(
for report_col in ReportCol::ALL {
let max_chars = report_col.get_max_chars(None);
let col_value = report_col.get_value(key, val);
let col_value = report_col.get_value(key, val, data_repr);
ret_val = ret_val.push(
Container::new(
Text::new(if col_value.len() <= max_chars {
@@ -476,7 +482,7 @@ fn filter_combobox(
combo_box_state: &combo_box::State<String>,
search_params: SearchParameters,
font: Font,
) -> Container<Message, StyleType> {
) -> Container<'_, Message, StyleType> {
let filter_value = filter_input_type.current_value(&search_params).to_string();
let is_filter_active = !filter_value.is_empty();
@@ -550,31 +556,22 @@ fn get_button_change_page<'a>(increment: bool) -> Button<'a, Message, StyleType>
fn get_agglomerates_row<'a>(
font: Font,
tot: DataInfo,
chart_type: ChartType,
data_repr: DataRepr,
) -> Row<'a, Message, StyleType> {
let tot_packets = tot.tot_packets();
let tot_bytes = tot.tot_bytes();
let (in_length, out_length) = get_bars_length(chart_type, &tot, &tot);
let (in_length, out_length) = get_bars_length(data_repr, &tot, &tot);
let bars = get_bars(in_length, out_length).width(ReportCol::FILTER_COLUMNS_WIDTH);
let bytes_col = Column::new()
let data_col = Column::new()
.align_x(Alignment::Center)
.width(ReportCol::Bytes.get_width())
.push(Text::new(ByteMultiple::formatted_string(tot_bytes)).font(font));
let packets_col = Column::new()
.align_x(Alignment::Center)
.width(ReportCol::Packets.get_width())
.push(Text::new(tot_packets.to_string()).font(font));
.width(ReportCol::Data.get_width())
.push(Text::new(data_repr.formatted_string(tot.tot_data(data_repr))).font(font));
Row::new()
.padding([0, 2])
.height(40)
.align_y(Alignment::Center)
.push(bars)
.push(bytes_col)
.push(packets_col)
.push(data_col)
}
fn get_change_page_row<'a>(
@@ -633,6 +630,7 @@ fn button_clear_filter<'a>(
#[cfg(test)]
mod tests {
use crate::gui::pages::inspect_page::title_report_col_display;
use crate::networking::types::data_representation::DataRepr;
use crate::report::types::report_col::ReportCol;
use crate::translations::types::language::Language;
@@ -641,78 +639,81 @@ fn test_table_titles_display_and_tooltip_values_for_each_language() {
// check glyph len when adding new language...
assert_eq!(Language::ALL.len(), 22);
for report_col in ReportCol::ALL {
for language in Language::ALL {
let (title, title_small, tooltip_val) =
title_report_col_display(&report_col, language);
let title_chars = title.chars().collect::<Vec<char>>();
let title_small_chars = title_small.chars().collect::<Vec<char>>();
let max_chars = report_col.get_max_chars(Some(language));
if tooltip_val.is_empty() {
// all is entirely displayed
assert!(title_chars.len() + title_small_chars.len() <= max_chars);
assert_eq!(title, report_col.get_title(language));
assert_eq!(title_small, report_col.get_title_direction_info(language));
} else {
// tooltip is the full concatenation
assert_eq!(
tooltip_val,
[
report_col.get_title(language),
report_col.get_title_direction_info(language)
]
.concat()
);
if report_col.get_title_direction_info(language).len() == 0 {
// displayed values have max len -1 (they include "…" that counts for 2 units)
assert_eq!(title_chars.len() + title_small_chars.len(), max_chars - 1);
for data_repr in DataRepr::ALL {
for language in Language::ALL {
let (title, title_small, tooltip_val) =
title_report_col_display(&report_col, data_repr, language);
let title_chars = title.chars().collect::<Vec<char>>();
let title_small_chars = title_small.chars().collect::<Vec<char>>();
let max_chars = report_col.get_max_chars(Some(language));
if tooltip_val.is_empty() {
// all is entirely displayed
assert!(title_chars.len() + title_small_chars.len() <= max_chars);
assert_eq!(title, report_col.get_title(language, data_repr));
assert_eq!(title_small, report_col.get_title_direction_info(language));
} else {
match title_chars.len() {
x if x == max_chars - 4 || x == max_chars - 3 => {
assert_eq!(title_small_chars.len(), 1)
}
_ => assert_eq!(
title_chars.len() + title_small_chars.len(),
max_chars - 1
),
}
}
if title != report_col.get_title(language) {
// first title part is not full, so second one is suspensions
assert_eq!(title_small, "");
// check len wrt max
assert!(title_chars.len() >= max_chars - 4);
// first title part is max - 2 chars of full self
// tooltip is the full concatenation
assert_eq!(
title,
report_col
.get_title(language)
.chars()
.collect::<Vec<char>>()[..max_chars - 2]
.iter()
.collect::<String>()
tooltip_val,
[
report_col.get_title(language, data_repr),
report_col.get_title_direction_info(language)
]
.concat()
);
} else {
// first part is untouched
// second title part is max - title.len - 2 chars of full self, plus suspensions
let mut second_part = [
&report_col
.get_title_direction_info(language)
.chars()
.collect::<Vec<char>>()[..max_chars - 2 - title_chars.len()]
.iter()
.collect::<String>(),
"",
]
.concat();
if second_part == String::from(" (…") || second_part == String::from("")
{
second_part = String::from("");
if report_col.get_title_direction_info(language).len() == 0 {
// displayed values have max len -1 (they include "…" that counts for 2 units)
assert_eq!(title_chars.len() + title_small_chars.len(), max_chars - 1);
} else {
match title_chars.len() {
x if x == max_chars - 4 || x == max_chars - 3 => {
assert_eq!(title_small_chars.len(), 1)
}
_ => assert_eq!(
title_chars.len() + title_small_chars.len(),
max_chars - 1
),
}
}
if title != report_col.get_title(language, data_repr) {
// first title part is not full, so second one is suspensions
assert_eq!(title_small, "");
// check len wrt max
assert!(title_chars.len() >= max_chars - 4);
// first title part is max - 2 chars of full self
assert_eq!(
title,
report_col
.get_title(language, data_repr)
.chars()
.collect::<Vec<char>>()[..max_chars - 2]
.iter()
.collect::<String>()
);
} else {
// first part is untouched
// second title part is max - title.len - 2 chars of full self, plus suspensions
let mut second_part = [
&report_col
.get_title_direction_info(language)
.chars()
.collect::<Vec<char>>()[..max_chars - 2 - title_chars.len()]
.iter()
.collect::<String>(),
"",
]
.concat();
if second_part == String::from(" (…")
|| second_part == String::from("")
{
second_part = String::from("");
}
assert_eq!(title_small, second_part);
// second part never terminates with "(…"
assert!(!title_small.ends_with("(…"));
// second part never terminates with " …"
assert!(!title_small.ends_with(""));
}
assert_eq!(title_small, second_part);
// second part never terminates with "(…"
assert!(!title_small.ends_with("(…"));
// second part never terminates with " …"
assert!(!title_small.ends_with(""));
}
}
}

View File

@@ -1,4 +1,3 @@
use crate::chart::types::chart_type::ChartType;
use crate::countries::country_utils::get_computer_tooltip;
use crate::countries::flags_pictures::FLAGS_HEIGHT_BIG;
use crate::gui::components::header::get_button_settings;
@@ -11,8 +10,10 @@
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
use crate::gui::styles::text::TextType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::service::Service;
use crate::networking::types::traffic_type::TrafficType;
@@ -21,13 +22,12 @@
};
use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
bytes_exceeded_translation, clear_all_translation, favorite_transmitted_translation,
no_notifications_received_translation, no_notifications_set_translation,
only_last_30_translation, packets_exceeded_translation, per_second_translation,
clear_all_translation, favorite_transmitted_translation, no_notifications_received_translation,
no_notifications_set_translation, only_last_30_translation, per_second_translation,
threshold_translation,
};
use crate::utils::types::icon::Icon;
use crate::{ByteMultiple, ConfigSettings, Language, RunningPage, Sniffer, StyleType};
use crate::{Language, RunningPage, Sniffer, StyleType};
use iced::Length::FillPortion;
use iced::widget::scrollable::Direction;
use iced::widget::text::LineHeight;
@@ -38,13 +38,13 @@
use std::cmp::max;
/// Computes the body of gui notifications page
pub fn notifications_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
let ConfigSettings {
pub fn notifications_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
style,
language,
notifications,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -128,7 +128,7 @@ fn body_no_notifications_received(
font: Font,
language: Language,
dots: &str,
) -> Column<Message, StyleType> {
) -> Column<'_, Message, StyleType> {
Column::new()
.padding(5)
.spacing(5)
@@ -150,13 +150,9 @@ fn data_notification_log<'a>(
language: Language,
font: Font,
) -> Container<'a, Message, StyleType> {
let chart_type = logged_notification.chart_type;
let data_string = if chart_type == ChartType::Bytes {
ByteMultiple::formatted_string(logged_notification.threshold.into())
} else {
logged_notification.threshold.to_string()
};
let icon = if chart_type == ChartType::Packets {
let data_repr = logged_notification.data_repr;
let data_string = data_repr.formatted_string(logged_notification.threshold.into());
let icon = if data_repr == DataRepr::Packets {
Icon::PacketsThreshold
} else {
Icon::BytesThreshold
@@ -184,13 +180,9 @@ fn data_notification_log<'a>(
.push(Text::new(logged_notification.timestamp.clone()).font(font)),
)
.push(
Text::new(if chart_type == ChartType::Bytes {
bytes_exceeded_translation(language)
} else {
packets_exceeded_translation(language)
})
.class(TextType::Title)
.font(font),
Text::new(data_repr.data_exceeded_translation(language).to_string())
.class(TextType::Title)
.font(font),
)
.push(
Text::new(threshold_str)
@@ -222,14 +214,14 @@ fn data_notification_log<'a>(
fn favorite_notification_log<'a>(
logged_notification: &FavoriteTransmitted,
first_entry_data_info: DataInfo,
chart_type: ChartType,
data_repr: DataRepr,
language: Language,
font: Font,
) -> Container<'a, Message, StyleType> {
let host_bar = host_bar(
&logged_notification.host,
&logged_notification.data_info_host,
chart_type,
data_repr,
first_entry_data_info,
font,
language,
@@ -291,10 +283,10 @@ fn get_button_clear_all<'a>(font: Font, language: Language) -> Tooltip<'a, Messa
}
fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
let chart_type = sniffer.traffic_chart.chart_type;
} = sniffer.conf.settings;
let data_repr = sniffer.traffic_chart.data_repr;
let font = style.get_extension().font;
let mut ret_val = Column::new()
.padding(Padding::ZERO.right(15))
@@ -306,7 +298,7 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType>
.0
.iter()
.map(LoggedNotification::data_info)
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for logged_notification in &sniffer.logged_notifications.0 {
@@ -323,7 +315,7 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType>
favorite_notification_log(
favorite_transmitted,
first_entry_data_info,
chart_type,
data_repr,
language,
font,
)
@@ -339,10 +331,10 @@ fn threshold_bar<'a>(
language: Language,
font: Font,
) -> Row<'a, Message, StyleType> {
let chart_type = logged_notification.chart_type;
let data_repr = logged_notification.data_repr;
let data_info = logged_notification.data_info;
let (incoming_bar_len, outgoing_bar_len) =
get_bars_length(chart_type, &first_entry_data_info, &data_info);
get_bars_length(data_repr, &first_entry_data_info, &data_info);
Row::new()
.align_y(Alignment::Center)
@@ -358,16 +350,9 @@ fn threshold_bar<'a>(
.push(
Column::new()
.spacing(1)
.push(
Row::new().push(horizontal_space()).push(
Text::new(if chart_type.eq(&ChartType::Packets) {
data_info.tot_packets().to_string()
} else {
ByteMultiple::formatted_string(data_info.tot_bytes())
})
.font(font),
),
)
.push(Row::new().push(horizontal_space()).push(
Text::new(data_repr.formatted_string(data_info.tot_data(data_repr))).font(font),
))
.push(get_bars(incoming_bar_len, outgoing_bar_len)),
)
}
@@ -424,7 +409,7 @@ fn data_notification_extra<'a>(
let host_bar = host_bar(
host,
data_info_host,
logged_notification.chart_type,
logged_notification.data_repr,
first_data_info_host,
font,
language,
@@ -442,7 +427,7 @@ fn data_notification_extra<'a>(
let service_bar = service_bar(
service,
data_info,
logged_notification.chart_type,
logged_notification.data_repr,
first_data_info_service,
font,
);

View File

@@ -1,7 +1,7 @@
//! Module defining the run page of the application.
//!
//! It contains elements to display traffic statistics: chart, detailed connections data
//! and overall statistics about the filtered traffic.
//! and overall statistics about the traffic.
use crate::chart::types::donut_chart::donut_chart;
use crate::countries::country_utils::get_flag_tooltip;
@@ -12,14 +12,16 @@
use crate::gui::styles::container::ContainerType;
use crate::gui::styles::rule::RuleType;
use crate::gui::styles::scrollbar::ScrollbarType;
use crate::gui::styles::style_constants::FONT_SIZE_TITLE;
use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_TITLE};
use crate::gui::styles::text::TextType;
use crate::gui::styles::types::palette_extension::PaletteExtension;
use crate::gui::types::filters::Filters;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::capture_context::CaptureSource;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::filters::Filters;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::service::Service;
use crate::report::get_report_entries::{get_host_entries, get_service_entries};
@@ -27,18 +29,16 @@
use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
active_filters_translation, error_translation, incoming_translation, no_addresses_translation,
none_translation, outgoing_translation, some_observed_translation, traffic_rate_translation,
waiting_translation,
none_translation, outgoing_translation, traffic_rate_translation, waiting_translation,
};
use crate::translations::translations_2::{
data_representation_translation, dropped_translation, host_translation,
only_top_30_items_translation,
};
use crate::translations::translations_3::{service_translation, unsupported_link_type_translation};
use crate::translations::translations_4::{excluded_translation, reading_from_pcap_translation};
use crate::utils::formatted_strings::get_active_filters_string;
use crate::translations::translations_4::reading_from_pcap_translation;
use crate::utils::types::icon::Icon;
use crate::{ByteMultiple, ChartType, ConfigSettings, Language, RunningPage, StyleType};
use crate::{Language, RunningPage, StyleType};
use iced::Length::{Fill, FillPortion};
use iced::alignment::{Horizontal, Vertical};
use iced::widget::scrollable::Direction;
@@ -48,76 +48,79 @@
Button, Column, Container, Row, Rule, Scrollable, Space, Text, Tooltip, button,
horizontal_space, vertical_space,
};
use iced::{Alignment, Font, Length, Padding};
use iced::{Alignment, Element, Font, Length, Padding};
use std::fmt::Write;
/// Computes the body of gui overview page
pub fn overview_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
let ConfigSettings {
pub fn overview_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
let mut body = Column::new();
let mut tab_and_body = Column::new().height(Length::Fill);
let dots = &sniffer.dots_pulse.0;
// some packets are there!
let tabs = get_pages_tabs(
RunningPage::Overview,
font,
font_headers,
language,
sniffer.unread_notifications,
);
tab_and_body = tab_and_body.push(tabs);
if let Some(error) = sniffer.pcap_error.as_ref() {
// pcap threw an ERROR!
body = body_pcap_error(error, dots, language, font);
} else {
// NO pcap error detected
let observed = sniffer.info_traffic.all_packets;
let filtered = sniffer.info_traffic.tot_data_info.tot_packets();
let container_chart = container_chart(sniffer, font);
match (observed, filtered) {
(0, 0) => {
//no packets observed at all
body = body_no_packets(&sniffer.capture_source, font, language, dots);
}
(observed, 0) => {
//no packets have been filtered but some have been observed
body = body_no_observed(&sniffer.filters, observed, font, language, dots);
}
(_observed, _filtered) => {
//observed > filtered > 0 || observed = filtered > 0
let tabs = get_pages_tabs(
RunningPage::Overview,
font,
font_headers,
language,
sniffer.unread_notifications,
);
tab_and_body = tab_and_body.push(tabs);
let container_info = col_info(sniffer);
let container_chart = container_chart(sniffer, font);
let container_report = row_report(sniffer);
let container_info = col_info(sniffer);
let container_report = row_report(sniffer);
body = body
.width(Length::Fill)
.padding(10)
.spacing(10)
.align_x(Alignment::Center)
.push(
Row::new()
.height(280)
.spacing(10)
.push(container_info)
.push(container_chart),
)
.push(container_report);
}
}
}
body = body
.width(Length::Fill)
.padding(10)
.spacing(10)
.align_x(Alignment::Center)
.push(
Row::new()
.height(280)
.spacing(10)
.push(container_info)
.push(container_chart),
)
.push(container_report);
Container::new(Column::new().push(tab_and_body.push(body))).height(Length::Fill)
}
pub fn waiting_page(sniffer: &Sniffer) -> Option<Container<'_, Message, StyleType>> {
let Settings {
style, language, ..
} = sniffer.conf.settings;
let font = style.get_extension().font;
let dots = &sniffer.dots_pulse.0;
let tot_packets = sniffer
.info_traffic
.tot_data_info
.tot_data(DataRepr::Packets);
let body = if let Some(error) = sniffer.pcap_error.as_ref() {
// pcap threw an ERROR!
body_pcap_error(error, dots, language, font)
} else if tot_packets == 0 {
// no packets observed at all
body_no_packets(&sniffer.capture_source, font, language, dots)
} else {
return None;
};
Some(Container::new(Column::new().push(body)).height(Length::Fill))
}
fn body_no_packets<'a>(
cs: &CaptureSource,
font: Font,
@@ -170,31 +173,6 @@ fn body_no_packets<'a>(
.push(Space::with_height(FillPortion(2)))
}
fn body_no_observed<'a>(
filters: &Filters,
observed: u128,
font: Font,
language: Language,
dots: &str,
) -> Column<'a, Message, StyleType> {
let tot_packets_text = some_observed_translation(language, observed)
.align_x(Alignment::Center)
.font(font);
Column::new()
.width(Length::Fill)
.padding(10)
.spacing(10)
.align_x(Alignment::Center)
.push(vertical_space())
.push(Icon::Funnel.to_text().size(60))
.push(get_active_filters_col(filters, language, font))
.push(Rule::horizontal(20))
.push(tot_packets_text)
.push(Text::new(dots.to_owned()).font(font).size(50))
.push(Space::with_height(FillPortion(2)))
}
fn body_pcap_error<'a>(
pcap_error: &'a str,
dots: &'a str,
@@ -241,20 +219,24 @@ fn row_report<'a>(sniffer: &Sniffer) -> Row<'a, Message, StyleType> {
}
fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let chart_type = sniffer.traffic_chart.chart_type;
let data_repr = sniffer.traffic_chart.data_repr;
let mut scroll_host = Column::new()
.padding(Padding::ZERO.right(11.0))
.align_x(Alignment::Center);
let entries = get_host_entries(&sniffer.info_traffic, chart_type, sniffer.host_sort_type);
let entries = get_host_entries(
&sniffer.info_traffic,
data_repr,
sniffer.conf.host_sort_type,
);
let first_entry_data_info = entries
.iter()
.map(|(_, d)| d.data_info)
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for (host, data_info_host) in &entries {
@@ -263,7 +245,7 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let host_bar = host_bar(
host,
data_info_host,
chart_type,
data_repr,
first_entry_data_info,
font,
language,
@@ -304,7 +286,7 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
)
.push(horizontal_space())
.push(sort_arrows(
sniffer.host_sort_type,
sniffer.conf.host_sort_type,
Message::HostSortSelection,
)),
)
@@ -318,24 +300,28 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
}
fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let chart_type = sniffer.traffic_chart.chart_type;
let data_repr = sniffer.traffic_chart.data_repr;
let mut scroll_service = Column::new()
.padding(Padding::ZERO.right(11.0))
.align_x(Alignment::Center);
let entries = get_service_entries(&sniffer.info_traffic, chart_type, sniffer.service_sort_type);
let entries = get_service_entries(
&sniffer.info_traffic,
data_repr,
sniffer.conf.service_sort_type,
);
let first_entry_data_info = entries
.iter()
.map(|&(_, d)| d)
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for (service, data_info) in &entries {
let content = service_bar(service, data_info, chart_type, first_entry_data_info, font);
let content = service_bar(service, data_info, data_repr, first_entry_data_info, font);
scroll_service = scroll_service.push(
button(content)
@@ -368,7 +354,7 @@ fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
)
.push(horizontal_space())
.push(sort_arrows(
sniffer.service_sort_type,
sniffer.conf.service_sort_type,
Message::ServiceSortSelection,
)),
)
@@ -384,16 +370,13 @@ fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
pub fn host_bar<'a>(
host: &Host,
data_info_host: &DataInfoHost,
chart_type: ChartType,
data_repr: DataRepr,
first_entry_data_info: DataInfo,
font: Font,
language: Language,
) -> Row<'a, Message, StyleType> {
let (incoming_bar_len, outgoing_bar_len) = get_bars_length(
chart_type,
&first_entry_data_info,
&data_info_host.data_info,
);
let (incoming_bar_len, outgoing_bar_len) =
get_bars_length(data_repr, &first_entry_data_info, &data_info_host.data_info);
Row::new()
.height(FLAGS_HEIGHT_BIG)
@@ -422,11 +405,10 @@ pub fn host_bar<'a>(
)
.push(horizontal_space())
.push(
Text::new(if chart_type.eq(&ChartType::Packets) {
data_info_host.data_info.tot_packets().to_string()
} else {
ByteMultiple::formatted_string(data_info_host.data_info.tot_bytes())
})
Text::new(
data_repr
.formatted_string(data_info_host.data_info.tot_data(data_repr)),
)
.font(font),
),
)
@@ -437,12 +419,12 @@ pub fn host_bar<'a>(
pub fn service_bar<'a>(
service: &Service,
data_info: &DataInfo,
chart_type: ChartType,
data_repr: DataRepr,
first_entry_data_info: DataInfo,
font: Font,
) -> Row<'a, Message, StyleType> {
let (incoming_bar_len, outgoing_bar_len) =
get_bars_length(chart_type, &first_entry_data_info, data_info);
get_bars_length(data_repr, &first_entry_data_info, data_info);
Row::new()
.height(FLAGS_HEIGHT_BIG)
@@ -456,28 +438,29 @@ pub fn service_bar<'a>(
.push(Text::new(service.to_string()).font(font))
.push(horizontal_space())
.push(
Text::new(if chart_type.eq(&ChartType::Packets) {
data_info.tot_packets().to_string()
} else {
ByteMultiple::formatted_string(data_info.tot_bytes())
})
.font(font),
Text::new(data_repr.formatted_string(data_info.tot_data(data_repr)))
.font(font),
),
)
.push(get_bars(incoming_bar_len, outgoing_bar_len)),
)
}
fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
let ConfigSettings {
fn col_info(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let PaletteExtension { font, .. } = style.get_extension();
let col_device = col_device(language, font, &sniffer.capture_source);
let col_device = col_device(
language,
font,
&sniffer.capture_source,
&sniffer.conf.filters,
);
let col_data_representation =
col_data_representation(language, font, sniffer.traffic_chart.chart_type);
col_data_representation(language, font, sniffer.traffic_chart.data_repr);
let donut_row = donut_row(language, font, sniffer);
@@ -507,8 +490,8 @@ fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
.class(ContainerType::BorderedRound)
}
fn container_chart(sniffer: &Sniffer, font: Font) -> Container<Message, StyleType> {
let ConfigSettings { language, .. } = sniffer.configs.settings;
fn container_chart(sniffer: &Sniffer, font: Font) -> Container<'_, Message, StyleType> {
let Settings { language, .. } = sniffer.conf.settings;
let traffic_chart = &sniffer.traffic_chart;
Container::new(
@@ -534,6 +517,7 @@ fn col_device<'a>(
language: Language,
font: Font,
cs: &CaptureSource,
filters: &'a Filters,
) -> Column<'a, Message, StyleType> {
let link_type = cs.get_link_type();
#[cfg(not(target_os = "windows"))]
@@ -541,21 +525,55 @@ fn col_device<'a>(
#[cfg(target_os = "windows")]
let cs_info = cs.get_desc().unwrap_or(cs.get_name());
let filters_desc: Element<Message, StyleType> = if filters.is_some_filter_active() {
Row::new()
.spacing(10)
.push(Text::new("BPF").font(font))
.push(get_info_tooltip(filters.bpf().to_string(), font))
.into()
} else {
Text::new(none_translation(language)).font(font).into()
};
Column::new()
.height(Length::Fill)
.spacing(10)
.push(TextType::highlighted_subtitle_with_desc(
cs.title(language),
&cs_info,
font,
))
.push(link_type.link_type_col(language, font))
.push(
Column::new()
.push(
Text::new(format!("{}:", cs.title(language)))
.class(TextType::Subtitle)
.font(font),
)
.push(
Row::new()
.spacing(10)
.push(Text::new(format!(" {}", &cs_info)).font(font))
.push(get_info_tooltip(
link_type.full_print_on_one_line(language),
font,
)),
),
)
.push(
Column::new()
.push(
Text::new(format!("{}:", active_filters_translation(language)))
.class(TextType::Subtitle)
.font(font),
)
.push(
Row::new()
.push(Text::new(" ".to_string()).font(font))
.push(filters_desc),
),
)
}
fn col_data_representation<'a>(
language: Language,
font: Font,
chart_type: ChartType,
data_repr: DataRepr,
) -> Column<'a, Message, StyleType> {
let mut ret_val = Column::new().spacing(5).push(
Text::new(format!("{}:", data_representation_translation(language)))
@@ -563,77 +581,61 @@ fn col_data_representation<'a>(
.font(font),
);
for option in ChartType::ALL {
let is_active = chart_type.eq(&option);
ret_val = ret_val.push(
Button::new(
Text::new(option.get_label(language).to_owned())
.width(Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center)
.font(font),
)
.width(Length::Fill)
.height(33)
.class(if is_active {
ButtonType::BorderedRoundSelected
} else {
ButtonType::BorderedRound
})
.on_press(Message::ChartSelection(option)),
);
}
let [bits, bytes, packets] = DataRepr::ALL.map(|option| {
let is_active = data_repr.eq(&option);
Button::new(
Text::new(option.get_label(language).to_owned())
.width(Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center)
.font(font),
)
.width(Length::Fill)
.height(33)
.class(if is_active {
ButtonType::BorderedRoundSelected
} else {
ButtonType::BorderedRound
})
.on_press(Message::DataReprSelection(option))
});
ret_val = ret_val
.push(Row::new().spacing(5).push(bits).push(bytes))
.push(packets);
ret_val
}
fn donut_row<'a>(
fn donut_row(
language: Language,
font: Font,
sniffer: &Sniffer,
) -> Container<'a, Message, StyleType> {
let chart_type = sniffer.traffic_chart.chart_type;
let filters = &sniffer.filters;
) -> Container<'_, Message, StyleType> {
let data_repr = sniffer.traffic_chart.data_repr;
let (in_data, out_data, filtered_out, dropped) =
sniffer.info_traffic.get_thumbnail_data(chart_type);
let legend_entry_filtered = if filters.none_active() {
None
} else {
Some(donut_legend_entry(
filtered_out,
chart_type,
RuleType::FilteredOut,
filters,
font,
language,
))
};
let (in_data, out_data, dropped) = sniffer.info_traffic.get_thumbnail_data(data_repr);
let legend_col = Column::new()
.spacing(5)
.push(donut_legend_entry(
in_data,
chart_type,
data_repr,
RuleType::Incoming,
filters,
font,
language,
))
.push(donut_legend_entry(
out_data,
chart_type,
data_repr,
RuleType::Outgoing,
filters,
font,
language,
))
.push_maybe(legend_entry_filtered)
.push(donut_legend_entry(
dropped,
chart_type,
data_repr,
RuleType::Dropped,
filters,
font,
language,
));
@@ -642,10 +644,9 @@ fn donut_row<'a>(
.align_y(Vertical::Center)
.spacing(20)
.push(donut_chart(
chart_type,
data_repr,
in_data,
out_data,
filtered_out,
dropped,
font,
sniffer.thumbnail,
@@ -661,32 +662,20 @@ fn donut_row<'a>(
fn donut_legend_entry<'a>(
value: u128,
chart_type: ChartType,
data_repr: DataRepr,
rule_type: RuleType,
filters: &Filters,
font: Font,
language: Language,
) -> Row<'a, Message, StyleType> {
let value_text = if chart_type.eq(&ChartType::Bytes) {
ByteMultiple::formatted_string(value)
} else {
value.to_string()
};
let value_text = data_repr.formatted_string(value);
let label = match rule_type {
RuleType::Incoming => incoming_translation(language),
RuleType::Outgoing => outgoing_translation(language),
RuleType::FilteredOut => excluded_translation(language),
RuleType::Dropped => dropped_translation(language),
_ => "",
};
let tooltip = if matches!(rule_type, RuleType::FilteredOut) {
Some(get_active_filters_tooltip(filters, language, font))
} else {
None
};
Row::new()
.spacing(10)
.align_y(Alignment::Center)
@@ -696,28 +685,18 @@ fn donut_legend_entry<'a>(
.push(Rule::horizontal(1).class(rule_type)),
)
.push(Text::new(format!("{label}: {value_text}")).font(font))
.push_maybe(tooltip)
}
const MIN_BARS_LENGTH: f32 = 4.0;
pub fn get_bars_length(
chart_type: ChartType,
data_repr: DataRepr,
first_entry: &DataInfo,
data_info: &DataInfo,
) -> (u16, u16) {
let (in_val, out_val, first_entry_tot_val) = match chart_type {
ChartType::Packets => (
data_info.incoming_packets(),
data_info.outgoing_packets(),
first_entry.tot_packets(),
),
ChartType::Bytes => (
data_info.incoming_bytes(),
data_info.outgoing_bytes(),
first_entry.tot_bytes(),
),
};
let in_val = data_info.incoming_data(data_repr);
let out_val = data_info.outgoing_data(data_repr);
let first_entry_tot_val = first_entry.tot_data(data_repr);
let tot_val = in_val + out_val;
if tot_val == 0 {
@@ -808,46 +787,12 @@ fn get_star_button<'a>(is_favorite: bool, host: Host) -> Button<'a, Message, Sty
.on_press(Message::AddOrRemoveFavorite(host, !is_favorite))
}
fn get_active_filters_col<'a>(
filters: &Filters,
language: Language,
font: Font,
) -> Column<'a, Message, StyleType> {
let mut ret_val = Column::new().push(
Text::new(active_filters_translation(language))
.font(font)
.class(TextType::Subtitle),
);
if filters.none_active() {
ret_val = ret_val.push(Text::new(format!(" {}", none_translation(language))).font(font));
} else {
let filters_string = get_active_filters_string(filters, language);
ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font)));
}
ret_val
}
fn get_active_filters_tooltip<'a>(
filters: &Filters,
language: Language,
font: Font,
) -> Tooltip<'a, Message, StyleType> {
let filters_string = get_active_filters_string(filters, language);
let mut ret_val = Column::new().push(
Text::new(active_filters_translation(language))
.font(font)
.class(TextType::Subtitle),
);
ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font)));
fn get_info_tooltip<'a>(info_str: String, font: Font) -> Tooltip<'a, Message, StyleType> {
Tooltip::new(
Container::new(
Text::new("i")
.size(FONT_SIZE_FOOTER)
.font(font)
.size(15)
.line_height(LineHeight::Relative(1.0)),
)
.align_x(Alignment::Center)
@@ -855,7 +800,7 @@ fn get_active_filters_tooltip<'a>(
.height(20)
.width(20)
.class(ContainerType::BadgeInfo),
ret_val,
Text::new(info_str).font(font),
Position::FollowCursor,
)
.class(ContainerType::Tooltip)
@@ -881,20 +826,24 @@ fn sort_arrows<'a>(
#[cfg(test)]
mod tests {
use crate::chart::types::chart_type::ChartType;
use crate::gui::pages::overview_page::{MIN_BARS_LENGTH, get_bars_length};
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_representation::DataRepr;
#[test]
fn test_get_bars_length_simple() {
let first_entry = DataInfo::new_for_tests(50, 50, 150, 50);
let data_info = DataInfo::new_for_tests(25, 55, 165, 30);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(25, 55)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(83, 15)
);
assert_eq!(
get_bars_length(DataRepr::Bits, &first_entry, &data_info),
(83, 15)
);
}
@@ -904,21 +853,21 @@ fn test_get_bars_length_normalize_small_values() {
let first_entry = DataInfo::new_for_tests(50, 50, 150, 50);
let mut data_info = DataInfo::new_for_tests(2, 1, 1, 0);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16, 0)
);
data_info = DataInfo::new_for_tests(0, 3, 0, 2);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
}
@@ -929,31 +878,31 @@ fn test_get_bars_length_normalize_very_small_values() {
DataInfo::new_for_tests(u128::MAX / 2, u128::MAX / 2, u128::MAX / 2, u128::MAX / 2);
let mut data_info = DataInfo::new_for_tests(1, 1, 1, 1);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
data_info = DataInfo::new_for_tests(0, 1, 0, 1);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
data_info = DataInfo::new_for_tests(1, 0, 1, 0);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16, 0)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16, 0)
);
}
@@ -964,93 +913,93 @@ fn test_get_bars_length_complex() {
let mut data_info = DataInfo::new_for_tests(0, 9, 0, 10);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, 16)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, 71)
);
data_info = DataInfo::new_for_tests(9, 0, 13, 0);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(16, 0)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(93, 0)
);
data_info = DataInfo::new_for_tests(4, 5, 6, 7);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(7, 9)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(43, 50)
);
data_info = DataInfo::new_for_tests(5, 4, 7, 6);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(9, 7)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(50, 43)
);
data_info = DataInfo::new_for_tests(1, 8, 1, 12);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, 14)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(7, 86)
);
data_info = DataInfo::new_for_tests(8, 1, 12, 1);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(14, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(86, 7)
);
data_info = DataInfo::new_for_tests(6, 1, 10, 1);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(11, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(71, 7)
);
data_info = DataInfo::new_for_tests(1, 6, 1, 9);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, 11,)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(7, 64)
);
data_info = DataInfo::new_for_tests(1, 6, 5, 5);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(36, 36)
);
data_info = DataInfo::new_for_tests(0, 0, 0, 0);
assert_eq!(
get_bars_length(ChartType::Packets, &first_entry, &data_info),
get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, 0)
);
assert_eq!(
get_bars_length(ChartType::Bytes, &first_entry, &data_info),
get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, 0)
);
}

View File

@@ -14,6 +14,7 @@
use crate::gui::styles::style_constants::FONT_SIZE_SUBTITLE;
use crate::gui::styles::text::TextType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders};
use crate::translations::translations::language_translation;
use crate::translations::translations_2::country_translation;
@@ -25,15 +26,15 @@
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
use crate::utils::types::web_page::WebPage;
use crate::{ConfigSettings, Language, RunningPage, Sniffer, StyleType};
use crate::{Language, Sniffer, StyleType};
pub fn settings_general_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
let ConfigSettings {
pub fn settings_general_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -56,16 +57,16 @@ pub fn settings_general_page(sniffer: &Sniffer) -> Container<Message, StyleType>
.class(ContainerType::Modal)
}
fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column<Message, StyleType> {
let ConfigSettings {
fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column<'_, Message, StyleType> {
let Settings {
language,
scale_factor,
mmdb_country,
mmdb_asn,
..
} = sniffer.configs.settings.clone();
} = sniffer.conf.settings.clone();
let is_editable = sniffer.running_page.eq(&RunningPage::Init);
let is_editable = sniffer.running_page.is_none();
let mut column = Column::new()
.align_x(Alignment::Center)

View File

@@ -3,7 +3,6 @@
use iced::widget::{Checkbox, Column, Container, Row, Scrollable, Space, Text, TextInput};
use iced::{Alignment, Font, Length, Padding};
use crate::chart::types::chart_type::ChartType;
use crate::gui::components::button::button_hide;
use crate::gui::components::tab::get_settings_tabs;
use crate::gui::pages::types::settings_page::SettingsPage;
@@ -14,6 +13,8 @@
use crate::gui::styles::text::TextType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::data_representation::DataRepr;
use crate::notifications::types::notifications::{
DataNotification, FavoriteNotification, Notification,
};
@@ -25,16 +26,16 @@
use crate::translations::translations_2::data_representation_translation;
use crate::translations::translations_4::data_exceeded_translation;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, Sniffer, StyleType};
use crate::{Language, Sniffer, StyleType};
pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
mut notifications,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -71,11 +72,14 @@ pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Messa
.push(Space::with_height(5));
let volume_notification_col = Column::new()
.padding(5)
.spacing(10)
.align_x(Alignment::Center)
.width(Length::Fill)
.push(volume_slider(language, font, notifications.volume))
.push(Scrollable::with_direction(
Column::new()
.spacing(10)
.align_x(Alignment::Center)
.width(Length::Fill)
.push(get_data_notify(
@@ -103,7 +107,7 @@ fn get_data_notify<'a>(
data_notification: DataNotification,
language: Language,
font: Font,
) -> Column<'a, Message, StyleType> {
) -> Container<'a, Message, StyleType> {
let checkbox = Checkbox::new(
data_exceeded_translation(language),
data_notification.threshold.is_some(),
@@ -133,18 +137,16 @@ fn get_data_notify<'a>(
let mut ret_val = Column::new().spacing(15).push(checkbox);
if data_notification.threshold.is_none() {
Column::new().padding(5).push(
Container::new(ret_val)
.padding(10)
.width(700)
.class(ContainerType::BorderedRound),
)
Container::new(ret_val)
.padding(15)
.width(700)
.class(ContainerType::BorderedRound)
} else {
let data_representation_row = row_data_representation(
data_notification,
language,
font,
data_notification.chart_type,
data_notification.data_repr,
);
let input_row = input_group_bytes(data_notification, font, language);
let sound_row = sound_buttons(Notification::Data(data_notification), font, language);
@@ -152,12 +154,11 @@ fn get_data_notify<'a>(
.push(sound_row)
.push(data_representation_row)
.push(input_row);
Column::new().padding(5).push(
Container::new(ret_val)
.padding(10)
.width(700)
.class(ContainerType::BorderedRound),
)
Container::new(ret_val)
.padding(15)
.width(700)
.class(ContainerType::BorderedRound)
}
}
@@ -165,7 +166,7 @@ fn get_favorite_notify<'a>(
favorite_notification: FavoriteNotification,
language: Language,
font: Font,
) -> Column<'a, Message, StyleType> {
) -> Container<'a, Message, StyleType> {
let checkbox = Checkbox::new(
favorite_transmitted_translation(language),
favorite_notification.notify_on_favorite,
@@ -192,19 +193,15 @@ fn get_favorite_notify<'a>(
language,
);
ret_val = ret_val.push(sound_row);
Column::new().padding(5).push(
Container::new(ret_val)
.padding(10)
.width(700)
.class(ContainerType::BorderedRound),
)
Container::new(ret_val)
.padding(15)
.width(700)
.class(ContainerType::BorderedRound)
} else {
Column::new().padding(5).push(
Container::new(ret_val)
.padding(10)
.width(700)
.class(ContainerType::BorderedRound),
)
Container::new(ret_val)
.padding(15)
.width(700)
.class(ContainerType::BorderedRound)
}
}
@@ -220,7 +217,7 @@ fn input_group_bytes<'a>(
let input_row = Row::new()
.spacing(5)
.align_y(Alignment::Center)
.push(Space::with_width(45))
.padding(Padding::ZERO.left(26))
.push(Text::new(format!("{}:", threshold_translation(language))).font(font))
.push(
TextInput::new(
@@ -305,7 +302,7 @@ fn sound_buttons<'a>(
.width(Length::Shrink)
.align_y(Alignment::Center)
.spacing(5)
.push(Space::with_width(45))
.padding(Padding::ZERO.left(26))
.push(Text::new(format!("{}:", sound_translation(language))).font(font));
for option in Sound::ALL {
@@ -372,17 +369,17 @@ fn row_data_representation<'a>(
data_notification: DataNotification,
language: Language,
font: Font,
chart_type: ChartType,
data_repr: DataRepr,
) -> Row<'a, Message, StyleType> {
let mut ret_val = Row::new()
.width(Length::Shrink)
.align_y(Alignment::Center)
.spacing(5)
.push(Space::with_width(45))
.padding(Padding::ZERO.left(26))
.push(Text::new(format!("{}:", data_representation_translation(language))).font(font));
for option in ChartType::ALL {
let is_active = chart_type.eq(&option);
for option in DataRepr::ALL {
let is_active = data_repr.eq(&option);
ret_val = ret_val.push(
Button::new(
Text::new(option.get_label(language).to_owned())
@@ -400,7 +397,7 @@ fn row_data_representation<'a>(
})
.on_press(Message::UpdateNotificationSettings(
Notification::Data(DataNotification {
chart_type: option,
data_repr: option,
..data_notification
}),
false,

View File

@@ -19,22 +19,23 @@
use crate::gui::styles::types::palette::Palette;
use crate::gui::styles::types::palette_extension::PaletteExtension;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::translations::translations::appearance_title_translation;
use crate::translations::translations_2::color_gradients_translation;
use crate::translations::translations_3::custom_style_translation;
use crate::utils::formatted_strings::get_path_termination_string;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, Sniffer, StyleType};
use crate::{Language, Sniffer, StyleType};
pub fn settings_style_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
let ConfigSettings {
pub fn settings_style_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
style,
language,
color_gradient,
style_path,
..
} = sniffer.configs.settings.clone();
} = sniffer.conf.settings.clone();
let PaletteExtension {
font, font_headers, ..
} = style.get_extension();

View File

@@ -4,14 +4,14 @@
use iced::widget::{Column, Container, Row, Rule, Space, Text, vertical_space};
use iced::{Alignment, Font, Length};
use crate::chart::types::chart_type::ChartType;
use crate::chart::types::donut_chart::donut_chart;
use crate::configs::types::config_settings::ConfigSettings;
use crate::countries::country_utils::get_flag_tooltip;
use crate::gui::sniffer::Sniffer;
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
use crate::gui::styles::types::style_type::StyleType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::{Host, ThumbnailHost};
use crate::networking::types::info_traffic::InfoTraffic;
use crate::report::get_report_entries::{get_host_entries, get_service_entries};
@@ -23,13 +23,16 @@
const MAX_CHARS_SERVICE: usize = 13;
/// Computes the body of the thumbnail view
pub fn thumbnail_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
let ConfigSettings { style, .. } = sniffer.configs.settings;
pub fn thumbnail_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings { style, .. } = sniffer.conf.settings;
let font = style.get_extension().font;
let filtered = sniffer.info_traffic.tot_data_info.tot_packets();
let tot_packets = sniffer
.info_traffic
.tot_data_info
.tot_data(DataRepr::Packets);
if filtered == 0 {
if tot_packets == 0 {
return Container::new(
Column::new()
.push(vertical_space())
@@ -41,19 +44,18 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
}
let info_traffic = &sniffer.info_traffic;
let chart_type = sniffer.traffic_chart.chart_type;
let data_repr = sniffer.traffic_chart.data_repr;
let (in_data, out_data, filtered_out, dropped) = info_traffic.get_thumbnail_data(chart_type);
let (in_data, out_data, dropped) = info_traffic.get_thumbnail_data(data_repr);
let charts = Row::new()
.padding(5)
.height(Length::Fill)
.align_y(Alignment::Center)
.push(donut_chart(
chart_type,
data_repr,
in_data,
out_data,
filtered_out,
dropped,
font,
sniffer.thumbnail,
@@ -71,16 +73,16 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
.align_y(Alignment::Start)
.push(host_col(
info_traffic,
chart_type,
data_repr,
font,
sniffer.host_sort_type,
sniffer.conf.host_sort_type,
))
.push(Rule::vertical(10))
.push(service_col(
info_traffic,
chart_type,
data_repr,
font,
sniffer.service_sort_type,
sniffer.conf.service_sort_type,
));
let content = Column::new()
@@ -93,7 +95,7 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
fn host_col<'a>(
info_traffic: &InfoTraffic,
chart_type: ChartType,
data_repr: DataRepr,
font: Font,
sort_type: SortType,
) -> Column<'a, Message, StyleType> {
@@ -101,7 +103,7 @@ fn host_col<'a>(
.padding([0, 5])
.spacing(3)
.width(Length::FillPortion(2));
let hosts = get_host_entries(info_traffic, chart_type, sort_type);
let hosts = get_host_entries(info_traffic, data_repr, sort_type);
let mut thumbnail_hosts = Vec::new();
for (host, data_info_host) in &hosts {
@@ -136,12 +138,12 @@ fn host_col<'a>(
fn service_col<'a>(
info_traffic: &InfoTraffic,
chart_type: ChartType,
data_repr: DataRepr,
font: Font,
sort_type: SortType,
) -> Column<'a, Message, StyleType> {
let mut service_col = Column::new().padding([0, 5]).spacing(3).width(Length::Fill);
let services = get_service_entries(info_traffic, chart_type, sort_type);
let services = get_service_entries(info_traffic, data_repr, sort_type);
let n_entry = min(services.len(), MAX_ENTRIES);
for (service, _) in services.get(..n_entry).unwrap_or_default() {
service_col = service_col.push(

View File

@@ -3,13 +3,13 @@
use crate::translations::translations_2::inspect_translation;
use crate::utils::types::icon::Icon;
use crate::{Language, StyleType};
use serde::{Deserialize, Serialize};
/// This enum defines the current GUI page.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
/// This enum defines the current running page.
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Default)]
pub enum RunningPage {
/// Initial page.
Init,
/// Overview page.
#[default]
Overview,
/// Inspect page.
Inspect,
@@ -29,7 +29,6 @@ pub fn get_tab_label(&self, language: Language) -> &str {
RunningPage::Overview => overview_translation(language),
RunningPage::Inspect => inspect_translation(language),
RunningPage::Notifications => notifications_translation(language),
RunningPage::Init => "",
}
}
@@ -38,7 +37,6 @@ pub fn next(self) -> Self {
RunningPage::Overview => RunningPage::Inspect,
RunningPage::Inspect => RunningPage::Notifications,
RunningPage::Notifications => RunningPage::Overview,
RunningPage::Init => RunningPage::Init,
}
}
@@ -47,7 +45,6 @@ pub fn previous(self) -> Self {
RunningPage::Overview => RunningPage::Notifications,
RunningPage::Inspect => RunningPage::Overview,
RunningPage::Notifications => RunningPage::Inspect,
RunningPage::Init => RunningPage::Init,
}
}
@@ -56,7 +53,6 @@ pub fn icon<'a>(self) -> iced::widget::Text<'a, StyleType> {
RunningPage::Overview => Icon::Overview,
RunningPage::Inspect => Icon::Inspect,
RunningPage::Notifications => Icon::Notification,
RunningPage::Init => Icon::Sniffnet,
}
.to_text()
}

View File

@@ -3,11 +3,13 @@
use crate::translations::translations_3::general_translation;
use crate::utils::types::icon::Icon;
use crate::{Language, StyleType};
use serde::{Deserialize, Serialize};
/// This enum defines the current settings page.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Default)]
pub enum SettingsPage {
/// Settings Notifications page.
#[default]
Notifications,
/// Settings Appearance page.
Appearance,

View File

File diff suppressed because it is too large Load Diff

View File

@@ -64,7 +64,9 @@ fn active(&self, style: &StyleType) -> Style {
radius: match self {
ButtonType::Neutral => 0.0.into(),
ButtonType::TabActive | ButtonType::TabInactive => Radius::new(0).bottom(30),
ButtonType::BorderedRound | ButtonType::BorderedRoundSelected => 12.0.into(),
ButtonType::BorderedRound
| ButtonType::BorderedRoundSelected
| ButtonType::Gradient(_) => 12.0.into(),
ButtonType::Starred | ButtonType::NotStarred => 100.0.into(),
_ => BORDER_BUTTON_RADIUS.into(),
},
@@ -154,7 +156,9 @@ fn hovered(&self, style: &StyleType) -> Style {
radius: match self {
ButtonType::Neutral => 0.0.into(),
ButtonType::TabActive | ButtonType::TabInactive => Radius::new(0).bottom(30),
ButtonType::BorderedRound | ButtonType::BorderedRoundSelected => 12.0.into(),
ButtonType::BorderedRound
| ButtonType::BorderedRoundSelected
| ButtonType::Gradient(_) => 12.0.into(),
ButtonType::Starred | ButtonType::NotStarred => 100.0.into(),
_ => BORDER_BUTTON_RADIUS.into(),
},
@@ -208,7 +212,7 @@ fn disabled(&self, style: &StyleType) -> Style {
_ => Background::Color(ext.buttons_color),
}),
border: Border {
radius: BORDER_BUTTON_RADIUS.into(),
radius: 12.0.into(),
width: BORDER_WIDTH,
color: Color {
a: ext.alpha_chart_badge,
@@ -216,7 +220,7 @@ fn disabled(&self, style: &StyleType) -> Style {
},
},
text_color: Color {
a: ext.alpha_chart_badge,
a: 0.5,
..colors.text_headers
},
shadow: Shadow::default(),

View File

@@ -25,8 +25,7 @@ fn active(&self, style: &StyleType) -> Style {
incoming: colors.secondary,
outgoing: colors.outgoing,
text_color: colors.text_body,
filtered_out: ext.buttons_color,
dropped: ext.red_alert_color,
dropped: ext.buttons_color,
}
}
}
@@ -48,7 +47,6 @@ pub struct Style {
pub(crate) text_color: Color,
pub(crate) incoming: Color,
pub(crate) outgoing: Color,
pub(crate) filtered_out: Color,
pub(crate) dropped: Color,
}

View File

@@ -13,7 +13,6 @@ pub enum RuleType {
PaletteColor(Color, u16),
Incoming,
Outgoing,
FilteredOut,
Dropped,
}
@@ -26,8 +25,7 @@ fn appearance(&self, style: &StyleType) -> Style {
RuleType::Incoming => colors.secondary,
RuleType::Outgoing => colors.outgoing,
RuleType::PaletteColor(color, _) => *color,
RuleType::Dropped => ext.red_alert_color,
RuleType::FilteredOut => ext.buttons_color,
RuleType::Dropped => ext.buttons_color,
RuleType::Standard => Color {
a: ext.alpha_round_borders,
..ext.buttons_color

View File

@@ -13,7 +13,7 @@ pub enum TextInputType {
#[default]
Standard,
Badge,
Error,
// Error,
}
const TEXT_INPUT_BORDER_RADIUS: f32 = 5.0;
@@ -25,7 +25,8 @@ fn active(&self, style: &StyleType) -> Style {
Style {
background: Background::Color(match self {
TextInputType::Badge => Color::TRANSPARENT,
_ => Color {
// TextInputType::Error |
TextInputType::Standard => Color {
a: ext.alpha_round_borders,
..ext.buttons_color
},
@@ -36,7 +37,7 @@ fn active(&self, style: &StyleType) -> Style {
color: match self {
TextInputType::Badge => Color::TRANSPARENT,
TextInputType::Standard => ext.buttons_color,
TextInputType::Error => ext.red_alert_color,
// TextInputType::Error => ext.red_alert_color,
},
},
icon: Color {
@@ -51,17 +52,17 @@ fn active(&self, style: &StyleType) -> Style {
fn focused(&self, style: &StyleType) -> Style {
let colors = style.get_palette();
let ext = style.get_extension();
// let ext = style.get_extension();
let is_nightly = style.get_extension().is_nightly;
Style {
background: Background::Color(colors.primary),
border: Border {
radius: TEXT_INPUT_BORDER_RADIUS.into(),
width: BORDER_WIDTH,
color: match self {
TextInputType::Error => ext.red_alert_color,
_ => colors.secondary,
},
color: colors.secondary, // match self {
// TextInputType::Error => ext.red_alert_color,
// _ => colors.secondary,
// },
},
icon: Color {
a: if is_nightly { 0.2 } else { 0.7 },
@@ -114,15 +115,16 @@ fn hovered(&self, style: &StyleType) -> Style {
Style {
background: Background::Color(match self {
TextInputType::Badge => Color::TRANSPARENT,
_ => ext.buttons_color,
// TextInputType::Error |
TextInputType::Standard => ext.buttons_color,
}),
border: Border {
radius: TEXT_INPUT_BORDER_RADIUS.into(),
width: BORDER_WIDTH,
color: match self {
TextInputType::Error => ext.red_alert_color,
_ => colors.secondary,
},
color: colors.secondary, // match self {
// TextInputType::Error => ext.red_alert_color,
// _ => colors.secondary,
// },
},
icon: Color {
a: if ext.is_nightly { 0.2 } else { 0.7 },
@@ -140,7 +142,8 @@ fn disabled(&self, style: &StyleType) -> Style {
Style {
background: Background::Color(match self {
TextInputType::Badge => Color::TRANSPARENT,
_ => Color {
// TextInputType::Error |
TextInputType::Standard => Color {
a: ext.alpha_round_containers,
..ext.buttons_color
},
@@ -154,10 +157,10 @@ fn disabled(&self, style: &StyleType) -> Style {
a: ext.alpha_round_borders,
..ext.buttons_color
},
TextInputType::Error => Color {
a: ext.alpha_round_borders,
..ext.red_alert_color
},
// TextInputType::Error => Color {
// a: ext.alpha_round_borders,
// ..ext.red_alert_color
// },
},
},
icon: Color {

87
src/gui/types/conf.rs Normal file
View File

@@ -0,0 +1,87 @@
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::types::config_window::ConfigWindow;
use crate::gui::types::export_pcap::ExportPcap;
use crate::gui::types::filters::Filters;
use crate::gui::types::settings::Settings;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::networking::types::config_device::ConfigDevice;
use crate::report::types::sort_type::SortType;
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
#[cfg(not(test))]
use confy::ConfyError;
use serde::{Deserialize, Serialize};
pub static CONF: std::sync::LazyLock<Conf> = std::sync::LazyLock::new(Conf::load);
#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct Conf {
/// Parameters from settings pages
pub settings: Settings,
/// Last selected network device name
pub device: ConfigDevice,
/// Window configuration, such as size and position
pub window: ConfigWindow,
/// Capture source picklist, to select the source of the capture
pub capture_source_picklist: CaptureSourcePicklist,
/// BPF filter program to be applied to the capture
pub filters: Filters,
/// Report sort type (inspect page)
pub report_sort_type: SortType,
/// Host sort type (overview page)
pub host_sort_type: SortType,
/// Service sort type (overview page)
pub service_sort_type: SortType,
/// Remembers the last opened setting page
pub last_opened_setting: SettingsPage,
/// Remembers the last opened running page
pub last_opened_page: RunningPage,
/// Information about PCAP file export
pub export_pcap: ExportPcap,
/// Import path for PCAP file
pub import_pcap_path: String,
}
impl Conf {
const FILE_NAME: &'static str = "conf";
/// This should only be used directly to load fresh configurations;
/// use `CONF` instead to access the initial instance
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(conf) = confy::load::<Conf>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
conf
} else {
let _ = Conf::default().store();
Conf::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
}
#[cfg(test)]
mod tests {
use crate::gui::types::conf::Conf;
impl Conf {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<Conf>(Conf::test_path()).unwrap_or_else(|_| Conf::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(Conf::test_path(), self)
}
}
}

View File

@@ -1,7 +1,3 @@
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
use iced::window::Position;
use iced::{Point, Size};
use serde::{Deserialize, Serialize};
@@ -12,6 +8,7 @@
pub struct SizeTuple(pub f32, pub f32);
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct ConfigWindow {
pub position: PositionTuple,
pub size: SizeTuple,
@@ -30,23 +27,6 @@ impl ConfigWindow {
const MIN_SIZE_X: f32 = 100.0;
const MIN_SIZE_Y: f32 = 100.0;
const FILE_NAME: &'static str = "window";
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(window) = confy::load::<ConfigWindow>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
window
} else {
let _ = confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, ConfigWindow::default())
.log_err(location!());
ConfigWindow::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
pub fn thumbnail_size(factor: f64) -> SizeTuple {
Self::THUMBNAIL_SIZE.scale_and_check(factor)
}
@@ -142,23 +122,3 @@ fn scale_and_check(self, factor: f64) -> PositionTuple {
PositionTuple(x, y)
}
}
#[cfg(test)]
mod tests {
use crate::ConfigWindow;
impl ConfigWindow {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<ConfigWindow>(ConfigWindow::test_path())
.unwrap_or_else(|_| ConfigWindow::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(ConfigWindow::test_path(), self)
}
}
}

View File

@@ -1,9 +1,12 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct ExportPcap {
enabled: bool,
file_name: String,
directory: String,
pub(crate) enabled: bool,
pub(crate) file_name: String,
pub(crate) directory: String,
}
impl ExportPcap {

87
src/gui/types/filters.rs Normal file
View File

@@ -0,0 +1,87 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
#[serde(default)]
pub struct Filters {
pub(crate) expanded: bool,
pub(crate) bpf: String,
}
impl Filters {
pub fn toggle(&mut self) {
self.expanded = !self.expanded;
}
pub fn set_bpf(&mut self, bpf: String) {
self.bpf = bpf;
}
pub fn expanded(&self) -> bool {
self.expanded
}
pub fn bpf(&self) -> &str {
&self.bpf
}
pub fn is_some_filter_active(&self) -> bool {
self.expanded && !self.bpf.trim().is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default() {
let filters = Filters::default();
assert_eq!(filters.expanded(), false);
assert_eq!(filters.bpf(), "");
}
#[test]
fn test_toggle() {
let mut filters = Filters::default();
assert_eq!(filters.expanded(), false);
filters.toggle();
assert_eq!(filters.expanded(), true);
filters.toggle();
assert_eq!(filters.expanded(), false);
}
#[test]
fn test_set_bpf() {
let mut filters = Filters::default();
assert_eq!(filters.bpf(), "");
filters.set_bpf("tcp port 80".to_string());
assert_eq!(filters.bpf(), "tcp port 80");
filters.set_bpf(" udp port 53 ".to_string());
assert_eq!(filters.bpf(), " udp port 53 ");
}
#[test]
fn test_is_some_filter_active() {
let mut filters = Filters::default();
assert_eq!(filters.is_some_filter_active(), false);
filters.toggle();
assert_eq!(filters.is_some_filter_active(), false);
filters.set_bpf("tcp port 80".to_string());
assert_eq!(filters.is_some_filter_active(), true);
filters.toggle();
assert_eq!(filters.is_some_filter_active(), false);
filters.toggle();
assert_eq!(filters.is_some_filter_active(), true);
filters.set_bpf(" \t \n ".to_string());
assert_eq!(filters.is_some_filter_active(), false);
}
}

View File

@@ -5,6 +5,8 @@
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::info_traffic::InfoTraffic;
use crate::notifications::types::notifications::Notification;
@@ -12,7 +14,7 @@
use crate::report::types::sort_type::SortType;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::web_page::WebPage;
use crate::{ChartType, IpVersion, Language, Protocol, ReportSortType, StyleType};
use crate::{Language, StyleType};
#[derive(Debug, Clone)]
/// Messages types that permit reacting to application interactions/subscriptions
@@ -21,20 +23,18 @@ pub enum Message {
StartApp(Option<window::Id>),
/// Sent by the backend parsing packets; includes the capture id, new data, new hosts batched data, and whether an offline capture has finished
TickRun(usize, InfoTraffic, Vec<HostMessage>, bool),
/// Capture source selected from the picklist
SetCaptureSource(CaptureSourcePicklist),
/// Select network device
DeviceSelection(String),
/// Select IP filter
IpVersionSelection(IpVersion, bool),
/// Select protocol filter
ProtocolSelection(Protocol, bool),
/// Changed address filter
AddressFilter(String),
/// Changed port filter
PortFilter(String),
/// Select chart type to be displayed
ChartSelection(ChartType),
/// Toggle BPF filter checkbox
ToggleFilters,
/// Change BPF filter string
BpfFilter(String),
/// Select data representation to use
DataReprSelection(DataRepr),
/// Select report sort type to be displayed (inspect page)
ReportSortSelection(ReportSortType),
ReportSortSelection(SortType),
/// Select host sort type to be displayed (overview page)
HostSortSelection(SortType),
/// Select service sort type to be displayed (overview page)

View File

@@ -1,3 +1,7 @@
pub mod conf;
pub mod config_window;
pub mod export_pcap;
pub mod filters;
pub mod message;
pub mod settings;
pub mod timing_events;

34
src/gui/types/settings.rs Normal file
View File

@@ -0,0 +1,34 @@
use serde::{Deserialize, Serialize};
use crate::gui::styles::types::gradient_type::GradientType;
use crate::notifications::types::notifications::Notifications;
use crate::{Language, StyleType};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct Settings {
pub color_gradient: GradientType,
pub language: Language,
pub scale_factor: f64,
pub mmdb_country: String,
pub mmdb_asn: String,
pub style_path: String,
pub notifications: Notifications,
// StyleType should be last in order to deserialize as a table properly
pub style: StyleType,
}
impl Default for Settings {
fn default() -> Self {
Settings {
color_gradient: GradientType::default(),
language: Language::default(),
scale_factor: 1.0,
mmdb_country: String::new(),
mmdb_asn: String::new(),
style_path: String::new(),
notifications: Notifications::default(),
style: StyleType::default(),
}
}
}

View File

@@ -7,34 +7,29 @@
use iced::advanced::graphics::image::image_rs::ImageFormat;
#[cfg(target_os = "linux")]
use iced::window::settings::PlatformSpecific;
use iced::{Font, Pixels, Settings, application, window};
use iced::{Font, Pixels, application, window};
use chart::types::chart_type::ChartType;
use chart::types::traffic_chart::TrafficChart;
use cli::handle_cli_args;
use configs::types::config_device::ConfigDevice;
use configs::types::config_settings::ConfigSettings;
use gui::pages::types::running_page::RunningPage;
use gui::sniffer::Sniffer;
use gui::styles::style_constants::FONT_SIZE_BODY;
use gui::styles::types::style_type::StyleType;
use networking::types::byte_multiple::ByteMultiple;
use networking::types::data_representation::ByteMultiple;
use networking::types::info_traffic::InfoTraffic;
use networking::types::ip_version::IpVersion;
use networking::types::protocol::Protocol;
use networking::types::service::Service;
use report::types::report_sort_type::ReportSortType;
use translations::types::language::Language;
use utils::formatted_strings::print_cli_welcome_message;
use crate::configs::types::config_window::{ConfigWindow, ToPosition, ToSize};
use crate::configs::types::configs::{CONFIGS, Configs};
use crate::gui::sniffer::FONT_FAMILY_NAME;
use crate::gui::styles::style_constants::{ICONS_BYTES, SARASA_MONO_BOLD_BYTES, SARASA_MONO_BYTES};
use crate::gui::types::conf::CONF;
use crate::gui::types::config_window::{ConfigWindow, ToPosition, ToSize};
mod chart;
mod cli;
mod configs;
mod countries;
mod gui;
mod mmdb;
@@ -63,7 +58,7 @@ pub fn main() -> iced::Result {
_gag2 = gag2;
}
let configs = CONFIGS.clone();
let conf = CONF.clone();
let boot_task_chain = handle_cli_args();
#[cfg(debug_assertions)]
@@ -79,10 +74,10 @@ pub fn main() -> iced::Result {
print_cli_welcome_message();
let ConfigWindow { size, position, .. } = configs.window;
let ConfigWindow { size, position, .. } = conf.window;
application(SNIFFNET_TITLECASE, Sniffer::update, Sniffer::view)
.settings(Settings {
.settings(iced::Settings {
// id needed for Linux Wayland; should match StartupWMClass in .desktop file; see issue #292
id: Some(String::from(SNIFFNET_LOWERCASE)),
fonts: vec![
@@ -115,5 +110,5 @@ pub fn main() -> iced::Result {
.subscription(Sniffer::subscription)
.theme(Sniffer::theme)
.scale_factor(Sniffer::scale_factor)
.run_with(move || (Sniffer::new(configs), boot_task_chain))
.run_with(move || (Sniffer::new(conf), boot_task_chain))
}

View File

@@ -77,19 +77,19 @@ fn test_get_asn_with_custom_ipinfo_single_reader() {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
let res = get_asn(&IpAddr::from([61, 8, 0, 0]), &reader);
assert_eq!(res.code, "AS1221");
assert_eq!(res.name, "Telstra Limited");
let res = get_asn(&IpAddr::from([185, 72, 2, 28]), &reader);
assert_eq!(res.code, "AS202583");
assert_eq!(res.name, "AVATEL TELECOM, SA");
// another known IP
let res = get_asn(&IpAddr::from([206, 180, 34, 99]), &reader);
assert_eq!(res.code, "AS63344");
assert_eq!(res.name, "The Reynolds and Reynolds Company");
let res = get_asn(&IpAddr::from([89, 187, 198, 0]), &reader);
assert_eq!(res.code, "AS210367");
assert_eq!(res.name, "Krajska zdravotni, a.s.");
// known IPv6
let res = get_asn(&IpAddr::from_str("2806:230:2057::").unwrap(), &reader);
assert_eq!(res.code, "AS11888");
assert_eq!(res.name, "Television Internacional, S.A. de C.V.");
let res = get_asn(&IpAddr::from_str("2408:8957:6280::").unwrap(), &reader);
assert_eq!(res.code, "AS17622");
assert_eq!(res.name, "China Unicom Guangzhou network");
// unknown IP
let res = get_asn(&IpAddr::from([127, 0, 0, 1]), &reader);
@@ -106,31 +106,29 @@ fn test_get_asn_with_custom_ipinfo_single_reader() {
#[test]
fn test_get_asn_with_custom_ipinfo_combined_reader() {
let reader_1 = MmdbReader::from(
&String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
&String::from("resources/test/ipinfo_lite_sample.mmdb"),
ASN_MMDB,
);
let reader_2 = MmdbReader::from(
&String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
&[],
);
let reader_2 =
MmdbReader::from(&String::from("resources/test/ipinfo_lite_sample.mmdb"), &[]);
for reader in vec![reader_1, reader_2] {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
let res = get_asn(&IpAddr::from([31, 171, 144, 141]), &reader);
assert_eq!(res.code, "AS197742");
assert_eq!(res.name, "IBB Energie AG");
let res = get_asn(&IpAddr::from([1, 0, 65, 1]), &reader);
assert_eq!(res.code, "AS18144");
assert_eq!(res.name, "Enecom,Inc.");
// another known IP
let res = get_asn(&IpAddr::from([103, 112, 220, 111]), &reader);
assert_eq!(res.code, "AS134077");
assert_eq!(res.name, "Magik Pivot Company Limited");
let res = get_asn(&IpAddr::from([1, 6, 230, 0]), &reader);
assert_eq!(res.code, "AS4755");
assert_eq!(res.name, "TATA Communications formerly VSNL is Leading ISP");
// known IPv6
let res = get_asn(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
assert_eq!(res.code, "AS60068");
assert_eq!(res.name, "Datacamp Limited");
// let res = get_asn(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
// assert_eq!(res.code, "AS60068");
// assert_eq!(res.name, "Datacamp Limited");
// unknown IP
let res = get_asn(&IpAddr::from([127, 0, 0, 1]), &reader);

View File

@@ -63,11 +63,11 @@ fn test_get_country_with_default_reader() {
#[test]
fn test_get_country_with_custom_ipinfo_single_reader() {
let reader_1 = MmdbReader::from(
&String::from("resources/test/ipinfo_country_sample.mmdb"),
&String::from("resources/test/ipinfo_location_sample.mmdb"),
COUNTRY_MMDB,
);
let reader_2 = MmdbReader::from(
&String::from("resources/test/ipinfo_country_sample.mmdb"),
&String::from("resources/test/ipinfo_location_sample.mmdb"),
&[],
);
@@ -75,16 +75,16 @@ fn test_get_country_with_custom_ipinfo_single_reader() {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
let res = get_country(&IpAddr::from([2, 2, 146, 0]), &reader);
assert_eq!(res, Country::GB);
let res = get_country(&IpAddr::from([1, 0, 6, 99]), &reader);
assert_eq!(res, Country::AU);
// another known IP
let res = get_country(&IpAddr::from([23, 193, 112, 81]), &reader);
assert_eq!(res, Country::US);
let res = get_country(&IpAddr::from([1, 0, 8, 0]), &reader);
assert_eq!(res, Country::CN);
// known IPv6
let res = get_country(&IpAddr::from_str("2a0e:1d80::").unwrap(), &reader);
assert_eq!(res, Country::RO);
// let res = get_country(&IpAddr::from_str("2a0e:1d80::").unwrap(), &reader);
// assert_eq!(res, Country::RO);
// unknown IP
let res = get_country(&IpAddr::from([127, 0, 0, 1]), &reader);
@@ -99,28 +99,26 @@ fn test_get_country_with_custom_ipinfo_single_reader() {
#[test]
fn test_get_country_with_custom_ipinfo_combined_reader() {
let reader_1 = MmdbReader::from(
&String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
&String::from("resources/test/ipinfo_lite_sample.mmdb"),
COUNTRY_MMDB,
);
let reader_2 = MmdbReader::from(
&String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
&[],
);
let reader_2 =
MmdbReader::from(&String::from("resources/test/ipinfo_lite_sample.mmdb"), &[]);
for reader in vec![reader_1, reader_2] {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
let res = get_country(&IpAddr::from([31, 171, 144, 141]), &reader);
assert_eq!(res, Country::IT);
let res = get_country(&IpAddr::from([1, 0, 65, 1]), &reader);
assert_eq!(res, Country::JP);
// another known IP
let res = get_country(&IpAddr::from([103, 112, 220, 111]), &reader);
assert_eq!(res, Country::TH);
let res = get_country(&IpAddr::from([1, 6, 230, 0]), &reader);
assert_eq!(res, Country::IN);
// known IPv6
let res = get_country(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
assert_eq!(res, Country::AR);
// let res = get_country(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
// assert_eq!(res, Country::AR);
// unknown IP
let res = get_country(&IpAddr::from([127, 0, 0, 1]), &reader);

View File

@@ -30,7 +30,9 @@ fn get_country(&self) -> Country {
Self::Standard(StandardCountryEntry {
country: Some(StandardCountryEntryInner { iso_code: Some(c) }),
})
| Self::Ipinfo(IpinfoCountryEntry { country: Some(c) }) => Country::from_str(c),
| Self::Ipinfo(IpinfoCountryEntry {
country_code: Some(c),
}) => Country::from_str(c),
_ => Country::ZZ,
}
}
@@ -49,5 +51,5 @@ struct StandardCountryEntryInner<'a> {
#[derive(Deserialize)]
struct IpinfoCountryEntry<'a> {
country: Option<&'a str>,
country_code: Option<&'a str>,
}

View File

@@ -1,7 +1,9 @@
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use etherparse::{EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader};
use etherparse::{
ArpHardwareId, EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader,
};
use pcap::Address;
use crate::networking::types::address_port_pair::AddressPortPair;
@@ -81,13 +83,28 @@ fn analyze_link_header(
mac_address2: &mut Option<String>,
exchanged_bytes: &mut u128,
) {
if let Some(LinkHeader::Ethernet2(header)) = link_header {
*exchanged_bytes += 14;
*mac_address1 = Some(mac_from_dec_to_hex(header.source));
*mac_address2 = Some(mac_from_dec_to_hex(header.destination));
} else {
*mac_address1 = None;
*mac_address2 = None;
match link_header {
Some(LinkHeader::Ethernet2(header)) => {
*exchanged_bytes += 14;
*mac_address1 = Some(mac_from_dec_to_hex(header.source));
*mac_address2 = Some(mac_from_dec_to_hex(header.destination));
}
Some(LinkHeader::LinuxSll(header)) => {
*exchanged_bytes += 16;
*mac_address1 = if header.sender_address_valid_length == 6
&& header.arp_hrd_type == ArpHardwareId::ETHERNET
&& let Ok(sender) = header.sender_address[0..6].try_into()
{
Some(mac_from_dec_to_hex(sender))
} else {
None
};
*mac_address2 = None;
}
None => {
*mac_address1 = None;
*mac_address2 = None;
}
}
}
@@ -151,7 +168,7 @@ fn analyze_network_header(
*arp_type = ArpType::from_etherparse(arp_packet.operation);
true
}
_ => false,
None => false,
}
}
@@ -192,7 +209,7 @@ fn analyze_transport_header(
*icmp_type = IcmpTypeV6::from_etherparse(&icmpv6_header.icmp_type);
true
}
_ => false,
None => false,
}
}
@@ -337,14 +354,15 @@ fn get_traffic_direction(
.collect();
// first let's handle TCP and UDP loopback
if source_ip.is_loopback() && destination_ip.is_loopback() {
if let (Some(sport), Some(dport)) = (source_port, dest_port) {
return if sport > dport {
TrafficDirection::Outgoing
} else {
TrafficDirection::Incoming
};
}
if source_ip.is_loopback()
&& destination_ip.is_loopback()
&& let (Some(sport), Some(dport)) = (source_port, dest_port)
{
return if sport > dport {
TrafficDirection::Outgoing
} else {
TrafficDirection::Incoming
};
}
// if interface_addresses is empty, check if the IP is a bogon (useful when importing pcap files)
@@ -1499,7 +1517,7 @@ fn test_get_service_different_tcp_udp() {
#[test]
fn test_get_service_not_applicable() {
for p in Protocol::ALL {
for p in [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP] {
for d in [TrafficDirection::Incoming, TrafficDirection::Outgoing] {
for (p1, p2) in [(None, Some(443)), (None, None), (Some(443), None)] {
let key = AddressPortPair::new(

View File

@@ -14,7 +14,6 @@
use crate::networking::types::capture_context::{CaptureContext, CaptureSource};
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::filters::Filters;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::icmp_type::IcmpType;
use crate::networking::types::info_traffic::InfoTraffic;
@@ -26,10 +25,8 @@
use crate::utils::types::timestamp::Timestamp;
use async_channel::Sender;
use dns_lookup::lookup_addr;
use etherparse::err::ip::{HeaderError, LaxHeaderSliceError};
use etherparse::err::{Layer, LenError};
use etherparse::{LaxPacketHeaders, LenSource};
use pcap::{Address, Device, Packet};
use etherparse::{EtherType, LaxPacketHeaders};
use pcap::{Address, Packet};
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::{Arc, Mutex};
@@ -40,7 +37,6 @@
pub fn parse_packets(
cap_id: usize,
mut cs: CaptureSource,
filters: &Filters,
mmdb_readers: &MmdbReaders,
capture_context: CaptureContext,
tx: &Sender<BackendTrafficMessage>,
@@ -97,7 +93,7 @@ pub fn parse_packets(
}
}
Ok(packet) => {
if let Ok(headers) = get_sniffable_headers(&packet, my_link_type) {
if let Some(headers) = get_sniffable_headers(&packet, my_link_type) {
#[allow(clippy::useless_conversion)]
let secs = i64::from(packet.header.ts.tv_sec);
#[allow(clippy::useless_conversion)]
@@ -137,148 +133,139 @@ pub fn parse_packets(
continue;
};
let passed_filters = filters.matches(&packet_filters_fields);
if passed_filters {
// save this packet to PCAP file
if let Some(file) = savefile.as_mut() {
file.write(&packet);
}
// update the map
let (traffic_direction, service) = modify_or_insert_in_map(
&mut info_traffic_msg,
&key,
&cs,
mac_addresses,
icmp_type,
arp_type,
exchanged_bytes,
);
// save this packet to PCAP file
if let Some(file) = savefile.as_mut() {
file.write(&packet);
}
// update the map
let (traffic_direction, service) = modify_or_insert_in_map(
&mut info_traffic_msg,
&key,
&cs,
mac_addresses,
icmp_type,
arp_type,
exchanged_bytes,
);
info_traffic_msg
.tot_data_info
.add_packet(exchanged_bytes, traffic_direction);
info_traffic_msg
.tot_data_info
.add_packet(exchanged_bytes, traffic_direction);
// check the rDNS status of this address and act accordingly
let address_to_lookup = get_address_to_lookup(&key, traffic_direction);
let mut r_dns_waiting_resolution = false;
let mut resolutions_lock = resolutions_state.lock().unwrap();
let r_dns_already_resolved = resolutions_lock
.addresses_resolved
// check the rDNS status of this address and act accordingly
let address_to_lookup = get_address_to_lookup(&key, traffic_direction);
let mut r_dns_waiting_resolution = false;
let mut resolutions_lock = resolutions_state.lock().unwrap();
let r_dns_already_resolved = resolutions_lock
.addresses_resolved
.contains_key(&address_to_lookup);
if !r_dns_already_resolved {
r_dns_waiting_resolution = resolutions_lock
.addresses_waiting_resolution
.contains_key(&address_to_lookup);
if !r_dns_already_resolved {
r_dns_waiting_resolution = resolutions_lock
.addresses_waiting_resolution
.contains_key(&address_to_lookup);
}
match (r_dns_waiting_resolution, r_dns_already_resolved) {
(false, false) => {
// rDNS not requested yet (first occurrence of this address to lookup)
// Add this address to the map of addresses waiting for a resolution
// Useful to NOT perform again a rDNS lookup for this entry
resolutions_lock.addresses_waiting_resolution.insert(
address_to_lookup,
DataInfo::new_with_first_packet(
exchanged_bytes,
traffic_direction,
),
);
drop(resolutions_lock);
// launch new thread to resolve host name
let key2 = key;
let resolutions_state2 = resolutions_state.clone();
let new_hosts_to_send2 = new_hosts_to_send.clone();
let interface_addresses = cs.get_addresses().clone();
let mmdb_readers_2 = mmdb_readers.clone();
let tx2 = tx.clone();
let _ = thread::Builder::new()
.name("thread_reverse_dns_lookup".to_string())
.spawn(move || {
reverse_dns_lookup(
&resolutions_state2,
&new_hosts_to_send2,
&key2,
traffic_direction,
&interface_addresses,
&mmdb_readers_2,
&tx2,
);
})
.log_err(location!());
}
(true, false) => {
// waiting for a previously requested rDNS resolution
// update the corresponding waiting address data
resolutions_lock
.addresses_waiting_resolution
.entry(address_to_lookup)
.and_modify(|data_info| {
data_info.add_packet(exchanged_bytes, traffic_direction);
});
drop(resolutions_lock);
}
(_, true) => {
// rDNS already resolved
// update the corresponding host's data info
let host = resolutions_lock
.addresses_resolved
.get(&address_to_lookup)
.unwrap_or(&Host::default())
.clone();
drop(resolutions_lock);
info_traffic_msg
.hosts
.entry(host)
.and_modify(|data_info_host| {
data_info_host
.data_info
.add_packet(exchanged_bytes, traffic_direction);
})
.or_insert_with(|| {
let my_interface_addresses = cs.get_addresses();
let traffic_type = get_traffic_type(
&address_to_lookup,
my_interface_addresses,
traffic_direction,
);
let is_loopback = address_to_lookup.is_loopback();
let is_local = is_local_connection(
&address_to_lookup,
my_interface_addresses,
);
let is_bogon = is_bogon(&address_to_lookup);
DataInfoHost {
data_info: DataInfo::new_with_first_packet(
exchanged_bytes,
traffic_direction,
),
is_favorite: false,
is_loopback,
is_local,
is_bogon,
traffic_type,
}
});
}
}
//increment the packet count for the sniffed service
info_traffic_msg
.services
.entry(service)
.and_modify(|data_info| {
data_info.add_packet(exchanged_bytes, traffic_direction);
})
.or_insert_with(|| {
DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction)
});
}
//increment number of sniffed packets and bytes
info_traffic_msg.all_packets += 1;
info_traffic_msg.all_bytes += exchanged_bytes;
match (r_dns_waiting_resolution, r_dns_already_resolved) {
(false, false) => {
// rDNS not requested yet (first occurrence of this address to lookup)
// Add this address to the map of addresses waiting for a resolution
// Useful to NOT perform again a rDNS lookup for this entry
resolutions_lock.addresses_waiting_resolution.insert(
address_to_lookup,
DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction),
);
drop(resolutions_lock);
// launch new thread to resolve host name
let key2 = key;
let resolutions_state2 = resolutions_state.clone();
let new_hosts_to_send2 = new_hosts_to_send.clone();
let interface_addresses = cs.get_addresses().clone();
let mmdb_readers_2 = mmdb_readers.clone();
let tx2 = tx.clone();
let _ = thread::Builder::new()
.name("thread_reverse_dns_lookup".to_string())
.spawn(move || {
reverse_dns_lookup(
&resolutions_state2,
&new_hosts_to_send2,
&key2,
traffic_direction,
&interface_addresses,
&mmdb_readers_2,
&tx2,
);
})
.log_err(location!());
}
(true, false) => {
// waiting for a previously requested rDNS resolution
// update the corresponding waiting address data
resolutions_lock
.addresses_waiting_resolution
.entry(address_to_lookup)
.and_modify(|data_info| {
data_info.add_packet(exchanged_bytes, traffic_direction);
});
drop(resolutions_lock);
}
(_, true) => {
// rDNS already resolved
// update the corresponding host's data info
let host = resolutions_lock
.addresses_resolved
.get(&address_to_lookup)
.unwrap_or(&Host::default())
.clone();
drop(resolutions_lock);
info_traffic_msg
.hosts
.entry(host)
.and_modify(|data_info_host| {
data_info_host
.data_info
.add_packet(exchanged_bytes, traffic_direction);
})
.or_insert_with(|| {
let my_interface_addresses = cs.get_addresses();
let traffic_type = get_traffic_type(
&address_to_lookup,
my_interface_addresses,
traffic_direction,
);
let is_loopback = address_to_lookup.is_loopback();
let is_local = is_local_connection(
&address_to_lookup,
my_interface_addresses,
);
let is_bogon = is_bogon(&address_to_lookup);
DataInfoHost {
data_info: DataInfo::new_with_first_packet(
exchanged_bytes,
traffic_direction,
),
is_favorite: false,
is_loopback,
is_local,
is_bogon,
traffic_type,
}
});
}
}
//increment the packet count for the sniffed service
info_traffic_msg
.services
.entry(service)
.and_modify(|data_info| {
data_info.add_packet(exchanged_bytes, traffic_direction);
})
.or_insert_with(|| {
DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction)
});
// update dropped packets number
if let Ok(stats) = cap.stats() {
info_traffic_msg.dropped_packets = stats.dropped;
@@ -292,27 +279,23 @@ pub fn parse_packets(
fn get_sniffable_headers<'a>(
packet: &'a Packet,
my_link_type: MyLinkType,
) -> Result<LaxPacketHeaders<'a>, LaxHeaderSliceError> {
) -> Option<LaxPacketHeaders<'a>> {
match my_link_type {
MyLinkType::Ethernet(_) | MyLinkType::Unsupported(_) | MyLinkType::NotYetAssigned => {
LaxPacketHeaders::from_ethernet(packet).map_err(LaxHeaderSliceError::Len)
LaxPacketHeaders::from_ethernet(packet).ok()
}
MyLinkType::RawIp(_) | MyLinkType::IPv4(_) | MyLinkType::IPv6(_) => {
LaxPacketHeaders::from_ip(packet)
LaxPacketHeaders::from_ip(packet).ok()
}
MyLinkType::LinuxSll(_) => from_linux_sll(packet, true),
MyLinkType::LinuxSll2(_) => from_linux_sll(packet, false),
MyLinkType::Null(_) | MyLinkType::Loop(_) => from_null(packet),
}
}
fn from_null(packet: &[u8]) -> Result<LaxPacketHeaders, LaxHeaderSliceError> {
fn from_null(packet: &[u8]) -> Option<LaxPacketHeaders<'_>> {
if packet.len() <= 4 {
return Err(LaxHeaderSliceError::Len(LenError {
required_len: 4,
len: packet.len(),
len_source: LenSource::Slice,
layer: Layer::Ethernet2Header,
layer_start_offset: 0,
}));
return None;
}
let is_valid_af_inet = {
@@ -333,14 +316,31 @@ fn matches(value: u32) -> bool {
};
if is_valid_af_inet {
LaxPacketHeaders::from_ip(&packet[4..])
LaxPacketHeaders::from_ip(&packet[4..]).ok()
} else {
Err(LaxHeaderSliceError::Content(
HeaderError::UnsupportedIpVersion { version_number: 0 },
))
None
}
}
fn from_linux_sll(packet: &[u8], is_v1: bool) -> Option<LaxPacketHeaders<'_>> {
let header_len = if is_v1 { 16 } else { 20 };
if packet.len() <= header_len {
return None;
}
let protocol_type = u16::from_be_bytes(if is_v1 {
[packet[14], packet[15]]
} else {
[packet[0], packet[1]]
});
let payload = &packet[header_len..];
Some(LaxPacketHeaders::from_ether_type(
EtherType(protocol_type),
payload,
))
}
fn reverse_dns_lookup(
resolutions_state: &Arc<Mutex<AddressesResolutionState>>,
new_hosts_to_send: &Arc<Mutex<Vec<HostMessage>>>,
@@ -442,12 +442,7 @@ fn maybe_send_tick_run_live(
new_hosts_to_send.lock().unwrap().drain(..).collect(),
false,
));
for dev in Device::list().log_err(location!()).unwrap_or_default() {
if dev.name.eq(&cs.get_name()) {
cs.set_addresses(dev.addresses);
break;
}
}
cs.set_addresses();
}
}

View File

@@ -1,20 +1,20 @@
use crate::networking::types::ip_collection::AddressCollection;
use crate::networking::types::ip_collection::IpCollection;
use std::net::IpAddr;
pub struct Bogon {
pub range: AddressCollection,
pub range: IpCollection,
pub description: &'static str,
}
// IPv4 bogons
static THIS_NETWORK: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("0.0.0.0-0.255.255.255").unwrap(),
range: IpCollection::new("0.0.0.0-0.255.255.255").unwrap(),
description: "\"this\" network",
});
static PRIVATE_USE: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new(
range: IpCollection::new(
"10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255",
)
.unwrap(),
@@ -22,112 +22,112 @@ pub struct Bogon {
});
static CARRIER_GRADE: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("100.64.0.0-100.127.255.255").unwrap(),
range: IpCollection::new("100.64.0.0-100.127.255.255").unwrap(),
description: "carrier-grade NAT",
});
static LOOPBACK: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("127.0.0.0-127.255.255.255").unwrap(),
range: IpCollection::new("127.0.0.0-127.255.255.255").unwrap(),
description: "loopback",
});
static LINK_LOCAL: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("169.254.0.0-169.254.255.255").unwrap(),
range: IpCollection::new("169.254.0.0-169.254.255.255").unwrap(),
description: "link local",
});
static IETF_PROTOCOL: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("192.0.0.0-192.0.0.255").unwrap(),
range: IpCollection::new("192.0.0.0-192.0.0.255").unwrap(),
description: "IETF protocol assignments",
});
static TEST_NET_1: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("192.0.2.0-192.0.2.255").unwrap(),
range: IpCollection::new("192.0.2.0-192.0.2.255").unwrap(),
description: "TEST-NET-1",
});
static NETWORK_INTERCONNECT: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("198.18.0.0-198.19.255.255").unwrap(),
range: IpCollection::new("198.18.0.0-198.19.255.255").unwrap(),
description: "network interconnect device benchmark testing",
});
static TEST_NET_2: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("198.51.100.0-198.51.100.255").unwrap(),
range: IpCollection::new("198.51.100.0-198.51.100.255").unwrap(),
description: "TEST-NET-2",
});
static TEST_NET_3: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("203.0.113.0-203.0.113.255").unwrap(),
range: IpCollection::new("203.0.113.0-203.0.113.255").unwrap(),
description: "TEST-NET-3",
});
static MULTICAST: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("224.0.0.0-239.255.255.255").unwrap(),
range: IpCollection::new("224.0.0.0-239.255.255.255").unwrap(),
description: "multicast",
});
static FUTURE_USE: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("240.0.0.0-255.255.255.255").unwrap(),
range: IpCollection::new("240.0.0.0-255.255.255.255").unwrap(),
description: "future use",
});
// IPv6 bogons
static NODE_SCOPE_UNSPECIFIED: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("::").unwrap(),
range: IpCollection::new("::").unwrap(),
description: "node-scope unicast unspecified",
});
static NODE_SCOPE_LOOPBACK: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("::1").unwrap(),
range: IpCollection::new("::1").unwrap(),
description: "node-scope unicast loopback",
});
static IPV4_MAPPED: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(),
range: IpCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(),
description: "IPv4-mapped",
});
static IPV4_COMPATIBLE: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("::-::255.255.255.255").unwrap(),
range: IpCollection::new("::-::255.255.255.255").unwrap(),
description: "IPv4-compatible",
});
static REMOTELY_TRIGGERED: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(),
range: IpCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(),
description: "remotely triggered black hole",
});
static ORCHID: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
range: IpCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "ORCHID",
});
static DOCUMENTATION_PREFIX: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| {
Bogon {
range: AddressCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff")
range: IpCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff")
.unwrap(),
description: "documentation prefix",
}
});
static ULA: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
range: IpCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "ULA",
});
static LINK_LOCAL_UNICAST: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
range: IpCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "link-local unicast",
});
static SITE_LOCAL_UNICAST: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
range: IpCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "site-local unicast",
});
static MULTICAST_V6: std::sync::LazyLock<Bogon> = std::sync::LazyLock::new(|| Bogon {
range: AddressCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
range: IpCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "multicast v6",
});

View File

@@ -1,225 +0,0 @@
use std::fmt;
use serde::{Deserialize, Serialize};
/// Enum representing the possible observed values of IP protocol version.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ByteMultiple {
/// A Byte
B,
/// 10^3 Bytes
KB,
/// 10^6 Bytes
MB,
/// 10^9 Bytes
GB,
/// 10^12 Bytes
TB,
/// 10^15 Bytes
PB,
}
impl fmt::Display for ByteMultiple {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl ByteMultiple {
pub fn multiplier(self) -> u64 {
match self {
ByteMultiple::B => 1,
ByteMultiple::KB => 1_000,
ByteMultiple::MB => 1_000_000,
ByteMultiple::GB => 1_000_000_000,
ByteMultiple::TB => 1_000_000_000_000,
ByteMultiple::PB => 1_000_000_000_000_000,
}
}
fn from_num_bytes(bytes: u128) -> Self {
match bytes {
x if (u128::MIN..u128::from(ByteMultiple::KB.multiplier())).contains(&x) => {
ByteMultiple::B
}
x if (u128::from(ByteMultiple::KB.multiplier())
..u128::from(ByteMultiple::MB.multiplier()))
.contains(&x) =>
{
ByteMultiple::KB
}
x if (u128::from(ByteMultiple::MB.multiplier())
..u128::from(ByteMultiple::GB.multiplier()))
.contains(&x) =>
{
ByteMultiple::MB
}
x if (u128::from(ByteMultiple::GB.multiplier())
..u128::from(ByteMultiple::TB.multiplier()))
.contains(&x) =>
{
ByteMultiple::GB
}
x if (u128::from(ByteMultiple::TB.multiplier())
..u128::from(ByteMultiple::PB.multiplier()))
.contains(&x) =>
{
ByteMultiple::TB
}
_ => ByteMultiple::PB,
}
}
pub fn get_char(self) -> String {
self.to_string()
.strip_suffix('B')
.unwrap_or_default()
.to_owned()
}
pub fn from_char(ch: char) -> Self {
match ch.to_ascii_uppercase() {
'K' => ByteMultiple::KB,
'M' => ByteMultiple::MB,
'G' => ByteMultiple::GB,
'T' => ByteMultiple::TB,
'P' => ByteMultiple::PB,
_ => ByteMultiple::B,
}
}
/// Returns a String representing a quantity of bytes with its proper multiple (B, KB, MB, GB, TB)
pub fn formatted_string(bytes: u128) -> String {
#[allow(clippy::cast_precision_loss)]
let mut n = bytes as f32;
let byte_multiple = ByteMultiple::from_num_bytes(bytes);
#[allow(clippy::cast_precision_loss)]
let multiplier = byte_multiple.multiplier() as f32;
n /= multiplier;
if n > 999.0 && byte_multiple != ByteMultiple::PB {
// this allows representing e.g. 999_999 as 999 KB instead of 1000 KB
n = 999.0;
}
let precision = usize::from(byte_multiple != ByteMultiple::B && n <= 9.95);
format!("{n:.precision$} {byte_multiple}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interpret_suffix_correctly() {
assert_eq!(ByteMultiple::from_char('B'), ByteMultiple::B);
assert_eq!(ByteMultiple::from_char('k'), ByteMultiple::KB);
assert_eq!(ByteMultiple::from_char('M'), ByteMultiple::MB);
assert_eq!(ByteMultiple::from_char('g'), ByteMultiple::GB);
assert_eq!(ByteMultiple::from_char('t'), ByteMultiple::TB);
assert_eq!(ByteMultiple::from_char('P'), ByteMultiple::PB);
}
#[test]
fn test_interpret_unknown_suffix_correctly() {
assert_eq!(ByteMultiple::from_char('E'), ByteMultiple::B);
assert_eq!(ByteMultiple::from_char('y'), ByteMultiple::B);
}
#[test]
fn test_byte_multiple_display() {
assert_eq!(format!("{}", ByteMultiple::B), "B");
assert_eq!(format!("{}", ByteMultiple::KB), "KB");
assert_eq!(format!("{}", ByteMultiple::MB), "MB");
assert_eq!(format!("{}", ByteMultiple::GB), "GB");
assert_eq!(format!("{}", ByteMultiple::TB), "TB");
assert_eq!(format!("{}", ByteMultiple::PB), "PB");
}
#[test]
fn test_byte_multiple_get_char() {
assert_eq!(ByteMultiple::B.get_char(), "");
assert_eq!(ByteMultiple::KB.get_char(), "K");
assert_eq!(ByteMultiple::MB.get_char(), "M");
assert_eq!(ByteMultiple::GB.get_char(), "G");
assert_eq!(ByteMultiple::TB.get_char(), "T");
assert_eq!(ByteMultiple::PB.get_char(), "P");
}
#[test]
fn test_byte_multiple_multiplier() {
assert_eq!(ByteMultiple::B.multiplier(), 1);
assert_eq!(ByteMultiple::KB.multiplier(), 1_000);
assert_eq!(ByteMultiple::MB.multiplier(), 1_000_000);
assert_eq!(ByteMultiple::GB.multiplier(), 1_000_000_000);
assert_eq!(ByteMultiple::TB.multiplier(), 1_000_000_000_000);
assert_eq!(ByteMultiple::PB.multiplier(), 1_000_000_000_000_000);
}
#[test]
fn test_byte_multiple_formatted_string() {
assert_eq!(ByteMultiple::formatted_string(u128::MIN), "0 B");
assert_eq!(ByteMultiple::formatted_string(1), "1 B");
assert_eq!(ByteMultiple::formatted_string(82), "82 B");
assert_eq!(ByteMultiple::formatted_string(999), "999 B");
assert_eq!(ByteMultiple::formatted_string(1_000), "1.0 KB");
assert_eq!(ByteMultiple::formatted_string(1_090), "1.1 KB");
assert_eq!(ByteMultiple::formatted_string(1_990), "2.0 KB");
assert_eq!(ByteMultiple::formatted_string(9_090), "9.1 KB");
assert_eq!(ByteMultiple::formatted_string(9_950), "9.9 KB");
assert_eq!(ByteMultiple::formatted_string(9_951), "10 KB");
assert_eq!(ByteMultiple::formatted_string(71_324), "71 KB");
assert_eq!(ByteMultiple::formatted_string(821_789), "822 KB");
assert_eq!(ByteMultiple::formatted_string(999_499), "999 KB");
assert_eq!(ByteMultiple::formatted_string(999_999), "999 KB");
assert_eq!(ByteMultiple::formatted_string(1_000_000), "1.0 MB");
assert_eq!(ByteMultiple::formatted_string(3_790_000), "3.8 MB");
assert_eq!(ByteMultiple::formatted_string(9_950_000), "9.9 MB");
assert_eq!(ByteMultiple::formatted_string(9_951_000), "10 MB");
assert_eq!(ByteMultiple::formatted_string(49_499_000), "49 MB");
assert_eq!(ByteMultiple::formatted_string(49_500_000), "50 MB");
assert_eq!(ByteMultiple::formatted_string(670_900_000), "671 MB");
assert_eq!(ByteMultiple::formatted_string(998_199_999), "998 MB");
assert_eq!(ByteMultiple::formatted_string(999_999_999), "999 MB");
assert_eq!(ByteMultiple::formatted_string(1_000_000_000), "1.0 GB");
assert_eq!(ByteMultiple::formatted_string(7_770_000_000), "7.8 GB");
assert_eq!(ByteMultiple::formatted_string(9_950_000_000), "9.9 GB");
assert_eq!(ByteMultiple::formatted_string(9_951_000_000), "10 GB");
assert_eq!(ByteMultiple::formatted_string(19_951_000_000), "20 GB");
assert_eq!(ByteMultiple::formatted_string(399_951_000_000), "400 GB");
assert_eq!(ByteMultiple::formatted_string(999_999_999_999), "999 GB");
assert_eq!(ByteMultiple::formatted_string(1_000_000_000_000), "1.0 TB");
assert_eq!(ByteMultiple::formatted_string(9_950_000_000_000), "9.9 TB");
assert_eq!(ByteMultiple::formatted_string(9_951_000_000_000), "10 TB");
assert_eq!(
ByteMultiple::formatted_string(999_950_000_000_000),
"999 TB"
);
assert_eq!(
ByteMultiple::formatted_string(999_999_999_999_999),
"999 TB"
);
assert_eq!(
ByteMultiple::formatted_string(1_000_000_000_000_000),
"1.0 PB"
);
assert_eq!(
ByteMultiple::formatted_string(1_000_000_000_000_000_0),
"10 PB"
);
assert_eq!(
ByteMultiple::formatted_string(999_999_999_000_000_000),
"1000 PB"
);
assert_eq!(
ByteMultiple::formatted_string(1_000_000_000_000_000_000_000),
"1000000 PB"
);
assert_eq!(
ByteMultiple::formatted_string(u128::MAX / 2),
"170141184077655307190272 PB"
);
assert_eq!(ByteMultiple::formatted_string(u128::MAX), "inf PB");
}
}

View File

@@ -1,10 +1,14 @@
use pcap::{Active, Address, Capture, Error, Packet, Savefile, Stat};
use crate::gui::types::conf::Conf;
use crate::gui::types::filters::Filters;
use crate::location;
use crate::networking::types::my_device::MyDevice;
use crate::networking::types::my_link_type::MyLinkType;
use crate::translations::translations::network_adapter_translation;
use crate::translations::translations_3::file_name_translation;
use crate::translations::translations_4::capture_file_translation;
use crate::translations::types::language::Language;
use crate::utils::error_logger::{ErrorLogger, Location};
use pcap::{Active, Address, Capture, Device, Error, Packet, Savefile, Stat};
use serde::{Deserialize, Serialize};
pub enum CaptureContext {
Live(Live),
@@ -14,11 +18,19 @@ pub enum CaptureContext {
}
impl CaptureContext {
pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>) -> Self {
let cap_type = match CaptureType::from_source(source, pcap_out_path) {
pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>, filters: &Filters) -> Self {
let mut cap_type = match CaptureType::from_source(source, pcap_out_path) {
Ok(c) => c,
Err(e) => return Self::Error(e.to_string()),
};
// only apply BPF filter if it is active, and return an error if it fails to apply
if filters.is_some_filter_active()
&& let Err(e) = cap_type.set_bpf(filters.bpf())
{
return Self::Error(e.to_string());
}
let cap = match cap_type {
CaptureType::Live(cap) => cap,
CaptureType::Offline(cap) => return Self::new_offline(cap),
@@ -97,7 +109,7 @@ pub enum CaptureType {
}
impl CaptureType {
pub fn next_packet(&mut self) -> Result<Packet, Error> {
pub fn next_packet(&mut self) -> Result<Packet<'_>, Error> {
match self {
Self::Live(on) => on.next_packet(),
Self::Offline(off) => off.next_packet(),
@@ -123,7 +135,7 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result
} else {
200 // limit stored packets slice dimension (to keep more in the buffer)
})
.immediate_mode(true) // parse packets ASAP
.immediate_mode(false)
.timeout(150) // ensure UI is updated even if no packets are captured
.open()?;
Ok(Self::Live(cap))
@@ -131,6 +143,13 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result
CaptureSource::File(file) => Ok(Self::Offline(Capture::from_file(&file.path)?)),
}
}
fn set_bpf(&mut self, bpf: &str) -> Result<(), Error> {
match self {
Self::Live(cap) => cap.filter(bpf, true),
Self::Offline(cap) => cap.filter(bpf, true),
}
}
}
#[derive(Clone)]
@@ -140,10 +159,23 @@ pub enum CaptureSource {
}
impl CaptureSource {
pub fn from_conf(conf: &Conf) -> Self {
match conf.capture_source_picklist {
CaptureSourcePicklist::Device => {
let device = conf.device.to_my_device();
Self::Device(device)
}
CaptureSourcePicklist::File => {
let path = conf.import_pcap_path.clone();
Self::File(MyPcapImport::new(path))
}
}
}
pub fn title(&self, language: Language) -> &str {
match self {
Self::Device(_) => network_adapter_translation(language),
Self::File(_) => file_name_translation(language),
Self::File(_) => capture_file_translation(language),
}
}
@@ -154,9 +186,21 @@ pub fn get_addresses(&self) -> &Vec<Address> {
}
}
pub fn set_addresses(&mut self, addresses: Vec<Address>) {
if let Self::Device(device) = self {
device.set_addresses(addresses);
pub fn set_addresses(&mut self) {
if let Self::Device(my_device) = self {
let mut addresses = Vec::new();
for dev in Device::list().log_err(location!()).unwrap_or_default() {
if matches!(
my_device.get_link_type(),
MyLinkType::LinuxSll(_) | MyLinkType::LinuxSll2(_)
) {
addresses.extend(dev.addresses);
} else if dev.name.eq(my_device.get_name()) {
addresses.extend(dev.addresses);
break;
}
}
my_device.set_addresses(addresses);
}
}
@@ -206,3 +250,10 @@ pub fn new(path: String) -> Self {
}
}
}
#[derive(Clone, Eq, PartialEq, Debug, Copy, Default, Serialize, Deserialize)]
pub enum CaptureSourcePicklist {
#[default]
Device,
File,
}

View File

@@ -0,0 +1,42 @@
use crate::networking::types::my_device::MyDevice;
use pcap::{Device, DeviceFlags};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct ConfigDevice {
pub device_name: String,
}
impl Default for ConfigDevice {
fn default() -> Self {
Self {
device_name: Device::lookup()
.unwrap_or(None)
.unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
})
.name,
}
}
}
impl ConfigDevice {
pub fn to_my_device(&self) -> MyDevice {
for device in Device::list().unwrap_or_default() {
if device.name.eq(&self.device_name) {
return MyDevice::from_pcap_device(device);
}
}
let standard_device = Device::lookup().unwrap_or(None).unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
});
MyDevice::from_pcap_device(standard_device)
}
}

View File

@@ -1,6 +1,6 @@
//! Module defining the `DataInfo` struct, which represents incoming and outgoing packets and bytes.
use crate::chart::types::chart_type::ChartType;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::report::types::sort_type::SortType;
use std::cmp::Ordering;
@@ -23,37 +23,26 @@ pub struct DataInfo {
}
impl DataInfo {
pub fn incoming_packets(&self) -> u128 {
self.incoming_packets
}
pub fn outgoing_packets(&self) -> u128 {
self.outgoing_packets
}
pub fn incoming_bytes(&self) -> u128 {
self.incoming_bytes
}
pub fn outgoing_bytes(&self) -> u128 {
self.outgoing_bytes
}
pub fn tot_packets(&self) -> u128 {
self.incoming_packets + self.outgoing_packets
}
pub fn tot_bytes(&self) -> u128 {
self.incoming_bytes + self.outgoing_bytes
}
pub fn tot_data(&self, chart_type: ChartType) -> u128 {
match chart_type {
ChartType::Packets => self.tot_packets(),
ChartType::Bytes => self.tot_bytes(),
pub fn incoming_data(&self, data_repr: DataRepr) -> u128 {
match data_repr {
DataRepr::Packets => self.incoming_packets,
DataRepr::Bytes => self.incoming_bytes,
DataRepr::Bits => self.incoming_bytes * 8,
}
}
pub fn outgoing_data(&self, data_repr: DataRepr) -> u128 {
match data_repr {
DataRepr::Packets => self.outgoing_packets,
DataRepr::Bytes => self.outgoing_bytes,
DataRepr::Bits => self.outgoing_bytes * 8,
}
}
pub fn tot_data(&self, data_repr: DataRepr) -> u128 {
self.incoming_data(data_repr) + self.outgoing_data(data_repr)
}
pub fn add_packet(&mut self, bytes: u128, traffic_direction: TrafficDirection) {
if traffic_direction.eq(&TrafficDirection::Outgoing) {
self.outgoing_packets += 1;
@@ -103,18 +92,11 @@ pub fn refresh(&mut self, rhs: Self) {
self.final_instant = rhs.final_instant;
}
pub fn compare(&self, other: &Self, sort_type: SortType, chart_type: ChartType) -> Ordering {
match chart_type {
ChartType::Packets => match sort_type {
SortType::Ascending => self.tot_packets().cmp(&other.tot_packets()),
SortType::Descending => other.tot_packets().cmp(&self.tot_packets()),
SortType::Neutral => other.final_instant.cmp(&self.final_instant),
},
ChartType::Bytes => match sort_type {
SortType::Ascending => self.tot_bytes().cmp(&other.tot_bytes()),
SortType::Descending => other.tot_bytes().cmp(&self.tot_bytes()),
SortType::Neutral => other.final_instant.cmp(&self.final_instant),
},
pub fn compare(&self, other: &Self, sort_type: SortType, data_repr: DataRepr) -> Ordering {
match sort_type {
SortType::Ascending => self.tot_data(data_repr).cmp(&other.tot_data(data_repr)),
SortType::Descending => other.tot_data(data_repr).cmp(&self.tot_data(data_repr)),
SortType::Neutral => other.final_instant.cmp(&self.final_instant),
}
}
@@ -146,3 +128,115 @@ fn default() -> Self {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::networking::types::traffic_direction::TrafficDirection;
#[test]
fn test_data_info() {
// in_packets: 0, out_packets: 0, in_bytes: 0, out_bytes: 0
let mut data_info_1 = DataInfo::new_with_first_packet(123, TrafficDirection::Incoming);
// 1, 0, 123, 0
data_info_1.add_packet(100, TrafficDirection::Incoming);
// 2, 0, 223, 0
data_info_1.add_packet(200, TrafficDirection::Outgoing);
// 2, 1, 223, 200
data_info_1.add_packets(11, 1200, TrafficDirection::Outgoing);
// 2, 12, 223, 1400
data_info_1.add_packets(5, 500, TrafficDirection::Incoming);
// 7, 12, 723, 1400
assert_eq!(data_info_1.incoming_packets, 7);
assert_eq!(data_info_1.outgoing_packets, 12);
assert_eq!(data_info_1.incoming_bytes, 723);
assert_eq!(data_info_1.outgoing_bytes, 1400);
assert_eq!(data_info_1.tot_data(DataRepr::Packets), 19);
assert_eq!(data_info_1.tot_data(DataRepr::Bytes), 2123);
assert_eq!(data_info_1.tot_data(DataRepr::Bits), 16984);
assert_eq!(data_info_1.incoming_data(DataRepr::Packets), 7);
assert_eq!(data_info_1.incoming_data(DataRepr::Bytes), 723);
assert_eq!(data_info_1.incoming_data(DataRepr::Bits), 5784);
assert_eq!(data_info_1.outgoing_data(DataRepr::Packets), 12);
assert_eq!(data_info_1.outgoing_data(DataRepr::Bytes), 1400);
assert_eq!(data_info_1.outgoing_data(DataRepr::Bits), 11200);
let mut data_info_2 = DataInfo::new_with_first_packet(100, TrafficDirection::Outgoing);
// 0, 1, 0, 100
data_info_2.add_packets(19, 300, TrafficDirection::Outgoing);
// 0, 20, 0, 400
assert_eq!(data_info_2.incoming_packets, 0);
assert_eq!(data_info_2.outgoing_packets, 20);
assert_eq!(data_info_2.incoming_bytes, 0);
assert_eq!(data_info_2.outgoing_bytes, 400);
assert_eq!(data_info_2.tot_data(DataRepr::Packets), 20);
assert_eq!(data_info_2.tot_data(DataRepr::Bytes), 400);
assert_eq!(data_info_2.tot_data(DataRepr::Bits), 3200);
assert_eq!(data_info_2.incoming_data(DataRepr::Packets), 0);
assert_eq!(data_info_2.incoming_data(DataRepr::Bytes), 0);
assert_eq!(data_info_2.incoming_data(DataRepr::Bits), 0);
assert_eq!(data_info_2.outgoing_data(DataRepr::Packets), 20);
assert_eq!(data_info_2.outgoing_data(DataRepr::Bytes), 400);
assert_eq!(data_info_2.outgoing_data(DataRepr::Bits), 3200);
// compare data_info_1 and data_info_2
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Packets),
Ordering::Less
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Packets),
Ordering::Greater
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Packets),
Ordering::Greater
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Bytes),
Ordering::Greater
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Bytes),
Ordering::Less
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Bytes),
Ordering::Greater
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Bits),
Ordering::Greater
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Bits),
Ordering::Less
);
assert_eq!(
data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Bits),
Ordering::Greater
);
// refresh data_info_1 with data_info_2
assert!(data_info_1.final_instant < data_info_2.final_instant);
data_info_1.refresh(data_info_2);
// data_info_1 should now contain the sum of both data_info_1 and data_info_2
assert_eq!(data_info_1.incoming_packets, 7);
assert_eq!(data_info_1.outgoing_packets, 32);
assert_eq!(data_info_1.incoming_bytes, 723);
assert_eq!(data_info_1.outgoing_bytes, 1800);
assert_eq!(data_info_1.final_instant, data_info_2.final_instant);
}
}

View File

@@ -0,0 +1,523 @@
use crate::translations::translations::{
bytes_exceeded_translation, bytes_translation, packets_exceeded_translation,
packets_translation,
};
use crate::translations::translations_4::{bits_exceeded_translation, bits_translation};
use crate::translations::types::language::Language;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DataRepr {
Packets,
Bytes,
Bits,
}
impl DataRepr {
pub(crate) const ALL: [DataRepr; 3] = [DataRepr::Bits, DataRepr::Bytes, DataRepr::Packets];
pub fn get_label(&self, language: Language) -> &str {
match self {
DataRepr::Packets => packets_translation(language),
DataRepr::Bytes => bytes_translation(language),
DataRepr::Bits => bits_translation(language),
}
}
/// Returns a String representing a quantity of traffic (packets / bytes / bits) with the proper multiple if applicable
pub fn formatted_string(self, amount: u128) -> String {
if self == DataRepr::Packets {
return amount.to_string();
}
#[allow(clippy::cast_precision_loss)]
let mut n = amount as f32;
let byte_multiple = ByteMultiple::from_amount(amount);
#[allow(clippy::cast_precision_loss)]
let multiplier = byte_multiple.multiplier() as f32;
n /= multiplier;
if n > 999.0 && byte_multiple != ByteMultiple::PB {
// this allows representing e.g. 999_999 as 999 KB instead of 1000 KB
n = 999.0;
}
let precision = usize::from(byte_multiple != ByteMultiple::B && n <= 9.95);
format!("{n:.precision$} {}", byte_multiple.pretty_print(self))
.trim()
.to_string()
}
pub fn data_exceeded_translation(&self, language: Language) -> &str {
match self {
DataRepr::Packets => packets_exceeded_translation(language),
DataRepr::Bytes => bytes_exceeded_translation(language),
DataRepr::Bits => bits_exceeded_translation(language),
}
}
}
/// Represents a Byte or bit multiple for displaying values in a human-readable format.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ByteMultiple {
/// A Byte
B,
/// 10^3 Bytes
KB,
/// 10^6 Bytes
MB,
/// 10^9 Bytes
GB,
/// 10^12 Bytes
TB,
/// 10^15 Bytes
PB,
}
impl ByteMultiple {
pub fn multiplier(self) -> u64 {
match self {
ByteMultiple::B => 1,
ByteMultiple::KB => 1_000,
ByteMultiple::MB => 1_000_000,
ByteMultiple::GB => 1_000_000_000,
ByteMultiple::TB => 1_000_000_000_000,
ByteMultiple::PB => 1_000_000_000_000_000,
}
}
fn from_amount(bytes: u128) -> Self {
match bytes {
x if (u128::MIN..u128::from(ByteMultiple::KB.multiplier())).contains(&x) => {
ByteMultiple::B
}
x if (u128::from(ByteMultiple::KB.multiplier())
..u128::from(ByteMultiple::MB.multiplier()))
.contains(&x) =>
{
ByteMultiple::KB
}
x if (u128::from(ByteMultiple::MB.multiplier())
..u128::from(ByteMultiple::GB.multiplier()))
.contains(&x) =>
{
ByteMultiple::MB
}
x if (u128::from(ByteMultiple::GB.multiplier())
..u128::from(ByteMultiple::TB.multiplier()))
.contains(&x) =>
{
ByteMultiple::GB
}
x if (u128::from(ByteMultiple::TB.multiplier())
..u128::from(ByteMultiple::PB.multiplier()))
.contains(&x) =>
{
ByteMultiple::TB
}
_ => ByteMultiple::PB,
}
}
pub fn get_char(self) -> String {
match self {
Self::B => String::new(),
Self::KB => "K".to_string(),
Self::MB => "M".to_string(),
Self::GB => "G".to_string(),
Self::TB => "T".to_string(),
Self::PB => "P".to_string(),
}
}
pub fn from_char(ch: char) -> Self {
match ch.to_ascii_uppercase() {
'K' => ByteMultiple::KB,
'M' => ByteMultiple::MB,
'G' => ByteMultiple::GB,
'T' => ByteMultiple::TB,
'P' => ByteMultiple::PB,
_ => ByteMultiple::B,
}
}
fn pretty_print(self, repr: DataRepr) -> String {
match repr {
DataRepr::Packets => String::new(),
DataRepr::Bytes => format!("{}B", self.get_char()),
DataRepr::Bits => format!("{}b", self.get_char()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interpret_suffix_correctly() {
assert_eq!(ByteMultiple::from_char('B'), ByteMultiple::B);
assert_eq!(ByteMultiple::from_char('k'), ByteMultiple::KB);
assert_eq!(ByteMultiple::from_char('M'), ByteMultiple::MB);
assert_eq!(ByteMultiple::from_char('g'), ByteMultiple::GB);
assert_eq!(ByteMultiple::from_char('t'), ByteMultiple::TB);
assert_eq!(ByteMultiple::from_char('P'), ByteMultiple::PB);
}
#[test]
fn test_interpret_unknown_suffix_correctly() {
assert_eq!(ByteMultiple::from_char('E'), ByteMultiple::B);
assert_eq!(ByteMultiple::from_char('y'), ByteMultiple::B);
}
#[test]
fn test_byte_multiple_display() {
assert_eq!(
format!("{}", ByteMultiple::B.pretty_print(DataRepr::Packets)),
""
);
assert_eq!(
format!("{}", ByteMultiple::B.pretty_print(DataRepr::Bytes)),
"B"
);
assert_eq!(
format!("{}", ByteMultiple::B.pretty_print(DataRepr::Bits)),
"b"
);
assert_eq!(
format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Packets)),
""
);
assert_eq!(
format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Bytes)),
"KB"
);
assert_eq!(
format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Bits)),
"Kb"
);
assert_eq!(
format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Packets)),
""
);
assert_eq!(
format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Bytes)),
"MB"
);
assert_eq!(
format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Bits)),
"Mb"
);
assert_eq!(
format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Packets)),
""
);
assert_eq!(
format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Bytes)),
"GB"
);
assert_eq!(
format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Bits)),
"Gb"
);
assert_eq!(
format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Packets)),
""
);
assert_eq!(
format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Bytes)),
"TB"
);
assert_eq!(
format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Bits)),
"Tb"
);
assert_eq!(
format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Packets)),
""
);
assert_eq!(
format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Bytes)),
"PB"
);
assert_eq!(
format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Bits)),
"Pb"
);
}
#[test]
fn test_byte_multiple_get_char() {
assert_eq!(ByteMultiple::B.get_char(), "");
assert_eq!(ByteMultiple::KB.get_char(), "K");
assert_eq!(ByteMultiple::MB.get_char(), "M");
assert_eq!(ByteMultiple::GB.get_char(), "G");
assert_eq!(ByteMultiple::TB.get_char(), "T");
assert_eq!(ByteMultiple::PB.get_char(), "P");
}
#[test]
fn test_byte_multiple_multiplier() {
assert_eq!(ByteMultiple::B.multiplier(), 1);
assert_eq!(ByteMultiple::KB.multiplier(), 1_000);
assert_eq!(ByteMultiple::MB.multiplier(), 1_000_000);
assert_eq!(ByteMultiple::GB.multiplier(), 1_000_000_000);
assert_eq!(ByteMultiple::TB.multiplier(), 1_000_000_000_000);
assert_eq!(ByteMultiple::PB.multiplier(), 1_000_000_000_000_000);
}
#[test]
fn test_byte_multiple_formatted_string() {
assert_eq!(DataRepr::Packets.formatted_string(u128::MIN), "0");
assert_eq!(DataRepr::Bytes.formatted_string(u128::MIN), "0 B");
assert_eq!(DataRepr::Bits.formatted_string(u128::MIN), "0 b");
assert_eq!(DataRepr::Packets.formatted_string(1), "1");
assert_eq!(DataRepr::Bytes.formatted_string(1), "1 B");
assert_eq!(DataRepr::Bits.formatted_string(1), "1 b");
assert_eq!(DataRepr::Packets.formatted_string(82), "82");
assert_eq!(DataRepr::Bytes.formatted_string(82), "82 B");
assert_eq!(DataRepr::Bits.formatted_string(82), "82 b");
assert_eq!(DataRepr::Packets.formatted_string(999), "999");
assert_eq!(DataRepr::Bytes.formatted_string(999), "999 B");
assert_eq!(DataRepr::Bits.formatted_string(999), "999 b");
assert_eq!(DataRepr::Packets.formatted_string(1_000), "1000");
assert_eq!(DataRepr::Bytes.formatted_string(1_000), "1.0 KB");
assert_eq!(DataRepr::Bits.formatted_string(1_000), "1.0 Kb");
assert_eq!(DataRepr::Packets.formatted_string(1_090), "1090");
assert_eq!(DataRepr::Bytes.formatted_string(1_090), "1.1 KB");
assert_eq!(DataRepr::Bits.formatted_string(1_090), "1.1 Kb");
assert_eq!(DataRepr::Packets.formatted_string(1_990), "1990");
assert_eq!(DataRepr::Bytes.formatted_string(1_990), "2.0 KB");
assert_eq!(DataRepr::Bits.formatted_string(1_990), "2.0 Kb");
assert_eq!(DataRepr::Packets.formatted_string(9_090), "9090");
assert_eq!(DataRepr::Bytes.formatted_string(9_090), "9.1 KB");
assert_eq!(DataRepr::Bits.formatted_string(9_090), "9.1 Kb");
assert_eq!(DataRepr::Packets.formatted_string(9_950), "9950");
assert_eq!(DataRepr::Bytes.formatted_string(9_950), "9.9 KB");
assert_eq!(DataRepr::Bits.formatted_string(9_950), "9.9 Kb");
assert_eq!(DataRepr::Packets.formatted_string(9_951), "9951");
assert_eq!(DataRepr::Bytes.formatted_string(9_951), "10 KB");
assert_eq!(DataRepr::Bits.formatted_string(9_951), "10 Kb");
assert_eq!(DataRepr::Packets.formatted_string(71_324), "71324");
assert_eq!(DataRepr::Bytes.formatted_string(71_324), "71 KB");
assert_eq!(DataRepr::Bits.formatted_string(71_324), "71 Kb");
assert_eq!(DataRepr::Packets.formatted_string(821_789), "821789");
assert_eq!(DataRepr::Bytes.formatted_string(821_789), "822 KB");
assert_eq!(DataRepr::Bits.formatted_string(821_789), "822 Kb");
assert_eq!(DataRepr::Packets.formatted_string(999_499), "999499");
assert_eq!(DataRepr::Bytes.formatted_string(999_499), "999 KB");
assert_eq!(DataRepr::Bits.formatted_string(999_499), "999 Kb");
assert_eq!(DataRepr::Packets.formatted_string(999_999), "999999");
assert_eq!(DataRepr::Bytes.formatted_string(999_999), "999 KB");
assert_eq!(DataRepr::Bits.formatted_string(999_999), "999 Kb");
assert_eq!(DataRepr::Packets.formatted_string(1_000_000), "1000000");
assert_eq!(DataRepr::Bytes.formatted_string(1_000_000), "1.0 MB");
assert_eq!(DataRepr::Bits.formatted_string(1_000_000), "1.0 Mb");
assert_eq!(DataRepr::Packets.formatted_string(3_790_000), "3790000");
assert_eq!(DataRepr::Bytes.formatted_string(3_790_000), "3.8 MB");
assert_eq!(DataRepr::Bits.formatted_string(3_790_000), "3.8 Mb");
assert_eq!(DataRepr::Packets.formatted_string(9_950_000), "9950000");
assert_eq!(DataRepr::Bytes.formatted_string(9_950_000), "9.9 MB");
assert_eq!(DataRepr::Bits.formatted_string(9_950_000), "9.9 Mb");
assert_eq!(DataRepr::Packets.formatted_string(9_951_000), "9951000");
assert_eq!(DataRepr::Bytes.formatted_string(9_951_000), "10 MB");
assert_eq!(DataRepr::Bits.formatted_string(9_951_000), "10 Mb");
assert_eq!(DataRepr::Packets.formatted_string(49_499_000), "49499000");
assert_eq!(DataRepr::Bytes.formatted_string(49_499_000), "49 MB");
assert_eq!(DataRepr::Bits.formatted_string(49_499_000), "49 Mb");
assert_eq!(DataRepr::Packets.formatted_string(49_500_000), "49500000");
assert_eq!(DataRepr::Bytes.formatted_string(49_500_000), "50 MB");
assert_eq!(DataRepr::Bits.formatted_string(49_500_000), "50 Mb");
assert_eq!(DataRepr::Packets.formatted_string(670_900_000), "670900000");
assert_eq!(DataRepr::Bytes.formatted_string(670_900_000), "671 MB");
assert_eq!(DataRepr::Bits.formatted_string(670_900_000), "671 Mb");
assert_eq!(DataRepr::Packets.formatted_string(998_199_999), "998199999");
assert_eq!(DataRepr::Bytes.formatted_string(998_199_999), "998 MB");
assert_eq!(DataRepr::Bits.formatted_string(998_199_999), "998 Mb");
assert_eq!(DataRepr::Packets.formatted_string(999_999_999), "999999999");
assert_eq!(DataRepr::Bytes.formatted_string(999_999_999), "999 MB");
assert_eq!(DataRepr::Bits.formatted_string(999_999_999), "999 Mb");
assert_eq!(
DataRepr::Packets.formatted_string(1_000_000_000),
"1000000000"
);
assert_eq!(DataRepr::Bytes.formatted_string(1_000_000_000), "1.0 GB");
assert_eq!(DataRepr::Bits.formatted_string(1_000_000_000), "1.0 Gb");
assert_eq!(
DataRepr::Packets.formatted_string(7_770_000_000),
"7770000000"
);
assert_eq!(DataRepr::Bytes.formatted_string(7_770_000_000), "7.8 GB");
assert_eq!(DataRepr::Bits.formatted_string(7_770_000_000), "7.8 Gb");
assert_eq!(
DataRepr::Packets.formatted_string(9_950_000_000),
"9950000000"
);
assert_eq!(DataRepr::Bytes.formatted_string(9_950_000_000), "9.9 GB");
assert_eq!(DataRepr::Bits.formatted_string(9_950_000_000), "9.9 Gb");
assert_eq!(
DataRepr::Packets.formatted_string(9_951_000_000),
"9951000000"
);
assert_eq!(DataRepr::Bytes.formatted_string(9_951_000_000), "10 GB");
assert_eq!(DataRepr::Bits.formatted_string(9_951_000_000), "10 Gb");
assert_eq!(
DataRepr::Packets.formatted_string(19_951_000_000),
"19951000000"
);
assert_eq!(DataRepr::Bytes.formatted_string(19_951_000_000), "20 GB");
assert_eq!(DataRepr::Bits.formatted_string(19_951_000_000), "20 Gb");
assert_eq!(
DataRepr::Packets.formatted_string(399_951_000_000),
"399951000000"
);
assert_eq!(DataRepr::Bytes.formatted_string(399_951_000_000), "400 GB");
assert_eq!(DataRepr::Bits.formatted_string(399_951_000_000), "400 Gb");
assert_eq!(
DataRepr::Packets.formatted_string(999_999_999_999),
"999999999999"
);
assert_eq!(DataRepr::Bytes.formatted_string(999_999_999_999), "999 GB");
assert_eq!(DataRepr::Bits.formatted_string(999_999_999_999), "999 Gb");
assert_eq!(
DataRepr::Packets.formatted_string(1_000_000_000_000),
"1000000000000"
);
assert_eq!(
DataRepr::Bytes.formatted_string(1_000_000_000_000),
"1.0 TB"
);
assert_eq!(DataRepr::Bits.formatted_string(1_000_000_000_000), "1.0 Tb");
assert_eq!(
DataRepr::Packets.formatted_string(9_950_000_000_000),
"9950000000000"
);
assert_eq!(
DataRepr::Bytes.formatted_string(9_950_000_000_000),
"9.9 TB"
);
assert_eq!(DataRepr::Bits.formatted_string(9_950_000_000_000), "9.9 Tb");
assert_eq!(
DataRepr::Packets.formatted_string(9_951_000_000_000),
"9951000000000"
);
assert_eq!(DataRepr::Bytes.formatted_string(9_951_000_000_000), "10 TB");
assert_eq!(DataRepr::Bits.formatted_string(9_951_000_000_000), "10 Tb");
assert_eq!(
DataRepr::Packets.formatted_string(999_950_000_000_000),
"999950000000000"
);
assert_eq!(
DataRepr::Bytes.formatted_string(999_950_000_000_000),
"999 TB"
);
assert_eq!(
DataRepr::Bits.formatted_string(999_950_000_000_000),
"999 Tb"
);
assert_eq!(
DataRepr::Packets.formatted_string(999_999_999_999_999),
"999999999999999"
);
assert_eq!(
DataRepr::Bytes.formatted_string(999_999_999_999_999),
"999 TB"
);
assert_eq!(
DataRepr::Bits.formatted_string(999_999_999_999_999),
"999 Tb"
);
assert_eq!(
DataRepr::Packets.formatted_string(1_000_000_000_000_000),
"1000000000000000"
);
assert_eq!(
DataRepr::Bytes.formatted_string(1_000_000_000_000_000),
"1.0 PB"
);
assert_eq!(
DataRepr::Bits.formatted_string(1_000_000_000_000_000),
"1.0 Pb"
);
assert_eq!(
DataRepr::Packets.formatted_string(1_000_000_000_000_000_0),
"10000000000000000"
);
assert_eq!(
DataRepr::Bytes.formatted_string(1_000_000_000_000_000_0),
"10 PB"
);
assert_eq!(
DataRepr::Bits.formatted_string(1_000_000_000_000_000_0),
"10 Pb"
);
assert_eq!(
DataRepr::Packets.formatted_string(999_999_999_000_000_000),
"999999999000000000"
);
assert_eq!(
DataRepr::Bytes.formatted_string(999_999_999_000_000_000),
"1000 PB"
);
assert_eq!(
DataRepr::Bits.formatted_string(999_999_999_000_000_000),
"1000 Pb"
);
assert_eq!(
DataRepr::Packets.formatted_string(u128::MAX / 2),
"170141183460469231731687303715884105727"
);
assert_eq!(
DataRepr::Bytes.formatted_string(u128::MAX / 2),
"170141184077655307190272 PB"
);
assert_eq!(
DataRepr::Bits.formatted_string(u128::MAX / 2),
"170141184077655307190272 Pb"
);
assert_eq!(
DataRepr::Packets.formatted_string(u128::MAX),
"340282366920938463463374607431768211455"
);
assert_eq!(DataRepr::Bytes.formatted_string(u128::MAX), "inf PB");
assert_eq!(DataRepr::Bits.formatted_string(u128::MAX), "inf Pb");
}
}

View File

@@ -1,112 +0,0 @@
//! Module defining the `Filters` struct, which represents the possible filters applicable on network traffic.
use std::collections::HashSet;
use crate::networking::types::ip_collection::AddressCollection;
use crate::networking::types::packet_filters_fields::PacketFiltersFields;
use crate::networking::types::port_collection::PortCollection;
use crate::{IpVersion, Protocol};
/// Possible filters applicable to network traffic
#[derive(Clone)]
pub struct Filters {
/// Internet Protocol versions
pub ip_versions: HashSet<IpVersion>,
/// Protocols
pub protocols: HashSet<Protocol>,
/// IP addresses string in Initial page text input
pub address_str: String,
/// IP address collection to match against traffic
pub address_collection: AddressCollection,
/// Ports string in Initial page text input
pub port_str: String,
/// Port collection to match against traffic
pub port_collection: PortCollection,
}
impl Default for Filters {
fn default() -> Self {
Self {
ip_versions: HashSet::from(IpVersion::ALL),
protocols: HashSet::from(Protocol::ALL),
address_str: String::new(),
address_collection: AddressCollection::default(),
port_str: String::new(),
port_collection: PortCollection::default(),
}
}
}
impl Filters {
/// Checks whether the filters match the current packet's protocols
pub fn matches(&self, packet_filters_fields: &PacketFiltersFields) -> bool {
self.ip_versions.contains(&packet_filters_fields.ip_version)
&& self.protocols.contains(&packet_filters_fields.protocol)
&& (self
.address_collection
.contains(&packet_filters_fields.source)
|| self
.address_collection
.contains(&packet_filters_fields.dest))
&& (self.port_collection.contains(packet_filters_fields.sport)
|| self.port_collection.contains(packet_filters_fields.dport))
}
pub fn are_valid(&self) -> bool {
self.ip_version_valid()
&& self.protocol_valid()
&& self.address_valid()
&& self.port_valid()
}
pub fn ip_version_valid(&self) -> bool {
!self.ip_versions.is_empty()
}
pub fn protocol_valid(&self) -> bool {
!self.protocols.is_empty()
}
pub fn address_valid(&self) -> bool {
AddressCollection::new(&self.address_str).is_some()
}
pub fn port_valid(&self) -> bool {
PortCollection::new(&self.port_str).is_some()
}
pub fn none_active(&self) -> bool {
!self.ip_version_active()
&& !self.protocol_active()
&& !self.address_active()
&& !self.port_active()
}
pub fn ip_version_active(&self) -> bool {
self.ip_versions.len() != IpVersion::ALL.len()
}
pub fn protocol_active(&self) -> bool {
self.protocols.len() != Protocol::ALL.len()
}
pub fn address_active(&self) -> bool {
self.address_collection != AddressCollection::default()
}
pub fn port_active(&self) -> bool {
self.port_collection != PortCollection::default()
}
pub fn pretty_print_ip(&self) -> String {
format!("{:?}", self.ip_versions)
.replace('{', "")
.replace('}', "")
}
pub fn pretty_print_protocol(&self) -> String {
format!("{:?}", self.protocols)
.replace('{', "")
.replace('}', "")
}
}

View File

@@ -216,15 +216,15 @@ pub fn from_etherparse(icmpv6type: &Icmpv6Type) -> IcmpType {
Icmpv6Type::ParameterProblem(_) => Self::ParameterProblem,
Icmpv6Type::EchoRequest(_) => Self::EchoRequest,
Icmpv6Type::EchoReply(_) => Self::EchoReply,
Icmpv6Type::RouterSolicitation => Self::RouterSolicitation,
Icmpv6Type::RouterAdvertisement(_) => Self::RouterAdvertisement,
Icmpv6Type::NeighborSolicitation => Self::NeighborSolicitation,
Icmpv6Type::NeighborAdvertisement(_) => Self::NeighborAdvertisement,
Icmpv6Type::Redirect => Self::RedirectMessage,
Icmpv6Type::Unknown { type_u8: id, .. } => match id {
130 => Self::MulticastListenerQuery,
131 => Self::MulticastListenerReport,
132 => Self::MulticastListenerDone,
133 => Self::RouterSolicitation,
134 => Self::RouterAdvertisement,
135 => Self::NeighborSolicitation,
136 => Self::NeighborAdvertisement,
137 => Self::RedirectMessage,
138 => Self::RouterRenumbering,
139 => Self::ICMPNodeInformationQuery,
140 => Self::ICMPNodeInformationResponse,

View File

@@ -1,12 +1,15 @@
//! Module defining the `InfoAddressPortPair` struct, useful to format the output report file and
//! to keep track of statistics about the sniffed traffic.
use std::cmp::Ordering;
use std::collections::HashMap;
use crate::Service;
use crate::networking::types::arp_type::ArpType;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::icmp_type::IcmpType;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::report::types::sort_type::SortType;
use crate::utils::types::timestamp::Timestamp;
/// Struct useful to format the output report file and to keep track of statistics about the sniffed traffic.
@@ -56,4 +59,94 @@ pub fn refresh(&mut self, other: &Self) {
.or_insert(*count);
}
}
pub fn transmitted_data(&self, data_repr: DataRepr) -> u128 {
match data_repr {
DataRepr::Packets => self.transmitted_packets,
DataRepr::Bytes => self.transmitted_bytes,
DataRepr::Bits => self.transmitted_bytes * 8,
}
}
pub fn compare(&self, other: &Self, sort_type: SortType, data_repr: DataRepr) -> Ordering {
match sort_type {
SortType::Ascending => self
.transmitted_data(data_repr)
.cmp(&other.transmitted_data(data_repr)),
SortType::Descending => other
.transmitted_data(data_repr)
.cmp(&self.transmitted_data(data_repr)),
SortType::Neutral => other.final_timestamp.cmp(&self.final_timestamp),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::networking::types::data_representation::DataRepr;
use crate::report::types::sort_type::SortType;
#[test]
fn test_info_address_port_pair_data() {
let pair1 = InfoAddressPortPair {
transmitted_bytes: 1000,
transmitted_packets: 10,
final_timestamp: Timestamp::new(8, 1300),
..Default::default()
};
let pair2 = InfoAddressPortPair {
transmitted_bytes: 1100,
transmitted_packets: 8,
final_timestamp: Timestamp::new(15, 0),
..Default::default()
};
assert_eq!(pair1.transmitted_data(DataRepr::Bytes), 1000);
assert_eq!(pair1.transmitted_data(DataRepr::Packets), 10);
assert_eq!(pair1.transmitted_data(DataRepr::Bits), 8000);
assert_eq!(pair2.transmitted_data(DataRepr::Bytes), 1100);
assert_eq!(pair2.transmitted_data(DataRepr::Packets), 8);
assert_eq!(pair2.transmitted_data(DataRepr::Bits), 8800);
assert_eq!(
pair1.compare(&pair2, SortType::Ascending, DataRepr::Bytes),
Ordering::Less
);
assert_eq!(
pair1.compare(&pair2, SortType::Descending, DataRepr::Bytes),
Ordering::Greater
);
assert_eq!(
pair1.compare(&pair2, SortType::Neutral, DataRepr::Bytes),
Ordering::Greater
);
assert_eq!(
pair1.compare(&pair2, SortType::Ascending, DataRepr::Packets),
Ordering::Greater
);
assert_eq!(
pair1.compare(&pair2, SortType::Descending, DataRepr::Packets),
Ordering::Less
);
assert_eq!(
pair1.compare(&pair2, SortType::Neutral, DataRepr::Packets),
Ordering::Greater
);
assert_eq!(
pair1.compare(&pair2, SortType::Ascending, DataRepr::Bits),
Ordering::Less
);
assert_eq!(
pair1.compare(&pair2, SortType::Descending, DataRepr::Bits),
Ordering::Greater
);
assert_eq!(
pair1.compare(&pair2, SortType::Neutral, DataRepr::Bits),
Ordering::Greater
);
}
}

View File

@@ -1,8 +1,8 @@
use crate::Service;
use crate::chart::types::chart_type::ChartType;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::utils::types::timestamp::Timestamp;
@@ -13,15 +13,11 @@
pub struct InfoTraffic {
/// Total amount of exchanged data
pub tot_data_info: DataInfo,
/// Total packets including those not filtered
pub all_packets: u128,
/// Total bytes including those not filtered
pub all_bytes: u128,
/// Number of dropped packets
pub dropped_packets: u32,
/// Timestamp of the latest parsed packet
pub last_packet_timestamp: Timestamp,
/// Map of the filtered traffic
/// Map of the traffic
pub map: HashMap<AddressPortPair, InfoAddressPortPair>,
/// Map of the upper layer services with their data info
pub services: HashMap<Service, DataInfo>,
@@ -33,8 +29,6 @@ impl InfoTraffic {
pub fn refresh(&mut self, msg: &mut InfoTraffic) {
self.tot_data_info.refresh(msg.tot_data_info);
self.all_packets += msg.all_packets;
self.all_bytes += msg.all_bytes;
self.dropped_packets = msg.dropped_packets;
// it can happen they're equal due to dis-alignments in the PCAP timestamp
@@ -65,27 +59,20 @@ pub fn refresh(&mut self, msg: &mut InfoTraffic) {
}
}
pub fn get_thumbnail_data(&self, chart_type: ChartType) -> (u128, u128, u128, u128) {
if chart_type.eq(&ChartType::Bytes) {
(
self.tot_data_info.incoming_bytes(),
self.tot_data_info.outgoing_bytes(),
self.all_bytes
- self.tot_data_info.outgoing_bytes()
- self.tot_data_info.incoming_bytes(),
pub fn get_thumbnail_data(&self, data_repr: DataRepr) -> (u128, u128, u128) {
let incoming = self.tot_data_info.incoming_data(data_repr);
let outgoing = self.tot_data_info.outgoing_data(data_repr);
let all = incoming + outgoing;
let all_packets = self.tot_data_info.tot_data(DataRepr::Packets);
let dropped = match data_repr {
DataRepr::Packets => u128::from(self.dropped_packets),
DataRepr::Bytes | DataRepr::Bits => {
// assume that the dropped packets have the same size as the average packet
u128::from(self.dropped_packets) * self.all_bytes / self.all_packets,
)
} else {
(
self.tot_data_info.incoming_packets(),
self.tot_data_info.outgoing_packets(),
self.all_packets
- self.tot_data_info.outgoing_packets()
- self.tot_data_info.incoming_packets(),
u128::from(self.dropped_packets),
)
}
u128::from(self.dropped_packets) * all / all_packets
}
};
(incoming, outgoing, dropped)
}
pub fn take_but_leave_something(&mut self) -> Self {

View File

@@ -3,18 +3,15 @@
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq, Clone)]
pub(crate) struct AddressCollection {
pub(crate) struct IpCollection {
ips: Vec<IpAddr>,
ranges: Vec<RangeInclusive<IpAddr>>,
}
impl AddressCollection {
impl IpCollection {
const SEPARATOR: char = ',';
const RANGE_SEPARATOR: char = '-';
pub const PLACEHOLDER_STR: &'static str =
"0.0.0.0-255.255.255.255, ::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
pub(crate) fn new(str: &str) -> Option<Self> {
let str = str.replace(' ', "");
@@ -62,9 +59,9 @@ pub(crate) fn contains(&self, ip: &IpAddr) -> bool {
}
}
impl Default for AddressCollection {
impl Default for IpCollection {
fn default() -> Self {
AddressCollection {
IpCollection {
ips: vec![],
ranges: vec![
RangeInclusive::new(
@@ -89,11 +86,11 @@ mod tests {
use std::ops::RangeInclusive;
use std::str::FromStr;
use crate::networking::types::ip_collection::AddressCollection;
use crate::networking::types::ip_collection::IpCollection;
#[test]
fn test_default_collection_contains_everything() {
let collection = AddressCollection::default();
let collection = IpCollection::default();
assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap()));
assert!(collection.contains(&IpAddr::from_str("0.0.0.0").unwrap()));
assert!(collection.contains(&IpAddr::from_str("255.255.255.255").unwrap()));
@@ -116,8 +113,8 @@ fn test_default_collection_contains_everything() {
#[test]
fn test_new_collections_1() {
assert_eq!(
AddressCollection::new("1.1.1.1,2.2.2.2").unwrap(),
AddressCollection {
IpCollection::new("1.1.1.1,2.2.2.2").unwrap(),
IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap()
@@ -127,11 +124,9 @@ fn test_new_collections_1() {
);
assert_eq!(
AddressCollection::new(
"1.1.1.1, 2.2.2.2, 3.3.3.3 - 5.5.5.5, 10.0.0.1-10.0.0.255,9.9.9.9",
)
.unwrap(),
AddressCollection {
IpCollection::new("1.1.1.1, 2.2.2.2, 3.3.3.3 - 5.5.5.5, 10.0.0.1-10.0.0.255,9.9.9.9",)
.unwrap(),
IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap(),
@@ -151,8 +146,8 @@ fn test_new_collections_1() {
);
assert_eq!(
AddressCollection::new(" aaaa::ffff,bbbb::1-cccc::2").unwrap(),
AddressCollection {
IpCollection::new(" aaaa::ffff,bbbb::1-cccc::2").unwrap(),
IpCollection {
ips: vec![IpAddr::from_str("aaaa::ffff").unwrap(),],
ranges: vec![RangeInclusive::new(
IpAddr::from_str("bbbb::1").unwrap(),
@@ -165,8 +160,8 @@ fn test_new_collections_1() {
#[test]
fn test_new_collections_2() {
assert_eq!(
AddressCollection::new("1.1.1.1,2.2.2.2, 8.8.8.8 ").unwrap(),
AddressCollection {
IpCollection::new("1.1.1.1,2.2.2.2, 8.8.8.8 ").unwrap(),
IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap(),
@@ -177,8 +172,8 @@ fn test_new_collections_2() {
);
assert_eq!(
AddressCollection::new(" 1.1.1.1 -1.1.1.1").unwrap(),
AddressCollection {
IpCollection::new(" 1.1.1.1 -1.1.1.1").unwrap(),
IpCollection {
ips: vec![],
ranges: vec![RangeInclusive::new(
IpAddr::from_str("1.1.1.1").unwrap(),
@@ -188,9 +183,9 @@ fn test_new_collections_2() {
);
assert_eq!(
AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9",)
IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9",)
.unwrap(),
AddressCollection {
IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap(),
@@ -210,8 +205,8 @@ fn test_new_collections_2() {
);
assert_eq!(
AddressCollection::new("aaaa::ffff,bbbb::1-cccc::2,ff::dd").unwrap(),
AddressCollection {
IpCollection::new("aaaa::ffff,bbbb::1-cccc::2,ff::dd").unwrap(),
IpCollection {
ips: vec![
IpAddr::from_str("aaaa::ffff").unwrap(),
IpAddr::from_str("ff::dd").unwrap()
@@ -227,32 +222,32 @@ fn test_new_collections_2() {
#[test]
fn test_new_collections_invalid() {
assert_eq!(
AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9"),
IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9"),
None
);
assert_eq!(
AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1:10.0.0.255,9.9.9.9"),
IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1:10.0.0.255,9.9.9.9"),
None
);
assert_eq!(AddressCollection::new("1.1.1.1-aa::ff"), None);
assert_eq!(IpCollection::new("1.1.1.1-aa::ff"), None);
assert_eq!(AddressCollection::new("aa::ff-1.1.1.1"), None);
assert_eq!(IpCollection::new("aa::ff-1.1.1.1"), None);
assert_eq!(AddressCollection::new("aa::ff-aa::ee"), None);
assert_eq!(IpCollection::new("aa::ff-aa::ee"), None);
assert_eq!(AddressCollection::new("1.1.1.1-1.1.0.1"), None);
assert_eq!(IpCollection::new("1.1.1.1-1.1.0.1"), None);
assert_eq!(AddressCollection::new("1.1.1.1-2.2.2.2-3.3.3.3"), None);
assert_eq!(IpCollection::new("1.1.1.1-2.2.2.2-3.3.3.3"), None);
assert_eq!(AddressCollection::new("1.1.1.1-2.2.2.2-"), None);
assert_eq!(IpCollection::new("1.1.1.1-2.2.2.2-"), None);
}
#[test]
fn test_ip_collection_contains() {
let collection =
AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9")
IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9")
.unwrap();
assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap()));
assert!(collection.contains(&IpAddr::from_str("2.2.2.2").unwrap()));
@@ -268,12 +263,12 @@ fn test_ip_collection_contains() {
assert!(!collection.contains(&IpAddr::from_str("9.9.9.10").unwrap()));
assert!(!collection.contains(&IpAddr::from_str("3.3.3.2").unwrap()));
let collection_2 = AddressCollection::new("1.1.1.0-1.1.9.0").unwrap();
let collection_2 = IpCollection::new("1.1.1.0-1.1.9.0").unwrap();
assert!(!collection_2.contains(&IpAddr::from_str("1.1.100.5").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("1.1.3.255").unwrap()));
// check that ipv4 range doesn't contain ipv6
let collection_3 = AddressCollection::new("0.0.0.0-255.255.255.255").unwrap();
let collection_3 = IpCollection::new("0.0.0.0-255.255.255.255").unwrap();
assert!(!collection_3.contains(&IpAddr::from_str("::").unwrap()));
assert!(!collection_3.contains(&IpAddr::from_str("1111::2222").unwrap()));
}
@@ -281,7 +276,7 @@ fn test_ip_collection_contains() {
#[test]
fn test_ip_collection_contains_ipv6() {
let collection =
AddressCollection::new( "2001:db8:1234:0000:0000:0000:0000:0000-2001:db8:1234:ffff:ffff:ffff:ffff:ffff,daa::aad,caa::aac").unwrap();
IpCollection::new( "2001:db8:1234:0000:0000:0000:0000:0000-2001:db8:1234:ffff:ffff:ffff:ffff:ffff,daa::aad,caa::aac").unwrap();
assert!(
collection
.contains(&IpAddr::from_str("2001:db8:1234:0000:0000:0000:0000:0000").unwrap())
@@ -315,14 +310,14 @@ fn test_ip_collection_contains_ipv6() {
assert!(!collection.contains(&IpAddr::from_str("da::aad").unwrap()));
assert!(!collection.contains(&IpAddr::from_str("caa::aab").unwrap()));
let collection_2 = AddressCollection::new("aa::bb-aa:1::00").unwrap();
let collection_2 = IpCollection::new("aa::bb-aa:1::00").unwrap();
assert!(!collection_2.contains(&IpAddr::from_str("aa:11::0").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("aa::bc").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("aa::bbcc").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("00aa:0001::00").unwrap()));
// check that ipv6 range doesn't contain ipv4
let collection_3 = AddressCollection::new("0000::0000-ffff::8888").unwrap();
let collection_3 = IpCollection::new("0000::0000-ffff::8888").unwrap();
assert!(!collection_3.contains(&IpAddr::from_str("192.168.1.1").unwrap()));
assert!(!collection_3.contains(&IpAddr::from_str("0.0.0.0").unwrap()));
}

View File

@@ -14,29 +14,3 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl IpVersion {
pub(crate) const ALL: [IpVersion; 2] = [IpVersion::IPv4, IpVersion::IPv6];
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ip_version_display() {
for version in IpVersion::ALL {
match version {
IpVersion::IPv4 => assert_eq!(version.to_string(), "IPv4"),
IpVersion::IPv6 => assert_eq!(version.to_string(), "IPv6"),
}
}
}
#[test]
fn test_all_ip_versions_collection() {
assert_eq!(IpVersion::ALL.len(), 2);
assert_eq!(IpVersion::ALL.get(0).unwrap(), &IpVersion::IPv4);
assert_eq!(IpVersion::ALL.get(1).unwrap(), &IpVersion::IPv6);
}
}

View File

@@ -2,11 +2,11 @@
pub mod arp_type;
pub mod asn;
pub mod bogon;
pub mod byte_multiple;
pub mod capture_context;
pub mod config_device;
pub mod data_info;
pub mod data_info_host;
pub mod filters;
pub mod data_representation;
pub mod host;
pub mod host_data_states;
pub mod icmp_type;
@@ -17,7 +17,6 @@
pub mod my_device;
pub mod my_link_type;
pub mod packet_filters_fields;
pub mod port_collection;
pub mod protocol;
pub mod service;
pub mod service_query;

View File

@@ -1,11 +1,7 @@
use iced::Font;
use iced::widget::Column;
use pcap::Linktype;
use crate::gui::styles::text::TextType;
use crate::gui::types::message::Message;
use crate::Language;
use crate::translations::translations_3::link_type_translation;
use crate::{Language, StyleType};
/// Currently supported link types
#[derive(Copy, Clone, Default)]
@@ -16,6 +12,8 @@ pub enum MyLinkType {
Loop(Linktype),
IPv4(Linktype),
IPv6(Linktype),
LinuxSll(Linktype),
LinuxSll2(Linktype),
Unsupported(Linktype),
#[default]
NotYetAssigned,
@@ -34,6 +32,8 @@ pub fn from_pcap_link_type(link_type: Linktype) -> Self {
Linktype::LOOP => Self::Loop(link_type),
Linktype::IPV4 => Self::IPv4(link_type),
Linktype::IPV6 => Self::IPv6(link_type),
Linktype::LINUX_SLL => Self::LinuxSll(link_type),
Linktype::LINUX_SLL2 => Self::LinuxSll2(link_type),
_ => Self::Unsupported(link_type),
}
}
@@ -46,6 +46,8 @@ pub fn full_print_on_one_line(self, language: Language) -> String {
| Self::Loop(l)
| Self::IPv4(l)
| Self::IPv6(l)
| Self::LinuxSll(l)
| Self::LinuxSll2(l)
| Self::Unsupported(l) => {
format!(
"{}: {} ({})",
@@ -57,32 +59,4 @@ pub fn full_print_on_one_line(self, language: Language) -> String {
Self::NotYetAssigned => String::new(),
}
}
pub fn link_type_col<'a>(
self,
language: Language,
font: Font,
) -> Column<'a, Message, StyleType> {
match self {
Self::Null(l)
| Self::Ethernet(l)
| Self::RawIp(l)
| Self::Loop(l)
| Self::IPv4(l)
| Self::IPv6(l)
| Self::Unsupported(l) => {
let link_info = format!(
"{} ({})",
l.get_name().unwrap_or_else(|_| l.0.to_string()),
l.get_description().unwrap_or_else(|_| String::new())
);
TextType::highlighted_subtitle_with_desc(
link_type_translation(language),
&link_info,
font,
)
}
Self::NotYetAssigned => Column::new().height(0),
}
}
}

View File

@@ -1,180 +0,0 @@
use std::ops::RangeInclusive;
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq, Clone)]
pub(crate) struct PortCollection {
pub(crate) ports: Vec<u16>,
pub(crate) ranges: Vec<RangeInclusive<u16>>,
}
impl PortCollection {
const SEPARATOR: char = ',';
const RANGE_SEPARATOR: char = '-';
pub const PLACEHOLDER_STR: &'static str = "0-65535";
pub(crate) fn new(str: &str) -> Option<Self> {
let str = str.replace(' ', "");
if str.is_empty() {
return Some(Self::default());
}
let mut ports = Vec::new();
let mut ranges = Vec::new();
let objects: Vec<&str> = str.split(Self::SEPARATOR).collect();
for object in objects {
if object.contains(Self::RANGE_SEPARATOR) {
// port range
let mut subparts = object.split(Self::RANGE_SEPARATOR);
if subparts.clone().count() != 2 {
return None;
}
let (lower_str, upper_str) =
(subparts.next().unwrap_or(""), subparts.next().unwrap_or(""));
let lower_port = u16::from_str(lower_str).ok()?;
let upper_port = u16::from_str(upper_str).ok()?;
let range = RangeInclusive::new(lower_port, upper_port);
if range.is_empty() {
return None;
}
ranges.push(range);
} else {
// individual port
let port = u16::from_str(object).ok()?;
ports.push(port);
}
}
Some(Self { ports, ranges })
}
pub(crate) fn contains(&self, port: Option<u16>) -> bool {
// ignore port filter in case of ICMP or ARP
let Some(p) = port else {
return true;
};
for range in &self.ranges {
if range.contains(&p) {
return true;
}
}
self.ports.contains(&p)
}
}
impl Default for PortCollection {
fn default() -> Self {
PortCollection {
ports: vec![],
ranges: vec![RangeInclusive::new(u16::MIN, u16::MAX)],
}
}
}
#[cfg(test)]
mod tests {
use crate::networking::types::port_collection::PortCollection;
#[test]
fn test_default_collection_contains_everything() {
let collection = PortCollection::default();
assert!(collection.contains(Some(0)));
assert!(collection.contains(Some(1)));
assert!(collection.contains(Some(2)));
assert!(collection.contains(Some(80)));
assert!(collection.contains(Some(8080)));
assert!(collection.contains(Some(55333)));
assert!(collection.contains(Some(65535)));
}
#[test]
fn test_new_port_collections() {
assert_eq!(
PortCollection::new("0").unwrap(),
PortCollection {
ports: vec![0],
ranges: vec![]
}
);
assert_eq!(
PortCollection::new(" 0 ").unwrap(),
PortCollection {
ports: vec![0],
ranges: vec![]
}
);
assert_eq!(
PortCollection::new("1,2,3,4,999").unwrap(),
PortCollection {
ports: vec![1, 2, 3, 4, 999],
ranges: vec![]
}
);
assert_eq!(
PortCollection::new("1, 2, 3, 4, 900-999").unwrap(),
PortCollection {
ports: vec![1, 2, 3, 4],
ranges: vec![900..=999]
}
);
assert_eq!(
PortCollection::new("1 - 999").unwrap(),
PortCollection {
ports: vec![],
ranges: vec![1..=999]
}
);
assert_eq!(
PortCollection::new(" 1,2,10-20,3,4, 999-1200 ").unwrap(),
PortCollection {
ports: vec![1, 2, 3, 4],
ranges: vec![10..=20, 999..=1200]
}
);
}
#[test]
fn test_new_port_collections_invalid() {
assert_eq!(PortCollection::new("1,2,10-20,3,4,-1200"), None);
assert_eq!(PortCollection::new("1,2,10-20,3,4,999:1200"), None);
assert_eq!(PortCollection::new("1,2,10-20,3,4,999-1200,"), None);
assert_eq!(PortCollection::new("999-1"), None);
assert_eq!(PortCollection::new("1:999"), None);
assert_eq!(PortCollection::new("1-2-3"), None);
assert_eq!(PortCollection::new("1-2-"), None);
}
#[test]
fn test_port_collection_contains() {
let collection = PortCollection::new("1,2,25-30,55,101-117").unwrap();
assert!(collection.contains(Some(1)));
assert!(collection.contains(Some(2)));
assert!(collection.contains(Some(25)));
assert!(collection.contains(Some(27)));
assert!(collection.contains(Some(30)));
assert!(collection.contains(Some(55)));
assert!(collection.contains(Some(101)));
assert!(collection.contains(Some(109)));
assert!(collection.contains(Some(117)));
assert!(!collection.contains(Some(4)));
assert!(!collection.contains(Some(24)));
assert!(!collection.contains(Some(31)));
assert!(!collection.contains(Some(100)));
assert!(!collection.contains(Some(118)));
assert!(!collection.contains(Some(8080)));
}
}

View File

@@ -19,33 +19,3 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl Protocol {
pub const ALL: [Protocol; 4] = [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP];
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_protocol_display() {
for protocol in Protocol::ALL {
match protocol {
Protocol::TCP => assert_eq!(protocol.to_string(), "TCP"),
Protocol::UDP => assert_eq!(protocol.to_string(), "UDP"),
Protocol::ICMP => assert_eq!(protocol.to_string(), "ICMP"),
Protocol::ARP => assert_eq!(protocol.to_string(), "ARP"),
}
}
}
#[test]
fn test_all_protocols_collection() {
assert_eq!(Protocol::ALL.len(), 4);
assert_eq!(Protocol::ALL.get(0).unwrap(), &Protocol::TCP);
assert_eq!(Protocol::ALL.get(1).unwrap(), &Protocol::UDP);
assert_eq!(Protocol::ALL.get(2).unwrap(), &Protocol::ICMP);
assert_eq!(Protocol::ALL.get(3).unwrap(), &Protocol::ARP);
}
}

View File

@@ -1,8 +1,8 @@
use crate::InfoTraffic;
use crate::chart::types::chart_type::ChartType;
use crate::networking::types::capture_context::CaptureSource;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::service::Service;
use crate::notifications::types::logged_notification::{
@@ -31,8 +31,8 @@ pub fn notify_and_log(
let data_info = info_traffic_msg.tot_data_info;
// data threshold
if let Some(threshold) = notifications.data_notification.threshold {
let chart_type = notifications.data_notification.chart_type;
if data_info.tot_data(chart_type) > u128::from(threshold) {
let data_repr = notifications.data_notification.data_repr;
if data_info.tot_data(data_repr) > u128::from(threshold) {
//log this notification
logged_notifications.1 += 1;
if logged_notifications.0.len() >= 30 {
@@ -43,13 +43,13 @@ pub fn notify_and_log(
.push_front(LoggedNotification::DataThresholdExceeded(
DataThresholdExceeded {
id: logged_notifications.1,
chart_type,
data_repr,
threshold: notifications.data_notification.previous_threshold,
data_info,
timestamp: get_formatted_timestamp(timestamp),
is_expanded: false,
hosts: hosts_list(info_traffic_msg, chart_type),
services: services_list(info_traffic_msg, chart_type),
hosts: hosts_list(info_traffic_msg, data_repr),
services: services_list(info_traffic_msg, data_repr),
},
));
if sound_to_play.eq(&Sound::None) {
@@ -98,7 +98,7 @@ pub fn notify_and_log(
logged_notifications.1 - emitted_notifications_prev
}
fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Host, DataInfoHost)> {
fn hosts_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> Vec<(Host, DataInfoHost)> {
let mut hosts: Vec<(Host, DataInfoHost)> = info_traffic_msg
.hosts
.iter()
@@ -106,7 +106,7 @@ fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Hos
.collect();
hosts.sort_by(|(_, a), (_, b)| {
a.data_info
.compare(&b.data_info, SortType::Descending, chart_type)
.compare(&b.data_info, SortType::Descending, data_repr)
});
let n_entry = min(hosts.len(), 4);
hosts
@@ -117,17 +117,14 @@ fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Hos
.collect()
}
fn services_list(
info_traffic_msg: &InfoTraffic,
chart_type: ChartType,
) -> Vec<(Service, DataInfo)> {
fn services_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> Vec<(Service, DataInfo)> {
let mut services: Vec<(Service, DataInfo)> = info_traffic_msg
.services
.iter()
.filter(|(service, _)| service != &&Service::NotApplicable)
.map(|(s, data)| (*s, *data))
.collect();
services.sort_by(|(_, a), (_, b)| a.compare(b, SortType::Descending, chart_type));
services.sort_by(|(_, a), (_, b)| a.compare(b, SortType::Descending, data_repr));
let n_entry = min(services.len(), 4);
services
.get(..n_entry)

View File

@@ -1,6 +1,6 @@
use crate::chart::types::chart_type::ChartType;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::service::Service;
@@ -38,7 +38,7 @@ pub fn expand(&mut self, expand: bool) {
#[derive(Clone)]
pub struct DataThresholdExceeded {
pub(crate) id: usize,
pub(crate) chart_type: ChartType,
pub(crate) data_repr: DataRepr,
pub(crate) threshold: u64,
pub(crate) data_info: DataInfo,
pub(crate) timestamp: String,

View File

@@ -1,11 +1,12 @@
use serde::{Deserialize, Serialize};
use crate::ByteMultiple;
use crate::chart::types::chart_type::ChartType;
use crate::networking::types::data_representation::DataRepr;
use crate::notifications::types::sound::Sound;
/// Used to contain the notifications configuration set by the user
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Debug)]
#[serde(default)]
pub struct Notifications {
pub volume: u8,
pub data_notification: DataNotification,
@@ -32,9 +33,10 @@ pub enum Notification {
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)]
#[serde(default)]
pub struct DataNotification {
/// Data representation
pub chart_type: ChartType,
pub data_repr: DataRepr,
/// Threshold of received + sent bytes; if exceeded a notification is emitted
pub threshold: Option<u64>,
/// B, KB, MB or GB
@@ -48,7 +50,7 @@ pub struct DataNotification {
impl Default for DataNotification {
fn default() -> Self {
DataNotification {
chart_type: ChartType::Bytes,
data_repr: DataRepr::Bytes,
threshold: None,
byte_multiple: ByteMultiple::KB,
sound: Sound::Pop,
@@ -101,6 +103,7 @@ pub fn from(value: &str, existing: Option<Self>) -> Self {
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)]
#[serde(default)]
pub struct FavoriteNotification {
/// Flag to determine if this notification is enabled
pub notify_on_favorite: bool,

View File

@@ -4,10 +4,11 @@
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::report::types::sort_type::SortType;
use crate::{ChartType, InfoTraffic, ReportSortType, Service, Sniffer};
use crate::{InfoTraffic, Service, Sniffer};
/// Return the elements that satisfy the search constraints and belong to the given page,
/// and the total number of elements which satisfy the search constraints,
@@ -46,24 +47,12 @@ pub fn get_searched_entries(
})
.collect();
all_results.sort_by(|&(_, a), &(_, b)| match sniffer.report_sort_type {
ReportSortType {
byte_sort,
packet_sort: SortType::Neutral,
} => match byte_sort {
SortType::Ascending => a.transmitted_bytes.cmp(&b.transmitted_bytes),
SortType::Descending => b.transmitted_bytes.cmp(&a.transmitted_bytes),
SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp),
},
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort,
} => match packet_sort {
SortType::Ascending => a.transmitted_packets.cmp(&b.transmitted_packets),
SortType::Descending => b.transmitted_packets.cmp(&a.transmitted_packets),
SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp),
},
_ => b.final_timestamp.cmp(&a.final_timestamp),
all_results.sort_by(|&(_, a), &(_, b)| {
a.compare(
b,
sniffer.conf.report_sort_type,
sniffer.traffic_chart.data_repr,
)
});
let upper_bound = min(sniffer.page_number * 20, all_results.len());
@@ -82,12 +71,12 @@ pub fn get_searched_entries(
pub fn get_host_entries(
info_traffic: &InfoTraffic,
chart_type: ChartType,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<(Host, DataInfoHost)> {
let mut sorted_vec: Vec<(&Host, &DataInfoHost)> = info_traffic.hosts.iter().collect();
sorted_vec.sort_by(|&(_, a), &(_, b)| a.data_info.compare(&b.data_info, sort_type, chart_type));
sorted_vec.sort_by(|&(_, a), &(_, b)| a.data_info.compare(&b.data_info, sort_type, data_repr));
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]
@@ -98,7 +87,7 @@ pub fn get_host_entries(
pub fn get_service_entries(
info_traffic: &InfoTraffic,
chart_type: ChartType,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<(Service, DataInfo)> {
let mut sorted_vec: Vec<(&Service, &DataInfo)> = info_traffic
@@ -107,7 +96,7 @@ pub fn get_service_entries(
.filter(|(service, _)| service != &&Service::NotApplicable)
.collect();
sorted_vec.sort_by(|&(_, a), &(_, b)| a.compare(b, sort_type, chart_type));
sorted_vec.sort_by(|&(_, a), &(_, b)| a.compare(b, sort_type, data_repr));
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]

View File

@@ -1,4 +1,3 @@
pub mod report_col;
pub mod report_sort_type;
pub mod search_parameters;
pub mod sort_type;

View File

@@ -1,10 +1,8 @@
use crate::ByteMultiple;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::report::types::search_parameters::FilterInputType;
use crate::translations::translations::{
address_translation, bytes_translation, packets_translation, protocol_translation,
};
use crate::translations::translations::{address_translation, protocol_translation};
use crate::translations::translations_2::{destination_translation, source_translation};
use crate::translations::translations_3::{port_translation, service_translation};
use crate::translations::types::language::Language;
@@ -25,36 +23,30 @@ pub enum ReportCol {
DstPort,
Proto,
Service,
Bytes,
Packets,
Data,
}
impl ReportCol {
pub(crate) const ALL: [ReportCol; 8] = [
pub(crate) const ALL: [ReportCol; 7] = [
ReportCol::SrcIp,
ReportCol::SrcPort,
ReportCol::DstIp,
ReportCol::DstPort,
ReportCol::Proto,
ReportCol::Service,
ReportCol::Bytes,
ReportCol::Packets,
ReportCol::Data,
];
pub(crate) const FILTER_COLUMNS_WIDTH: f32 = 4.0 * SMALL_COL_WIDTH + 2.0 * LARGE_COL_WIDTH;
pub(crate) fn get_title(&self, language: Language) -> String {
pub(crate) fn get_title(&self, language: Language, data_repr: DataRepr) -> String {
match self {
ReportCol::SrcIp | ReportCol::DstIp => address_translation(language).to_string(),
ReportCol::SrcPort | ReportCol::DstPort => port_translation(language).to_string(),
ReportCol::Proto => protocol_translation(language).to_string(),
ReportCol::Service => service_translation(language).to_string(),
ReportCol::Bytes => {
let mut str = bytes_translation(language).to_string();
str.remove(0).to_uppercase().to_string() + &str
}
ReportCol::Packets => {
let mut str = packets_translation(language).to_string();
ReportCol::Data => {
let mut str = data_repr.get_label(language).to_string();
str.remove(0).to_uppercase().to_string() + &str
}
}
@@ -72,7 +64,12 @@ pub(crate) fn get_title_direction_info(&self, language: Language) -> String {
}
}
pub(crate) fn get_value(&self, key: &AddressPortPair, val: &InfoAddressPortPair) -> String {
pub(crate) fn get_value(
&self,
key: &AddressPortPair,
val: &InfoAddressPortPair,
data_repr: DataRepr,
) -> String {
match self {
ReportCol::SrcIp => key.address1.to_string(),
ReportCol::SrcPort => {
@@ -92,8 +89,7 @@ pub(crate) fn get_value(&self, key: &AddressPortPair, val: &InfoAddressPortPair)
}
ReportCol::Proto => key.protocol.to_string(),
ReportCol::Service => val.service.to_string(),
ReportCol::Bytes => ByteMultiple::formatted_string(val.transmitted_bytes),
ReportCol::Packets => val.transmitted_packets.to_string(),
ReportCol::Data => data_repr.formatted_string(val.transmitted_data(data_repr)),
}
}
@@ -126,7 +122,7 @@ pub(crate) fn get_filter_input_type(&self) -> FilterInputType {
ReportCol::DstPort => FilterInputType::PortDst,
ReportCol::Proto => FilterInputType::Proto,
ReportCol::Service => FilterInputType::Service,
ReportCol::Bytes | ReportCol::Packets => FilterInputType::Country, // just to not panic...
ReportCol::Data => FilterInputType::Country, // just to not panic...
}
}
}

View File

@@ -1,175 +0,0 @@
use std::fmt::Debug;
use iced::widget::Text;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::types::style_type::StyleType;
use crate::report::types::report_col::ReportCol;
use crate::report::types::sort_type::SortType;
use crate::utils::types::icon::Icon;
/// Struct representing the possible kinds of sort for displayed relevant connections.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ReportSortType {
pub byte_sort: SortType,
pub packet_sort: SortType,
}
impl ReportSortType {
pub fn next_sort(self, report_col: &ReportCol) -> Self {
match report_col {
ReportCol::Bytes => Self {
byte_sort: self.byte_sort.next_sort(),
packet_sort: SortType::Neutral,
},
ReportCol::Packets => Self {
byte_sort: SortType::Neutral,
packet_sort: self.packet_sort.next_sort(),
},
_ => Self::default(),
}
}
pub fn icon<'a>(self, report_col: &ReportCol) -> Text<'a, StyleType> {
match report_col {
ReportCol::Bytes => self.byte_sort.icon(),
ReportCol::Packets => self.packet_sort.icon(),
_ => Icon::SortNeutral.to_text(),
}
}
pub fn button_type(self, report_col: &ReportCol) -> ButtonType {
match report_col {
ReportCol::Bytes => self.byte_sort.button_type(),
ReportCol::Packets => self.packet_sort.button_type(),
_ => ButtonType::SortArrows,
}
}
}
#[cfg(test)]
mod tests {
use crate::report::types::report_col::ReportCol;
use crate::report::types::report_sort_type::ReportSortType;
use crate::report::types::sort_type::SortType;
#[test]
fn test_next_report_sort() {
let mut sort = ReportSortType::default();
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Neutral
}
);
sort = sort.next_sort(&ReportCol::Packets);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Descending
}
);
sort = sort.next_sort(&ReportCol::Packets);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Ascending
}
);
sort = sort.next_sort(&ReportCol::Packets);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Neutral
}
);
sort = sort.next_sort(&ReportCol::Packets);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Descending
}
);
sort = sort.next_sort(&ReportCol::Bytes);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Descending,
packet_sort: SortType::Neutral
}
);
sort = sort.next_sort(&ReportCol::Packets);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Descending
}
);
sort = sort.next_sort(&ReportCol::Bytes);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Descending,
packet_sort: SortType::Neutral
}
);
sort = sort.next_sort(&ReportCol::Bytes);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Ascending,
packet_sort: SortType::Neutral
}
);
sort = sort.next_sort(&ReportCol::Packets);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Descending
}
);
sort = sort.next_sort(&ReportCol::Bytes);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Descending,
packet_sort: SortType::Neutral
}
);
sort = sort.next_sort(&ReportCol::Bytes);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Ascending,
packet_sort: SortType::Neutral
}
);
sort = sort.next_sort(&ReportCol::Bytes);
assert_eq!(
sort,
ReportSortType {
byte_sort: SortType::Neutral,
packet_sort: SortType::Neutral
}
);
}
}

View File

@@ -1,10 +1,10 @@
use iced::widget::Text;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::types::style_type::StyleType;
use crate::utils::types::icon::Icon;
use iced::widget::Text;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum SortType {
Ascending,
Descending,

View File

@@ -3,4 +3,5 @@
pub mod translations_2;
pub mod translations_3;
pub mod translations_4;
pub mod translations_5;
pub mod types;

View File

@@ -5,33 +5,33 @@
use crate::StyleType;
use crate::translations::types::language::Language;
pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType> {
Text::new(match language {
Language::EN => "Select network adapter to inspect",
Language::IT => "Seleziona la scheda di rete da ispezionare",
Language::FR => "Sélectionnez une carte réseau à inspecter",
Language::ES => "Seleccione el adaptador de red que desea inspeccionar",
Language::PL => "Wybierz adapter sieciowy do inspekcji",
Language::DE => "Wähle einen Netzwerkadapter zum überwachen aus",
Language::UK => "Виберіть мережевий адаптер для перевірки",
Language::ZH => "选择需要监控的网络适配器",
Language::ZH_TW => "選取要檢視的網路介面卡",
Language::RO => "Selectați adaptor de rețea pentru a inspecta",
Language::KO => "검사할 네트워크 어댑터 선택",
Language::TR => "İncelemek için bir ağ adaptörü seçiniz",
Language::RU => "Выберите сетевой адаптер для инспекции",
Language::PT => "Selecione o adaptador de rede a inspecionar",
Language::EL => "Επίλεξε τον προσαρμογέα δικτύου για επιθεώρηση",
// Language::FA => "مبدل شبکه را برای بازرسی انتخاب کنید",
Language::SV => "Välj nätverksadapter att inspektera",
Language::FI => "Valitse tarkasteltava verkkosovitin",
Language::JA => "使用するネットワーク アダプターを選択してください",
Language::UZ => "Tekshirish uchun tarmoq adapterini tanlang",
Language::VI => "Hãy chọn network adapter để quan sát",
Language::ID => "Pilih Adapter Jaringan yang ingin dicek",
Language::NL => "Selecteer netwerkadapter om te inspecteren",
})
}
// pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType> {
// Text::new(match language {
// Language::EN => "Select network adapter to inspect",
// Language::IT => "Seleziona la scheda di rete da ispezionare",
// Language::FR => "Sélectionnez une carte réseau à inspecter",
// Language::ES => "Seleccione el adaptador de red que desea inspeccionar",
// Language::PL => "Wybierz adapter sieciowy do inspekcji",
// Language::DE => "Wähle einen Netzwerkadapter zum überwachen aus",
// Language::UK => "Виберіть мережевий адаптер для перевірки",
// Language::ZH => "选择需要监控的网络适配器",
// Language::ZH_TW => "選取要檢視的網路介面卡",
// Language::RO => "Selectați adaptor de rețea pentru a inspecta",
// Language::KO => "검사할 네트워크 어댑터 선택",
// Language::TR => "İncelemek için bir ağ adaptörü seçiniz",
// Language::RU => "Выберите сетевой адаптер для инспекции",
// Language::PT => "Selecione o adaptador de rede a inspecionar",
// Language::EL => "Επιλέξτε τον προσαρμογέα δικτύου για επιθεώρηση",
// // Language::FA => "مبدل شبکه را برای بازرسی انتخاب کنید",
// Language::SV => "Välj nätverksadapter att inspektera",
// Language::FI => "Valitse tarkasteltava verkkosovitin",
// Language::JA => "使用するネットワーク アダプターを選択してください",
// Language::UZ => "Tekshirish uchun tarmoq adapterini tanlang",
// Language::VI => "Hãy chọn network adapter để quan sát",
// Language::ID => "Pilih Adapter Jaringan yang ingin dicek",
// Language::NL => "Selecteer netwerkadapter om te inspecteren",
// })
// }
// pub fn application_protocol_translation(language: Language) -> &'static str {
// match language {
@@ -60,33 +60,33 @@ pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType
// }
// }
pub fn select_filters_translation<'a>(language: Language) -> Text<'a, StyleType> {
Text::new(match language {
Language::EN => "Select filters to be applied on network traffic",
Language::IT => "Seleziona i filtri da applicare al traffico di rete",
Language::FR => "Sélectionnez les filtres à appliquer sur le traffic réseau",
Language::ES => "Seleccionar los filtros que se aplicarán al tráfico de red",
Language::PL => "Wybierz filtry, które mają być zastosowane na ruchu sieciowym",
Language::DE => "Wähle die Filter, die auf den Netzwerkverkehr angewendet werden sollen",
Language::UK => "Виберіть фільтри, які мають бути застосовані до мережевого руху",
Language::ZH => "选择需要监控的目标",
Language::ZH_TW => "選取要套用於網路流量的篩選器",
Language::RO => "Selectați filtre pentru traficul de rețea",
Language::KO => "네트워크 트레픽에 적용할 필터 선택",
Language::TR => "Ağ trafiğine uygulanacak filtreleri seçiniz",
Language::RU => "Выберите фильтры для применения к сетевому трафику",
Language::PT => "Selecione os filtros a serem aplicados no tráfego de rede",
Language::EL => "Επίλεξε τα φίλτρα για εφαρμογή στην κίνηση του δικτύου",
// Language::FA => "صافی ها را جهت اعمال بر آمد و شد شبکه انتخاب کنید",
Language::SV => "Välj filtren som ska appliceras på nätverkstrafiken",
Language::FI => "Valitse suodattimet verkkoliikenteelle",
Language::JA => "トラフィックに適用するフィルターを選択してください",
Language::UZ => "Tarmoq trafigiga qo'llaniladigan filtrlarni tanlang",
Language::VI => "Hãy chọn bộ lọc cho lưu lượng mạng",
Language::ID => "Pilih filter yang ingin dipasang dilalulintas jaringan",
Language::NL => "Selecteer filters om toe te passen op netwerkverkeer",
})
}
// pub fn select_filters_translation<'a>(language: Language) -> Text<'a, StyleType> {
// Text::new(match language {
// Language::EN => "Select filters to be applied on network traffic",
// Language::IT => "Seleziona i filtri da applicare al traffico di rete",
// Language::FR => "Sélectionnez les filtres à appliquer sur le traffic réseau",
// Language::ES => "Seleccionar los filtros que se aplicarán al tráfico de red",
// Language::PL => "Wybierz filtry, które mają być zastosowane na ruchu sieciowym",
// Language::DE => "Wähle die Filter, die auf den Netzwerkverkehr angewendet werden sollen",
// Language::UK => "Виберіть фільтри, які мають бути застосовані до мережевого руху",
// Language::ZH => "选择需要监控的目标",
// Language::ZH_TW => "選取要套用於網路流量的篩選器",
// Language::RO => "Selectați filtre pentru traficul de rețea",
// Language::KO => "네트워크 트레픽에 적용할 필터 선택",
// Language::TR => "Ağ trafiğine uygulanacak filtreleri seçiniz",
// Language::RU => "Выберите фильтры для применения к сетевому трафику",
// Language::PT => "Selecione os filtros a serem aplicados no tráfego de rede",
// Language::EL => "Επιλέξτε τα φίλτρα που θα εφαρμοστούν στην κίνηση του δικτύου",
// // Language::FA => "صافی ها را جهت اعمال بر آمد و شد شبکه انتخاب کنید",
// Language::SV => "Välj filtren som ska appliceras på nätverkstrafiken",
// Language::FI => "Valitse suodattimet verkkoliikenteelle",
// Language::JA => "トラフィックに適用するフィルターを選択してください",
// Language::UZ => "Tarmoq trafigiga qo'llaniladigan filtrlarni tanlang",
// Language::VI => "Hãy chọn bộ lọc cho lưu lượng mạng",
// Language::ID => "Pilih filter yang ingin dipasang dilalulintas jaringan",
// Language::NL => "Selecteer filters om toe te passen op netwerkverkeer",
// })
// }
pub fn start_translation(language: Language) -> &'static str {
match language {
@@ -163,33 +163,33 @@ pub fn addresses_translation(language: Language) -> &'static str {
}
}
pub fn ip_version_translation(language: Language) -> &'static str {
match language {
Language::EN => "IP version",
Language::IT => "Versione IP",
Language::FR => "Version IP",
Language::ES => "Versión IP",
Language::PL => "Wersja IP",
Language::DE => "IP Version",
Language::UK => "Версія IP",
Language::ZH => "目标IP协议版本",
Language::ZH_TW => "IP 版本",
Language::RO => "Versiune IP",
Language::KO => "IP 버전",
Language::TR => "IP versiyonu",
Language::RU => "Версия IP",
Language::PT => "Versão de IP",
Language::EL => "Έκδοση IP",
// Language::FA => "نسخهٔ IP",
Language::SV => "IP-version",
Language::FI => "IP-versio",
Language::JA => "IP バージョン",
Language::UZ => "IP versiyasi",
Language::VI => "Phiên bản IP",
Language::ID => "Versi IP",
Language::NL => "IP versie",
}
}
// pub fn ip_version_translation(language: Language) -> &'static str {
// match language {
// Language::EN => "IP version",
// Language::IT => "Versione IP",
// Language::FR => "Version IP",
// Language::ES => "Versión IP",
// Language::PL => "Wersja IP",
// Language::DE => "IP Version",
// Language::UK => "Версія IP",
// Language::ZH => "目标IP协议版本",
// Language::ZH_TW => "IP 版本",
// Language::RO => "Versiune IP",
// Language::KO => "IP 버전",
// Language::TR => "IP versiyonu",
// Language::RU => "Версия IP",
// Language::PT => "Versão de IP",
// Language::EL => "Έκδοση IP",
// // Language::FA => "نسخهٔ IP",
// Language::SV => "IP-version",
// Language::FI => "IP-versio",
// Language::JA => "IP バージョン",
// Language::UZ => "IP versiyasi",
// Language::VI => "Phiên bản IP",
// Language::ID => "Versi IP",
// Language::NL => "IP versie",
// }
// }
// pub fn transport_protocol_translation(language: Language) -> &'static str {
// match language {
@@ -361,7 +361,7 @@ pub fn ask_quit_translation<'a>(language: Language) -> Text<'a, StyleType> {
Language::TR => "Bu analizden çıkmak istediğine emin misin?",
Language::RU => "Вы уверены, что хотите выйти из текущего анализа?",
Language::PT => "Tem a certeza que deseja sair desta análise?",
Language::EL => "Είσαι σίγουρος ότι θες να κλείσεις την ανάλυση;",
Language::EL => "Είστε βέβαιοι ότι θέλετε να τερματίσετε την ανάλυση;",
// Language::FA => "آیا مطمئن هستید می خواهید از این تحلیل خارج شوید؟",
Language::SV => "Är du säker på att du vill avsluta analysen?",
Language::FI => "Haluatko varmasti lopettaa analyysin?",
@@ -417,7 +417,7 @@ pub fn ask_clear_all_translation<'a>(language: Language) -> Text<'a, StyleType>
Language::TR => "Bildirimleri temizlemek istediğine emin misin?",
Language::RU => "Вы уверены, что хотите удлить все уведомления?",
Language::PT => "Tem a certeza que deseja eliminar as notificações?",
Language::EL => "Είσαι σίγουρος ότι θες να κάνεις εκκαθάριση των ειδοποιήσεων;",
Language::EL => "Είστε βέβαιοι ότι θέλετε να εκκαθαρίσετε τις ειδοποιήσεις;",
// Language::FA => "آیا مطمئن هستید می خواهید اعلان ها را پاک کنید؟",
Language::SV => "Är du säker på att du vill radera notifikationerna?",
Language::FI => "Haluatko varmasti tyhjentää ilmoitukset?",
@@ -751,124 +751,124 @@ pub fn waiting_translation<'a>(language: Language, adapter: &str) -> Text<'a, St
})
}
#[allow(clippy::too_many_lines)]
pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text<'a, StyleType> {
Text::new(match language {
Language::EN => format!(
"Total intercepted packets: {observed}\n\n\
Filtered packets: 0\n\n\
Some packets have been intercepted, but still none has been selected according to the filters you specified..."
),
Language::IT => format!(
"Totale pacchetti intercettati: {observed}\n\n\
Pacchetti filtrati: 0\n\n\
Alcuni pacchetti sono stati intercettati, ma ancora nessuno è stato selezionato secondo i filtri specificati..."
),
Language::FR => format!(
"Total des paquets interceptés: {observed}\n\n\
Paquets filtrés: 0\n\n\
Certains paquets ont é interceptés, mais aucun ne satisfait les critères des filtres sélectionnés..."
),
Language::ES => format!(
"Total de paquetes interceptados: {observed}\n\n\
Paquetes filtrados: 0\n\n\
Se interceptaron algunos paquetes, pero ninguno de ellos cumplía los criterios de los filtros seleccionados..."
),
Language::PL => format!(
"Suma przechwyconych pakietów: {observed}\n\n\
Przefiltrowane pakiety: 0\n\n\
Niektóre pakiety zostały przechwycone, ale żaden nie został wybrany zgodnie z wskazanymi filtrami..."
),
Language::DE => format!(
"Anzahl der empfangenen Pakete: {observed}\n\n\
Gefilterte Pakete: 0\n\n\
Ein Paar Pakete wurden empfangen, aber es entsprechen noch keine den gewählten Filtern..."
),
Language::UK => format!(
"Сума перехоплених пакетів: {observed}\n\n\
Відфільтровані пакети: 0\n\n\
Деякі пакети були перехоплені, але жоден з них не був вибраний відповідно до вказаних фільтрів..."
),
Language::ZH => format!(
"监测到的数据包总数: {observed}\n\n\
: 0\n\n\
, ......"
),
Language::ZH_TW => format!(
"攔截到的封包總數: {observed}\n\n\
0\n\n\
..."
),
Language::RO => format!(
"Total pachete interceptate: {observed}\n\n\
Pachete filtrate: 0\n\n\
Unele pachete au fost interceptate, dar încă niciunul nu a fost selectat conform filtrelor pe care le-ați specificat..."
),
Language::KO => format!(
"감지한 총 패킷: {observed}\n\n\
: 0\n\n\
, ..."
),
Language::TR => format!(
"Toplam yakalanan paketler: {observed}\n\n\
Filterelenen paketler: 0\n\n\
Bazı paketler yakalandı, fakat belirttiğiniz filtrelere göre hiç biri seçilmedi..."
),
Language::RU => format!(
"Всего пакетов перехвачено: {observed}\n\n\
Фильтровано пакетов: 0\n\n\
Сетевые пакеты были перехвачены, но ни один из них не соответствует заданным фильтрам..."
),
Language::PT => format!(
"Total de pacotes interceptados: {observed}\n\n\
Pacotes filtrados: 0\n\n\
Alguns pacotes foram interceptados, mas nenhum deles foi selecionado de acordo com os filtros especificados..."
),
Language::EL => format!(
"Συνολικά αναχαιτισμένα πακέτα: {observed}\n\n\
Φιλτραρισμένα πακέτα: 0\n\n\
Κάποια από τα πακέτα έχουν αναχαιτιστεί, αλλά κανένα ακόμη δεν έχει επιλεγεί σύμφωνα με τα φίλτρα που επέλεξες..."
),
// Language::FA => format!("مجموع بسته های رهگیری شده: {observed}\n\n\
// بسته های صاف شده: 0\n\n\
// شماری از بسته ها رهگیری شده اند، ولی هنوز هیچ کدام بر اساس صافی تعیین شده شما انتخاب نشده اند..."),
Language::SV => format!(
"Antal fångade paket: {observed}\n\n\
Filtrerade paket: 0\n\n\
Några paket har fångats, men än har inget valts enligt de angivna filtren ..."
),
Language::FI => format!(
"Siepattuja paketteja yhteensä: {observed}\n\n\
Suodatettuja paketteja: 0\n\n\
Joitakin paketteja on siepattu, mutta yhtäkään ei ole valittu määrittämiesi suodattimien mukaan..."
),
Language::JA => format!(
"取得したパケット数: {observed}\n\n\
: 0\n\n\
..."
),
Language::UZ => format!(
"Jami ushlangan paketlar: {observed}\n\n\
Filtrlangan paketlar: 0\n\n\
Tarmoq paketlari ushlandi, lekin ularning hech biri belgilangan filtrlarga mos kelmadi..."
),
Language::VI => format!(
"Tổng số gói tin bị chặn: {observed}\n\n\
Các gói tin đã lọc: 0\n\n\
Một số gói đã bị chặn, nhưng vẫn chưa gói tin nào đưc bắt theo bộ lọc bạn đã chọn..."
),
Language::ID => format!(
"Total paket yang dilacak: {observed}\n\n\
Paket yg difilter: 0\n\n\
Beberapa paket dilacak, tetapi tidak ada yg terlihat berdasarkan filter yang kamu pilih..."
),
Language::NL => format!(
"Totaal aantal onderschepte pakketten: {observed}\n\n\
Gefilterde pakketten: 0\n\n\
Er zijn enkele pakketten onderschept, maar nog geen enkele is geselecteerd volgens de filters die je hebt opgegeven..."
),
})
}
// #[allow(clippy::too_many_lines)]
// pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text<'a, StyleType> {
// Text::new(match language {
// Language::EN => format!(
// "Total intercepted packets: {observed}\n\n\
// Filtered packets: 0\n\n\
// Some packets have been intercepted, but still none has been selected according to the filters you specified..."
// ),
// Language::IT => format!(
// "Totale pacchetti intercettati: {observed}\n\n\
// Pacchetti filtrati: 0\n\n\
// Alcuni pacchetti sono stati intercettati, ma ancora nessuno è stato selezionato secondo i filtri specificati..."
// ),
// Language::FR => format!(
// "Total des paquets interceptés: {observed}\n\n\
// Paquets filtrés: 0\n\n\
// Certains paquets ont été interceptés, mais aucun ne satisfait les critères des filtres sélectionnés..."
// ),
// Language::ES => format!(
// "Total de paquetes interceptados: {observed}\n\n\
// Paquetes filtrados: 0\n\n\
// Se interceptaron algunos paquetes, pero ninguno de ellos cumplía los criterios de los filtros seleccionados..."
// ),
// Language::PL => format!(
// "Suma przechwyconych pakietów: {observed}\n\n\
// Przefiltrowane pakiety: 0\n\n\
// Niektóre pakiety zostały przechwycone, ale żaden nie został wybrany zgodnie z wskazanymi filtrami..."
// ),
// Language::DE => format!(
// "Anzahl der empfangenen Pakete: {observed}\n\n\
// Gefilterte Pakete: 0\n\n\
// Ein Paar Pakete wurden empfangen, aber es entsprechen noch keine den gewählten Filtern..."
// ),
// Language::UK => format!(
// "Сума перехоплених пакетів: {observed}\n\n\
// Відфільтровані пакети: 0\n\n\
// Деякі пакети були перехоплені, але жоден з них не був вибраний відповідно до вказаних фільтрів..."
// ),
// Language::ZH => format!(
// "监测到的数据包总数: {observed}\n\n\
// 目标数据包总数: 0\n\n\
// 当前已监测到一些数据包, 但其中并未包含您的目标数据包......"
// ),
// Language::ZH_TW => format!(
// "攔截到的封包總數: {observed}\n\n\
// 已篩選封包: 0\n\n\
// 已攔截到部分封包,但尚未有任何封包符合您指定的篩選條件..."
// ),
// Language::RO => format!(
// "Total pachete interceptate: {observed}\n\n\
// Pachete filtrate: 0\n\n\
// Unele pachete au fost interceptate, dar încă niciunul nu a fost selectat conform filtrelor pe care le-ați specificat..."
// ),
// Language::KO => format!(
// "감지한 총 패킷: {observed}\n\n\
// 필터링된 패킷: 0\n\n\
// 일부 패킷이 감지되었지만, 지정한 필터에 따라 선택되지 않았습니다..."
// ),
// Language::TR => format!(
// "Toplam yakalanan paketler: {observed}\n\n\
// Filterelenen paketler: 0\n\n\
// Bazı paketler yakalandı, fakat belirttiğiniz filtrelere göre hiç biri seçilmedi..."
// ),
// Language::RU => format!(
// "Всего пакетов перехвачено: {observed}\n\n\
// Фильтровано пакетов: 0\n\n\
// Сетевые пакеты были перехвачены, но ни один из них не соответствует заданным фильтрам..."
// ),
// Language::PT => format!(
// "Total de pacotes interceptados: {observed}\n\n\
// Pacotes filtrados: 0\n\n\
// Alguns pacotes foram interceptados, mas nenhum deles foi selecionado de acordo com os filtros especificados..."
// ),
// Language::EL => format!(
// "Συνολικά αναχαιτισμένα πακέτα: {observed}\n\n\
// Φιλτραρισμένα πακέτα: 0\n\n\
// Κάποια από τα πακέτα έχουν αναχαιτιστεί, αλλά κανένα ακόμη δεν έχει επιλεγεί σύμφωνα με τα φίλτρα που επέλεξες..."
// ),
// // Language::FA => format!("مجموع بسته های رهگیری شده: {observed}\n\n\
// // بسته های صاف شده: 0\n\n\
// // شماری از بسته ها رهگیری شده اند، ولی هنوز هیچ کدام بر اساس صافی تعیین شده شما انتخاب نشده اند..."),
// Language::SV => format!(
// "Antal fångade paket: {observed}\n\n\
// Filtrerade paket: 0\n\n\
// Några paket har fångats, men än har inget valts enligt de angivna filtren ..."
// ),
// Language::FI => format!(
// "Siepattuja paketteja yhteensä: {observed}\n\n\
// Suodatettuja paketteja: 0\n\n\
// Joitakin paketteja on siepattu, mutta yhtäkään ei ole valittu määrittämiesi suodattimien mukaan..."
// ),
// Language::JA => format!(
// "取得したパケット数: {observed}\n\n\
// フィルター後のパケット数: 0\n\n\
// パケットは取得できていますが、設定されたフィルタリングにより表示されません..."
// ),
// Language::UZ => format!(
// "Jami ushlangan paketlar: {observed}\n\n\
// Filtrlangan paketlar: 0\n\n\
// Tarmoq paketlari ushlandi, lekin ularning hech biri belgilangan filtrlarga mos kelmadi..."
// ),
// Language::VI => format!(
// "Tổng số gói tin bị chặn: {observed}\n\n\
// Các gói tin đã lọc: 0\n\n\
// Một số gói đã bị chặn, nhưng vẫn chưa có gói tin nào được bắt theo bộ lọc bạn đã chọn..."
// ),
// Language::ID => format!(
// "Total paket yang dilacak: {observed}\n\n\
// Paket yg difilter: 0\n\n\
// Beberapa paket dilacak, tetapi tidak ada yg terlihat berdasarkan filter yang kamu pilih..."
// ),
// Language::NL => format!(
// "Totaal aantal onderschepte pakketten: {observed}\n\n\
// Gefilterde pakketten: 0\n\n\
// Er zijn enkele pakketten onderschept, maar nog geen enkele is geselecteerd volgens de filters die je hebt opgegeven..."
// ),
// })
// }
// pub fn filtered_packets_translation(language: Language) -> &'static str {
// match language {
@@ -1038,7 +1038,7 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
// })
// }
pub fn error_translation(language: Language, error: &str) -> Text<StyleType> {
pub fn error_translation(language: Language, error: &str) -> Text<'_, StyleType> {
Text::new(match language {
Language::EN => format!(
"An error occurred! \n\n\
@@ -1462,7 +1462,7 @@ pub fn appearance_title_translation<'a>(language: Language) -> Text<'a, StyleTyp
Language::TR => "Favori temanızı seçin",
Language::RU => "Выберите предпочительную тему",
Language::PT => "Escolha o seu tema favorito",
Language::EL => "Επίλεξε το αγαπημένο σου θέμα",
Language::EL => "Επιλέξτε το αγαπημένο σας θέμα",
// Language::FA => "زمینه دلخواه خود را انتخاب کنید",
Language::SV => "Välj ditt favorittema",
Language::FI => "Valitse suosikkiteemasi",
@@ -1787,7 +1787,7 @@ pub fn overview_translation(language: Language) -> &'static str {
Language::TR => "Ön izleme",
Language::RU => "Обзор",
Language::PT => "Visão geral",
Language::EL => "επισκόπηση",
Language::EL => "Επισκόπηση",
// Language::FA => "نمای کلی",
Language::SV => "Översikt",
Language::FI => "Yleiskatsaus",
@@ -2167,7 +2167,7 @@ pub fn favorite_transmitted_translation(language: Language) -> &'static str {
Language::TR => "Favorilerden yeni veri aktarıldı",
Language::RU => "Новый обмен данными в избранных соедиениях",
Language::PT => "Novos dados trocados dos favoritos",
Language::EL => "Καινούρια δεδομένα έχουν ανταλλαγεί στα αγαπημένα",
Language::EL => "Νέα δεδομένα έχουν ανταλλαγεί στα αγαπημένα",
// Language::FA => "مبادله داده جدید از پسندیده ها",
Language::SV => "Ny data utbytt av favoriter",
Language::FI => "Uusia tietoja vaihdettu suosikeista",
@@ -2253,9 +2253,9 @@ pub fn no_notifications_set_translation<'a>(language: Language) -> Text<'a, Styl
Pode ativar as notificações nas definições:"
}
Language::EL => {
"Δεν έχεις ενεργοποιήσει τις ειδοποιήσεις ακόμη!\n\n\
Αφότου τις ενεργοποιήσεις, αυτή η σελίδα θα απεικονίσει μια καταγραφή των ειδοποιήσεών σου\n\n\
Μπορείς να ενεργοποιήσεις τις ειδοποιήσεις από τις ρυθμίσεις:"
"Δεν έχετε ενεργοποιήσει τις ειδοποιήσεις ακόμη!\n\n\
Αφότου τις ενεργοποιήσετε, αυτή η σελίδα θα εμφανίσει ένα αρχείο καταγραφής των ειδοποιήσεών σας\n\n\
Μπορείτε να ενεργοποιήσετε τις ειδοποιήσεις από τις ρυθμίσεις:"
}
// Language::FA => "شما هنوز اعلان ها را فعال نکرده اید!\n\n\
// پس از آنکه آن ها را فعال کنید، این صفحه یک کارنامه از اعلان های شما را نمایش خواهد داد\n\n
@@ -2357,8 +2357,8 @@ pub fn no_notifications_received_translation<'a>(language: Language) -> Text<'a,
Quando receber uma notificação, ela será mostrada aqui"
}
Language::EL => {
"Δεν υπάρχει κάτι για απεικόνιση αυτή τη στιγμή...\n\n\
Όταν λάβεις μια ειδοποίηση, αυτή θα εμφανιστεί εδώ"
"Δεν υπάρχουν ειδοποιήσεις αυτή τη στιγμή...\n\n\
Όταν λάβετε μια ειδοποίηση, θα εμφανιστεί εδώ"
}
// Language::FA => {
// "در حال حاضر هیچ چیزی برای دیدن نیست...\n\n\

View File

@@ -1,5 +1,3 @@
#![allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)]
use crate::Language;
pub fn new_version_available_translation(language: Language) -> &'static str {
@@ -54,7 +52,7 @@ pub fn inspect_translation(language: Language) -> &'static str {
Language::VI => "Quan sát",
Language::ID => "Memeriksa",
Language::NL => "Inspecteren",
_ => "Inspect",
Language::EL => "Επιθεώρηση",
}
}
@@ -82,7 +80,7 @@ pub fn connection_details_translation(language: Language) -> &'static str {
Language::VI => "Thông tin kết nối",
Language::ID => "Rincian koneksi",
Language::NL => "Verbindingsdetails",
_ => "Connection details",
Language::EL => "Λεπτομέρειες σύνδεσης",
}
}
@@ -94,7 +92,7 @@ pub fn dropped_translation(language: Language) -> &'static str {
Language::RU => "Потеряно",
Language::SV => "Tappade",
Language::FI => "Pudotetut",
Language::DE => "Verloren",
Language::DE | Language::NL => "Verloren",
Language::TR => "Düşen",
// Language::FA => "رها شده",
Language::ES | Language::PT => "Perdidos",
@@ -109,8 +107,7 @@ pub fn dropped_translation(language: Language) -> &'static str {
Language::UZ => "Yig'ilgan",
Language::VI => "Mất",
Language::ID => "Dihapus",
Language::NL => "Verloren",
_ => "Dropped",
Language::EL => "Απορριμμένα",
}
}
@@ -138,7 +135,7 @@ pub fn data_representation_translation(language: Language) -> &'static str {
Language::VI => "Miêu tả dữ liệu",
Language::ID => "Penyajian ulang data",
Language::NL => "Gegevensweergave",
_ => "Data representation",
Language::EL => "Αναπαράσταση δεδομένων",
}
}
@@ -166,7 +163,7 @@ pub fn host_translation(language: Language) -> &'static str {
Language::VI => "Máy chủ",
Language::ID => "Jaringan asal",
Language::NL => "Netwerk host",
_ => "Network host",
Language::EL => "Κόμβος δικτύου",
}
}
@@ -194,7 +191,7 @@ pub fn only_top_30_items_translation(language: Language) -> &'static str {
Language::VI => "Chỉ có 30 mục gần nhất được hiển thị ở đây",
Language::ID => "Hanya 30 teratas yang ditampilkan disini",
Language::NL => "Alleen de bovenste 30 items worden hier weergegeven",
_ => "Only the top 30 items are displayed here",
Language::EL => "Εμφανίζονται μόνο τα κορυφαία 30 στοιχεία",
}
}
@@ -248,7 +245,7 @@ pub fn local_translation(language: Language) -> &'static str {
Language::VI => "Mạng nội bộ",
Language::ID => "Jaringan lokal",
Language::NL => "Lokaal netwerk",
_ => "Local network",
Language::EL => "Τοπικό δίκτυο",
}
}
@@ -276,7 +273,7 @@ pub fn unknown_translation(language: Language) -> &'static str {
Language::VI => "Không rõ địa điểm",
Language::ID => "Lokasi tidak diketahui",
Language::NL => "Onbekende locatie",
_ => "Unknown location",
Language::EL => "Άγνωστη τοποθεσία",
}
}
@@ -304,7 +301,7 @@ pub fn your_network_adapter_translation(language: Language) -> &'static str {
Language::VI => "Network adapter của bạn",
Language::ID => "Adaptor jaringan kamu",
Language::NL => "Uw netwerkadapter",
_ => "Your network adapter",
Language::EL => "Ο προσαρμογέας δικτύου σας",
}
}
@@ -332,7 +329,7 @@ pub fn socket_address_translation(language: Language) -> &'static str {
Language::VI => "Địa chỉ socket",
Language::ID => "Alamat sambungan",
Language::NL => "Socket adres",
_ => "Socket address",
Language::EL => "Διεύθυνση υποδοχής",
}
}
@@ -360,13 +357,13 @@ pub fn mac_address_translation(language: Language) -> &'static str {
Language::VI => "Địa chỉ MAC",
Language::ID => "Alamat MAC",
Language::NL => "MAC-adres",
_ => "MAC address",
Language::EL => "Διεύθυνση MAC",
}
}
pub fn source_translation(language: Language) -> &'static str {
match language {
Language::EN => "Source",
Language::EN | Language::FR => "Source",
Language::IT => "Sorgente",
Language::RU => "Источник",
Language::SV => "Källa",
@@ -381,20 +378,19 @@ pub fn source_translation(language: Language) -> &'static str {
Language::UK => "Джерело",
Language::RO => "Sursă",
Language::PL => "Źródło",
Language::FR => "Source",
Language::JA => "送信元",
Language::UZ => "Manba",
Language::PT => "Fonte",
Language::VI => "Nguồn",
Language::ID => "Asal",
Language::NL => "Bron",
_ => "Source",
Language::EL => "Πηγή",
}
}
pub fn destination_translation(language: Language) -> &'static str {
match language {
Language::EN | Language::SV => "Destination",
Language::EN | Language::SV | Language::FR => "Destination",
Language::IT => "Destinazione",
Language::RU => "Получатель",
Language::FI => "Määränpää",
@@ -408,13 +404,12 @@ pub fn destination_translation(language: Language) -> &'static str {
Language::UK => "Призначення",
Language::RO => "Destinație",
Language::PL => "Miejsce docelowe",
Language::FR => "Destination",
Language::JA => "送信先",
Language::UZ => "Qabul qiluvchi",
Language::VI => "Đích",
Language::ID => "Tujuan",
Language::NL => "Bestemming",
_ => "Destination",
Language::EL => "Προορισμός",
}
}
@@ -430,8 +425,7 @@ pub fn fqdn_translation(language: Language) -> &'static str {
// Language::FA => "نام دامنه جامع الشرایط",
Language::ES => "Nombre de dominio completo",
Language::KO => "절대 도메인 네임",
Language::ZH | Language::JA => "FQDN",
Language::ZH_TW => "FQDN",
Language::ZH | Language::JA | Language::ZH_TW => "FQDN",
Language::UK => "Повністю визначене доменне ім'я",
Language::RO => "Nume de domeniu complet calificat",
Language::PL => "Pełna nazwa domeny",
@@ -441,7 +435,7 @@ pub fn fqdn_translation(language: Language) -> &'static str {
Language::VI => "Tên miền đầy đủ",
Language::ID => "Nama domain yang memenuhi syarat",
Language::NL => "Volledig gekwalificeerde domeinnaam",
_ => "Fully qualified domain name",
Language::EL => "Πλήρως προσδιορισμένο όνομα τομέα",
}
}
@@ -469,7 +463,7 @@ pub fn administrative_entity_translation(language: Language) -> &'static str {
Language::VI => "Tên Autonomous System",
Language::ID => "Nama System Otomatis",
Language::NL => "Naam van het autonome systeem",
_ => "Autonomous System name",
Language::EL => "Όνομα αυτόνομου συστήματος",
}
}
@@ -497,7 +491,7 @@ pub fn transmitted_data_translation(language: Language) -> &'static str {
Language::VI => "Dữ liệu được truyền",
Language::ID => "Data terkirim",
Language::NL => "Verzonden gegevens",
_ => "Transmitted data",
Language::EL => "Μεταδιδόμενα δεδομένα",
}
}
@@ -506,9 +500,8 @@ pub fn country_translation(language: Language) -> &'static str {
Language::EN => "Country",
Language::IT => "Paese",
Language::RU => "Страна",
Language::SV => "Land",
Language::SV | Language::DE | Language::NL => "Land",
Language::FI => "Maa",
Language::DE => "Land",
Language::TR => "Ülke",
// Language::FA => "کشور",
Language::ES | Language::PT => "País",
@@ -523,8 +516,7 @@ pub fn country_translation(language: Language) -> &'static str {
Language::UZ => "Davlat",
Language::VI => "Quốc gia",
Language::ID => "Negara",
Language::NL => "Land",
_ => "Country",
Language::EL => "Χώρα",
}
}
@@ -552,7 +544,7 @@ pub fn domain_name_translation(language: Language) -> &'static str {
Language::VI => "Tên miền",
Language::ID => "Nama Domain",
Language::NL => "Domeinnaam",
_ => "Domain name",
Language::EL => "Όνομα τομέα",
}
}
@@ -580,7 +572,7 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str {
Language::VI => "Chỉ hiển thị mục ưa thích",
Language::ID => "Hanya tunjukkan favorit",
Language::NL => "Toon alleen favorieten",
_ => "Only show favorites",
Language::EL => "Εμφάνιση μόνο αγαπημένων",
}
}
@@ -635,7 +627,9 @@ pub fn no_search_results_translation(language: Language) -> &'static str {
Language::VI => "Không có kết quả nào theo các bộ lọc được chỉ định",
Language::ID => "Tidak ada hasil berdasarkan filter pencarian spesifik",
Language::NL => "Geen resultaten beschikbaar volgens de opgegeven zoekfilters",
_ => "No result available according to the specified search filters",
Language::EL => {
"Δεν υπάρχουν διαθέσιμα αποτελέσματα σύμφωνα με τα καθορισμένα φίλτρα αναζήτησης"
}
}
}
@@ -670,11 +664,10 @@ pub fn showing_results_translation(
Language::NL => {
format!("{start}-{end} van de {total} totale resultaten worden weergegeven")
}
_ => format!("Showing {start}-{end} of {total} total results"),
Language::EL => format!("Εμφάνιση {start}-{end} από {total} συνολικά αποτελέσματα"),
}
}
#[allow(dead_code)]
pub fn color_gradients_translation(language: Language) -> &'static str {
match language {
Language::EN => "Apply color gradients",
@@ -699,6 +692,6 @@ pub fn color_gradients_translation(language: Language) -> &'static str {
Language::VI => "Áp dụng color gradients",
Language::ID => "Aplikasikan gradasi warna",
Language::NL => "Kleurverlopen toepassen",
_ => "Apply color gradients",
Language::EL => "Εφαρμογή χρωματικών διαβαθμίσεων",
}
}

View File

@@ -1,4 +1,4 @@
#![allow(clippy::match_same_arms)]
#![allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)]
use iced::widget::Text;
@@ -28,6 +28,7 @@ pub fn general_translation(language: Language) -> &'static str {
Language::UK => "Загальні",
Language::ID => "Umum",
Language::NL => "Algemeen",
Language::EL => "Γενικά",
_ => "General",
}
}
@@ -55,6 +56,7 @@ pub fn zoom_translation(language: Language) -> &'static str {
Language::TR => "Yakınlaştırma",
Language::UK => "Масштабування",
Language::ID => "Perbesar",
Language::EL => "Εστίαση",
_ => "Zoom",
}
}
@@ -82,6 +84,7 @@ pub fn mmdb_files_translation(language: Language) -> &'static str {
Language::UK => "Файли бази даних",
Language::ID => "Berkas database",
Language::NL => "Database bestanden",
Language::EL => "Αρχεία βάσης δεδομένων",
_ => "Database files",
}
}
@@ -109,6 +112,9 @@ pub fn params_not_editable_translation(language: Language) -> &'static str {
Language::UK => "Наступні параметри не можна змінювати під час аналізу трафіку",
Language::ID => "Parameter berikut tidak bisa diubah saat dianalisa",
Language::NL => "De volgende parameters kunnen niet worden aangepast tijdens de analyse",
Language::EL => {
"Οι ακόλουθες παράμετροι δεν μπορούν να τροποποιηθούν κατά τη διάρκεια της ανάλυσης"
}
_ => "The following parameters can't be modified during the analysis",
}
}
@@ -135,6 +141,7 @@ pub fn custom_style_translation(language: Language) -> &'static str {
Language::UK => "Власний стиль",
Language::ID => "Ubah Model",
Language::NL => "Aangepaste stijl",
Language::EL => "Προσαρμοσμένο στυλ",
_ => "Custom style",
}
}
@@ -160,6 +167,7 @@ pub fn copy_translation(language: Language) -> &'static str {
Language::UK => "Копіювати",
Language::ID => "Salin",
Language::NL => "Kopiëren",
Language::EL => "Αντιγραφή",
_ => "Copy",
}
}
@@ -186,35 +194,37 @@ pub fn port_translation(language: Language) -> &'static str {
Language::UK => "Порт",
Language::ID => "Port",
Language::NL => "Poort",
Language::EL => "Θύρα",
_ => "Port",
}
}
pub fn invalid_filters_translation(language: Language) -> &'static str {
match language {
Language::EN => "Invalid filters",
// Language::FA => "صافی نامعتبر",
Language::ES | Language::PT => "Filtros inválidos",
Language::IT => "Filtri non validi",
Language::FR => "Filtres invalides",
Language::DE => "Ungültige Filter",
Language::PL => "Nieprawidłowe filtry",
Language::RU => "Неверный формат фильтров",
Language::RO => "Filtre invalide",
Language::JA => "無効なフィルター",
Language::UZ => "Noto'g'ri filtrlar",
Language::SV => "Ogiltiga filter",
Language::VI => "Bộ lọc không khả dụng",
Language::ZH => "无效的过滤器",
Language::ZH_TW => "無效的篩選器",
Language::KO => "잘못된 필터",
Language::TR => "Geçersiz filtreler",
Language::UK => "Неправильний формат фільтрів",
Language::ID => "Filter salah",
Language::NL => "Ongeldige filters",
_ => "Invalid filters",
}
}
// pub fn invalid_filters_translation(language: Language) -> &'static str {
// match language {
// Language::EN => "Invalid filters",
// // Language::FA => "صافی نامعتبر",
// Language::ES | Language::PT => "Filtros inválidos",
// Language::IT => "Filtri non validi",
// Language::FR => "Filtres invalides",
// Language::DE => "Ungültige Filter",
// Language::PL => "Nieprawidłowe filtry",
// Language::RU => "Неверный формат фильтров",
// Language::RO => "Filtre invalide",
// Language::JA => "無効なフィルター",
// Language::UZ => "Noto'g'ri filterlar",
// Language::SV => "Ogiltiga filter",
// Language::VI => "Bộ lọc không khả dụng",
// Language::ZH => "无效的过滤器",
// Language::ZH_TW => "無效的篩選器",
// Language::KO => "잘못된 필터",
// Language::TR => "Geçersiz filtreler",
// Language::UK => "Неправильний формат фільтрів",
// Language::ID => "Filter salah",
// Language::NL => "Ongeldige filters",
// Language::EL => "Μη έγκυρα φίλτρα",
// _ => "Invalid filters",
// }
// }
pub fn messages_translation(language: Language) -> &'static str {
match language {
@@ -238,6 +248,7 @@ pub fn messages_translation(language: Language) -> &'static str {
Language::UK => "Повідомлення",
Language::ID => "Pesan",
Language::NL => "Berichten",
Language::EL => "Μηνύματα",
_ => "Messages",
}
}
@@ -264,6 +275,7 @@ pub fn link_type_translation(language: Language) -> &'static str {
Language::PT => "Tipo de conexão",
Language::UK => "Різновид зʼєднання",
Language::ID => "Tipe koneksi",
Language::EL => "Τύπος σύνδεσης",
_ => "Link type",
}
}
@@ -324,6 +336,9 @@ pub fn unsupported_link_type_translation<'a>(
Language::NL => {
"Het linktype dat is gekoppeld aan deze adapter wordt nog niet ondersteund door Sniffnet..."
}
Language::EL => {
"Ο τύπος σύνδεσης που σχετίζεται με αυτόν τον προσαρμογέα δεν υποστηρίζεται ακόμη από το Sniffnet..."
}
_ => "The link type associated with this adapter is not supported by Sniffnet yet...",
};
@@ -356,6 +371,7 @@ pub fn style_from_file_translation(language: Language) -> &'static str {
Language::UK => "Виберіть стиль з файлу",
Language::ID => "Pilih model / gaya dari berkas",
Language::NL => "Selecteer stijl vanuit een bestand",
Language::EL => "Επιλογή στυλ από αρχείο",
_ => "Select style from a file",
}
}
@@ -383,6 +399,7 @@ pub fn database_from_file_translation(language: Language) -> &'static str {
Language::UK => "Виберіть файл бази даних",
Language::ID => "Pilih berkas database",
Language::NL => "Selecteer database bestand",
Language::EL => "Επιλογή αρχείου βάσης δεδομένων",
_ => "Select database file",
}
}
@@ -410,6 +427,7 @@ pub fn filter_by_host_translation(language: Language) -> &'static str {
Language::UK => "Фільтр за хостом мережі",
Language::ID => "Filter berdasarkan jaringan asal",
Language::NL => "Filteren op netwerk host",
Language::EL => "Φίλτρο ανά διακομιστή δικτύου",
_ => "Filter by network host",
}
}
@@ -434,6 +452,7 @@ pub fn service_translation(language: Language) -> &'static str {
Language::UK => "Сервіс",
Language::ID => "Layanan",
Language::NL => "Dienst",
Language::EL => "Υπηρεσία",
_ => "Service",
}
}
@@ -461,6 +480,7 @@ pub fn export_capture_translation(language: Language) -> &'static str {
Language::ID => "Ekspor data tangkapan",
Language::ES => "Exportar archivo de captura",
Language::NL => "Exporteer capture bestand",
Language::EL => "Εξαγωγή αρχείου καταγραφής",
_ => "Export capture file",
}
}
@@ -487,6 +507,7 @@ pub fn directory_translation(language: Language) -> &'static str {
Language::ID => "Direktori",
Language::ES => "Directorio",
Language::NL => "Map",
Language::EL => "Κατάλογος",
_ => "Directory",
}
}
@@ -514,6 +535,7 @@ pub fn select_directory_translation(language: Language) -> &'static str {
Language::ID => "Pilih direktori tujuan",
Language::ES => "Selecciona el directorio de destino",
Language::NL => "Selecteer doelmap",
Language::EL => "Επιλογή καταλόγου προορισμού",
_ => "Select destination directory",
}
}
@@ -541,6 +563,7 @@ pub fn file_name_translation(language: Language) -> &'static str {
Language::ID => "Nama berkas",
Language::ES => "Nombre del archivo",
Language::NL => "Bestandsnaam",
Language::EL => "Όνομα αρχείου",
_ => "File name",
}
}
@@ -567,6 +590,7 @@ pub fn thumbnail_mode_translation(language: Language) -> &'static str {
Language::UK => "Режим мініатюри",
Language::ID => "Mode gambar kecil",
Language::NL => "Miniatuur modus",
Language::EL => "Λειτουργία μικρογραφιών",
_ => "Thumbnail mode",
}
}

View File

@@ -9,12 +9,17 @@ pub fn reserved_address_translation(language: Language, info: &str) -> String {
match language {
Language::EN => format!("Reserved address ({info})"),
Language::IT => format!("Indirizzo riservato ({info})"),
Language::JA => format!("予約済みアドレス ({info})"),
Language::PT => format!("Endereço reservado ({info})"),
Language::UK => format!("Зарезервована адреса ({info})"),
Language::ZH => format!("预留地址 ({info})"),
Language::ZH_TW => format!("保留的網路位址 ({info})"),
Language::FR => format!("Adresse réservée ({info})"),
Language::NL => format!("Gereserveerd adres ({info})"),
Language::RO => format!("Adresă rezervată ({info})"),
Language::DE => format!("Reservierte Adresse ({info})"),
Language::UZ => format!("Rezervlangan manzil ({info})"),
Language::EL => format!("Δεσμευμένη διεύθυνση ({info})"),
_ => format!("Reserved address ({info})"),
}
}
@@ -23,36 +28,51 @@ pub fn share_feedback_translation(language: Language) -> &'static str {
match language {
Language::EN => "Share your feedback",
Language::IT => "Condividi il tuo feedback",
Language::JA => "フィードバックを共有",
Language::ZH => "分享您的反馈",
Language::ZH_TW => "分享您的意見回饋",
Language::FR => "Partagez vos commentaires",
Language::NL => "Deel uw feedback",
Language::RO => "Împărtășiți feedback-ul dvs.",
Language::DE => "Feedback geben",
Language::UZ => "Fikr-mulohazalaringizni ulashing",
Language::EL => "Μοιραστείτε τα σχόλιά σας",
_ => "Share your feedback",
}
}
// refers to bytes or packets excluded because of the filters
pub fn excluded_translation(language: Language) -> &'static str {
match language {
Language::EN => "Excluded",
Language::IT => "Esclusi",
Language::UZ => "Chiqarib tashlangan",
Language::ZH_TW => "已排除",
Language::NL => "Uitgesloten",
Language::DE => "Herausgefiltert",
_ => "Excluded",
}
}
// pub fn excluded_translation(language: Language) -> &'static str {
// match language {
// Language::EN => "Excluded",
// Language::IT => "Esclusi",
// Language::JA => "除外",
// Language::ZH => "已被过滤",
// Language::UZ => "Chiqarib tashlangan",
// Language::ZH_TW => "已排除",
// Language::FR => "Exclus",
// Language::NL => "Uitgesloten",
// Language::DE => "Herausgefiltert",
// Language::EL => "Εξαιρούμενα",
// Language::RO => "Excluși",
// _ => "Excluded",
// }
// }
pub fn import_capture_translation(language: Language) -> &'static str {
pub fn capture_file_translation(language: Language) -> &'static str {
match language {
Language::EN => "Import capture file",
Language::IT => "Importa file di cattura",
Language::NL => "Importeer capture bestand",
Language::DE => "Aufzeichnungsdatei importieren",
Language::UZ => "Tahlil faylini import qilish",
Language::EN => "Capture file",
Language::IT => "File di cattura",
Language::FR => "Fichier de capture",
Language::JA => "キャプチャファイル",
Language::ZH => "捕获文件",
Language::NL => "Capture bestand",
Language::DE => "Aufzeichnungsdatei",
Language::UZ => "Tahlil faylini",
Language::EL => "Αρχείου καταγραφής",
Language::RO => "Importă fișierul de captură",
Language::ZH_TW => "導入擷取文件",
_ => "Import capture file",
_ => "Capture file",
}
}
@@ -60,10 +80,15 @@ pub fn select_capture_translation(language: Language) -> &'static str {
match language {
Language::EN => "Select capture file",
Language::IT => "Seleziona file di cattura",
Language::FR => "Sélectionner un fichier de capture",
Language::JA => "キャプチャファイルを選択",
Language::ZH => "选择捕获文件",
Language::NL => "Selecteer capture bestand",
Language::RO => "Selectează fișierul de captură",
Language::DE => "Aufzeichnungsdatei auswählen",
Language::UZ => "Tahlil faylini tanlang",
Language::ZH_TW => "選擇擷取文件",
Language::EL => "Επιλογή αρχείου καταγραφής",
_ => "Select capture file",
}
}
@@ -81,11 +106,31 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text
{file_name_translation}: {file}\n\n\
Sei sicuro che il file che hai selezionato non sia vuoto?"
),
Language::FR => format!(
"Lecture des paquets depuis le fichier...\n\n\
{file_name_translation}: {file}\n\n\
Êtes-vous sûr que le fichier sélectionné n'est pas vide?"
),
Language::JA => format!(
"ファイルからパケットを読み込み中...\n\n\
{file_name_translation}: {file}\n\n\
"
),
Language::ZH => format!(
"从文件中读取数据包...\n\n\
{file_name_translation}: {file}\n\n\
?"
),
Language::NL => format!(
"Pakketten lezen uit bestand...\n\n\
{file_name_translation}: {file}\n\n\
Weet je zeker dat het geselecteerde bestand niet leeg is?"
),
Language::RO => format!(
"Citirea pachetelor din fișier...\n\n\
{file_name_translation}: {file}\n\n\
Ești sigur fișierul selectat nu este gol?"
),
Language::DE => format!(
"Pakete aus Datei laden... \n\n\
{file_name_translation}: {file}\n\n\
@@ -101,10 +146,15 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text
{file_name_translation}: {file}\n\n\
"
),
Language::EL => format!(
"Ανάγνωση πακέτων από αρχείο...\n\n\
{file_name_translation}: {file}\n\n\
Είστε βέβαιοι ότι το επιλεγμένο αρχείο δεν είναι κενό;"
),
_ => format!(
"Reading packets from file...\n\n\
{file_name_translation}: {file}\n\n\
Are you sure the file you selected isn't empty?"
{file_name_translation}: {file}\n\n\
Are you sure the file you selected isn't empty?"
),
})
}
@@ -113,45 +163,61 @@ pub fn data_exceeded_translation(language: Language) -> &'static str {
match language {
Language::EN => "Data threshold exceeded",
Language::IT => "Soglia di dati superata",
Language::FR => "Seuil de données dépassé",
Language::JA => "データの閾値を超えました",
Language::ZH => "已超出数据阈值",
Language::NL => "Gegevenslimiet overschreden",
Language::RO => "Limita de date depășită",
Language::DE => "Datenschwelle überschritten",
Language::UZ => "Ma'lumotlar chegarasidan oshib ketdi",
Language::ZH_TW => "已排除",
Language::EL => "Υπέρβαση ορίου δεδομένων",
_ => "Data threshold exceeded",
}
}
#[allow(dead_code)]
pub fn bits_exceeded_translation(language: Language) -> &'static str {
match language {
Language::EN => "Bits threshold exceeded",
Language::IT => "Soglia di bit superata",
Language::FR => "Seuil de bits dépassé",
Language::JA => "ビットの閾値を超えました",
Language::ZH => "已超出比特阈值",
Language::NL => "Bits limiet overschreden",
Language::RO => "Limita de biți depășită",
Language::DE => "Bitschwelle überschritten",
Language::UZ => "Bitlar chegarasidan oshib ketdi",
Language::ZH_TW => "超出數據界限",
Language::EL => "Υπέρβαση ορίου δυφίων",
_ => "Bits threshold exceeded",
}
}
#[allow(dead_code)]
pub fn bits_translation(language: Language) -> &'static str {
match language {
Language::EN | Language::IT | Language::NL | Language::DE => "Bits",
Language::EN | Language::IT | Language::NL | Language::DE | Language::FR => "bits",
Language::JA => "ビット",
Language::ZH => "比特",
Language::UZ => "bitlar",
Language::EL => "Δυφία",
Language::RO => "Biți",
Language::ZH_TW => "位元",
Language::UZ => "Bitlar",
_ => "Bits",
_ => "bits",
}
}
#[allow(dead_code)]
pub fn pause_translation(language: Language) -> &'static str {
match language {
Language::EN | Language::DE => "Pause",
Language::EN | Language::DE | Language::FR => "Pause",
Language::IT => "Pausa",
Language::JA => "一時停止",
Language::ZH => "暂停",
Language::NL => "Pauzeren",
Language::RO => "Pauză",
Language::UZ => "To'xtatish",
Language::ZH_TW => "暫停",
Language::EL => "Παύση",
_ => "Pause",
}
}
@@ -161,10 +227,15 @@ pub fn resume_translation(language: Language) -> &'static str {
match language {
Language::EN => "Resume",
Language::IT => "Riprendi",
Language::FR => "Reprendre",
Language::JA => "再開",
Language::ZH => "恢复",
Language::NL => "Hervatten",
Language::RO => "Continuă",
Language::DE => "Fortsetzen",
Language::UZ => "Davom ettirish",
Language::ZH_TW => "繼續",
Language::EL => "Συνέχεια",
_ => "Resume",
}
}

View File

@@ -0,0 +1,21 @@
#![allow(clippy::match_same_arms)]
use crate::translations::types::language::Language;
pub fn filter_traffic_translation(language: Language) -> String {
match language {
Language::EN => "Filter traffic",
Language::IT => "Filtra il traffico",
_ => "Filter traffic",
}
.to_string()
}
// the source from which Sniffnet reads network traffic (i.e., a capture file or a network adapter)
pub fn traffic_source_translation(language: Language) -> &'static str {
match language {
Language::EN => "Traffic source",
Language::IT => "Fonte del traffico",
_ => "Traffic source",
}
}

View File

@@ -121,7 +121,15 @@ pub fn get_flag<'a>(self) -> Svg<'a, StyleType> {
pub fn is_up_to_date(self) -> bool {
matches!(
self,
Language::EN | Language::IT | Language::NL | Language::DE | Language::UZ
Language::EN
| Language::IT
| Language::NL
| Language::DE
| Language::UZ
| Language::ZH
| Language::JA
| Language::FR
| Language::EL
)
}
}

View File

@@ -1,15 +1,8 @@
use std::cmp::min;
use std::net::IpAddr;
use crate::Language;
use crate::networking::types::filters::Filters;
use crate::translations::translations::{
address_translation, ip_version_translation, protocol_translation,
};
use crate::translations::translations_3::{invalid_filters_translation, port_translation};
use crate::utils::types::timestamp::Timestamp;
use chrono::{Local, TimeZone};
use std::fmt::Write;
/// Application version number (to be displayed in gui footer)
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -27,61 +20,6 @@
// }
// }
pub fn get_invalid_filters_string(filters: &Filters, language: Language) -> String {
let mut ret_val = format!("{}:", invalid_filters_translation(language));
if !filters.ip_version_valid() {
let _ = write!(ret_val, "\n • {}", ip_version_translation(language));
}
if !filters.protocol_valid() {
let _ = write!(ret_val, "\n • {}", protocol_translation(language));
}
if !filters.address_valid() {
let _ = write!(ret_val, "\n • {}", address_translation(language));
}
if !filters.port_valid() {
let _ = write!(ret_val, "\n • {}", port_translation(language));
}
ret_val
}
/// Computes the string representing the active filters
pub fn get_active_filters_string(filters: &Filters, language: Language) -> String {
let mut filters_string = String::new();
if filters.ip_version_active() {
let _ = writeln!(
filters_string,
"• {}: {}",
ip_version_translation(language),
filters.pretty_print_ip()
);
}
if filters.protocol_active() {
let _ = writeln!(
filters_string,
"• {}: {}",
protocol_translation(language),
filters.pretty_print_protocol()
);
}
if filters.address_active() {
let _ = writeln!(
filters_string,
"• {}: {}",
address_translation(language),
filters.address_str
);
}
if filters.port_active() {
let _ = writeln!(
filters_string,
"• {}: {}",
port_translation(language),
filters.port_str
);
}
filters_string
}
pub fn print_cli_welcome_message() {
let ver = APP_VERSION;
print!(

View File

@@ -39,7 +39,7 @@ pub enum Icon {
PacketsThreshold,
// Restore,
Roadmap,
Rocket,
// Rocket,
Settings,
Sniffnet,
SortAscending,
@@ -85,7 +85,7 @@ pub fn codepoint(&self) -> char {
Icon::Overview => 'd',
Icon::PacketsThreshold => '\\',
// Icon::Restore => 'k',
Icon::Rocket => 'S',
// Icon::Rocket => 'S',
Icon::Settings => 'a',
Icon::Sniffnet => 'A',
Icon::Star => 'g',