distracted by fuzzing

This commit is contained in:
Michael Zhang 2021-08-23 01:52:20 -05:00
parent b0c8423968
commit e78022f8ab
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
29 changed files with 2274 additions and 34 deletions

23
Cargo.lock generated
View file

@ -17,6 +17,15 @@ version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
[[package]]
name = "arbitrary"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237430fd6ed3740afe94eefcc278ae21e050285be882804e0d6e8695f0c94691"
dependencies = [
"derive_arbitrary",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.5.2" version = "0.5.2"
@ -200,6 +209,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "derive_arbitrary"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f1281ee141df08871db9fe261ab5312179eac32d1e314134ceaa8dd7c042f5a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "derive_builder" name = "derive_builder"
version = "0.10.2" version = "0.10.2"
@ -701,6 +721,7 @@ name = "panorama-imap"
version = "0.0.4" version = "0.0.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arbitrary",
"async-trait", "async-trait",
"bitflags", "bitflags",
"bytes", "bytes",
@ -729,6 +750,7 @@ dependencies = [
"derivative", "derivative",
"derive_builder", "derive_builder",
"env_logger", "env_logger",
"futures",
"log", "log",
"panorama-imap", "panorama-imap",
"serde", "serde",
@ -740,6 +762,7 @@ name = "panorama-proto-common"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arbitrary",
"bstr", "bstr",
"bytes", "bytes",
"format-bytes", "format-bytes",

View file

@ -83,7 +83,7 @@ pub async fn sync_main(
if !new_uids.is_empty() { if !new_uids.is_empty() {
debug!("fetching uids {:?}", new_uids); debug!("fetching uids {:?}", new_uids);
let _fetched = authed let _fetched = authed
.uid_fetch(&new_uids, FetchItems::PanoramaAll) .uid_fetch(&new_uids, &[], FetchItems::Envelope)
.await .await
.context("error fetching uids")?; .context("error fetching uids")?;
// fetched // fetched
@ -113,7 +113,7 @@ pub async fn sync_main(
// )); // ));
// TODO: make this happen concurrently with the main loop? // TODO: make this happen concurrently with the main loop?
let mut message_list = authed let mut message_list = authed
.uid_fetch(&message_uids, FetchItems::All) .uid_fetch(&message_uids, &[], FetchItems::All)
.await .await
.unwrap(); .unwrap();
while let Some((_uid, _attrs)) = message_list.next().await { while let Some((_uid, _attrs)) = message_list.next().await {
@ -151,7 +151,7 @@ pub async fn sync_main(
// )); // ));
// TODO: make this happen concurrently with the main loop? // TODO: make this happen concurrently with the main loop?
let mut message_list = authed let mut message_list = authed
.uid_fetch(&message_uids, FetchItems::All) .uid_fetch(&message_uids, &[], FetchItems::All)
.await .await
.unwrap(); .unwrap();
while let Some((_uid, _attrs)) = message_list.next().await { while let Some((_uid, _attrs)) = message_list.next().await {

0
imap/.gitignore vendored Normal file
View file

View file

@ -18,6 +18,7 @@ low-level = []
rfc2087 = [] # quota rfc2087 = [] # quota
rfc2177 = [] # idle rfc2177 = [] # idle
rfc6154 = [] # list rfc6154 = [] # list
fuzzing = ["arbitrary", "panorama-proto-common/fuzzing"]
[dependencies] [dependencies]
anyhow = "1.0.42" anyhow = "1.0.42"
@ -36,4 +37,7 @@ tokio = { version = "1.9.0", features = ["full"] }
tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"] } tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"] }
tokio-util = { version = "0.6.7", features = ["codec"] } tokio-util = { version = "0.6.7", features = ["codec"] }
webpki-roots = "0.21.1" webpki-roots = "0.21.1"
panorama-proto-common = { path = "../proto-common" } panorama-proto-common = { path = "../proto-common" }
# for fuzzing
arbitrary = { version = "1", optional = true, features = ["derive"] }

9
imap/Justfile Normal file
View file

@ -0,0 +1,9 @@
afl:
#!/bin/bash
cd imap-parsing-fuzz-target
pwd
cargo afl build
cargo afl fuzz -i in -o out target/debug/imap-parsing-fuzz-target
fuzz:
cargo +nightly fuzz run parse_response

4
imap/fuzz/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
target
corpus
artifacts

943
imap/fuzz/Cargo.lock generated Normal file
View file

@ -0,0 +1,943 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
[[package]]
name = "arbitrary"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237430fd6ed3740afe94eefcc278ae21e050285be882804e0d6e8695f0c94691"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-trait"
version = "0.1.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitvec"
version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "bstr"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
]
[[package]]
name = "bumpalo"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
[[package]]
name = "bytes"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
[[package]]
name = "cc"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]]
name = "darling"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_arbitrary"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f1281ee141df08871db9fe261ab5312179eac32d1e314134ceaa8dd7c042f5a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
dependencies = [
"derive_builder_core",
"syn",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "format-bytes"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c4e89040c7fd7b4e6ba2820ac705a45def8a0c098ec78d170ae88f1ef1d5762"
dependencies = [
"format-bytes-macros",
"proc-macro-hack",
]
[[package]]
name = "format-bytes-macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05089e341a0460449e2210c3bf7b61597860b07f0deae58da38dbed0a4c6b6d"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "futures"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
[[package]]
name = "futures-executor"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582"
[[package]]
name = "futures-macro"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57"
dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53"
[[package]]
name = "futures-task"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2"
[[package]]
name = "futures-util"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
dependencies = [
"autocfg",
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "instant"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
dependencies = [
"cfg-if",
]
[[package]]
name = "js-sys"
version = "0.3.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lexical-core"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]]
name = "libfuzzer-sys"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3"
dependencies = [
"arbitrary",
"cc",
"once_cell",
]
[[package]]
name = "lock_api"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "mio"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "nom"
version = "6.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
dependencies = [
"bitvec",
"funty",
"lexical-core",
"memchr",
"version_check",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "panorama-imap"
version = "0.0.4"
dependencies = [
"anyhow",
"arbitrary",
"async-trait",
"bitflags",
"bytes",
"chrono",
"derivative",
"derive_builder",
"format-bytes",
"futures",
"log",
"nom",
"panorama-proto-common",
"tokio",
"tokio-rustls",
"tokio-util",
"webpki-roots",
]
[[package]]
name = "panorama-imap-fuzz"
version = "0.0.0"
dependencies = [
"format-bytes",
"libfuzzer-sys",
"panorama-imap",
"panorama-proto-common",
]
[[package]]
name = "panorama-proto-common"
version = "0.0.1"
dependencies = [
"anyhow",
"arbitrary",
"bstr",
"bytes",
"format-bytes",
"log",
"nom",
]
[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "pin-project-lite"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rustls"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
dependencies = [
"base64",
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sct"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "tokio"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
dependencies = [
"rustls",
"tokio",
"webpki",
]
[[package]]
name = "tokio-util"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"log",
"pin-project-lite",
"tokio",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasm-bindgen"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29"
[[package]]
name = "web-sys"
version = "0.3.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224b2f6b67919060055ef1a67807367c2066ed520c3862cc013d26cf893a783c"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [
"webpki",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"

29
imap/fuzz/Cargo.toml Normal file
View file

@ -0,0 +1,29 @@
[package]
name = "panorama-imap-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
format-bytes = "0.2.2"
panorama-proto-common = { path = "../../proto-common" }
[dependencies.panorama-imap]
path = ".."
features = ["fuzzing"]
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "parse_response"
path = "fuzz_targets/parse_response.rs"
test = false
doc = false

View file

@ -0,0 +1,16 @@
#![no_main]
use std::io::Cursor;
use format_bytes::write_bytes;
use libfuzzer_sys::fuzz_target;
use panorama_imap::proto::{response::Response, rfc3501::response};
use panorama_proto_common::Bytes;
fuzz_target!(|resp: Response| {
let data = Vec::new();
let mut curs = Cursor::new(data);
write_bytes!(&mut curs, b"{}", resp).unwrap();
let data = curs.into_inner();
let data = Bytes::from(data);
let _ = response(data);
});

View file

@ -0,0 +1,2 @@
target
out

1032
imap/imap-parsing-fuzz-target/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
[package]
name = "imap-parsing-fuzz-target"
version = "0.1.0"
edition = "2018"
[workspace]
[dependencies]
afl = "*"
panorama-imap = { path = ".." }
panorama-proto-common = { path = "../../proto-common" }

View file

@ -0,0 +1 @@
* 4544444444 444

View file

@ -0,0 +1 @@
* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 XYZZY SASL-IR AUTH=XOAUTH2 AUTH=PLAIN AUTH=PLAIN-CLIENTTOKEN AUTH=OAUTHBEARER AUTH=XOAUTH

View file

@ -0,0 +1 @@
* 8211 FETCH (UID 8390 FLAGS (\Answered \Seen))

View file

@ -0,0 +1 @@
* 8045 FETCH (UID 8225 ENVELOPE ("Sun, 21 Mar 2021 18:44:10 -0700" "SUBJECT" (("SENDER" NIL "sender" "example.com")) (("SENDER" NIL "sender" "example.com")) (("noreply" NIL "noreply" "example.com")) (("NAME" NIL "user" "gmail.com")) NIL NIL NIL "<HASH-99c91810@example.com>") FLAGS () INTERNALDATE "22-Mar-2021 01:44:12 +0000" RFC822.SIZE 13503)

View file

@ -0,0 +1 @@
* OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen $NotPhishing $Phishing \*)] Flags permitted.

View file

@ -0,0 +1,11 @@
#[macro_use]
extern crate afl;
fn main() {
fuzz!(|data: &[u8]| {
use panorama_imap::proto::rfc3501::response;
use panorama_proto_common::Bytes;
let data = Bytes::from(data.to_vec());
let _ = response(data);
});
}

View file

@ -1,3 +1,4 @@
use std::ops::Range;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@ -13,7 +14,7 @@ use tokio_rustls::client::TlsStream;
use crate::proto::{ use crate::proto::{
command::{ command::{
Command, CommandFetch, CommandList, CommandSearch, CommandSelect, FetchItems, Command, CommandFetch, CommandList, CommandSearch, CommandSelect, FetchItems,
SearchCriteria, SearchCriteria, Sequence,
}, },
response::{ response::{
Condition, Flag, Mailbox, MailboxData, MailboxList, MessageAttribute, Response, Condition, Flag, Mailbox, MailboxData, MailboxList, MessageAttribute, Response,
@ -199,12 +200,17 @@ impl ClientAuthenticated {
pub async fn fetch( pub async fn fetch(
&mut self, &mut self,
uids: &[u32], uids: &[u32],
uid_seqs: &[Range<u32>],
items: FetchItems, items: FetchItems,
) -> Result<impl Stream<Item = (u32, Vec<MessageAttribute>)>> { ) -> Result<impl Stream<Item = (u32, Vec<MessageAttribute>)>> {
let cmd = Command::Fetch(CommandFetch { let mut ids = Vec::new();
ids: uids.to_vec(), for uid in uids {
items, ids.push(Sequence::Single(*uid));
}); }
for seq in uid_seqs {
ids.push(Sequence::Range(seq.start, seq.end));
}
let cmd = Command::Fetch(CommandFetch { ids, items });
debug!("fetch: {:?}", cmd); debug!("fetch: {:?}", cmd);
let stream = self.execute(cmd).await?; let stream = self.execute(cmd).await?;
// let (done, data) = stream.wait().await?; // let (done, data) = stream.wait().await?;
@ -219,12 +225,17 @@ impl ClientAuthenticated {
pub async fn uid_fetch( pub async fn uid_fetch(
&mut self, &mut self,
uids: &[u32], uids: &[u32],
uid_seqs: &[Range<u32>],
items: FetchItems, items: FetchItems,
) -> Result<impl Stream<Item = (u32, Vec<MessageAttribute>)>> { ) -> Result<impl Stream<Item = (u32, Vec<MessageAttribute>)>> {
let cmd = Command::UidFetch(CommandFetch { let mut ids = Vec::new();
ids: uids.to_vec(), for uid in uids {
items, ids.push(Sequence::Single(*uid));
}); }
for seq in uid_seqs {
ids.push(Sequence::Range(seq.start, seq.end));
}
let cmd = Command::UidFetch(CommandFetch { ids, items });
debug!("uid fetch: {:?}", cmd); debug!("uid fetch: {:?}", cmd);
let stream = self.execute(cmd).await?; let stream = self.execute(cmd).await?;
// let (done, data) = stream.wait().await?; // let (done, data) = stream.wait().await?;

View file

@ -78,8 +78,8 @@ impl<'a> Decoder for ImapCodec {
buf.unsplit(buf2); buf.unsplit(buf2);
// and then move to after the message we just parsed // and then move to after the message we just parsed
let _ = buf.split_to(len); let first_part = buf.split_to(len);
debug!("buf: {:?}", buf); trace!("parsed from: {:?}", first_part);
// since we're done parsing a complete message, set this to zero // since we're done parsing a complete message, set this to zero
self.decode_need_message_bytes = 0; self.decode_need_message_bytes = 0;

View file

@ -81,6 +81,10 @@ impl DisplayBytes for Command {
} }
Command::Select(select) => write_bytes!(w, b"SELECT {}", quote(&select.mailbox)), Command::Select(select) => write_bytes!(w, b"SELECT {}", quote(&select.mailbox)),
// selected
Command::UidFetch(fetch) => write_bytes!(w, b"UID FETCH {}", fetch),
// extensions
#[cfg(feature = "rfc2177")] #[cfg(feature = "rfc2177")]
Command::Idle => write_bytes!(w, b"IDLE"), Command::Idle => write_bytes!(w, b"IDLE"),
@ -91,10 +95,33 @@ impl DisplayBytes for Command {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CommandFetch { pub struct CommandFetch {
pub ids: Vec<u32>, pub ids: Vec<Sequence>,
pub items: FetchItems, pub items: FetchItems,
} }
impl DisplayBytes for CommandFetch {
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> {
for (i, seq) in self.ids.iter().enumerate() {
if i != 0 {
write_bytes!(w, b",")?;
}
match seq {
Sequence::Single(n) => write_bytes!(w, b"{}", n)?,
Sequence::Range(m, n) => write_bytes!(w, b"{}:{}", m, n)?,
}
}
write_bytes!(w, b" {}", self.items)
}
}
#[derive(Clone, Debug)]
pub enum Sequence {
Single(u32),
Range(u32, u32),
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CommandList { pub struct CommandList {
pub reference: Bytes, pub reference: Bytes,
@ -127,10 +154,20 @@ pub enum FetchItems {
All, All,
Fast, Fast,
Full, Full,
BodyPeek, Flags,
Items(Vec<FetchAttr>), Envelope,
/// item set that panorama uses, TODO: remove when FetchItems has a builder }
PanoramaAll,
impl DisplayBytes for FetchItems {
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> {
match self {
FetchItems::All => write_bytes!(w, b"ALL"),
FetchItems::Fast => write_bytes!(w, b"FAST"),
FetchItems::Full => write_bytes!(w, b"FULL"),
FetchItems::Flags => write_bytes!(w, b"FLAGS"),
FetchItems::Envelope => write_bytes!(w, b"ENVEOPE"),
}
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -1,15 +1,47 @@
use std::io::{self, Write};
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use format_bytes::DisplayBytes;
use panorama_proto_common::Bytes; use panorama_proto_common::Bytes;
#[cfg(feature = "fuzzing")]
use arbitrary::{self, Arbitrary, Unstructured};
#[cfg(feature = "fuzzing")]
use chrono::TimeZone;
pub type Atom = Bytes; pub type Atom = Bytes;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub struct Tag(pub Bytes); pub struct Tag(pub Bytes);
#[derive(Clone, Debug)]
pub struct Timestamp(DateTime<FixedOffset>);
#[cfg(feature = "fuzzing")]
impl<'a> Arbitrary<'a> for Timestamp {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let (y, m, d, H, M, S) = (
// TODO: year range?
u.int_in_range(-4000..=4000i32)?,
u.int_in_range(1..=12u32)?,
u.int_in_range(1..=28u32)?,
u.int_in_range(0..=23u32)?,
u.int_in_range(0..=59u32)?,
u.int_in_range(0..=59u32)?,
);
println!("{:?}", (y, m, d, H, M, S));
Ok(Timestamp(
// TODO: introduce offset
FixedOffset::west(0).ymd(y, m, d).and_hms(H, M, S),
))
}
}
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive] #[non_exhaustive]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum Response { pub enum Response {
Capabilities(Vec<Capability>), Capabilities(Vec<Capability>),
Continue(ResponseText), Continue(ResponseText),
@ -22,19 +54,29 @@ pub enum Response {
Tagged(Tag, Condition), Tagged(Tag, Condition),
} }
impl DisplayBytes for Response {
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> {
match self {
_ => todo!(),
}
}
}
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub struct ResponseText { pub struct ResponseText {
pub code: Option<ResponseCode>, pub code: Option<ResponseCode>,
pub info: Bytes, pub info: Bytes,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum MessageAttribute { pub enum MessageAttribute {
BodySection, BodySection,
BodyStructure, BodyStructure,
Envelope(Envelope), Envelope(Envelope),
Flags(Vec<Flag>), Flags(Vec<Flag>),
InternalDate(DateTime<FixedOffset>), InternalDate(Timestamp),
ModSeq(u64), // RFC 4551, section 3.3.2 ModSeq(u64), // RFC 4551, section 3.3.2
Rfc822(Option<String>), Rfc822(Option<String>),
Rfc822Header(Option<String>), Rfc822Header(Option<String>),
@ -44,6 +86,7 @@ pub enum MessageAttribute {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub struct Envelope { pub struct Envelope {
pub date: Option<Bytes>, pub date: Option<Bytes>,
pub subject: Option<Bytes>, pub subject: Option<Bytes>,
@ -58,6 +101,7 @@ pub struct Envelope {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub struct Address { pub struct Address {
pub name: Option<Bytes>, pub name: Option<Bytes>,
pub adl: Option<Bytes>, pub adl: Option<Bytes>,
@ -66,6 +110,7 @@ pub struct Address {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub struct ResponseDone { pub struct ResponseDone {
pub tag: Tag, pub tag: Tag,
pub status: Status, pub status: Status,
@ -74,6 +119,7 @@ pub struct ResponseDone {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub struct Condition { pub struct Condition {
pub status: Status, pub status: Status,
pub code: Option<ResponseCode>, pub code: Option<ResponseCode>,
@ -81,6 +127,7 @@ pub struct Condition {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum Status { pub enum Status {
Ok, Ok,
No, No,
@ -91,6 +138,7 @@ pub enum Status {
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive] #[non_exhaustive]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum ResponseCode { pub enum ResponseCode {
Alert, Alert,
BadCharset(Option<Vec<Bytes>>), BadCharset(Option<Vec<Bytes>>),
@ -111,12 +159,14 @@ pub enum ResponseCode {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum UidSetMember { pub enum UidSetMember {
UidRange(RangeInclusive<u32>), UidRange(RangeInclusive<u32>),
Uid(u32), Uid(u32),
} }
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum Capability { pub enum Capability {
Imap4rev1, Imap4rev1,
Auth(Atom), Auth(Atom),
@ -124,6 +174,7 @@ pub enum Capability {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum MailboxData { pub enum MailboxData {
Flags(Vec<Flag>), Flags(Vec<Flag>),
List(MailboxList), List(MailboxList),
@ -135,12 +186,14 @@ pub enum MailboxData {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum Mailbox { pub enum Mailbox {
Inbox, Inbox,
Name(Bytes), Name(Bytes),
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum Flag { pub enum Flag {
Answered, Answered,
Flagged, Flagged,
@ -153,6 +206,7 @@ pub enum Flag {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub struct MailboxList { pub struct MailboxList {
pub flags: Vec<MailboxListFlag>, pub flags: Vec<MailboxListFlag>,
pub delimiter: Option<u8>, pub delimiter: Option<u8>,
@ -160,6 +214,7 @@ pub struct MailboxList {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum MailboxListFlag { pub enum MailboxListFlag {
NoInferiors, NoInferiors,
NoSelect, NoSelect,

View file

@ -46,10 +46,10 @@ rule!(pub astring : Bytes => alt((take_while1(is_astring_char), string)));
pub(crate) fn is_astring_char(c: u8) -> bool { is_atom_char(c) || is_resp_specials(c) } pub(crate) fn is_astring_char(c: u8) -> bool { is_atom_char(c) || is_resp_specials(c) }
rule!(pub ASTRING_CHAR : u8 => alt((ATOM_CHAR, resp_specials))); rule!(pub ASTRING_CHAR : u8 => alt((ATOM_CHAR, resp_specials)));
// really odd behavior about take_while1 is that if there isn't a character that's // really odd behavior about take_while1 is that if there isn't a character
// not is_atom_char, then it's actually going to error out and require another character // that's not is_atom_char, then it's actually going to error out and require
// in case there's more. makes sense, just need to keep in mind that we need more // another character in case there's more. makes sense, just need to keep in
// content in order to satisfy this // mind that we need more content in order to satisfy this
rule!(pub atom : Bytes => take_while1(is_atom_char)); rule!(pub atom : Bytes => take_while1(is_atom_char));
pub(crate) fn is_atom_char(c: u8) -> bool { is_char(c) && !is_atom_specials(c) } pub(crate) fn is_atom_char(c: u8) -> bool { is_char(c) && !is_atom_specials(c) }

View file

@ -1,11 +1,11 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use nom::{multi::*, sequence::*};
use panorama_proto_common::*; use panorama_proto_common::*;
use nom::{sequence::*, multi::*};
use super::response::*; use super::response::*;
use super::rfc3501::*;
use super::rfc2234::*; use super::rfc2234::*;
use super::rfc3501::*;
#[test] #[test]
fn test_literal() { fn test_literal() {
@ -18,15 +18,33 @@ fn test_literal() {
); );
} }
#[test]
fn afl() { let _ = response(Bytes::from(b"* 4544444444 444 ")); }
#[test] #[test]
fn test_capabilities() { fn test_capabilities() {
assert_eq!(capability(Bytes::from(b"UNSELECT\r\n")).unwrap().1, Capability::Atom(Bytes::from(b"UNSELECT"))); assert_eq!(
capability(Bytes::from(b"UNSELECT\r\n")).unwrap().1,
Capability::Atom(Bytes::from(b"UNSELECT"))
);
// trivial case // trivial case
assert_eq!(capability_data(Bytes::from(b"CAPABILITY IMAP4rev1\r\n")).unwrap().1, vec![]); assert_eq!(
capability_data(Bytes::from(b"CAPABILITY IMAP4rev1\r\n"))
.unwrap()
.1,
vec![]
);
assert_eq!(capability_data(Bytes::from(b"CAPABILITY UNSELECT IMAP4rev1 NAMESPACE\r\n")).unwrap().1, assert_eq!(
vec![Capability::Atom(Bytes::from(b"UNSELECT")), Capability::Atom(Bytes::from(b"NAMESPACE"))]); capability_data(Bytes::from(b"CAPABILITY UNSELECT IMAP4rev1 NAMESPACE\r\n"))
.unwrap()
.1,
vec![
Capability::Atom(Bytes::from(b"UNSELECT")),
Capability::Atom(Bytes::from(b"NAMESPACE"))
]
);
} }
#[test] #[test]

View file

@ -22,3 +22,4 @@ serde = { version = "1.0.127", features = ["derive"] }
tokio = { version = "1.9.0", features = ["full"] } tokio = { version = "1.9.0", features = ["full"] }
log = "0.4.14" log = "0.4.14"
env_logger = "0.9.0" env_logger = "0.9.0"
futures = "0.3.16"

View file

@ -51,7 +51,16 @@ pub async fn open(config: &Config, store_name: impl AsRef<str>) -> Result<Opened
username: account.user.clone(), username: account.user.clone(),
password: account.pass.clone(), password: account.pass.clone(),
}; };
let client = client.auth(login).await?; let mut client = client.auth(login).await?;
let select = client.select("INBOX").await?;
println!("select: {:?}", select);
use futures::stream::StreamExt;
use panorama_imap::proto::command::FetchItems;
let mut result = client.uid_fetch(&[8225], &[], FetchItems::All).await?;
while let Some(item) = result.next().await {
println!("epic: {:?}", item);
}
println!("authenticated"); println!("authenticated");
Ok(OpenedStore::Imap(ImapStore { Ok(OpenedStore::Imap(ImapStore {

View file

@ -8,10 +8,17 @@ license = "GPL-3.0-or-later"
repository = "https://git.mzhang.io/michael/panorama" repository = "https://git.mzhang.io/michael/panorama"
workspace = ".." workspace = ".."
[features]
default = []
fuzzing = ["arbitrary"]
[dependencies] [dependencies]
anyhow = "1.0.42" anyhow = "1.0.42"
bstr = "0.2.15" bstr = "0.2.15"
bytes = "1.0.1" bytes = "1.0.1"
format-bytes = "0.2.2" format-bytes = "0.2.2"
log = "0.4.14" log = "0.4.14"
nom = "6.2.1" nom = "6.2.1"
# for fuzzing
arbitrary = { version = "1", optional = true, features = ["derive"] }

View file

@ -7,10 +7,20 @@ use nom::{
CompareResult, Err, IResult, InputLength, Needed, CompareResult, Err, IResult, InputLength, Needed,
}; };
#[cfg(feature = "fuzzing")]
use arbitrary::{self, Arbitrary, Unstructured};
/// Glue code between nom and Bytes so they work together. /// Glue code between nom and Bytes so they work together.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Bytes(bytes::Bytes); pub struct Bytes(bytes::Bytes);
#[cfg(feature = "fuzzing")]
impl<'a> Arbitrary<'a> for Bytes {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Bytes::from(<Vec<u8>>::arbitrary(u)?))
}
}
impl Bytes { impl Bytes {
/// Length of the internal `Bytes`. /// Length of the internal `Bytes`.
/// ///

View file

@ -76,7 +76,10 @@ pub fn parse_u32(s: impl AsRef<[u8]>) -> Result<u32> {
let s = s.as_ref(); let s = s.as_ref();
for digit in s.iter() { for digit in s.iter() {
let digit = *digit; let digit = *digit;
total *= 10; total = match total.checked_mul(10) {
Some(v) => v,
None => bail!("number {:?} overflows u32", s),
};
if !(digit >= b'0' && digit <= b'9') { if !(digit >= b'0' && digit <= b'9') {
bail!("invalid digit {}", digit) bail!("invalid digit {}", digit)
} }