Merge branch 'main' into spanish_translation
@@ -601,7 +601,8 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/171437458?v=4",
|
||||
"profile": "https://github.com/shu-kitamura",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -703,6 +704,96 @@
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "celtic-coder",
|
||||
"name": "Liam OBrien",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11787866?v=4",
|
||||
"profile": "https://github.com/celtic-coder",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SignPath",
|
||||
"name": "SignPath GmbH",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/34448643?v=4",
|
||||
"profile": "https://signpath.io",
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MelcuGoa",
|
||||
"name": "MelcuGoa",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/80478973?v=4",
|
||||
"profile": "https://github.com/MelcuGoa",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "paco1127",
|
||||
"name": "Paco",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/52984682?v=4",
|
||||
"profile": "https://github.com/paco1127",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
13
.github/workflows/docker.yml
vendored
@@ -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 }}
|
||||
|
||||
97
.github/workflows/package.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/rust.yml
vendored
@@ -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
@@ -57,3 +57,6 @@ $RECYCLE.BIN/
|
||||
### Custom... ###
|
||||
lcov.info
|
||||
*.pcap
|
||||
node_modules
|
||||
package.json
|
||||
yarn.lock
|
||||
|
||||
17
CHANGELOG.md
@@ -3,7 +3,24 @@ # 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))
|
||||
- Romanian ([#890](https://github.com/GyulyVGC/sniffnet/pull/890))
|
||||
- Traditional Chinese (Taiwan) ([#904](https://github.com/GyulyVGC/sniffnet/pull/904))
|
||||
- Indonesian ([#909](https://github.com/GyulyVGC/sniffnet/pull/909))
|
||||
- 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))
|
||||
|
||||
@@ -6,56 +6,65 @@
|
||||
<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>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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://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/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/MelcuGoa"><img src="https://avatars.githubusercontent.com/u/80478973?v=4?s=100" width="100px;" alt="MelcuGoa"/><br /><sub><b>MelcuGoa</b></sub></a><br /><a href="#translation-MelcuGoa" title="Translation">🌍</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>
|
||||
</tr>
|
||||
@@ -64,42 +73,47 @@
|
||||
<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://github.com/paco1127"><img src="https://avatars.githubusercontent.com/u/52984682?v=4?s=100" width="100px;" alt="Paco"/><br /><sub><b>Paco</b></sub></a><br /><a href="#translation-paco1127" title="Translation">🌍</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
<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/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>
|
||||
<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>
|
||||
<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/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>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
1001
Cargo.lock
generated
32
Cargo.toml
@@ -37,8 +37,8 @@ strip = true
|
||||
#═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[dependencies]
|
||||
pcap = "2.2.0"
|
||||
etherparse = "0.18.0"
|
||||
pcap = "2.3.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"] }
|
||||
@@ -46,40 +46,40 @@ plotters-iced = "0.11.0"
|
||||
maxminddb = "0.26.0"
|
||||
confy = "1.0.0"
|
||||
serde = { version = "1.0.219", default-features = false, features = ["derive"] }
|
||||
rodio = { version = "0.20.1", default-features = false, features = ["mp3"] }
|
||||
dns-lookup = "2.0.4"
|
||||
toml = "0.9.1"
|
||||
rodio = { version = "0.21.1", default-features = false, features = ["mp3", "playback"] }
|
||||
dns-lookup = "3.0.0"
|
||||
toml = "0.9.5"
|
||||
ctrlc = { version = "3.4.7", features = ["termination"] }
|
||||
rfd = "0.15.3"
|
||||
phf = "0.12.1"
|
||||
phf_shared = "0.12.1"
|
||||
rfd = "0.15.4"
|
||||
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"
|
||||
|
||||
31
README.md
@@ -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> |
|
||||
|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||
| [64‑bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_64-bit.msi) \| [32‑bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_32-bit.msi) | [Intel](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_Intel.dmg) \| [Apple 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) \| [arm64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_arm64.deb) \| [i386](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_i386.deb) \| [armhf](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_armhf.deb) | [x86_64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_x86_64.rpm) \| [aarch64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_aarch64.rpm) |
|
||||
<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
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"origins": ["https://sniffnet.net"],
|
||||
"originsRegex": ["http://localhost:[0-9]+"],
|
||||
"defaultCommentOrder": "oldest"
|
||||
}
|
||||
@@ -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
|
||||
士
|
||||
変
|
||||
复
|
||||
外
|
||||
多
|
||||
夢
|
||||
夹
|
||||
@@ -583,6 +593,7 @@ z
|
||||
将
|
||||
將
|
||||
尋
|
||||
導
|
||||
小
|
||||
尚
|
||||
尝
|
||||
@@ -608,6 +619,7 @@ z
|
||||
從
|
||||
志
|
||||
总
|
||||
恢
|
||||
息
|
||||
您
|
||||
情
|
||||
@@ -664,6 +676,7 @@ z
|
||||
景
|
||||
暂
|
||||
暗
|
||||
暫
|
||||
更
|
||||
最
|
||||
會
|
||||
@@ -695,9 +708,11 @@ z
|
||||
檔
|
||||
檢
|
||||
欢
|
||||
止
|
||||
正
|
||||
此
|
||||
每
|
||||
比
|
||||
気
|
||||
沒
|
||||
没
|
||||
@@ -711,6 +726,7 @@ z
|
||||
深
|
||||
淺
|
||||
清
|
||||
済
|
||||
渐
|
||||
測
|
||||
源
|
||||
@@ -721,11 +737,13 @@ z
|
||||
為
|
||||
無
|
||||
版
|
||||
特
|
||||
率
|
||||
現
|
||||
生
|
||||
用
|
||||
画
|
||||
界
|
||||
留
|
||||
略
|
||||
當
|
||||
@@ -754,6 +772,7 @@ z
|
||||
篩
|
||||
类
|
||||
紀
|
||||
約
|
||||
索
|
||||
累
|
||||
細
|
||||
@@ -766,6 +785,8 @@ z
|
||||
縮
|
||||
總
|
||||
繁
|
||||
繼
|
||||
續
|
||||
络
|
||||
缩
|
||||
网
|
||||
@@ -785,6 +806,7 @@ z
|
||||
處
|
||||
號
|
||||
表
|
||||
被
|
||||
裡
|
||||
製
|
||||
複
|
||||
@@ -805,8 +827,10 @@ z
|
||||
認
|
||||
語
|
||||
誤
|
||||
読
|
||||
調
|
||||
請
|
||||
讀
|
||||
计
|
||||
议
|
||||
设
|
||||
@@ -815,6 +839,7 @@ z
|
||||
语
|
||||
误
|
||||
请
|
||||
读
|
||||
資
|
||||
贴
|
||||
超
|
||||
@@ -823,6 +848,7 @@ z
|
||||
転
|
||||
輸
|
||||
输
|
||||
込
|
||||
达
|
||||
过
|
||||
近
|
||||
@@ -857,6 +883,7 @@ z
|
||||
關
|
||||
间
|
||||
阈
|
||||
限
|
||||
除
|
||||
隐
|
||||
際
|
||||
@@ -873,8 +900,10 @@ z
|
||||
顯
|
||||
页
|
||||
项
|
||||
预
|
||||
题
|
||||
饋
|
||||
馈
|
||||
體
|
||||
黑
|
||||
가
|
||||
|
||||
9
resources/packaging/linux/AppImage/sniffnet.yml
Normal 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
|
||||
13
resources/repository/badges/linux.svg
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
@@ -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 |
@@ -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 |
BIN
resources/test/ipinfo_lite_sample.mmdb
Normal file
BIN
resources/test/ipinfo_location_sample.mmdb
Normal 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()
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
pub mod chart_type;
|
||||
pub mod donut_chart;
|
||||
pub mod traffic_chart;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub mod types;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod config_device;
|
||||
pub mod config_settings;
|
||||
pub mod config_window;
|
||||
pub mod configs;
|
||||
@@ -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()
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(" …"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/main.rs
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
42
src/networking/types/config_device.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
523
src/networking/types/data_representation.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
@@ -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('}', "")
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use iced::Font;
|
||||
use iced::widget::Text;
|
||||
use rodio::{Decoder, OutputStream, Sink};
|
||||
use rodio::{Decoder, OutputStreamBuilder, Sink};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
|
||||
@@ -63,13 +63,13 @@ pub fn play(sound: Sound, volume: u8) {
|
||||
.name("thread_play_sound".to_string())
|
||||
.spawn(move || {
|
||||
// Get an output stream handle to the default physical sound device
|
||||
let Ok((_stream, stream_handle)) = OutputStream::try_default().log_err(location!())
|
||||
let Ok(mut stream_handle) =
|
||||
OutputStreamBuilder::open_default_stream().log_err(location!())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok(sink) = Sink::try_new(&stream_handle).log_err(location!()) else {
|
||||
return;
|
||||
};
|
||||
stream_handle.log_on_drop(false);
|
||||
let sink = Sink::connect_new(stream_handle.mixer());
|
||||
//load data
|
||||
let data = std::io::Cursor::new(mp3_sound);
|
||||
// Decode that sound file into a source
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod report_col;
|
||||
pub mod report_sort_type;
|
||||
pub mod search_parameters;
|
||||
pub mod sort_type;
|
||||
|
||||
@@ -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...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
pub mod translations_2;
|
||||
pub mod translations_3;
|
||||
pub mod translations_4;
|
||||
pub mod translations_5;
|
||||
pub mod types;
|
||||
|
||||
@@ -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 é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..."
|
||||
),
|
||||
})
|
||||
}
|
||||
// #[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\
|
||||
|
||||
@@ -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 => "Επιθεώρηση",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +75,12 @@ pub fn connection_details_translation(language: Language) -> &'static str {
|
||||
Language::PL => "Szczegóły połączenia",
|
||||
Language::FR => "Détails de la connexion",
|
||||
Language::JA => "接続の詳細",
|
||||
Language::UZ => "Ulanish tafsilotlari",
|
||||
Language::UZ => "Ulanish ma'lumotlari",
|
||||
Language::PT => "Detalhes da conexão",
|
||||
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 => "Verlorene",
|
||||
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 => "Εφαρμογή χρωματικών διαβαθμίσεων",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
@@ -98,7 +101,7 @@ pub fn params_not_editable_translation(language: Language) -> &'static str {
|
||||
Language::RU => "Следующие параметры не могут быть изменены во время анализа трафика",
|
||||
Language::RO => "Următorii parametri nu sunt modificabili în timpul analizei",
|
||||
Language::JA => "以下のパラメーターは分析中は変更できません",
|
||||
Language::UZ => "Tahlil vaqtida quydagi parametrlarni o'zgartirib bo'lmaydi",
|
||||
Language::UZ => "Tahlil vaqtida quyidagi parametrlarni o'zgartirib bo'lmaydi",
|
||||
Language::SV => "Följande parametrar kan inte ändras under analysen",
|
||||
Language::VI => "Các tham số sau không thể bị thay đổi khi đang phân tích",
|
||||
Language::ZH => "以下参数在分析过程中不能修改",
|
||||
@@ -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 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",
|
||||
_ => "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",
|
||||
}
|
||||
}
|
||||
@@ -399,7 +416,7 @@ pub fn filter_by_host_translation(language: Language) -> &'static str {
|
||||
Language::RU => "Фильтр по сетевому хосту",
|
||||
Language::RO => "Filtrează după host-ul de rețea",
|
||||
Language::JA => "ネットワーク ホストでフィルター",
|
||||
Language::UZ => "Tarmoq host bo'yicha filterlash",
|
||||
Language::UZ => "Tarmoq xosti bo'yicha filtrlash",
|
||||
Language::SV => "Filtrera efter nätverksvärd",
|
||||
Language::VI => "Lọc bởi máy chủ mạng",
|
||||
Language::ZH => "按网络主机筛选",
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
@@ -449,7 +468,7 @@ pub fn export_capture_translation(language: Language) -> &'static str {
|
||||
Language::RU => "Экспорт файла захвата",
|
||||
Language::RO => "Export fișier captură",
|
||||
Language::JA => "キャプチャ ファイルをエクスポート",
|
||||
Language::UZ => "Cap faylni export qilish",
|
||||
Language::UZ => "Cap faylni eksport qilish",
|
||||
Language::SV => "Exportera inspelningsfil",
|
||||
Language::VI => "Xuất tập tin đã bắt",
|
||||
Language::ZH => "导出捕获文件",
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
@@ -556,7 +579,7 @@ pub fn thumbnail_mode_translation(language: Language) -> &'static str {
|
||||
Language::RU => "Режим миниатюры",
|
||||
Language::RO => "Mod thumbnail",
|
||||
Language::JA => "サムネイル モード",
|
||||
Language::UZ => "Eskiz rejim",
|
||||
Language::UZ => "Kichik rasm rejimi",
|
||||
Language::SV => "Miniatyrläge",
|
||||
Language::VI => "Chế độ thu nhỏ",
|
||||
Language::ZH => "缩略图模式",
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,19 @@ 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::ES => format!("Dirección reservada ({info})"),
|
||||
Language::RO => format!("Adresă rezervată ({info})"),
|
||||
Language::DE => format!("Reservierte Adresse ({info})"),
|
||||
Language::UZ => format!("Rezervlangan manzil ({info})"),
|
||||
Language::ID => format!("Alamat disimpan ({info})"),
|
||||
Language::EL => format!("Δεσμευμένη διεύθυνση ({info})"),
|
||||
_ => format!("Reserved address ({info})"),
|
||||
}
|
||||
}
|
||||
@@ -22,32 +30,57 @@ 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::ES => "Comparte tus comentarios",
|
||||
Language::RO => "Împărtășiți feedback-ul dvs",
|
||||
Language::DE => "Feedback geben",
|
||||
Language::UZ => "Fikr-mulohazalaringizni ulashing",
|
||||
Language::ID => "Berikan masukanmu",
|
||||
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::ZH_TW => "已排除",
|
||||
Language::NL => "Uitgesloten",
|
||||
Language::ES => "Excluidos",
|
||||
_ => "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",
|
||||
// Language::ID => "Kecuali",
|
||||
// Language::ES => "Excluidos",
|
||||
// _ => "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::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 => "Fișierul de captură",
|
||||
Language::ZH_TW => "擷取文件",
|
||||
Language::ID => "File tangkapan",
|
||||
Language::ES => "Importar archivo de captura",
|
||||
_ => "Import capture file",
|
||||
_ => "Capture file",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +88,17 @@ 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::ES => "Seleccionar archivo de captura",
|
||||
Language::RO => "Selectează fișierul de captură",
|
||||
Language::DE => "Aufzeichnungsdatei auswählen",
|
||||
Language::UZ => "Tahlil faylini tanlang",
|
||||
Language::ID => "Pilih file tangkapan",
|
||||
Language::ZH_TW => "選擇擷取文件",
|
||||
Language::EL => "Επιλογή αρχείου καταγραφής",
|
||||
_ => "Select capture file",
|
||||
}
|
||||
}
|
||||
@@ -74,6 +116,21 @@ 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\
|
||||
@@ -83,11 +140,40 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text
|
||||
"Leyendo paquetes desde el archivo...\n\n\
|
||||
{file_name_translation}: {file}\n\n\
|
||||
¿Seguro que el archivo seleccionado no está vacío?"
|
||||
Language::RO => format!(
|
||||
"Citirea pachetelor din fișier...\n\n\
|
||||
{file_name_translation}: {file}\n\n\
|
||||
Ești sigur că fișierul selectat nu este gol?"
|
||||
),
|
||||
Language::DE => format!(
|
||||
"Pakete aus Datei laden... \n\n\
|
||||
{file_name_translation}: {file}\n\n\
|
||||
Bist du sicher, dass die gewählte Datei nicht leer ist?"
|
||||
),
|
||||
Language::UZ => format!(
|
||||
"Faylni o'qish...\n\n\
|
||||
{file_name_translation}: {file}\n\n\
|
||||
Fayl bo'sh emasligiga aminmisiz?"
|
||||
),
|
||||
Language::ID => format!(
|
||||
"Membaca paket dari berkas...\n\n\
|
||||
{file_name_translation}: {file}\n\n\
|
||||
Apa kamu yakin berkasnya tidak kosong?"
|
||||
),
|
||||
Language::ZH_TW => format!(
|
||||
"從檔案讀取資料包...\n\n\
|
||||
{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?"
|
||||
),
|
||||
})
|
||||
}
|
||||
@@ -96,29 +182,51 @@ 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::ZH_TW => "已超出数据阈值",
|
||||
Language::NL => "Gegevenslimiet overschreden",
|
||||
Language::ES => "Umbral de datos superado",
|
||||
Language::RO => "Limita de date depășită",
|
||||
Language::DE => "Datenschwelle überschritten",
|
||||
Language::UZ => "Ma'lumotlar chegarasidan oshib ketdi",
|
||||
Language::ID => "Ambang batas data terlampaui",
|
||||
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::ES => "Umbral de bits superado",
|
||||
Language::RO => "Limita de biți depășită",
|
||||
Language::DE => "Bitschwelle überschritten",
|
||||
Language::UZ => "Bitlar chegarasidan oshib ketdi",
|
||||
Language::ID => "Ambang batas bit terlampaui",
|
||||
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::ES => "Bits",
|
||||
Language::NL => "Bits",
|
||||
_ => "Bits",
|
||||
Language::EN | Language::IT | Language::NL | Language::DE | Language::FR | Language::ID | Language::ES => {
|
||||
"bits"
|
||||
}
|
||||
Language::JA => "ビット",
|
||||
Language::ZH => "比特",
|
||||
Language::UZ => "bitlar",
|
||||
Language::EL => "Δυφία",
|
||||
Language::RO => "biți",
|
||||
Language::ZH_TW => "位元",
|
||||
_ => "bits",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +234,16 @@ pub fn bits_translation(language: Language) -> &'static str {
|
||||
pub fn pause_translation(language: Language) -> &'static str {
|
||||
match language {
|
||||
Language::EN => "Pause",
|
||||
Language::EN | Language::DE | Language::FR => "Pause",
|
||||
Language::IT | Language::ES => "Pausa",
|
||||
Language::JA => "一時停止",
|
||||
Language::ZH => "暂停",
|
||||
Language::NL => "Pauzeren",
|
||||
Language::RO => "Pauză",
|
||||
Language::UZ => "To'xtatish",
|
||||
Language::ID => "Dijeda",
|
||||
Language::ZH_TW => "暫停",
|
||||
Language::EL => "Παύση",
|
||||
_ => "Pause",
|
||||
}
|
||||
}
|
||||
@@ -137,8 +253,17 @@ 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::ES => "Reanudar",
|
||||
Language::RO => "Continuă",
|
||||
Language::DE => "Fortsetzen",
|
||||
Language::UZ => "Davom ettirish",
|
||||
Language::ID => "Dilanjut",
|
||||
Language::ZH_TW => "繼續",
|
||||
Language::EL => "Συνέχεια",
|
||||
_ => "Resume",
|
||||
}
|
||||
}
|
||||
|
||||
21
src/translations/translations_5.rs
Normal 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",
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,21 @@ 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)
|
||||
matches!(
|
||||
self,
|
||||
Language::EN
|
||||
| Language::IT
|
||||
| Language::NL
|
||||
| Language::DE
|
||||
| Language::UZ
|
||||
| Language::ZH
|
||||
| Language::JA
|
||||
| Language::FR
|
||||
| Language::EL
|
||||
| Language::RO
|
||||
| Language::ZH_TW
|
||||
| Language::ID
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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!(
|
||||
|
||||