Compare commits

...

545 commits

Author SHA1 Message Date
Pierre
df0dacb85a
Merge pull request #309 from ivre/dependabot/cargo/chrono-0.4.42
chore(deps): bump chrono from 0.4.41 to 0.4.42
2025-09-10 10:16:54 +02:00
dependabot[bot]
d47b3d7b62
chore(deps): bump chrono from 0.4.41 to 0.4.42
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.41 to 0.4.42.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.41...v0.4.42)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 00:04:13 +00:00
Pierre
ff8b360021
Merge pull request #307 from ivre/dependabot/cargo/bitflags-2.9.4
chore(deps): bump bitflags from 2.9.1 to 2.9.4
2025-09-06 00:05:09 +02:00
dependabot[bot]
6fd6872372
chore(deps): bump bitflags from 2.9.1 to 2.9.4
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.9.1 to 2.9.4.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.9.1...2.9.4)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-version: 2.9.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 21:59:33 +00:00
Pierre
6cc6232066
Merge pull request #308 from ivre/dependabot/cargo/log-0.4.28
chore(deps): bump log from 0.4.27 to 0.4.28
2025-09-05 23:58:51 +02:00
Pierre
40f1c38f4c
Merge pull request #306 from ivre/dependabot/cargo/clap-4.5.47
chore(deps): bump clap from 4.5.42 to 4.5.47
2025-09-05 23:58:25 +02:00
dependabot[bot]
ebf55aff47
chore(deps): bump log from 0.4.27 to 0.4.28
Bumps [log](https://github.com/rust-lang/log) from 0.4.27 to 0.4.28.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.27...0.4.28)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 08:58:31 +00:00
dependabot[bot]
de8d6bfafc
chore(deps): bump clap from 4.5.42 to 4.5.47
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.42 to 4.5.47.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.42...clap_complete-v4.5.47)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-03 15:38:31 +00:00
dependabot[bot]
76704c71c4
chore(deps): bump clap from 4.5.40 to 4.5.42 (#299)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.40 to 4.5.42.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.40...clap_complete-v4.5.42)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 23:59:25 +02:00
dependabot[bot]
68db6e757e
chore(deps): bump strum from 0.27.1 to 0.27.2 (#298)
---
updated-dependencies:
- dependency-name: strum
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 23:42:09 +02:00
dependabot[bot]
48070b0fca
chore(deps): bump strum_macros from 0.27.1 to 0.27.2 (#297)
---
updated-dependencies:
- dependency-name: strum_macros
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 23:14:54 +02:00
dependabot[bot]
b5b8dbd328
chore(deps): bump rand from 0.9.1 to 0.9.2 (#296)
---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 23:14:19 +02:00
dependabot[bot]
a52ae83fdd
chore(deps): bump pcap from 2.2.0 to 2.3.0 (#295)
---
updated-dependencies:
- dependency-name: pcap
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 23:07:04 +02:00
Pierre
ce33df4ffc
Merge pull request #293 from ivre/dependabot/cargo/clap-4.5.40
chore(deps): bump clap from 4.5.38 to 4.5.40
2025-06-15 22:08:03 +02:00
Pierre
4c14fe4c4c
Merge pull request #292 from ivre/dependabot/cargo/flate2-1.1.2
chore(deps): bump flate2 from 1.1.1 to 1.1.2
2025-06-15 22:07:41 +02:00
dependabot[bot]
2f3dccfd7f
chore(deps): bump clap from 4.5.38 to 4.5.40
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.38 to 4.5.40.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.38...clap_complete-v4.5.40)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.40
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-10 00:30:36 +00:00
dependabot[bot]
e31cab4b4f
chore(deps): bump flate2 from 1.1.1 to 1.1.2
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.1.1...1.1.2)

---
updated-dependencies:
- dependency-name: flate2
  dependency-version: 1.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 00:19:35 +00:00
Pierre
53cb47f493
Merge pull request #290 from ivre/dependabot/cargo/bitflags-2.9.1
chore(deps): bump bitflags from 2.9.0 to 2.9.1
2025-05-16 17:26:27 +02:00
dependabot[bot]
70d74ce840
chore(deps): bump bitflags from 2.9.0 to 2.9.1
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.9.0...2.9.1)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-version: 2.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-16 00:13:01 +00:00
Pierre
046145ac90
Merge pull request #289 from ivre/dependabot/cargo/clap-4.5.38
chore(deps): bump clap from 4.5.37 to 4.5.38
2025-05-15 10:32:11 +02:00
dependabot[bot]
9c04957c32
chore(deps): bump clap from 4.5.37 to 4.5.38
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.37 to 4.5.38.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.37...clap_complete-v4.5.38)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.38
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 00:32:45 +00:00
Pierre
8e25fae44d
Merge pull request #286 from ivre/dependabot/cargo/chrono-0.4.41
chore(deps): bump chrono from 0.4.39 to 0.4.41
2025-05-05 23:50:59 +02:00
dependabot[bot]
aa91ed8c26
chore(deps): bump chrono from 0.4.39 to 0.4.41
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.39 to 0.4.41.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.39...v0.4.41)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 21:03:19 +00:00
Pierre
ab748df291
Merge pull request #287 from ivre/dependabot/cargo/strum_macros-0.27.1
chore(deps): bump strum_macros from 0.26.4 to 0.27.1
2025-05-05 23:02:47 +02:00
Pierre
092e6821ec
Merge pull request #285 from ivre/dependabot/cargo/flate2-1.1.1
chore(deps): bump flate2 from 1.0.35 to 1.1.1
2025-05-05 23:02:15 +02:00
Pierre
1a8566c736
Merge pull request #284 from ivre/dependabot/cargo/rand-0.9.1
chore(deps): bump rand from 0.8.5 to 0.9.1
2025-05-05 23:01:54 +02:00
Pierre
34db7abc0e
Merge pull request #283 from ivre/dependabot/cargo/clap-4.5.37
chore(deps): bump clap from 4.5.34 to 4.5.37
2025-05-05 23:01:32 +02:00
dependabot[bot]
5d8c332043
chore(deps): bump strum_macros from 0.26.4 to 0.27.1
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.26.4 to 0.27.1.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits/v0.27.1)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-version: 0.27.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 20:45:15 +00:00
dependabot[bot]
93807104c4
chore(deps): bump flate2 from 1.0.35 to 1.1.1
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.35 to 1.1.1.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.35...1.1.1)

---
updated-dependencies:
- dependency-name: flate2
  dependency-version: 1.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 20:44:54 +00:00
dependabot[bot]
3d2ae6a62a
chore(deps): bump rand from 0.8.5 to 0.9.1
Bumps [rand](https://github.com/rust-random/rand) from 0.8.5 to 0.9.1.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.8.5...rand_core-0.9.1)

---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 20:44:42 +00:00
dependabot[bot]
4f54661828
chore(deps): bump clap from 4.5.34 to 4.5.37
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.34 to 4.5.37.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.34...clap_complete-v4.5.37)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 20:44:26 +00:00
Pierre
d5feca7d21
CI: apply flake8 fixes (#288) 2025-05-05 22:07:23 +02:00
Pierre
1f113a09c3
Merge pull request #264 from ivre/dependabot/cargo/itertools-0.14.0
chore(deps): bump itertools from 0.13.0 to 0.14.0
2025-05-03 15:17:23 +02:00
Pierre
e8d595ec52
Merge pull request #274 from ivre/dependabot/cargo/strum-0.27.1
chore(deps): bump strum from 0.26.3 to 0.27.1
2025-05-03 15:17:07 +02:00
Pierre
3e3a7b5fd0
Merge pull request #278 from ivre/dependabot/cargo/bitflags-2.9.0
chore(deps): bump bitflags from 2.6.0 to 2.9.0
2025-05-03 15:16:49 +02:00
Pierre
98d1631ca1
Merge pull request #280 from ivre/dependabot/cargo/log-0.4.27
chore(deps): bump log from 0.4.22 to 0.4.27
2025-05-03 15:16:29 +02:00
Pierre
4cc2ed2bf4
Merge pull request #282 from ivre/dependabot/cargo/clap-4.5.34
chore(deps): bump clap from 4.5.23 to 4.5.34
2025-05-03 15:16:06 +02:00
dependabot[bot]
e9dca669e9
chore(deps): bump clap from 4.5.23 to 4.5.34
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.23 to 4.5.34.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.23...clap_complete-v4.5.34)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-28 00:57:48 +00:00
dependabot[bot]
ec1d564897
chore(deps): bump log from 0.4.22 to 0.4.27
Bumps [log](https://github.com/rust-lang/log) from 0.4.22 to 0.4.27.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.22...0.4.27)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 01:51:07 +00:00
dependabot[bot]
9ba56354f3
chore(deps): bump bitflags from 2.6.0 to 2.9.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.6.0 to 2.9.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.6.0...2.9.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 00:14:53 +00:00
dependabot[bot]
966bf7fad3
chore(deps): bump strum from 0.26.3 to 0.27.1
Bumps [strum](https://github.com/Peternator7/strum) from 0.26.3 to 0.27.1.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/compare/v0.26.3...v0.27.1)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 00:26:28 +00:00
dependabot[bot]
0e971f0306
chore(deps): bump itertools from 0.13.0 to 0.14.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.13.0 to 0.14.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-01 00:55:49 +00:00
Pierre
9345a1294b
Merge pull request #263 from ivre/dependabot/cargo/chrono-0.4.39
chore(deps): bump chrono from 0.4.38 to 0.4.39
2024-12-29 21:35:06 +01:00
dependabot[bot]
a01f63839c
chore(deps): bump chrono from 0.4.38 to 0.4.39
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.38 to 0.4.39.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.38...v0.4.39)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 22:46:53 +00:00
Pierre
42d8b70d28
Merge pull request #260 from ivre/dependabot/cargo/flate2-1.0.35
chore(deps): bump flate2 from 1.0.34 to 1.0.35
2024-12-16 23:46:20 +01:00
Pierre
da0a3b02ab
Merge pull request #262 from ivre/dependabot/cargo/clap-4.5.23
chore(deps): bump clap from 4.5.20 to 4.5.23
2024-12-16 23:45:53 +01:00
dependabot[bot]
d43ab4716f
chore(deps): bump clap from 4.5.20 to 4.5.23
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.20 to 4.5.23.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.20...clap_complete-v4.5.23)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-06 00:09:33 +00:00
dependabot[bot]
e7d573b4db
chore(deps): bump flate2 from 1.0.34 to 1.0.35
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.34 to 1.0.35.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Changelog](https://github.com/rust-lang/flate2-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.34...1.0.35)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-15 00:50:33 +00:00
Pierre
ca3d7dc6f1
Merge pull request #255 from ivre/dependabot/cargo/flate2-1.0.34
chore(deps): bump flate2 from 1.0.33 to 1.0.34
2024-10-14 23:41:03 +02:00
Pierre
e6454d5f68
Merge pull request #257 from ivre/dependabot/cargo/clap-4.5.20
chore(deps): bump clap from 4.5.17 to 4.5.20
2024-10-14 23:40:53 +02:00
dependabot[bot]
695b132605
chore(deps): bump flate2 from 1.0.33 to 1.0.34
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.33 to 1.0.34.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Changelog](https://github.com/rust-lang/flate2-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.33...1.0.34)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-14 21:37:01 +00:00
dependabot[bot]
34ca9b6ae0
chore(deps): bump clap from 4.5.17 to 4.5.20
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.17 to 4.5.20.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.17...clap_complete-v4.5.20)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-14 21:36:45 +00:00
Pierre
bb8c5fa184
Merge pull request #258 from p-l-/update-upload-artifact
chore(deps): bump {down,up}load-artifact from v2 to v4
2024-10-14 23:35:50 +02:00
Pierre Lalet
64698a6625 Tests: update Scapy to current stable 2024-10-14 18:38:31 +02:00
Pierre Lalet
fe0312b066 Tests: lock Scapy version to 2.5.0 2024-10-14 18:07:09 +02:00
Pierre Lalet
c852680930 Tests: add values to assert messages 2024-10-14 16:30:11 +02:00
Pierre Lalet
f5d40dd1a8 chore(deps): bump {down,up}load-artifact from v2 to v4 2024-10-14 16:30:11 +02:00
Pierre
7fd54daafd
Merge pull request #253 from ivre/dependabot/cargo/clap-4.5.17
chore(deps): bump clap from 4.5.16 to 4.5.17
2024-09-11 11:58:08 +02:00
dependabot[bot]
5b99525852
chore(deps): bump clap from 4.5.16 to 4.5.17
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.16 to 4.5.17.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.16...clap_complete-v4.5.17)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-05 00:31:52 +00:00
Pierre
bb95ceeef0
Merge pull request #252 from ivre/dependabot/cargo/pcap-2.2.0
chore(deps): bump pcap from 2.1.0 to 2.2.0
2024-09-02 11:00:42 +02:00
dependabot[bot]
436026f83c
chore(deps): bump pcap from 2.1.0 to 2.2.0
Bumps [pcap](https://github.com/rust-pcap/pcap) from 2.1.0 to 2.2.0.
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 00:34:15 +00:00
Pierre
a83f80f8c8
Merge pull request #250 from ivre/dependabot/cargo/flate2-1.0.33
chore(deps): bump flate2 from 1.0.31 to 1.0.33
2024-09-01 10:29:42 +02:00
Pierre
c01ab14225
Merge pull request #251 from ivre/dependabot/cargo/pcap-2.1.0
chore(deps): bump pcap from 2.0.0 to 2.1.0
2024-09-01 10:29:12 +02:00
dependabot[bot]
6e133021b7
chore(deps): bump pcap from 2.0.0 to 2.1.0
Bumps [pcap](https://github.com/rust-pcap/pcap) from 2.0.0 to 2.1.0.
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-28 00:32:06 +00:00
dependabot[bot]
e07aacd2a3
chore(deps): bump flate2 from 1.0.31 to 1.0.33
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.31 to 1.0.33.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Changelog](https://github.com/rust-lang/flate2-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.31...1.0.33)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 00:29:19 +00:00
Pierre
a41cdc4dd3
Merge pull request #248 from ivre/dependabot/cargo/clap-4.5.16
chore(deps): bump clap from 4.5.11 to 4.5.16
2024-08-16 08:34:29 +02:00
Pierre
fa94f2a809
Merge pull request #245 from ivre/dependabot/cargo/flate2-1.0.31
chore(deps): bump flate2 from 1.0.30 to 1.0.31
2024-08-16 08:34:21 +02:00
dependabot[bot]
515a4752a0
chore(deps): bump clap from 4.5.11 to 4.5.16
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.11 to 4.5.16.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.11...clap_complete-v4.5.16)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-16 00:46:30 +00:00
dependabot[bot]
28da801ceb
chore(deps): bump flate2 from 1.0.30 to 1.0.31
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.30 to 1.0.31.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/commits)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 00:27:36 +00:00
Pierre
6ea7ec069a
Merge pull request #243 from ivre/dependabot/cargo/clap-4.5.11
chore(deps): bump clap from 4.5.9 to 4.5.11
2024-07-29 09:30:53 +02:00
dependabot[bot]
0969f3f59b
chore(deps): bump clap from 4.5.9 to 4.5.11
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.9 to 4.5.11.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.9...clap_complete-v4.5.11)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-26 00:19:34 +00:00
Pierre
7b8f79903e
Merge pull request #241 from ivre/dependabot/cargo/clap-4.5.9
chore(deps): bump clap from 4.5.8 to 4.5.9
2024-07-12 21:24:15 +02:00
dependabot[bot]
1428fec25b
chore(deps): bump clap from 4.5.8 to 4.5.9
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.8 to 4.5.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.8...v4.5.9)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 00:56:43 +00:00
Pierre
ec828f83b3
Merge pull request #236 from ivre/dependabot/cargo/lazy_static-1.5.0
chore(deps): bump lazy_static from 1.4.0 to 1.5.0
2024-07-08 22:40:17 +02:00
dependabot[bot]
e540a1e0f5
chore(deps): bump lazy_static from 1.4.0 to 1.5.0
Bumps [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/rust-lang-nursery/lazy-static.rs/releases)
- [Commits](https://github.com/rust-lang-nursery/lazy-static.rs/compare/1.4.0...1.5.0)

---
updated-dependencies:
- dependency-name: lazy_static
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-08 20:37:00 +00:00
Pierre
b1af76e4fd
Merge pull request #237 from ivre/dependabot/cargo/strum-0.26.3
chore(deps): bump strum from 0.26.2 to 0.26.3
2024-07-08 22:36:50 +02:00
Pierre
058790f07c
Merge pull request #238 from ivre/dependabot/cargo/bitflags-2.6.0
chore(deps): bump bitflags from 2.5.0 to 2.6.0
2024-07-08 22:36:34 +02:00
Pierre
fd26da0652
Merge pull request #239 from ivre/dependabot/cargo/log-0.4.22
chore(deps): bump log from 0.4.21 to 0.4.22
2024-07-08 22:36:17 +02:00
Pierre
e8922074e3
Merge pull request #240 from ivre/dependabot/cargo/clap-4.5.8
chore(deps): bump clap from 4.5.7 to 4.5.8
2024-07-08 22:36:00 +02:00
dependabot[bot]
08a8c7f3a9
chore(deps): bump clap from 4.5.7 to 4.5.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.7 to 4.5.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.7...v4.5.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 00:52:46 +00:00
dependabot[bot]
a78934b911
chore(deps): bump log from 0.4.21 to 0.4.22
Bumps [log](https://github.com/rust-lang/log) from 0.4.21 to 0.4.22.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.21...0.4.22)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 00:52:16 +00:00
dependabot[bot]
d563bb36a5
chore(deps): bump bitflags from 2.5.0 to 2.6.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.5.0...2.6.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-25 00:42:27 +00:00
dependabot[bot]
3d3058dc94
chore(deps): bump strum from 0.26.2 to 0.26.3
Bumps [strum](https://github.com/Peternator7/strum) from 0.26.2 to 0.26.3.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/compare/v0.26.2...v0.26.3)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 00:56:29 +00:00
Pierre
4666ae3785
Merge pull request #235 from ivre/dependabot/cargo/clap-4.5.7
chore(deps): bump clap from 4.5.6 to 4.5.7
2024-06-15 22:33:47 +02:00
dependabot[bot]
04ab094f0f
chore(deps): bump clap from 4.5.6 to 4.5.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.6 to 4.5.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.6...v4.5.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 00:28:18 +00:00
Pierre
625672f8f8
Merge pull request #234 from ivre/dependabot/cargo/clap-4.5.6
chore(deps): bump clap from 4.5.4 to 4.5.6
2024-06-07 22:07:42 +02:00
Pierre
84cc2dce86
Merge pull request #233 from ivre/dependabot/cargo/strum_macros-0.26.4
chore(deps): bump strum_macros from 0.26.2 to 0.26.4
2024-06-07 22:07:19 +02:00
dependabot[bot]
24be84814c
chore(deps): bump clap from 4.5.4 to 4.5.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.4 to 4.5.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.4...v4.5.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-07 00:30:03 +00:00
dependabot[bot]
91ec1d2b1b
chore(deps): bump strum_macros from 0.26.2 to 0.26.4
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.26.2 to 0.26.4.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-06 00:52:29 +00:00
Pierre
45f815d60a
Merge pull request #231 from ivre/dependabot/cargo/itertools-0.13.0
chore(deps): bump itertools from 0.12.1 to 0.13.0
2024-05-18 04:16:40 +02:00
dependabot[bot]
101a62c090
chore(deps): bump itertools from 0.12.1 to 0.13.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.1 to 0.13.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.1...v0.13.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-17 00:24:29 +00:00
Pierre
36f24b8977
Merge pull request #230 from ivre/dependabot/cargo/flate2-1.0.30
chore(deps): bump flate2 from 1.0.29 to 1.0.30
2024-05-07 23:44:06 +02:00
dependabot[bot]
570829cee6
chore(deps): bump flate2 from 1.0.29 to 1.0.30
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.29 to 1.0.30.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.29...1.0.30)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-30 00:34:22 +00:00
Pierre
9fdfd8bf72
Merge pull request #229 from ivre/dependabot/cargo/flate2-1.0.29
chore(deps): bump flate2 from 1.0.28 to 1.0.29
2024-04-29 14:09:51 +02:00
dependabot[bot]
6c34c625f1
chore(deps): bump flate2 from 1.0.28 to 1.0.29
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.28 to 1.0.29.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.28...1.0.29)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 01:00:54 +00:00
dependabot[bot]
65e053e842
chore(deps): bump pcap from 1.3.0 to 2.0.0 (#228)
* chore(deps): bump pcap from 1.3.0 to 2.0.0

Bumps [pcap](https://github.com/rust-pcap/pcap) from 1.3.0 to 2.0.0.
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/commits)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-23 23:12:32 +02:00
_Frky
623a144609
Merge pull request #216 from p-l-/fix-docker
Docker: use rust image as builder (latest version)
2024-04-23 22:53:40 +02:00
Pierre
631b6868d6
Merge pull request #227 from ivre/dependabot/cargo/chrono-0.4.38
chore(deps): bump chrono from 0.4.37 to 0.4.38
2024-04-16 14:04:48 +02:00
dependabot[bot]
126425833e
chore(deps): bump chrono from 0.4.37 to 0.4.38
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.37 to 0.4.38.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.37...v0.4.38)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-16 00:44:38 +00:00
Pierre
1b3755df91
Merge pull request #226 from ivre/dependabot/cargo/chrono-0.4.37
chore(deps): bump chrono from 0.4.35 to 0.4.37
2024-03-28 16:03:55 +01:00
dependabot[bot]
fb547aff3b
chore(deps): bump chrono from 0.4.35 to 0.4.37
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.35 to 0.4.37.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.35...v0.4.37)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-28 00:44:05 +00:00
Pierre
7fc4ac2441
Merge pull request #225 from ivre/dependabot/cargo/clap-4.5.4
chore(deps): bump clap from 4.5.3 to 4.5.4
2024-03-26 17:03:39 +01:00
dependabot[bot]
bd799eaed7
chore(deps): bump clap from 4.5.3 to 4.5.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.3 to 4.5.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.3...v4.5.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-26 00:04:00 +00:00
Pierre
16da61c797
Merge pull request #224 from ivre/dependabot/cargo/bitflags-2.5.0
chore(deps): bump bitflags from 2.4.2 to 2.5.0
2024-03-20 11:32:52 +01:00
dependabot[bot]
a3e0f468c2
chore(deps): bump bitflags from 2.4.2 to 2.5.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.4.2 to 2.5.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.4.2...2.5.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-20 00:03:36 +00:00
Pierre
40191729db
Merge pull request #223 from ivre/dependabot/cargo/pcap-1.3.0
chore(deps): bump pcap from 1.2.0 to 1.3.0
2024-03-18 22:28:47 +01:00
Pierre
456b9a92b6
Merge pull request #222 from ivre/dependabot/cargo/clap-4.5.3
chore(deps): bump clap from 4.5.2 to 4.5.3
2024-03-18 22:28:29 +01:00
Pierre
85c88a94e4
Merge pull request #221 from ivre/dependabot/cargo/siphasher-1.0.1
chore(deps): bump siphasher from 1.0.0 to 1.0.1
2024-03-18 22:27:16 +01:00
dependabot[bot]
20f422dfba
chore(deps): bump pcap from 1.2.0 to 1.3.0
Bumps [pcap](https://github.com/rust-pcap/pcap) from 1.2.0 to 1.3.0.
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/commits)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 00:49:22 +00:00
dependabot[bot]
7787da5a5d
chore(deps): bump clap from 4.5.2 to 4.5.3
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.2 to 4.5.3.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.2...v4.5.3)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 00:49:09 +00:00
dependabot[bot]
f903d9db86
chore(deps): bump siphasher from 1.0.0 to 1.0.1
Bumps [siphasher](https://github.com/jedisct1/rust-siphash) from 1.0.0 to 1.0.1.
- [Commits](https://github.com/jedisct1/rust-siphash/compare/1.0.0...1.0.1)

---
updated-dependencies:
- dependency-name: siphasher
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 00:48:53 +00:00
Pierre
ec6a7856c7
Merge pull request #219 from ivre/dependabot/cargo/strum_macros-0.26.2
chore(deps): bump strum_macros from 0.26.1 to 0.26.2
2024-03-11 21:40:19 +01:00
dependabot[bot]
42821d2ae7
chore(deps): bump strum_macros from 0.26.1 to 0.26.2
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 12:05:54 +00:00
Pierre
c74f24bcdb
Merge pull request #220 from ivre/dependabot/cargo/strum-0.26.2
chore(deps): bump strum from 0.26.1 to 0.26.2
2024-03-11 13:05:09 +01:00
dependabot[bot]
f1bb7c40da
chore(deps): bump strum from 0.26.1 to 0.26.2
Bumps [strum](https://github.com/Peternator7/strum) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 00:43:22 +00:00
Pierre
8f2cea06a0
Merge pull request #217 from ivre/dependabot/cargo/chrono-0.4.35
chore(deps): bump chrono from 0.4.34 to 0.4.35
2024-03-07 16:30:01 +01:00
dependabot[bot]
9e7e816cdd
chore(deps): bump chrono from 0.4.34 to 0.4.35
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.34 to 0.4.35.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.34...v0.4.35)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 14:35:34 +00:00
Pierre
f68e6e3484
Merge pull request #218 from ivre/dependabot/cargo/clap-4.5.2
chore(deps): bump clap from 4.5.1 to 4.5.2
2024-03-07 15:26:30 +01:00
dependabot[bot]
70e9edb8ea
chore(deps): bump clap from 4.5.1 to 4.5.2
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.1 to 4.5.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.1...v4.5.2)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 00:26:47 +00:00
Pierre
8a34ff6214
Merge pull request #215 from ivre/dependabot/cargo/log-0.4.21
chore(deps): bump log from 0.4.20 to 0.4.21
2024-03-01 01:52:01 +01:00
Pierre Lalet
4a24a30ee6 CI: add a Docker build job 2024-03-01 01:46:38 +01:00
Pierre Lalet
b29a95da7b Docker: usr rust image as builder (latest version) 2024-03-01 01:28:13 +01:00
dependabot[bot]
db8d1dbf52
chore(deps): bump log from 0.4.20 to 0.4.21
Bumps [log](https://github.com/rust-lang/log) from 0.4.20 to 0.4.21.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.20...0.4.21)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-29 00:32:25 +00:00
Pierre
55f8509209
Merge pull request #214 from ivre/dependabot/cargo/clap-4.5.1
chore(deps): bump clap from 4.5.0 to 4.5.1
2024-02-28 01:04:37 +01:00
dependabot[bot]
9ca4609b66
chore(deps): bump clap from 4.5.0 to 4.5.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.0...clap_complete-v4.5.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 01:16:19 +00:00
Pierre
b677b1c4be
Merge pull request #212 from ivre/dependabot/cargo/clap-4.5.0
chore(deps): bump clap from 4.4.18 to 4.5.0
2024-02-18 21:26:43 +01:00
dependabot[bot]
647ca441ad
chore(deps): bump clap from 4.4.18 to 4.5.0
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.18 to 4.5.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.18...clap_complete-v4.5.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-14 21:32:46 +00:00
Pierre
8690aed6d7
Merge pull request #213 from ivre/dependabot/cargo/chrono-0.4.34
chore(deps): bump chrono from 0.4.33 to 0.4.34
2024-02-14 22:00:40 +01:00
dependabot[bot]
d1d035890a
chore(deps): bump chrono from 0.4.33 to 0.4.34
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.33 to 0.4.34.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.33...v0.4.34)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 01:12:33 +00:00
Pierre
69af3a42c0
Merge pull request #209 from ivre/dependabot/cargo/strum-0.26.1
Bump strum from 0.25.0 to 0.26.1
2024-02-06 14:52:37 +01:00
dependabot[bot]
1cdc7b0b31
Bump strum from 0.25.0 to 0.26.1
Bumps [strum](https://github.com/Peternator7/strum) from 0.25.0 to 0.26.1.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-06 13:38:21 +00:00
Pierre
0354f68b72
Merge pull request #210 from ivre/dependabot/cargo/itertools-0.12.1
Bump itertools from 0.12.0 to 0.12.1
2024-02-06 14:37:58 +01:00
Pierre
ab4cf78638
Merge pull request #211 from ivre/dependabot/cargo/stderrlog-0.6.0
Bump stderrlog from 0.5.4 to 0.6.0
2024-02-06 14:37:35 +01:00
Pierre
936207f0a3
Merge pull request #208 from ivre/dependabot/cargo/strum_macros-0.26.1
Bump strum_macros from 0.25.3 to 0.26.1
2024-02-06 14:37:05 +01:00
dependabot[bot]
dc1d0740ad
Bump stderrlog from 0.5.4 to 0.6.0
Bumps [stderrlog](https://github.com/cardoe/stderrlog-rs) from 0.5.4 to 0.6.0.
- [Release notes](https://github.com/cardoe/stderrlog-rs/releases)
- [Changelog](https://github.com/cardoe/stderrlog-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cardoe/stderrlog-rs/compare/v0.5.4...v0.6.0)

---
updated-dependencies:
- dependency-name: stderrlog
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 00:31:31 +00:00
dependabot[bot]
541b1c2887
Bump itertools from 0.12.0 to 0.12.1
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.0 to 0.12.1.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.0...v0.12.1)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-30 00:42:04 +00:00
dependabot[bot]
283cf235b8
Bump strum_macros from 0.25.3 to 0.26.1
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.25.3 to 0.26.1.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-29 01:01:28 +00:00
Pierre
14a1387251
Merge pull request #207 from ivre/dependabot/cargo/chrono-0.4.33
Bump chrono from 0.4.31 to 0.4.33
2024-01-26 13:18:45 +01:00
dependabot[bot]
80abf6b685
Bump chrono from 0.4.31 to 0.4.33
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.31 to 0.4.33.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.31...v0.4.33)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-26 01:00:57 +00:00
Pierre
c383c6ed88
Merge pull request #205 from ivre/dependabot/cargo/pcap-1.2.0
Bump pcap from 1.1.0 to 1.2.0
2024-01-22 10:12:48 +01:00
dependabot[bot]
10c353940f
Bump pcap from 1.1.0 to 1.2.0
Bumps [pcap](https://github.com/rust-pcap/pcap) from 1.1.0 to 1.2.0.
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/compare/v1.1.0...v1.2.0)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 01:12:22 +00:00
Pierre
656bf3951e
Merge pull request #203 from ivre/dependabot/cargo/bitflags-2.4.2
Bump bitflags from 2.4.1 to 2.4.2
2024-01-19 22:40:22 +01:00
Pierre
8b5ba06ae3
Merge pull request #204 from ivre/dependabot/cargo/clap-4.4.18
Bump clap from 4.4.17 to 4.4.18
2024-01-19 22:40:14 +01:00
dependabot[bot]
513cc8f43a
Bump clap from 4.4.17 to 4.4.18
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.17 to 4.4.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.17...v4.4.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-17 00:53:07 +00:00
dependabot[bot]
e0aa8ae62c
Bump bitflags from 2.4.1 to 2.4.2
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.4.1 to 2.4.2.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.4.1...2.4.2)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-17 00:52:54 +00:00
Pierre
0541223ee1
Merge pull request #202 from ivre/dependabot/cargo/clap-4.4.17
Bump clap from 4.4.15 to 4.4.17
2024-01-16 02:13:33 +01:00
dependabot[bot]
9d9b8b2757
Bump clap from 4.4.15 to 4.4.17
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.15 to 4.4.17.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.15...v4.4.17)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-16 00:49:49 +00:00
Pierre
890f2abb55
Merge pull request #200 from ivre/dependabot/cargo/clap-4.4.15
Bump clap from 4.4.14 to 4.4.15
2024-01-13 14:36:52 +01:00
dependabot[bot]
fa7c35049d
Bump clap from 4.4.14 to 4.4.15
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.14 to 4.4.15.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.14...v4.4.15)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-12 00:24:38 +00:00
Pierre
b7ae19c109
Merge pull request #199 from ivre/dependabot/cargo/clap-4.4.14
Bump clap from 4.4.13 to 4.4.14
2024-01-09 10:37:03 +01:00
dependabot[bot]
627d1ec22b
Bump clap from 4.4.13 to 4.4.14
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.13 to 4.4.14.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.13...v4.4.14)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 00:11:54 +00:00
Pierre
4ccb4f05d7
Merge pull request #198 from ivre/dependabot/cargo/clap-4.4.13
Bump clap from 4.4.11 to 4.4.13
2024-01-05 09:58:30 +01:00
dependabot[bot]
254e6ebc7e
Bump clap from 4.4.11 to 4.4.13
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.11 to 4.4.13.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.11...v4.4.13)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-05 00:44:28 +00:00
Pierre
be4d9480ab
Merge pull request #196 from ivre/dependabot/cargo/clap-4.4.11
Bump clap from 4.4.10 to 4.4.11
2023-12-05 10:55:58 +01:00
dependabot[bot]
104a91839a
Bump clap from 4.4.10 to 4.4.11
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.10 to 4.4.11.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.10...v4.4.11)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-05 00:59:01 +00:00
Pierre
050cbdd897
Merge pull request #195 from ivre/dependabot/cargo/clap-4.4.10
Bump clap from 4.4.9 to 4.4.10
2023-11-29 20:21:49 +01:00
dependabot[bot]
f2ae05b3af
Bump clap from 4.4.9 to 4.4.10
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.9 to 4.4.10.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.9...v4.4.10)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 00:50:14 +00:00
Pierre
227af61527
Merge pull request #194 from ivre/dependabot/cargo/clap-4.4.9
Bump clap from 4.4.8 to 4.4.9
2023-11-28 20:45:14 +01:00
dependabot[bot]
e96cb84ed4
Bump clap from 4.4.8 to 4.4.9
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.8 to 4.4.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.8...v4.4.9)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 01:02:14 +00:00
Pierre
b8eae57022
Merge pull request #193 from ivre/dependabot/cargo/itertools-0.12.0
Bump itertools from 0.11.0 to 0.12.0
2023-11-18 15:34:29 +01:00
dependabot[bot]
d134404860
Bump itertools from 0.11.0 to 0.12.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.11.0 to 0.12.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-15 00:06:40 +00:00
Pierre
35b4a53b0b
Merge pull request #192 from ivre/dependabot/cargo/clap-4.4.8
Bump clap from 4.4.7 to 4.4.8
2023-11-13 13:52:34 +01:00
dependabot[bot]
4ea3a8df38
Bump clap from 4.4.7 to 4.4.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.7 to 4.4.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.7...v4.4.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 00:27:57 +00:00
_Frky
9e23e342a6
Merge pull request #190 from p-l-/fix-warning
Remove unneeded .clone() call
2023-11-03 06:28:35 +01:00
_Frky
3ceff8e112
Merge pull request #191 from ivre/dependabot/cargo/clap-4.4.7
Bump clap from 4.4.6 to 4.4.7
2023-11-03 06:27:47 +01:00
dependabot[bot]
006bf6713a
Bump clap from 4.4.6 to 4.4.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.6 to 4.4.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.6...v4.4.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-25 00:48:11 +00:00
Pierre Lalet
b642c34e42 Remove unneeded .clone() call
`pattern` is a reference, and according to `cargo test` output: "the
type `[u8]` does not implement `Clone`, so calling `clone` on `&[u8]`
copies the reference, which does not do anything and can be removed".
2023-10-18 02:19:51 +02:00
Pierre
7406a1a191
Merge pull request #189 from ivre/dependabot/cargo/flate2-1.0.28
Bump flate2 from 1.0.27 to 1.0.28
2023-10-18 02:13:07 +02:00
Pierre
e5d9633904
Merge pull request #188 from ivre/dependabot/cargo/strum_macros-0.25.3
Bump strum_macros from 0.25.2 to 0.25.3
2023-10-18 02:11:57 +02:00
Pierre
e1756f9345
Merge pull request #187 from ivre/dependabot/cargo/bitflags-2.4.1
Bump bitflags from 2.4.0 to 2.4.1
2023-10-18 02:11:03 +02:00
dependabot[bot]
07b9af36f3
Bump flate2 from 1.0.27 to 1.0.28
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.27 to 1.0.28.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.27...1.0.28)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 01:02:31 +00:00
dependabot[bot]
521236b60c
Bump strum_macros from 0.25.2 to 0.25.3
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.25.2 to 0.25.3.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 01:02:23 +00:00
dependabot[bot]
c02ea0d62e
Bump bitflags from 2.4.0 to 2.4.1
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.4.0...2.4.1)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 01:02:18 +00:00
_Frky
13c7d2d3c1
Merge pull request #186 from raphaelts3/fix/macos-compilation
Remove dependency to library netdevice
2023-10-12 06:56:12 +02:00
Raphael Tomé Santana
a0b8572089
fix: remove netdevice crate usage 2023-10-09 10:50:58 -03:00
Raphael Tomé Santana
5383927684
chore: remove netdevice dependency 2023-10-09 10:50:41 -03:00
Pierre
e9bc2f611b
Merge pull request #185 from ivre/dependabot/cargo/byteorder-1.5.0
Bump byteorder from 1.4.3 to 1.5.0
2023-10-09 15:08:20 +02:00
dependabot[bot]
66d4622238
Bump byteorder from 1.4.3 to 1.5.0
Bumps [byteorder](https://github.com/BurntSushi/byteorder) from 1.4.3 to 1.5.0.
- [Changelog](https://github.com/BurntSushi/byteorder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/byteorder/compare/1.4.3...1.5.0)

---
updated-dependencies:
- dependency-name: byteorder
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-09 00:11:58 +00:00
Pierre
446fbf9bd7
Merge pull request #184 from ivre/dependabot/cargo/clap-4.4.6
Bump clap from 4.4.5 to 4.4.6
2023-09-29 18:41:11 +02:00
dependabot[bot]
ffcbc54702
Bump clap from 4.4.5 to 4.4.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.5...v4.4.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-29 00:30:46 +00:00
Pierre
d6dda62b4e
Merge pull request #183 from ivre/dependabot/cargo/clap-4.4.5
Bump clap from 4.4.4 to 4.4.5
2023-09-26 09:13:13 +02:00
dependabot[bot]
0ada44b229
Bump clap from 4.4.4 to 4.4.5
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.4...v4.4.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-26 00:18:52 +00:00
_Frky
28095b5a4e
Merge pull request #172 from p-l-/fix-warning
Remove unneeded `mut`
2023-09-25 06:33:45 +02:00
Pierre
15e981b45d
Merge pull request #182 from ivre/dependabot/cargo/clap-4.4.4
Bump clap from 4.4.3 to 4.4.4
2023-09-19 11:23:03 +02:00
dependabot[bot]
f804962dd3
Bump clap from 4.4.3 to 4.4.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.3 to 4.4.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.3...v4.4.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-19 00:19:55 +00:00
Pierre
7cfa11015b
Merge pull request #181 from ivre/dependabot/cargo/chrono-0.4.31
Bump chrono from 0.4.30 to 0.4.31
2023-09-18 12:14:42 +02:00
dependabot[bot]
1f676ae06d
Bump chrono from 0.4.30 to 0.4.31
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.30 to 0.4.31.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.30...v0.4.31)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 01:08:26 +00:00
Pierre
a3e7fa92e7
Merge pull request #180 from ivre/dependabot/cargo/clap-4.4.3
Bump clap from 4.4.2 to 4.4.3
2023-09-15 07:29:19 +02:00
dependabot[bot]
50c9984f54
Bump clap from 4.4.2 to 4.4.3
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.2 to 4.4.3.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.2...v4.4.3)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-14 00:10:32 +00:00
Pierre
0b20421e7c
Merge pull request #179 from ivre/dependabot/cargo/chrono-0.4.30
Bump chrono from 0.4.29 to 0.4.30
2023-09-08 08:52:35 +02:00
dependabot[bot]
a28d450eb1
Bump chrono from 0.4.29 to 0.4.30
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.29 to 0.4.30.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.29...v0.4.30)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-08 00:50:00 +00:00
Pierre
b2ce253dbc
Merge pull request #177 from ivre/dependabot/cargo/chrono-0.4.29
Bump chrono from 0.4.28 to 0.4.29
2023-09-06 15:45:00 +02:00
dependabot[bot]
4513e93a92
Bump chrono from 0.4.28 to 0.4.29
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.28 to 0.4.29.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.28...v0.4.29)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-06 00:48:51 +00:00
Pierre
c0bb698834
Merge pull request #176 from ivre/dependabot/cargo/clap-4.4.2
Bump clap from 4.4.1 to 4.4.2
2023-09-01 13:02:29 +02:00
dependabot[bot]
74474df673
Bump clap from 4.4.1 to 4.4.2
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.1 to 4.4.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.1...v4.4.2)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-01 00:54:57 +00:00
Pierre
12d95aa35c
Merge pull request #175 from ivre/dependabot/cargo/chrono-0.4.28
Bump chrono from 0.4.27 to 0.4.28
2023-08-31 02:45:20 +02:00
dependabot[bot]
bc99e527ab
Bump chrono from 0.4.27 to 0.4.28
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.27 to 0.4.28.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.27...v0.4.28)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-31 00:07:37 +00:00
Pierre
807a185db9
Merge pull request #174 from ivre/dependabot/cargo/chrono-0.4.27
Bump chrono from 0.4.26 to 0.4.27
2023-08-30 14:25:10 +02:00
dependabot[bot]
caab648c48
Bump chrono from 0.4.26 to 0.4.27
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.26 to 0.4.27.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.26...v0.4.27)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-30 00:43:44 +00:00
Pierre
055a0ca6d3
Merge pull request #173 from ivre/dependabot/cargo/clap-4.4.1
Bump clap from 4.4.0 to 4.4.1
2023-08-29 08:19:15 +02:00
dependabot[bot]
bec538b52f
Bump clap from 4.4.0 to 4.4.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.0 to 4.4.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.4.0...v4.4.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-29 00:55:28 +00:00
Pierre Lalet
b498d70602 Remove unneeded mut 2023-08-26 22:32:10 +02:00
Pierre
0b200ceaf4
Merge pull request #171 from ivre/dependabot/cargo/clap-4.4.0
Bump clap from 4.3.24 to 4.4.0
2023-08-25 14:06:36 +02:00
dependabot[bot]
e654f416d3
Bump clap from 4.3.24 to 4.4.0
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.24 to 4.4.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.24...clap_complete-v4.4.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-25 00:28:18 +00:00
Pierre
cd3b31e9b4
Merge pull request #170 from ivre/dependabot/cargo/siphasher-1.0.0
Bump siphasher from 0.3.10 to 1.0.0
2023-08-24 16:15:47 +02:00
Pierre
a85a7b6027
Merge pull request #169 from ivre/dependabot/cargo/clap-4.3.24
Bump clap from 4.3.23 to 4.3.24
2023-08-24 16:15:37 +02:00
dependabot[bot]
130a7601cb
Bump siphasher from 0.3.10 to 1.0.0
Bumps [siphasher](https://github.com/jedisct1/rust-siphash) from 0.3.10 to 1.0.0.
- [Commits](https://github.com/jedisct1/rust-siphash/compare/0.3.10...1.0.0)

---
updated-dependencies:
- dependency-name: siphasher
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-24 00:11:32 +00:00
dependabot[bot]
14a271b9f4
Bump clap from 4.3.23 to 4.3.24
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.23 to 4.3.24.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.23...v4.3.24)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-24 00:11:25 +00:00
Pierre
8223c98304
Merge pull request #168 from ivre/dependabot/cargo/clap-4.3.23
Bump clap from 4.3.22 to 4.3.23
2023-08-21 13:45:28 +02:00
dependabot[bot]
7fcb05cb2a
Bump clap from 4.3.22 to 4.3.23
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.22 to 4.3.23.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.22...v4.3.23)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 01:02:14 +00:00
Pierre
dbde2bf663
Merge pull request #167 from ivre/dependabot/cargo/clap-4.3.22
Bump clap from 4.3.21 to 4.3.22
2023-08-18 15:07:08 +02:00
dependabot[bot]
3ebe8c604b
Bump clap from 4.3.21 to 4.3.22
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.21 to 4.3.22.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.21...v4.3.22)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-18 00:04:57 +00:00
Pierre
641479cbe6
Merge pull request #166 from ivre/dependabot/cargo/flate2-1.0.27
Bump flate2 from 1.0.26 to 1.0.27
2023-08-17 09:09:37 +02:00
dependabot[bot]
34c5dcc31b
Bump flate2 from 1.0.26 to 1.0.27
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.26 to 1.0.27.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.26...1.0.27)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-16 01:01:52 +00:00
Pierre
ac50f58e02
Merge pull request #164 from ivre/dependabot/cargo/log-0.4.20
Bump log from 0.4.19 to 0.4.20
2023-08-14 10:43:03 +02:00
Pierre
eae17b33b5
Merge pull request #165 from ivre/dependabot/cargo/bitflags-2.4.0
Bump bitflags from 2.3.3 to 2.4.0
2023-08-14 10:42:53 +02:00
dependabot[bot]
4662692ab8
Bump bitflags from 2.3.3 to 2.4.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.3.3 to 2.4.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.3.3...2.4.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-14 01:11:06 +00:00
dependabot[bot]
039dc81139
Bump log from 0.4.19 to 0.4.20
Bumps [log](https://github.com/rust-lang/log) from 0.4.19 to 0.4.20.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.19...0.4.20)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-14 01:10:40 +00:00
Pierre
ff0b447538
Merge pull request #163 from ivre/dependabot/cargo/clap-4.3.21
Bump clap from 4.3.19 to 4.3.21
2023-08-09 22:27:27 +02:00
_Frky
bc8fbaaf99
Add Docker documentation in README 2023-08-09 11:13:30 +02:00
_Frky
9f67e3ca18
Merge pull request #154 from p-l-/docker
Add a Docker image
2023-08-09 11:12:07 +02:00
dependabot[bot]
3b328be1b8
Bump clap from 4.3.19 to 4.3.21
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.19 to 4.3.21.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.19...v4.3.21)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-09 00:15:47 +00:00
Pierre
039700dd46
Merge pull request #161 from ivre/dependabot/cargo/strum_macros-0.25.2
Bump strum_macros from 0.25.1 to 0.25.2
2023-08-07 11:48:15 +02:00
dependabot[bot]
12a4ba04d8
Bump strum_macros from 0.25.1 to 0.25.2
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.25.1 to 0.25.2.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 00:49:39 +00:00
Pierre
ae6b7829e0
Merge pull request #160 from ivre/dependabot/cargo/clap-4.3.19
Bump clap from 4.3.17 to 4.3.19
2023-07-24 13:14:07 +02:00
dependabot[bot]
4b6a53caf3
Bump clap from 4.3.17 to 4.3.19
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.17 to 4.3.19.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.17...v4.3.19)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-24 00:47:45 +00:00
Pierre
293c50dcc3
Merge pull request #159 from ivre/dependabot/cargo/clap-4.3.17
Bump clap from 4.3.16 to 4.3.17
2023-07-23 00:15:51 +02:00
dependabot[bot]
8c396bce57
Bump clap from 4.3.16 to 4.3.17
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.16 to 4.3.17.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.16...v4.3.17)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-21 00:37:00 +00:00
Pierre
7ee5eca0ba
Merge pull request #158 from ivre/dependabot/cargo/clap-4.3.16
Bump clap from 4.3.12 to 4.3.16
2023-07-19 12:46:17 +02:00
dependabot[bot]
4ede8c06cc
Bump clap from 4.3.12 to 4.3.16
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.12 to 4.3.16.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.12...v4.3.16)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-19 00:18:56 +00:00
Pierre
8989d0c357
Merge pull request #156 from ivre/dependabot/cargo/clap-4.3.12
Bump clap from 4.3.11 to 4.3.12
2023-07-17 22:00:05 +02:00
dependabot[bot]
3e075de237
Bump clap from 4.3.11 to 4.3.12
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.11 to 4.3.12.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.11...v4.3.12)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 00:54:46 +00:00
Pierre
f6f3fca3d5
Merge pull request #155 from ivre/dependabot/cargo/clap-4.3.11
Bump clap from 4.3.10 to 4.3.11
2023-07-06 09:23:25 +02:00
dependabot[bot]
496d40c03c
Bump clap from 4.3.10 to 4.3.11
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.10 to 4.3.11.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.10...v4.3.11)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-06 00:52:20 +00:00
Pierre Lalet
dd25bcb6f8 Add a Docker image 2023-07-06 01:08:34 +02:00
_Frky
c132c39ebf
Fix syn (#102)
* Add unit test for TCP SYN replies
* Add functional test for TCP SYN+flag packets
* Fix bug: prevent anwsers to SYN+flag first packets
* Fix TCP behaviour to match Linux network stack
* Update the documentation according to the new behaviour for TCP SYN packets
2023-07-05 22:20:06 +02:00
Pierre
092a16631c
Merge pull request #153 from ivre/dependabot/cargo/strum_macros-0.25.1
Bump strum_macros from 0.25.0 to 0.25.1
2023-07-04 23:23:36 +02:00
dependabot[bot]
4134008d11
Bump strum_macros from 0.25.0 to 0.25.1
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.25.0 to 0.25.1.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-04 00:20:13 +00:00
Pierre
4318d5bf74
Merge pull request #150 from ivre/dependabot/cargo/bitflags-2.3.3
Bump bitflags from 1.3.2 to 2.3.3
2023-07-03 18:31:06 +02:00
Pierre Lalet
cc17dec2d5 Update SmackFlags to support Copy
Needed to update bitflags from 1.x to 2.x
2023-07-03 18:26:17 +02:00
dependabot[bot]
66f2d6138d
Bump bitflags from 1.3.2 to 2.3.3
Bumps [bitflags](https://github.com/bitflags/bitflags) from 1.3.2 to 2.3.3.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/1.3.2...2.3.3)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 14:53:55 +00:00
Pierre
67be84abb4
Merge pull request #152 from ivre/dependabot/cargo/clap-4.3.10
Bump clap from 4.3.9 to 4.3.10
2023-07-03 16:53:09 +02:00
dependabot[bot]
45810978a9
Bump clap from 4.3.9 to 4.3.10
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.9 to 4.3.10.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.9...v4.3.10)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 00:36:52 +00:00
Pierre
748608a71f
Merge pull request #151 from ivre/dependabot/cargo/clap-4.3.9
Bump clap from 4.3.8 to 4.3.9
2023-06-30 00:43:27 +02:00
dependabot[bot]
d48e20971e
Bump clap from 4.3.8 to 4.3.9
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.8 to 4.3.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.8...v4.3.9)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-29 00:12:52 +00:00
Pierre
ae76ed9df2
Merge pull request #149 from ivre/dependabot/cargo/clap-4.3.8
Bump clap from 4.3.5 to 4.3.8
2023-06-26 09:46:09 +02:00
dependabot[bot]
0dd011be04
Bump clap from 4.3.5 to 4.3.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.5 to 4.3.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.5...v4.3.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-26 01:30:12 +00:00
Pierre
ea61dd668a
Merge pull request #148 from ivre/dependabot/cargo/itertools-0.11.0
Bump itertools from 0.10.5 to 0.11.0
2023-06-24 23:10:23 +02:00
dependabot[bot]
c69fd9012e
Bump itertools from 0.10.5 to 0.11.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.5 to 0.11.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.10.5...v0.11.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-23 00:59:23 +00:00
Pierre
974ed60687
Merge pull request #147 from ivre/dependabot/cargo/clap-4.3.5
Bump clap from 4.3.4 to 4.3.5
2023-06-21 19:40:52 +02:00
dependabot[bot]
47b681742c
Bump clap from 4.3.4 to 4.3.5
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.4 to 4.3.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.4...v4.3.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-21 01:00:00 +00:00
Pierre
0a2e007e0c
Merge pull request #146 from ivre/dependabot/cargo/strum_macros-0.25.0
Bump strum_macros from 0.24.3 to 0.25.0
2023-06-19 09:26:23 +02:00
dependabot[bot]
4297389cd3
Bump strum_macros from 0.24.3 to 0.25.0
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.24.3 to 0.25.0.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 07:19:59 +00:00
Pierre
935283b543
Merge pull request #145 from ivre/dependabot/cargo/strum-0.25.0
Bump strum from 0.24.1 to 0.25.0
2023-06-19 09:19:16 +02:00
dependabot[bot]
938661663b
Bump strum from 0.24.1 to 0.25.0
Bumps [strum](https://github.com/Peternator7/strum) from 0.24.1 to 0.25.0.
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 01:18:29 +00:00
Pierre
11591420ab
Merge pull request #144 from ivre/dependabot/cargo/clap-4.3.4
Bump clap from 4.3.3 to 4.3.4
2023-06-15 03:56:10 +02:00
dependabot[bot]
f9d272c2dd
Bump clap from 4.3.3 to 4.3.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.3 to 4.3.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.3...v4.3.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-15 00:59:44 +00:00
Pierre
c5fbd7e60d
Merge pull request #142 from ivre/dependabot/cargo/clap-4.3.3
Bump clap from 4.3.2 to 4.3.3
2023-06-12 17:51:27 +02:00
Pierre
7e95fc8cb1
Merge pull request #141 from ivre/dependabot/cargo/log-0.4.19
Bump log from 0.4.18 to 0.4.19
2023-06-12 17:50:49 +02:00
dependabot[bot]
0fd38bb0e6
Bump clap from 4.3.2 to 4.3.3
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.2...v4.3.3)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 01:21:12 +00:00
dependabot[bot]
09c0a9176f
Bump log from 0.4.18 to 0.4.19
Bumps [log](https://github.com/rust-lang/log) from 0.4.18 to 0.4.19.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.18...0.4.19)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 01:21:02 +00:00
Pierre
f27b8572aa
Merge pull request #140 from ivre/dependabot/cargo/clap-4.3.2
Bump clap from 4.3.1 to 4.3.2
2023-06-07 00:37:38 +02:00
dependabot[bot]
0e1d391d74
Bump clap from 4.3.1 to 4.3.2
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.1 to 4.3.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.3.1...v4.3.2)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-06 00:59:11 +00:00
Pierre
18e3e1700d
Merge pull request #139 from ivre/dependabot/cargo/clap-4.3.1
Bump clap from 4.3.0 to 4.3.1
2023-06-05 08:55:47 +02:00
dependabot[bot]
968bd4ac7f
Bump clap from 4.3.0 to 4.3.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.3.0...clap_complete-v4.3.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-05 01:23:19 +00:00
Pierre
a1ab84b81a
Merge pull request #137 from ivre/dependabot/cargo/chrono-0.4.26
Bump chrono from 0.4.25 to 0.4.26
2023-05-31 08:38:43 +02:00
dependabot[bot]
fd3ee36dd0
Bump chrono from 0.4.25 to 0.4.26
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.25 to 0.4.26.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.25...v0.4.26)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-31 01:00:12 +00:00
Pierre
a86d4b7c68
Merge pull request #136 from ivre/dependabot/cargo/chrono-0.4.25
Bump chrono from 0.4.24 to 0.4.25
2023-05-30 11:16:29 +02:00
dependabot[bot]
0a07dc2b3d
Bump chrono from 0.4.24 to 0.4.25
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.24 to 0.4.25.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.24...v0.4.25)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-30 00:59:09 +00:00
Pierre
cfe6fae649
Merge pull request #135 from ivre/dependabot/cargo/log-0.4.18
Bump log from 0.4.17 to 0.4.18
2023-05-29 12:49:05 +02:00
dependabot[bot]
a32335b6df
Bump log from 0.4.17 to 0.4.18
Bumps [log](https://github.com/rust-lang/log) from 0.4.17 to 0.4.18.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.17...0.4.18)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 01:27:28 +00:00
Pierre
f151126c72
Merge pull request #134 from ivre/dependabot/cargo/clap-4.3.0
Bump clap from 4.2.7 to 4.3.0
2023-05-22 10:36:09 +02:00
dependabot[bot]
3afb763a42
Bump clap from 4.2.7 to 4.3.0
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.7 to 4.3.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.7...clap_complete-v4.3.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-22 01:28:16 +00:00
Pierre
3835780505
Merge pull request #132 from ivre/dependabot/cargo/pcap-1.1.0
Bump pcap from 1.0.0 to 1.1.0
2023-05-19 13:13:34 +02:00
dependabot[bot]
c86752057d
Bump pcap from 1.0.0 to 1.1.0
Bumps [pcap](https://github.com/rust-pcap/pcap) from 1.0.0 to 1.1.0.
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/compare/v1.0.0...v1.1.0)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-15 01:25:58 +00:00
Pierre
40a4047961
Merge pull request #131 from ivre/dependabot/cargo/clap-4.2.7
Bump clap from 4.2.5 to 4.2.7
2023-05-03 10:12:05 +02:00
dependabot[bot]
d9c9edd136
Bump clap from 4.2.5 to 4.2.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.5 to 4.2.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.5...v4.2.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-03 00:59:13 +00:00
Pierre
8e7b5a7fbc
Merge pull request #130 from ivre/dependabot/cargo/flate2-1.0.26
Bump flate2 from 1.0.25 to 1.0.26
2023-05-01 22:02:12 +02:00
dependabot[bot]
60ec9e9b00
Bump flate2 from 1.0.25 to 1.0.26
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.25 to 1.0.26.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/commits)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-01 01:13:34 +00:00
Pierre
1b1c826bee
Merge pull request #129 from ivre/dependabot/cargo/clap-4.2.5
Bump clap from 4.2.4 to 4.2.5
2023-04-30 02:37:12 +02:00
dependabot[bot]
774fbb694a
Bump clap from 4.2.4 to 4.2.5
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.4 to 4.2.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.4...v4.2.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-28 00:59:34 +00:00
Pierre
2313d3013e
Merge pull request #127 from ivre/dependabot/cargo/clap-4.2.4
Bump clap from 4.2.2 to 4.2.4
2023-04-21 14:53:39 +02:00
dependabot[bot]
e8fe8bd8e9
Bump clap from 4.2.2 to 4.2.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.2 to 4.2.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.2...v4.2.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-20 00:59:27 +00:00
Pierre
d57cdab782
Merge pull request #125 from ivre/dependabot/cargo/clap-4.2.2
Bump clap from 4.2.1 to 4.2.2
2023-04-14 11:57:05 +02:00
dependabot[bot]
a2fa57c64c
Bump clap from 4.2.1 to 4.2.2
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-14 00:59:36 +00:00
Pierre
511c816645
Merge pull request #123 from ivre/dependabot/cargo/clap-4.2.1
Bump clap from 4.1.13 to 4.2.1
2023-03-30 23:13:27 +02:00
dependabot[bot]
f63b43f0b5
Bump clap from 4.1.13 to 4.2.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.13 to 4.2.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.13...v4.2.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-30 01:02:36 +00:00
Pierre
e3a6171b57
Merge pull request #120 from ivre/dependabot/cargo/clap-4.1.13
Bump clap from 4.1.11 to 4.1.13
2023-03-24 22:04:51 +01:00
dependabot[bot]
ed44da5267
Bump clap from 4.1.11 to 4.1.13
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.11 to 4.1.13.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.11...v4.1.13)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-24 01:01:36 +00:00
Pierre
9047faf871
Merge pull request #119 from ivre/dependabot/cargo/clap-4.1.11
Bump clap from 4.1.9 to 4.1.11
2023-03-20 12:54:49 +01:00
dependabot[bot]
70d5721472
Bump clap from 4.1.9 to 4.1.11
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.9 to 4.1.11.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.9...v4.1.11)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 01:31:56 +00:00
Pierre
a6004f6528
Merge pull request #117 from ivre/dependabot/cargo/clap-4.1.9
Bump clap from 4.1.8 to 4.1.9
2023-03-17 09:27:00 +01:00
dependabot[bot]
bbe78bcae9
Bump clap from 4.1.8 to 4.1.9
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.8 to 4.1.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.8...v4.1.9)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-17 01:03:14 +00:00
Pierre
e68491c709
Merge pull request #114 from ivre/dependabot/cargo/chrono-0.4.24
Bump chrono from 0.4.23 to 0.4.24
2023-03-13 10:43:32 +01:00
dependabot[bot]
4af9804110
Bump chrono from 0.4.23 to 0.4.24
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.23 to 0.4.24.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.23...v0.4.24)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-13 01:33:24 +00:00
Pierre
a4b1908909
Merge pull request #113 from ivre/dependabot/cargo/clap-4.1.8
Bump clap from 4.1.7 to 4.1.8
2023-03-03 02:51:27 +01:00
dependabot[bot]
43b2cfe499
Bump clap from 4.1.7 to 4.1.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.7 to 4.1.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.7...v4.1.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-01 01:10:52 +00:00
Pierre
b6808d37df
Merge pull request #112 from ivre/dependabot/cargo/clap-4.1.7
Bump clap from 4.1.6 to 4.1.7
2023-02-28 14:18:40 +01:00
dependabot[bot]
1cdc68c298
Bump clap from 4.1.6 to 4.1.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.6 to 4.1.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.6...v4.1.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 01:05:11 +00:00
Pierre
0019e821fd
Merge pull request #111 from ivre/dependabot/cargo/clap-4.1.6
Bump clap from 4.1.4 to 4.1.6
2023-02-16 02:11:16 +01:00
dependabot[bot]
ba358181d4
Bump clap from 4.1.4 to 4.1.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.4 to 4.1.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.4...v4.1.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-16 01:02:46 +00:00
Pierre
8f4467a421
Merge pull request #106 from p-l-/update-dependencies
Update dependencies
2023-02-14 00:36:46 +01:00
Pierre Lalet
643d5868aa Update dependencies 2023-02-14 00:31:11 +01:00
Pierre
8620c5ff20
Merge pull request #107 from p-l-/fix-warning
Fix build warning
2023-02-14 00:29:07 +01:00
Pierre Lalet
7c6d8258ef Fix build warning 2023-02-14 00:24:04 +01:00
Pierre
fe92f12af0
Merge pull request #110 from p-l-/pcap-file-2.0.0
Bump pcap-file from 1.1.1 to 2.0.0
2023-02-14 00:22:50 +01:00
Pierre Lalet
3159ecf743 pcap-file: switch to 2.0 API 2023-02-14 00:16:46 +01:00
dependabot[bot]
498c811323
Bump pcap-file from 1.1.1 to 2.0.0
Bumps [pcap-file](https://github.com/courvoif/pcap-file) from 1.1.1 to 2.0.0.
- [Release notes](https://github.com/courvoif/pcap-file/releases)
- [Commits](https://github.com/courvoif/pcap-file/commits)

---
updated-dependencies:
- dependency-name: pcap-file
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-07 08:10:09 +00:00
Pierre
8d82f7e04a
Merge pull request #109 from ivre/dependabot/cargo/pnet-0.33.0
Bump pnet from 0.31.0 to 0.33.0
2023-02-07 09:06:31 +01:00
dependabot[bot]
27aac1c444
Bump pnet from 0.31.0 to 0.33.0
Bumps [pnet](https://github.com/libpnet/libpnet) from 0.31.0 to 0.33.0.
- [Release notes](https://github.com/libpnet/libpnet/releases)
- [Commits](https://github.com/libpnet/libpnet/compare/v0.31.0...v0.33.0)

---
updated-dependencies:
- dependency-name: pnet
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 01:11:36 +00:00
Pierre
97c4618f32
Merge pull request #105 from ivre/dependabot/cargo/clap-4.1.4
Bump clap from 4.1.3 to 4.1.4
2023-01-25 14:11:22 +01:00
dependabot[bot]
e3ea48444d
Bump clap from 4.1.3 to 4.1.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.3...v4.1.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-25 00:16:20 +00:00
Pierre
8ae727d554
Merge pull request #104 from ivre/dependabot/cargo/clap-4.1.3
Bump clap from 4.1.1 to 4.1.3
2023-01-24 10:46:35 +01:00
dependabot[bot]
e63da23be0
Bump clap from 4.1.1 to 4.1.3
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.1 to 4.1.3.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.1.1...v4.1.3)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-24 00:16:19 +00:00
Pierre
beed2c4f4c
Merge pull request #103 from ivre/dependabot/cargo/clap-4.1.1
Bump clap from 4.0.32 to 4.1.1
2023-01-16 11:31:14 +01:00
dependabot[bot]
42e3e908ab
Bump clap from 4.0.32 to 4.1.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.32 to 4.1.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.32...v4.1.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 01:26:33 +00:00
_Frky
047e15a579
Merge pull request #101 from p-l-/fix-tests
Fix DNS tests after Scapy 2.5.0 release
2022-12-30 16:02:19 +01:00
Pierre Lalet
cc644e81aa Fix DNS tests after Scapy 2.5.0 release 2022-12-28 22:27:45 +01:00
Pierre
4607370c7c
Merge pull request #100 from ivre/dependabot/cargo/clap-4.0.32
Bump clap from 4.0.30 to 4.0.32
2022-12-27 08:56:42 +01:00
dependabot[bot]
e0c2a87e0b
Bump clap from 4.0.30 to 4.0.32
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.30 to 4.0.32.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.30...v4.0.32)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-23 00:13:40 +00:00
Pierre
3eb975decc
Merge pull request #98 from ivre/dependabot/cargo/clap-4.0.30
Bump clap from 4.0.29 to 4.0.30
2022-12-22 17:33:18 +01:00
dependabot[bot]
9f4a14abbf
Bump clap from 4.0.29 to 4.0.30
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.29 to 4.0.30.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.29...v4.0.30)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-22 16:27:47 +00:00
Pierre
9f786e9a6e
Merge pull request #99 from p-l-/fix-tests
Fix randomly failing tests
2022-12-22 17:26:25 +01:00
Pierre Lalet
089c971ae9 Fix randomly failing tests 2022-12-22 17:17:32 +01:00
_Frky
61a8cebf8f
Merge pull request #93 from Unactived/master
Option to avoid responding to chosen IP addresses
2022-12-09 12:10:05 +01:00
_Frky
58fba4bf31 Fix command line options in tests 2022-12-08 21:40:38 +01:00
_Frky
deb7df490b Update README with new options 2022-12-08 21:28:50 +01:00
_Frky
bad2c5e02c Rename options for IP (self and remote) for more clarity 2022-12-08 21:28:03 +01:00
Pierre
044b1a1b14
Merge pull request #97 from ivre/dependabot/cargo/clap-4.0.29
Bump clap from 4.0.27 to 4.0.29
2022-11-30 12:05:03 +01:00
dependabot[bot]
1d208c769b
Bump clap from 4.0.27 to 4.0.29
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.27 to 4.0.29.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.27...v4.0.29)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-30 00:22:22 +00:00
Pierre
9fde25ca27
Merge pull request #94 from ivre/enh-ssh-banner
SSH: reply with the client's banner
2022-11-25 14:44:03 +01:00
_Frky
ad7d9bd4f0
Merge pull request #95 from ivre/dependabot/cargo/flate2-1.0.25
Bump flate2 from 1.0.24 to 1.0.25
2022-11-25 07:14:36 +01:00
dependabot[bot]
adbbf2259a
Bump flate2 from 1.0.24 to 1.0.25
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.24 to 1.0.25.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/commits/1.0.25)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-25 06:00:49 +00:00
_Frky
d6768cac11
Merge pull request #96 from ivre/dependabot/cargo/clap-4.0.27
Bump clap from 4.0.26 to 4.0.27
2022-11-25 06:59:24 +01:00
dependabot[bot]
67a2113fd9
Bump clap from 4.0.26 to 4.0.27
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.26 to 4.0.27.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.26...v4.0.27)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-25 00:24:22 +00:00
_Frky
1181d6eb93 SSH: reply with the client's banner 2022-11-24 19:24:09 +01:00
_Frky
e541d1f5ee Add unit test for IPv6 blacklist 2022-11-24 17:12:37 +01:00
_Frky
b136728f8f Add unit test for IPv4 blacklist 2022-11-24 17:12:32 +01:00
Unactive
ca3b9a75fc
Fix typo in options 2022-11-24 15:56:33 +01:00
Unactive
774af0b781 Document ignore and format options 2022-11-24 15:49:56 +01:00
Unactive
0f8ef335a0 Added option to forbid responding to chosen IP addresses 2022-11-24 15:00:35 +01:00
Pierre
29f89f21e4
Merge pull request #92 from ivre/dependabot/cargo/pcap-1.0.0
Bump pcap from 0.11.0 to 1.0.0
2022-11-21 09:21:49 +01:00
dependabot[bot]
86e8b6a607
Bump pcap from 0.11.0 to 1.0.0
Bumps [pcap](https://github.com/rust-pcap/pcap) from 0.11.0 to 1.0.0.
- [Release notes](https://github.com/rust-pcap/pcap/releases)
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/compare/v0.11.0...v1.0.0)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-21 01:14:22 +00:00
Pierre
668f8a143c
Merge pull request #91 from ivre/dependabot/cargo/clap-4.0.26
Bump clap from 4.0.25 to 4.0.26
2022-11-17 17:29:55 +01:00
dependabot[bot]
fd4f04671f
Bump clap from 4.0.25 to 4.0.26
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.25 to 4.0.26.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.25...v4.0.26)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-17 00:27:26 +00:00
Pierre
e38e2e6e6f
Merge pull request #90 from ivre/dependabot/cargo/clap-4.0.25
Bump clap from 4.0.24 to 4.0.25
2022-11-16 02:34:28 +01:00
dependabot[bot]
1ccd464ba6
Bump clap from 4.0.24 to 4.0.25
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.24 to 4.0.25.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.24...v4.0.25)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-16 00:22:21 +00:00
Pierre
8bb6f6a41f
Merge pull request #88 from ivre/dependabot/cargo/chrono-0.4.23
Bump chrono from 0.4.22 to 0.4.23
2022-11-15 14:08:33 +01:00
dependabot[bot]
046ecfd443
Bump chrono from 0.4.22 to 0.4.23
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.22 to 0.4.23.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.22...v0.4.23)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 12:07:45 +00:00
Pierre
d5264c8077
Merge pull request #89 from ivre/dependabot/cargo/clap-4.0.24
Bump clap from 4.0.22 to 4.0.24
2022-11-15 13:05:36 +01:00
dependabot[bot]
eb1228a7aa
Bump clap from 4.0.22 to 4.0.24
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.22 to 4.0.24.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.22...v4.0.24)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-15 00:09:55 +00:00
Pierre
38a0384f82
Merge pull request #86 from ivre/dependabot/cargo/clap-4.0.22
Bump clap from 4.0.18 to 4.0.22
2022-11-08 11:21:51 +01:00
Pierre
890451b0b4
Merge pull request #85 from ivre/dependabot/cargo/stderrlog-0.5.4
Bump stderrlog from 0.5.3 to 0.5.4
2022-11-08 11:21:42 +01:00
dependabot[bot]
375f0e3d04
Bump clap from 4.0.18 to 4.0.22
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.18 to 4.0.22.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.18...v4.0.22)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 00:27:59 +00:00
dependabot[bot]
b564e9762f
Bump stderrlog from 0.5.3 to 0.5.4
Bumps [stderrlog](https://github.com/cardoe/stderrlog-rs) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/cardoe/stderrlog-rs/releases)
- [Changelog](https://github.com/cardoe/stderrlog-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cardoe/stderrlog-rs/compare/v0.5.3...v0.5.4)

---
updated-dependencies:
- dependency-name: stderrlog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 00:27:46 +00:00
Pierre
31544ff464
Merge pull request #84 from ivre/dependabot/cargo/clap-4.0.18
Bump clap from 4.0.17 to 4.0.18
2022-10-21 11:56:57 +02:00
dependabot[bot]
090770a5ee
Bump clap from 4.0.17 to 4.0.18
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.17 to 4.0.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.17...v4.0.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-21 01:00:28 +00:00
Pierre
523e2117b4
Merge pull request #83 from ivre/fix-clap
Fix other bug in clap
2022-10-21 00:18:00 +02:00
_Frky
ae175e7b77 Fix other bug in clap 2022-10-20 20:03:15 +02:00
_Frky
d5d2e7c966
Merge pull request #82 from ivre/fix-clap
Fix clap default options since last update
2022-10-20 19:46:22 +02:00
_Frky
04c9621c7e Fix clap default options since last update 2022-10-20 17:49:36 +02:00
Pierre
6573194b6e
Merge pull request #81 from ivre/dependabot/cargo/clap-4.0.17
Bump clap from 4.0.15 to 4.0.17
2022-10-19 08:58:47 +02:00
dependabot[bot]
4932805271
Bump clap from 4.0.15 to 4.0.17
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.15 to 4.0.17.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.15...v4.0.17)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-19 00:33:07 +00:00
Pierre
c1e2da5714
Merge pull request #80 from ivre/dependabot/cargo/clap-4.0.15
Bump clap from 4.0.14 to 4.0.15
2022-10-14 15:08:56 +02:00
dependabot[bot]
e86fadf32e
Bump clap from 4.0.14 to 4.0.15
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.14 to 4.0.15.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.14...v4.0.15)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-14 00:34:29 +00:00
Pierre
cd1cbcfa53
Merge pull request #79 from ivre/dependabot/cargo/clap-4.0.14
Bump clap from 4.0.13 to 4.0.14
2022-10-13 08:20:24 +02:00
dependabot[bot]
87c789953b
Bump clap from 4.0.13 to 4.0.14
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.13 to 4.0.14.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.13...v4.0.14)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-13 00:22:33 +00:00
Pierre
ba66e7ccaa
Merge pull request #78 from ivre/dependabot/cargo/clap-4.0.13
Bump clap from 4.0.12 to 4.0.13
2022-10-12 14:20:57 +02:00
dependabot[bot]
29400cba61
Bump clap from 4.0.12 to 4.0.13
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.12 to 4.0.13.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.12...v4.0.13)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-12 00:48:41 +00:00
Pierre
7cf72610b8
Merge pull request #77 from ivre/dependabot/cargo/clap-4.0.12
Bump clap from 4.0.11 to 4.0.12
2022-10-11 10:57:36 +02:00
dependabot[bot]
0fc137ac3b
Bump clap from 4.0.11 to 4.0.12
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.11 to 4.0.12.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.11...v4.0.12)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-11 00:47:27 +00:00
Pierre
b4dc2e210e
Merge pull request #76 from ivre/dependabot/cargo/clap-4.0.11
Bump clap from 4.0.10 to 4.0.11
2022-10-10 11:44:08 +02:00
dependabot[bot]
409efd4bc5
Bump clap from 4.0.10 to 4.0.11
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.10 to 4.0.11.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.10...v4.0.11)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-10 01:10:06 +00:00
_Frky
0fbdbaa4a1
Merge pull request #74 from Unactived/master
Format option and Logfmt support
2022-10-08 12:28:49 +02:00
Unactive
e5bbd08d49 et -> eth_type and cargo fmt 2022-10-06 10:07:00 +02:00
_Frky
bf6881e2f8
Merge pull request #75 from ivre/dependabot/cargo/clap-4.0.10
Bump clap from 4.0.9 to 4.0.10
2022-10-06 06:23:56 +02:00
dependabot[bot]
366d891d6e
Bump clap from 4.0.9 to 4.0.10
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.9 to 4.0.10.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.9...v4.0.10)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-06 00:34:49 +00:00
Unactive
1f02849656 Format option and Logfmt support 2022-10-05 22:16:39 +02:00
Pierre
60492a052e
Merge pull request #72 from p-l-/update-cargo-lock
Update Cargo.lock; fixes security issue with thread_local
2022-10-05 10:25:01 +02:00
Pierre Lalet
b5525d1060 Update Cargo.lock; fixes security issue with thread_local 2022-10-05 10:18:09 +02:00
_Frky
1104123e7a
Merge pull request #71 from ivre/fix-cargo
Add Cargo.lock to repo
2022-10-05 08:40:36 +02:00
_Frky
a9da565785 Add Cargo.lock to repo 2022-10-05 08:33:32 +02:00
_Frky
b91ac120b9
Update README.md 2022-10-05 06:36:46 +02:00
Pierre
0f93e1309f
Merge pull request #69 from ivre/dependabot/cargo/pcap-0.11.0
Update pcap requirement from 0.10.0 to 0.11.0
2022-10-03 11:14:53 +02:00
dependabot[bot]
699f7572ef
Update pcap requirement from 0.10.0 to 0.11.0
Updates the requirements on [pcap](https://github.com/rust-pcap/pcap) to permit the latest version.
- [Release notes](https://github.com/rust-pcap/pcap/releases)
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 01:18:14 +00:00
Pierre
2fb1bc699a
Merge pull request #68 from ivre/dependabot/cargo/clap-4.0.4
Update clap requirement from 3.1.12 to 4.0.4
2022-09-30 13:44:26 +02:00
Pierre Lalet
ce4ac2858d Fixes for clap v4
See https://github.com/clap-rs/clap/blob/master/CHANGELOG.md
2022-09-30 13:35:35 +02:00
dependabot[bot]
fdf95c63bb
Update clap requirement from 3.1.12 to 4.0.4
Updates the requirements on [clap](https://github.com/clap-rs/clap) to permit the latest version.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v3.2.0...v4.0.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-30 00:31:34 +00:00
_Frky
15c0d9c8b0
Merge pull request #66 from ivre/fix-log
Fix bug in console logger for TCP/UDP
2022-09-21 14:40:02 +02:00
_Frky
8b24ab4f24 Fix bug in console logger for TCP/UDP 2022-09-21 14:32:23 +02:00
_Frky
3122d4e362
Merge pull request #64 from ivre/enh-quiet
Add quiet mode
2022-09-20 15:12:00 +02:00
_Frky
cc2457db67 Add quiet mode 2022-09-20 14:44:23 +02:00
Pierre
4df3d17626
Merge pull request #63 from ivre/enh-ip-from-cmd
Add option for list of IP addresses to bind in CLI
2022-09-19 10:05:01 +02:00
_Frky
26eeb73db7 Update test with new CLI options 2022-09-15 17:55:31 +02:00
_Frky
331aca3d21 Format code 2022-09-15 17:48:36 +02:00
_Frky
b65ed20c1a Update README 2022-09-15 17:47:06 +02:00
_Frky
726b5d2e87 Add option for list of IP to bind in command-line argument 2022-09-15 17:46:56 +02:00
_Frky
851a418add
Merge pull request #60 from p-l-/master
Update pnet requirement from 0.29.0 to 0.31.0
2022-08-25 09:40:36 +02:00
Pierre Lalet
a4e9db8ead Update pnet requirement from 0.29.0 to 0.31.0
Fixes #55
See <https://github.com/libpnet/libpnet/issues/572>
2022-08-25 08:49:40 +02:00
_Frky
64161228ed
Merge pull request #61 from p-l-/fix-ssh-output
Display data using safe byte2str()
2022-08-25 08:07:39 +02:00
Pierre Lalet
321b8813aa Display data using safe byte2str()
Regression from 87c95ff240 (#13)
2022-08-25 01:24:22 +02:00
_Frky
754232de9d
Merge pull request #59 from ivre/dependabot/cargo/pcap-0.10.0
Update pcap requirement from 0.9.1 to 0.10.0
2022-08-17 16:12:47 +02:00
dependabot[bot]
6a2b7e0666
Update pcap requirement from 0.9.1 to 0.10.0
Updates the requirements on [pcap](https://github.com/rust-pcap/pcap) to permit the latest version.
- [Release notes](https://github.com/rust-pcap/pcap/releases)
- [Changelog](https://github.com/rust-pcap/pcap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pcap/pcap/compare/v0.9.1...v0.9.2)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-17 00:37:20 +00:00
_Frky
cc26132081
Merge pull request #58 from Frky/master
Update readme
2022-08-05 10:16:25 +02:00
_Frky
2b2fcd8125 Update readme 2022-08-05 10:16:15 +02:00
Pierre
5d43397cf1
Merge pull request #13 from ivre/proto-ssh
Improve proto SSH
2022-08-04 23:41:14 +02:00
Pierre
b7dc26f288
Merge pull request #56 from ivre/enh-dns
Add support for DNS - Class IN Type A
2022-08-04 23:07:51 +02:00
_Frky
ed464181bc Run cargo fmt 2022-08-04 17:57:20 +02:00
_Frky
1030dc7d43 Fix flake8 2022-08-04 17:33:30 +02:00
_Frky
7cf67e01b3 Answer to SSH-1.99 banners too 2022-08-04 17:27:06 +02:00
_Frky
1b3c0cbd2f Format code 2022-08-04 17:21:46 +02:00
_Frky
87c95ff240 Implement FSM for parsing SSH banner 2022-08-04 17:21:44 +02:00
_Frky
aaf2eb5e8f Run black on python tests 2022-08-04 16:52:37 +02:00
_Frky
9638e0900c Add test for multiple queries in one request 2022-08-04 16:49:18 +02:00
_Frky
2e296d7546 Add DNS to supported protocols + functionnal tests 2022-08-04 15:14:31 +02:00
_Frky
c6be16382f Move dissector into a separate file 2022-08-04 15:13:45 +02:00
_Frky
0dd0e1d645 Add DNS implementation - Query and RR for IN/A 2022-08-04 15:13:09 +02:00
_Frky
379f48ed80 Add dependency to iterate over enums (for tests) 2022-08-04 15:12:19 +02:00
_Frky
1d4feb49ec
Merge pull request #44 from gpotter2/smb
SMB: add SessionSetup responses
2022-05-31 14:51:24 +02:00
gpotter2
ea65f23a6c Reply to SMB1 SessionSetup 2022-05-31 14:13:37 +02:00
gpotter2
6eecc91ce4 Reply to SMB2 SessionSetup 2022-05-31 14:13:37 +02:00
Pierre
d0aa42ab33
Merge pull request #53 from ivre/fix-expect
Avoid panic with empty/misformed payloads
2022-05-30 17:50:23 +02:00
_Frky
c59893c2a6 Run Flake8 2022-05-30 17:27:51 +02:00
_Frky
f696afa45d Run Black 2022-05-30 15:28:05 +02:00
_Frky
9fdecf5be7 Cargo format 2022-05-30 14:48:55 +02:00
_Frky
e34f3a6bcd Fix bug with empty or misformed payload 2022-05-30 14:47:30 +02:00
_Frky
d2d916239d Add Rust unit tests for empty payload 2022-05-30 14:47:02 +02:00
_Frky
843729b961 Add check for masscanned panic in python tests 2022-05-30 14:45:49 +02:00
_Frky
bf1a2c7429 Add pytohn tests for empty payload (TCP/UDP) 2022-05-30 14:45:23 +02:00
_Frky
2ebeefb730
Merge pull request #52 from ivre/fix-pnet-version
Update Cargo.toml
2022-05-30 12:19:34 +02:00
_Frky
c2adf50f59
Update Cargo.toml 2022-05-30 12:19:15 +02:00
_Frky
b5f1846ad7
Merge pull request #51 from p-l-/fix-50
TCP: fix int overflow in ack
2022-05-19 21:19:26 +02:00
Pierre Lalet
28a8f9b033 TCP: fix int overflow in ack 2022-05-19 15:26:35 +02:00
Pierre Lalet
d6b6de7d3f Add test for issue GH#50 2022-05-19 15:25:33 +02:00
Pierre
b8fd84bf94
Merge pull request #49 from ivre/dependabot/cargo/pnet-0.30.0
Update pnet requirement from 0.29.0 to 0.30.0
2022-05-13 09:27:30 +02:00
dependabot[bot]
088639109f
Update pnet requirement from 0.29.0 to 0.30.0
Updates the requirements on [pnet](https://github.com/libpnet/libpnet) to permit the latest version.
- [Release notes](https://github.com/libpnet/libpnet/releases)
- [Commits](https://github.com/libpnet/libpnet/compare/v0.29.0...v0.29.0)

---
updated-dependencies:
- dependency-name: pnet
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-13 00:45:14 +00:00
_Frky
e38498a3cd
Merge pull request #48 from p-l-/update-clap
Update clap, switch to Command (App deprecated)
2022-05-01 09:46:56 +02:00
_Frky
b0bb49dd00
Merge pull request #47 from p-l-/fix-output
SSH: fix output + add byte2str util function
2022-05-01 09:45:43 +02:00
Pierre Lalet
44d00831fa Update clap, switch to Command (App deprecated) 2022-04-27 19:00:21 +02:00
Pierre Lalet
48003a383e SSH: fix output 2022-04-27 18:48:13 +02:00
Pierre Lalet
dfe49d26de Add SSH wrong banner test 2022-04-27 17:56:50 +02:00
_Frky
b465a7f78e
Merge pull request #45 from gpotter2/tcb-upgrade
TCB: move to layer 4
2022-04-05 13:15:37 +02:00
gpotter2
b24d0df124 TCB: move to layer 4 2022-02-17 15:39:36 +01:00
_Frky
dbe1b608a5
Merge pull request #21 from ivre/fix-tcb
Keep TCP and proto state in TCB
2022-02-16 12:21:19 +01:00
_Frky
9d892d90c2 Run latest version of Black 2022-02-16 12:13:19 +01:00
_Frky
c6118e1448 Fix bug in HTTP segmented test + clean 2022-02-16 12:07:48 +01:00
_Frky
b74d891385 Add MetaLogger in TCB unit tests 2022-02-16 11:55:02 +01:00
_Frky
950d40fbbd Add TCB in SMB repl functions 2022-02-16 11:55:00 +01:00
_Frky
0008cae1ef Fix protocol ID and state in TCB 2022-02-16 11:53:52 +01:00
_Frky
5922dcd370 Add unit tests for protocol ID and protocol state over TCP packets 2022-02-16 11:53:52 +01:00
_Frky
997b6ae2b6 Add protocol id in TCB 2022-02-16 11:53:52 +01:00
_Frky
4152f19fe2 Cargo Format 2022-02-16 11:53:12 +01:00
_Frky
fe2fd6ca5b Add TCB argument to repl functions in protos 2022-02-16 11:53:12 +01:00
_Frky
f1368df0de Add test to highlight bug - protocol parsing state not kept 2022-02-16 11:53:10 +01:00
_Frky
e28ea53b5d
Merge pull request #43 from gpotter2/refactor-test-suite
Refactor test suite
2022-02-16 11:40:49 +01:00
gpotter2
5a2b2927d9 Split ip46->ip/tcp 2022-02-15 15:32:32 +01:00
gpotter2
beefcc9185 Test suite refactor 2022-02-15 15:20:24 +01:00
_Frky
b356e52a93
Merge pull request #41 from gpotter2/smb
Add SMB1/SMB2
2022-02-15 15:08:01 +01:00
gpotter2
cafef21436 Rename Packet->MPacket 2022-02-14 14:10:03 +01:00
gpotter2
cda878bd3c Fix formatting 2022-02-14 13:49:17 +01:00
gpotter2
99bafe232d Cleanup & Abstractify 2022-02-14 13:44:52 +01:00
gpotter2
5b97b738e9 SMB1/SMB2 Negotiate replies 2022-02-14 13:44:52 +01:00
Pierre
7590b02a2f
Merge pull request #24 from ivre/enh-log
Enhance logging in masscanned
2022-02-14 13:33:13 +01:00
_Frky
6cace5d64b Fix bug 2022-02-12 10:33:51 +01:00
_Frky
f3d8ff3d12 Fix import in unit tests 2022-02-12 10:29:00 +01:00
_Frky
77ee5e2401 Add logging function calls for L4 dissectors 2022-02-12 10:28:58 +01:00
_Frky
7b431950eb Add logging function calls for L3 dissectors 2022-02-12 10:27:39 +01:00
_Frky
7e5cb39dd3 Create crate for logging + add L3-L4 logging functions in Console Logger 2022-02-12 10:27:39 +01:00
_Frky
26f74ad6a5 Change log format (add timestamp) 2022-02-12 10:27:39 +01:00
_Frky
27f1c4ba65 Add Console Logger for Ethernet 2022-02-12 10:27:39 +01:00
_Frky
908ff3689d Fix typo in ConsoleLogger 2022-02-12 10:27:39 +01:00
_Frky
290f236157 Fix test in proto 2022-02-12 10:27:39 +01:00
_Frky
e9212ae438 Fix tests and warning 2022-02-12 10:27:39 +01:00
_Frky
7c4e2bac55 Cargo fmt 2022-02-12 10:27:39 +01:00
_Frky
e76ba12611 Add Logger Trait and ConsoleLogger as an example for ARP and Ethernet 2022-02-12 10:27:39 +01:00
Pierre
1825336826
Merge pull request #42 from gpotter2/clnup
Cleanup: use derive instead of impl
2022-02-11 22:05:08 +01:00
gpotter2
f6870c98b5 Cleanup: use derive instead of impl 2022-02-11 18:19:04 +01:00
Pierre
b1c49a977a
Merge pull request #40 from p-l-/fix-tests
Tests: fix (black) Python code
2022-02-04 14:45:34 +01:00
Pierre Lalet
479ee9a034 Tests: fix (black) Python code 2022-02-03 10:21:22 +01:00
Pierre
07c6f1f9ec
Merge pull request #39 from ivre/dependabot/cargo/pnet-0.29.0
Update pnet requirement from 0.28.0 to 0.29.0
2022-01-14 16:41:28 +01:00
dependabot[bot]
25a9d431f8
Update pnet requirement from 0.28.0 to 0.29.0
Updates the requirements on [pnet](https://github.com/libpnet/libpnet) to permit the latest version.
- [Release notes](https://github.com/libpnet/libpnet/releases)
- [Commits](https://github.com/libpnet/libpnet/compare/v0.28.0...v0.29.0)

---
updated-dependencies:
- dependency-name: pnet
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-14 00:31:09 +00:00
_Frky
a0cefe4f46
Merge pull request #35 from p-l-/clap-3.0.0
Update clap requirement from 2.33.3 to 3.0.5
2022-01-07 06:39:33 +01:00
Pierre Lalet
cc399f3828 clap: switch to new API 2022-01-06 17:36:05 +01:00
dependabot[bot]
334e9743d2
Update clap requirement from 2.33.3 to 3.0.5
Updates the requirements on [clap](https://github.com/clap-rs/clap) to permit the latest version.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_generate-v3.0.0-rc.0...v3.0.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-06 00:48:44 +00:00
_Frky
d3ab52dcad
Merge pull request #33 from p-l-/fix-pcap-0.9.1
Update pcap requirement from 0.7.0 to 0.9.1
2022-01-03 07:00:04 +01:00
Pierre Lalet
7c56fd27a8 Install libpcap-dev to build rust package pcap 0.9.1 2022-01-02 15:53:02 +01:00
dependabot[bot]
310bd1a9fa Update pcap requirement from 0.7.0 to 0.9.1
Updates the requirements on [pcap](https://github.com/ebfull/pcap) to permit the latest version.
- [Release notes](https://github.com/ebfull/pcap/releases)
- [Changelog](https://github.com/ebfull/pcap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ebfull/pcap/commits/v0.9.1)

---
updated-dependencies:
- dependency-name: pcap
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-02 15:52:31 +01:00
Pierre
891f4e2aeb
Merge pull request #28 from ivre/dependabot/cargo/rand-0.8.4
Update rand requirement from 0.7.3 to 0.8.4
2022-01-02 12:29:09 +01:00
Pierre
57bba9e700
Merge pull request #29 from ivre/dependabot/cargo/pnet-0.28.0
Update pnet requirement from 0.26.0 to 0.28.0
2022-01-02 12:28:50 +01:00
dependabot[bot]
294aa3ba3e
Update rand requirement from 0.7.3 to 0.8.4
Updates the requirements on [rand](https://github.com/rust-random/rand) to permit the latest version.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.7.3...0.8.4)

---
updated-dependencies:
- dependency-name: rand
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-01 22:09:24 +00:00
dependabot[bot]
cb5cba7a4d
Update pnet requirement from 0.26.0 to 0.28.0
Updates the requirements on [pnet](https://github.com/libpnet/libpnet) to permit the latest version.
- [Release notes](https://github.com/libpnet/libpnet/releases)
- [Commits](https://github.com/libpnet/libpnet/compare/v0.26.0...v0.28.0)

---
updated-dependencies:
- dependency-name: pnet
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-01 22:09:19 +00:00
Pierre
0718e35c15
Merge pull request #26 from ivre/dependabot/cargo/itertools-0.10.3
Update itertools requirement from 0.9.0 to 0.10.3
2022-01-01 23:08:10 +01:00
Pierre
b068a2e617
Merge pull request #31 from ivre/fix-kill
Fix terminaison of processes in python test script
2022-01-01 18:16:46 +01:00
_Frky
e85f527ca6 Use SIGINT and remove unecessary terminate 2021-12-31 16:03:10 +01:00
Pierre
dab5cb737f
Merge pull request #30 from ivre/fix-finack
Fix FIN,ACK behaviour
2021-12-31 15:44:46 +01:00
_Frky
81e6100713 Fix terminaison of processes in python test script 2021-12-31 12:04:34 +01:00
_Frky
0749e23eea Fix TCP FIN,ACK behaviour 2021-12-31 11:21:33 +01:00
_Frky
797c30ce91 Add unit tests for FIN,ACK packets 2021-12-31 11:14:15 +01:00
dependabot[bot]
d5c3fddc19
Update itertools requirement from 0.9.0 to 0.10.3
Updates the requirements on [itertools](https://github.com/rust-itertools/itertools) to permit the latest version.
- [Release notes](https://github.com/rust-itertools/itertools/releases)
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.9.0...v0.10.3)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-31 06:24:25 +00:00
_Frky
d29ee1553c
Merge pull request #25 from p-l-/enh-dependabot
Add a Dependabot config file
2021-12-31 07:23:33 +01:00
Pierre Lalet
0f742f4f2c Add a Dependabot config file 2021-12-30 23:56:54 +01:00
_Frky
1874bc5d61
Merge pull request #19 from p-l-/enh-rpc
Proto/RPC: support RPC over UDP
2021-12-24 15:02:08 +01:00
Pierre Lalet
166f121d76 Proto/RPC: support RPC over UDP 2021-12-23 16:21:16 +01:00
Pierre
f28e0770f6
Merge pull request #23 from Frky/master
Clean a few commented lines
2021-12-23 15:19:54 +01:00
Pierre
ddfeb1461d
Merge pull request #22 from ivre/fix-http
Fix bug in HTTP FSM
2021-12-23 15:19:26 +01:00
_Frky
6f2f3226a4 Clean a few commented lines 2021-12-23 11:19:14 +01:00
_Frky
cffc94feb7 Fix bug in HTTP FSM 2021-12-23 11:06:48 +01:00
_Frky
a422f60a9a Add test to emphasis bug in HTTP FSM 2021-12-23 11:03:40 +01:00
_Frky
54fede96a2
Merge pull request #16 from p-l-/enh-rpc
Add Unix RPC
2021-12-22 10:15:16 +01:00
Pierre Lalet
c70d9b3f1d Test: add rpcinfo test for RPC 2021-12-22 09:54:02 +01:00
Pierre Lalet
5a52dcace7 Tests: add Nmap test for RPC 2021-12-22 09:54:02 +01:00
Pierre Lalet
37895f9528 Add Unix RPC 2021-12-22 09:54:02 +01:00
_Frky
122607190a
Merge pull request #17 from p-l-/enh-tests
Tests: use a veth pair of interfaces rather than a tap
2021-12-21 07:15:47 +01:00
Pierre Lalet
951b5a0ba0 Tests: use a veth pair of interfaces rather than a tap
This will allow the use of "regular" network tools and scanners (nc /
socat, Nmap, Masscan).
2021-12-19 18:45:45 +01:00
Pierre
719101110f
Merge pull request #15 from p-l-/doc
Initial documentation
2021-12-17 18:32:34 +01:00
_Frky
dfd35d233d
Review of usage.rst 2021-12-17 18:21:21 +01:00
Pierre Lalet
920a0a0768 Initial documentation 2021-12-17 17:05:50 +01:00
_Frky
c116c7bfdb Move HTTP tests into a module 2021-12-16 22:40:54 +01:00
_Frky
dfb4707577
Merge pull request #9 from p-l-/add-ghost
Add Ghost RAT protocol
2021-12-16 22:22:39 +01:00
Pierre Lalet
c127fec54c Add Ghost RAT protocol 2021-12-16 22:11:46 +01:00
_Frky
6da8a23ede
Merge pull request #12 from p-l-/enh-proto
Replace if / else if using a match
2021-12-16 20:25:02 +01:00
_Frky
af659e272b
Merge pull request #11 from p-l-/test-fmt
CI: check Rust format
2021-12-16 20:24:20 +01:00
Pierre Lalet
7427d28d52 Replace if / else if using a match 2021-12-16 02:00:40 +01:00
Pierre Lalet
e533b1ce25 CI: check Rust format 2021-12-16 01:14:43 +01:00
_Frky
5fb3481607
Merge pull request #10 from p-l-/tests-fix-ipv6-warn
Tests: fix IPv6 warnings from Scapy
2021-12-15 22:29:45 +01:00
Pierre Lalet
b5fb589361 Tests: fix IPv6 warnings from Scapy 2021-12-15 16:06:53 +01:00
_Frky
96b82bdce2
Merge pull request #8 from p-l-/tests-add-p0f
Tests: add p0f logs
2021-12-15 06:16:27 +01:00
Pierre Lalet
e65ce63e70 Tests: use OUTDIR config value 2021-12-13 19:32:48 +01:00
Pierre Lalet
ec8011a6e4 Tests: add p0f logs 2021-12-13 19:30:40 +01:00
Pierre
2163383613
Merge pull request #7 from 0xflotus/patch-1
fix: small typo errors
2021-12-11 11:08:22 +01:00
0xflotus
afe1619b9e
fix: small typo errors 2021-12-11 00:11:07 +01:00
_Frky
2c73d44923
Merge pull request #6 from p-l-/enh-tests
Tests: cosmetic changes
2021-12-09 16:03:15 +01:00
Pierre Lalet
d2054ffc05 Tests: remove hardcoded addresses 2021-12-09 15:57:19 +01:00
Pierre Lalet
05a739bdb1 Tests: find IVRE Zeek module location 2021-12-09 15:57:02 +01:00
_Frky
f010a38d17
Merge pull request #5 from p-l-/enh-clean-tests
Tests: clean Python code, add linting to CI
2021-12-09 15:33:57 +01:00
Pierre Lalet
6efcbbbf59 Tests: add Zeek to get passiverecon logs 2021-12-09 15:11:36 +01:00
Pierre Lalet
1619261ebf Tests: update addr to use a real network address 2021-12-09 15:01:47 +01:00
Pierre Lalet
db75ffb9c5 Tests: fix TCP seq # 2021-12-09 14:52:15 +01:00
Pierre Lalet
023b3f9b38 Tests: fix stun expected data 2021-12-09 12:44:51 +01:00
Pierre Lalet
4c508ccea6 Tests: add routes to prevent wrong values from appearing in packets 2021-12-09 12:32:59 +01:00
Pierre Lalet
70eae9bc0c Tests: clean Python code, add linting to CI 2021-12-09 12:08:19 +01:00
_Frky
f397198d75
Merge pull request #4 from p-l-/add-actions
CI: add build & test workflow
2021-12-09 06:59:58 +01:00
Pierre Lalet
9fb050188d Clean-up Python test script 2021-12-08 23:35:26 +01:00
Pierre Lalet
12aa60b848 CI: add build workflow 2021-12-08 22:50:05 +01:00
_Frky
ea5b58556f
Update README.md 2021-12-08 21:47:32 +01:00
67 changed files with 10975 additions and 1215 deletions

22
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,22 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
version: 2
updates:
- package-ecosystem: cargo
directory: "/"
schedule:
interval: daily

120
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,120 @@
# This file is part of masscanned.
# Copyright 2021 - 2024 The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
name: Build masscanned
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Get Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check
- name: Install packages for build
run: sudo apt-get -q update && sudo apt-get -qy install libpcap-dev
- name: Run cargo build
uses: actions-rs/cargo@v1
with:
command: build
- name: Run cargo test
uses: actions-rs/cargo@v1
with:
command: test
- name: Create build archive
run: tar cf masscanned.tar target/debug/masscanned
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: masscanned.tar
path: masscanned.tar
test:
needs: build
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Get binary
uses: actions/download-artifact@v4
with:
name: masscanned.tar
- name: Extract build archive
run: tar xf masscanned.tar
- name: Use Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: sudo pip install -U -r test/requirements.txt
- name: Install linting tools
run: sudo pip install -U flake8 black
- name: Install packages for tests
run: sudo apt-get -q update && sudo apt-get -qy install nmap rpcbind smbclient
- name: Run black
run: black -t py36 --check test/test_masscanned.py test/src/
- name: Run flake8
run: flake8 --ignore=E266,E501,W503 test/test_masscanned.py test/src/
- name: Run tests
run: sudo python test/test_masscanned.py
- name: Display logs
run: echo STDOUT; cat test/res/masscanned.stdout && echo && echo STDERR && cat test/res/masscanned.stderr
if: failure()
docker:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Build archive
run: git archive --format=tar --prefix=masscanned-master/ HEAD -o docker/masscanned.tar
- name: Build image
uses: docker/build-push-action@v5
with:
push: false
context: docker/
file: docker/Dockerfile-local

3
.gitignore vendored
View file

@ -1,10 +1,11 @@
/target/ /target/
Cargo.lock
**/*.rs.bk **/*.rs.bk
# Vim temporary files # Vim temporary files
*.swp *.swp
*.swo *.swo
# Emacs temporary files
*~
*__pycache__* *__pycache__*
test/res/* test/res/*

1139
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -21,22 +21,23 @@ authors = ["_Frky <3105926+Frky@users.noreply.github.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
pcap = "0.7.0" bitflags = "2.9.4"
pcap-file = "1.1.1" byteorder = "1.5.0"
pnet = "0.26.0" chrono = "0.4.42"
# pnet = { path = "libpnet" } clap = "4.5.47"
clap = "2.33.3"
log = "0.4.11"
stderrlog = "0.5.0"
itertools = "0.9.0"
rand = "0.7.3"
dns-parser = "0.8.0" dns-parser = "0.8.0"
netdevice = "0.1.1" flate2 = "1.1"
bitflags = "1.2.1" itertools = "0.14.0"
lazy_static = "1.4.0" lazy_static = "1.5.0"
siphasher = "0.3" log = "0.4.28"
chrono = "0.4.19" pcap = "2.3.0"
byteorder = "1.4.3" pcap-file = "2.0.0"
pnet = { version = "0.33.0", features = ["std"] }
rand = "0.9.2"
siphasher = "1.0"
stderrlog = "0.6.0"
strum = "0.27.2"
strum_macros = "0.27.2"
[[bin]] [[bin]]
name = "masscanned" name = "masscanned"

396
README.md
View file

@ -1,8 +1,10 @@
[![Build masscanned](https://github.com/ivre/masscanned/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/ivre/masscanned/actions/workflows/test.yml?branch=master)
# Masscanned # Masscanned
**Masscanned** (name inspired, of course, by [masscan](https://github.com/robertdavidgraham/masscan)) **Masscanned** (name inspired, of course, by [masscan](https://github.com/robertdavidgraham/masscan))
is a network responder. Its purpose is to provide generic answers to as many protocols as possible, is a network responder. Its purpose is to provide generic answers to as many protocols as possible,
and with as few asumptions as possible on the client's intentions. and with as few assumptions as possible on the client's intentions.
> *Let them talk first.* > *Let them talk first.*
@ -17,22 +19,32 @@ For example, when it receives network packets:
* **masscanned** answers to `TCP SYN` (any port) with `TCP SYN/ACK` on any port, * **masscanned** answers to `TCP SYN` (any port) with `TCP SYN/ACK` on any port,
* **masscanned** answers to `HTTP` requests (any verb) over `TCP/UDP` (any port) with a `HTTP 401` web page. * **masscanned** answers to `HTTP` requests (any verb) over `TCP/UDP` (any port) with a `HTTP 401` web page.
![demo](doc/demo.gif) ![demo](doc/img/demo.gif)
## Overview
**Masscanned** currently supports most common protocols at layers 2-3-4, and a few application **Masscanned** currently supports most common protocols at layers 2-3-4, and a few application
protocols: protocols.
* `Eth::ARP::REQ`, ### Network protocols
* `Eth::IPv{4,6}::ICMP::ECHO-REQ`,
* `Eth::IPv{4,6}::TCP::SYN` (all ports), * ARP (answers to ARP requests)
* `Eth::IPv{4,6}::TCP::PSHACK` (all ports), * ICMP (answers to ping)
* `Eth::IPv6::ICMP::ND_NS`. * ICMPv6 (answers to ND NS)
* `Eth::IPv{4,6}::{TCP,UDP}::HTTP` (all HTTP verbs), * TCP (answers to SYN and PUSH)
* `Eth::IPv{4,6}::{TCP,UDP}::STUN`,
* `Eth::IPv{4,6}::{TCP,UDP}::SSH` (Server Protocol only). ### Application protocols
* HTTP (answers to all verbs)
* SSH (answers to the client banner)
* STUN (answers to binding requests)
* SMB
* DNS (answers to IN/A queries)
## Try it locally ## Try it locally
### On your host
1. Build **masscanned** 1. Build **masscanned**
``` ```
$ cargo build $ cargo build
@ -64,13 +76,77 @@ $ cargo build
... ...
``` ```
## Protocols ### In a Docker
1. Install docker:
```
# apt install docker.io
```
1. Build docker container:
```
$ cd masscanned/docker && docker build -t masscanned:test .
```
1. Run docker container:
```
$ docker run --cap-add=NET_ADMIN masscanned:test
```
1. Send packets to **masscanned**
```
# arping 172.17.0.2
# ping 172.17.0.2
# nc -n -v 172.17.0.2 80
# nc -n -v -u 172.17.0.2 80
...
```
## Use it
A good use of **masscanned** is to deploy it on a VPS with one or more public IP addresses.
To use the results, the best way is to capture all network traffic on the interface **masscanned** is listening to/responding on.
The pcaps can then be analyzed using [zeek](https://zeek.org/) and the output files can typically be pushed in an instance of **IVRE**.
A documentation on how to deploy an instance of **masscanned** on a VPS is coming (see [Issue #2](https://github.com/ivre/masscanned/issues/2)).
### Supported options
```
Network answering machine for various network protocols (L2-L3-L4 + applications)
Usage: masscanned [OPTIONS] --iface <iface>
Options:
-i, --iface <iface>
the interface to use for receiving/sending packets
-m, --mac-addr <mac>
MAC address to use in the response packets
--self-ip-file <selfipfile>
File with the list of IP addresses handled by masscanned
--self-ip-list <selfiplist>
Inline list of IP addresses handled by masscanned, comma-separated
--remote-ip-deny-file <remoteipdenyfile>
File with the list of IP addresses from which masscanned will ignore packets
--remote-ip-deny-list <remoteipdenylist>
Inline list of IP addresses from which masscanned will ignore packets
-v...
Increase message verbosity
-q, --quiet
Quiet mode: do not output anything on stdout
--format <format>
Format in which to output logs [default: console] [possible values: console, logfmt]
-h, --help
Print help information
-V, --version
Print version information
```
## Supported protocols - details
### Layer 2 ### Layer 2
#### ARP #### ARP
`masscanned` anwsers to `ARP` requests, for requests that target an `IPv4` address `masscanned` answers to `ARP` requests, for requests that target an `IPv4` address
that is handled by `masscanned` (*i.e.*, an address that is in the that is handled by `masscanned` (*i.e.*, an address that is in the
IP address file given with option `-f`). IP address file given with option `-f`).
@ -112,7 +188,7 @@ An additionnal requirement is that the next layer protocol is supported - see be
#### IPv4 #### IPv4
The following L4 protocols are suppported for an `IPv4` packet: The following L3+/4 protocols are supported for an `IPv4` packet:
* `ICMPv4` * `ICMPv4`
* `UDP` * `UDP`
@ -122,7 +198,7 @@ If the next layer protocol is not one of them, the packet is dropped.
#### IPv6 #### IPv6
The following L4 protocols are suppported for an `IPv6` packet: The following L3+/4 protocols are supported for an `IPv6` packet:
* `ICMPv6` * `ICMPv6`
* `UDP` * `UDP`
@ -148,7 +224,7 @@ code `0` and the same payload as the incoming packet, as specified by [RFC 792](
* the `ICMP` type is `NeighborSol` (`135`) **and**: * the `ICMP` type is `NeighborSol` (`135`) **and**:
* no IP (v4 or v6) was speficied for `masscanned` * no IP (v4 or v6) was speficied for `masscanned`
* **or** the target address of the Neighbor Solicitation is one of `masccanned` * **or** the target address of the Neighbor Solicitation is one of `masscanned`
*In that case, the answer is a `Neighbor Advertisement` (`136`) packet with `masscanned` `MAC` address* *In that case, the answer is a `Neighbor Advertisement` (`136`) packet with `masscanned` `MAC` address*
@ -166,19 +242,68 @@ code `0` and the same payload as the incoming packet, as specified by [RFC 792](
a supported protocol (Layer 5/6/7) has been detected, a supported protocol (Layer 5/6/7) has been detected,
* if the received packet has flag `ACK`, it is ignored, * if the received packet has flag `ACK`, it is ignored,
* if the received packet has flag `RST` or `FIN-ACK`, it is ignored, * if the received packet has flag `RST` or `FIN-ACK`, it is ignored,
* if the received packet has flag `SYN`, then `masscanned` answers with a `SYN-ACK` packet, setting a **SYNACK-cookie** in the sequence number. * if the received packet has flag `SYN`, then `masscanned` tries to imitate the behaviour
of a standard Linux stack - which is:
* if there are additional flags that are not among `PSH`, `URG`, `CWR`, `ECE`, then the `SYN` is ignored,
* if the flags `CWR` and`ECE` are simultaneously set, then the `SYN` is ignored,
* in any other case, `masscanned` answers with a `SYN-ACK` packet, setting a **SYNACK-cookie** in the sequence number.
#### UDP #### UDP
`masscanned` answers to an `UDP` packet if and only if the upper-layer protocol `masscanned` answers to an `UDP` packet if and only if the upper-layer protocol
is handled and provides an answer. is handled and provides an answer.
### Protocols ### Application protocols
#### HTTP #### HTTP
`masscanned` answers to any `HTTP` request (any **valid** verb) with a `401 Authorization Required`.
Note that `HTTP` requests with an invalid verb will not be answered.
Example:
```
$ curl -X GET 10.11.10.129
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>
$ curl -X OPTIONS 10.11.10.129
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>
$ curl -X HEAD 10.11.10.129
Warning: Setting custom HTTP method to HEAD with -X/--request may not work the
Warning: way you want. Consider using -I/--head instead.
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>
$ curl -X XXX 10.11.10.129
[timeout]
```
#### STUN #### STUN
Example:
```
$ stun 10.11.10.129
STUN client version 0.97
Primary: Open
Return value is 0x000001
```
#### SSH #### SSH
`masscanned` answers to `SSH` `Client: Protocol` messages with the following `Server: Protocol` message: `masscanned` answers to `SSH` `Client: Protocol` messages with the following `Server: Protocol` message:
@ -187,6 +312,57 @@ is handled and provides an answer.
SSH-2.0-1\r\n SSH-2.0-1\r\n
``` ```
#### SMB
`masscanned` answers to `Negotiate Protocol Request` packets in order for the
client to send a `NTLMSSP_NEGOTIATE`, to which `masscanned` answers with a challenge.
Example:
```
##$ smbclient -U user \\\\10.11.10.129\\shared
Enter WORKGROUP\user's password:
```
#### DNS
`masscanned` answers to `DNS` queries of class `IN` and type `A` (for now).
The answer it provides always contains the IP address the query was sent to.
Example:
```
$ host -t A masscan.ned 10.11.10.129
Using domain server:
Name: 10.11.10.129
Address: 10.11.10.129#53
Aliases:
masscan.ned has address 10.11.10.129
$ host -t A masscan.ned 10.11.10.130
Using domain server:
Name: 10.11.10.130
Address: 10.11.10.130#53
Aliases:
masscan.ned has address 10.11.10.130
$ host -t A masscan.ned 10.11.10.131
Using domain server:
Name: 10.11.10.131
Address: 10.11.10.131#53
Aliases:
masscan.ned has address 10.11.10.131
$ host -t A masscan.ned 10.11.10.132
Using domain server:
Name: 10.11.10.132
Address: 10.11.10.132#53
Aliases:
masscan.ned has address 10.11.10.132
```
## Internals ## Internals
### Tests ### Tests
@ -196,68 +372,118 @@ SSH-2.0-1\r\n
``` ```
$ cargo test $ cargo test
Compiling masscanned v0.2.0 (/zdata/workdir/masscanned) Compiling masscanned v0.2.0 (/zdata/workdir/masscanned)
Finished test [unoptimized + debuginfo] target(s) in 2.34s Finished test [unoptimized + debuginfo] target(s) in 3.83s
Running target/debug/deps/masscanned-b86211a090e50323 Running unittests (target/debug/deps/masscanned-f9292f8600038978)
running 36 tests running 92 tests
test client::client_info::tests::test_client_info_eq ... ok test client::client_info::tests::test_client_info_eq ... ok
test layer_2::arp::tests::test_arp_reply ... ok test layer_2::arp::tests::test_arp_reply ... ok
test layer_3::ipv4::tests::test_ipv4_reply ... ok test layer_2::tests::test_eth_empty ... ok
test layer_3::ipv6::tests::test_ipv6_reply ... ok
test layer_4::icmpv6::tests::test_icmpv6_reply ... ok
test layer_2::tests::test_eth_reply ... ok test layer_2::tests::test_eth_reply ... ok
test layer_4::icmpv6::tests::test_nd_na_reply ... ok test layer_3::ipv4::tests::test_ipv4_reply ... ok
test layer_4::tcp::tests::test_synack_cookie_ipv4 ... ok test layer_3::ipv4::tests::test_ipv4_empty ... ok
test layer_3::ipv6::tests::test_ipv6_empty ... ok
test layer_3::ipv6::tests::test_ipv6_reply ... ok
test layer_4::icmpv4::tests::test_icmpv4_reply ... ok test layer_4::icmpv4::tests::test_icmpv4_reply ... ok
test layer_4::icmpv6::tests::test_icmpv6_reply ... ok
test layer_4::icmpv6::tests::test_nd_na_reply ... ok
test layer_4::tcp::tests::test_synack_cookie_ipv6 ... ok test layer_4::tcp::tests::test_synack_cookie_ipv6 ... ok
test proto::http::test_http_request_field ... ok test layer_4::tcp::tests::test_tcp_fin_ack_wrap ... ok
test proto::http::test_http_request_no_field ... ok test proto::dns::cst::tests::class_parse ... ok
test proto::http::test_http_request_line ... ok test layer_4::tcp::tests::test_tcp_fin_ack ... ok
test proto::http::test_http_verb ... ok test layer_4::tcp::tests::test_synack_cookie_ipv4 ... ok
test proto::stun::tests::test_change_request_port ... ok test proto::dns::cst::tests::type_parse ... ok
test proto::stun::tests::test_proto_stun_ipv6 ... ok test proto::dns::header::tests::parse_byte_by_byte ... ok
test proto::stun::tests::test_proto_stun_ipv4 ... ok test proto::dns::header::tests::repl_id ... ok
test proto::dns::header::tests::repl_opcode ... ok
test proto::dns::header::tests::repl_ancount ... ok
test proto::dns::header::tests::repl_rd ... ok
test proto::dns::query::tests::parse_in_a_all ... ok
test proto::dns::header::tests::parse_all ... ok
test proto::dns::query::tests::repl ... ok
test proto::dns::query::tests::reply_in_a ... ok
test proto::dns::rr::tests::parse_all ... ok
test proto::dns::rr::tests::parse_byte_by_byte ... ok
test proto::dns::query::tests::parse_in_a_byte_by_byte ... ok
test proto::dns::tests::parse_qd_all ... ok
test proto::dns::tests::parse_qd_byte_by_byte ... ok
test proto::dns::rr::tests::build ... ok
test proto::dns::tests::parse_qd_rr_all ... ok
test proto::dns::tests::parse_qr_rr_byte_by_byte ... ok
test proto::dns::tests::parse_rr_byte_by_byte ... ok
test proto::dns::tests::parse_rr_all ... ok
test proto::dns::tests::reply_in_a ... ok
test proto::http::tests::test_http_request_line ... ok
test proto::http::tests::test_http_request_no_field ... ok
test proto::http::tests::test_http_request_field ... ok
test proto::http::tests::test_http_verb ... ok
test proto::rpc::tests::test_probe_nmap ... ok
test proto::rpc::tests::test_probe_nmap_split1 ... ok
test proto::rpc::tests::test_probe_portmap_v4_dump ... ok
test proto::rpc::tests::test_probe_nmap_split2 ... ok
test proto::rpc::tests::test_probe_nmap_udp ... ok
test proto::smb::tests::test_smb1_session_setup_request_parse ... ok
test proto::smb::tests::test_smb1_protocol_nego_parsing ... ok
test proto::smb::tests::test_smb1_protocol_nego_reply ... ok
test proto::smb::tests::test_smb1_session_setup_request_reply ... ok
test proto::smb::tests::test_smb2_protocol_nego_parsing ... ok
test proto::smb::tests::test_smb2_protocol_nego_reply ... ok
test proto::smb::tests::test_smb2_session_setup_request_reply ... ok
test proto::smb::tests::test_smb2_session_setup_request_parse ... ok
test proto::ssh::tests::ssh_1_banner_cr ... ok
test proto::ssh::tests::ssh_1_banner_crlf ... ok
test proto::ssh::tests::ssh_1_banner_lf ... ok
test proto::ssh::tests::ssh_1_banner_space ... ok
test proto::ssh::tests::ssh_2_banner_cr ... ok
test proto::ssh::tests::ssh_1_banner_parse ... ok
test proto::ssh::tests::ssh_2_banner_parse ... ok
test proto::ssh::tests::ssh_2_banner_lf ... ok
test proto::ssh::tests::ssh_2_banner_crlf ... ok
test proto::stun::tests::test_change_request_port_overflow ... ok test proto::stun::tests::test_change_request_port_overflow ... ok
test smack::smack::tests::test_anchor_end ... ok test proto::stun::tests::test_proto_stun_ipv4 ... ok
test smack::smack::tests::test_anchor_begin ... ok test proto::stun::tests::test_change_request_port ... ok
test smack::smack::tests::test_multiple_matches ... ok test proto::ssh::tests::ssh_2_banner_space ... ok
test smack::smack::tests::test_http_banner ... ok test proto::stun::tests::test_proto_stun_ipv6 ... ok
test smack::smack::tests::test_multiple_matches_wildcard ... ok test proto::tcb::tests::test_proto_tcb_proto_state_http ... ok
test smack::smack::tests::test_proto ... ok test proto::tests::dispatch_dns ... ok
test smack::smack::tests::test_wildcard ... ok test proto::tcb::tests::test_proto_tcb_proto_state_rpc ... ok
test proto::tcb::tests::test_proto_tcb_proto_id ... ok
test proto::tests::test_proto_dispatch_http ... ok
test proto::tests::test_proto_dispatch_ssh ... ok test proto::tests::test_proto_dispatch_ssh ... ok
test proto::tests::test_proto_dispatch_ghost ... ok
test proto::tests::test_proto_dispatch_stun ... ok test proto::tests::test_proto_dispatch_stun ... ok
test smack::smack::tests::test_anchor_end ... ok
test smack::smack::tests::test_multiple_matches_wildcard ... ok
test smack::smack::tests::test_multiple_matches ... ok
test smack::smack::tests::test_anchor_begin ... ok
test smack::smack::tests::test_http_banner ... ok
test synackcookie::tests::test_clientinfo ... ok test synackcookie::tests::test_clientinfo ... ok
test synackcookie::tests::test_ip4 ... ok
test synackcookie::tests::test_ip4_dst ... ok test synackcookie::tests::test_ip4_dst ... ok
test synackcookie::tests::test_ip4_src ... ok test synackcookie::tests::test_ip4_src ... ok
test synackcookie::tests::test_ip4 ... ok
test synackcookie::tests::test_ip6 ... ok test synackcookie::tests::test_ip6 ... ok
test synackcookie::tests::test_key ... ok test synackcookie::tests::test_key ... ok
test synackcookie::tests::test_tcp_dst ... ok test synackcookie::tests::test_tcp_dst ... ok
test synackcookie::tests::test_tcp_src ... ok test synackcookie::tests::test_tcp_src ... ok
test smack::smack::tests::test_wildcard ... ok
test smack::smack::tests::test_proto ... ok
test smack::smack::tests::test_pattern ... ok test smack::smack::tests::test_pattern ... ok
test result: ok. 36 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out test result: ok. 92 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.41s
``` ```
#### Functional tests #### Functional tests
``` ```
# ./test/test_masscanned.py # ./test/test_masscanned.py
tcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
INFO test_arp_req......................................OK INFO test_arp_req......................................OK
INFO test_arp_req_other_ip.............................OK INFO test_arp_req_other_ip.............................OK
INFO test_ipv4_req.....................................OK INFO test_ipv4_udp_dns_in_a............................OK
INFO test_eth_req_other_mac............................OK INFO test_ipv4_udp_dns_in_a_multiple_queries...........OK
INFO test_ipv4_req_other_ip............................OK INFO test_ipv4_tcp_ghost...............................OK
INFO test_icmpv4_echo_req..............................OK
INFO test_icmpv6_neighbor_solicitation.................OK
INFO test_icmpv6_neighbor_solicitation_other_ip........OK
INFO test_icmpv6_echo_req..............................OK
INFO test_tcp_syn......................................OK
INFO test_ipv4_tcp_psh_ack.............................OK
INFO test_ipv6_tcp_psh_ack.............................OK
INFO test_ipv4_tcp_http................................OK INFO test_ipv4_tcp_http................................OK
INFO test_ipv4_tcp_http_segmented......................OK
INFO test_ipv4_tcp_http_incomplete.....................OK
INFO test_ipv6_tcp_http................................OK INFO test_ipv6_tcp_http................................OK
INFO test_ipv4_udp_http................................OK INFO test_ipv4_udp_http................................OK
INFO test_ipv6_udp_http................................OK INFO test_ipv6_udp_http................................OK
@ -265,26 +491,64 @@ INFO test_ipv4_tcp_http_ko.............................OK
INFO test_ipv4_udp_http_ko.............................OK INFO test_ipv4_udp_http_ko.............................OK
INFO test_ipv6_tcp_http_ko.............................OK INFO test_ipv6_tcp_http_ko.............................OK
INFO test_ipv6_udp_http_ko.............................OK INFO test_ipv6_udp_http_ko.............................OK
INFO test_ipv4_udp_stun................................OK INFO test_icmpv4_echo_req..............................OK
INFO test_ipv6_udp_stun................................OK INFO test_icmpv6_neighbor_solicitation.................OK
INFO test_ipv4_udp_stun_change_port....................OK INFO test_icmpv6_neighbor_solicitation_other_ip........OK
INFO test_ipv6_udp_stun_change_port....................OK INFO test_icmpv6_echo_req..............................OK
INFO test_ipv4_req.....................................OK
INFO test_eth_req_other_mac............................OK
INFO test_ipv4_req_other_ip............................OK
INFO test_rpc_nmap.....................................OK
INFO test_rpcinfo......................................OK
INFO test_smb1_network_req.............................OK
INFO test_smb2_network_req.............................OK
INFO test_ipv4_tcp_ssh.................................OK INFO test_ipv4_tcp_ssh.................................OK
INFO test_ipv4_udp_ssh.................................OK INFO test_ipv4_udp_ssh.................................OK
INFO test_ipv6_tcp_ssh.................................OK INFO test_ipv6_tcp_ssh.................................OK
INFO test_ipv6_udp_ssh.................................OK INFO test_ipv6_udp_ssh.................................OK
tcpdump: pcap_loop: The interface disappeared INFO test_ipv4_udp_stun................................OK
604 packets captured INFO test_ipv6_udp_stun................................OK
604 packets received by filter INFO test_ipv4_udp_stun_change_port....................OK
0 packets dropped by kernel INFO test_ipv6_udp_stun_change_port....................OK
INFO test_ipv4_tcp_empty...............................OK
INFO test_ipv6_tcp_empty...............................OK
INFO test_tcp_syn......................................OK
INFO test_ipv4_tcp_psh_ack.............................OK
INFO test_ipv6_tcp_psh_ack.............................OK
INFO test_ipv4_udp_empty...............................OK
INFO test_ipv6_udp_empty...............................OK
INFO Ran 41 tests with 0 errors
``` ```
### Logging Policy You can also chose what tests to run using the `TESTS` environment variable
```
TESTS=smb ./test/test_masscanned.py
INFO test_smb1_network_req.............................OK
INFO test_smb2_network_req.............................OK
INFO Ran 2 tests with 0 errors
```
* `ERR`: any error - will always be displayed. ## Logging
* `WARN`, `-v`: responses sent by `masscanned`.
* `INFO`, `-vv`: packets not handled, packets ignored. ### Console Logger
* `DEBUG`, `-vvv`: all packets received and sent by `masscanned`.
**Verbs**:
* `init`
* `recv`
* `send`
* `drop`
#### ARP
```
$ts arp $verb $operation $client_mac $client_ip $masscanned_mac $masscanned_ip
```
#### Ethernet
```
$ts eth $verb $ethertype $client_mac $masscanned_mac
```
## To Do ## To Do

19
doc/Makefile Normal file
View file

@ -0,0 +1,19 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

53
doc/conf.py Normal file
View file

@ -0,0 +1,53 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
from ast import literal_eval
import configparser
import os
# -- Path setup --------------------------------------------------------------
# -- Project information -----------------------------------------------------
project = "IVRE"
copyright = "2021, The IVRE project"
html_logo = "img/logo.png"
master_doc = "index"
def parse_cargo():
config = configparser.ConfigParser()
config.read(os.path.join("..", "Cargo.toml"))
if "package" not in config:
return None, None, None
package = config["package"]
try:
author = literal_eval(package.get("authors"))[0].split("<", 1)[0].strip()
except KeyError:
authors = None
return literal_eval(package.get("name")), author, literal_eval(package.get("version"))
project, author, version = parse_cargo()
# -- General configuration ---------------------------------------------------
extensions = []
autosectionlabel_prefix_document = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"

View file

Before

Width:  |  Height:  |  Size: 4.2 MiB

After

Width:  |  Height:  |  Size: 4.2 MiB

Before After
Before After

BIN
doc/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

35
doc/index.rst Normal file
View file

@ -0,0 +1,35 @@
Welcome to Masscanned documentation!
====================================
Introduction
------------
Masscanned is a low-interaction honeypot, primarily designed to help
gather intelligence about network scanners and bots.
It has been built as a companion tool for `IVRE
<https://ivre.rocks/>`_ but can be used independently.
The code is on `GitHub <https://github.com/ivre/masscanned>`_.
Here is a quick demo:
|demo|
Status of this documentation
----------------------------
This documentation is a work in progress!
Content
-------
.. toctree::
:maxdepth: 3
:caption: Usage:
:glob:
usage
.. |demo| image:: img/demo.gif

92
doc/usage.rst Normal file
View file

@ -0,0 +1,92 @@
Using Masscanned
================
Dedicated addresses
~~~~~~~~~~~~~~~~~~~
Masscanned is designed to handle its own IP addresses, which means
that the host should not have those addresses configured, and
Masscanned will answer ``ARP`` requests (or ``ICMPv6`` ``ND`` neighbor
sollicitations).
The host may have one or more (``IPv4`` and/or ``IPv6``) addresses configured
on an interface also used by masscanned, but those addresses must be
different from those configured to be used by masscanned.
In that situation (dedicated addresses), just run:
::
# masscanned -i <iface> -f <ip_addr_file>
where ``<ip_addr_file>`` is the path of a text file with one address (``IPv4``
or ``IPv6``) per line.
Addresses shared with the host
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes it is desirable to have an IP address used by the host
(*e.g.*, for administration tasks) and by masscanned (to handle all
other incoming packets).
Since this is not implemented in masscanned, a tiny hack is needed: we
are going to run it on a ``veth`` interface.
For this example, we suppose:
- The interface is ``eth0``, the address is ``192.168.0.10``.
- We want masscanned to handle all the traffic except for incoming SSH
connections on TCP/22 port.
We create a ``veth`` pair of interfaces, on which we are going to use
the 0.255.0.0/31 network (which should not be a problem since
0.0.0.0/8 is reserved as "Current Network"):
::
# ip link add to_masscanned type veth peer masscanned
# ip link set masscanned up
# ip link set to_masscanned up
# ip addr add 0.255.0.0/31 dev to_masscanned
# masscanned -i masscanned
Masscanned can now be used, but only from the host where it runs:
::
# ping -c 1 0.255.0.1
PING 0.255.0.1 (0.255.0.1) 56(84) octets de données.
64 octets de 0.255.0.1 : icmp_seq=1 ttl=64 temps=0.442 ms
--- statistiques ping 0.255.0.1 ---
1 paquets transmis, 1 reçus, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.442/0.442/0.442/0.000 ms
Now, we are going to use Netfilter / ``iptables`` to redirect incoming
traffic to masscanned:
::
# sysctl -w net.ipv4.ip_forward=1
# iptables -t nat -A PREROUTING -i eth0 -d 192.168.0.10 -p tcp --dport 22 -j ACCEPT
# iptables -t nat -A PREROUTING -i eth0 -d 192.168.0.10/32 -j DNAT --to-destination 0.255.0.1
And, from another host on the 192.168.0.0/24 network:
::
# ping -c 1 192.168.0.10
PING 192.168.0.10 (192.168.0.10) 56(84) octets de données.
64 octets de 192.168.0.10 : icmp_seq=1 ttl=63 temps=0.366 ms
--- statistiques ping 192.168.0.10 ---
1 paquets transmis, 1 reçus, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.366/0.366/0.366/0.000 ms
The masscanned output:
::
WARN - ARP-Reply to ea:c0:d6:20:0c:6a for IP 0.255.0.1
WARN - ICMP-Echo-Reply to ICMP-Echo-Request

45
docker/Dockerfile Normal file
View file

@ -0,0 +1,45 @@
# This file is part of masscanned.
# Copyright 2021 - 2024 The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
FROM debian:12 AS fetcher
RUN apt-get -q update && \
apt-get -qy --no-install-recommends install ca-certificates curl && \
curl -L https://github.com/ivre/masscanned/archive/refs/heads/master.tar.gz | tar zxf -
FROM rust AS builder
COPY --from=fetcher /masscanned-master /masscanned-master
RUN cd masscanned-master && \
cargo build --release
FROM debian:12
LABEL maintainer="Pierre LALET <pierre@droids-corp.org>"
COPY --from=builder /masscanned-master/target/release/masscanned /usr/local/bin/masscanned
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get -q update && \
apt-get -qy --no-install-recommends install iproute2 iptables && \
apt-get clean && rm -rf /var/lib/apt/lists/*
COPY runmasscanned /usr/local/bin/runmasscanned
CMD /usr/local/bin/runmasscanned

38
docker/Dockerfile-local Normal file
View file

@ -0,0 +1,38 @@
# This file is part of masscanned.
# Copyright 2021 - 2024 The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
FROM rust AS builder
ADD masscanned.tar ./
RUN cd masscanned-master && \
cargo build --release
FROM debian:12
LABEL maintainer="Pierre LALET <pierre@droids-corp.org>"
COPY --from=builder /masscanned-master/target/release/masscanned /usr/local/bin/masscanned
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get -q update && \
apt-get -qy --no-install-recommends install iproute2 iptables && \
apt-get clean && rm -rf /var/lib/apt/lists/*
COPY runmasscanned /usr/local/bin/runmasscanned
CMD /usr/local/bin/runmasscanned

35
docker/runmasscanned Executable file
View file

@ -0,0 +1,35 @@
#! /bin/bash
# This file is part of masscanned.
# Copyright 2021 - 2023 The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
iface="$(ip route get 0.0.0.1 | awk '/^0\.0\.0\.1 via / {print $5}')"
addrs="$(ip a show eth0 | awk '/ inet6? / {print $2}' | sed 's#/.*##' | tr '\n' ',' | sed 's#,$##')"
if ! capsh --print | awk '/^Current: / {print $2}' | tr ',' '\n' | grep -q '^cap_net_admin$'; then
echo "WARNING: cannot run iptables (need capability cap_net_admin)" >&2
exit 1
fi
for v in '' 6; do
for c in INPUT OUTPUT FORWARD; do
ip${v}tables -P $c DROP
done
done
echo Interface: "$iface"
echo Addresses: "$addrs"
/usr/local/bin/masscanned -i "$iface" --self-ip-list "$addrs"

View file

@ -21,7 +21,7 @@ use std::net::IpAddr;
use pnet::packet::ip::IpNextHeaderProtocol; use pnet::packet::ip::IpNextHeaderProtocol;
use pnet::util::MacAddr; use pnet::util::MacAddr;
#[derive(PartialEq, Hash, Copy, Clone)] #[derive(PartialEq, Hash, Copy, Clone, Debug)]
pub struct ClientInfoSrcDst<A: Hash + PartialEq + Clone> { pub struct ClientInfoSrcDst<A: Hash + PartialEq + Clone> {
pub src: Option<A>, pub src: Option<A>,
pub dst: Option<A>, pub dst: Option<A>,
@ -35,7 +35,7 @@ pub struct ClientInfoSrcDst<A: Hash + PartialEq + Clone> {
* - source and dest. transport port * - source and dest. transport port
* - syn cookie * - syn cookie
**/ **/
#[derive(Copy, Clone)] #[derive(Copy, Clone, PartialEq, Debug)]
pub struct ClientInfo { pub struct ClientInfo {
pub mac: ClientInfoSrcDst<MacAddr>, pub mac: ClientInfoSrcDst<MacAddr>,
pub ip: ClientInfoSrcDst<IpAddr>, pub ip: ClientInfoSrcDst<IpAddr>,
@ -65,30 +65,6 @@ impl ClientInfo {
} }
} }
impl PartialEq for ClientInfo {
fn eq(&self, other: &Self) -> bool {
if self.mac != other.mac {
return false;
}
if self.ip != other.ip {
return false;
}
if self.transport != other.transport {
return false;
}
if self.port != other.port {
return false;
}
/* this next case should never occur with TCP and UDP,
* but this implementation tries to remain transport-protocol-agnostic
**/
if self.cookie != other.cookie {
return false;
}
true
}
}
impl Eq for ClientInfo {} impl Eq for ClientInfo {}
impl Display for ClientInfo { impl Display for ClientInfo {

View file

@ -29,20 +29,18 @@ pub fn repl<'a, 'b>(
arp_req: &'a ArpPacket, arp_req: &'a ArpPacket,
masscanned: &Masscanned, masscanned: &Masscanned,
) -> Option<MutableArpPacket<'b>> { ) -> Option<MutableArpPacket<'b>> {
masscanned.log.arp_recv(arp_req);
let mut arp_repl = let mut arp_repl =
MutableArpPacket::owned(arp_req.packet().to_vec()).expect("error parsing ARP packet"); MutableArpPacket::owned(arp_req.packet().to_vec()).expect("error parsing ARP packet");
/* Build ARP answer depending of the type of request */ /* Build ARP answer depending of the type of request */
match arp_req.get_operation() { match arp_req.get_operation() {
ArpOperations::Request => { ArpOperations::Request => {
masscanned.log.arp_recv(arp_req);
let ip = IpAddr::V4(arp_req.get_target_proto_addr()); let ip = IpAddr::V4(arp_req.get_target_proto_addr());
/* Ignore ARP requests for IP addresses not handled by masscanned */ /* Ignore ARP requests for IP addresses not handled by masscanned */
if let Some(ip_addr_list) = masscanned.ip_addresses { if let Some(ip_addr_list) = masscanned.self_ip_list {
if !ip_addr_list.contains(&ip) { if !ip_addr_list.contains(&ip) {
info!( masscanned.log.arp_drop(arp_req);
"Ignoring ARP request from {} for IP {}",
arp_req.get_sender_hw_addr(),
ip
);
return None; return None;
} }
} }
@ -53,17 +51,15 @@ pub fn repl<'a, 'b>(
arp_repl.set_target_hw_addr(arp_req.get_sender_hw_addr().to_owned()); arp_repl.set_target_hw_addr(arp_req.get_sender_hw_addr().to_owned());
arp_repl.set_target_proto_addr(arp_req.get_sender_proto_addr().to_owned()); arp_repl.set_target_proto_addr(arp_req.get_sender_proto_addr().to_owned());
arp_repl.set_sender_proto_addr(arp_req.get_target_proto_addr().to_owned()); arp_repl.set_sender_proto_addr(arp_req.get_target_proto_addr().to_owned());
warn!( masscanned.log.arp_send(&arp_repl);
"ARP-Reply to {} for IP {}",
arp_req.get_sender_hw_addr(),
arp_repl.get_sender_proto_addr()
);
} }
_ => { _ => {
info!("ARP Operation not handled: {:?}", arp_repl.get_operation()); info!("ARP Operation not handled: {:?}", arp_repl.get_operation());
masscanned.log.arp_drop(arp_req);
return None; return None;
} }
}; };
masscanned.log.arp_send(&arp_repl);
Some(arp_repl) Some(arp_repl)
} }
@ -76,6 +72,8 @@ mod tests {
use pnet::util::MacAddr; use pnet::util::MacAddr;
use crate::logger::MetaLogger;
#[test] #[test]
fn test_arp_reply() { fn test_arp_reply() {
let mut ips = HashSet::new(); let mut ips = HashSet::new();
@ -85,7 +83,9 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
let mut arp_req = let mut arp_req =
MutableArpPacket::owned([0; 28].to_vec()).expect("error constructing ARP request"); MutableArpPacket::owned([0; 28].to_vec()).expect("error constructing ARP request");

View file

@ -104,30 +104,32 @@ pub fn reply<'a, 'b>(
masscanned: &Masscanned, masscanned: &Masscanned,
mut client_info: &mut ClientInfo, mut client_info: &mut ClientInfo,
) -> Option<MutableEthernetPacket<'b>> { ) -> Option<MutableEthernetPacket<'b>> {
debug!("receiving Ethernet packet: {:?}", eth_req); /* Fill client information for this packet with MAC addresses (src and dst) */
client_info.mac.src = Some(eth_req.get_source());
client_info.mac.dst = Some(eth_req.get_destination());
masscanned.log.eth_recv(eth_req, &client_info);
let mut eth_repl; let mut eth_repl;
/* First, check if the destination MAC address is one of those masscanned /* First, check if the destination MAC address is one of those masscanned
* is authorized to answer to (avoid answering to packets addressed to * is authorized to answer to (avoid answering to packets addressed to
* other machines) * other machines)
**/ **/
if !get_authorized_eth_addr(&masscanned.mac, masscanned.ip_addresses) if !get_authorized_eth_addr(&masscanned.mac, masscanned.self_ip_list)
.contains(&eth_req.get_destination()) .contains(&eth_req.get_destination())
{ {
info!( masscanned.log.eth_drop(eth_req, &client_info);
"Ignoring Ethernet packet from {} to {}",
eth_req.get_source(),
eth_req.get_destination(),
);
return None; return None;
} }
/* Fill client information for this packet with MAC addresses (src and dst) */
client_info.mac.src = Some(eth_req.get_source());
client_info.mac.dst = Some(eth_req.get_destination());
/* Build next layer payload for answer depending on the incoming packet */ /* Build next layer payload for answer depending on the incoming packet */
match eth_req.get_ethertype() { match eth_req.get_ethertype() {
/* Construct answer to ARP request */ /* Construct answer to ARP request */
EtherTypes::Arp => { EtherTypes::Arp => {
let arp_req = ArpPacket::new(eth_req.payload()).expect("error parsing ARP packet"); let arp_req = if let Some(p) = ArpPacket::new(eth_req.payload()) {
p
} else {
warn!("error parsing ARP packet");
masscanned.log.eth_drop(eth_req, &client_info);
return None;
};
if let Some(arp_repl) = arp::repl(&arp_req, masscanned) { if let Some(arp_repl) = arp::repl(&arp_req, masscanned) {
let arp_len = arp_repl.packet().len(); let arp_len = arp_repl.packet().len();
let eth_len = EthernetPacket::minimum_packet_size() + arp_len; let eth_len = EthernetPacket::minimum_packet_size() + arp_len;
@ -136,6 +138,7 @@ pub fn reply<'a, 'b>(
eth_repl.set_ethertype(EtherTypes::Arp); eth_repl.set_ethertype(EtherTypes::Arp);
eth_repl.set_payload(arp_repl.packet()); eth_repl.set_payload(arp_repl.packet());
} else { } else {
masscanned.log.eth_drop(eth_req, &client_info);
return None; return None;
} }
} }
@ -145,6 +148,7 @@ pub fn reply<'a, 'b>(
p p
} else { } else {
warn!("error parsing IPv4 packet"); warn!("error parsing IPv4 packet");
masscanned.log.eth_drop(eth_req, &client_info);
return None; return None;
}; };
if let Some(mut ipv4_repl) = if let Some(mut ipv4_repl) =
@ -158,12 +162,19 @@ pub fn reply<'a, 'b>(
eth_repl.set_ethertype(EtherTypes::Ipv4); eth_repl.set_ethertype(EtherTypes::Ipv4);
eth_repl.set_payload(ipv4_repl.packet()); eth_repl.set_payload(ipv4_repl.packet());
} else { } else {
masscanned.log.eth_drop(eth_req, &client_info);
return None; return None;
} }
} }
/* Construct answer to IPv6 packet */ /* Construct answer to IPv6 packet */
EtherTypes::Ipv6 => { EtherTypes::Ipv6 => {
let ipv6_req = Ipv6Packet::new(eth_req.payload()).expect("error parsing IPv6 packet"); let ipv6_req = if let Some(p) = Ipv6Packet::new(eth_req.payload()) {
p
} else {
warn!("error parsing IPv6 packet");
masscanned.log.eth_drop(eth_req, &client_info);
return None;
};
if let Some(ipv6_repl) = layer_3::ipv6::repl(&ipv6_req, masscanned, &mut client_info) { if let Some(ipv6_repl) = layer_3::ipv6::repl(&ipv6_req, masscanned, &mut client_info) {
let ipv6_len = ipv6_repl.packet().len(); let ipv6_len = ipv6_repl.packet().len();
let eth_len = EthernetPacket::minimum_packet_size() + ipv6_len; let eth_len = EthernetPacket::minimum_packet_size() + ipv6_len;
@ -172,18 +183,20 @@ pub fn reply<'a, 'b>(
eth_repl.set_ethertype(EtherTypes::Ipv6); eth_repl.set_ethertype(EtherTypes::Ipv6);
eth_repl.set_payload(ipv6_repl.packet()); eth_repl.set_payload(ipv6_repl.packet());
} else { } else {
masscanned.log.eth_drop(eth_req, &client_info);
return None; return None;
} }
} }
/* Log & drop unknown network protocol */ /* Log & drop unknown network protocol */
_ => { _ => {
info!("Ethernet type not handled: {:?}", eth_req.get_ethertype()); info!("Ethernet type not handled: {:?}", eth_req.get_ethertype());
masscanned.log.eth_drop(eth_req, &client_info);
return None; return None;
} }
}; };
eth_repl.set_source(masscanned.mac); eth_repl.set_source(masscanned.mac);
eth_repl.set_destination(eth_req.get_source()); eth_repl.set_destination(eth_req.get_source());
debug!("sending Ethernet packet: {:?}", eth_repl); masscanned.log.eth_send(&eth_repl, &client_info);
Some(eth_repl) Some(eth_repl)
} }
@ -193,6 +206,46 @@ mod tests {
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr; use std::str::FromStr;
use crate::logger::MetaLogger;
#[test]
fn test_eth_empty() {
let payload = b"";
let test_mac_addr =
MacAddr::from_str("55:44:33:22:11:00").expect("error parsing MAC address");
let mac = MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address");
let mut client_info = ClientInfo::new();
let mut ips = HashSet::new();
ips.insert(IpAddr::V4(Ipv4Addr::new(0xaa, 0x99, 0x88, 0x77)));
ips.insert(IpAddr::V6(Ipv6Addr::new(
0x7777, 0x7777, 0x7777, 0x7777, 0x7777, 0x7777, 0xaabb, 0xccdd,
)));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: mac,
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
for proto in [EtherTypes::Ipv4, EtherTypes::Ipv6, EtherTypes::Arp] {
let mut eth_req = MutableEthernetPacket::owned(vec![
0;
EthernetPacket::minimum_packet_size(
) + payload.len()
])
.expect("error constructing ethernet packet");
eth_req.set_source(test_mac_addr);
eth_req.set_payload(payload);
eth_req.set_ethertype(proto);
eth_req.set_destination(mac);
if let Some(_) = reply(&eth_req.to_immutable(), &masscanned, &mut client_info) {
panic!("expected no Ethernet answer, got one");
}
}
}
#[test] #[test]
fn test_eth_reply() { fn test_eth_reply() {
/* test payload is IP(src="3.2.1.0", dst=".".join(str(b) for b in [0xaa, 0x99, /* test payload is IP(src="3.2.1.0", dst=".".join(str(b) for b in [0xaa, 0x99,
@ -211,7 +264,9 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
let mut eth_req = MutableEthernetPacket::owned(vec![ let mut eth_req = MutableEthernetPacket::owned(vec![
0; 0;

View file

@ -39,31 +39,43 @@ pub fn repl<'a, 'b>(
masscanned: &Masscanned, masscanned: &Masscanned,
mut client_info: &mut ClientInfo, mut client_info: &mut ClientInfo,
) -> Option<MutableIpv4Packet<'b>> { ) -> Option<MutableIpv4Packet<'b>> {
debug!("receiving IPv4 packet: {:?}", ip_req); /* Fill client info with source and dest. IP addresses */
client_info.ip.src = Some(IpAddr::V4(ip_req.get_source()));
client_info.ip.dst = Some(IpAddr::V4(ip_req.get_destination()));
masscanned.log.ipv4_recv(&ip_req, &client_info);
/* If masscanned is configured with IP addresses, then /* If masscanned is configured with IP addresses, then
* check that the dest. IP address of the packet is one of * check that the dest. IP address of the packet is one of
* those handled by masscanned - otherwise, drop the packet. * those handled by masscanned - otherwise, drop the packet.
**/ **/
if let Some(ip_addr_list) = masscanned.ip_addresses { if let Some(ip_addr_list) = masscanned.self_ip_list {
if !ip_addr_list.contains(&IpAddr::V4(ip_req.get_destination())) { if !ip_addr_list.contains(&IpAddr::V4(ip_req.get_destination())) {
info!( masscanned.log.ipv4_drop(&ip_req, &client_info);
"Ignoring IP packet from {} for {}", return None;
ip_req.get_source(), }
ip_req.get_destination() }
); /* If masscanned is configured with a remote ip deny list, then
* check if the src. IP address of the packet is one of
* those ignored by masscanned - if so, drop the packet.
**/
if let Some(remote_ip_deny_list) = masscanned.remote_ip_deny_list {
if remote_ip_deny_list.contains(&IpAddr::V4(ip_req.get_source())) {
masscanned.log.ipv4_drop(&ip_req, &client_info);
return None; return None;
} }
} }
/* Fill client info with source and dest. IP addresses */
client_info.ip.src = Some(IpAddr::V4(ip_req.get_source()));
client_info.ip.dst = Some(IpAddr::V4(ip_req.get_destination()));
/* Fill client info with transport layer procotol */ /* Fill client info with transport layer procotol */
client_info.transport = Some(ip_req.get_next_level_protocol()); client_info.transport = Some(ip_req.get_next_level_protocol());
let mut ip_repl; let mut ip_repl;
match ip_req.get_next_level_protocol() { match ip_req.get_next_level_protocol() {
/* Answer to an ICMP packet */ /* Answer to an ICMP packet */
IpNextHeaderProtocols::Icmp => { IpNextHeaderProtocols::Icmp => {
let icmp_req = IcmpPacket::new(ip_req.payload()).expect("error parsing ICMP packet"); let icmp_req = if let Some(p) = IcmpPacket::new(ip_req.payload()) {
p
} else {
warn!("error parsing ICMP packet");
masscanned.log.ipv4_drop(&ip_req, &client_info);
return None;
};
if let Some(mut icmp_repl) = layer_4::icmpv4::repl(&icmp_req, masscanned, &client_info) if let Some(mut icmp_repl) = layer_4::icmpv4::repl(&icmp_req, masscanned, &client_info)
{ {
icmp_repl.set_checksum(ipv4_checksum_icmp(&icmp_repl.to_immutable())); icmp_repl.set_checksum(ipv4_checksum_icmp(&icmp_repl.to_immutable()));
@ -77,12 +89,19 @@ pub fn repl<'a, 'b>(
ip_repl.set_payload(icmp_repl.packet()); ip_repl.set_payload(icmp_repl.packet());
ip_repl.set_next_level_protocol(IpNextHeaderProtocols::Icmp); ip_repl.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
} else { } else {
masscanned.log.ipv4_drop(&ip_req, &client_info);
return None; return None;
} }
} }
/* Answer to a TCP packet */ /* Answer to a TCP packet */
IpNextHeaderProtocols::Tcp => { IpNextHeaderProtocols::Tcp => {
let tcp_req = TcpPacket::new(ip_req.payload()).expect("error parsing TCP packet"); let tcp_req = if let Some(p) = TcpPacket::new(ip_req.payload()) {
p
} else {
warn!("error parsing TCP packet");
masscanned.log.ipv4_drop(&ip_req, &client_info);
return None;
};
if let Some(mut tcp_repl) = layer_4::tcp::repl(&tcp_req, masscanned, &mut client_info) { if let Some(mut tcp_repl) = layer_4::tcp::repl(&tcp_req, masscanned, &mut client_info) {
tcp_repl.set_checksum(ipv4_checksum_tcp( tcp_repl.set_checksum(ipv4_checksum_tcp(
&tcp_repl.to_immutable(), &tcp_repl.to_immutable(),
@ -99,12 +118,19 @@ pub fn repl<'a, 'b>(
ip_repl.set_payload(tcp_repl.packet()); ip_repl.set_payload(tcp_repl.packet());
ip_repl.set_next_level_protocol(IpNextHeaderProtocols::Tcp); ip_repl.set_next_level_protocol(IpNextHeaderProtocols::Tcp);
} else { } else {
masscanned.log.ipv4_drop(&ip_req, &client_info);
return None; return None;
} }
} }
/* Answer to an UDP packet */ /* Answer to an UDP packet */
IpNextHeaderProtocols::Udp => { IpNextHeaderProtocols::Udp => {
let udp_req = UdpPacket::new(ip_req.payload()).expect("error parsing UDP packet"); let udp_req = if let Some(p) = UdpPacket::new(ip_req.payload()) {
p
} else {
warn!("error parsing UDP packet");
masscanned.log.ipv4_drop(&ip_req, &client_info);
return None;
};
if let Some(mut udp_repl) = layer_4::udp::repl(&udp_req, masscanned, &mut client_info) { if let Some(mut udp_repl) = layer_4::udp::repl(&udp_req, masscanned, &mut client_info) {
udp_repl.set_checksum(ipv4_checksum_udp( udp_repl.set_checksum(ipv4_checksum_udp(
&udp_repl.to_immutable(), &udp_repl.to_immutable(),
@ -123,15 +149,13 @@ pub fn repl<'a, 'b>(
ip_repl.set_payload(udp_repl.packet()); ip_repl.set_payload(udp_repl.packet());
ip_repl.set_next_level_protocol(IpNextHeaderProtocols::Udp); ip_repl.set_next_level_protocol(IpNextHeaderProtocols::Udp);
} else { } else {
masscanned.log.ipv4_drop(&ip_req, &client_info);
return None; return None;
} }
} }
/* Next layer protocol not handled (yet) - dropping packet */ /* Next layer protocol not handled (yet) - dropping packet */
_ => { _ => {
info!( masscanned.log.ipv4_drop(&ip_req, &client_info);
"IPv4 upper layer not handled: {:?}",
ip_req.get_next_level_protocol()
);
return None; return None;
} }
}; };
@ -150,7 +174,7 @@ pub fn repl<'a, 'b>(
/* FIXME when dest. was a multicast IP address */ /* FIXME when dest. was a multicast IP address */
ip_repl.set_source(ip_req.get_destination()); ip_repl.set_source(ip_req.get_destination());
ip_repl.set_destination(ip_req.get_source()); ip_repl.set_destination(ip_req.get_source());
debug!("sending IPv4 packet: {:?}", ip_repl); masscanned.log.ipv4_send(&ip_repl, &client_info);
Some(ip_repl) Some(ip_repl)
} }
@ -163,10 +187,11 @@ mod tests {
use pnet::util::MacAddr; use pnet::util::MacAddr;
use crate::logger::MetaLogger;
#[test] #[test]
fn test_ipv4_reply() { fn test_ipv4_empty() {
/* test payload is scapy> ICMP() */ let payload = b"";
let payload = b"\x08\x00\xf7\xff\x00\x00\x00\x00";
let mut client_info = ClientInfo::new(); let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0); let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3); let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
@ -177,7 +202,60 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
for proto in [
IpNextHeaderProtocols::Tcp,
IpNextHeaderProtocols::Udp,
IpNextHeaderProtocols::Icmp,
] {
let mut ip_req = MutableIpv4Packet::owned(vec![
0;
Ipv4Packet::minimum_packet_size()
+ payload.len()
])
.expect("error constructing IPv4 packet");
ip_req.set_version(4);
ip_req.set_ttl(64);
ip_req.set_identification(0);
ip_req.set_flags(Ipv4Flags::DontFragment);
ip_req.set_source(test_ip_addr);
ip_req.set_header_length(5);
/* Set test payload for layer 4 */
ip_req.set_total_length(ip_req.packet().len() as u16);
ip_req.set_payload(payload);
/* Set next protocol */
ip_req.set_next_level_protocol(proto);
/* Send to a legitimate IP address */
ip_req.set_destination(masscanned_ip_addr);
if let Some(_) = repl(&ip_req.to_immutable(), &masscanned, &mut client_info) {
panic!("expected no IP answer, got one");
}
}
}
#[test]
fn test_ipv4_reply() {
/* test payload is scapy> ICMP() */
let payload = b"\x08\x00\xf7\xff\x00\x00\x00\x00";
let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
let blacklist_ip_addr = Ipv4Addr::new(3, 3, 3, 3);
let mut ips = HashSet::new();
ips.insert(IpAddr::V4(masscanned_ip_addr));
let mut blacklist_ips = HashSet::new();
blacklist_ips.insert(IpAddr::V4(blacklist_ip_addr));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: Some(&blacklist_ips),
log: MetaLogger::new(),
}; };
let mut ip_req = let mut ip_req =
MutableIpv4Packet::owned(vec![0; Ipv4Packet::minimum_packet_size() + payload.len()]) MutableIpv4Packet::owned(vec![0; Ipv4Packet::minimum_packet_size() + payload.len()])
@ -206,5 +284,9 @@ mod tests {
/* Send to a non-legitimate IP address */ /* Send to a non-legitimate IP address */
ip_req.set_destination(Ipv4Addr::new(2, 2, 2, 2)); ip_req.set_destination(Ipv4Addr::new(2, 2, 2, 2));
assert!(repl(&ip_req.to_immutable(), &masscanned, &mut client_info) == None); assert!(repl(&ip_req.to_immutable(), &masscanned, &mut client_info) == None);
/* Send from a non-legitimate IP address */
ip_req.set_source(blacklist_ip_addr);
ip_req.set_destination(masscanned_ip_addr);
assert!(repl(&ip_req.to_immutable(), &masscanned, &mut client_info) == None);
} }
} }

View file

@ -35,18 +35,31 @@ pub fn repl<'a, 'b>(
masscanned: &Masscanned, masscanned: &Masscanned,
mut client_info: &mut ClientInfo, mut client_info: &mut ClientInfo,
) -> Option<MutableIpv6Packet<'b>> { ) -> Option<MutableIpv6Packet<'b>> {
debug!("receiving IPv6 packet: {:?}", ip_req); /* Fill client info with source and dest. IP address */
client_info.ip.src = Some(IpAddr::V6(ip_req.get_source()));
client_info.ip.dst = Some(IpAddr::V6(ip_req.get_destination()));
masscanned.log.ipv6_recv(ip_req, client_info);
let src = ip_req.get_source(); let src = ip_req.get_source();
let mut dst = ip_req.get_destination(); let mut dst = ip_req.get_destination();
/* If masscanned is configured with IP addresses, check that /* If masscanned is configured with IP addresses, then
* the dest. IP address corresponds to one of those * check that the dest. IP address of the packet is one of
* Otherwise, drop the packet. * those handled by masscanned - otherwise, drop the packet.
**/ **/
if let Some(ip_addr_list) = masscanned.ip_addresses { if let Some(ip_addr_list) = masscanned.self_ip_list {
if !ip_addr_list.contains(&IpAddr::V6(dst)) if !ip_addr_list.contains(&IpAddr::V6(dst))
&& ip_req.get_next_header() != IpNextHeaderProtocols::Icmpv6 && ip_req.get_next_header() != IpNextHeaderProtocols::Icmpv6
{ {
info!("Ignoring IP packet from {} for {}", &src, &dst); masscanned.log.ipv6_drop(ip_req, client_info);
return None;
}
}
/* If masscanned is configured with a remote ip deny list, then
* check if the src. IP address of the packet is one of
* those ignored by masscanned - if so, drop the packet.
**/
if let Some(remote_ip_deny_list) = masscanned.remote_ip_deny_list {
if remote_ip_deny_list.contains(&IpAddr::V6(src)) {
masscanned.log.ipv6_drop(ip_req, client_info);
return None; return None;
} }
} }
@ -59,8 +72,13 @@ pub fn repl<'a, 'b>(
match ip_req.get_next_header() { match ip_req.get_next_header() {
/* Answer to ICMPv6 */ /* Answer to ICMPv6 */
IpNextHeaderProtocols::Icmpv6 => { IpNextHeaderProtocols::Icmpv6 => {
let icmp_req = let icmp_req = if let Some(p) = Icmpv6Packet::new(ip_req.payload()) {
Icmpv6Packet::new(ip_req.payload()).expect("error parsing ICMPv6 packet"); p
} else {
warn!("error parsing ICMPv6 packet");
masscanned.log.ipv6_drop(&ip_req, &client_info);
return None;
};
if let (Some(mut icmp_repl), dst_addr) = if let (Some(mut icmp_repl), dst_addr) =
layer_4::icmpv6::repl(&icmp_req, masscanned, &client_info) layer_4::icmpv6::repl(&icmp_req, masscanned, &client_info)
{ {
@ -84,12 +102,19 @@ pub fn repl<'a, 'b>(
ip_repl.set_hop_limit(255); ip_repl.set_hop_limit(255);
}; };
} else { } else {
masscanned.log.ipv6_drop(ip_req, client_info);
return None; return None;
} }
} }
/* Answer to TCP */ /* Answer to TCP */
IpNextHeaderProtocols::Tcp => { IpNextHeaderProtocols::Tcp => {
let tcp_req = TcpPacket::new(ip_req.payload()).expect("error parsing TCP packet"); let tcp_req = if let Some(p) = TcpPacket::new(ip_req.payload()) {
p
} else {
warn!("error parsing TCP packet");
masscanned.log.ipv6_drop(&ip_req, &client_info);
return None;
};
if let Some(mut tcp_repl) = layer_4::tcp::repl(&tcp_req, masscanned, &mut client_info) { if let Some(mut tcp_repl) = layer_4::tcp::repl(&tcp_req, masscanned, &mut client_info) {
/* Compute and set TCP checksum */ /* Compute and set TCP checksum */
tcp_repl.set_checksum(ipv6_checksum_tcp( tcp_repl.set_checksum(ipv6_checksum_tcp(
@ -108,12 +133,19 @@ pub fn repl<'a, 'b>(
ip_repl.set_payload_length(tcp_len as u16); ip_repl.set_payload_length(tcp_len as u16);
ip_repl.set_payload(&tcp_repl.packet()); ip_repl.set_payload(&tcp_repl.packet());
} else { } else {
masscanned.log.ipv6_drop(ip_req, client_info);
return None; return None;
} }
} }
/* Answer to UDP */ /* Answer to UDP */
IpNextHeaderProtocols::Udp => { IpNextHeaderProtocols::Udp => {
let udp_req = UdpPacket::new(ip_req.payload()).expect("error parsing UDP packet"); let udp_req = if let Some(p) = UdpPacket::new(ip_req.payload()) {
p
} else {
warn!("error parsing UDP packet");
masscanned.log.ipv6_drop(&ip_req, &client_info);
return None;
};
if let Some(mut udp_repl) = layer_4::udp::repl(&udp_req, masscanned, &mut client_info) { if let Some(mut udp_repl) = layer_4::udp::repl(&udp_req, masscanned, &mut client_info) {
/* Compute and set UDP checksum */ /* Compute and set UDP checksum */
udp_repl.set_checksum(ipv6_checksum_udp( udp_repl.set_checksum(ipv6_checksum_udp(
@ -132,15 +164,13 @@ pub fn repl<'a, 'b>(
ip_repl.set_payload_length(udp_len as u16); ip_repl.set_payload_length(udp_len as u16);
ip_repl.set_payload(&udp_repl.packet()); ip_repl.set_payload(&udp_repl.packet());
} else { } else {
masscanned.log.ipv6_drop(ip_req, client_info);
return None; return None;
} }
} }
/* Other protocols are not handled (yet) - dropping */ /* Other protocols are not handled (yet) - dropping */
_ => { _ => {
info!( masscanned.log.ipv6_drop(ip_req, client_info);
"IPv6 upper layer not handled: {:?}",
ip_req.get_next_header()
);
return None; return None;
} }
}; };
@ -153,7 +183,7 @@ pub fn repl<'a, 'b>(
/* Set packet source and dest. */ /* Set packet source and dest. */
ip_repl.set_source(dst); ip_repl.set_source(dst);
ip_repl.set_destination(src); ip_repl.set_destination(src);
debug!("sending IPv6 packet: {:?}", ip_repl); masscanned.log.ipv6_send(&ip_repl, client_info);
Some(ip_repl) Some(ip_repl)
} }
@ -166,12 +196,11 @@ mod tests {
use pnet::util::MacAddr; use pnet::util::MacAddr;
use crate::logger::MetaLogger;
#[test] #[test]
fn test_ipv6_reply() { fn test_ipv6_empty() {
/* test payload is scapy> IPv6(src="7777:6666:5555:4444:3333:2222:1111:0000", let payload = b"";
* dst="0000:1111:2222:3333:4444:5555:6666:7777")/TCP(sport=12345, dport=54321,
* flags="S"))[TCP] */
let payload = b"09\xd41\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcf\xbc\x00\x00";
let mut client_info = ClientInfo::new(); let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv6Addr::new( let test_ip_addr = Ipv6Addr::new(
0x7777, 0x6666, 0x5555, 0x4444, 0x3333, 0x2222, 0x1111, 0x0000, 0x7777, 0x6666, 0x5555, 0x4444, 0x3333, 0x2222, 0x1111, 0x0000,
@ -186,7 +215,64 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
for proto in [
IpNextHeaderProtocols::Tcp,
IpNextHeaderProtocols::Udp,
IpNextHeaderProtocols::Icmp,
] {
let mut ip_req = MutableIpv6Packet::owned(vec![
0;
Ipv6Packet::minimum_packet_size()
+ payload.len()
])
.expect("error constructing IPv6 packet");
ip_req.set_version(6);
ip_req.set_source(test_ip_addr);
/* Set test payload for layer 4 */
ip_req.set_payload_length(payload.len() as u16);
ip_req.set_payload(payload);
/* Set next protocol */
ip_req.set_next_header(proto);
/* Send to a legitimate IP address */
ip_req.set_destination(masscanned_ip_addr);
if let Some(_) = repl(&ip_req.to_immutable(), &masscanned, &mut client_info) {
panic!("expected no IP answer, got one");
}
}
}
#[test]
fn test_ipv6_reply() {
/* test payload is scapy> IPv6(src="7777:6666:5555:4444:3333:2222:1111:0000",
* dst="0000:1111:2222:3333:4444:5555:6666:7777")/TCP(sport=12345, dport=54321,
* flags="S"))[TCP] */
let payload = b"09\xd41\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcf\xbc\x00\x00";
let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv6Addr::new(
0x7777, 0x6666, 0x5555, 0x4444, 0x3333, 0x2222, 0x1111, 0x0000,
);
let masscanned_ip_addr = Ipv6Addr::new(
0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777,
);
let blacklist_ip_addr = Ipv6Addr::new(
0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111,
);
let mut ips = HashSet::new();
ips.insert(IpAddr::V6(masscanned_ip_addr));
let mut blacklist_ips = HashSet::new();
blacklist_ips.insert(IpAddr::V6(blacklist_ip_addr));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: Some(&blacklist_ips),
log: MetaLogger::new(),
}; };
let mut ip_req = let mut ip_req =
MutableIpv6Packet::owned(vec![0; Ipv6Packet::minimum_packet_size() + payload.len()]) MutableIpv6Packet::owned(vec![0; Ipv6Packet::minimum_packet_size() + payload.len()])
@ -213,5 +299,9 @@ mod tests {
0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7778, 0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7778,
)); ));
assert!(repl(&ip_req.to_immutable(), &masscanned, &mut client_info) == None); assert!(repl(&ip_req.to_immutable(), &masscanned, &mut client_info) == None);
/* Send from a non-legitimate IP address */
ip_req.set_source(blacklist_ip_addr);
ip_req.set_destination(masscanned_ip_addr);
assert!(repl(&ip_req.to_immutable(), &masscanned, &mut client_info) == None);
} }
} }

View file

@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>. // along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use log::*;
use pnet::packet::{ use pnet::packet::{
icmp::{IcmpCode, IcmpPacket, IcmpTypes, MutableIcmpPacket}, icmp::{IcmpCode, IcmpPacket, IcmpTypes, MutableIcmpPacket},
Packet, Packet,
@ -26,16 +24,16 @@ use crate::Masscanned;
pub fn repl<'a, 'b>( pub fn repl<'a, 'b>(
icmp_req: &'a IcmpPacket, icmp_req: &'a IcmpPacket,
_masscanned: &Masscanned, masscanned: &Masscanned,
mut _client_info: &ClientInfo, client_info: &ClientInfo,
) -> Option<MutableIcmpPacket<'b>> { ) -> Option<MutableIcmpPacket<'b>> {
debug!("receiving ICMPv4 packet: {:?}", icmp_req); masscanned.log.icmpv4_recv(icmp_req, client_info);
let mut icmp_repl; let mut icmp_repl;
match icmp_req.get_icmp_type() { match icmp_req.get_icmp_type() {
IcmpTypes::EchoRequest => { IcmpTypes::EchoRequest => {
/* Check code of ICMP packet */ /* Check code of ICMP packet */
if icmp_req.get_icmp_code() != IcmpCode(0) { if icmp_req.get_icmp_code() != IcmpCode(0) {
info!("ICMP code not handled: {:?}", icmp_req.get_icmp_code()); masscanned.log.icmpv4_drop(icmp_req, client_info);
return None; return None;
} }
/* Compute answer length */ /* Compute answer length */
@ -53,13 +51,13 @@ pub fn repl<'a, 'b>(
* reply message." * reply message."
**/ **/
icmp_repl.set_payload(icmp_req.payload()); icmp_repl.set_payload(icmp_req.payload());
warn!("ICMP-Echo-Reply to ICMP-Echo-Request");
} }
_ => { _ => {
masscanned.log.icmpv4_drop(icmp_req, client_info);
return None; return None;
} }
}; };
debug!("sending ICMPv4 packet: {:?}", icmp_repl); masscanned.log.icmpv4_send(&icmp_repl, client_info);
Some(icmp_repl) Some(icmp_repl)
} }
@ -70,6 +68,8 @@ mod tests {
use pnet::util::MacAddr; use pnet::util::MacAddr;
use crate::logger::MetaLogger;
#[test] #[test]
fn test_icmpv4_reply() { fn test_icmpv4_reply() {
/* test payload is scapy> ICMP() */ /* test payload is scapy> ICMP() */
@ -80,7 +80,9 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: None, self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
let mut icmp_req = let mut icmp_req =
MutableIcmpPacket::owned(vec![0; IcmpPacket::minimum_packet_size() + payload.len()]) MutableIcmpPacket::owned(vec![0; IcmpPacket::minimum_packet_size() + payload.len()])

View file

@ -40,7 +40,7 @@ pub fn nd_ns_repl<'a, 'b>(
* check that the dest. IP address of the packet is one of * check that the dest. IP address of the packet is one of
* those handled by masscanned - otherwise, drop the packet. * those handled by masscanned - otherwise, drop the packet.
**/ **/
if let Some(addresses) = masscanned.ip_addresses { if let Some(addresses) = masscanned.self_ip_list {
if !addresses.contains(&IpAddr::V6(nd_ns_req.get_target_addr())) { if !addresses.contains(&IpAddr::V6(nd_ns_req.get_target_addr())) {
return None; return None;
} }
@ -103,7 +103,7 @@ pub fn repl<'a, 'b>(
masscanned: &Masscanned, masscanned: &Masscanned,
client_info: &ClientInfo, client_info: &ClientInfo,
) -> (Option<MutableIcmpv6Packet<'b>>, Option<Ipv6Addr>) { ) -> (Option<MutableIcmpv6Packet<'b>>, Option<Ipv6Addr>) {
debug!("receiving ICMPv6 packet: {:?}", icmp_req); masscanned.log.icmpv6_recv(icmp_req, client_info);
let mut dst_ip = None; let mut dst_ip = None;
if icmp_req.get_icmpv6_code() != Icmpv6Codes::NoCode { if icmp_req.get_icmpv6_code() != Icmpv6Codes::NoCode {
return (None, None); return (None, None);
@ -120,6 +120,7 @@ pub fn repl<'a, 'b>(
icmp_repl = MutableIcmpv6Packet::owned(nd_na_repl.packet().to_vec()) icmp_repl = MutableIcmpv6Packet::owned(nd_na_repl.packet().to_vec())
.expect("error constructing an ICMPv6 packet"); .expect("error constructing an ICMPv6 packet");
} else { } else {
masscanned.log.icmpv6_drop(icmp_req, client_info);
return (None, None); return (None, None);
} }
} }
@ -136,17 +137,13 @@ pub fn repl<'a, 'b>(
icmp_repl = MutableIcmpv6Packet::owned(vec![0; Icmpv6Packet::packet_size(&echo_repl)]) icmp_repl = MutableIcmpv6Packet::owned(vec![0; Icmpv6Packet::packet_size(&echo_repl)])
.expect("error constructing an ICMPv6 packet"); .expect("error constructing an ICMPv6 packet");
icmp_repl.populate(&echo_repl); icmp_repl.populate(&echo_repl);
warn!("ICMPv6-Echo-Reply to ICMPv6-Echo-Request");
} }
_ => { _ => {
info!( masscanned.log.icmpv6_drop(icmp_req, client_info);
"ICMPv6 packet not handled: {:?}",
icmp_req.get_icmpv6_type()
);
return (None, None); return (None, None);
} }
}; };
debug!("sending ICMPv6 packet: {:?}", icmp_repl); masscanned.log.icmpv6_send(&icmp_repl, client_info);
(Some(icmp_repl), dst_ip) (Some(icmp_repl), dst_ip)
} }
@ -160,6 +157,8 @@ mod tests {
use pnet::packet::icmpv6::ndp::{MutableNeighborSolicitPacket, NeighborSolicit}; use pnet::packet::icmpv6::ndp::{MutableNeighborSolicitPacket, NeighborSolicit};
use pnet::util::MacAddr; use pnet::util::MacAddr;
use crate::logger::MetaLogger;
#[test] #[test]
fn test_nd_na_reply() { fn test_nd_na_reply() {
let client_info = ClientInfo::new(); let client_info = ClientInfo::new();
@ -173,7 +172,9 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
/* Legitimate solicitation */ /* Legitimate solicitation */
let ndp_ns = NeighborSolicit { let ndp_ns = NeighborSolicit {
@ -245,7 +246,9 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
let mut icmpv6_echo_req = MutableIcmpv6Packet::owned(vec![ let mut icmpv6_echo_req = MutableIcmpv6Packet::owned(vec![
0; 0;

View file

@ -31,10 +31,10 @@ pub fn repl<'a, 'b>(
masscanned: &Masscanned, masscanned: &Masscanned,
mut client_info: &mut ClientInfo, mut client_info: &mut ClientInfo,
) -> Option<MutableTcpPacket<'b>> { ) -> Option<MutableTcpPacket<'b>> {
debug!("receiving TCP packet: {:?}", tcp_req);
/* Fill client info with source and dest. TCP port */ /* Fill client info with source and dest. TCP port */
client_info.port.src = Some(tcp_req.get_source()); client_info.port.src = Some(tcp_req.get_source());
client_info.port.dst = Some(tcp_req.get_destination()); client_info.port.dst = Some(tcp_req.get_destination());
masscanned.log.tcp_recv(tcp_req, client_info);
/* Construct response TCP packet */ /* Construct response TCP packet */
let mut tcp_repl; let mut tcp_repl;
match tcp_req.get_flags() { match tcp_req.get_flags() {
@ -49,16 +49,24 @@ pub fn repl<'a, 'b>(
}; };
/* Compute syncookie */ /* Compute syncookie */
if let Ok(cookie) = synackcookie::generate(&client_info, &masscanned.synack_key) { if let Ok(cookie) = synackcookie::generate(&client_info, &masscanned.synack_key) {
client_info.cookie = Some(cookie);
if !proto::is_tcb_set(cookie) {
/* First Ack: check syncookie, create tcb */
if cookie != ackno { if cookie != ackno {
info!("PSH-ACK ignored: synackcookie not valid"); masscanned.log.tcp_drop(tcp_req, client_info);
return None; return None;
} }
client_info.cookie = Some(cookie); proto::add_tcb(cookie);
}
} }
warn!("ACK to PSH-ACK on port {}", tcp_req.get_destination()); warn!("ACK to PSH-ACK on port {}", tcp_req.get_destination());
let payload = tcp_req.payload(); let payload = tcp_req.payload();
/* Any answer to upper-layer protocol? */ /* Any answer to upper-layer protocol? */
if let Some(repl) = proto::repl(&payload, masscanned, &mut client_info) { let mut payload_repl = None;
proto::get_tcb(client_info.cookie.unwrap(), |tcb| {
payload_repl = proto::repl(&payload, masscanned, &mut client_info, tcb);
});
if let Some(repl) = payload_repl {
tcp_repl = MutableTcpPacket::owned( tcp_repl = MutableTcpPacket::owned(
[vec![0; MutableTcpPacket::minimum_packet_size()], repl].concat(), [vec![0; MutableTcpPacket::minimum_packet_size()], repl].concat(),
) )
@ -70,33 +78,52 @@ pub fn repl<'a, 'b>(
.expect("error constructing a TCP packet"); .expect("error constructing a TCP packet");
tcp_repl.set_flags(TcpFlags::ACK); tcp_repl.set_flags(TcpFlags::ACK);
} }
tcp_repl.set_acknowledgement(tcp_req.get_sequence() + (tcp_req.payload().len() as u32)); tcp_repl.set_acknowledgement(
tcp_req
.get_sequence()
.wrapping_add(tcp_req.payload().len() as u32),
);
tcp_repl.set_sequence(tcp_req.get_acknowledgement()); tcp_repl.set_sequence(tcp_req.get_acknowledgement());
} }
/* Answer to ACK: nothing */ /* Answer to ACK: nothing */
flags if flags == TcpFlags::ACK => { flags if flags == TcpFlags::ACK => {
/* answer here when server needs to speak first after handshake */ /* answer here when server needs to speak first after handshake */
masscanned.log.tcp_drop(tcp_req, client_info);
return None; return None;
} }
/* Answer to RST and FIN: nothing */ /* Answer to RST: nothing */
flags if (flags == TcpFlags::RST || flags == (TcpFlags::FIN | TcpFlags::ACK)) => { flags if flags == TcpFlags::RST => {
masscanned.log.tcp_drop(tcp_req, client_info);
return None; return None;
} }
/* Answer to SYN */ /* Answer to FIN,ACK with FIN,ACK */
flags if flags & TcpFlags::SYN == TcpFlags::SYN => { flags if flags == (TcpFlags::FIN | TcpFlags::ACK) => {
tcp_repl = MutableTcpPacket::owned(vec![0; MutableTcpPacket::minimum_packet_size()])
.expect("error constructing a TCP packet");
tcp_repl.set_flags(TcpFlags::FIN | TcpFlags::ACK);
tcp_repl.set_acknowledgement(tcp_req.get_sequence().wrapping_add(1));
tcp_repl.set_sequence(tcp_req.get_acknowledgement());
}
/* Answer to SYN + P|U|C|E + !(C && E) to imitate Linux network stack */
flags
if (flags & TcpFlags::SYN) == TcpFlags::SYN &&
/* no other flag than S,P,U,C,E */
(flags & !(TcpFlags::SYN | TcpFlags::PSH | TcpFlags::URG | TcpFlags::CWR | TcpFlags::ECE)) == 0 &&
/* not C && E */
((flags & TcpFlags::CWR == 0) || (flags & TcpFlags::ECE == 0)) =>
{
tcp_repl = MutableTcpPacket::owned(vec![0; MutableTcpPacket::minimum_packet_size()]) tcp_repl = MutableTcpPacket::owned(vec![0; MutableTcpPacket::minimum_packet_size()])
.expect("error constructing a TCP packet"); .expect("error constructing a TCP packet");
tcp_repl.set_flags(TcpFlags::ACK); tcp_repl.set_flags(TcpFlags::ACK);
tcp_repl.set_flags(TcpFlags::SYN | TcpFlags::ACK); tcp_repl.set_flags(TcpFlags::SYN | TcpFlags::ACK);
tcp_repl.set_acknowledgement(tcp_req.get_sequence() + 1); tcp_repl.set_acknowledgement(tcp_req.get_sequence().wrapping_add(1));
/* generate a SYNACK-cookie (same as masscan) */ /* generate a SYNACK-cookie (same as masscan) */
tcp_repl.set_sequence( tcp_repl.set_sequence(
synackcookie::generate(&client_info, &masscanned.synack_key).unwrap(), synackcookie::generate(&client_info, &masscanned.synack_key).unwrap(),
); );
warn!("SYN-ACK to ACK on port {}", tcp_req.get_destination());
} }
_ => { _ => {
info!("TCP flag not handled: {}", tcp_req.get_flags()); masscanned.log.tcp_drop(tcp_req, client_info);
return None; return None;
} }
} }
@ -107,7 +134,7 @@ pub fn repl<'a, 'b>(
/* Set TCP headers */ /* Set TCP headers */
tcp_repl.set_data_offset(5); tcp_repl.set_data_offset(5);
tcp_repl.set_window(65535); tcp_repl.set_window(65535);
debug!("sending TCP packet: {:?}", tcp_repl); masscanned.log.tcp_send(&tcp_repl, client_info);
Some(tcp_repl) Some(tcp_repl)
} }
@ -118,13 +145,212 @@ mod tests {
use pnet::util::MacAddr; use pnet::util::MacAddr;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::logger::MetaLogger;
#[test]
fn test_tcp_syn() {
let masscanned = Masscanned {
mac: MacAddr(0, 0, 0, 0, 0, 0),
self_ip_list: None,
remote_ip_deny_list: None,
synack_key: [0x06a0a1d63f305e9b, 0xd4d4bcbb7304875f],
iface: None,
log: MetaLogger::new(),
};
/* reference */
let ip_src = IpAddr::V4(Ipv4Addr::new(27, 198, 143, 1));
let ip_dst = IpAddr::V4(Ipv4Addr::new(90, 64, 122, 203));
let tcp_sport = 65500;
let tcp_dport = 80;
let seq = 1234567;
let ack = 0;
let mut client_info = ClientInfo {
mac: ClientInfoSrcDst {
src: None,
dst: None,
},
ip: ClientInfoSrcDst {
src: Some(ip_src),
dst: Some(ip_dst),
},
transport: None,
port: ClientInfoSrcDst {
src: Some(tcp_sport),
dst: Some(tcp_dport),
},
cookie: None,
};
/* flags OK - list is exhaustive */
/* aim at imitating a Linux network stack */
let flags_ok = [
TcpFlags::SYN,
TcpFlags::SYN | TcpFlags::PSH,
TcpFlags::SYN | TcpFlags::URG,
TcpFlags::SYN | TcpFlags::CWR,
TcpFlags::SYN | TcpFlags::ECE,
TcpFlags::SYN | TcpFlags::PSH | TcpFlags::URG,
TcpFlags::SYN | TcpFlags::PSH | TcpFlags::CWR,
TcpFlags::SYN | TcpFlags::PSH | TcpFlags::ECE,
TcpFlags::SYN | TcpFlags::PSH | TcpFlags::ECE,
TcpFlags::SYN | TcpFlags::URG | TcpFlags::CWR,
TcpFlags::SYN | TcpFlags::URG | TcpFlags::ECE,
TcpFlags::SYN | TcpFlags::PSH | TcpFlags::URG | TcpFlags::CWR,
TcpFlags::SYN | TcpFlags::PSH | TcpFlags::URG | TcpFlags::ECE,
];
for flags in flags_ok {
let mut tcp_req =
MutableTcpPacket::owned(vec![0; MutableTcpPacket::minimum_packet_size()]).unwrap();
tcp_req.set_source(tcp_sport);
tcp_req.set_destination(tcp_dport);
tcp_req.set_sequence(seq);
tcp_req.set_acknowledgement(ack);
tcp_req.set_flags(flags);
let some_tcp_repl = repl(&tcp_req.to_immutable(), &masscanned, &mut client_info);
if some_tcp_repl == None {
panic!("expected a reply, got none for flags: {:?}", flags);
}
let tcp_repl = some_tcp_repl.unwrap();
/* check reply flags */
assert!(tcp_repl.get_flags() == (TcpFlags::SYN | TcpFlags::ACK));
/* check reply seq and ack */
assert!(tcp_repl.get_acknowledgement() == seq.wrapping_add(1));
}
/* flags KO - list is *not* exhaustive */
let flags_ko = [
TcpFlags::SYN | TcpFlags::ACK,
TcpFlags::SYN | TcpFlags::FIN,
TcpFlags::SYN | TcpFlags::CWR | TcpFlags::ECE,
TcpFlags::SYN | TcpFlags::PSH | TcpFlags::URG | TcpFlags::CWR | TcpFlags::ECE,
TcpFlags::PSH,
];
for flags in flags_ko {
let mut tcp_req =
MutableTcpPacket::owned(vec![0; MutableTcpPacket::minimum_packet_size()]).unwrap();
tcp_req.set_source(tcp_sport);
tcp_req.set_destination(tcp_dport);
tcp_req.set_sequence(seq);
tcp_req.set_acknowledgement(ack);
tcp_req.set_flags(flags);
let some_tcp_repl = repl(&tcp_req.to_immutable(), &masscanned, &mut client_info);
if some_tcp_repl != None {
panic!("expected no reply, got one");
}
}
}
#[test]
fn test_tcp_fin_ack() {
let masscanned = Masscanned {
mac: MacAddr(0, 0, 0, 0, 0, 0),
self_ip_list: None,
remote_ip_deny_list: None,
synack_key: [0x06a0a1d63f305e9b, 0xd4d4bcbb7304875f],
iface: None,
log: MetaLogger::new(),
};
/* reference */
let ip_src = IpAddr::V4(Ipv4Addr::new(27, 198, 143, 1));
let ip_dst = IpAddr::V4(Ipv4Addr::new(90, 64, 122, 203));
let tcp_sport = 65500;
let tcp_dport = 80;
let seq = 1234567;
let ack = 7654321;
let mut client_info = ClientInfo {
mac: ClientInfoSrcDst {
src: None,
dst: None,
},
ip: ClientInfoSrcDst {
src: Some(ip_src),
dst: Some(ip_dst),
},
transport: None,
port: ClientInfoSrcDst {
src: Some(tcp_sport),
dst: Some(tcp_dport),
},
cookie: None,
};
let mut tcp_req =
MutableTcpPacket::owned(vec![0; MutableTcpPacket::minimum_packet_size()]).unwrap();
tcp_req.set_source(tcp_sport);
tcp_req.set_destination(tcp_dport);
tcp_req.set_sequence(seq);
tcp_req.set_acknowledgement(ack);
tcp_req.set_flags(TcpFlags::FIN | TcpFlags::ACK);
let some_tcp_repl = repl(&tcp_req.to_immutable(), &masscanned, &mut client_info);
if some_tcp_repl == None {
panic!("expected a reply, got none");
}
let tcp_repl = some_tcp_repl.unwrap();
/* check reply flags */
assert!(tcp_repl.get_flags() == (TcpFlags::FIN | TcpFlags::ACK));
/* check reply seq and ack */
assert!(tcp_repl.get_sequence() == ack);
assert!(tcp_repl.get_acknowledgement() == seq.wrapping_add(1));
}
#[test]
fn test_tcp_fin_ack_wrap() {
let masscanned = Masscanned {
mac: MacAddr(0, 0, 0, 0, 0, 0),
self_ip_list: None,
remote_ip_deny_list: None,
synack_key: [0x06a0a1d63f305e9b, 0xd4d4bcbb7304875f],
iface: None,
log: MetaLogger::new(),
};
/* reference */
let ip_src = IpAddr::V4(Ipv4Addr::new(27, 198, 143, 1));
let ip_dst = IpAddr::V4(Ipv4Addr::new(90, 64, 122, 203));
let tcp_sport = 65500;
let tcp_dport = 80;
let seq = 0xffffffff;
let ack = 0xffffffff;
let mut client_info = ClientInfo {
mac: ClientInfoSrcDst {
src: None,
dst: None,
},
ip: ClientInfoSrcDst {
src: Some(ip_src),
dst: Some(ip_dst),
},
transport: None,
port: ClientInfoSrcDst {
src: Some(tcp_sport),
dst: Some(tcp_dport),
},
cookie: None,
};
let mut tcp_req =
MutableTcpPacket::owned(vec![0; MutableTcpPacket::minimum_packet_size()]).unwrap();
tcp_req.set_source(tcp_sport);
tcp_req.set_destination(tcp_dport);
tcp_req.set_sequence(seq);
tcp_req.set_acknowledgement(ack);
tcp_req.set_flags(TcpFlags::FIN | TcpFlags::ACK);
let some_tcp_repl = repl(&tcp_req.to_immutable(), &masscanned, &mut client_info);
if some_tcp_repl == None {
panic!("expected a reply, got none");
}
let tcp_repl = some_tcp_repl.unwrap();
/* check reply flags */
assert!(tcp_repl.get_flags() == (TcpFlags::FIN | TcpFlags::ACK));
/* check reply seq and ack */
assert!(tcp_repl.get_sequence() == ack);
assert!(tcp_repl.get_acknowledgement() == seq.wrapping_add(1));
}
#[test] #[test]
fn test_synack_cookie_ipv4() { fn test_synack_cookie_ipv4() {
let masscanned = Masscanned { let masscanned = Masscanned {
mac: MacAddr(0, 0, 0, 0, 0, 0), mac: MacAddr(0, 0, 0, 0, 0, 0),
ip_addresses: None, self_ip_list: None,
remote_ip_deny_list: None,
synack_key: [0x06a0a1d63f305e9b, 0xd4d4bcbb7304875f], synack_key: [0x06a0a1d63f305e9b, 0xd4d4bcbb7304875f],
iface: None, iface: None,
log: MetaLogger::new(),
}; };
/* reference */ /* reference */
let ip_src = IpAddr::V4(Ipv4Addr::new(27, 198, 143, 1)); let ip_src = IpAddr::V4(Ipv4Addr::new(27, 198, 143, 1));
@ -171,9 +397,11 @@ mod tests {
fn test_synack_cookie_ipv6() { fn test_synack_cookie_ipv6() {
let masscanned = Masscanned { let masscanned = Masscanned {
mac: MacAddr(0, 0, 0, 0, 0, 0), mac: MacAddr(0, 0, 0, 0, 0, 0),
ip_addresses: None, self_ip_list: None,
remote_ip_deny_list: None,
synack_key: [0x06a0a1d63f305e9b, 0xd4d4bcbb7304875f], synack_key: [0x06a0a1d63f305e9b, 0xd4d4bcbb7304875f],
iface: None, iface: None,
log: MetaLogger::new(),
}; };
/* reference */ /* reference */
let ip_src = IpAddr::V6(Ipv6Addr::new(234, 52, 183, 47, 184, 172, 64, 141)); let ip_src = IpAddr::V6(Ipv6Addr::new(234, 52, 183, 47, 184, 172, 64, 141));

View file

@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>. // along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use log::*;
use pnet::packet::{ use pnet::packet::{
udp::{MutableUdpPacket, UdpPacket}, udp::{MutableUdpPacket, UdpPacket},
Packet, Packet,
@ -30,25 +28,26 @@ pub fn repl<'a, 'b>(
masscanned: &Masscanned, masscanned: &Masscanned,
mut client_info: &mut ClientInfo, mut client_info: &mut ClientInfo,
) -> Option<MutableUdpPacket<'b>> { ) -> Option<MutableUdpPacket<'b>> {
debug!("receiving UDP packet: {:?}", udp_req);
/* Fill client info with source and dest. UDP port */ /* Fill client info with source and dest. UDP port */
client_info.port.src = Some(udp_req.get_source()); client_info.port.src = Some(udp_req.get_source());
client_info.port.dst = Some(udp_req.get_destination()); client_info.port.dst = Some(udp_req.get_destination());
masscanned.log.udp_recv(udp_req, client_info);
let payload = udp_req.payload(); let payload = udp_req.payload();
let mut udp_repl; let mut udp_repl;
if let Some(repl) = proto::repl(&payload, masscanned, &mut client_info) { if let Some(repl) = proto::repl(&payload, masscanned, &mut client_info, None) {
udp_repl = MutableUdpPacket::owned( udp_repl = MutableUdpPacket::owned(
[vec![0; MutableUdpPacket::minimum_packet_size()], repl].concat(), [vec![0; MutableUdpPacket::minimum_packet_size()], repl].concat(),
) )
.expect("error constructing a UDP packet"); .expect("error constructing a UDP packet");
udp_repl.set_length(udp_repl.packet().len() as u16); udp_repl.set_length(udp_repl.packet().len() as u16);
} else { } else {
masscanned.log.udp_drop(udp_req, client_info);
return None; return None;
} }
/* Set source and dest. port for response packet from client info */ /* Set source and dest. port for response packet from client info */
/* Note: client info could have been modified by upper layers (e.g., STUN) */ /* Note: client info could have been modified by upper layers (e.g., STUN) */
udp_repl.set_source(client_info.port.dst.unwrap()); udp_repl.set_source(client_info.port.dst.unwrap());
udp_repl.set_destination(client_info.port.src.unwrap()); udp_repl.set_destination(client_info.port.src.unwrap());
debug!("sending UDP packet: {:?}", udp_repl); masscanned.log.udp_send(&udp_repl, client_info);
Some(udp_repl) Some(udp_repl)
} }

308
src/logger/console.rs Normal file
View file

@ -0,0 +1,308 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use std::time::SystemTime;
use pnet::packet::{
arp::{ArpPacket, MutableArpPacket},
ethernet::{EthernetPacket, MutableEthernetPacket},
icmp::{IcmpPacket, MutableIcmpPacket},
icmpv6::{Icmpv6Packet, MutableIcmpv6Packet},
ipv4::{Ipv4Packet, MutableIpv4Packet},
ipv6::{Ipv6Packet, MutableIpv6Packet},
tcp::{MutableTcpPacket, TcpPacket},
udp::{MutableUdpPacket, UdpPacket},
};
use crate::client::ClientInfo;
use crate::logger::Logger;
pub struct ConsoleLogger {
arp: bool,
eth: bool,
ipv4: bool,
ipv6: bool,
icmpv4: bool,
icmpv6: bool,
tcp: bool,
udp: bool,
}
impl ConsoleLogger {
pub fn new() -> Self {
ConsoleLogger {
arp: true,
eth: true,
ipv4: true,
ipv6: true,
icmpv4: true,
icmpv6: true,
tcp: true,
udp: true,
}
}
fn prolog(&self, proto: &str, verb: &str, crlf: bool) {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
print!(
"{}.{}\t{}\t{}{}",
now.as_secs(),
now.subsec_millis(),
proto,
verb,
if crlf { "\n" } else { "\t" },
);
}
fn client_info(&self, c: &ClientInfo) {
print!(
"{}\t{}\t{}\t{}\t{}\t{}\t{}\t",
if let Some(m) = c.mac.src {
format!("{}", m)
} else {
"".to_string()
},
if let Some(m) = c.mac.dst {
format!("{}", m)
} else {
"".to_string()
},
if let Some(i) = c.ip.src {
format!("{}", i)
} else {
"".to_string()
},
if let Some(i) = c.ip.dst {
format!("{}", i)
} else {
"".to_string()
},
if let Some(t) = c.transport {
format!("{}", t)
} else {
"".to_string()
},
if let Some(p) = c.port.src {
format!("{}", p)
} else {
"".to_string()
},
if let Some(p) = c.port.dst {
format!("{}", p)
} else {
"".to_string()
},
);
}
}
impl Logger for ConsoleLogger {
fn init(&self) {
self.prolog("arp", "init", true);
self.prolog("eth", "init", true);
self.prolog("ipv4", "init", true);
self.prolog("ipv6", "init", true);
self.prolog("icmpv4", "init", true);
self.prolog("icmpv6", "init", true);
self.prolog("tcp", "init", true);
self.prolog("udp", "init", true);
}
/* ARP */
fn arp_enabled(&self) -> bool {
self.arp
}
fn arp_recv(&self, p: &ArpPacket) {
self.prolog("arp", "recv", false);
println!(
"{:}\t{:}\t{:}\t{:}\t{:?}",
p.get_sender_hw_addr(),
p.get_target_hw_addr(),
p.get_sender_proto_addr(),
p.get_target_proto_addr(),
p.get_operation(),
);
}
fn arp_drop(&self, p: &ArpPacket) {
self.prolog("arp", "drop", false);
println!(
"{:}\t{:}\t{:}\t{:}\t{:?}",
p.get_sender_hw_addr(),
p.get_target_hw_addr(),
p.get_sender_proto_addr(),
p.get_target_proto_addr(),
p.get_operation(),
);
}
fn arp_send(&self, p: &MutableArpPacket) {
self.prolog("arp", "send", false);
println!(
"{:}\t{:}\t{:}\t{:}\t{:?}",
p.get_target_hw_addr(),
p.get_sender_hw_addr(),
p.get_target_proto_addr(),
p.get_sender_proto_addr(),
p.get_operation(),
);
}
/* Ethernet */
fn eth_enabled(&self) -> bool {
self.eth
}
fn eth_recv(&self, p: &EthernetPacket, c: &ClientInfo) {
self.prolog("eth", "recv", false);
self.client_info(c);
println!("{:}", p.get_ethertype(),);
}
fn eth_drop(&self, p: &EthernetPacket, c: &ClientInfo) {
self.prolog("eth", "drop", false);
self.client_info(c);
println!("{:}", p.get_ethertype(),);
}
fn eth_send(&self, p: &MutableEthernetPacket, c: &ClientInfo) {
self.prolog("eth", "send", false);
self.client_info(c);
println!("{:}", p.get_ethertype(),);
}
/* IPv4 */
fn ipv4_enabled(&self) -> bool {
self.ipv4
}
fn ipv4_recv(&self, p: &Ipv4Packet, c: &ClientInfo) {
self.prolog("ipv4", "recv", false);
self.client_info(c);
println!("{:}", p.get_next_level_protocol(),);
}
fn ipv4_drop(&self, p: &Ipv4Packet, c: &ClientInfo) {
self.prolog("ipv4", "drop", false);
self.client_info(c);
println!("{:}", p.get_next_level_protocol(),);
}
fn ipv4_send(&self, p: &MutableIpv4Packet, c: &ClientInfo) {
self.prolog("ipv4", "send", false);
self.client_info(c);
println!("{:}", p.get_next_level_protocol(),);
}
/* IPv6 */
fn ipv6_enabled(&self) -> bool {
self.ipv6
}
fn ipv6_recv(&self, p: &Ipv6Packet, c: &ClientInfo) {
self.prolog("ipv6", "recv", false);
self.client_info(c);
println!("{:}", p.get_next_header(),);
}
fn ipv6_drop(&self, p: &Ipv6Packet, c: &ClientInfo) {
self.prolog("ipv6", "drop", false);
self.client_info(c);
println!("{:}", p.get_next_header(),);
}
fn ipv6_send(&self, p: &MutableIpv6Packet, c: &ClientInfo) {
self.prolog("ipv6", "send", false);
self.client_info(c);
println!("{:}", p.get_next_header(),);
}
/* ICMPv4 */
fn icmpv4_enabled(&self) -> bool {
self.icmpv4
}
fn icmpv4_recv(&self, p: &IcmpPacket, c: &ClientInfo) {
self.prolog("icmpv4", "recv", false);
self.client_info(c);
println!("{:?}\t{:?}", p.get_icmp_type(), p.get_icmp_code(),);
}
fn icmpv4_drop(&self, p: &IcmpPacket, c: &ClientInfo) {
self.prolog("icmpv4", "drop", false);
self.client_info(c);
println!("{:?}\t{:?}", p.get_icmp_type(), p.get_icmp_code(),);
}
fn icmpv4_send(&self, p: &MutableIcmpPacket, c: &ClientInfo) {
self.prolog("icmpv4", "send", false);
self.client_info(c);
println!("{:?}\t{:?}", p.get_icmp_type(), p.get_icmp_code(),);
}
/* ICMPv6 */
fn icmpv6_enabled(&self) -> bool {
self.icmpv6
}
fn icmpv6_recv(&self, p: &Icmpv6Packet, c: &ClientInfo) {
self.prolog("icmpv6", "recv", false);
self.client_info(c);
println!("{:?}\t{:?}", p.get_icmpv6_type(), p.get_icmpv6_code(),);
}
fn icmpv6_drop(&self, p: &Icmpv6Packet, c: &ClientInfo) {
self.prolog("icmpv6", "drop", false);
self.client_info(c);
println!("{:?}\t{:?}", p.get_icmpv6_type(), p.get_icmpv6_code(),);
}
fn icmpv6_send(&self, p: &MutableIcmpv6Packet, c: &ClientInfo) {
self.prolog("icmpv6", "send", false);
self.client_info(c);
println!("{:?}\t{:?}", p.get_icmpv6_type(), p.get_icmpv6_code(),);
}
/* TCP */
fn tcp_enabled(&self) -> bool {
self.tcp
}
fn tcp_recv(&self, p: &TcpPacket, c: &ClientInfo) {
self.prolog("tcp", "recv", false);
self.client_info(c);
println!(
"{:?}\t{:}\t{:}",
p.get_flags(),
p.get_sequence(),
p.get_acknowledgement(),
);
}
fn tcp_drop(&self, p: &TcpPacket, c: &ClientInfo) {
self.prolog("tcp", "drop", false);
self.client_info(c);
println!(
"{:?}\t{:}\t{:}",
p.get_flags(),
p.get_sequence(),
p.get_acknowledgement(),
);
}
fn tcp_send(&self, p: &MutableTcpPacket, c: &ClientInfo) {
self.prolog("tcp", "send", false);
self.client_info(c);
println!(
"{:?}\t{:}\t{:}",
p.get_flags(),
p.get_sequence(),
p.get_acknowledgement(),
);
}
/* UDP */
fn udp_enabled(&self) -> bool {
self.udp
}
fn udp_recv(&self, _p: &UdpPacket, c: &ClientInfo) {
self.prolog("udp", "recv", false);
self.client_info(c);
println!("");
}
fn udp_drop(&self, _p: &UdpPacket, c: &ClientInfo) {
self.prolog("udp", "drop", false);
self.client_info(c);
println!("");
}
fn udp_send(&self, _p: &MutableUdpPacket, c: &ClientInfo) {
self.prolog("udp", "send", false);
self.client_info(c);
println!("");
}
}

332
src/logger/logfmt.rs Normal file
View file

@ -0,0 +1,332 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use std::time::SystemTime;
use pnet::packet::{
arp::{ArpPacket, MutableArpPacket},
ethernet::{EthernetPacket, MutableEthernetPacket},
icmp::{IcmpPacket, MutableIcmpPacket},
icmpv6::{Icmpv6Packet, MutableIcmpv6Packet},
ipv4::{Ipv4Packet, MutableIpv4Packet},
ipv6::{Ipv6Packet, MutableIpv6Packet},
tcp::{MutableTcpPacket, TcpPacket},
udp::{MutableUdpPacket, UdpPacket},
};
use crate::client::ClientInfo;
use crate::logger::Logger;
pub struct LogfmtLogger {
arp: bool,
eth: bool,
ipv4: bool,
ipv6: bool,
icmpv4: bool,
icmpv6: bool,
tcp: bool,
udp: bool,
}
impl LogfmtLogger {
pub fn new() -> Self {
LogfmtLogger {
arp: true,
eth: true,
ipv4: true,
ipv6: true,
icmpv4: true,
icmpv6: true,
tcp: true,
udp: true,
}
}
fn prolog(&self, proto: &str, verb: &str, crlf: bool) {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
print!(
"ts={}.{} proto={} verb={}{}",
now.as_secs(),
now.subsec_millis(),
proto,
verb,
if crlf { "\n" } else { " " },
);
}
fn client_info(&self, c: &ClientInfo) {
print!(
"{}{}{}{}{}{}{}",
if let Some(m) = c.mac.src {
format!(" mac_src={}", m)
} else {
"".to_string()
},
if let Some(m) = c.mac.dst {
format!(" mac_dst={}", m)
} else {
"".to_string()
},
if let Some(i) = c.ip.src {
format!(" ip_src={}", i)
} else {
"".to_string()
},
if let Some(i) = c.ip.dst {
format!(" ip_dst={}", i)
} else {
"".to_string()
},
if let Some(t) = c.transport {
format!(" transport={}", t)
} else {
"".to_string()
},
if let Some(p) = c.port.src {
format!(" port_src={}", p)
} else {
"".to_string()
},
if let Some(p) = c.port.dst {
format!(" port_dst={}", p)
} else {
"".to_string()
},
);
}
}
impl Logger for LogfmtLogger {
fn init(&self) {
self.prolog("arp", "init", true);
self.prolog("eth", "init", true);
self.prolog("ipv4", "init", true);
self.prolog("ipv6", "init", true);
self.prolog("icmpv4", "init", true);
self.prolog("icmpv6", "init", true);
self.prolog("tcp", "init", true);
self.prolog("udp", "init", true);
}
/* ARP */
fn arp_enabled(&self) -> bool {
self.arp
}
fn arp_recv(&self, p: &ArpPacket) {
self.prolog("arp", "recv", false);
println!(
" mac_src={:} mac_dst={:} ip_src={:} ip_dst={:} op={:?}",
p.get_sender_hw_addr(),
p.get_target_hw_addr(),
p.get_sender_proto_addr(),
p.get_target_proto_addr(),
p.get_operation(),
);
}
fn arp_drop(&self, p: &ArpPacket) {
self.prolog("arp", "drop", false);
println!(
" mac_src={:} mac_dst={:} ip_src={:} ip_dst={:} op={:?}",
p.get_sender_hw_addr(),
p.get_target_hw_addr(),
p.get_sender_proto_addr(),
p.get_target_proto_addr(),
p.get_operation(),
);
}
fn arp_send(&self, p: &MutableArpPacket) {
self.prolog("arp", "send", false);
println!(
" mac_dst={:} mac_src={:} ip_dst={:} ip_src={:} op={:?}",
p.get_target_hw_addr(),
p.get_sender_hw_addr(),
p.get_target_proto_addr(),
p.get_sender_proto_addr(),
p.get_operation(),
);
}
/* Ethernet */
fn eth_enabled(&self) -> bool {
self.eth
}
fn eth_recv(&self, p: &EthernetPacket, c: &ClientInfo) {
self.prolog("eth", "recv", false);
self.client_info(c);
println!(" eth_type={:}", p.get_ethertype(),);
}
fn eth_drop(&self, p: &EthernetPacket, c: &ClientInfo) {
self.prolog("eth", "drop", false);
self.client_info(c);
println!(" eth_type={:}", p.get_ethertype(),);
}
fn eth_send(&self, p: &MutableEthernetPacket, c: &ClientInfo) {
self.prolog("eth", "send", false);
self.client_info(c);
println!(" eth_type={:}", p.get_ethertype(),);
}
/* IPv4 */
fn ipv4_enabled(&self) -> bool {
self.ipv4
}
fn ipv4_recv(&self, p: &Ipv4Packet, c: &ClientInfo) {
self.prolog("ipv4", "recv", false);
self.client_info(c);
println!(" next_proto={:}", p.get_next_level_protocol(),);
}
fn ipv4_drop(&self, p: &Ipv4Packet, c: &ClientInfo) {
self.prolog("ipv4", "drop", false);
self.client_info(c);
println!(" next_proto={:}", p.get_next_level_protocol(),);
}
fn ipv4_send(&self, p: &MutableIpv4Packet, c: &ClientInfo) {
self.prolog("ipv4", "send", false);
self.client_info(c);
println!(" next_proto={:}", p.get_next_level_protocol(),);
}
/* IPv6 */
fn ipv6_enabled(&self) -> bool {
self.ipv6
}
fn ipv6_recv(&self, p: &Ipv6Packet, c: &ClientInfo) {
self.prolog("ipv6", "recv", false);
self.client_info(c);
println!(" next_proto={:}", p.get_next_header(),);
}
fn ipv6_drop(&self, p: &Ipv6Packet, c: &ClientInfo) {
self.prolog("ipv6", "drop", false);
self.client_info(c);
println!(" next_proto={:}", p.get_next_header(),);
}
fn ipv6_send(&self, p: &MutableIpv6Packet, c: &ClientInfo) {
self.prolog("ipv6", "send", false);
self.client_info(c);
println!(" next_proto={:}", p.get_next_header(),);
}
/* ICMPv4 */
fn icmpv4_enabled(&self) -> bool {
self.icmpv4
}
fn icmpv4_recv(&self, p: &IcmpPacket, c: &ClientInfo) {
self.prolog("icmpv4", "recv", false);
self.client_info(c);
println!(
" icmp_type={:?} icmp_code={:?}",
p.get_icmp_type(),
p.get_icmp_code(),
);
}
fn icmpv4_drop(&self, p: &IcmpPacket, c: &ClientInfo) {
self.prolog("icmpv4", "drop", false);
self.client_info(c);
println!(
" icmp_type={:?} icmp_code={:?}",
p.get_icmp_type(),
p.get_icmp_code(),
);
}
fn icmpv4_send(&self, p: &MutableIcmpPacket, c: &ClientInfo) {
self.prolog("icmpv4", "send", false);
self.client_info(c);
println!(
" icmp_type={:?} icmp_code={:?}",
p.get_icmp_type(),
p.get_icmp_code(),
);
}
/* ICMPv6 */
fn icmpv6_enabled(&self) -> bool {
self.icmpv6
}
fn icmpv6_recv(&self, p: &Icmpv6Packet, c: &ClientInfo) {
self.prolog("icmpv6", "recv", false);
self.client_info(c);
println!(
" icmpv6_type={:?} icmpv6_code={:?}",
p.get_icmpv6_type(),
p.get_icmpv6_code(),
);
}
fn icmpv6_drop(&self, p: &Icmpv6Packet, c: &ClientInfo) {
self.prolog("icmpv6", "drop", false);
self.client_info(c);
println!(
" icmpv6_type={:?} icmpv6_code={:?}",
p.get_icmpv6_type(),
p.get_icmpv6_code(),
);
}
fn icmpv6_send(&self, p: &MutableIcmpv6Packet, c: &ClientInfo) {
self.prolog("icmpv6", "send", false);
self.client_info(c);
println!(
" icmpv6_type={:?} icmpv6_code={:?}",
p.get_icmpv6_type(),
p.get_icmpv6_code(),
);
}
/* TCP */
fn tcp_enabled(&self) -> bool {
self.tcp
}
fn tcp_recv(&self, p: &TcpPacket, c: &ClientInfo) {
self.prolog("tcp", "recv", false);
self.client_info(c);
println!(
" flags={:?} seq={:} ack={:}",
p.get_flags(),
p.get_sequence(),
p.get_acknowledgement(),
);
}
fn tcp_drop(&self, p: &TcpPacket, c: &ClientInfo) {
self.prolog("tcp", "drop", false);
self.client_info(c);
println!(
" flags={:?} seq={:} ack={:}",
p.get_flags(),
p.get_sequence(),
p.get_acknowledgement(),
);
}
fn tcp_send(&self, p: &MutableTcpPacket, c: &ClientInfo) {
self.prolog("tcp", "send", false);
self.client_info(c);
println!(
" flags={:?} seq={:} ack={:}",
p.get_flags(),
p.get_sequence(),
p.get_acknowledgement(),
);
}
/* UDP */
fn udp_enabled(&self) -> bool {
self.udp
}
fn udp_recv(&self, _p: &UdpPacket, c: &ClientInfo) {
self.prolog("udp", "recv", false);
self.client_info(c);
println!("");
}
fn udp_drop(&self, _p: &UdpPacket, c: &ClientInfo) {
self.prolog("udp", "drop", false);
self.client_info(c);
println!("");
}
fn udp_send(&self, _p: &MutableUdpPacket, c: &ClientInfo) {
self.prolog("udp", "send", false);
self.client_info(c);
println!("");
}
}

225
src/logger/meta.rs Normal file
View file

@ -0,0 +1,225 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use pnet::packet::{
arp::{ArpPacket, MutableArpPacket},
ethernet::{EthernetPacket, MutableEthernetPacket},
icmp::{IcmpPacket, MutableIcmpPacket},
icmpv6::{Icmpv6Packet, MutableIcmpv6Packet},
ipv4::{Ipv4Packet, MutableIpv4Packet},
ipv6::{Ipv6Packet, MutableIpv6Packet},
tcp::{MutableTcpPacket, TcpPacket},
udp::{MutableUdpPacket, UdpPacket},
};
use crate::client::ClientInfo;
use crate::logger::Logger;
pub struct MetaLogger {
loggers: Vec<Box<dyn Logger>>,
}
impl MetaLogger {
pub fn new() -> Self {
MetaLogger {
loggers: Vec::new(),
}
}
pub fn add(&mut self, log: Box<dyn Logger>) {
self.loggers.push(log);
}
pub fn init(&self) {
for l in &self.loggers {
l.init();
}
}
/* ARP */
pub fn arp_recv(&self, p: &ArpPacket) {
for l in &self.loggers {
if l.arp_enabled() {
l.arp_recv(p);
}
}
}
pub fn arp_drop(&self, p: &ArpPacket) {
for l in &self.loggers {
if l.arp_enabled() {
l.arp_drop(p);
}
}
}
pub fn arp_send(&self, p: &MutableArpPacket) {
for l in &self.loggers {
if l.arp_enabled() {
l.arp_send(p);
}
}
}
/* Ethernet */
pub fn eth_recv(&self, p: &EthernetPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.eth_enabled() {
l.eth_recv(p, c);
}
}
}
pub fn eth_drop(&self, p: &EthernetPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.eth_enabled() {
l.eth_drop(p, c);
}
}
}
pub fn eth_send(&self, p: &MutableEthernetPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.eth_enabled() {
l.eth_send(p, c);
}
}
}
/* IPv4 */
pub fn ipv4_recv(&self, p: &Ipv4Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.ipv4_enabled() {
l.ipv4_recv(p, c);
}
}
}
pub fn ipv4_drop(&self, p: &Ipv4Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.ipv4_enabled() {
l.ipv4_drop(p, c);
}
}
}
pub fn ipv4_send(&self, p: &MutableIpv4Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.ipv4_enabled() {
l.ipv4_send(p, c);
}
}
}
/* IPv6 */
pub fn ipv6_recv(&self, p: &Ipv6Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.ipv6_enabled() {
l.ipv6_recv(p, c);
}
}
}
pub fn ipv6_drop(&self, p: &Ipv6Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.ipv6_enabled() {
l.ipv6_drop(p, c);
}
}
}
pub fn ipv6_send(&self, p: &MutableIpv6Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.ipv6_enabled() {
l.ipv6_send(p, c);
}
}
}
/* ICMPv4 */
pub fn icmpv4_recv(&self, p: &IcmpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.icmpv4_enabled() {
l.icmpv4_recv(p, c);
}
}
}
pub fn icmpv4_drop(&self, p: &IcmpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.icmpv4_enabled() {
l.icmpv4_drop(p, c);
}
}
}
pub fn icmpv4_send(&self, p: &MutableIcmpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.icmpv4_enabled() {
l.icmpv4_send(p, c);
}
}
}
/* ICMPv6 */
pub fn icmpv6_recv(&self, p: &Icmpv6Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.icmpv6_enabled() {
l.icmpv6_recv(p, c);
}
}
}
pub fn icmpv6_drop(&self, p: &Icmpv6Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.icmpv6_enabled() {
l.icmpv6_drop(p, c);
}
}
}
pub fn icmpv6_send(&self, p: &MutableIcmpv6Packet, c: &ClientInfo) {
for l in &self.loggers {
if l.icmpv6_enabled() {
l.icmpv6_send(p, c);
}
}
}
/* TCP */
pub fn tcp_recv(&self, p: &TcpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.tcp_enabled() {
l.tcp_recv(p, c);
}
}
}
pub fn tcp_drop(&self, p: &TcpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.tcp_enabled() {
l.tcp_drop(p, c);
}
}
}
pub fn tcp_send(&self, p: &MutableTcpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.tcp_enabled() {
l.tcp_send(p, c);
}
}
}
/* UDP */
pub fn udp_recv(&self, p: &UdpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.udp_enabled() {
l.udp_recv(p, c);
}
}
}
pub fn udp_drop(&self, p: &UdpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.udp_enabled() {
l.udp_drop(p, c);
}
}
}
pub fn udp_send(&self, p: &MutableUdpPacket, c: &ClientInfo) {
for l in &self.loggers {
if l.udp_enabled() {
l.udp_send(p, c);
}
}
}
}

97
src/logger/mod.rs Normal file
View file

@ -0,0 +1,97 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use pnet::packet::{
arp::{ArpPacket, MutableArpPacket},
ethernet::{EthernetPacket, MutableEthernetPacket},
icmp::{IcmpPacket, MutableIcmpPacket},
icmpv6::{Icmpv6Packet, MutableIcmpv6Packet},
ipv4::{Ipv4Packet, MutableIpv4Packet},
ipv6::{Ipv6Packet, MutableIpv6Packet},
tcp::{MutableTcpPacket, TcpPacket},
udp::{MutableUdpPacket, UdpPacket},
};
use crate::client::ClientInfo;
mod console;
mod logfmt;
mod meta;
pub use console::ConsoleLogger;
pub use logfmt::LogfmtLogger;
pub use meta::MetaLogger;
pub trait Logger {
fn init(&self);
/* list of notifications that a logger might or might not implement */
/* ARP */
fn arp_enabled(&self) -> bool {
true
}
fn arp_recv(&self, _p: &ArpPacket) {}
fn arp_drop(&self, _p: &ArpPacket) {}
fn arp_send(&self, _p: &MutableArpPacket) {}
/* Ethernet */
fn eth_enabled(&self) -> bool {
true
}
fn eth_recv(&self, _p: &EthernetPacket, _c: &ClientInfo) {}
fn eth_drop(&self, _p: &EthernetPacket, _c: &ClientInfo) {}
fn eth_send(&self, _p: &MutableEthernetPacket, _c: &ClientInfo) {}
/* IPv4 */
fn ipv4_enabled(&self) -> bool {
true
}
fn ipv4_recv(&self, _p: &Ipv4Packet, _c: &ClientInfo) {}
fn ipv4_drop(&self, _p: &Ipv4Packet, _c: &ClientInfo) {}
fn ipv4_send(&self, _p: &MutableIpv4Packet, _c: &ClientInfo) {}
/* IPv6 */
fn ipv6_enabled(&self) -> bool {
true
}
fn ipv6_recv(&self, _p: &Ipv6Packet, _c: &ClientInfo) {}
fn ipv6_drop(&self, _p: &Ipv6Packet, _c: &ClientInfo) {}
fn ipv6_send(&self, _p: &MutableIpv6Packet, _c: &ClientInfo) {}
/* ICMPv4 */
fn icmpv4_enabled(&self) -> bool {
true
}
fn icmpv4_recv(&self, _p: &IcmpPacket, _c: &ClientInfo) {}
fn icmpv4_drop(&self, _p: &IcmpPacket, _c: &ClientInfo) {}
fn icmpv4_send(&self, _p: &MutableIcmpPacket, _c: &ClientInfo) {}
/* ICMPv6 */
fn icmpv6_enabled(&self) -> bool {
true
}
fn icmpv6_recv(&self, _p: &Icmpv6Packet, _c: &ClientInfo) {}
fn icmpv6_drop(&self, _p: &Icmpv6Packet, _c: &ClientInfo) {}
fn icmpv6_send(&self, _p: &MutableIcmpv6Packet, _c: &ClientInfo) {}
/* TCP */
fn tcp_enabled(&self) -> bool {
true
}
fn tcp_recv(&self, _p: &TcpPacket, _c: &ClientInfo) {}
fn tcp_drop(&self, _p: &TcpPacket, _c: &ClientInfo) {}
fn tcp_send(&self, _p: &MutableTcpPacket, _c: &ClientInfo) {}
/* UDP */
fn udp_enabled(&self) -> bool {
true
}
fn udp_recv(&self, _p: &UdpPacket, _c: &ClientInfo) {}
fn udp_drop(&self, _p: &UdpPacket, _c: &ClientInfo) {}
fn udp_send(&self, _p: &MutableUdpPacket, _c: &ClientInfo) {}
}

View file

@ -1,5 +1,5 @@
// This file is part of masscanned. // This file is part of masscanned.
// Copyright 2021 - The IVRE project // Copyright 2021 - 2022 The IVRE project
// //
// Masscanned is free software: you can redistribute it and/or modify it // Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by // under the terms of the GNU General Public License as published by
@ -24,7 +24,7 @@ use std::fs::File;
use std::net::IpAddr; use std::net::IpAddr;
use std::str::FromStr; use std::str::FromStr;
use clap::{App, Arg}; use clap::{builder::PossibleValuesParser, Arg, ArgAction, Command};
use log::*; use log::*;
use pnet::{ use pnet::{
datalink::{self, Channel::Ethernet, DataLinkReceiver, DataLinkSender, NetworkInterface}, datalink::{self, Channel::Ethernet, DataLinkReceiver, DataLinkSender, NetworkInterface},
@ -35,12 +35,14 @@ use pnet::{
util::MacAddr, util::MacAddr,
}; };
use crate::logger::{ConsoleLogger, LogfmtLogger, Logger, MetaLogger};
use crate::utils::IpAddrParser; use crate::utils::IpAddrParser;
mod client; mod client;
mod layer_2; mod layer_2;
mod layer_3; mod layer_3;
mod layer_4; mod layer_4;
mod logger;
mod proto; mod proto;
mod smack; mod smack;
mod synackcookie; mod synackcookie;
@ -54,7 +56,10 @@ pub struct Masscanned<'a> {
pub mac: MacAddr, pub mac: MacAddr,
/* iface is an Option to make tests easier */ /* iface is an Option to make tests easier */
pub iface: Option<&'a NetworkInterface>, pub iface: Option<&'a NetworkInterface>,
pub ip_addresses: Option<&'a HashSet<IpAddr>>, pub self_ip_list: Option<&'a HashSet<IpAddr>>,
pub remote_ip_deny_list: Option<&'a HashSet<IpAddr>>,
/* loggers */
pub log: MetaLogger,
} }
/* Get the L2 network interface from its name */ /* Get the L2 network interface from its name */
@ -98,40 +103,75 @@ fn reply<'a, 'b>(packet: &'a [u8], masscanned: &Masscanned) -> Option<MutableEth
fn main() { fn main() {
/* parse arguments from CLI */ /* parse arguments from CLI */
let args = App::new("Network responder - answer them all") let args = Command::new("Network responder - answer them all")
.version(VERSION) .version(VERSION)
.about("Network answering machine for various network protocols (L2-L3-L4 + applications)") .about("Network answering machine for various network protocols (L2-L3-L4 + applications)")
.arg( .arg(
Arg::with_name("interface") Arg::new("interface")
.short("i") .short('i')
.long("iface") .long("iface")
.value_name("iface") .value_name("iface")
.help("the interface to use for receiving/sending packets") .help("the interface to use for receiving/sending packets")
.required(true) .required(true)
.takes_value(true), .num_args(1),
) )
.arg( .arg(
Arg::with_name("mac") Arg::new("mac")
.short("a") .short('m')
.long("mac-addr") .long("mac-addr")
.help("MAC address to use in the response packets") .help("MAC address to use in the response packets")
.takes_value(true), .num_args(1),
) )
.arg( .arg(
Arg::with_name("ip") Arg::new("selfipfile")
.short("f") .long("self-ip-file")
.long("ip-addr-file") .help("File with the list of IP addresses handled by masscanned")
.help("File with the list of IP addresses to impersonate") .num_args(1),
.takes_value(true),
) )
.arg( .arg(
Arg::with_name("verbosity") Arg::new("selfiplist")
.short("v") .long("self-ip-list")
.multiple(true) .help("Inline list of IP addresses handled by masscanned, comma-separated")
.num_args(1),
)
.arg(
Arg::new("remoteipdenyfile")
.long("remote-ip-deny-file")
.help(
"File with the list of IP addresses from which masscanned will ignore packets",
)
.num_args(1),
)
.arg(
Arg::new("remoteipdenylist")
.long("remote-ip-deny-list")
.help("Inline list of IP addresses from which masscanned will ignore packets")
.num_args(1),
)
.arg(
Arg::new("verbosity")
.short('v')
.action(ArgAction::Count)
.help("Increase message verbosity"), .help("Increase message verbosity"),
) )
.arg(
Arg::new("quiet")
.long("quiet")
.short('q')
.action(ArgAction::SetTrue)
.required(false)
.help("Quiet mode: do not output anything on stdout"),
)
.arg(
Arg::new("format")
.long("format")
.help("Format in which to output logs")
.default_value("console")
.value_parser(PossibleValuesParser::new(["console", "logfmt"]))
.num_args(1),
)
.get_matches(); .get_matches();
let verbose = args.occurrences_of("verbosity") as usize; let verbose = args.value_source("verbosity").unwrap() as usize;
/* initialise logger */ /* initialise logger */
stderrlog::new() stderrlog::new()
.module(module_path!()) .module(module_path!())
@ -143,27 +183,24 @@ fn main() {
debug!("debug messages enabled"); debug!("debug messages enabled");
trace!("trace messages enabled"); trace!("trace messages enabled");
info!("Command line arguments:"); info!("Command line arguments:");
for arg in &args.args {
info!("....{:?}", arg);
}
let iface = if let Some(i) = get_interface( let iface = if let Some(i) = get_interface(
args.value_of("interface") args.get_one::<String>("interface")
.expect("error parsing iface argument"), .expect("error parsing iface argument"),
) { ) {
i i
} else { } else {
error!( error!(
"Cannot open interface \"{}\" - are you sure it exists?", "Cannot open interface \"{}\" - are you sure it exists?",
args.value_of("interface") args.get_one::<String>("interface")
.expect("error parsing iface argument") .expect("error parsing iface argument")
); );
return; return;
}; };
if iface.flags & (netdevice::IFF_UP.bits() as u32) == 0 { if !iface.is_up() {
error!("specified interface is DOWN"); error!("specified interface is DOWN");
return; return;
} }
let mac = if let Some(m) = args.value_of("mac") { let mac = if let Some(m) = args.get_one::<String>("mac") {
MacAddr::from_str(m).expect("error parsing provided MAC address") MacAddr::from_str(m).expect("error parsing provided MAC address")
} else if let Some(m) = iface.mac { } else if let Some(m) = iface.mac {
m m
@ -172,9 +209,9 @@ fn main() {
}; };
/* Parse ip address file specified */ /* Parse ip address file specified */
/* FIXME: .and_then(|path| File::open(path).map(|file| )).unwrap_or_default() ? */ /* FIXME: .and_then(|path| File::open(path).map(|file| )).unwrap_or_default() ? */
let ip_list = if let Some(ref path) = args.value_of("ip") { let mut ip_list = if let Some(ref path) = args.get_one::<String>("selfipfile") {
if let Ok(file) = File::open(path) { if let Ok(file) = File::open(path) {
info!("parsing ip address file: {}", &path); info!("parsing self ip file: {}", &path);
file.extract_ip_addresses_only(None) file.extract_ip_addresses_only(None)
} else { } else {
HashSet::new() HashSet::new()
@ -182,23 +219,74 @@ fn main() {
} else { } else {
HashSet::new() HashSet::new()
}; };
let ip_addresses = if !ip_list.is_empty() { if let Some(ip_inline) = args.get_one::<String>("selfiplist") {
ip_list.extend(ip_inline.extract_ip_addresses_only(None));
}
let self_ip_list = if !ip_list.is_empty() {
for ip in &ip_list {
info!("binding........{}", ip);
}
Some(&ip_list)
} else {
info!("binding........0.0.0.0");
info!("binding........::");
None
};
/* Parse remote ip deny file specified */
let mut ip_list = if let Some(ref path) = args.get_one::<String>("remoteipdenyfile") {
if let Ok(file) = File::open(path) {
info!("parsing remote ip deny file: {}", &path);
file.extract_ip_addresses_only(None)
} else {
HashSet::new()
}
} else {
HashSet::new()
};
if let Some(ip_inline) = args.get_one::<String>("remoteipdenylist") {
ip_list.extend(ip_inline.extract_ip_addresses_only(None));
}
let remote_ip_deny_list = if !ip_list.is_empty() {
for ip in &ip_list {
info!("ignoring.......{}", ip);
}
Some(&ip_list) Some(&ip_list)
} else { } else {
None None
}; };
let masscanned = Masscanned {
let mut masscanned = Masscanned {
synack_key: [0, 0], synack_key: [0, 0],
mac, mac,
iface: Some(&iface), iface: Some(&iface),
ip_addresses, self_ip_list,
remote_ip_deny_list,
log: MetaLogger::new(),
}; };
info!("interface......{}", masscanned.iface.unwrap().name); info!("interface......{}", masscanned.iface.unwrap().name);
info!("mac address....{}", masscanned.mac); info!("mac address....{}", masscanned.mac);
if !args
.get_one::<bool>("quiet")
.expect("unexpected error parsing argument")
{
if let Some(format) = args.get_one::<String>("format") {
let chosen_logger: Box<dyn Logger> = match format.as_str() {
"console" => Box::new(ConsoleLogger::new()),
"logfmt" => Box::new(LogfmtLogger::new()),
// clap should already ensure we're using a valid format
_ => panic!("illegal format"),
};
masscanned.log.add(chosen_logger);
} else {
masscanned.log.add(Box::new(ConsoleLogger::new()));
}
masscanned.log.init();
}
let (mut tx, mut rx) = get_channel(masscanned.iface.unwrap()); let (mut tx, mut rx) = get_channel(masscanned.iface.unwrap());
loop { loop {
/* check if network interface is still up */ /* check if network interface is still up */
if masscanned.iface.unwrap().flags & (netdevice::IFF_UP.bits() as u32) == 0 { if !masscanned.iface.unwrap().is_up() {
error!("interface is DOWN - aborting"); error!("interface is DOWN - aborting");
break; break;
} }

92
src/proto/dissector.rs Normal file
View file

@ -0,0 +1,92 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
////////////
// Common //
////////////
/// ### PacketDissector
/// A util class used to dissect fields.
#[derive(Debug, Clone)]
pub struct PacketDissector<T> {
pub i: usize,
pub state: T,
}
impl<T> PacketDissector<T> {
pub fn new(initial_state: T) -> PacketDissector<T> {
return PacketDissector {
i: 0,
state: initial_state,
};
}
pub fn next_state(&mut self, state: T) {
self.state = state;
self.i = 0;
}
pub fn next_state_when_i_reaches(&mut self, state: T, i: usize) {
if self.i == i {
self.next_state(state);
}
}
fn _read_usize(&mut self, byte: &u8, value: usize, next_state: T, size: usize) -> usize {
self.i += 1;
self.next_state_when_i_reaches(next_state, size);
(value << 8) + *byte as usize
}
fn _read_ulesize(&mut self, byte: &u8, value: usize, next_state: T, size: usize) -> usize {
let ret = value + ((*byte as usize) << (8 * self.i));
self.i += 1;
self.next_state_when_i_reaches(next_state, size);
ret
}
pub fn read_u16(&mut self, byte: &u8, value: u16, next_state: T) -> u16 {
self._read_usize(byte, value as usize, next_state, 2) as u16
}
pub fn read_ule16(&mut self, byte: &u8, value: u16, next_state: T) -> u16 {
self._read_ulesize(byte, value as usize, next_state, 2) as u16
}
pub fn read_u32(&mut self, byte: &u8, value: u32, next_state: T) -> u32 {
self._read_usize(byte, value as usize, next_state, 4) as u32
}
pub fn read_ule32(&mut self, byte: &u8, value: u32, next_state: T) -> u32 {
self._read_ulesize(byte, value as usize, next_state, 4) as u32
}
pub fn read_ule64(&mut self, byte: &u8, value: u64, next_state: T) -> u64 {
self._read_ulesize(byte, value as usize, next_state, 8) as u64
}
}
pub trait MPacket {
fn new() -> Self;
fn repl(
&self,
_masscanned: &Masscanned,
_client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>>;
fn parse(&mut self, byte: &u8);
fn parse_all(&mut self, bytes: &[u8]) {
for byte in bytes {
self.parse(byte);
}
}
}

93
src/proto/dns/cst.rs Normal file
View file

@ -0,0 +1,93 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use strum_macros::EnumIter;
#[derive(PartialEq, Debug, Clone, Copy, EnumIter)]
pub enum DNSType {
NONE,
A,
TXT, // value: 16 - text strings
}
impl From<u16> for DNSType {
fn from(item: u16) -> Self {
match item {
1 => DNSType::A,
16 => DNSType::TXT,
_ => DNSType::NONE,
}
}
}
impl From<DNSType> for u16 {
fn from(item: DNSType) -> Self {
match item {
DNSType::A => 1,
DNSType::TXT => 16,
_ => 0,
}
}
}
#[derive(PartialEq, Debug, Clone, Copy, EnumIter)]
pub enum DNSClass {
NONE,
IN, // value: 1 - the Internet
CH, // value: 3 - the CHAOS class
}
impl From<u16> for DNSClass {
fn from(item: u16) -> Self {
match item {
1 => DNSClass::IN,
3 => DNSClass::CH,
_ => DNSClass::NONE,
}
}
}
impl From<DNSClass> for u16 {
fn from(item: DNSClass) -> Self {
match item {
DNSClass::IN => 1,
DNSClass::CH => 3,
_ => 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn type_parse() {
/* type TXT */
assert!(DNSType::from(1) == DNSType::A);
assert!(1 as u16 == DNSType::A.into());
assert!(DNSType::from(16) == DNSType::TXT);
assert!(16 as u16 == DNSType::TXT.into());
}
#[test]
fn class_parse() {
assert!(DNSClass::from(1) == DNSClass::IN);
assert!(1 as u16 == DNSClass::IN.into());
assert!(DNSClass::from(3) == DNSClass::CH);
assert!(3 as u16 == DNSClass::CH.into());
}
}

387
src/proto/dns/header.rs Normal file
View file

@ -0,0 +1,387 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use std::convert::TryFrom;
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq)]
pub enum DNSHeaderState {
Id,
Flags,
QDCount,
ANCount,
NSCount,
ARCount,
End,
}
pub struct DNSHeader {
pub d: PacketDissector<DNSHeaderState>,
pub id: u16,
pub flags: u16,
pub _qr: bool,
pub _opcode: u8,
pub _aa: bool,
pub _tc: bool,
pub _rd: bool,
pub _ra: bool,
pub _z: u8,
pub _rcode: u8,
pub qdcount: u16,
pub ancount: u16,
pub nscount: u16,
pub arcount: u16,
}
impl TryFrom<Vec<u8>> for DNSHeader {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut hdr = DNSHeader::new();
for b in item {
hdr.parse(&b);
}
if hdr.d.state == DNSHeaderState::End {
Ok(hdr)
} else {
Err("packet is incomplete")
}
}
}
impl From<&DNSHeader> for Vec<u8> {
fn from(item: &DNSHeader) -> Self {
let mut v = Vec::new();
/* id */
v.push((item.id >> 8) as u8);
v.push((item.id & 0xFF) as u8);
/* flags */
/* QR | OPCODE | AA | TC | RD */
v.push(
((item._qr as u8) << 7)
| (item._opcode << 3)
| ((item._aa as u8) << 2)
| ((item._tc as u8) << 1)
| (item._rd as u8),
);
/* AA | ZZZ | RCODE */
v.push(0);
/* qdcount */
v.push((item.qdcount >> 8) as u8);
v.push((item.qdcount & 0xFF) as u8);
/* ancount */
v.push((item.ancount >> 8) as u8);
v.push((item.ancount & 0xFF) as u8);
/* nscount */
v.push((item.nscount >> 8) as u8);
v.push((item.nscount & 0xFF) as u8);
/* arcount */
v.push((item.arcount >> 8) as u8);
v.push((item.arcount & 0xFF) as u8);
v
}
}
impl MPacket for DNSHeader {
fn new() -> Self {
DNSHeader {
d: PacketDissector::new(DNSHeaderState::Id),
id: 0,
flags: 0,
_qr: false,
_opcode: 0,
_aa: false,
_tc: false,
_rd: false,
_ra: false,
_z: 0,
_rcode: 0,
qdcount: 0,
ancount: 0,
nscount: 0,
arcount: 0,
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSHeaderState::Id => {
self.id = self.d.read_u16(byte, self.id, DNSHeaderState::Flags);
}
DNSHeaderState::Flags => {
self.flags = self.d.read_u16(byte, self.flags, DNSHeaderState::QDCount);
}
DNSHeaderState::QDCount => {
self.qdcount = self.d.read_u16(byte, self.qdcount, DNSHeaderState::ANCount);
}
DNSHeaderState::ANCount => {
self.ancount = self.d.read_u16(byte, self.ancount, DNSHeaderState::NSCount);
}
DNSHeaderState::NSCount => {
self.nscount = self.d.read_u16(byte, self.nscount, DNSHeaderState::ARCount);
}
DNSHeaderState::ARCount => {
self.arcount = self.d.read_u16(byte, self.arcount, DNSHeaderState::End);
}
DNSHeaderState::End => {}
}
/* we need this to be executed at the same call
* the state changes to End, hence it is not in the
* match structure
**/
if self.d.state == DNSHeaderState::End {
self._qr = (self.flags >> 15) == 1;
self._opcode = ((self.flags >> 11) & 0x0F) as u8;
self._aa = (self.flags >> 10) & 0x01 == 1;
self._tc = (self.flags >> 9) & 0x01 == 1;
self._rd = (self.flags >> 8) & 0x01 == 1;
self._ra = (self.flags >> 7) & 0x01 == 1;
self._z = ((self.flags >> 4) & 0x07) as u8;
self._rcode = (self.flags & 0x0F) as u8;
}
}
fn repl(
&self,
_masscanned: &Masscanned,
_client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
let mut r = DNSHeader::new();
r.id = self.id;
r._qr = true;
r._opcode = self._opcode;
r._aa = true;
r._tc = false;
/* RFC1035
* Recursion Desired - this bit may be set in a query and
* is copied into the response. */
r._rd = self._rd;
r._ra = false;
r.qdcount = self.qdcount;
r.ancount = self.qdcount;
Some(Vec::<u8>::from(&r))
}
}
#[cfg(test)]
mod tests {
use super::*;
use pnet::util::MacAddr;
use std::str::FromStr;
use crate::logger::MetaLogger;
#[test]
fn parse_all() {
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00";
let hdr = match DNSHeader::try_from(payload.to_vec()) {
Ok(_hdr) => _hdr,
Err(e) => panic!("error while parsing DNS header: {}", e),
};
assert!(hdr.d.state == DNSHeaderState::End);
assert!(hdr.id == 0xb307);
assert!(hdr.flags == 0x0100);
assert!(hdr._qr == false);
assert!(hdr._opcode == 0);
assert!(hdr._aa == false);
assert!(hdr._tc == false);
assert!(hdr._rd == true);
assert!(hdr._ra == false);
assert!(hdr._z == 0);
assert!(hdr._rcode == 0);
assert!(hdr.qdcount == 1);
assert!(hdr.ancount == 0);
assert!(hdr.nscount == 0);
assert!(hdr.arcount == 0);
assert!(Vec::<u8>::from(&hdr) == payload.to_vec());
/* KO */
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00";
match DNSHeader::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
};
}
#[test]
fn parse_byte_by_byte() {
/* OK */
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00";
let mut hdr = DNSHeader::new();
for b in payload {
assert!(hdr.d.state != DNSHeaderState::End);
hdr.parse(b);
}
assert!(hdr.d.state == DNSHeaderState::End);
assert!(hdr.id == 0xb307);
assert!(hdr.flags == 0x0100);
assert!(hdr._qr == false);
assert!(hdr._opcode == 0);
assert!(hdr._aa == false);
assert!(hdr._tc == false);
assert!(hdr._rd == true);
assert!(hdr._ra == false);
assert!(hdr._z == 0);
assert!(hdr._rcode == 0);
assert!(hdr.qdcount == 1);
assert!(hdr.ancount == 0);
assert!(hdr.nscount == 0);
assert!(hdr.arcount == 0);
assert!(Vec::<u8>::from(&hdr) == payload.to_vec());
/* KO */
let payload = b"\xb3\x07\x01\x00\x00\x01\x00\x00\x00\x00\x00";
let mut hdr = DNSHeader::new();
for b in payload {
hdr.parse(b);
}
assert!(hdr.d.state != DNSHeaderState::End);
}
fn consistency_qd_rr(qd: &DNSHeader, rr: &DNSHeader) {
assert!(rr.id == qd.id);
assert!(rr._qr == true);
assert!(rr._opcode == qd._opcode);
assert!(rr._aa == true);
assert!(rr._tc == false);
assert!(rr._rd == qd._rd);
assert!(rr._ra == false);
assert!(rr._z == 0);
assert!(rr._rcode == 0);
/* check flags */
assert!(rr.flags >> 15 == rr._qr as u16);
assert!((rr.flags >> 11) & 0xF == rr._opcode as u16);
assert!((rr.flags >> 10) & 0x1 == rr._aa as u16);
assert!((rr.flags >> 9) & 0x1 == rr._tc as u16);
assert!((rr.flags >> 8) & 0x1 == rr._rd as u16);
assert!((rr.flags >> 7) & 0x1 == rr._ra as u16);
assert!((rr.flags >> 4) & 0x7 == rr._z as u16);
assert!(rr.flags & 0xF == rr._rcode as u16);
assert!(rr.qdcount == qd.qdcount);
assert!(rr.ancount == qd.qdcount);
assert!(rr.nscount == 0);
assert!(rr.arcount == 0);
}
#[test]
fn repl_id() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
for id in [0x1234, 0x4321, 0xffff, 0x0, 0x1337] {
hdr.id = id;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
#[test]
fn repl_opcode() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
/* opcode */
for opcode in 0..3 {
hdr._opcode = opcode;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
#[test]
fn repl_rd() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
/* rd */
for rd in [false, true] {
hdr._rd = rd;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
#[test]
fn repl_ancount() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
let mut hdr = DNSHeader::new();
hdr._qr = false;
/* rd */
for qdcount in 0..16 {
hdr.qdcount = qdcount;
let hdr_repl = if let Some(r) = hdr.repl(&masscanned, &client_info, None) {
DNSHeader::try_from(r).unwrap()
} else {
panic!("expected DNS header answer, got None");
};
consistency_qd_rr(&hdr, &hdr_repl);
}
}
}

688
src/proto/dns/mod.rs Normal file
View file

@ -0,0 +1,688 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use std::convert::TryFrom;
mod cst;
mod header;
use header::{DNSHeader, DNSHeaderState};
mod query;
use query::{DNSQuery, DNSQueryState};
mod rr;
use rr::{DNSRRState, DNSRR};
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq, Debug)]
enum DNSState {
Header,
Query,
Answer,
Authority,
Additional,
End,
}
pub struct DNSPacket {
d: PacketDissector<DNSState>,
header: DNSHeader,
qd: Vec<DNSQuery>,
rr: Vec<DNSRR>,
ns: Vec<DNSRR>,
ar: Vec<DNSRR>,
}
impl TryFrom<Vec<u8>> for DNSPacket {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut dns = DNSPacket::new();
for b in item {
dns.parse(&b);
}
if dns.d.state == DNSState::End {
Ok(dns)
} else {
Err("packet is incomplete")
}
}
}
impl From<&DNSPacket> for Vec<u8> {
fn from(item: &DNSPacket) -> Self {
let mut v = Vec::new();
v.extend(Vec::<u8>::from(&item.header));
for qd in &item.qd {
v.extend(Vec::<u8>::from(qd));
}
for rr in &item.rr {
v.extend(Vec::<u8>::from(rr));
}
for ns in &item.ns {
v.extend(Vec::<u8>::from(ns));
}
for ar in &item.ar {
v.extend(Vec::<u8>::from(ar));
}
v
}
}
impl MPacket for DNSPacket {
fn new() -> Self {
DNSPacket {
d: PacketDissector::new(DNSState::Header),
header: DNSHeader::new(),
qd: Vec::new(),
rr: Vec::new(),
ns: Vec::new(),
ar: Vec::new(),
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSState::Header => {
self.header.parse(byte);
if self.header.d.state == DNSHeaderState::End {
if self.header.qdcount > 0 {
self.qd.push(DNSQuery::new());
self.d.next_state(DNSState::Query);
} else if self.header.ancount > 0 {
self.rr.push(DNSRR::new());
self.d.next_state(DNSState::Answer);
} else if self.header.nscount > 0 {
self.d.next_state(DNSState::Authority);
} else if self.header.arcount > 0 {
self.d.next_state(DNSState::Additional);
} else {
self.d.next_state(DNSState::End);
}
}
}
DNSState::Query => {
let qdcount = self.qd.len();
self.qd[qdcount - 1].parse(byte);
if self.qd[qdcount - 1].d.state == DNSQueryState::End {
if self.header.qdcount as usize > self.qd.len() {
self.qd.push(DNSQuery::new());
} else if self.header.ancount > 0 {
self.rr.push(DNSRR::new());
self.d.next_state(DNSState::Answer);
} else if self.header.nscount > 0 {
self.d.next_state(DNSState::Authority);
} else if self.header.arcount > 0 {
self.d.next_state(DNSState::Additional);
} else {
self.d.next_state(DNSState::End);
}
}
}
DNSState::Answer => {
let ancount = self.rr.len();
self.rr[ancount - 1].parse(byte);
if self.rr[ancount - 1].d.state == DNSRRState::End {
if self.header.ancount as usize > self.rr.len() {
self.rr.push(DNSRR::new());
} else if self.header.nscount > 0 {
self.d.next_state(DNSState::Authority);
} else if self.header.arcount > 0 {
self.d.next_state(DNSState::Additional);
} else {
self.d.next_state(DNSState::End);
}
}
}
_ => {}
}
}
fn repl(
&self,
masscanned: &Masscanned,
client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
let mut ans = DNSPacket::new();
ans.header = if let Some(hdr) = self.header.repl(&masscanned, &client_info, None) {
if let Ok(h) = DNSHeader::try_from(hdr) {
h
} else {
return None;
}
} else {
return None;
};
/* reply to qd */
for qd in &self.qd {
if let Ok(q) = DNSQuery::try_from(Vec::<u8>::from(qd)) {
ans.qd.push(q);
} else {
return None;
}
if let Some(raw_rr) = qd.repl(&masscanned, &client_info, None) {
if let Ok(rr) = DNSRR::try_from(raw_rr) {
ans.rr.push(rr);
} else {
return None;
}
} else {
return None;
}
}
Some(Vec::<u8>::from(&ans))
}
}
#[cfg(test)]
mod tests {
use super::cst::{DNSClass, DNSType};
use super::*;
use pnet::util::MacAddr;
use std::net::{IpAddr, Ipv4Addr};
use std::str::FromStr;
use crate::logger::MetaLogger;
#[test]
fn parse_qd_all() {
/* OK */
/* scapy: DNS(id=0x1337,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"))
**/
let payload = b"\x137\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01";
let dns = match DNSPacket::try_from(payload.to_vec()) {
Ok(_dns) => _dns,
Err(e) => panic!("error while parsing DNS packet: {}", e),
};
assert!(dns.header.id == 0x1337);
assert!(dns.header._qr == false);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == false);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 0);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
/* KO */
let payload = b"\x137\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
let payload = b"xxx";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
}
#[test]
fn parse_qd_byte_by_byte() {
/* scapy: DNS(id=0x1337,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"))
**/
let payload = b"\x137\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01";
let mut dns = DNSPacket::new();
for b in payload {
assert!(dns.d.state != DNSState::End);
dns.parse(&b);
}
assert!(dns.d.state == DNSState::End);
assert!(dns.header.id == 0x1337);
assert!(dns.header._qr == false);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == false);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 0);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
}
#[test]
fn parse_rr_all() {
/* OK */
/* scapy: DNS(id=1234, qr=True, aa=True, qd=None,
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
**/
let payload = b"\x04\xd2\x85\x00\x00\x00\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let dns = match DNSPacket::try_from(payload.to_vec()) {
Ok(_dns) => _dns,
Err(e) => panic!("error while parsing DNS packet: {}", e),
};
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 0);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
/* KO */
let payload = b"\x04\xd2\x85\x00\x00\x00\x00\x04\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
let payload = b"xxx";
match DNSPacket::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
}
#[test]
fn parse_rr_byte_by_byte() {
/* scapy: DNS(id=1234, qr=True, aa=True, qd=None,
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
**/
let payload = b"\x04\xd2\x85\x00\x00\x00\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let mut dns = DNSPacket::new();
for b in payload {
assert!(dns.d.state != DNSState::End);
dns.parse(&b);
}
assert!(dns.d.state == DNSState::End);
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 0);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
}
#[test]
fn parse_qd_rr_all() {
/* scapy: DNS(id=1234, qr=True, aa=True,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"),
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
*/
let payload = b"\x04\xd2\x85\x00\x00\x03\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let dns = match DNSPacket::try_from(payload.to_vec()) {
Ok(_dns) => _dns,
Err(e) => panic!("error while parsing DNS packet: {}", e),
};
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
}
#[test]
fn parse_qr_rr_byte_by_byte() {
/* scapy: DNS(id=1234, qr=True, aa=True,
* qd=DNSQR(qname="www.example1.com")/DNSQR(qname="www.example2.com")/DNSQR(qname="www.example3.com"),
* an=DNSRR(rrname="www.example1.com", rdata="127.0.0.1")/DNSRR(rrname="www.example2.com", rdata="127.0.0.2")/DNSRR(rrname="www.example3.com", rdata="127.0.0.3"))
*/
let payload = b"\x04\xd2\x85\x00\x00\x03\x00\x03\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x02\x03www\x08example3\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x03";
let mut dns = DNSPacket::new();
for b in payload {
assert!(dns.d.state != DNSState::End);
dns.parse(&b);
}
assert!(dns.d.state == DNSState::End);
assert!(dns.header.id == 1234);
assert!(dns.header._qr == true);
assert!(dns.header._opcode == 0);
assert!(dns.header._aa == true);
assert!(dns.header._tc == false);
assert!(dns.header._rd == true);
assert!(dns.header._ra == false);
assert!(dns.header._z == 0);
assert!(dns.header._rcode == 0);
assert!(dns.header.qdcount == 3);
assert!(dns.header.ancount == 3);
assert!(dns.header.nscount == 0);
assert!(dns.header.arcount == 0);
assert!(dns.qd.len() == 3);
assert!(
dns.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[0].type_ == DNSType::A);
assert!(dns.qd[0].class == DNSClass::IN);
assert!(
dns.qd[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[1].type_ == DNSType::A);
assert!(dns.qd[1].class == DNSClass::IN);
assert!(
dns.qd[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.qd[2].type_ == DNSType::A);
assert!(dns.qd[2].class == DNSClass::IN);
assert!(dns.rr.len() == 3);
assert!(
dns.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[0].type_ == DNSType::A);
assert!(dns.rr[0].class == DNSClass::IN);
assert!(dns.rr[0].rdata == [0x7f, 0x00, 0x00, 0x01]);
assert!(
dns.rr[1].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[1].type_ == DNSType::A);
assert!(dns.rr[1].class == DNSClass::IN);
assert!(dns.rr[1].rdata == [0x7f, 0x00, 0x00, 0x02]);
assert!(
dns.rr[2].name
== [
0x03, 0x77, 0x77, 0x77, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x33,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(dns.rr[2].type_ == DNSType::A);
assert!(dns.rr[2].class == DNSClass::IN);
assert!(dns.rr[2].rdata == [0x7f, 0x00, 0x00, 0x03]);
}
#[test]
fn reply_in_a() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let mut client_info = ClientInfo::new();
/* scapy: DNS(id=0x1337,
* qd=DNSQR(qname="www.example.com"))
**/
let payload = b"\x137\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let dns = DNSPacket::try_from(payload.to_vec()).unwrap();
for ip in [
Ipv4Addr::new(127, 0, 0, 1),
Ipv4Addr::new(0, 0, 0, 0),
Ipv4Addr::new(4, 3, 2, 1),
] {
client_info.ip.dst = Some(IpAddr::V4(ip));
let ans = if let Some(a) = dns.repl(&masscanned, &client_info, None) {
DNSPacket::try_from(a).unwrap()
} else {
panic!("expected a reply, got None");
};
assert!(ans.header.id == 0x1337);
assert!(ans.header._qr == true);
assert!(ans.header._opcode == 0);
assert!(ans.header._aa == true);
assert!(ans.header._tc == false);
assert!(ans.header._rd == dns.header._rd);
assert!(ans.header._ra == false);
assert!(ans.header._z == 0);
assert!(ans.header._rcode == 0);
assert!(ans.header.qdcount == 1);
assert!(ans.header.ancount == 1);
assert!(ans.header.nscount == 0);
assert!(ans.header.arcount == 0);
assert!(ans.qd.len() == 1);
assert!(
ans.qd[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(ans.qd[0].type_ == DNSType::A);
assert!(ans.qd[0].class == DNSClass::IN);
assert!(ans.rr.len() == 1);
assert!(
ans.rr[0].name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00
]
);
assert!(ans.rr[0].type_ == DNSType::A);
assert!(ans.rr[0].class == DNSClass::IN);
assert!(ans.rr[0].rdata == ip.octets());
}
}
}

337
src/proto/dns/query.rs Normal file
View file

@ -0,0 +1,337 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use super::cst::{DNSClass, DNSType};
use super::rr::DNSRR;
use std::convert::TryFrom;
use std::net::IpAddr;
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq)]
pub enum DNSQueryState {
Name,
Type,
Class,
End,
}
pub struct DNSQuery {
pub d: PacketDissector<DNSQueryState>,
/* RFC 1035 - Section 4.1.2 */
pub name: Vec<u8>,
_u_type: u16,
pub type_: DNSType,
_u_class: u16,
pub class: DNSClass,
}
impl TryFrom<Vec<u8>> for DNSQuery {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut query = DNSQuery::new();
for b in item {
query.parse(&b);
}
if query.d.state == DNSQueryState::End {
Ok(query)
} else {
Err("packet is incomplete")
}
}
}
impl From<&DNSQuery> for Vec<u8> {
fn from(item: &DNSQuery) -> Self {
let mut v = Vec::new();
/* name */
v.extend(&item.name);
/* type */
v.push(((u16::from(item.type_)) >> 8) as u8);
v.push(((u16::from(item.type_)) & 0xFF) as u8);
/* class */
v.push(((u16::from(item.class)) >> 8) as u8);
v.push(((u16::from(item.class)) & 0xFF) as u8);
/* return */
v
}
}
impl MPacket for DNSQuery {
fn new() -> Self {
DNSQuery {
d: PacketDissector::new(DNSQueryState::Name),
name: Vec::new(),
_u_type: 0,
type_: DNSType::NONE,
_u_class: 0,
class: DNSClass::NONE,
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSQueryState::Name => {
self.name.push(*byte);
if *byte == 0 {
self.d.next_state(DNSQueryState::Type);
}
}
DNSQueryState::Type => {
self._u_type = self.d.read_u16(byte, self._u_type, DNSQueryState::Class);
}
DNSQueryState::Class => {
self._u_class = self.d.read_u16(byte, self._u_class, DNSQueryState::End);
}
DNSQueryState::End => {}
}
/* we need this to be executed at the same call
* the state changes to End, hence it is not in the
* match structure
**/
if self.d.state == DNSQueryState::End {
self.type_ = DNSType::from(self._u_type);
self.class = DNSClass::from(self._u_class);
}
}
fn repl(
&self,
_masscanned: &Masscanned,
client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
match self.class {
DNSClass::IN => {
match self.type_ {
DNSType::A => {
let mut rr = DNSRR::new();
/* copy request */
for b in &self.name {
rr.name.push(*b);
}
rr.type_ = DNSType::A;
rr.class = DNSClass::IN;
rr.ttl = 43200;
rr.rdata = match client_info.ip.dst {
Some(IpAddr::V4(ip)) => ip.octets().to_vec(),
Some(IpAddr::V6(_)) => Vec::new(),
None => Vec::new(),
};
rr.rdlen = rr.rdata.len() as u16;
Some(Vec::<u8>::from(&rr))
}
_ => None,
}
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pnet::util::MacAddr;
use std::net::{IpAddr, Ipv4Addr};
use std::str::FromStr;
use strum::IntoEnumIterator;
use crate::client::ClientInfoSrcDst;
use crate::logger::MetaLogger;
#[test]
fn parse_in_a_all() {
/* A */
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let qr = match DNSQuery::try_from(payload.to_vec()) {
Ok(_qr) => _qr,
Err(e) => panic!("error while parsing DNS query: {}", e),
};
assert!(
qr.name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00
]
);
assert!(qr.type_ == DNSType::A);
assert!(qr.class == DNSClass::IN);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* TXT */
let payload = b"\x07version\x04bind\x00\x00\x10\x00\x03";
let qr = match DNSQuery::try_from(payload.to_vec()) {
Ok(_qr) => _qr,
Err(e) => panic!("error while parsing DNS query: {}", e),
};
assert!(qr.type_ == DNSType::TXT);
assert!(qr.class == DNSClass::CH);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* KO */
let payload = b"xxx";
match DNSQuery::try_from(payload.to_vec()) {
Ok(_) => panic!("parsing should have failed"),
Err(_) => {}
}
}
#[test]
fn parse_in_a_byte_by_byte() {
/* A */
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.d.state == DNSQueryState::End);
assert!(
qr.name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00
]
);
assert!(qr.type_ == DNSType::A);
assert!(qr.class == DNSClass::IN);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* TXT */
let payload = b"\x07version\x04bind\x00\x00\x10\x00\x03";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.d.state == DNSQueryState::End);
assert!(qr.type_ == DNSType::TXT);
assert!(qr.class == DNSClass::CH);
assert!(Vec::<u8>::from(&qr) == payload.to_vec());
/* KO */
let payload = b"xxx";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.d.state != DNSQueryState::End);
}
#[test]
fn reply_in_a() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let ip_src = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let ip_dst = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2));
let client_info = ClientInfo {
mac: ClientInfoSrcDst {
src: None,
dst: None,
},
ip: ClientInfoSrcDst {
src: Some(ip_src),
dst: Some(ip_dst),
},
transport: None,
port: ClientInfoSrcDst {
src: None,
dst: None,
},
cookie: None,
};
/* TXT */
let payload = b"\x07version\x04bind\x00\x00\x10\x00\x03";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(qr.type_ == DNSType::TXT);
assert!(qr.class == DNSClass::CH);
/* A */
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01";
let mut qr = DNSQuery::new();
for b in payload {
qr.parse(b);
}
assert!(
qr.name
== [
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00
]
);
assert!(qr.type_ == DNSType::A);
assert!(qr.class == DNSClass::IN);
let rr_raw = match qr.repl(&masscanned, &client_info, None) {
None => {
panic!()
}
Some(r) => r,
};
let mut rr = DNSRR::new();
for b in rr_raw {
rr.parse(&b);
}
assert!(rr.name == qr.name);
assert!(rr.type_ == DNSType::A);
assert!(rr.class == DNSClass::IN);
assert!(rr.ttl == 43200);
assert!(rr.rdata == [127, 0, 0, 2]);
}
#[test]
fn repl() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let client_info = ClientInfo::new();
/* exhaustive tests */
let supported: Vec<(DNSClass, DNSType)> = vec![(DNSClass::IN, DNSType::A)];
let mut qd = DNSQuery::new();
qd.name = vec![
0x03, 0x77, 0x77, 0x77, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63,
0x6f, 0x6d, 0x00,
];
for c in DNSClass::iter() {
qd.class = c;
for t in DNSType::iter() {
qd.type_ = t;
if supported.contains(&(c, t)) {
if qd.repl(&masscanned, &client_info, None) == None {
panic!("expected reply, got None");
}
} else {
if qd.repl(&masscanned, &client_info, None) != None {
panic!("expected no reply, got one for {:?}, {:?}", c, t);
}
}
}
}
}
}

251
src/proto/dns/rr.rs Normal file
View file

@ -0,0 +1,251 @@
// This file is part of masscanned.
// Copyright 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use super::cst::{DNSClass, DNSType};
use std::convert::TryFrom;
use crate::proto::dissector::{MPacket, PacketDissector};
use crate::proto::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
#[derive(PartialEq, Debug)]
pub enum DNSRRState {
Name,
Type,
Class,
TTL,
RDLength,
RData,
End,
}
pub struct DNSRR {
pub d: PacketDissector<DNSRRState>,
/* RFC 1035 - Section 3.2.1 */
pub name: Vec<u8>,
_u_type: u16,
pub type_: DNSType,
_u_class: u16,
pub class: DNSClass,
pub ttl: u32,
pub rdlen: u16,
pub rdata: Vec<u8>,
}
impl From<&DNSRR> for Vec<u8> {
fn from(item: &DNSRR) -> Self {
/* CAUTION: for the rdlen field:
* - if item.rdlen is not 0, its value is packed
* - if item.rdlen = 0, then the length of item.rdata is used instead
*/
let mut v = Vec::new();
/* name */
for b in &item.name {
v.push(b.clone());
}
/* type */
let type_: u16 = item.type_.into();
v.push((type_ >> 8) as u8);
v.push((type_ & 0xFF) as u8);
/* class */
let class: u16 = item.class.into();
v.push((class >> 8) as u8);
v.push((class & 0xFF) as u8);
/* ttl */
v.push((item.ttl >> 24) as u8);
v.push((item.ttl >> 16) as u8);
v.push((item.ttl >> 8) as u8);
v.push((item.ttl & 0xFF) as u8);
/* rdlen */
let rdlen = if item.rdlen == 0 {
item.rdata.len() as u16
} else {
item.rdlen
};
v.push((rdlen >> 8) as u8);
v.push((rdlen & 0xFF) as u8);
/* rdata */
for b in &item.rdata {
v.push(b.clone());
}
v
}
}
impl TryFrom<Vec<u8>> for DNSRR {
type Error = &'static str;
fn try_from(item: Vec<u8>) -> Result<Self, Self::Error> {
let mut rr = DNSRR::new();
for b in item {
rr.parse(&b);
}
if rr.d.state == DNSRRState::End {
Ok(rr)
} else {
Err("packet is incomplete")
}
}
}
impl MPacket for DNSRR {
fn new() -> Self {
DNSRR {
d: PacketDissector::new(DNSRRState::Name),
name: Vec::new(),
_u_type: 0,
type_: DNSType::NONE,
_u_class: 0,
class: DNSClass::NONE,
rdlen: 0,
ttl: 0,
rdata: Vec::new(),
}
}
fn parse(&mut self, byte: &u8) {
match self.d.state {
DNSRRState::Name => {
self.name.push(*byte);
if *byte == 0 {
self.d.next_state(DNSRRState::Type);
}
}
DNSRRState::Type => {
self._u_type = self.d.read_u16(byte, self._u_type, DNSRRState::Class);
}
DNSRRState::Class => {
self._u_class = self.d.read_u16(byte, self._u_class, DNSRRState::TTL);
}
DNSRRState::TTL => {
self.ttl = self.d.read_u32(byte, self.ttl, DNSRRState::RDLength);
}
DNSRRState::RDLength => {
self.rdlen = self.d.read_u16(byte, self.rdlen, DNSRRState::RData);
/* when read the rdlen, check if len is 0 */
if self.d.state == DNSRRState::RData && self.rdlen == 0 {
self.d.state = DNSRRState::End;
}
}
DNSRRState::RData => {
self.rdata.push(*byte);
if self.rdata.len() == self.rdlen as usize {
self.d.next_state(DNSRRState::End);
}
}
DNSRRState::End => {}
}
/* we need this to be executed at the same call
* the state changes to End, hence it is not in the
* match structure
**/
if self.d.state == DNSRRState::End {
self.type_ = DNSType::from(self._u_type);
self.class = DNSClass::from(self._u_class);
}
}
fn repl(
&self,
_masscanned: &Masscanned,
_client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build() {
let mut rr = DNSRR::new();
rr.name = b"\x03www\x07example\x03com\x00".to_vec();
rr.class = DNSClass::IN;
rr.type_ = DNSType::A;
rr.ttl = 1234;
rr.rdlen = 4;
rr.rdata = b"\x7f\x00\x00\x01".to_vec();
assert!(Vec::<u8>::from(&rr) == b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x04\xd2\x00\x04\x7f\x00\x00\x01");
}
#[test]
fn parse_all() {
/*
* raw(DNSRR(rrname="www.example.com", rdata="127.0.0.1"))
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01";
let rr = match DNSRR::try_from(payload.to_vec()) {
Ok(r) => r,
Err(e) => panic!("error while parsing DNS RR: {}", e),
};
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"\x7f\x00\x00\x01");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
/*
* empty data
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00";
let rr = match DNSRR::try_from(payload.to_vec()) {
Ok(r) => r,
Err(e) => panic!("error while parsing DNS RR: {}", e),
};
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
}
#[test]
fn parse_byte_by_byte() {
/*
* raw(DNSRR(rrname="www.example.com", rdata="127.0.0.1"))
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x7f\x00\x00\x01";
let mut rr = DNSRR::new();
for b in payload {
assert!(rr.d.state != DNSRRState::End);
rr.parse(b);
}
assert!(rr.d.state == DNSRRState::End);
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"\x7f\x00\x00\x01");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
/*
* empty data
*/
let payload = b"\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00";
let mut rr = DNSRR::new();
for b in payload {
assert!(rr.d.state != DNSRRState::End);
rr.parse(b);
}
assert!(rr.name == b"\x03www\x07example\x03com\x00");
assert!(rr.class == DNSClass::IN);
assert!(rr.type_ == DNSType::A);
assert!(rr.rdata == b"");
assert!(Vec::<u8>::from(&rr) == payload.to_vec());
}
}

59
src/proto/ghost.rs Normal file
View file

@ -0,0 +1,59 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use log::*;
use std::io::Write;
use flate2::write::ZlibEncoder;
use flate2::Compression;
use crate::client::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned;
pub const GHOST_PATTERN_SIGNATURE: &[u8; 5] = b"Gh0st";
pub fn repl<'a>(
_data: &'a [u8],
_masscanned: &Masscanned,
_client_info: &mut ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
debug!("receiving Gh0st data, sending one null byte payload");
// Packet structure:
// GHOST_PATTERN_SIGNATURE + [ packet size ] + [ uncompressed payload size ] + payload
let mut result = GHOST_PATTERN_SIGNATURE.to_vec();
let uncompressed_data = b"\x00";
let mut compressed_data = ZlibEncoder::new(Vec::new(), Compression::default());
compressed_data
.write_all(uncompressed_data)
.expect("Ghost: cannot decompress payload");
let mut compressed_data = compressed_data
.finish()
.expect("Ghost: cannot decompress payload");
let mut packet_len = compressed_data.len() + GHOST_PATTERN_SIGNATURE.len() + 4 * 2;
for _ in 0..4 {
result.push((packet_len % 256) as u8);
packet_len /= 256;
}
let mut uncompressed_len = uncompressed_data.len();
for _ in 0..4 {
result.push((uncompressed_len % 256) as u8);
uncompressed_len /= 256;
}
result.append(&mut compressed_data);
Some(result)
}

View file

@ -21,6 +21,7 @@ use lazy_static::lazy_static;
use std::str; use std::str;
use crate::client::ClientInfo; use crate::client::ClientInfo;
use crate::proto::{ProtocolState as GenericProtocolState, TCPControlBlock};
use crate::smack::{ use crate::smack::{
Smack, SmackFlags, BASE_STATE, NO_MATCH, SMACK_CASE_INSENSITIVE, UNANCHORED_STATE, Smack, SmackFlags, BASE_STATE, NO_MATCH, SMACK_CASE_INSENSITIVE, UNANCHORED_STATE,
}; };
@ -62,7 +63,7 @@ const HTTP_STATE_CONTENT: usize = 64;
const HTTP_STATE_FAIL: usize = 0xFFFF; const HTTP_STATE_FAIL: usize = 0xFFFF;
struct ProtocolState { pub struct ProtocolState {
state: usize, state: usize,
state_bis: usize, state_bis: usize,
smack_state: usize, smack_state: usize,
@ -223,15 +224,39 @@ pub fn repl<'a>(
data: &'a [u8], data: &'a [u8],
_masscanned: &Masscanned, _masscanned: &Masscanned,
_client_info: &ClientInfo, _client_info: &ClientInfo,
tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> { ) -> Option<Vec<u8>> {
debug!("receiving HTTP data"); debug!("receiving HTTP data");
let mut pstate = ProtocolState::new(); let mut state = ProtocolState::new();
let mut pstate = {
if let Some(t) = tcb {
match t.proto_state {
None => t.proto_state = Some(GenericProtocolState::HTTP(ProtocolState::new())),
Some(GenericProtocolState::HTTP(_)) => {}
_ => {
panic!()
}
};
if let Some(GenericProtocolState::HTTP(p)) = &mut t.proto_state {
p
} else {
panic!();
}
} else {
&mut state
}
};
http_parse(&mut pstate, data); http_parse(&mut pstate, data);
if pstate.state == HTTP_STATE_FAIL { if pstate.state == HTTP_STATE_FAIL {
debug!("data in not correctly formatted - not responding"); debug!("data in not correctly formatted - not responding");
debug!("pstate: {}", pstate.state); debug!("pstate: {}", pstate.state);
return None; return None;
} }
/* if not in CONTENT state, not responding yet (it means the client
* has not finished sending headers yet) */
if pstate.state != HTTP_STATE_CONTENT {
return None;
}
let content = "\ let content = "\
<html> <html>
<head><title>401 Authorization Required</title></head> <head><title>401 Authorization Required</title></head>
@ -267,6 +292,10 @@ WWW-Authenticate: Basic realm=\"Access to admin page\"
Some(repl_data) Some(repl_data)
} }
#[cfg(test)]
mod tests {
use super::*;
#[test] #[test]
fn test_http_verb() { fn test_http_verb() {
/* all at once */ /* all at once */
@ -386,3 +415,4 @@ fn test_http_request_no_field() {
http_parse(&mut pstate, crlf); http_parse(&mut pstate, crlf);
assert!(pstate.state == HTTP_STATE_CONTENT); assert!(pstate.state == HTTP_STATE_CONTENT);
} }
}

View file

@ -17,13 +17,15 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::*; use log::*;
use pnet::packet::ip::IpNextHeaderProtocols; use pnet::packet::ip::IpNextHeaderProtocols;
use std::collections::HashMap; use std::convert::TryFrom;
use std::sync::Mutex;
use crate::client::ClientInfo; use crate::client::ClientInfo;
use crate::smack::{Smack, SmackFlags, BASE_STATE, NO_MATCH, SMACK_CASE_SENSITIVE}; use crate::smack::{Smack, SmackFlags, BASE_STATE, NO_MATCH, SMACK_CASE_SENSITIVE};
use crate::Masscanned; use crate::Masscanned;
mod dns;
use dns::DNSPacket;
mod http; mod http;
use http::HTTP_VERBS; use http::HTTP_VERBS;
@ -31,19 +33,38 @@ mod stun;
use stun::{STUN_PATTERN_CHANGE_REQUEST, STUN_PATTERN_EMPTY, STUN_PATTERN_MAGIC}; use stun::{STUN_PATTERN_CHANGE_REQUEST, STUN_PATTERN_EMPTY, STUN_PATTERN_MAGIC};
mod ssh; mod ssh;
use ssh::SSH_PATTERN_CLIENT_PROTOCOL; use ssh::{SSH_PATTERN_CLIENT_PROTOCOL_1, SSH_PATTERN_CLIENT_PROTOCOL_2};
mod ghost;
use ghost::GHOST_PATTERN_SIGNATURE;
mod rpc;
use rpc::{RPC_CALL_TCP, RPC_CALL_UDP};
mod smb;
use smb::{SMB1_PATTERN_MAGIC, SMB2_PATTERN_MAGIC};
mod dissector;
use dissector::MPacket;
// mod dissector;
// pub use dissector::PacketDissector;
//
mod tcb;
pub use tcb::{add_tcb, get_tcb, is_tcb_set, ProtocolState, TCPControlBlock};
const PROTO_NONE: usize = 0;
const PROTO_HTTP: usize = 1; const PROTO_HTTP: usize = 1;
const PROTO_STUN: usize = 2; const PROTO_STUN: usize = 2;
const PROTO_SSH: usize = 3; const PROTO_SSH: usize = 3;
const PROTO_GHOST: usize = 4;
struct TCPControlBlock { const PROTO_RPC_TCP: usize = 5;
proto_state: usize, const PROTO_RPC_UDP: usize = 6;
} const PROTO_SMB1: usize = 7;
const PROTO_SMB2: usize = 8;
lazy_static! { lazy_static! {
static ref PROTO_SMACK: Smack = proto_init(); static ref PROTO_SMACK: Smack = proto_init();
static ref CONTABLE: Mutex<HashMap<u32, TCPControlBlock>> = Mutex::new(HashMap::new());
} }
fn proto_init() -> Smack { fn proto_init() -> Smack {
@ -72,10 +93,40 @@ fn proto_init() -> Smack {
SmackFlags::ANCHOR_BEGIN | SmackFlags::ANCHOR_END | SmackFlags::WILDCARDS, SmackFlags::ANCHOR_BEGIN | SmackFlags::ANCHOR_END | SmackFlags::WILDCARDS,
); );
smack.add_pattern( smack.add_pattern(
SSH_PATTERN_CLIENT_PROTOCOL, SSH_PATTERN_CLIENT_PROTOCOL_2,
PROTO_SSH, PROTO_SSH,
SmackFlags::ANCHOR_BEGIN, SmackFlags::ANCHOR_BEGIN,
); );
smack.add_pattern(
SSH_PATTERN_CLIENT_PROTOCOL_1,
PROTO_SSH,
SmackFlags::ANCHOR_BEGIN,
);
smack.add_pattern(
GHOST_PATTERN_SIGNATURE,
PROTO_GHOST,
SmackFlags::ANCHOR_BEGIN,
);
smack.add_pattern(
RPC_CALL_TCP,
PROTO_RPC_TCP,
SmackFlags::ANCHOR_BEGIN | SmackFlags::WILDCARDS,
);
smack.add_pattern(
RPC_CALL_UDP,
PROTO_RPC_UDP,
SmackFlags::ANCHOR_BEGIN | SmackFlags::WILDCARDS,
);
smack.add_pattern(
SMB1_PATTERN_MAGIC,
PROTO_SMB1,
SmackFlags::ANCHOR_BEGIN | SmackFlags::WILDCARDS,
);
smack.add_pattern(
SMB2_PATTERN_MAGIC,
PROTO_SMB2,
SmackFlags::ANCHOR_BEGIN | SmackFlags::WILDCARDS,
);
smack.compile(); smack.compile();
smack smack
} }
@ -84,51 +135,60 @@ pub fn repl<'a>(
data: &'a [u8], data: &'a [u8],
masscanned: &Masscanned, masscanned: &Masscanned,
mut client_info: &mut ClientInfo, mut client_info: &mut ClientInfo,
mut tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> { ) -> Option<Vec<u8>> {
debug!("packet payload: {:?}", data); debug!("packet payload: {:?}", data);
let mut id; let mut id;
if client_info.transport == Some(IpNextHeaderProtocols::Tcp) && client_info.cookie == None { if client_info.transport == Some(IpNextHeaderProtocols::Tcp) && client_info.cookie == None {
error!("Unexpected empty cookie"); error!("Unexpected empty cookie");
return None; return None;
} else if client_info.cookie != None { } else if let Some(t) = &mut tcb {
/* proto over TCP */ /* proto over TCP */
let cookie = client_info.cookie.unwrap();
let mut ct = CONTABLE.lock().unwrap();
if !ct.contains_key(&cookie) {
ct.insert(
cookie,
TCPControlBlock {
proto_state: BASE_STATE,
},
);
}
let mut i = 0; let mut i = 0;
let mut tcb = ct.get_mut(&cookie).unwrap(); if t.proto_id == PROTO_NONE {
let mut state = tcb.proto_state; let mut state = t.smack_state;
id = PROTO_SMACK.search_next(&mut state, &data.to_vec(), &mut i); t.proto_id = PROTO_SMACK.search_next(&mut state, data, &mut i);
tcb.proto_state = state; t.smack_state = state;
}
id = t.proto_id;
} else { } else {
/* proto over else (e.g., UDP) */ /* proto over else (e.g., UDP) */
let mut i = 0; let mut i = 0;
let mut state = BASE_STATE; let mut state = BASE_STATE;
id = PROTO_SMACK.search_next(&mut state, &data.to_vec(), &mut i); id = PROTO_SMACK.search_next(&mut state, data, &mut i);
/* because we are not over TCP, we can afford to assume end of pattern */ /* because we are not over TCP, we can afford to assume end of pattern */
if id == NO_MATCH { if id == NO_MATCH {
id = PROTO_SMACK.search_next_end(&mut state); id = PROTO_SMACK.search_next_end(&mut state);
} }
/* still no match: let us try to parse packet with protocoles
* that are not matched with a regex */
if id == NO_MATCH {
/* try to parse data as a DNS packet */
if let Ok(dns) = DNSPacket::try_from(data.to_vec()) {
if let Some(r) = dns.repl(&masscanned, &client_info, None) {
return Some(r);
}
}
}
} }
/* proto over else (e.g., UDP) */ /* proto over else (e.g., UDP) */
if id == PROTO_HTTP { match id {
return http::repl(data, masscanned, client_info); PROTO_HTTP => http::repl(data, masscanned, client_info, tcb),
} else if id == PROTO_STUN { PROTO_STUN => stun::repl(data, masscanned, &mut client_info, tcb),
return stun::repl(data, masscanned, &mut client_info); PROTO_SSH => ssh::repl(data, masscanned, &mut client_info, tcb),
} else if id == PROTO_SSH { PROTO_GHOST => ghost::repl(data, masscanned, &mut client_info, tcb),
return ssh::repl(data, masscanned, &mut client_info); PROTO_RPC_TCP => rpc::repl_tcp(data, masscanned, &mut client_info, tcb),
} else { PROTO_RPC_UDP => rpc::repl_udp(data, masscanned, &mut client_info, tcb),
debug!("id: {}", id); PROTO_SMB1 => smb::repl_smb1(data, masscanned, &mut client_info, tcb),
PROTO_SMB2 => smb::repl_smb2(data, masscanned, &mut client_info, tcb),
_ => {
if let Some(t) = &mut tcb {
t.proto_id = PROTO_NONE;
} }
None None
} }
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -139,6 +199,8 @@ mod tests {
use pnet::util::MacAddr; use pnet::util::MacAddr;
use crate::logger::MetaLogger;
#[test] #[test]
fn test_proto_dispatch_stun() { fn test_proto_dispatch_stun() {
let mut client_info = ClientInfo::new(); let mut client_info = ClientInfo::new();
@ -153,7 +215,9 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
/***** TEST STUN - MAGIC *****/ /***** TEST STUN - MAGIC *****/
/* test payload is: /* test payload is:
@ -164,7 +228,7 @@ mod tests {
*/ */
let payload = let payload =
b"\x00\x01\x00\x00\x21\x12\xa4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; b"\x00\x01\x00\x00\x21\x12\xa4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let _stun_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info) { let _stun_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got nothing"); panic!("expected an answer, got nothing");
@ -178,7 +242,7 @@ mod tests {
*/ */
let payload = let payload =
b"\x00\x01\x00\x00\xaa\xbb\xcc\xdd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; b"\x00\x01\x00\x00\xaa\xbb\xcc\xdd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let _stun_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info) { let _stun_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got nothing"); panic!("expected an answer, got nothing");
@ -191,7 +255,7 @@ mod tests {
*/ */
let payload = let payload =
b"\x00\x01\x00\x08\x01\xdb\xd4]4\x9f\xe2RQ\x19\x05,\x93\x14f4\x00\x03\x00\x04\x00\x00\x00\x00"; b"\x00\x01\x00\x08\x01\xdb\xd4]4\x9f\xe2RQ\x19\x05,\x93\x14f4\x00\x03\x00\x04\x00\x00\x00\x00";
let _stun_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info) { let _stun_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got nothing"); panic!("expected an answer, got nothing");
@ -212,28 +276,123 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
/***** TEST SSH *****/ /***** TEST SSH *****/
let payloads = [ let payloads = [
"SSH-2.0-PUTTY", "SSH-2.0-PUTTY\r\n",
"SSH-2.0-Go", "SSH-2.0-Go\r\n",
"SSH-2.0-libssh2_1.4.3", "SSH-2.0-libssh2_1.4.3\r\n",
"SSH-2.0-PuTTY", "SSH-2.0-PuTTY\r\n",
"SSH-2.0-AsyncSSH_2.1.0", "SSH-2.0-AsyncSSH_2.1.0\r\n",
"SSH-2.0-libssh2_1.9.0", "SSH-2.0-libssh2_1.9.0\r\n",
"SSH-2.0-libssh2_1.7.0", "SSH-2.0-libssh2_1.7.0\r\n",
"SSH-2.0-8.35 FlowSsh: FlowSshNet_SftpStress54.38.116.473", "SSH-2.0-8.35 FlowSsh: FlowSshNet_SftpStress54.38.116.473\r\n",
"SSH-2.0-libssh_0.9.5", "SSH-2.0-libssh_0.9.5\r\n",
"SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u3", "SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u3\r\n",
"SSH-1.99-Cisco-1.25\r\n",
]; ];
for payload in payloads.iter() { for payload in payloads.iter() {
let _ssh_resp = if let Some(r) = repl(payload.as_bytes(), &masscanned, &mut client_info) let _ssh_resp =
{ if let Some(r) = repl(payload.as_bytes(), &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got nothing"); panic!("expected an answer, got nothing");
}; };
} }
} }
#[test]
fn test_proto_dispatch_ghost() {
let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
client_info.port.src = Some(65000);
let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
let mut ips = HashSet::new();
ips.insert(IpAddr::V4(masscanned_ip_addr));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
/***** TEST GHOST *****/
let payloads = [
b"Gh0st\xad\x00\x00\x00\xe0\x00\x00\x00x\x9cKS``\x98\xc3\xc0\xc0\xc0\x06\xc4\x8c@\xbcQ\x96\x81\x81\tH\x07\xa7\x16\x95e&\xa7*\x04$&g+\x182\x94\xf6\xb000\xac\xa8rc\x00\x01\x11\xa0\x82\x1f\\`&\x83\xc7K7\x86\x19\xe5n\x0c9\x95n\x0c;\x84\x0f3\xac\xe8sch\xa8^\xcf4'J\x97\xa9\x82\xe30\xc3\x91h]&\x90\xf8\xce\x97S\xcbA4L?2=\xe1\xc4\x92\x86\x0b@\xf5`\x0cT\x1f\xae\xaf]\nr\x0b\x03#\xa3\xdc\x02~\x06\x86\x03+\x18m\xc2=\xfdtC,C\xfdL<<==\\\x9d\x19\x88\x00\xe5 \x02\x00T\xf5+\\"
];
for payload in payloads.iter() {
let _ghost_resp =
if let Some(r) = repl(&payload.to_vec(), &masscanned, &mut client_info, None) {
r
} else {
panic!("expected an answer, got nothing");
};
}
}
#[test]
fn test_proto_dispatch_http() {
/* ensure that HTTP FSM does not answer until completion of request
* (at least headers) */
let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
client_info.port.src = Some(65000);
let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
let mut ips = HashSet::new();
ips.insert(IpAddr::V4(masscanned_ip_addr));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
/***** TEST COMPLETE REQUEST *****/
let payload = b"GET / HTTP/1.1\r\n\r\n";
if let None = repl(&payload.to_vec(), &masscanned, &mut client_info, None) {
panic!("expected an answer, got nothing");
}
/***** TEST INCOMPLETE REQUEST *****/
let payload = b"GET / HTTP/1.1\r\n";
if let Some(_) = repl(&payload.to_vec(), &masscanned, &mut client_info, None) {
panic!("expected no answer, got one");
}
}
#[test]
fn dispatch_dns() {
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: None,
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let mut client_info = ClientInfo::new();
client_info.ip.dst = Some(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)));
let payloads = [
b"\x04\xd2\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x03www\x08example1\x03com\x00\x00\x01\x00\x01\x03www\x08example2\x03com\x00\x00\x01\x00\x01\x03www\x08example3\x03com\x00\x00\x01\x00\x01",
];
for payload in payloads.iter() {
let dns_resp =
if let Some(r) = repl(&payload.to_vec(), &masscanned, &mut client_info, None) {
r
} else {
panic!("expected an answer, got nothing");
};
if let Err(e) = DNSPacket::try_from(dns_resp) {
panic!("error trying to parse the DNS answer: {}", e);
}
}
}
} }

561
src/proto/rpc.rs Normal file
View file

@ -0,0 +1,561 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use log::warn;
use std::convert::TryInto;
use std::net::IpAddr;
use crate::client::ClientInfo;
use crate::proto::{ProtocolState as GenericProtocolState, TCPControlBlock};
use crate::Masscanned;
// last fragment (1 bit) + fragment len (31 bits) / length XID (random) / message type: call (0) / RPC version (0-255) / Program: Portmap (99840 - 100095) / Program version (*, random versions used, see below) / / Procedure: ??? (0-255)
pub const RPC_CALL_TCP: &[u8; 28] =
b"********\x00\x00\x00\x00\x00\x00\x00*\x00\x01\x86*****\x00\x00\x00*";
// UDP: last fragment and fragment len are missing
pub const RPC_CALL_UDP: &[u8; 24] =
b"****\x00\x00\x00\x00\x00\x00\x00*\x00\x01\x86*****\x00\x00\x00*";
#[derive(Debug)]
enum RpcState {
Frag,
Xid,
MessageType,
RpcVersion,
Program,
ProgramVersion,
Procedure,
CredsFlavor,
CredsLen,
Creds,
VerifFlavor,
VerifLen,
Verif,
End,
}
#[derive(Debug)]
pub struct ProtocolState {
state: RpcState,
last_frag: bool,
frag_len: u32,
xid: u32,
message_type: u32,
rpc_version: u32,
program: u32,
prog_version: u32,
procedure: u32,
creds_flavor: u32,
creds_data: Vec<u8>,
verif_flavor: u32,
verif_data: Vec<u8>,
payload: Vec<u8>,
cur_len: u32,
data_len: u32,
}
struct Rpcb {
program: u32,
version: u32,
netid: String,
addr: String,
port: u16,
owner: String,
}
impl ProtocolState {
fn new() -> Self {
ProtocolState {
state: RpcState::Frag,
last_frag: false,
frag_len: 0,
xid: 0,
message_type: 0,
rpc_version: 0,
program: 0,
prog_version: 0,
procedure: 0,
creds_flavor: 0,
creds_data: Vec::<u8>::new(),
verif_flavor: 0,
verif_data: Vec::<u8>::new(),
payload: Vec::<u8>::new(),
cur_len: 0,
data_len: 0,
}
}
}
fn read_u32(pstate: &mut ProtocolState, byte: u8, value: u32, next_state: RpcState) -> u32 {
pstate.cur_len += 1;
if pstate.cur_len == 4 {
pstate.state = next_state;
pstate.cur_len = 0;
}
value * 256 + byte as u32
}
fn read_string(pstate: &mut ProtocolState, next_state: RpcState) {
pstate.data_len -= 1;
if pstate.data_len == 0 {
pstate.state = next_state;
}
}
fn rpc_parse(pstate: &mut ProtocolState, data: &[u8]) {
for byte in data {
match pstate.state {
RpcState::Frag => {
if pstate.cur_len == 0 {
match byte & 128 {
0 => pstate.last_frag = false,
_ => pstate.last_frag = true,
};
pstate.frag_len = (*byte & 127) as u32;
} else {
pstate.frag_len = *byte as u32;
}
pstate.cur_len += 1;
if pstate.cur_len == 4 {
pstate.state = RpcState::Xid;
pstate.cur_len = 0;
}
}
RpcState::Xid => {
pstate.xid = read_u32(pstate, *byte, pstate.xid, RpcState::MessageType)
}
RpcState::MessageType => {
pstate.message_type =
read_u32(pstate, *byte, pstate.message_type, RpcState::RpcVersion)
}
RpcState::RpcVersion => {
pstate.rpc_version = read_u32(pstate, *byte, pstate.rpc_version, RpcState::Program)
}
RpcState::Program => {
pstate.program = read_u32(pstate, *byte, pstate.program, RpcState::ProgramVersion)
}
RpcState::ProgramVersion => {
pstate.prog_version =
read_u32(pstate, *byte, pstate.prog_version, RpcState::Procedure)
}
RpcState::Procedure => {
pstate.procedure = read_u32(pstate, *byte, pstate.procedure, RpcState::CredsFlavor)
}
RpcState::CredsFlavor => {
pstate.creds_flavor =
read_u32(pstate, *byte, pstate.creds_flavor, RpcState::CredsLen)
}
RpcState::CredsLen => {
pstate.data_len = read_u32(pstate, *byte, pstate.data_len, RpcState::Creds);
if matches!(pstate.state, RpcState::Creds) && pstate.data_len == 0 {
pstate.state = RpcState::VerifFlavor
}
}
RpcState::Creds => {
pstate.creds_data.push(*byte);
read_string(pstate, RpcState::VerifFlavor)
}
RpcState::VerifFlavor => {
pstate.verif_flavor =
read_u32(pstate, *byte, pstate.verif_flavor, RpcState::VerifLen)
}
RpcState::VerifLen => {
pstate.data_len = read_u32(pstate, *byte, pstate.data_len, RpcState::Verif);
if matches!(pstate.state, RpcState::Verif) && pstate.cur_len == 0 {
pstate.state = RpcState::End
}
}
RpcState::Verif => {
pstate.verif_data.push(*byte);
read_string(pstate, RpcState::End)
}
RpcState::End => {
pstate.payload.push(*byte);
}
};
}
}
fn get_nth_byte(value: u32, nth: u8) -> u8 {
let shift = 8 * (3 - nth);
((value & (0xff << shift)) >> shift).try_into().unwrap()
}
fn push_u32(buffer: &mut Vec<u8>, data: u32) {
for i in 0..4 {
buffer.push(get_nth_byte(data, i));
}
}
fn push_string_pad(buffer: &mut Vec<u8>, data: String) {
let len: u32 = data.len().try_into().unwrap();
push_u32(buffer, len);
buffer.append(&mut data.as_bytes().to_vec());
if len % 4 != 0 {
for _ in 0..(4 - (len % 4)) {
buffer.append(&mut b"\x00".to_vec());
}
}
}
fn build_repl_portmap(pstate: &mut ProtocolState, client_info: &ClientInfo) -> Vec<u8> {
let mut resp = Vec::<u8>::new();
match pstate.procedure {
// 0 => {}
3 => {
// getaddr / getport
// accepted state: 0 (RPC executed successfully)
resp.extend([0, 0, 0, 0]);
let localport = client_info.port.dst.unwrap();
match pstate.prog_version {
2 => {
push_u32(&mut resp, localport as u32);
}
3 | 4 => {
let addr = format!(
"{}.{}.{}",
client_info.ip.dst.unwrap(),
localport >> 8,
localport % 256
);
push_string_pad(&mut resp, addr);
}
_ => panic!("Wrong RPC version"),
}
}
4 => {
// dump
// accepted state: 0 (RPC executed successfully)
resp.extend([0, 0, 0, 0]);
let localaddr = client_info.ip.dst.unwrap();
let localport = client_info.port.dst.unwrap();
let netid = match localaddr {
IpAddr::V4(_) => "tcp",
IpAddr::V6(_) => "tcp6",
};
for rpcb in [
Rpcb {
program: 100000,
version: 2,
netid: netid.to_string(),
addr: format!("{}", localaddr),
port: localport,
owner: "superuser".to_string(),
},
Rpcb {
program: 100000,
version: 3,
netid: netid.to_string(),
addr: format!("{}", localaddr),
port: localport,
owner: "superuser".to_string(),
},
Rpcb {
program: 100000,
version: 4,
netid: netid.to_string(),
addr: format!("{}", localaddr),
port: localport,
owner: "superuser".to_string(),
},
] {
resp.append(&mut b"\x00\x00\x00\x01".to_vec()); // value follows: yes
push_u32(&mut resp, rpcb.program);
push_u32(&mut resp, rpcb.version);
match pstate.prog_version {
2 => {
push_u32(
&mut resp,
match rpcb.netid.as_str() {
"tcp" => 6,
"tcp6" => 6,
"udp" => 17,
"udp6" => 17,
_ => 0,
},
);
push_u32(&mut resp, localport as u32);
}
3 | 4 => {
push_string_pad(&mut resp, rpcb.netid);
push_string_pad(
&mut resp,
format!("{}.{}.{}", rpcb.addr, rpcb.port >> 8, rpcb.port & 0xff),
);
push_string_pad(&mut resp, rpcb.owner);
}
_ => panic!("Wrong RPC version"),
}
}
resp.append(&mut b"\x00\x00\x00\x00".to_vec()); // value follows: no
}
_ => {
// accepted state: 5 (program can't support procedure)
resp.extend([0, 0, 0, 5]);
}
}
warn!(
"RPC: Portmap version {}, procedure {}",
pstate.prog_version, pstate.procedure
);
resp
}
fn build_repl_unknownprog(pstate: &mut ProtocolState, _client_info: &ClientInfo) -> Vec<u8> {
warn!(
"Unknown program {}, procedure {}: accepted state 1",
pstate.program, pstate.procedure
);
// accepted state: 1 (remote hasn't exported program)
vec![0, 0, 0, 1]
}
fn build_repl(pstate: &mut ProtocolState, client_info: &ClientInfo) -> Vec<u8> {
// TODO: test RPC versions, drop non calls?
let mut resp = Vec::<u8>::new();
push_u32(&mut resp, pstate.xid);
// message_type: 1 (reply)
// reply_state: 0 (accepted)
// verifier: 0 (auth null)
// verifier length: 0
resp.extend([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
if pstate.prog_version < 2 || pstate.prog_version > 4 {
/*
* Scanners (e.g., Nmap script rpc-grind) often use random
* values for program version to find out if a program is
* supported, so for any program, we answer with "remote can't
* support version" accepted state.
*/
// accepted state: 2 (remote can't support version)
// prog_version min: 2
// prog_version max: 4
let prog_version = match pstate.prog_version {
104316 => "104316 (Nmap probe TCP RPCCheck)".to_string(),
x => x.to_string(),
};
warn!(
"RPC: unsupported version {} for program {}",
prog_version, pstate.program
);
resp.extend([0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 4]);
} else if pstate.procedure == 0 {
/*
* RPC clients (e.g., Linux kernel NFS client, rpcbind CLI
* tool) would often send a NULL procedure (0) call before any
* real operation .
*/
// accepted state: 0 (RPC executed successfully)
warn!("RPC: NULL procedure call for program {}", pstate.program);
resp.extend([0, 0, 0, 0]);
} else {
let mut specif_resp = match pstate.program {
100000 => build_repl_portmap(pstate, client_info),
_ => build_repl_unknownprog(pstate, client_info),
};
resp.append(&mut specif_resp);
}
resp
}
pub fn repl_tcp<'a>(
data: &'a [u8],
_masscanned: &Masscanned,
client_info: &ClientInfo,
tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
let mut state = ProtocolState::new();
let mut pstate = {
if let Some(t) = tcb {
match t.proto_state {
None => t.proto_state = Some(GenericProtocolState::RPC(ProtocolState::new())),
Some(GenericProtocolState::RPC(_)) => {}
_ => {
panic!()
}
};
if let Some(GenericProtocolState::RPC(p)) = &mut t.proto_state {
p
} else {
panic!();
}
} else {
&mut state
}
};
rpc_parse(&mut pstate, data);
// warn!("RPC {:#?}", pstate);
let resp = match pstate.state {
RpcState::End => Some(build_repl(pstate, client_info)),
_ => None,
};
match resp {
Some(mut resp) => {
let length: u32 = resp.len().try_into().unwrap();
let mut final_resp = Vec::<u8>::new();
for i in 0..4 {
match i {
0 => final_resp.push(get_nth_byte(length, i) | 0x80),
_ => final_resp.push(get_nth_byte(length, i)),
};
}
final_resp.append(&mut resp);
Some(final_resp)
}
_ => None,
}
}
pub fn repl_udp<'a>(
data: &'a [u8],
_masscanned: &Masscanned,
client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> {
let mut pstate = ProtocolState::new();
pstate.state = RpcState::Xid;
pstate.last_frag = true;
pstate.frag_len = data.len().try_into().unwrap();
rpc_parse(&mut pstate, data);
// warn!("RPC {:#?}", pstate);
match pstate.state {
RpcState::End => Some(build_repl(&mut pstate, client_info)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::ClientInfoSrcDst;
use std::net::Ipv4Addr;
const CLIENT_INFO: ClientInfo = ClientInfo {
mac: ClientInfoSrcDst {
src: None,
dst: None,
},
ip: ClientInfoSrcDst {
src: Some(IpAddr::V4(Ipv4Addr::new(192, 0, 0, 0))),
dst: Some(IpAddr::V4(Ipv4Addr::new(192, 0, 0, 1))),
},
transport: None,
port: ClientInfoSrcDst {
src: Some(12345),
dst: Some(111),
},
cookie: None,
};
#[test]
fn test_probe_nmap() {
let mut pstate = ProtocolState::new();
rpc_parse(&mut pstate, b"\x80\x00\x00\x28\x72\xfe\x1d\x13\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x86\xa0\x00\x01\x97\x7c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
assert!(matches!(pstate.state, RpcState::End));
assert!(pstate.xid == 0x72fe1d13);
assert!(pstate.rpc_version == 2);
assert!(pstate.program == 100000);
assert!(pstate.prog_version == 104316);
assert!(pstate.procedure == 0);
assert!(pstate.creds_flavor == 0);
assert!(pstate.creds_data.len() == 0);
assert!(pstate.verif_flavor == 0);
assert!(pstate.verif_data.len() == 0);
let resp = build_repl(&mut pstate, &CLIENT_INFO);
assert!(resp == b"\x72\xfe\x1d\x13\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x04");
}
#[test]
fn test_probe_nmap_udp() {
let mut pstate = ProtocolState::new();
pstate.state = RpcState::Xid;
rpc_parse(&mut pstate, b"\x72\xfe\x1d\x13\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x86\xa0\x00\x01\x97\x7c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
assert!(matches!(pstate.state, RpcState::End));
assert!(pstate.xid == 0x72fe1d13);
assert!(pstate.rpc_version == 2);
assert!(pstate.program == 100000);
assert!(pstate.prog_version == 104316);
assert!(pstate.procedure == 0);
assert!(pstate.creds_flavor == 0);
assert!(pstate.creds_data.len() == 0);
assert!(pstate.verif_flavor == 0);
assert!(pstate.verif_data.len() == 0);
let resp = build_repl(&mut pstate, &CLIENT_INFO);
assert!(resp == b"\x72\xfe\x1d\x13\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x04");
}
#[test]
fn test_probe_nmap_split1() {
let mut pstate = ProtocolState::new();
for byte in b"\x80\x00\x00\x28\x72\xfe\x1d\x13\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x86\xa0\x00\x01\x97\x7c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" {
rpc_parse(&mut pstate, &[*byte]);
}
assert!(matches!(pstate.state, RpcState::End));
assert!(pstate.xid == 0x72fe1d13);
assert!(pstate.rpc_version == 2);
assert!(pstate.program == 100000);
assert!(pstate.prog_version == 104316);
assert!(pstate.procedure == 0);
assert!(pstate.creds_flavor == 0);
assert!(pstate.creds_data.len() == 0);
assert!(pstate.verif_flavor == 0);
assert!(pstate.verif_data.len() == 0);
let resp = build_repl(&mut pstate, &CLIENT_INFO);
assert!(resp == b"\x72\xfe\x1d\x13\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x04");
}
#[test]
fn test_probe_nmap_split2() {
let mut pstate = ProtocolState::new();
for data in [
b"\x80\x00\x00\x28\x72\xfe\x1d",
b"\x13\x00\x00\x00\x00\x00\x00",
b"\x00\x02\x00\x01\x86\xa0\x00",
b"\x01\x97\x7c\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00",
] {
rpc_parse(&mut pstate, data);
}
rpc_parse(&mut pstate, b"\x00\x00");
assert!(matches!(pstate.state, RpcState::End));
assert!(pstate.xid == 0x72fe1d13);
assert!(pstate.rpc_version == 2);
assert!(pstate.program == 100000);
assert!(pstate.prog_version == 104316);
assert!(pstate.procedure == 0);
assert!(pstate.creds_flavor == 0);
assert!(pstate.creds_data.len() == 0);
assert!(pstate.verif_flavor == 0);
assert!(pstate.verif_data.len() == 0);
let resp = build_repl(&mut pstate, &CLIENT_INFO);
assert!(resp == b"\x72\xfe\x1d\x13\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x04");
}
#[test]
fn test_probe_portmap_v4_dump() {
let mut pstate = ProtocolState::new();
rpc_parse(&mut pstate, b"\x80\x00\x00\x28\x01\x1b\x60\xa6\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x86\xa0\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
assert!(matches!(pstate.state, RpcState::End));
assert!(pstate.rpc_version == 2);
assert!(pstate.program == 100000);
assert!(pstate.prog_version == 4);
assert!(pstate.procedure == 4); // dump
assert!(pstate.creds_flavor == 0);
assert!(pstate.creds_data.len() == 0);
assert!(pstate.verif_flavor == 0);
assert!(pstate.verif_data.len() == 0);
}
}

1418
src/proto/smb.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -16,21 +16,635 @@
use log::*; use log::*;
use std::str;
use crate::client::ClientInfo; use crate::client::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::utils::byte2str;
use crate::Masscanned; use crate::Masscanned;
pub const SSH_PATTERN_CLIENT_PROTOCOL: &[u8; 7] = b"SSH-2.0"; pub const SSH_PATTERN_CLIENT_PROTOCOL_2: &[u8; 7] = b"SSH-2.0";
pub const SSH_PATTERN_CLIENT_PROTOCOL_1: &[u8; 8] = b"SSH-1.99";
const SSH_STATE_START: usize = 0;
const SSH_STATE_S1: usize = 1;
const SSH_STATE_S2: usize = 2;
const SSH_STATE_H: usize = 3;
const SSH_STATE_DASH: usize = 4;
const SSH_STATE_VERSION: usize = 5;
const SSH_STATE_SOFTWARE: usize = 6;
const SSH_STATE_COMMENT: usize = 7;
const SSH_STATE_EOB: usize = 8;
const SSH_STATE_LF: usize = 9;
const SSH_STATE_FAIL: usize = 0xFFFF;
struct ProtocolState {
state: usize,
prev_state: usize,
ssh_version: Vec<u8>,
ssh_software: Vec<u8>,
ssh_comment: Vec<u8>,
}
impl ProtocolState {
fn new() -> Self {
ProtocolState {
state: SSH_STATE_START,
prev_state: SSH_STATE_START,
ssh_version: Vec::<u8>::new(),
ssh_software: Vec::<u8>::new(),
ssh_comment: Vec::<u8>::new(),
}
}
}
fn ssh_parse(pstate: &mut ProtocolState, data: &[u8]) {
/* RFC 4253:
*
* 4.2. Protocol Version Exchange
*
* When the connection has been established, both sides MUST send an
* identification string. This identification string MUST be
*
* SSH-protoversion-softwareversion SP comments CR LF
*
* Since the protocol being defined in this set of documents is version
* 2.0, the 'protoversion' MUST be "2.0". The 'comments' string is
* OPTIONAL. If the 'comments' string is included, a 'space' character
* (denoted above as SP, ASCII 32) MUST separate the 'softwareversion'
* and 'comments' strings. The identification MUST be terminated by a
* single Carriage Return (CR) and a single Line Feed (LF) character
* (ASCII 13 and 10, respectively). Implementers who wish to maintain
* compatibility with older, undocumented versions of this protocol may
* want to process the identification string without expecting the
* presence of the carriage return character for reasons described in
* Section 5 of this document. The null character MUST NOT be sent.
* The maximum length of the string is 255 characters, including the
* Carriage Return and Line Feed.
*/
let mut i = 0;
while i < data.len() {
match pstate.state {
SSH_STATE_START => {
pstate.state = SSH_STATE_S1;
continue;
}
/* first bytes should be "SSH-" */
SSH_STATE_S1 | SSH_STATE_S2 | SSH_STATE_H | SSH_STATE_DASH => {
if data[i] != b"SSH-"[pstate.state - SSH_STATE_S1] {
pstate.state = SSH_STATE_FAIL;
} else {
pstate.state += 1;
}
}
/* expect LF after a CR was read */
SSH_STATE_LF => {
if data[i] == b'\n' {
pstate.state = SSH_STATE_EOB;
} else {
if pstate.prev_state == SSH_STATE_SOFTWARE {
/* when reading software, \r can be followed by something else than \n */
pstate.state = pstate.prev_state;
/* cancel the read of this char */
i -= 1;
/* add the previously read \r to the software string */
pstate.ssh_software.push(b'\r');
} else if pstate.prev_state == SSH_STATE_COMMENT {
/* when reading comment, \r can be followed by something else than \n */
pstate.state = pstate.prev_state;
/* cancel the read of this char */
i -= 1;
/* add the previously read \r to the software string */
pstate.ssh_comment.push(b'\r');
} else {
/* in some other cases, it fails */
pstate.state = SSH_STATE_FAIL;
}
}
}
SSH_STATE_VERSION => {
if data[i] == b'-' {
pstate.state = SSH_STATE_SOFTWARE;
} else if !data[i].is_ascii_digit() && data[i] != b'.' {
pstate.state = SSH_STATE_FAIL;
} else {
pstate.ssh_version.push(data[i]);
}
}
SSH_STATE_SOFTWARE => {
if data[i] == b'\r' {
/* look for LF in the next char */
pstate.prev_state = pstate.state;
pstate.state = SSH_STATE_LF;
} else if data[i] == b' ' {
pstate.state = SSH_STATE_COMMENT;
} else {
pstate.ssh_software.push(data[i]);
}
}
SSH_STATE_COMMENT => {
if data[i] == b'\r' {
/* look for LF in the next char */
pstate.prev_state = pstate.state;
pstate.state = SSH_STATE_LF;
} else {
pstate.ssh_comment.push(data[i]);
}
}
SSH_STATE_FAIL => {
return;
}
SSH_STATE_EOB => { /* so far, do not parse after banner */ }
_ => {}
};
i += 1;
}
}
pub fn repl<'a>( pub fn repl<'a>(
data: &'a [u8], data: &'a [u8],
_masscanned: &Masscanned, _masscanned: &Masscanned,
mut _client_info: &mut ClientInfo, mut _client_info: &ClientInfo,
_tcb: Option<&mut TCPControlBlock>,
) -> Option<Vec<u8>> { ) -> Option<Vec<u8>> {
debug!("receiving SSH data"); debug!("receiving SSH data");
let mut pstate = ProtocolState::new();
ssh_parse(&mut pstate, data);
if pstate.state != SSH_STATE_EOB {
debug!("data in not correctly formatted - not responding");
debug!("pstate: {}", pstate.state);
return None;
}
let repl_data = b"SSH-2.0-1\r\n".to_vec(); let repl_data = b"SSH-2.0-1\r\n".to_vec();
debug!("sending SSH answer"); debug!("sending SSH answer");
warn!("SSH server banner to {}", str::from_utf8(&data).unwrap().trim_end()); warn!("SSH server banner to {}", byte2str(&pstate.ssh_software));
return Some(repl_data); Some(repl_data)
}
#[cfg(test)]
mod tests {
use super::*;
/* Reconstruct client's banner from the parsed information */
fn ssh_banner(pstate: &ProtocolState) -> Vec<u8> {
let mut banner = b"SSH-".to_vec();
for b in &pstate.ssh_version {
banner.push(*b);
}
banner.push(b'-');
for b in &pstate.ssh_software {
banner.push(*b);
}
if pstate.ssh_comment.len() > 0 {
banner.push(b' ');
for b in &pstate.ssh_comment {
banner.push(*b);
}
}
banner.push(b'\r');
banner.push(b'\n');
banner
}
#[test]
fn ssh_2_banner_parse() {
/* all at once */
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT");
/* byte by byte */
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
for i in 0..test_banner.len() {
if i == 0 {
assert!(pstate.state == SSH_STATE_START);
} else if i > 0 && i < 4 {
assert!(pstate.state == SSH_STATE_S1 + i);
} else if i >= 4 && i < 8 {
assert!(pstate.state == SSH_STATE_VERSION);
} else if i >= 8 && i < 17 {
assert!(pstate.state == SSH_STATE_SOFTWARE);
} else if i >= 17 && i < test_banner.len() - 1 {
assert!(pstate.state == SSH_STATE_COMMENT);
} else {
assert!(pstate.state == SSH_STATE_LF);
}
ssh_parse(&mut pstate, &test_banner[i..i + 1]);
}
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_1_banner_parse() {
/* all at once */
let test_banner = b"SSH-1.99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* byte by byte */
let test_banner = b"SSH-1.99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
for i in 0..test_banner.len() {
if i == 0 {
assert!(pstate.state == SSH_STATE_START);
} else if i > 0 && i < 4 {
assert!(pstate.state == SSH_STATE_S1 + i);
} else if i >= 4 && i < 9 {
assert!(pstate.state == SSH_STATE_VERSION);
} else if i >= 9 && i < 18 {
assert!(pstate.state == SSH_STATE_SOFTWARE);
} else if i >= 18 && i < test_banner.len() - 1 {
assert!(pstate.state == SSH_STATE_COMMENT);
} else {
assert!(pstate.state == SSH_STATE_LF);
}
ssh_parse(&mut pstate, &test_banner[i..i + 1]);
}
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_2_banner_space() {
/* space in SSH */
let test_banner = b"S SH-2.0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* space in VERSION */
let test_banner = b"SSH-2. 0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* space in software */
let test_banner = b"SSH-2.0-SOFT WARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFT");
assert!(pstate.ssh_comment == b"WARE COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* space in comment */
let test_banner = b"SSH-2.0-SOFTWARE COM MENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM MENT");
assert!(ssh_banner(&pstate) == test_banner);
/* double space */
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b" COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_1_banner_space() {
/* space in SSH */
let test_banner = b"S SH-1.99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* space in VERSION */
let test_banner = b"SSH-1. 99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* space in software */
let test_banner = b"SSH-1.99-SOFT WARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFT");
assert!(pstate.ssh_comment == b"WARE COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* space in comment */
let test_banner = b"SSH-1.99-SOFTWARE COM MENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM MENT");
assert!(ssh_banner(&pstate) == test_banner);
/* double space */
let test_banner = b"SSH-1.99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b" COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_2_banner_cr() {
/* CR in SSH */
let test_banner = b"S\rSH-2.0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CR in VERSION */
let test_banner = b"SSH-2.\r0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CR in SOFTWARE */
let test_banner = b"SSH-2.0-SOFT\rWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFT\rWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* CR in COMMENT */
let test_banner = b"SSH-2.0-SOFTWARE COM\rMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM\rMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* CR at the end */
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT\r");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_1_banner_cr() {
/* CR in SSH */
let test_banner = b"S\rSH-1.99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CR in VERSION */
let test_banner = b"SSH-1.\r99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CR in SOFTWARE */
let test_banner = b"SSH-1.99-SOFT\rWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFT\rWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* CR in COMMENT */
let test_banner = b"SSH-1.99-SOFTWARE COM\rMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM\rMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* CR at the end */
let test_banner = b"SSH-1.99-SOFTWARE COMMENT\r\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT\r");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_2_banner_lf() {
/* LF in SSH */
let test_banner = b"S\nSH-2.0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* LF in VERSION */
let test_banner = b"SSH-2.\n0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* LF in SOFTWARE */
let test_banner = b"SSH-2.0-SOFT\nWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFT\nWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* LF in COMMENT */
let test_banner = b"SSH-2.0-SOFTWARE COM\nMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM\nMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* LF at the end */
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\n\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT\n");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_1_banner_lf() {
/* LF in SSH */
let test_banner = b"S\nSH-1.99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* LF in VERSION */
let test_banner = b"SSH-1.\n99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* LF in SOFTWARE */
let test_banner = b"SSH-1.99-SOFT\nWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFT\nWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* LF in COMMENT */
let test_banner = b"SSH-1.99-SOFTWARE COM\nMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM\nMENT");
assert!(ssh_banner(&pstate) == test_banner);
/* LF at the end */
let test_banner = b"SSH-1.99-SOFTWARE COMMENT\n\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT\n");
assert!(ssh_banner(&pstate) == test_banner);
}
#[test]
fn ssh_2_banner_crlf() {
/* CRLF in SSH */
let test_banner = b"S\r\nSH-2.0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CRLF in VERSION */
let test_banner = b"SSH-2.\r\n0-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CRLF in SOFTWARE */
let test_banner = b"SSH-2.0-SOFT\r\nWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFT");
assert!(pstate.ssh_comment == b"");
assert!(ssh_banner(&pstate) == b"SSH-2.0-SOFT\r\n");
/* CRLF in COMMENT */
let test_banner = b"SSH-2.0-SOFTWARE COM\r\nMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM");
assert!(ssh_banner(&pstate) == b"SSH-2.0-SOFTWARE COM\r\n");
/* CRLF at the end */
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"2.0");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == b"SSH-2.0-SOFTWARE COMMENT\r\n");
}
#[test]
fn ssh_1_banner_crlf() {
/* CRLF in SSH */
let test_banner = b"S\r\nSH-1.99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CRLF in VERSION */
let test_banner = b"SSH-1.\r\n99-SOFTWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_FAIL);
/* CRLF in SOFTWARE */
let test_banner = b"SSH-1.99-SOFT\r\nWARE COMMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFT");
assert!(pstate.ssh_comment == b"");
assert!(ssh_banner(&pstate) == b"SSH-1.99-SOFT\r\n");
/* CRLF in COMMENT */
let test_banner = b"SSH-1.99-SOFTWARE COM\r\nMENT\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COM");
assert!(ssh_banner(&pstate) == b"SSH-1.99-SOFTWARE COM\r\n");
/* CRLF at the end */
let test_banner = b"SSH-1.99-SOFTWARE COMMENT\r\n\r\n";
let mut pstate = ProtocolState::new();
assert!(pstate.state == SSH_STATE_START);
ssh_parse(&mut pstate, test_banner);
assert!(pstate.state == SSH_STATE_EOB);
assert!(pstate.ssh_version == b"1.99");
assert!(pstate.ssh_software == b"SOFTWARE");
assert!(pstate.ssh_comment == b"COMMENT");
assert!(ssh_banner(&pstate) == b"SSH-1.99-SOFTWARE COMMENT\r\n");
}
} }

View file

@ -24,6 +24,7 @@ use byteorder::{BigEndian, ByteOrder};
use std::io; use std::io;
use crate::client::ClientInfo; use crate::client::ClientInfo;
use crate::proto::TCPControlBlock;
use crate::Masscanned; use crate::Masscanned;
/* RFC 5389: The magic cookie field MUST contain the fixed value 0x2112A442 in /* RFC 5389: The magic cookie field MUST contain the fixed value 0x2112A442 in
@ -257,107 +258,6 @@ impl Into<Vec<u8>> for &StunAttribute {
} }
} }
/*
struct StunPacket {
class: u8,
method: u16,
length: u16,
magic: u32,
id: u128,
data: Vec<u8>,
attributes: Vec<StunAttribute>,
}
impl StunPacket {
fn new(data: &[u8]) -> Result<Self, io::Error> {
if data.len() < 20 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"not enough data",
));
}
let class: u8 = ((data[0] & 0x01) << 1) | ((data[1] & 0x10) >> 4);
let method: u16 = (((data[0] & 0b00111110) << 7) as u16) | ((data[1] & 0b11101111) as u16);
let length: u16 = BigEndian::read_u16(&data[2..4]);
let magic: u32 = BigEndian::read_u32(&data[4..8]);
let id: u128 = ((BigEndian::read_u64(&data[8..16]) as u128) << 32)
| (BigEndian::read_u32(&data[16..20]) as u128);
if data.len() < 20 + length as usize {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"not enough data",
));
}
let data: Vec<u8> = data[20..(20 + length) as usize].to_vec();
let mut stun = StunPacket {
class,
method,
length,
magic,
id,
data,
attributes: Vec::<StunAttribute>::new(),
};
stun.attributes = stun.get_attributes();
Ok(stun)
}
fn empty() -> Self {
StunPacket {
class: 0,
method: 0,
length: 0,
magic: 0,
id: 0,
data: Vec::new(),
attributes: Vec::new(),
}
}
fn get_attributes(&self) -> Vec<StunAttribute> {
let mut i = 0;
let mut attributes = Vec::<StunAttribute>::new();
while i + 4 < self.data.len() {
let attr = StunAttribute::from(self.data[i..].to_vec());
i += 4 + attr.len() as usize;
attributes.push(attr);
}
attributes
}
fn set_length(&mut self) {
self.length = 0;
for attr in &self.attributes {
self.length += 4 + attr.len();
}
}
}
impl Into<Vec<u8>> for StunPacket {
fn into(self) -> Vec<u8> {
let mut v = Vec::<u8>::new();
// first cocktail with class and method bits
v.push(
TryInto::<u8>::try_into((self.method >> 7) & 0b00111110).unwrap()
| TryInto::<u8>::try_into((self.class & 0b10) >> 1).unwrap(),
);
// second cocktail with class and method bits
v.push(
TryInto::<u8>::try_into((self.method & 0b01110000) << 1).unwrap()
| TryInto::<u8>::try_into((self.class & 0b01) << 4).unwrap()
| TryInto::<u8>::try_into(self.method & 0b00001111).unwrap(),
);
v.append(&mut self.length.to_be_bytes().to_vec());
v.append(&mut self.magic.to_be_bytes().to_vec());
v.append(&mut self.id.to_be_bytes()[4..].to_vec());
for attr in &self.attributes {
v.append(&mut attr.into());
}
v
}
}
*/
struct StunPacket { struct StunPacket {
class: u8, class: u8,
method: u16, method: u16,
@ -451,67 +351,11 @@ impl Into<Vec<u8>> for StunPacket {
} }
} }
/*
pub fn repl<'a>( pub fn repl<'a>(
data: &'a [u8], data: &'a [u8],
_masscanned: &Masscanned, _masscanned: &Masscanned,
client_info: ClientInfo, client_info: &mut ClientInfo,
) -> Option<Vec<u8>> { _tcb: Option<&mut TCPControlBlock>,
debug!("receiving STUN data");
let stun_req: StunPacket = if let Ok(s) = StunPacket::new(&data) {
s
} else {
return None;
};
if stun_req.class != STUN_CLASS_REQUEST {
info!(
"STUN packet not handled (class unknown: 0b{:b})",
stun_req.class
);
return None;
}
if stun_req.method != STUN_METHOD_BINDING {
info!(
"STUN packet not handled (method unknown: 0x{:03x})",
stun_req.method
);
return None;
}
/*
* To be compatible with RFC3489: ignore magic
if stun_req.magic != STUN_MAGIC {
info!(
"STUN packet not handled (magic unknown: 0x{:04x})",
stun_req.magic
);
return None;
}
*/
if client_info.ip.src == None {
error!("STUN packet not handled (expected client ip address not found)");
return None;
}
if client_info.port.src == None {
error!("STUN packet not handled (expected client port address not found)");
return None;
}
let mut stun_resp: StunPacket = StunPacket::empty();
stun_resp.class = STUN_CLASS_SUCCESS_RESPONSE;
stun_resp.method = STUN_METHOD_BINDING;
stun_resp.id = stun_req.id;
stun_resp.attributes = Vec::<StunAttribute>::new();
stun_resp.attributes.push(StunAttribute::MappedAddress(
StunMappedAddressAttribute::new(client_info.ip.src.unwrap(), client_info.port.src.unwrap()),
));
stun_resp.set_length();
return Some(stun_resp.into());
}
*/
pub fn repl<'a>(
data: &'a [u8],
_masscanned: &Masscanned,
mut client_info: &mut ClientInfo,
) -> Option<Vec<u8>> { ) -> Option<Vec<u8>> {
debug!("receiving STUN data"); debug!("receiving STUN data");
let stun_req: StunPacket = if let Ok(s) = StunPacket::new(&data) { let stun_req: StunPacket = if let Ok(s) = StunPacket::new(&data) {
@ -571,6 +415,8 @@ mod tests {
use pnet::util::MacAddr; use pnet::util::MacAddr;
use crate::logger::MetaLogger;
#[test] #[test]
fn test_proto_stun_ipv4() { fn test_proto_stun_ipv4() {
/* test payload is: /* test payload is:
@ -596,9 +442,11 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info) { let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got None"); panic!("expected an answer, got None");
@ -655,13 +503,15 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
client_info.ip.src = Some(IpAddr::V6(test_ip_addr)); client_info.ip.src = Some(IpAddr::V6(test_ip_addr));
client_info.ip.dst = Some(IpAddr::V6(masscanned_ip_addr)); client_info.ip.dst = Some(IpAddr::V6(masscanned_ip_addr));
client_info.port.src = Some(55000); client_info.port.src = Some(55000);
client_info.port.dst = Some(65000); client_info.port.dst = Some(65000);
let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info) { let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got None"); panic!("expected an answer, got None");
@ -706,13 +556,15 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
client_info.ip.src = Some(IpAddr::V4(test_ip_addr)); client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
client_info.ip.dst = Some(IpAddr::V4(masscanned_ip_addr)); client_info.ip.dst = Some(IpAddr::V4(masscanned_ip_addr));
client_info.port.src = Some(55000); client_info.port.src = Some(55000);
client_info.port.dst = Some(65000); client_info.port.dst = Some(65000);
let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info) { let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got None"); panic!("expected an answer, got None");
@ -755,13 +607,15 @@ mod tests {
synack_key: [0, 0], synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"), mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None, iface: None,
ip_addresses: Some(&ips), self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
}; };
client_info.ip.src = Some(IpAddr::V4(test_ip_addr)); client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
client_info.ip.dst = Some(IpAddr::V4(masscanned_ip_addr)); client_info.ip.dst = Some(IpAddr::V4(masscanned_ip_addr));
client_info.port.src = Some(55000); client_info.port.src = Some(55000);
client_info.port.dst = Some(65535); client_info.port.dst = Some(65535);
let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info) { let payload_resp = if let Some(r) = repl(payload, &masscanned, &mut client_info, None) {
r r
} else { } else {
panic!("expected an answer, got None"); panic!("expected an answer, got None");

270
src/proto/tcb.rs Normal file
View file

@ -0,0 +1,270 @@
// This file is part of masscanned.
// Copyright 2021 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::sync::Mutex;
use super::http::ProtocolState as HTTPProtocolState;
use super::rpc::ProtocolState as RPCProtocolState;
use crate::proto::{BASE_STATE, PROTO_NONE};
pub enum ProtocolState {
HTTP(HTTPProtocolState),
RPC(RPCProtocolState),
}
pub struct TCPControlBlock {
/* state used to detect protocols (not specific) */
pub smack_state: usize,
/* detected protocol */
pub proto_id: usize,
/* internal state of protocol parser (e.g., HTTP parsing) */
pub proto_state: Option<ProtocolState>,
}
lazy_static! {
static ref CONTABLE: Mutex<HashMap<u32, TCPControlBlock>> = Mutex::new(HashMap::new());
}
pub fn is_tcb_set(cookie: u32) -> bool {
CONTABLE.lock().unwrap().contains_key(&cookie)
}
pub fn get_tcb<F>(cookie: u32, mut f: F)
where
F: FnMut(Option<&mut TCPControlBlock>),
{
f(CONTABLE.lock().unwrap().get_mut(&cookie));
}
pub fn add_tcb(cookie: u32) {
let mut ct = CONTABLE.lock().unwrap();
let tcb = TCPControlBlock {
smack_state: BASE_STATE,
proto_id: PROTO_NONE,
proto_state: None,
};
if !ct.contains_key(&cookie) {
ct.insert(cookie, tcb);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use std::net::{IpAddr, Ipv4Addr};
use std::str::FromStr;
use pnet::{
packet::{ip::IpNextHeaderProtocols, tcp::TcpPacket},
util::MacAddr,
};
use crate::client::ClientInfo;
use crate::layer_4::tcp;
use crate::logger::MetaLogger;
use crate::proto::{PROTO_HTTP, PROTO_RPC_TCP};
use crate::synackcookie;
use crate::Masscanned;
fn get_dummy_tcp(&client_info: &ClientInfo) -> Vec<u8> {
/* Craft a TCP ACK+PUSH packet with correct ports and ack */
let mut pkt = Vec::new();
pkt.extend_from_slice(&client_info.port.src.unwrap().to_be_bytes());
pkt.extend_from_slice(&client_info.port.dst.unwrap().to_be_bytes());
pkt.extend_from_slice(b"\x00\x00\x00\x00");
pkt.extend_from_slice(&(client_info.cookie.unwrap() + 1).to_be_bytes());
pkt.extend_from_slice(b"P\x18 \x00\x00\x00\x00\x00");
pkt
}
#[test]
fn test_proto_tcb_proto_id() {
let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
client_info.port.src = Some(65000);
client_info.port.dst = Some(80);
client_info.transport = Some(IpNextHeaderProtocols::Tcp);
let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
client_info.ip.dst = Some(IpAddr::V4(masscanned_ip_addr));
let mut ips = HashSet::new();
ips.insert(IpAddr::V4(masscanned_ip_addr));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let cookie = synackcookie::generate(&client_info, &masscanned.synack_key).unwrap();
client_info.cookie = Some(cookie);
assert!(!is_tcb_set(cookie), "expected no TCB entry, found one");
/***** TEST PROTOCOL ID IN TCB *****/
let payload = [get_dummy_tcp(&client_info), b"GET / HTTP/1.1\r\n".to_vec()].concat();
tcp::repl(
&TcpPacket::new(&payload).unwrap(),
&masscanned,
&mut client_info,
);
assert!(is_tcb_set(cookie), "expected a TCB entry, not found");
get_tcb(cookie, |t| {
let t = t.unwrap();
assert!(t.proto_id == PROTO_HTTP);
});
/***** SENDING MORE DATA *****/
let payload = [
get_dummy_tcp(&client_info),
b"garbage data with no specific format (no protocol)\r\n\r\n".to_vec(),
]
.concat();
tcp::repl(
&TcpPacket::new(&payload).unwrap(),
&masscanned,
&mut client_info,
);
assert!(is_tcb_set(cookie), "expected a TCB entry, not found");
get_tcb(cookie, |t| {
let t = t.unwrap();
assert!(t.proto_id == PROTO_HTTP);
});
}
#[test]
fn test_proto_tcb_proto_state_http() {
let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
client_info.port.src = Some(65001);
client_info.port.dst = Some(80);
client_info.transport = Some(IpNextHeaderProtocols::Tcp);
let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
client_info.ip.dst = Some(IpAddr::V4(masscanned_ip_addr));
let mut ips = HashSet::new();
ips.insert(IpAddr::V4(masscanned_ip_addr));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let cookie = synackcookie::generate(&client_info, &masscanned.synack_key).unwrap();
client_info.cookie = Some(cookie);
assert!(!is_tcb_set(cookie), "expected no TCB entry, found one");
/***** TEST PROTOCOL ID IN TCB *****/
let payload = [get_dummy_tcp(&client_info), b"GET / HTTP/1.1\r\n".to_vec()].concat();
tcp::repl(
&TcpPacket::new(&payload).unwrap(),
&masscanned,
&mut client_info,
);
assert!(is_tcb_set(cookie), "expected a TCB entry, not found");
get_tcb(cookie, |t| {
let t = t.unwrap();
assert!(t.proto_id == PROTO_HTTP);
if let Some(ProtocolState::HTTP(_)) = t.proto_state {
} else {
panic!("expected a HTTP protocole state, found None");
}
});
/***** SENDING MORE DATA *****/
let payload = [
get_dummy_tcp(&client_info),
b"Field: empty\r\n\r\n".to_vec(),
]
.concat();
/* Should have an answer here */
if let None = tcp::repl(
&TcpPacket::new(&payload).unwrap(),
&masscanned,
&mut client_info,
) {
panic!("expected an HTTP response, got nothing");
}
assert!(is_tcb_set(cookie), "expected a TCB entry, not found");
get_tcb(cookie, |t| {
let t = t.unwrap();
assert!(t.proto_id == PROTO_HTTP);
})
}
#[test]
fn test_proto_tcb_proto_state_rpc() {
let mut client_info = ClientInfo::new();
let test_ip_addr = Ipv4Addr::new(3, 2, 1, 0);
client_info.ip.src = Some(IpAddr::V4(test_ip_addr));
client_info.port.src = Some(65002);
client_info.port.dst = Some(80);
client_info.transport = Some(IpNextHeaderProtocols::Tcp);
let masscanned_ip_addr = Ipv4Addr::new(0, 1, 2, 3);
client_info.ip.dst = Some(IpAddr::V4(masscanned_ip_addr));
let mut ips = HashSet::new();
ips.insert(IpAddr::V4(masscanned_ip_addr));
/* Construct masscanned context object */
let masscanned = Masscanned {
synack_key: [0, 0],
mac: MacAddr::from_str("00:11:22:33:44:55").expect("error parsing MAC address"),
iface: None,
self_ip_list: Some(&ips),
remote_ip_deny_list: None,
log: MetaLogger::new(),
};
let cookie = synackcookie::generate(&client_info, &masscanned.synack_key).unwrap();
client_info.cookie = Some(cookie);
assert!(!is_tcb_set(cookie), "expected no TCB entry, found one");
/***** TEST PROTOCOL ID IN TCB *****/
let full_payload = b"\x80\x00\x00\x28\x72\xfe\x1d\x13\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x86\xa0\x00\x01\x97\x7c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let payload = [get_dummy_tcp(&client_info), full_payload[0..28].to_vec()].concat();
tcp::repl(
&TcpPacket::new(&payload).unwrap(),
&masscanned,
&mut client_info,
);
assert!(is_tcb_set(cookie), "expected a TCB entry, not found");
get_tcb(cookie, |t| {
let t = t.unwrap();
assert!(t.proto_id == PROTO_RPC_TCP);
if let Some(ProtocolState::RPC(_)) = t.proto_state {
} else {
panic!("expected a RPC protocole state, found None");
}
});
/***** SENDING MORE DATA *****/
/* Should have an answer here */
let payload = [get_dummy_tcp(&client_info), full_payload[28..].to_vec()].concat();
if let None = tcp::repl(
&TcpPacket::new(&payload).unwrap(),
&masscanned,
&mut client_info,
) {
panic!("expected a RPC response, got nothing");
}
assert!(is_tcb_set(cookie), "expected a TCB entry, not found");
get_tcb(cookie, |t| {
let t = t.unwrap();
assert!(t.proto_id == PROTO_RPC_TCP);
});
}
}

View file

@ -61,7 +61,7 @@ pub struct Smack {
} }
fn make_copy_of_pattern(pattern: &[u8], is_nocase: bool) -> Vec<u8> { fn make_copy_of_pattern(pattern: &[u8], is_nocase: bool) -> Vec<u8> {
let mut p = pattern.clone().to_vec(); let mut p = pattern.to_vec();
for i in 0..p.len() { for i in 0..p.len() {
if is_nocase { if is_nocase {
p[i] = p[i].to_ascii_lowercase(); p[i] = p[i].to_ascii_lowercase();

View file

@ -1,4 +1,5 @@
bitflags! { bitflags! {
#[derive(Clone, Copy)]
pub struct SmackFlags: usize { pub struct SmackFlags: usize {
const EMPTY = 0x00; const EMPTY = 0x00;
const ANCHOR_BEGIN = 0x01; const ANCHOR_BEGIN = 0x01;

48
src/utils/display.rs Normal file
View file

@ -0,0 +1,48 @@
// This file is part of masscanned.
// Copyright 2021 - 2022 - The IVRE project
//
// Masscanned is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Masscanned is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
static CHARS: [&'static str; 256] = [
"\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\x07", "\\x08", "\\x09",
"\\x0a", "\\x0b", "\\x0c", "\\x0d", "\\x0e", "\\x0f", "\\x10", "\\x11", "\\x12", "\\x13",
"\\x14", "\\x15", "\\x16", "\\x17", "\\x18", "\\x19", "\\x1a", "\\x1b", "\\x1c", "\\x1d",
"\\x1e", "\\x1f", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".",
"/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A",
"B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"{", "|", "}", "~", "\\x7f", "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86",
"\\x87", "\\x88", "\\x89", "\\x8a", "\\x8b", "\\x8c", "\\x8d", "\\x8e", "\\x8f", "\\x90",
"\\x91", "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97", "\\x98", "\\x99", "\\x9a",
"\\x9b", "\\x9c", "\\x9d", "\\x9e", "\\x9f", "\\xa0", "\\xa1", "\\xa2", "\\xa3", "\\xa4",
"\\xa5", "\\xa6", "\\xa7", "\\xa8", "\\xa9", "\\xaa", "\\xab", "\\xac", "\\xad", "\\xae",
"\\xaf", "\\xb0", "\\xb1", "\\xb2", "\\xb3", "\\xb4", "\\xb5", "\\xb6", "\\xb7", "\\xb8",
"\\xb9", "\\xba", "\\xbb", "\\xbc", "\\xbd", "\\xbe", "\\xbf", "\\xc0", "\\xc1", "\\xc2",
"\\xc3", "\\xc4", "\\xc5", "\\xc6", "\\xc7", "\\xc8", "\\xc9", "\\xca", "\\xcb", "\\xcc",
"\\xcd", "\\xce", "\\xcf", "\\xd0", "\\xd1", "\\xd2", "\\xd3", "\\xd4", "\\xd5", "\\xd6",
"\\xd7", "\\xd8", "\\xd9", "\\xda", "\\xdb", "\\xdc", "\\xdd", "\\xde", "\\xdf", "\\xe0",
"\\xe1", "\\xe2", "\\xe3", "\\xe4", "\\xe5", "\\xe6", "\\xe7", "\\xe8", "\\xe9", "\\xea",
"\\xeb", "\\xec", "\\xed", "\\xee", "\\xef", "\\xf0", "\\xf1", "\\xf2", "\\xf3", "\\xf4",
"\\xf5", "\\xf6", "\\xf7", "\\xf8", "\\xf9", "\\xfa", "\\xfb", "\\xfc", "\\xfd", "\\xfe",
"\\xff",
];
pub fn byte2str(data: &[u8]) -> String {
let mut result = String::new();
for byte in data {
result.push_str(CHARS[usize::from(*byte)]);
}
return result;
}

View file

@ -1,3 +1,7 @@
mod parsers; mod parsers;
pub use parsers::IpAddrParser; pub use parsers::IpAddrParser;
mod display;
pub use display::byte2str;

View file

@ -5,12 +5,12 @@ use std::io::BufReader;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use log::*; use log::*;
use pcap_file::pcap::{Packet, PcapReader}; use pcap_file::pcap::{PcapPacket, PcapReader};
use pnet::packet::{ use pnet::packet::{
ethernet::{EtherTypes, EthernetPacket}, ethernet::{EtherTypes, EthernetPacket},
ipv4::Ipv4Packet, ipv4::Ipv4Packet,
ipv6::Ipv6Packet, ipv6::Ipv6Packet,
Packet as Pkt, Packet,
}; };
/* Generic IP packet (either IPv4 or IPv6) */ /* Generic IP packet (either IPv4 or IPv6) */
@ -134,9 +134,46 @@ impl IpAddrParser for File {
} }
} }
/* Parse IP addresses from a comma-separated list in a string */
impl IpAddrParser for &str {
fn extract_ip_addresses_with_count(
self,
_blacklist: Option<HashSet<IpAddr>>,
) -> HashMap<IpAddr, u32> {
panic!("not implemented");
}
fn extract_ip_addresses_only(self, blacklist: Option<HashSet<IpAddr>>) -> HashSet<IpAddr> {
let mut ip_addresses = HashSet::new();
for line in self.split(",") {
/* Should never occur */
if line.is_empty() {
warn!("cannot parse line: {}", line);
continue;
}
let ip: IpAddr;
if let Ok(val) = line.parse::<Ipv4Addr>() {
ip = IpAddr::V4(val);
} else if let Ok(val) = line.parse::<Ipv6Addr>() {
ip = IpAddr::V6(val);
} else {
warn!("cannot parse IP address from line: {}", line);
continue;
}
if let Some(ref b) = blacklist {
if b.contains(&ip) {
info!("[blacklist] ignoring {}", &ip);
continue;
}
}
ip_addresses.insert(ip);
}
ip_addresses
}
}
/* Get the IP address of source and dest. from an IP packet. /* Get the IP address of source and dest. from an IP packet.
* works with both IPv4 and IPv6 packets/addresses */ * works with both IPv4 and IPv6 packets/addresses */
fn extract_ip(pkt: Packet) -> Option<(IpAddr, IpAddr)> { fn extract_ip(pkt: PcapPacket) -> Option<(IpAddr, IpAddr)> {
let eth = EthernetPacket::new(&pkt.data).expect("error parsing Ethernet packet"); let eth = EthernetPacket::new(&pkt.data).expect("error parsing Ethernet packet");
let payload = eth.payload(); let payload = eth.payload();
let ip = match eth.get_ethertype() { let ip = match eth.get_ethertype() {
@ -169,13 +206,13 @@ impl IpAddrParser for PcapReader<std::fs::File> {
/* Extract IP addresses (v4 and v6) from a capture and count occurrences of /* Extract IP addresses (v4 and v6) from a capture and count occurrences of
* each. */ * each. */
fn extract_ip_addresses_with_count( fn extract_ip_addresses_with_count(
self: PcapReader<std::fs::File>, mut self: PcapReader<std::fs::File>,
blacklist: Option<HashSet<IpAddr>>, blacklist: Option<HashSet<IpAddr>>,
) -> HashMap<IpAddr, u32> { ) -> HashMap<IpAddr, u32> {
let mut ip_addresses = HashMap::new(); let mut ip_addresses = HashMap::new();
// pcap.map(fn) , map_Ok // pcap.map(fn) , map_Ok
// .iter, into_iter // .iter, into_iter
for pkt in self { while let Some(pkt) = self.next_packet() {
match pkt { match pkt {
Ok(pkt) => { Ok(pkt) => {
// map_Some map_None // map_Some map_None
@ -209,13 +246,13 @@ impl IpAddrParser for PcapReader<std::fs::File> {
ip_addresses ip_addresses
} }
fn extract_ip_addresses_only( fn extract_ip_addresses_only(
self: PcapReader<std::fs::File>, mut self: PcapReader<std::fs::File>,
blacklist: Option<HashSet<IpAddr>>, blacklist: Option<HashSet<IpAddr>>,
) -> HashSet<IpAddr> { ) -> HashSet<IpAddr> {
let mut ip_addresses = HashSet::new(); let mut ip_addresses = HashSet::new();
// pcap.map(fn) , map_Ok // pcap.map(fn) , map_Ok
// .iter, into_iter // .iter, into_iter
for pkt in self { while let Some(pkt) = self.next_packet() {
match pkt { match pkt {
Ok(pkt) => { Ok(pkt) => {
// map_Some map_None // map_Some map_None

3
test/requirements.txt Normal file
View file

@ -0,0 +1,3 @@
ivre
scapy
requests

View file

@ -14,580 +14,31 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>. # along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.all import * import importlib
import requests import os
import requests.packages.urllib3.util.connection as urllib3_cn
import logging
from .conf import * # Export / other tests
from .core import test_all # noqa: F401
fmt = logging.Formatter("%(levelname)s\t%(message)s") DEFAULT_TESTS = [
ch = logging.StreamHandler() "arp",
ch.setFormatter(fmt) "dns",
ch.setLevel(logging.DEBUG) "ghost",
LOG = logging.getLogger(__name__) "http",
LOG.setLevel(logging.DEBUG) "icmpv4",
LOG.addHandler(ch) "icmpv6",
"ip",
tests = list() "rpc",
"smb",
# decorator to automatically add a function to tests "ssh",
def test(f): "stun",
OK = "\033[1mOK\033[0m" "tcp",
KO = "\033[1m\033[1;%dmKO\033[0m" % 31 "udp",
global tests
fname = f.__name__.ljust(50, '.')
def w(iface):
try:
f(iface)
LOG.info("{}{}".format(fname, OK))
except AssertionError as e:
LOG.info("{}{}: {}".format(fname, KO, e))
tests.append(w)
return w
def multicast(ip6):
a, b = ip6.split(":")[-2:]
mac = ["33", "33", "ff"]
if len(a) == 4:
mac.append(a[2:])
else:
mac.append("00")
if len(b) >= 2:
mac.append(b[:2])
else:
mac.append("00")
if len(b) >= 4:
mac.append(b[2:])
else:
mac.append("00")
return ":".join(mac)
def check_ip_checksum(pkt):
assert(IP in pkt), "no IP layer found"
ip_pkt = pkt[IP]
chksum = ip_pkt.chksum
del ip_pkt.chksum
assert(IP(raw(ip_pkt)).chksum == chksum), "bad IPv4 checksum"
def check_ipv6_checksum(pkt):
assert(IPv6 in pkt), "no IP layer found"
ip_pkt = pkt[IPv6]
chksum = ip_pkt.chksum
del ip_pkt.chksum
assert(IPv6(raw(ip_pkt)).chksum == chksum), "bad IPv6 checksum"
@test
def test_arp_req(iface):
##### ARP #####
arp_req = Ether()/ARP(psrc='192.0.0.2', pdst=IPV4_ADDR)
arp_repl = iface.sr1(arp_req, timeout=1)
assert(arp_repl is not None), "expecting answer, got nothing"
assert(ARP in arp_repl), "no ARP layer found"
arp_repl = arp_repl[ARP]
# check answer
## op is "is-at"
assert(arp_repl.op == 2), "unexpected ARP op: {}".format(arp_repl.op)
## answer for the requested IP
assert(arp_repl.psrc == arp_req.pdst), "unexpected ARP psrc: {}".format(arp_repl.psrc)
assert(arp_repl.pdst == arp_req.psrc), "unexpected ARP pdst: {}".format(arp_repl.pdst)
## answer is expected MAC address
assert(arp_repl.hwsrc == MAC_ADDR), "unexpected ARP hwsrc: {}".format(arp_repl.hwsrc)
@test
def test_arp_req_other_ip(iface):
##### ARP #####
arp_req = Ether()/ARP(psrc='192.0.0.2', pdst='1.2.3.4')
arp_repl = iface.sr1(arp_req, timeout=1)
assert(arp_repl is None), "responding to ARP requests for other IP addresses"
@test
def test_ipv4_req(iface):
##### IP #####
ip_req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR, id=0x1337)/ICMP(type=8, code=0)
ip_repl = iface.sr1(ip_req, timeout=1)
assert(ip_repl is not None), "expecting answer, got nothing"
check_ip_checksum(ip_repl)
assert(IP in ip_repl), "no IP layer in response"
ip_repl = ip_repl[IP]
assert(ip_repl.id == 0), "IP identification unexpected"
@test
def test_eth_req_other_mac(iface):
#### ETH ####
ip_req = Ether(dst="00:00:00:11:11:11")/IP(dst=IPV4_ADDR)/ICMP(type=8, code=0)
ip_repl = iface.sr1(ip_req, timeout=1)
assert(ip_repl is None), "responding to other MAC addresses"
@test
def test_ipv4_req_other_ip(iface):
##### IP #####
ip_req = Ether(dst=MAC_ADDR)/IP(dst="1.2.3.4")/ICMP(type=8, code=0)
ip_repl = iface.sr1(ip_req, timeout=1)
assert(ip_repl is None), "responding to other IP addresses"
@test
def test_icmpv4_echo_req(iface):
##### ICMPv4 #####
icmp_req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/ICMP(type=8, code=0)/Raw("idrinkwaytoomuchcoffee")
icmp_repl = iface.sr1(icmp_req, timeout=1)
assert(icmp_repl is not None), "expecting answer, got nothing"
check_ip_checksum(icmp_repl)
assert(ICMP in icmp_repl)
icmp_repl = icmp_repl[ICMP]
# check answer
## type is "echo-reply"
assert(icmp_repl.type == 0)
assert(icmp_repl.code == 0)
## data is the same as sent
assert(icmp_repl.load == icmp_req.load)
@test
def test_icmpv6_neighbor_solicitation(iface):
##### IPv6 Neighbor Solicitation #####
for mac in ["ff:ff:ff:ff:ff:ff", "33:33:00:00:00:01", MAC_ADDR, multicast(IPV6_ADDR)]:
nd_ns = Ether(dst=mac)/IPv6()/ICMPv6ND_NS(tgt=IPV6_ADDR)
nd_na = iface.sr1(nd_ns, timeout=1)
assert(nd_na is not None), "expecting answer, got nothing"
assert(ICMPv6ND_NA in nd_na)
nd_na = nd_na[ICMPv6ND_NA]
# check answer content
assert(nd_na.code == 0)
assert(nd_na.R == 0)
assert(nd_na.S == 1)
assert(nd_na.O == 1)
assert(nd_na.tgt == IPV6_ADDR)
# check ND Option
assert(nd_na.haslayer(ICMPv6NDOptDstLLAddr))
assert(nd_na.getlayer(ICMPv6NDOptDstLLAddr).lladdr == MAC_ADDR)
for mac in ["00:00:00:00:00:00", "33:33:33:00:00:01"]:
nd_ns = Ether(dst="ff:ff:ff:ff:ff:ff")/IPv6()/ICMPv6ND_NS(tgt=IPV6_ADDR)
nd_na = iface.sr1(nd_ns, timeout=1)
assert(nd_na is not None), "expecting no answer, got one"
@test
def test_icmpv6_neighbor_solicitation_other_ip(iface):
##### IPv6 Neighbor Solicitation #####
nd_ns = Ether(dst="ff:ff:ff:ff:ff:ff")/IPv6()/ICMPv6ND_NS(tgt="2020:4141:3030:2020::bdbd")
nd_na = iface.sr1(nd_ns, timeout=1)
assert(nd_na is None), "responding to ND_NS for other IP addresses"
@test
def test_icmpv6_echo_req(iface):
##### IPv6 Ping #####
echo_req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/ICMPv6EchoRequest(data="waytoomanynapkins")
echo_repl = iface.sr1(echo_req, timeout=1)
assert(echo_repl is not None), "expecting answer, got nothing"
assert(ICMPv6EchoReply in echo_repl)
echo_repl = echo_repl[ICMPv6EchoReply]
# check answer content
assert(echo_repl.code == 0)
assert(echo_repl.data == echo_req.data)
@test
def test_tcp_syn(iface):
##### SYN-ACK #####
# test a list of ports, randomly generated once
ports_to_test = [
1152, 2003, 2193, 3709, 4054, 6605, 6737, 6875, 7320, 8898, 9513, 9738, 10623, 10723,
11253, 12125, 12189, 12873, 14648, 14659, 16242, 16243, 17209, 17492, 17667, 17838,
18081, 18682, 18790, 19124, 19288, 19558, 19628, 19789, 20093, 21014, 21459, 21740,
24070, 24312, 24576, 26939, 27136, 27165, 27361, 29971, 31088, 33011, 33068, 34990,
35093, 35958, 36626, 36789, 37130, 37238, 37256, 37697, 37890, 38958, 42131, 43864,
44420, 44655, 44868, 45157, 46213, 46497, 46955, 49049, 49067, 49452, 49480, 50498,
50945, 51181, 52890, 53301, 53407, 53417, 53980, 55827, 56483, 58552, 58713, 58836,
59362, 59560, 60534, 60555, 60660, 61615, 62402, 62533, 62941, 63240, 63339, 63616,
64380, 65438,
] ]
for p in ports_to_test:
syn = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="S", dport=p)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
@test ENABLED_TESTS = DEFAULT_TESTS
def test_ipv4_tcp_psh_ack(iface): if tests := os.environ.get("TESTS"):
##### PSH-ACK ##### ENABLED_TESTS = [x.strip() for x in tests.split(",")]
sport = 26695
port = 445
# send PSH-ACK first
psh_ack = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="PA", dport=port)/Raw("payload")
syn_ack = iface.sr1(psh_ack, timeout=1)
assert(syn_ack is None), "no answer expected, got one"
# test the anti-injection mechanism
syn = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="S", dport=port)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="A", dport=port)
# should fail because no ack given
psh_ack = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="PA", dport=port)
ack = iface.sr1(psh_ack, timeout=1)
assert(ack is None), "no answer expected, got one"
# should get an answer this time
psh_ack = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="PA", dport=port, ack=syn_ack.seq + 1)
ack = iface.sr1(psh_ack, timeout=1)
assert(ack is not None), "expecting answer, got nothing"
check_ip_checksum(ack)
assert(TCP in ack)
ack = ack[TCP]
assert(ack.flags == "A")
@test for test in ENABLED_TESTS:
def test_ipv6_tcp_psh_ack(iface): importlib.import_module(".tests." + test, package="src")
##### PSH-ACK #####
sport = 26695
port = 445
# send PSH-ACK first
psh_ack = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="PA", dport=port)/Raw("payload")
syn_ack = iface.sr1(psh_ack, timeout=1)
assert(syn_ack is None), "no answer expected, got one"
# test the anti-injection mechanism
syn = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="S", dport=port)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="A", dport=port)
# should fail because no ack given
psh_ack = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="PA", dport=port)
ack = iface.sr1(psh_ack, timeout=1)
assert(ack is None), "no answer expected, got one"
# should get an answer this time
psh_ack = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="PA", dport=port, ack=syn_ack.seq + 1)
ack = iface.sr1(psh_ack, timeout=1)
assert(ack is not None), "expecting answer, got nothing"
check_ipv6_checksum(ack)
assert(TCP in ack)
ack = ack[TCP]
assert(ack.flags == "A")
@test
def test_ipv4_tcp_http(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
syn = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="S", sport=sport, dport=dport)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="A", sport=sport, dport=dport, ack=syn_ack.seq + 1)
_ = iface.sr1(ack, timeout=1)
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="PA", ack=syn_ack.seq + 1, sport=sport, dport=dport)/Raw("GET / HTTP/1.1\r\n\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ip_checksum(resp)
assert(TCP in resp)
tcp = resp[TCP]
assert(tcp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n"))
@test
def test_ipv6_tcp_http(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
syn = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="S", sport=sport, dport=dport)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="A", sport=sport, dport=dport, ack=syn_ack.seq + 1)
_ = iface.sr1(ack, timeout=1)
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="PA", ack=syn_ack.seq + 1, sport=sport, dport=dport)/Raw("GET / HTTP/1.1\r\n\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert(TCP in resp)
tcp = resp[TCP]
assert(tcp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n"))
@test
def test_ipv4_udp_http(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/UDP(sport=sport, dport=dport)/Raw("GET / HTTP/1.1\r\n\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ip_checksum(resp)
assert(UDP in resp)
udp = resp[UDP]
assert(udp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n"))
@test
def test_ipv6_udp_http(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/UDP(sport=sport, dport=dport)/Raw("GET / HTTP/1.1\r\n\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert(UDP in resp)
udp = resp[UDP]
assert(udp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n"))
@test
def test_ipv4_tcp_http_ko(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
syn = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="S", sport=sport, dport=dport)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="A", sport=sport, dport=dport, ack=syn_ack.seq + 1)
_ = iface.sr1(ack, timeout=1)
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="PA", ack=syn_ack.seq + 1, sport=sport, dport=dport)/Raw(bytes.fromhex("4f5054494f4e53"))
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ip_checksum(resp)
assert(TCP in resp)
assert("P" not in resp[TCP].flags)
assert(len(resp[TCP].payload) == 0)
@test
def test_ipv4_udp_http_ko(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/UDP(sport=sport, dport=dport)/Raw(bytes.fromhex("4f5054494f4e53"))
resp = iface.sr1(req, timeout=1)
assert(resp is None), "expecting no answer, got one"
@test
def test_ipv6_tcp_http_ko(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
syn = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="S", sport=sport, dport=dport)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="A", sport=sport, dport=dport, ack=syn_ack.seq + 1)
_ = iface.sr1(ack, timeout=1)
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="PA", ack=syn_ack.seq + 1, sport=sport, dport=dport)/Raw(bytes.fromhex("4f5054494f4e53"))
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert(TCP in resp)
assert("P" not in resp[TCP].flags)
assert(len(resp[TCP].payload) == 0)
@test
def test_ipv6_udp_http_ko(iface):
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/UDP(sport=sport, dport=dport)/Raw(bytes.fromhex("4f5054494f4e53"))
resp = iface.sr1(req, timeout=1)
assert(resp is None), "expecting no answer, got one"
@test
def test_ipv4_udp_stun(iface):
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478]
payload = bytes.fromhex("000100002112a442000000000000000000000000")
for sport in sports:
for dport in dports:
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/UDP(sport=sport, dport=dport)/Raw(payload)
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ip_checksum(resp)
assert(UDP in resp), "no UDP layer found"
udp = resp[UDP]
assert(udp.sport == dport), "unexpected UDP sport: {}".format(udp.sport)
assert(udp.dport == sport), "unexpected UDP dport: {}".format(udp.dport)
resp_payload = udp.payload.load
type_, length, magic = struct.unpack(">HHI", resp_payload[:8])
tid = resp_payload[8:20]
data = resp_payload[20:]
assert(type_ == 0x0101), "expected type 0X0101, got 0x{:04x}".format(type_)
assert(length == 12), "expected length 12, got {}".format(length)
assert(magic == 0x2112a442), "expected magic 0x2112a442, got 0x{:08x}".format(magic)
assert(tid == b'\x00' * 12), "expected tid 0x000000000000000000000000, got {:x}".format(tid)
assert(data == bytes.fromhex("000100080001") + struct.pack(">H", sport) + bytes.fromhex("00000000")), "unexpected data"
@test
def test_ipv6_udp_stun(iface):
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478]
payload = bytes.fromhex("000100002112a442000000000000000000000000")
for sport in sports:
for dport in dports:
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/UDP(sport=sport, dport=dport)/Raw(payload)
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert(UDP in resp)
udp = resp[UDP]
assert(udp.sport == dport)
assert(udp.dport == sport)
resp_payload = udp.payload.load
type_, length, magic = struct.unpack(">HHI", resp_payload[:8])
tid = resp_payload[8:20]
data = resp_payload[20:]
assert(type_ == 0x0101), "expected type 0X0101, got 0x{:04x}".format(type_)
assert(length == 24), "expected length 24, got {}".format(length)
assert(magic == 0x2112a442), "expected magic 0x2112a442, got 0x{:08x}".format(magic)
assert(tid == b'\x00' * 12), "expected tid 0x000000000000000000000000, got {:x}".format(tid)
assert(data == bytes.fromhex("000100140002") + struct.pack(">H", sport) + bytes.fromhex("00000000" * 4)), "unexpected data: {}".format(data)
@test
def test_ipv4_udp_stun_change_port(iface):
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478, 65535]
payload = bytes.fromhex("0001000803a3b9464dd8eb75e19481474293845c0003000400000002")
for sport in sports:
for dport in dports:
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/UDP(sport=sport, dport=dport)/Raw(payload)
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ip_checksum(resp)
assert(UDP in resp), "no UDP layer found"
udp = resp[UDP]
assert(udp.sport == (dport + 1) % 2**16), "expected answer from UDP/{}, got it from UDP/{}".format((dport + 1) % 2**16, udp.sport)
assert(udp.dport == sport), "expected answer to UDP/{}, got it to UDP/{}".format(sport, udp.dport)
resp_payload = udp.payload.load
type_, length = struct.unpack(">HH", resp_payload[:4])
tid = resp_payload[4:20]
data = resp_payload[20:]
assert(type_ == 0x0101), "expected type 0X0101, got 0x{:04x}".format(type_)
assert(length == 12), "expected length 12, got {}".format(length)
assert(tid == bytes.fromhex("03a3b9464dd8eb75e19481474293845c")), "expected tid 0x03a3b9464dd8eb75e19481474293845c, got %r" % tid
assert(data == bytes.fromhex("000100080001") + struct.pack(">H", sport) + bytes.fromhex("00000000")), "unexpected data"
@test
def test_ipv6_udp_stun_change_port(iface):
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478, 65535]
payload = bytes.fromhex("0001000803a3b9464dd8eb75e19481474293845c0003000400000002")
for sport in sports:
for dport in dports:
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/UDP(sport=sport, dport=dport)/Raw(payload)
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert(UDP in resp), "expecting UDP layer in answer, got nothing"
udp = resp[UDP]
assert(udp.sport == (dport + 1) % 2**16), "expected answer from UDP/{}, got it from UDP/{}".format((dport + 1) % 2**16, udp.sport)
assert(udp.dport == sport), "expected answer to UDP/{}, got it to UDP/{}".format(sport, udp.dport)
resp_payload = udp.payload.load
type_, length = struct.unpack(">HH", resp_payload[:4])
tid = resp_payload[4:20]
data = resp_payload[20:]
assert(type_ == 0x0101), "expected type 0X0101, got 0x{:04x}".format(type_)
assert(length == 24), "expected length 12, got {}".format(length)
assert(tid == bytes.fromhex("03a3b9464dd8eb75e19481474293845c")), "expected tid 0x03a3b9464dd8eb75e19481474293845c, got %r" % tid
assert(data == bytes.fromhex("000100140002") + struct.pack(">H", sport) + bytes.fromhex("00000000" * 4))
@test
def test_ipv4_tcp_ssh(iface):
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
banner = [b"SSH-2.0-AsyncSSH_2.1.0", b"SSH-2.0-PuTTY", b"SSH-2.0-libssh2_1.4.3", b"SSH-2.0-Go", b"SSH-2.0-PUTTY"][i % 5]
syn = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="S", sport=sport, dport=dport)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="A", sport=sport, dport=dport, ack=syn_ack.seq + 1)
_ = iface.sr1(ack, timeout=1)
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/TCP(flags="PA", ack=syn_ack.seq + 1, sport=sport, dport=dport)/Raw(banner + b"\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ip_checksum(resp)
assert(TCP in resp)
tcp = resp[TCP]
assert("A" in tcp.flags), "expecting ACK flag, not set (%r)" % tcp.flags
assert("P" in tcp.flags), "expecting PSH flag, not set (%r)" % tcp.flags
assert(len(tcp.payload) > 0), "expecting payload, got none"
assert(tcp.payload.load.startswith(b"SSH-2.0-")), "unexpected banner: %r" % tcp.payload.load
assert(tcp.payload.load.endswith(b"\r\n")), "unexpected banner: %r" % tcp.payload.load
@test
def test_ipv4_udp_ssh(iface):
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
banner = [b"SSH-2.0-AsyncSSH_2.1.0", b"SSH-2.0-PuTTY", b"SSH-2.0-libssh2_1.4.3", b"SSH-2.0-Go", b"SSH-2.0-PUTTY"][i % 5]
req = Ether(dst=MAC_ADDR)/IP(dst=IPV4_ADDR)/UDP(sport=sport, dport=dport)/Raw(banner + b"\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ip_checksum(resp)
assert(UDP in resp)
udp = resp[UDP]
assert(len(udp.payload) > 0), "expecting payload, got none"
assert(udp.payload.load.startswith(b"SSH-2.0-")), "unexpected banner: %r" % udp.payload.load
assert(udp.payload.load.endswith(b"\r\n")), "unexpected banner: %r" % udp.payload.load
@test
def test_ipv6_tcp_ssh(iface):
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
banner = [b"SSH-2.0-AsyncSSH_2.1.0", b"SSH-2.0-PuTTY", b"SSH-2.0-libssh2_1.4.3", b"SSH-2.0-Go", b"SSH-2.0-PUTTY"][i % 5]
syn = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="S", sport=sport, dport=dport)
syn_ack = iface.sr1(syn, timeout=1)
assert(syn_ack is not None), "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert(TCP in syn_ack)
syn_ack = syn_ack[TCP]
assert(syn_ack.flags == "SA")
ack = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="A", sport=sport, dport=dport, ack=syn_ack.seq + 1)
_ = iface.sr1(ack, timeout=1)
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/TCP(flags="PA", ack=syn_ack.seq + 1, sport=sport, dport=dport)/Raw(banner + b"\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert(TCP in resp)
tcp = resp[TCP]
assert("A" in tcp.flags), "expecting ACK flag, not set (%r)" % tcp.flags
assert("P" in tcp.flags), "expecting PSH flag, not set (%r)" % tcp.flags
assert(len(tcp.payload) > 0), "expecting payload, got none"
assert(tcp.payload.load.startswith(b"SSH-2.0-")), "unexpected banner: %r" % tcp.payload.load
assert(tcp.payload.load.endswith(b"\r\n")), "unexpected banner: %r" % tcp.payload.load
@test
def test_ipv6_udp_ssh(iface):
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
banner = [b"SSH-2.0-AsyncSSH_2.1.0", b"SSH-2.0-PuTTY", b"SSH-2.0-libssh2_1.4.3", b"SSH-2.0-Go", b"SSH-2.0-PUTTY"][i % 5]
req = Ether(dst=MAC_ADDR)/IPv6(dst=IPV6_ADDR)/UDP(sport=sport, dport=dport)/Raw(banner + b"\r\n")
resp = iface.sr1(req, timeout=1)
assert(resp is not None), "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert(UDP in resp)
udp = resp[UDP]
assert(len(udp.payload) > 0), "expecting payload, got none"
assert(udp.payload.load.startswith(b"SSH-2.0-")), "unexpected banner: %r" % udp.payload.load
assert(udp.payload.load.endswith(b"\r\n")), "unexpected banner: %r" % udp.payload.load
def test_all(iface):
global tests
# execute tests
for t in tests:
t(iface)

102
test/src/core.py Normal file
View file

@ -0,0 +1,102 @@
# This file is part of masscanned.
# Copyright 2021 - 2025 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
import logging
from scapy.compat import raw
from scapy.layers.inet import IP
from scapy.layers.inet6 import IPv6
def setup_logs():
log = logging.getLogger()
log.setLevel(logging.DEBUG)
if not log.handlers:
ch = logging.StreamHandler()
ch.setFormatter(logging.Formatter("%(levelname)s\t%(message)s"))
ch.setLevel(logging.DEBUG)
log.addHandler(ch)
return log
LOG = setup_logs()
TESTS = []
ERRORS = []
# decorator to automatically add a function to tests
def test(f):
OK = "\033[1mOK\033[0m"
KO = "\033[1m\033[1;%dmKO\033[0m" % 31
fname = f.__name__.ljust(50, ".")
def w(m):
try:
# check that masscanned is still running
assert m.poll() is None, "masscanned not running"
f()
# check that masscanned is still running
assert m.poll() is None, "masscanned terminated unexpectedly"
LOG.info("{}{}".format(fname, OK))
except AssertionError as e:
LOG.error("{}{}: {}".format(fname, KO, e))
ERRORS.append(fname)
TESTS.append(w)
return w
def test_all(m):
# execute tests
for t in TESTS:
# perform unit test
t(m)
LOG.info(f"\033[1mRan {len(TESTS)} tests with {len(ERRORS)} errors\033[0m")
return len(ERRORS)
def multicast(ip6):
a, b = ip6.split(":")[-2:]
mac = ["33", "33", "ff"]
if len(a) == 4:
mac.append(a[2:])
else:
mac.append("00")
if len(b) >= 2:
mac.append(b[:2])
else:
mac.append("00")
if len(b) >= 4:
mac.append(b[2:])
else:
mac.append("00")
return ":".join(mac)
def check_ip_checksum(pkt):
assert IP in pkt, "no IP layer found"
ip_pkt = pkt[IP]
chksum = ip_pkt.chksum
del ip_pkt.chksum
assert IP(raw(ip_pkt)).chksum == chksum, "bad IPv4 checksum"
def check_ipv6_checksum(pkt):
assert IPv6 in pkt, "no IP layer found"
ip_pkt = pkt[IPv6]
chksum = ip_pkt.chksum
del ip_pkt.chksum
assert IPv6(raw(ip_pkt)).chksum == chksum, "bad IPv6 checksum"

View file

@ -0,0 +1,15 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.

51
test/src/tests/arp.py Normal file
View file

@ -0,0 +1,51 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.l2 import Ether, ARP, ETHER_BROADCAST
from scapy.sendrecv import srp1
from ..conf import IPV4_ADDR, MAC_ADDR
from ..core import test
@test
def test_arp_req():
##### ARP #####
arp_req = Ether(dst=ETHER_BROADCAST) / ARP(pdst=IPV4_ADDR)
arp_repl = srp1(arp_req, timeout=1)
assert arp_repl is not None, "expecting answer, got nothing"
assert ARP in arp_repl, "no ARP layer found"
arp_repl = arp_repl[ARP]
# check answer
## op is "is-at"
assert arp_repl.op == 2, "unexpected ARP op: {}".format(arp_repl.op)
## answer for the requested IP
assert arp_repl.psrc == arp_req.pdst, "unexpected ARP psrc: {}".format(
arp_repl.psrc
)
assert arp_repl.pdst == arp_req.psrc, "unexpected ARP pdst: {}".format(
arp_repl.pdst
)
## answer is expected MAC address
assert arp_repl.hwsrc == MAC_ADDR, "unexpected ARP hwsrc: {}".format(arp_repl.hwsrc)
@test
def test_arp_req_other_ip():
##### ARP #####
arp_req = Ether(dst=ETHER_BROADCAST) / ARP(pdst="1.2.3.4")
arp_repl = srp1(arp_req, timeout=1)
assert arp_repl is None, "responding to ARP requests for other IP addresses"

158
test/src/tests/dns.py Normal file
View file

@ -0,0 +1,158 @@
# This file is part of masscanned.
# Copyright 2022 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.compat import raw
from scapy.layers.dns import DNS, DNSQR
from scapy.layers.inet import IP, UDP
from scapy.layers.l2 import Ether
from scapy.sendrecv import srp1
from ..conf import IPV4_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum
@test
def test_ipv4_udp_dns_in_a():
sports = [53, 13274, 0]
dports = [53, 5353, 80, 161, 24732]
for sport in sports:
for dport in dports:
for domain in ["example.com", "www.example.com", "masscan.ned"]:
qd = DNSQR(qname=domain, qtype="A", qclass="IN")
dns_req = DNS(id=1234, rd=False, opcode=0, qd=qd)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ UDP(sport=sport, dport=dport)
/ dns_req
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert UDP in resp, "no UDP layer found"
udp = resp[UDP]
assert (
udp.sport == dport
), f"unexpected UDP sport: {udp.sport!r} ({domain})"
assert (
udp.dport == sport
), f"unexpected UDP dport: {udp.dport!r} ({domain})"
if DNS not in udp:
try:
dns_rep = DNS(udp.load)
except Exception:
raise AssertionError("no DNS layer found")
else:
dns_rep = udp[DNS]
assert (
dns_rep.id == 1234
), f"unexpected id value: {dns_rep.id!r} ({domain})"
assert dns_rep.qr, "unexpected qr value"
assert dns_rep.opcode == 0, "unexpected opcode value"
assert dns_rep.aa, "unexpected aa value"
assert not dns_rep.tc, "unexpected tc value"
assert not dns_rep.rd, "unexpected rd value"
assert not dns_rep.ra, "unexpected ra value"
assert dns_rep.z == 0, "unexpected z value"
assert dns_rep.rcode == 0, "unexpected rcode value"
assert (
dns_rep.qdcount == 1
), f"unexpected qdcount value: {dns_rep.qdcount!r} vs 1 ({domain})"
assert dns_rep.ancount == 1, "unexpected ancount value"
assert dns_rep.nscount == 0, "unexpected nscount value"
assert dns_rep.arcount == 0, "unexpected arcount value"
assert raw(dns_rep.qd[0]) == raw(
dns_req.qd[0]
), "query in request and response do not match"
assert raw(dns_rep.qd[0].qname) == raw(
dns_req.qd[0].qname
), "qname query in request and response do not match"
assert (
dns_rep.an[0].rrname == dns_req.qd[0].qname
), "rrname in answer does not match qname in request"
assert (
dns_rep.an[0].rclass == dns_req.qd[0].qclass
), "class in answer does not match query"
assert (
dns_rep.an[0].type == dns_req.qd[0].qtype
), "type in answer does not match query"
assert dns_rep.an[0].rdata == IPV4_ADDR
@test
def test_ipv4_udp_dns_in_a_multiple_queries():
sports = [53, 13274, 12198, 888, 0]
dports = [53, 5353, 80, 161, 24732]
for sport in sports:
for dport in dports:
qd = [
DNSQR(qname="www.example1.com", qtype="A", qclass="IN"),
DNSQR(qname="www.example2.com", qtype="A", qclass="IN"),
DNSQR(qname="www.example3.com", qtype="A", qclass="IN"),
]
dns_req = DNS(id=1234, rd=False, opcode=0, qd=qd)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ UDP(sport=sport, dport=dport)
/ dns_req
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert UDP in resp, "no UDP layer found"
udp = resp[UDP]
assert udp.sport == dport, "unexpected UDP sport: {}".format(udp.sport)
assert udp.dport == sport, "unexpected UDP dport: {}".format(udp.dport)
if DNS not in udp:
try:
dns_rep = DNS(udp.load)
except Exception:
raise AssertionError("no DNS layer found")
else:
dns_rep = udp[DNS]
assert dns_rep.id == 1234, f"unexpected id value: {dns_rep.id}"
assert dns_rep.qr, "unexpected qr value"
assert dns_rep.opcode == 0, "unexpected opcode value"
assert dns_rep.aa, "unexpected aa value"
assert not dns_rep.tc, "unexpected tc value"
assert not dns_rep.rd, "unexpected rd value"
assert not dns_rep.ra, "unexpected ra value"
assert dns_rep.z == 0, "unexpected z value"
assert dns_rep.rcode == 0, "unexpected rcode value"
assert (
dns_rep.qdcount == 3
), f"unexpected qdcount value: {dns_rep.qdcount} vs 3"
assert dns_rep.ancount == 3, "unexpected ancount value"
assert dns_rep.nscount == 0, "unexpected nscount value"
assert dns_rep.arcount == 0, "unexpected arcount value"
for i, q in enumerate(qd):
assert raw(dns_rep.qd[i]) == raw(
dns_req.qd[i]
), f"query in request and response do not match ({i})"
assert raw(dns_rep.qd[i].qname) == raw(
dns_req.qd[i].qname
), f"qname query in request and response do not match ({i})"
assert (
dns_rep.an[i].rrname == dns_req.qd[i].qname
), f"rrname in answer does not match qname in request ({i})"
assert (
dns_rep.an[i].rclass == dns_req.qd[i].qclass
), f"class in answer does not match query ({i})"
assert (
dns_rep.an[i].type == dns_req.qd[i].qtype
), f"type in answer does not match query ({i})"
assert dns_rep.an[i].rdata == IPV4_ADDR

87
test/src/tests/ghost.py Normal file
View file

@ -0,0 +1,87 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
import struct
import zlib
from scapy.compat import raw
from scapy.layers.inet import IP, TCP
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.sendrecv import srp1
from scapy.volatile import RandInt
from ..conf import IPV4_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum
@test
def test_ipv4_tcp_ghost():
sport = 37184
dports = [22, 23874]
for dport in dports:
seq_init = int(RandInt())
banner = b"Gh0st\xad\x00\x00\x00\xe0\x00\x00\x00x\x9cKS``\x98\xc3\xc0\xc0\xc0\x06\xc4\x8c@\xbcQ\x96\x81\x81\tH\x07\xa7\x16\x95e&\xa7*\x04$&g+\x182\x94\xf6\xb000\xac\xa8rc\x00\x01\x11\xa0\x82\x1f\\`&\x83\xc7K7\x86\x19\xe5n\x0c9\x95n\x0c;\x84\x0f3\xac\xe8sch\xa8^\xcf4'J\x97\xa9\x82\xe30\xc3\x91h]&\x90\xf8\xce\x97S\xcbA4L?2=\xe1\xc4\x92\x86\x0b@\xf5`\x0cT\x1f\xae\xaf]\nr\x0b\x03#\xa3\xdc\x02~\x06\x86\x03+\x18m\xc2=\xfdtC,C\xfdL<<==\\\x9d\x19\x88\x00\xe5 \x02\x00T\xf5+\\"
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA"
ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw(banner)
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
tcp = resp[TCP]
assert "A" in tcp.flags, "expecting ACK flag, not set (%r)" % tcp.flags
assert "P" in tcp.flags, "expecting PSH flag, not set (%r)" % tcp.flags
data = raw(tcp.payload)
assert data, "expecting payload, got none"
assert data.startswith(b"Gh0st"), "unexpected banner: %r" % tcp.payload.load
data_len, uncompressed_len = struct.unpack("<II", data[5:13])
assert len(data) == data_len, "invalid Ghost payload: %r" % data
assert len(zlib.decompress(data[13:])) == uncompressed_len, (
"invalid Ghost payload: %r" % data
)

409
test/src/tests/http.py Normal file
View file

@ -0,0 +1,409 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.inet import IP, TCP, UDP
from scapy.layers.inet6 import IPv6
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.sendrecv import srp1
from scapy.volatile import RandInt
from ..conf import IPV4_ADDR, IPV6_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum, check_ipv6_checksum
@test
def test_ipv4_tcp_http():
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA", "expecting TCP SA, got %r" % syn_ack.flags
ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw("GET / HTTP/1.1\r\n\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
tcp = resp[TCP]
assert tcp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n")
@test
def test_ipv4_tcp_http_segmented():
sport = 24593
dports = [80, 443, 5000, 53228]
for dport in dports:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA", "expecting TCP SA, got %r" % syn_ack.flags
ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
# request is not complete yet
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw("GET / HTTP/1.1\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
assert resp[TCP].flags == "A", (
'expecting TCP flag "A", got %r' % resp[TCP].flags
)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + len(req) + 1,
ack=syn_ack.seq + 1,
)
/ Raw("\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
tcp = resp[TCP]
assert tcp.flags == "PA"
assert tcp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n")
@test
def test_ipv4_tcp_http_incomplete():
sport = 24595
dports = [80, 443, 5000, 53228]
for dport in dports:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA", "expecting TCP SA, got %r" % syn_ack.flags
ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
# purposedly incomplete request (missing additionnal ending \r\n)
/ Raw("GET / HTTP/1.1\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting an answer, got none"
check_ip_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
tcp = resp[TCP]
assert tcp.flags == "A", "expecting TCP flag A, got {}".format(tcp.flags)
@test
def test_ipv6_tcp_http():
sport = 24594
dports = [80, 443, 5000, 53228]
for dport in dports:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA"
ack = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw("GET / HTTP/1.1\r\n\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
tcp = resp[TCP]
assert tcp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n")
@test
def test_ipv4_udp_http():
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw("GET / HTTP/1.1\r\n\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert UDP in resp
udp = resp[UDP]
assert udp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n")
@test
def test_ipv6_udp_http():
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw("GET / HTTP/1.1\r\n\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert UDP in resp
udp = resp[UDP]
assert udp.payload.load.startswith(b"HTTP/1.1 401 Unauthorized\n")
@test
def test_ipv4_tcp_http_ko():
sport = 24596
dports = [80, 443, 5000, 53228]
for dport in dports:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA"
ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw(bytes.fromhex("4f5054494f4e53"))
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
assert "P" not in resp[TCP].flags
assert len(resp[TCP].payload) == 0
@test
def test_ipv4_udp_http_ko():
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(bytes.fromhex("4f5054494f4e53"))
)
resp = srp1(req, timeout=1)
assert resp is None, "expecting no answer, got one"
@test
def test_ipv6_tcp_http_ko():
sport = 24597
dports = [80, 443, 5000, 53228]
for dport in dports:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA"
ack = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw(bytes.fromhex("4f5054494f4e53"))
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
assert "P" not in resp[TCP].flags
assert len(resp[TCP].payload) == 0
@test
def test_ipv6_udp_http_ko():
sport = 24592
dports = [80, 443, 5000, 53228]
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(bytes.fromhex("4f5054494f4e53"))
)
resp = srp1(req, timeout=1)
assert resp is None, "expecting no answer, got one"

45
test/src/tests/icmpv4.py Normal file
View file

@ -0,0 +1,45 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.inet import IP, ICMP
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.sendrecv import srp1
from ..conf import IPV4_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum
@test
def test_icmpv4_echo_req():
##### ICMPv4 #####
icmp_req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ ICMP(type=8, code=0)
/ Raw("idrinkwaytoomuchcoffee")
)
icmp_repl = srp1(icmp_req, timeout=1)
assert icmp_repl is not None, "expecting answer, got nothing"
check_ip_checksum(icmp_repl)
assert ICMP in icmp_repl
icmp_repl = icmp_repl[ICMP]
# check answer
## type is "echo-reply"
assert icmp_repl.type == 0
assert icmp_repl.code == 0
## data is the same as sent
assert icmp_repl.load == icmp_req.load

87
test/src/tests/icmpv6.py Normal file
View file

@ -0,0 +1,87 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.inet6 import (
ICMPv6EchoReply,
ICMPv6EchoRequest,
ICMPv6NDOptDstLLAddr,
ICMPv6ND_NA,
ICMPv6ND_NS,
IPv6,
)
from scapy.layers.l2 import Ether
from scapy.sendrecv import srp1
from ..conf import IPV6_ADDR, MAC_ADDR
from ..core import test, multicast
@test
def test_icmpv6_neighbor_solicitation():
##### IPv6 Neighbor Solicitation #####
for mac in [
"ff:ff:ff:ff:ff:ff",
"33:33:00:00:00:01",
MAC_ADDR,
multicast(IPV6_ADDR),
]:
nd_ns = Ether(dst=mac) / IPv6() / ICMPv6ND_NS(tgt=IPV6_ADDR)
nd_na = srp1(nd_ns, timeout=1)
assert nd_na is not None, "expecting answer, got nothing"
assert ICMPv6ND_NA in nd_na
nd_na = nd_na[ICMPv6ND_NA]
# check answer content
assert nd_na.code == 0
assert nd_na.R == 0
assert nd_na.S == 1
assert nd_na.O == 1 # noqa: E741
assert nd_na.tgt == IPV6_ADDR
# check ND Option
assert nd_na.haslayer(ICMPv6NDOptDstLLAddr)
assert nd_na.getlayer(ICMPv6NDOptDstLLAddr).lladdr == MAC_ADDR
for mac in ["00:00:00:00:00:00", "33:33:33:00:00:01"]:
nd_ns = Ether(dst="ff:ff:ff:ff:ff:ff") / IPv6() / ICMPv6ND_NS(tgt=IPV6_ADDR)
nd_na = srp1(nd_ns, timeout=1)
assert nd_na is not None, "expecting no answer, got one"
@test
def test_icmpv6_neighbor_solicitation_other_ip():
##### IPv6 Neighbor Solicitation #####
nd_ns = (
Ether(dst="ff:ff:ff:ff:ff:ff")
/ IPv6()
/ ICMPv6ND_NS(tgt="2020:4141:3030:2020::bdbd")
)
nd_na = srp1(nd_ns, timeout=1)
assert nd_na is None, "responding to ND_NS for other IP addresses"
@test
def test_icmpv6_echo_req():
##### IPv6 Ping #####
echo_req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ ICMPv6EchoRequest(data="waytoomanynapkins")
)
echo_repl = srp1(echo_req, timeout=1)
assert echo_repl is not None, "expecting answer, got nothing"
assert ICMPv6EchoReply in echo_repl
echo_repl = echo_repl[ICMPv6EchoReply]
# check answer content
assert echo_repl.code == 0
assert echo_repl.data == echo_req.data

50
test/src/tests/ip.py Normal file
View file

@ -0,0 +1,50 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.inet import IP, ICMP
from scapy.layers.l2 import Ether
from scapy.sendrecv import srp1
from ..conf import IPV4_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum
@test
def test_ipv4_req():
##### IP #####
ip_req = Ether(dst=MAC_ADDR) / IP(dst=IPV4_ADDR, id=0x1337) / ICMP(type=8, code=0)
ip_repl = srp1(ip_req, timeout=1)
assert ip_repl is not None, "expecting answer, got nothing"
check_ip_checksum(ip_repl)
assert IP in ip_repl, "no IP layer in response"
ip_repl = ip_repl[IP]
assert ip_repl.id == 0, "IP identification unexpected"
@test
def test_eth_req_other_mac():
#### ETH ####
ip_req = Ether(dst="00:00:00:11:11:11") / IP(dst=IPV4_ADDR) / ICMP(type=8, code=0)
ip_repl = srp1(ip_req, timeout=1)
assert ip_repl is None, "responding to other MAC addresses"
@test
def test_ipv4_req_other_ip():
##### IP #####
ip_req = Ether(dst=MAC_ADDR) / IP(dst="1.2.3.4") / ICMP(type=8, code=0)
ip_repl = srp1(ip_req, timeout=1)
assert ip_repl is None, "responding to other IP addresses"

112
test/src/tests/rpc.py Normal file
View file

@ -0,0 +1,112 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from subprocess import check_call
from tempfile import NamedTemporaryFile
import json
import os
import re
from ivre.db import DBNmap
from ..conf import IPV4_ADDR
from ..core import test
@test
def test_rpc_nmap():
for scan in "SU":
with NamedTemporaryFile(delete=False) as xml_result:
check_call(
[
"nmap",
"-n",
"-vv",
"-oX",
"-",
IPV4_ADDR,
f"-s{scan}V",
"-p",
"111",
"--script",
"rpcinfo,rpc-grind",
],
stdout=xml_result,
)
with NamedTemporaryFile(delete=False, mode="w") as json_result:
DBNmap(output=json_result).store_scan(xml_result.name)
os.unlink(xml_result.name)
with open(json_result.name) as fdesc:
results = [json.loads(line) for line in fdesc]
os.unlink(json_result.name)
assert len(results) == 1, f"Expected 1 result, got {len(results)}"
result = results[0]
assert len(result["ports"]) == 1, f"Expected 1 port, got {len(result['ports'])}"
port = result["ports"][0]
assert port["port"] == 111, f"Expected port 111, got {port['port']}"
assert port["protocol"] == (
"tcp" if scan == "S" else "udp"
), f"Unexpected proto {port['protocol']} for scan {scan}"
assert port["service_name"] in {
"nfs",
"rpcbind",
"rstatd",
"rusersd",
}, f"Unexpected service_name: {port['service_name']}"
assert port["service_extrainfo"] in {
"RPC #100000",
"RPC #100001",
"RPC #100002",
"RPC #100003",
}, f"Unexpected service_extrainfo: {port['service_extrainfo']}"
assert (
len(port["scripts"]) == 1
), f"Expected 1 script, got {len(port['scripts'])}"
script = port["scripts"][0]
assert script["id"] == "rpcinfo", "Expected rpcinfo script, not found"
assert (
len(script["rpcinfo"]) == 1
), f"Expected 1 rpcinfo, got {len(script['rpcinfo'])}"
@test
def test_rpcinfo():
with NamedTemporaryFile(delete=False) as rpcout:
check_call(["rpcinfo", "-p", IPV4_ADDR], stdout=rpcout)
with open(rpcout.name) as fdesc:
found = []
for line in fdesc:
line = line.split()
if line[0] == "program":
# header
continue
assert line[0] == "100000", f"Expected program 100000, got {line[0]}"
found.append(int(line[1]))
assert len(found) == 3, f"Expected three versions, got {found}"
for i in range(2, 5):
assert i in found, f"Missing version {i} in {found}"
os.unlink(rpcout.name)
with NamedTemporaryFile(delete=False) as rpcout:
check_call(["rpcinfo", "-u", IPV4_ADDR, "100000"], stdout=rpcout)
with open(rpcout.name) as fdesc:
found = []
expr = re.compile("^program 100000 version ([0-9]) ready and waiting$")
for line in fdesc:
found.append(int(expr.search(line.strip()).group(1)))
assert len(found) == 3, f"Expected three versions, got {found}"
for i in range(2, 5):
assert i in found, f"Missing version {i} in {found}"
os.unlink(rpcout.name)

65
test/src/tests/smb.py Normal file
View file

@ -0,0 +1,65 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
import subprocess
from ..core import test
from ..conf import IPV4_ADDR
@test
def test_smb1_network_req():
proc = subprocess.Popen(
[
"smbclient",
"-U ''",
"-N",
"-d 6",
"-t 1",
"-L",
IPV4_ADDR,
"--option=client min protocol=NT1",
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
out, _ = proc.communicate()
assert f"Connecting to {IPV4_ADDR} at port 445" in out, "\n" + out
assert "session request ok" in out, "\n" + out
assert f"negotiated dialect[NT1] against server[{IPV4_ADDR}]" in out, "\n" + out
@test
def test_smb2_network_req():
proc = subprocess.Popen(
[
"smbclient",
"-U ''",
"-N",
"-d 5",
"-t 1",
"-L",
IPV4_ADDR,
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
out, _ = proc.communicate()
assert f"Connecting to {IPV4_ADDR} at port 445" in out, "\n" + out
assert "session request ok" in out, "\n" + out
assert f"negotiated dialect[SMB2_02] against server[{IPV4_ADDR}]" in out, "\n" + out

217
test/src/tests/ssh.py Normal file
View file

@ -0,0 +1,217 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.inet import IP, TCP, UDP
from scapy.layers.inet6 import IPv6
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.sendrecv import srp1
from scapy.volatile import RandInt
from ..conf import IPV4_ADDR, IPV6_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum, check_ipv6_checksum
@test
def test_ipv4_tcp_ssh():
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
seq_init = int(RandInt())
banner = [
b"SSH-2.0-AsyncSSH_2.1.0",
b"SSH-2.0-PuTTY",
b"SSH-2.0-libssh2_1.4.3",
b"SSH-2.0-Go",
b"SSH-2.0-PUTTY",
][i % 5]
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA"
ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw(banner + b"\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
tcp = resp[TCP]
assert "A" in tcp.flags, "expecting ACK flag, not set (%r)" % tcp.flags
assert "P" in tcp.flags, "expecting PSH flag, not set (%r)" % tcp.flags
assert len(tcp.payload) > 0, "expecting payload, got none"
assert tcp.payload.load.startswith(b"SSH-2.0-"), (
"unexpected banner: %r" % tcp.payload.load
)
assert tcp.payload.load.endswith(b"\r\n"), (
"unexpected banner: %r" % tcp.payload.load
)
@test
def test_ipv4_udp_ssh():
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
banner = [
b"SSH-2.0-AsyncSSH_2.1.0",
b"SSH-2.0-PuTTY",
b"SSH-2.0-libssh2_1.4.3",
b"SSH-2.0-Go",
b"SSH-2.0-PUTTY",
][i % 5]
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(banner + b"\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert UDP in resp
udp = resp[UDP]
assert len(udp.payload) > 0, "expecting payload, got none"
assert udp.payload.load.startswith(b"SSH-2.0-"), (
"unexpected banner: %r" % udp.payload.load
)
assert udp.payload.load.endswith(b"\r\n"), (
"unexpected banner: %r" % udp.payload.load
)
@test
def test_ipv6_tcp_ssh():
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
seq_init = int(RandInt())
banner = [
b"SSH-2.0-AsyncSSH_2.1.0",
b"SSH-2.0-PuTTY",
b"SSH-2.0-libssh2_1.4.3",
b"SSH-2.0-Go",
b"SSH-2.0-PUTTY",
][i % 5]
syn = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(flags="S", sport=sport, dport=dport, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA"
ack = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(
flags="A",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
)
_ = srp1(ack, timeout=1)
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(
flags="PA",
sport=sport,
dport=dport,
seq=seq_init + 1,
ack=syn_ack.seq + 1,
)
/ Raw(banner + b"\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert TCP in resp, "expecting TCP, got %r" % resp.summary()
tcp = resp[TCP]
assert "A" in tcp.flags, "expecting ACK flag, not set (%r)" % tcp.flags
assert "P" in tcp.flags, "expecting PSH flag, not set (%r)" % tcp.flags
assert len(tcp.payload) > 0, "expecting payload, got none"
assert tcp.payload.load.startswith(b"SSH-2.0-"), (
"unexpected banner: %r" % tcp.payload.load
)
assert tcp.payload.load.endswith(b"\r\n"), (
"unexpected banner: %r" % tcp.payload.load
)
@test
def test_ipv6_udp_ssh():
sport = 37183
dports = [22, 80, 2222, 2022, 23874, 50000]
for i, dport in enumerate(dports):
banner = [
b"SSH-2.0-AsyncSSH_2.1.0",
b"SSH-2.0-PuTTY",
b"SSH-2.0-libssh2_1.4.3",
b"SSH-2.0-Go",
b"SSH-2.0-PUTTY",
][i % 5]
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(banner + b"\r\n")
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert UDP in resp
udp = resp[UDP]
assert len(udp.payload) > 0, "expecting payload, got none"
assert udp.payload.load.startswith(b"SSH-2.0-"), (
"unexpected banner: %r" % udp.payload.load
)
assert udp.payload.load.endswith(b"\r\n"), (
"unexpected banner: %r" % udp.payload.load
)

196
test/src/tests/stun.py Normal file
View file

@ -0,0 +1,196 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from socket import AF_INET6
import struct
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.pton_ntop import inet_pton
from scapy.sendrecv import srp1
from ..conf import IPV4_ADDR, IPV6_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum, check_ipv6_checksum
@test
def test_ipv4_udp_stun():
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478]
payload = bytes.fromhex("000100002112a442000000000000000000000000")
for sport in sports:
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(payload)
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert UDP in resp, "no UDP layer found"
udp = resp[UDP]
assert udp.sport == dport, "unexpected UDP sport: {}".format(udp.sport)
assert udp.dport == sport, "unexpected UDP dport: {}".format(udp.dport)
resp_payload = udp.payload.load
type_, length, magic = struct.unpack(">HHI", resp_payload[:8])
tid = resp_payload[8:20]
data = resp_payload[20:]
assert type_ == 0x0101, "expected type 0X0101, got 0x{:04x}".format(type_)
assert length == 12, "expected length 12, got {}".format(length)
assert (
magic == 0x2112A442
), "expected magic 0x2112a442, got 0x{:08x}".format(magic)
assert (
tid == b"\x00" * 12
), "expected tid 0x000000000000000000000000, got {:x}".format(tid)
expected_data = b"\x00\x01\x00\x08\x00\x01" + struct.pack(
">HBBBB", sport, 192, 0, 0, 0
)
assert (
data == expected_data
), f"unexpected data {data!r} != {expected_data!r}"
@test
def test_ipv6_udp_stun():
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478]
payload = bytes.fromhex("000100002112a442000000000000000000000000")
for sport in sports:
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(payload)
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert UDP in resp
udp = resp[UDP]
assert udp.sport == dport
assert udp.dport == sport
resp_payload = udp.payload.load
type_, length, magic = struct.unpack(">HHI", resp_payload[:8])
tid = resp_payload[8:20]
data = resp_payload[20:]
assert type_ == 0x0101, "expected type 0X0101, got 0x{:04x}".format(type_)
assert length == 24, "expected length 24, got {}".format(length)
assert (
magic == 0x2112A442
), "expected magic 0x2112a442, got 0x{:08x}".format(magic)
assert (
tid == b"\x00" * 12
), "expected tid 0x000000000000000000000000, got {:x}".format(tid)
expected_data = (
bytes.fromhex("000100140002")
+ struct.pack(">H", sport)
+ inet_pton(AF_INET6, "2001:41d0::1234:5678")
)
assert data == expected_data, "unexpected data: {}".format(data)
@test
def test_ipv4_udp_stun_change_port():
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478, 65535]
payload = bytes.fromhex("0001000803a3b9464dd8eb75e19481474293845c0003000400000002")
for sport in sports:
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(payload)
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ip_checksum(resp)
assert UDP in resp, "no UDP layer found"
udp = resp[UDP]
assert (
udp.sport == (dport + 1) % 2**16
), "expected answer from UDP/{}, got it from UDP/{}".format(
(dport + 1) % 2**16, udp.sport
)
assert (
udp.dport == sport
), "expected answer to UDP/{}, got it to UDP/{}".format(sport, udp.dport)
resp_payload = udp.payload.load
type_, length = struct.unpack(">HH", resp_payload[:4])
tid = resp_payload[4:20]
data = resp_payload[20:]
assert type_ == 0x0101, "expected type 0X0101, got 0x{:04x}".format(type_)
assert length == 12, "expected length 12, got {}".format(length)
assert tid == bytes.fromhex("03a3b9464dd8eb75e19481474293845c"), (
"expected tid 0x03a3b9464dd8eb75e19481474293845c, got %r" % tid
)
expected_data = b"\x00\x01\x00\x08\x00\x01" + struct.pack(
">HBBBB", sport, 192, 0, 0, 0
)
assert (
data == expected_data
), f"unexpected data {data!r} != {expected_data!r}"
@test
def test_ipv6_udp_stun_change_port():
sports = [12345, 55555, 80, 43273]
dports = [80, 800, 8000, 3478, 65535]
payload = bytes.fromhex("0001000803a3b9464dd8eb75e19481474293845c0003000400000002")
for sport in sports:
for dport in dports:
req = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ UDP(sport=sport, dport=dport)
/ Raw(payload)
)
resp = srp1(req, timeout=1)
assert resp is not None, "expecting answer, got nothing"
check_ipv6_checksum(resp)
assert UDP in resp, "expecting UDP layer in answer, got nothing"
udp = resp[UDP]
assert (
udp.sport == (dport + 1) % 2**16
), "expected answer from UDP/{}, got it from UDP/{}".format(
(dport + 1) % 2**16, udp.sport
)
assert (
udp.dport == sport
), "expected answer to UDP/{}, got it to UDP/{}".format(sport, udp.dport)
resp_payload = udp.payload.load
type_, length = struct.unpack(">HH", resp_payload[:4])
tid = resp_payload[4:20]
data = resp_payload[20:]
assert type_ == 0x0101, "expected type 0X0101, got 0x{:04x}".format(type_)
assert length == 24, "expected length 12, got {}".format(length)
assert tid == bytes.fromhex("03a3b9464dd8eb75e19481474293845c"), (
"expected tid 0x03a3b9464dd8eb75e19481474293845c, got %r" % tid
)
expected_data = (
bytes.fromhex("000100140002")
+ struct.pack(">H", sport)
+ inet_pton(AF_INET6, "2001:41d0::1234:5678")
)
assert (
data == expected_data
), f"unexpected data {data!r} != {expected_data!r}"

303
test/src/tests/tcp.py Normal file
View file

@ -0,0 +1,303 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.inet import IP, TCP
from scapy.layers.inet6 import IPv6
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.sendrecv import srp1
from scapy.volatile import RandInt
from ..conf import IPV4_ADDR, IPV6_ADDR, MAC_ADDR
from ..core import test, check_ip_checksum, check_ipv6_checksum
@test
def test_ipv4_tcp_empty():
for p in [0, 80, 443]:
req = Ether(dst=MAC_ADDR) / IP(dst=IPV4_ADDR, proto=6) / Raw() # UDP
repl = srp1(req, timeout=1)
assert repl is None, "expecting no answer, got one"
@test
def test_ipv6_tcp_empty():
for p in [0, 80, 443]:
req = Ether(dst=MAC_ADDR) / IPv6(dst=IPV6_ADDR, nh=6) / Raw() # UDP
repl = srp1(req, timeout=1)
assert repl is None, "expecting no answer, got one"
@test
def test_tcp_syn():
##### SYN-ACK #####
# test a list of ports, randomly generated once
ports_to_test = [
1152,
2003,
2193,
3709,
4054,
6605,
6737,
6875,
7320,
8898,
9513,
9738,
10623,
10723,
11253,
12125,
12189,
12873,
14648,
14659,
16242,
16243,
17209,
17492,
17667,
17838,
18081,
18682,
18790,
19124,
19288,
19558,
19628,
19789,
20093,
21014,
21459,
21740,
24070,
24312,
24576,
26939,
27136,
27165,
27361,
29971,
31088,
33011,
33068,
34990,
35093,
35958,
36626,
36789,
37130,
37238,
37256,
37697,
37890,
38958,
42131,
43864,
44420,
44655,
44868,
45157,
46213,
46497,
46955,
49049,
49067,
49452,
49480,
50498,
50945,
51181,
52890,
53301,
53407,
53417,
53980,
55827,
56483,
58552,
58713,
58836,
59362,
59560,
60534,
60555,
60660,
61615,
62402,
62533,
62941,
63240,
63339,
63616,
64380,
65438,
]
for p in ports_to_test:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", dport=p, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA", "expecting TCP SA, got %r" % syn_ack.flags
assert syn_ack.ack == seq_init + 1, "wrong TCP ack value (%r != %r)" % (
syn_ack.ack,
seq_init + 1,
)
@test
def test_ipv4_tcp_psh_ack():
##### PSH-ACK #####
sport = 26695
port = 445
seq_init = int(RandInt())
# send PSH-ACK first
psh_ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="PA", sport=sport, dport=port, seq=seq_init)
/ Raw("payload")
)
syn_ack = srp1(psh_ack, timeout=1)
assert syn_ack is None, "no answer expected, got one"
# test the anti-injection mechanism
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="S", sport=sport, dport=port, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ip_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA", "expecting TCP SA, got %r" % syn_ack.flags
assert syn_ack.ack == seq_init + 1, "wrong TCP ack value (%r != %r)" % (
syn_ack.ack,
seq_init + 1,
)
ack = Ether(dst=MAC_ADDR) / IP(dst=IPV4_ADDR) / TCP(flags="A", dport=port)
# should fail because no ack given
psh_ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags="PA", sport=sport, dport=port, ack=0, seq=seq_init + 1)
)
ack = srp1(psh_ack, timeout=1)
assert ack is None, "no answer expected, got one"
# should get an answer this time
psh_ack = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(
flags="PA", sport=sport, dport=port, ack=syn_ack.seq + 1, seq=seq_init + 1
)
)
ack = srp1(psh_ack, timeout=1)
assert ack is not None, "expecting answer, got nothing"
check_ip_checksum(ack)
assert TCP in ack, "expecting TCP, got %r" % ack.summary()
ack = ack[TCP]
assert ack.flags == "A", "expecting TCP A, got %r" % syn_ack.flags
@test
def test_ipv6_tcp_psh_ack():
##### PSH-ACK #####
sport = 26695
port = 445
seq_init = int(RandInt())
# send PSH-ACK first
psh_ack = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(flags="PA", sport=sport, dport=port, seq=seq_init)
/ Raw("payload")
)
syn_ack = srp1(psh_ack, timeout=1)
assert syn_ack is None, "no answer expected, got one"
# test the anti-injection mechanism
syn = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(flags="S", sport=sport, dport=port, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got nothing"
check_ipv6_checksum(syn_ack)
assert TCP in syn_ack, "expecting TCP, got %r" % syn_ack.summary()
syn_ack = syn_ack[TCP]
assert syn_ack.flags == "SA", "expecting TCP SA, got %r" % syn_ack.flags
assert syn_ack.ack == seq_init + 1, "wrong TCP ack value (%r != %r)" % (
syn_ack.ack,
seq_init + 1,
)
ack = Ether(dst=MAC_ADDR) / IPv6(dst=IPV6_ADDR) / TCP(flags="A", dport=port)
# should fail because no ack given
psh_ack = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(flags="PA", sport=sport, dport=port, ack=0, seq=seq_init + 1)
)
ack = srp1(psh_ack, timeout=1)
assert ack is None, "no answer expected, got one"
# should get an answer this time
psh_ack = (
Ether(dst=MAC_ADDR)
/ IPv6(dst=IPV6_ADDR)
/ TCP(
flags="PA", sport=sport, dport=port, ack=syn_ack.seq + 1, seq=seq_init + 1
)
)
ack = srp1(psh_ack, timeout=1)
assert ack is not None, "expecting answer, got nothing"
check_ipv6_checksum(ack)
assert TCP in ack, "expecting TCP, got %r" % ack.summary()
ack = ack[TCP]
assert ack.flags == "A", "expecting TCP A, got %r" % syn_ack.flags
@test
def test_tcp_syn_with_flags():
# send a SYN packet with other TCP flags, should not be answered
for flags in ["SA", "SR", "SF", "SPUCE"]:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags=flags, dport=80, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is None, "expecting no answer, got one"
# some should be accepted to imitate a Linux network stack
for flags in ["SP", "SU", "SC", "SE", "SPU", "SPC", "SPE", "SPUC", "SPUE"]:
seq_init = int(RandInt())
syn = (
Ether(dst=MAC_ADDR)
/ IP(dst=IPV4_ADDR)
/ TCP(flags=flags, dport=80, seq=seq_init)
)
syn_ack = srp1(syn, timeout=1)
assert syn_ack is not None, "expecting answer, got None"

40
test/src/tests/udp.py Normal file
View file

@ -0,0 +1,40 @@
# This file is part of masscanned.
# Copyright 2021 - The IVRE project
#
# Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Masscanned is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.layers.inet import IP
from scapy.layers.inet6 import IPv6
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.sendrecv import srp1
from ..conf import IPV4_ADDR, IPV6_ADDR, MAC_ADDR
from ..core import test
@test
def test_ipv4_udp_empty():
for p in [0, 53, 1000]:
req = Ether(dst=MAC_ADDR) / IP(dst=IPV4_ADDR, proto=17) / Raw() # UDP
repl = srp1(req, timeout=1)
assert repl is None, "expecting no answer, got one"
@test
def test_ipv6_udp_empty():
for p in [0, 53, 1000]:
req = Ether(dst=MAC_ADDR) / IPv6(dst=IPV6_ADDR, nh=17) / Raw() # UDP
repl = srp1(req, timeout=1)
assert repl is None, "expecting no answer, got one"

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# This file is part of masscanned. # This file is part of masscanned.
# Copyright 2021 - The IVRE project # Copyright 2021 - 2025 - The IVRE project
# #
# Masscanned is free software: you can redistribute it and/or modify it # Masscanned is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by # under the terms of the GNU General Public License as published by
@ -16,63 +16,180 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Masscanned. If not, see <http://www.gnu.org/licenses/>. # along with Masscanned. If not, see <http://www.gnu.org/licenses/>.
from scapy.all import * import atexit
from time import sleep import functools
from tempfile import _get_candidate_names as gen_tmp_filename
from tempfile import gettempdir
import subprocess
import logging
import sys
import os import os
from signal import SIGINT
import subprocess
import sys
from time import sleep
from tempfile import NamedTemporaryFile
try:
from ivre.config import guess_prefix
except ImportError:
HAS_IVRE = False
else:
HAS_IVRE = True
from scapy.config import conf
from scapy.interfaces import resolve_iface
from src.all import test_all from src.all import test_all
from src.conf import * from src.conf import IPV4_ADDR, IPV6_ADDR, MAC_ADDR, OUTDIR
# if args in CLI, they are passed to masscanned
if len(sys.argv) > 1: def cleanup_net(iface):
args = " ".join(sys.argv[1:]) subprocess.check_call(["ip", "link", "delete", iface])
subprocess.check_call(
[
"iptables",
"-D",
"INPUT",
"-i",
iface,
"-m",
"state",
"--state",
"ESTABLISHED",
"-j",
"ACCEPT",
]
)
subprocess.check_call(["iptables", "-D", "INPUT", "-i", iface, "-j", "DROP"])
try:
os.unlink(ipfile.name)
except NameError:
pass
def setup_net(iface):
# create the interfaces pair
atexit.register(functools.partial(cleanup_net, f"{iface}a"))
subprocess.check_call(
["ip", "link", "add", f"{iface}a", "type", "veth", "peer", f"{iface}b"]
)
for sub in "a", "b":
subprocess.check_call(["ip", "link", "set", f"{iface}{sub}", "up"])
subprocess.check_call(["ip", "addr", "add", "dev", f"{iface}a", "192.0.0.0/31"])
subprocess.check_call(
["ip", "addr", "add", "dev", f"{iface}a", "2001:41d0::1234:5678/96"]
)
subprocess.check_call(["ip", "route", "add", "1.2.3.4/32", "via", IPV4_ADDR])
# prevent problems between raw scanners (Scapy, Nmap, Masscan) and
# the host IP stack
subprocess.check_call(
[
"iptables",
"-A",
"INPUT",
"-i",
f"{iface}a",
"-m",
"state",
"--state",
"ESTABLISHED",
"-j",
"ACCEPT",
]
)
subprocess.check_call(["iptables", "-A", "INPUT", "-i", f"{iface}a", "-j", "DROP"])
conf.route.resync()
conf.route6.resync()
IFACE = "masscanned"
setup_net(IFACE)
TCPDUMP = bool(os.environ.get("USE_TCPDUMP"))
if HAS_IVRE:
ZEEK_PASSIVERECON = bool(os.environ.get("USE_ZEEK"))
else: else:
args = "" ZEEK_PASSIVERECON = False
P0F = bool(os.environ.get("USE_P0F"))
fmt = logging.Formatter("%(levelname)s\t%(message)s")
ch = logging.StreamHandler()
ch.setFormatter(fmt)
ch.setLevel(logging.INFO)
LOG = logging.getLogger(__name__)
LOG.setLevel(logging.INFO)
LOG.addHandler(ch)
conf.iface = 'tap0'
conf.verb = 0 conf.verb = 0
# prepare configuration file for masscanned # prepare configuration file for masscanned
ipfile = os.path.join(gettempdir(), next(gen_tmp_filename())) with NamedTemporaryFile(delete=False, mode="w") as ipfile:
with open(ipfile, "w") as f: ipfile.write(f"{IPV4_ADDR}\n")
f.write("{}\n".format(IPV4_ADDR)) ipfile.write(f"{IPV6_ADDR}\n")
f.write("{}\n".format(IPV6_ADDR))
# create test interface # create test interface
tap = TunTapInterface(resolve_iface(conf.iface)) conf.iface = resolve_iface(f"{IFACE}a")
# set interface
subprocess.run("ip a a dev {} 192.0.0.2".format(conf.iface), shell=True)
subprocess.run("ip link set {} up".format(conf.iface), shell=True)
# start capture # start capture
tcpdump = subprocess.Popen("tcpdump -enli {} -w {}".format(conf.iface, os.path.join(OUTDIR, "test_capture.pcap")), shell=True, if TCPDUMP:
stdin=None, stdout=None, stderr=None, close_fds=True) tcpdump = subprocess.Popen(
[
"tcpdump",
"-enli",
f"{IFACE}a",
"-w",
os.path.join(OUTDIR, "test_capture.pcap"),
]
)
if ZEEK_PASSIVERECON:
zeek = subprocess.Popen(
[
"zeek",
"-C",
"-b",
"-i",
f"{IFACE}a",
os.path.join(
guess_prefix("zeek"),
"ivre",
"passiverecon",
"bare.zeek",
),
"-e",
"redef tcp_content_deliver_all_resp = T; "
"redef tcp_content_deliver_all_orig = T; "
f"redef PassiveRecon::HONEYPOTS += {{ {IPV4_ADDR}, [{IPV6_ADDR}] }}",
],
stdout=open(os.path.join(OUTDIR, "zeek_passiverecon.stdout"), "w"),
stderr=open(os.path.join(OUTDIR, "zeek_passiverecon.stderr"), "w"),
)
if P0F:
p0f = subprocess.Popen(
["p0f", "-i", f"{IFACE}a", "-o", os.path.join(OUTDIR, "p0f_log.txt")],
stdout=open(os.path.join(OUTDIR, "p0f.stdout"), "w"),
stderr=open(os.path.join(OUTDIR, "p0f.stderr"), "w"),
)
# run masscanned # run masscanned
masscanned = subprocess.Popen("RUST_BACKTRACE=1 ./target/debug/masscanned -vvvvv -i {} -f {} -a {} {}".format(conf.iface, ipfile, MAC_ADDR, args), shell=True, masscanned = subprocess.Popen(
stdin=None, stdout=open("test/res/masscanned.stdout", "w"), stderr=open("test/res/masscanned.stderr", "w"), close_fds=True) [
"./target/debug/masscanned",
"-vvvvv",
"-i",
f"{IFACE}b",
"--self-ip-file",
ipfile.name,
"-m",
MAC_ADDR,
]
# if args in CLI, they are passed to masscanned
+ sys.argv[1:],
env=dict(os.environ, RUST_BACKTRACE="1"),
stdout=open(os.path.join(OUTDIR, "masscanned.stdout"), "w"),
stderr=open(os.path.join(OUTDIR, "masscanned.stderr"), "w"),
)
sleep(1) sleep(1)
try: try:
test_all(tap) result = test_all(masscanned)
except AssertionError: except AssertionError:
pass result = -1
# terminate masscanned # terminate masscanned
masscanned.kill() masscanned.send_signal(SIGINT)
masscanned.wait()
# terminate capture # terminate capture
sleep(2) if TCPDUMP:
tcpdump.kill() tcpdump.send_signal(SIGINT)
tcpdump.wait()
if ZEEK_PASSIVERECON:
zeek.send_signal(SIGINT)
zeek.wait()
if P0F:
p0f.send_signal(SIGINT)
p0f.wait()
sys.exit(result)