From: Dietmar Maurer <dietmar@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox 3/4] proxmox-schema: add IP address regex/api-types
Date: Fri, 15 Mar 2024 12:27:31 +0100 [thread overview]
Message-ID: <20240315112732.368831-3-dietmar@proxmox.com> (raw)
In-Reply-To: <20240315112732.368831-1-dietmar@proxmox.com>
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
---
proxmox-schema/src/api_types.rs | 84 +++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/proxmox-schema/src/api_types.rs b/proxmox-schema/src/api_types.rs
index 4e10ebb..381d4cb 100644
--- a/proxmox-schema/src/api_types.rs
+++ b/proxmox-schema/src/api_types.rs
@@ -3,10 +3,68 @@ use const_format::concatcp;
use crate::{ApiStringFormat, Schema, StringSchema};
+#[rustfmt::skip]
+const IPV4OCTET: &str = r"(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])";
+
+#[rustfmt::skip]
+const IPV6H16: &str = r"(?:[0-9a-fA-F]{1,4})";
+
+/// Returns the regular expression string to match IPv4 addresses
+#[rustfmt::skip]
+pub const IPV4RE_STR: &str = concatcp!(r"(?:(?:", IPV4OCTET, r"\.){3}", IPV4OCTET, ")");
+
+#[rustfmt::skip]
+const IPV6LS32: &str = concatcp!(r"(?:(?:", IPV4RE_STR, "|", IPV6H16, ":", IPV6H16, "))" );
+
+/// Returns the regular expression string to match IPv6 addresses
+#[rustfmt::skip]
+pub const IPV6RE_STR: &str = concatcp!(r"(?:",
+ r"(?:(?:", r"(?:", IPV6H16, r":){6})", IPV6LS32, r")|",
+ r"(?:(?:", r"::(?:", IPV6H16, r":){5})", IPV6LS32, r")|",
+ r"(?:(?:(?:", IPV6H16, r")?::(?:", IPV6H16, r":){4})", IPV6LS32, r")|",
+ r"(?:(?:(?:(?:", IPV6H16, r":){0,1}", IPV6H16, r")?::(?:", IPV6H16, r":){3})", IPV6LS32, r")|",
+ r"(?:(?:(?:(?:", IPV6H16, r":){0,2}", IPV6H16, r")?::(?:", IPV6H16, r":){2})", IPV6LS32, r")|",
+ r"(?:(?:(?:(?:", IPV6H16, r":){0,3}", IPV6H16, r")?::(?:", IPV6H16, r":){1})", IPV6LS32, r")|",
+ r"(?:(?:(?:(?:", IPV6H16, r":){0,4}", IPV6H16, r")?::", ")", IPV6LS32, r")|",
+ r"(?:(?:(?:(?:", IPV6H16, r":){0,5}", IPV6H16, r")?::", ")", IPV6H16, r")|",
+ r"(?:(?:(?:(?:", IPV6H16, r":){0,6}", IPV6H16, r")?::", ")))");
+
+/// Returns the regular expression string to match IP addresses (v4 or v6)
+#[rustfmt::skip]
+pub const IPRE_STR: &str = concatcp!(r"(?:", IPV4RE_STR, "|", IPV6RE_STR, ")");
+
+/// Regular expression string to match IP addresses where IPv6 addresses require brackets around
+/// them, while for IPv4 they are forbidden.
+#[rustfmt::skip]
+pub const IPRE_BRACKET_STR: &str = concatcp!(r"(?:", IPV4RE_STR, r"|\[(?:", IPV6RE_STR, r")\]", r")");
+
+#[rustfmt::skip]
+pub const CIDR_V4_REGEX_STR: &str = concatcp!(r"(?:", IPV4RE_STR, r"/\d{1,2})$");
+
+#[rustfmt::skip]
+pub const CIDR_V6_REGEX_STR: &str = concatcp!(r"(?:", IPV6RE_STR, r"/\d{1,3})$");
+
#[rustfmt::skip]
const SAFE_ID_REGEX_STR: &str = r"(?:[A-Za-z0-9_][A-Za-z0-9._\-]*)";
const_regex! {
+ /// IPv4 regular expression.
+ pub IP_V4_REGEX = concatcp!(r"^", IPV4RE_STR, r"$");
+
+ /// IPv6 regular expression.
+ pub IP_V6_REGEX = concatcp!(r"^", IPV6RE_STR, r"$");
+
+ /// Regex to match IP addresses (V4 or V6)
+ pub IP_REGEX = concatcp!(r"^", IPRE_STR, r"$");
+
+ /// Regex to match IP addresses where IPv6 addresses require brackets around
+ /// them, while for IPv4 they are forbidden.
+ pub IP_BRACKET_REGEX = concatcp!(r"^", IPRE_BRACKET_STR, r"$");
+
+ pub CIDR_V4_REGEX = concatcp!(r"^", CIDR_V4_REGEX_STR, r"$");
+ pub CIDR_V6_REGEX = concatcp!(r"^", CIDR_V6_REGEX_STR, r"$");
+ pub CIDR_REGEX = concatcp!(r"^(?:", CIDR_V4_REGEX_STR, "|", CIDR_V6_REGEX_STR, r")$");
+
/// Regex for safe identifiers.
///
/// This
@@ -36,3 +94,29 @@ pub const COMMENT_SCHEMA: Schema = StringSchema::new("Comment.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.max_length(128)
.schema();
+
+#[test]
+fn test_regexes() {
+ assert!(IP_REGEX.is_match("127.0.0.1"));
+ assert!(IP_V4_REGEX.is_match("127.0.0.1"));
+ assert!(!IP_V6_REGEX.is_match("127.0.0.1"));
+
+ assert!(CIDR_V4_REGEX.is_match("127.0.0.1/24"));
+ assert!(CIDR_REGEX.is_match("127.0.0.1/24"));
+
+ assert!(IP_REGEX.is_match("::1"));
+ assert!(IP_REGEX.is_match("2014:b3a::27"));
+ assert!(IP_REGEX.is_match("2014:b3a::192.168.0.1"));
+ assert!(IP_REGEX.is_match("2014:b3a:0102:adf1:1234:4321:4afA:BCDF"));
+ assert!(!IP_V4_REGEX.is_match("2014:b3a:0102:adf1:1234:4321:4afA:BCDF"));
+ assert!(IP_V6_REGEX.is_match("2014:b3a:0102:adf1:1234:4321:4afA:BCDF"));
+
+ assert!(CIDR_V6_REGEX.is_match("2014:b3a:0102:adf1:1234:4321:4afA:BCDF/60"));
+ assert!(CIDR_REGEX.is_match("2014:b3a:0102:adf1:1234:4321:4afA:BCDF/60"));
+
+ assert!(IP_BRACKET_REGEX.is_match("127.0.0.1"));
+ assert!(IP_BRACKET_REGEX.is_match("[::1]"));
+ assert!(IP_BRACKET_REGEX.is_match("[2014:b3a::27]"));
+ assert!(IP_BRACKET_REGEX.is_match("[2014:b3a::192.168.0.1]"));
+ assert!(IP_BRACKET_REGEX.is_match("[2014:b3a:0102:adf1:1234:4321:4afA:BCDF]"));
+}
--
2.39.2
next prev parent reply other threads:[~2024-03-15 11:28 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-15 11:27 [pbs-devel] [PATCH proxmox 1/4] proxmox-schema: use const_format to define static strings Dietmar Maurer
2024-03-15 11:27 ` [pbs-devel] [PATCH proxmox 2/4] proxmox-auth-api: " Dietmar Maurer
2024-03-15 11:27 ` Dietmar Maurer [this message]
2024-03-15 11:27 ` [pbs-devel] [PATCH proxmox 4/4] proxmox-schema: moved common api types from pbs-api-types Dietmar Maurer
2024-03-19 10:14 ` [pbs-devel] applied-series: [PATCH proxmox 1/4] proxmox-schema: use const_format to define static strings Wolfgang Bumiller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240315112732.368831-3-dietmar@proxmox.com \
--to=dietmar@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox