1use std::cmp::Ordering;
8
9use rupg_common::SqlState;
10
11use crate::binary::Recv;
12use crate::error::TypeError;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub enum NetFamily {
17 V4,
18 V6,
19}
20
21impl NetFamily {
22 pub fn max_bits(self) -> u8 {
24 match self {
25 NetFamily::V4 => 32,
26 NetFamily::V6 => 128,
27 }
28 }
29
30 pub fn size(self) -> usize {
32 match self {
33 NetFamily::V4 => 4,
34 NetFamily::V6 => 16,
35 }
36 }
37
38 fn code(self) -> u8 {
40 match self {
41 NetFamily::V4 => 2,
42 NetFamily::V6 => 3,
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
49pub struct Inet {
50 pub family: NetFamily,
51 pub bits: u8,
53 pub addr: [u8; 16],
55}
56
57impl Inet {
58 pub fn bytes(&self) -> &[u8] {
60 &self.addr[..self.family.size()]
61 }
62
63 #[must_use]
65 pub fn masked(&self, bits: u8) -> Inet {
66 let mut addr = [0u8; 16];
67 let whole = usize::from(bits / 8);
68 addr[..whole].copy_from_slice(&self.addr[..whole]);
69 if !bits.is_multiple_of(8) {
70 addr[whole] = self.addr[whole] & !(0xff >> (bits % 8));
71 }
72 Inet { family: self.family, bits, addr }
73 }
74}
75
76fn type_name(cidr: bool) -> &'static str {
78 if cidr { "cidr" } else { "inet" }
79}
80
81pub fn inet_in(text: &str, cidr: bool) -> Result<Inet, TypeError> {
87 let mut src = text.as_bytes().to_vec();
89 src.push(0);
90 let family = if text.contains(':') { NetFamily::V6 } else { NetFamily::V4 };
91 let mut addr = [0u8; 16];
92 let bits = match (family, cidr) {
93 (NetFamily::V4, false) => net_pton_v4(&src, &mut addr),
94 (NetFamily::V4, true) => cidr_pton_v4(&src, &mut addr),
95 (NetFamily::V6, _) => pton_v6(&src, &mut addr),
96 };
97 let Some(bits) = bits.and_then(|b| u8::try_from(b).ok()).filter(|b| *b <= family.max_bits())
98 else {
99 return Err(TypeError::syntax(type_name(cidr), text));
100 };
101 if cidr && !address_ok(&addr, bits, family) {
102 let mut error = TypeError::new(
103 SqlState::INVALID_TEXT_REPRESENTATION,
104 format!("invalid cidr value: \"{text}\""),
105 );
106 error.detail = Some("Value has bits set to right of mask.".into());
107 return Err(error);
108 }
109 Ok(Inet { family, bits, addr })
110}
111
112fn digit(ch: u8) -> Option<i32> {
114 ch.is_ascii_digit().then(|| i32::from(ch - b'0'))
115}
116
117fn xdigit(ch: u8) -> Option<u32> {
119 char::from(ch).to_digit(16)
120}
121
122fn mask_length(src: &[u8], mut i: usize) -> (i32, u8, usize) {
124 let mut bits = 0;
125 loop {
126 let ch = src[i];
127 i += 1;
128 match digit(ch) {
129 Some(n) => bits = (bits * 10 + n).min(1000),
130 None => return (bits, ch, i),
131 }
132 }
133}
134
135fn cidr_pton_v4(src: &[u8], dst: &mut [u8; 16]) -> Option<i32> {
137 let mut written = 0usize;
138 let mut i = 0usize;
139 let mut ch = src[i];
140 i += 1;
141 if ch == b'0' && (src[i] == b'x' || src[i] == b'X') && src[i + 1].is_ascii_hexdigit() {
142 let mut dirty = 0;
143 let mut tmp = 0u32;
144 i += 1;
145 loop {
146 ch = src[i];
147 i += 1;
148 let Some(n) = xdigit(ch) else { break };
149 tmp = if dirty == 0 { n } else { (tmp << 4) | n };
150 dirty += 1;
151 if dirty == 2 {
152 if written == 4 {
153 return None;
154 }
155 dst[written] = u8::try_from(tmp).ok()?;
156 written += 1;
157 dirty = 0;
158 }
159 }
160 if dirty != 0 {
161 if written == 4 {
162 return None;
163 }
164 dst[written] = u8::try_from(tmp << 4).ok()?;
165 written += 1;
166 }
167 } else if ch.is_ascii_digit() {
168 loop {
169 let mut tmp = 0;
170 while let Some(n) = digit(ch) {
171 tmp = tmp * 10 + n;
172 if tmp > 255 {
173 return None;
174 }
175 ch = src[i];
176 i += 1;
177 }
178 if written == 4 {
179 return None;
180 }
181 dst[written] = u8::try_from(tmp).ok()?;
182 written += 1;
183 if ch == 0 || ch == b'/' {
184 break;
185 }
186 if ch != b'.' {
187 return None;
188 }
189 ch = src[i];
190 i += 1;
191 if !ch.is_ascii_digit() {
192 return None;
193 }
194 }
195 } else {
196 return None;
197 }
198 let mut bits = -1;
199 if ch == b'/' && src[i].is_ascii_digit() && written > 0 {
200 let (n, end, _) = mask_length(src, i);
201 if end != 0 || n > 32 {
202 return None;
203 }
204 bits = n;
205 ch = end;
206 }
207 if ch != 0 || written == 0 {
208 return None;
209 }
210 let octets = i32::try_from(written).ok()? * 8;
211 if bits == -1 {
212 bits = match dst[0] {
213 240.. => 32,
214 224.. => 8,
215 192.. => 24,
216 128.. => 16,
217 _ => 8,
218 };
219 bits = bits.max(octets);
220 if bits == 8 && dst[0] == 224 {
221 bits = 4;
222 }
223 }
224 Some(bits)
226}
227
228fn net_pton_v4(src: &[u8], dst: &mut [u8; 16]) -> Option<i32> {
230 let mut written = 0usize;
231 let mut i = 0usize;
232 let mut ch;
233 loop {
234 ch = src[i];
235 i += 1;
236 if !ch.is_ascii_digit() {
237 break;
238 }
239 let mut tmp = 0;
240 while let Some(n) = digit(ch) {
241 tmp = tmp * 10 + n;
242 if tmp > 255 {
243 return None;
244 }
245 ch = src[i];
246 i += 1;
247 }
248 if written == 4 {
249 return None;
250 }
251 dst[written] = u8::try_from(tmp).ok()?;
252 written += 1;
253 if ch == 0 || ch == b'/' {
254 break;
255 }
256 if ch != b'.' {
257 return None;
258 }
259 }
260 let mut bits = -1;
261 if ch == b'/' && src[i].is_ascii_digit() && written > 0 {
262 let (n, end, _) = mask_length(src, i);
263 if end != 0 || n > 32 {
264 return None;
265 }
266 bits = n;
267 ch = end;
268 }
269 if ch != 0 {
270 return None;
271 }
272 if bits == -1 {
273 if written != 4 {
274 return None;
275 }
276 bits = 32;
277 }
278 if written == 0 || usize::try_from(bits / 8).ok()? > written {
279 return None;
280 }
281 Some(bits)
282}
283
284fn getbits(src: &[u8]) -> Option<i32> {
286 let mut val = 0;
287 let mut n = 0;
288 for &ch in src.iter().take_while(|ch| **ch != 0) {
289 let d = digit(ch)?;
290 if n != 0 && val == 0 {
291 return None;
292 }
293 n += 1;
294 val = val * 10 + d;
295 if val > 128 {
296 return None;
297 }
298 }
299 (n != 0).then_some(val)
300}
301
302fn getv4(src: &[u8], dst: &mut [u8], bits: &mut i32) -> bool {
304 let mut written = 0usize;
305 let mut val = 0;
306 let mut n = 0;
307 for (i, &ch) in src.iter().enumerate() {
308 if ch == 0 {
309 break;
310 }
311 if let Some(d) = digit(ch) {
312 if n != 0 && val == 0 {
313 return false;
314 }
315 n += 1;
316 val = val * 10 + d;
317 if val > 255 {
318 return false;
319 }
320 continue;
321 }
322 if ch == b'.' || ch == b'/' {
323 if written > 3 {
324 return false;
325 }
326 dst[written] = u8::try_from(val).unwrap_or(0);
327 written += 1;
328 if ch == b'/' {
329 return match getbits(&src[i + 1..]) {
330 Some(b) => {
331 *bits = b;
332 true
333 }
334 None => false,
335 };
336 }
337 val = 0;
338 n = 0;
339 continue;
340 }
341 return false;
342 }
343 if n == 0 || written > 3 {
344 return false;
345 }
346 dst[written] = u8::try_from(val).unwrap_or(0);
347 true
348}
349
350fn pton_v6(src: &[u8], dst: &mut [u8; 16]) -> Option<i32> {
352 const END: usize = 16;
353 let mut tmp = [0u8; 16];
354 let mut tp = 0usize;
355 let mut colon: Option<usize> = None;
356 let mut i = 0usize;
357 if src[0] == b':' {
358 i = 1;
359 if src[1] != b':' {
360 return None;
361 }
362 }
363 let mut curtok = i;
364 let mut saw_xdigit = false;
365 let mut val = 0u32;
366 let mut digits = 0;
367 let mut bits = -1;
368 loop {
369 let ch = src[i];
370 i += 1;
371 if ch == 0 {
372 break;
373 }
374 if let Some(n) = xdigit(ch) {
375 val = (val << 4) | n;
376 digits += 1;
377 if digits > 4 {
378 return None;
379 }
380 saw_xdigit = true;
381 continue;
382 }
383 if ch == b':' {
384 curtok = i;
385 if !saw_xdigit {
386 if colon.is_some() {
387 return None;
388 }
389 colon = Some(tp);
390 continue;
391 } else if src[i] == 0 || tp + 2 > END {
392 return None;
393 }
394 tmp[tp..tp + 2].copy_from_slice(&u16::try_from(val).ok()?.to_be_bytes());
395 tp += 2;
396 saw_xdigit = false;
397 digits = 0;
398 val = 0;
399 continue;
400 }
401 if ch == b'.' && tp + 4 <= END && getv4(&src[curtok..], &mut tmp[tp..tp + 4], &mut bits) {
402 tp += 4;
403 saw_xdigit = false;
404 break;
405 }
406 if ch == b'/'
407 && let Some(b) = getbits(&src[i..])
408 {
409 bits = b;
410 break;
411 }
412 return None;
413 }
414 if saw_xdigit {
415 if tp + 2 > END {
416 return None;
417 }
418 tmp[tp..tp + 2].copy_from_slice(&u16::try_from(val).ok()?.to_be_bytes());
419 tp += 2;
420 }
421 if bits == -1 {
422 bits = 128;
423 }
424 if let Some(colon) = colon {
425 if tp == END {
426 return None;
427 }
428 let n = tp - colon;
429 for k in 1..=n {
430 tmp[END - k] = tmp[colon + n - k];
431 tmp[colon + n - k] = 0;
432 }
433 tp = END;
434 }
435 if tp != END {
436 return None;
437 }
438 *dst = tmp;
439 Some(bits)
440}
441
442fn address_ok(addr: &[u8; 16], bits: u8, family: NetFamily) -> bool {
444 if bits == family.max_bits() {
445 return true;
446 }
447 let mut byte = usize::from(bits / 8);
448 let mut mask = if bits == 0 { 0xff } else { 0xffu8 >> (bits % 8) };
449 while byte < family.size() {
450 if addr[byte] & mask != 0 {
451 return false;
452 }
453 mask = 0xff;
454 byte += 1;
455 }
456 true
457}
458
459pub fn net_ntop(value: &Inet, bits: u8, out: &mut String) {
461 use std::fmt::Write;
462 match value.family {
463 NetFamily::V4 => {
464 let [a, b, c, d, ..] = value.addr;
465 let _ = write!(out, "{a}.{b}.{c}.{d}");
466 if bits != 32 {
467 let _ = write!(out, "/{bits}");
468 }
469 }
470 NetFamily::V6 => {
471 ntop_v6(&value.addr, out);
472 if bits != 128 {
473 let _ = write!(out, "/{bits}");
474 }
475 }
476 }
477}
478
479fn ntop_v6(src: &[u8; 16], out: &mut String) {
481 use std::fmt::Write;
482 let mut words = [0u32; 8];
483 for (i, byte) in src.iter().enumerate() {
484 words[i / 2] |= u32::from(*byte) << ((1 - (i % 2)) * 8);
485 }
486 let mut best: Option<(usize, usize)> = None;
488 let mut cur: Option<(usize, usize)> = None;
489 for (i, word) in words.iter().enumerate() {
490 if *word == 0 {
491 cur = Some(cur.map_or((i, 1), |(base, len)| (base, len + 1)));
492 } else if let Some(run) = cur.take()
493 && best.is_none_or(|b| run.1 > b.1)
494 {
495 best = Some(run);
496 }
497 }
498 if let Some(run) = cur
499 && best.is_none_or(|b| run.1 > b.1)
500 {
501 best = Some(run);
502 }
503 if best.is_some_and(|b| b.1 < 2) {
504 best = None;
505 }
506 for (i, word) in words.iter().enumerate() {
507 if let Some((base, len)) = best
508 && i >= base
509 && i < base + len
510 {
511 if i == base {
512 out.push(':');
513 }
514 continue;
515 }
516 if i != 0 {
517 out.push(':');
518 }
519 if i == 6
520 && let Some((0, len)) = best
521 && (len == 6 || (len == 7 && words[7] != 1) || (len == 5 && words[5] == 0xffff))
522 {
523 let _ = write!(out, "{}.{}.{}.{}", src[12], src[13], src[14], src[15]);
524 break;
525 }
526 let _ = write!(out, "{word:x}");
527 }
528 if best.is_some_and(|(base, len)| base + len == 8) {
529 out.push(':');
530 }
531}
532
533pub fn inet_out(value: &Inet, cidr: bool, out: &mut Vec<u8>) {
535 let mut text = String::new();
536 net_ntop(value, value.bits, &mut text);
537 if cidr && !text.contains('/') {
538 text.push('/');
539 text.push_str(&value.bits.to_string());
540 }
541 out.extend_from_slice(text.as_bytes());
542}
543
544pub fn cidr_abbrev(value: &Inet, text: &mut String) {
546 use std::fmt::Write;
547 let bits = value.bits;
548 let mut out = String::new();
549 match value.family {
550 NetFamily::V4 => {
551 if bits == 0 {
552 out.push('0');
553 }
554 let whole = usize::from(bits / 8);
555 for (n, octet) in value.addr[..whole].iter().enumerate() {
556 let _ = write!(out, "{octet}");
557 if n + 1 < whole {
558 out.push('.');
559 }
560 }
561 let b = bits % 8;
562 if b > 0 {
563 if !out.is_empty() {
564 out.push('.');
565 }
566 let m = 0xffu8 << (8 - b);
567 let _ = write!(out, "{}", value.addr[whole] & m);
568 }
569 }
570 NetFamily::V6 => cidr_ntop_v6(&value.masked(bits).addr, bits, &mut out),
571 }
572 let _ = write!(text, "{out}/{bits}");
573}
574
575fn cidr_ntop_v6(s: &[u8; 16], bits: u8, out: &mut String) {
577 use std::fmt::Write;
578 if bits == 0 {
579 out.push_str("::");
580 return;
581 }
582 let words = usize::from(bits).div_ceil(16).max(2);
583 let (mut zero_s, mut zero_l, mut tmp_s, mut tmp_l) = (0, 0, 0, 0);
585 for i in 0..words {
586 if s[2 * i] | s[2 * i + 1] == 0 {
587 if tmp_l == 0 {
588 tmp_s = i;
589 }
590 tmp_l += 1;
591 } else if tmp_l != 0 && zero_l < tmp_l {
592 zero_s = tmp_s;
593 zero_l = tmp_l;
594 tmp_l = 0;
595 }
596 }
597 if tmp_l != 0 && zero_l < tmp_l {
598 zero_s = tmp_s;
599 zero_l = tmp_l;
600 }
601 let ipv4 = zero_l != words
602 && zero_s == 0
603 && (zero_l == 6
604 || (zero_l == 5 && s[10] == 0xff && s[11] == 0xff)
605 || (zero_l == 7 && s[14] != 0 && s[15] != 1));
606 for p in 0..words {
607 if zero_l != 0 && p >= zero_s && p < zero_s + zero_l {
608 if p == zero_s {
609 out.push(':');
610 }
611 if p == words - 1 {
612 out.push(':');
613 }
614 continue;
615 }
616 if ipv4 && p > 5 {
617 out.push(if p == 6 { ':' } else { '.' });
618 let _ = write!(out, "{}", s[2 * p]);
619 if p != 7 || bits > 120 {
620 let _ = write!(out, ".{}", s[2 * p + 1]);
621 }
622 } else {
623 if !out.is_empty() {
624 out.push(':');
625 }
626 let _ = write!(out, "{:x}", u32::from(s[2 * p]) * 256 + u32::from(s[2 * p + 1]));
627 }
628 }
629}
630
631fn recv_error(message: String) -> TypeError {
633 TypeError::new(SqlState::INVALID_BINARY_REPRESENTATION, message)
634}
635
636pub fn inet_recv(recv: &mut Recv<'_>, cidr: bool) -> Result<Inet, TypeError> {
642 let name = type_name(cidr);
643 let family = match recv.byte()? {
644 2 => NetFamily::V4,
645 3 => NetFamily::V6,
646 _ => {
647 return Err(recv_error(format!("invalid address family in external \"{name}\" value")));
648 }
649 };
650 let bits = recv.byte()?;
651 if bits > family.max_bits() {
652 return Err(recv_error(format!("invalid bits in external \"{name}\" value")));
653 }
654 recv.byte()?;
655 let size = recv.byte()?;
656 if usize::from(size) != family.size() {
657 return Err(recv_error(format!("invalid length in external \"{name}\" value")));
658 }
659 let mut addr = [0u8; 16];
660 addr[..family.size()].copy_from_slice(recv.bytes(family.size())?);
661 if cidr && !address_ok(&addr, bits, family) {
662 let mut error = recv_error("invalid external \"cidr\" value".into());
663 error.detail = Some("Value has bits set to right of mask.".into());
664 return Err(error);
665 }
666 Ok(Inet { family, bits, addr })
667}
668
669pub fn inet_send(value: &Inet, cidr: bool, out: &mut Vec<u8>) {
671 let size = value.family.size();
672 out.extend_from_slice(&[value.family.code(), value.bits, u8::from(cidr)]);
673 out.push(u8::try_from(size).unwrap_or(16));
674 out.extend_from_slice(value.bytes());
675}
676
677pub fn bitncmp(l: &[u8; 16], r: &[u8; 16], n: u8) -> i32 {
679 let whole = usize::from(n / 8);
680 if let Some((a, b)) = l[..whole].iter().zip(&r[..whole]).find(|(a, b)| a != b) {
681 return i32::from(*a) - i32::from(*b);
682 }
683 if n.is_multiple_of(8) {
684 return 0;
685 }
686 let mask = 0xffu8 << (8 - n % 8);
687 match (l[whole] & mask).cmp(&(r[whole] & mask)) {
688 Ordering::Less => -1,
689 Ordering::Equal => 0,
690 Ordering::Greater => 1,
691 }
692}
693
694pub fn bitncommon(l: &[u8; 16], r: &[u8; 16], n: u8) -> u8 {
696 let mut nbits = n % 8;
697 let mut byte = 0usize;
698 while byte < usize::from(n / 8) {
699 if l[byte] != r[byte] {
700 nbits = 7;
701 break;
702 }
703 byte += 1;
704 }
705 if nbits != 0 {
706 let diff = u32::from(l[byte] ^ r[byte]);
707 while diff >> (8 - nbits) != 0 {
708 nbits -= 1;
709 }
710 }
711 u8::try_from(byte * 8).unwrap_or(128) + nbits
712}
713
714pub fn network_cmp(a: &Inet, b: &Inet) -> i32 {
716 if a.family != b.family {
717 return i32::from(a.family.code()) - i32::from(b.family.code());
718 }
719 let order = bitncmp(&a.addr, &b.addr, a.bits.min(b.bits));
720 if order != 0 {
721 return order;
722 }
723 let order = i32::from(a.bits) - i32::from(b.bits);
724 if order != 0 {
725 return order;
726 }
727 bitncmp(&a.addr, &b.addr, a.family.max_bits())
728}
729
730#[cfg(test)]
731mod tests {
732 use super::*;
733
734 fn text(input: &str, cidr: bool) -> String {
735 let mut out = Vec::new();
736 inet_out(&inet_in(input, cidr).unwrap(), cidr, &mut out);
737 String::from_utf8(out).unwrap()
738 }
739
740 #[test]
741 fn inet_keeps_the_host_bits_and_omits_a_full_mask() {
742 assert_eq!(text("192.168.1.5", false), "192.168.1.5");
743 assert_eq!(text("192.168.1.5/24", false), "192.168.1.5/24");
744 assert_eq!(text("::ffff:1.2.3.4", false), "::ffff:1.2.3.4");
745 assert_eq!(text("2001:db8:0:0:1:0:0:1", false), "2001:db8::1:0:0:1");
746 assert_eq!(text("::1", false), "::1");
747 assert_eq!(text("1::/64", false), "1::/64");
748 }
749
750 #[test]
751 fn cidr_finds_the_mask_from_the_class() {
752 assert_eq!(text("10", true), "10.0.0.0/8");
753 assert_eq!(text("172.16", true), "172.16.0.0/16");
754 assert_eq!(text("192.168.1", true), "192.168.1.0/24");
755 assert_eq!(text("224", true), "224.0.0.0/4");
756 assert_eq!(text("224.0.0.0", true), "224.0.0.0/32");
757 assert_eq!(text("0x0a", true), "10.0.0.0/8");
758 }
759
760 #[test]
761 fn bad_values_have_the_errors_of_postgres() {
762 assert_eq!(
763 inet_in("1.2.3", false).unwrap_err().message,
764 "invalid input syntax for type inet: \"1.2.3\""
765 );
766 assert_eq!(
767 inet_in("1.2.3.4/33", false).unwrap_err().message,
768 "invalid input syntax for type inet: \"1.2.3.4/33\""
769 );
770 let error = inet_in("192.168.1.5/24", true).unwrap_err();
771 assert_eq!(error.message, "invalid cidr value: \"192.168.1.5/24\"");
772 assert_eq!(error.detail.as_deref(), Some("Value has bits set to right of mask."));
773 }
774
775 #[test]
776 fn abbrev_of_cidr_drops_the_zero_octets() {
777 let mut out = String::new();
778 cidr_abbrev(&inet_in("10.1.0.0/16", true).unwrap(), &mut out);
779 assert_eq!(out, "10.1/16");
780 }
781
782 #[test]
783 fn the_binary_form_reads_back() {
784 let value = inet_in("2001:db8::/32", true).unwrap();
785 let mut out = Vec::new();
786 inet_send(&value, true, &mut out);
787 assert_eq!(&out[..4], &[3, 32, 1, 16]);
788 let mut recv = Recv::new(&out);
789 assert_eq!(inet_recv(&mut recv, true), Ok(value));
790 }
791
792 #[test]
793 fn the_network_part_sorts_first() {
794 let a = inet_in("10.0.0.0/8", false).unwrap();
795 let b = inet_in("10.0.0.1/32", false).unwrap();
796 let c = inet_in("::1", false).unwrap();
797 assert_eq!(network_cmp(&a, &b), -24);
798 assert_eq!(network_cmp(&b, &c), -1);
799 let d = inet_in("10.0.0.9", false).unwrap();
800 assert_eq!(network_cmp(&d, &b), 8);
801 assert_eq!(bitncommon(&a.addr, &b.addr, 8), 8);
802 }
803}