1use std::{
6 collections::VecDeque,
7 io::{ErrorKind, Read, Write},
8 path::Path,
9};
10
11use crate::{
12 Error, Interface, InterfaceAddress, InterfaceProperties, InterfaceSummary, InvalidTokenError,
13 Message, Protocol, ProtocolDetail, Result, ShowInterfacesMessage, ShowProtocolDetailsMessage,
14 ShowStatusMessage,
15};
16
17pub struct Connection {
24 stream: tokio::net::UnixStream,
25 unparsed_bytes: Vec<u8>,
26 unsent_messages: VecDeque<Message>,
27 request_in_progress: bool,
28}
29
30impl Connection {
31 pub(crate) async fn new<P: AsRef<Path>>(unix_socket: P) -> Result<Self> {
34 let stream = tokio::net::UnixStream::connect(unix_socket).await?;
36
37 let mut connection = Connection {
38 stream,
39 unparsed_bytes: Vec::with_capacity(2 * READ_FRAME_SIZE),
40 unsent_messages: VecDeque::with_capacity(20),
41 request_in_progress: true,
43 };
44
45 if let Message::Welcome(ref greeting) = connection.next_message().await? {
47 log::trace!("received greeting {greeting}");
48 if let Message::Ok = connection.next_message().await? {
50 log::trace!("handshake completed. connection active");
51 connection.allow_new_requests();
52 return Ok(connection);
53 }
54 }
55 Err(Error::InvalidToken(InvalidTokenError::Other(
56 "did not find greeting".into(),
57 )))
58 }
59
60 #[inline]
63 fn allow_new_requests(&mut self) {
64 self.request_in_progress = false;
65 }
66
67 pub async fn send_request(&mut self, request: &str) -> Result<Vec<Message>> {
70 if self.request_in_progress {
73 return Err(Error::OperationInProgress);
74 }
75
76 self.unparsed_bytes.clear();
78 self.unsent_messages.clear();
79
80 let request = if request.ends_with('\n') {
82 request.to_owned()
83 } else {
84 format!("{}\n", &request)
85 };
86 let mut result: Vec<Message> = Vec::new();
87 self.write_to_server(&request).await?;
88 self.request_in_progress = true; loop {
92 let message = self.next_message().await?;
93 if let Message::Ok = message {
94 self.allow_new_requests();
95 return Ok(result);
96 } else {
97 result.push(message);
98 }
99 }
100 }
101
102 pub async fn show_interfaces_summary(&mut self) -> Result<Vec<InterfaceSummary>> {
105 let messages = self.send_request("show interfaces summary").await?;
106
107 for message in &messages {
109 if let Message::InterfaceSummary(_) = message {
111 return if let Some(ifcs) = InterfaceSummary::from_enum(message) {
112 Ok(ifcs)
113 } else {
114 Err(Error::ParseError(messages))
115 };
116 }
117 }
118
119 Err(Error::ParseError(messages))
121 }
122
123 pub async fn show_interfaces(&mut self) -> Result<Vec<ShowInterfacesMessage>> {
126 let messages = self.send_request("show interfaces").await?;
127 handle_show_interfaces(messages)
128 }
129
130 pub async fn show_protocols(&mut self, pattern: Option<&str>) -> Result<Vec<Protocol>> {
136 let cmd = if let Some(pattern) = pattern {
137 format!("show protocols \"{pattern}\"")
138 } else {
139 "show protocols".into()
140 };
141 let messages = self.send_request(&cmd).await?;
142 handle_show_protocols(messages)
143 }
144
145 pub async fn show_protocols_details(
151 &mut self,
152 pattern: Option<&str>,
153 ) -> Result<Vec<ShowProtocolDetailsMessage>> {
154 let cmd = if let Some(pattern) = pattern {
155 format!("show protocols all \"{pattern}\"")
156 } else {
157 "show protocols all".into()
158 };
159 let messages = self.send_request(&cmd).await?;
160 handle_show_protocols_details(messages)
161 }
162
163 pub async fn show_status(&mut self) -> Result<ShowStatusMessage> {
166 let messages = self.send_request("show status").await?;
167
168 match ShowStatusMessage::from_messages(&messages) {
169 Some(ssm) => Ok(ssm),
170 None => Err(Error::ParseError(messages)),
171 }
172 }
173
174 async fn next_message(&mut self) -> Result<Message> {
176 if let Some(pending_message) = self.unsent_messages.pop_front() {
178 return Ok(pending_message);
179 }
180
181 self.fetch_new_messages().await?;
184 if let Some(new_message) = self.unsent_messages.pop_front() {
185 Ok(new_message)
186 } else {
187 Err(Error::eof("was expecting more messages"))
189 }
190 }
191
192 async fn write_to_server(&self, request: &str) -> Result<()> {
195 let data = request.as_bytes();
196 let total_size = data.len();
197 let mut written_size = 0;
198 loop {
199 self.stream.writable().await?;
200 match self.stream.try_write(data) {
201 Ok(n) => {
202 written_size += n;
203 if written_size >= total_size {
204 return Ok(());
205 }
206 }
207 Err(err) => {
208 if err.kind() != ErrorKind::WouldBlock {
209 return Err(Error::from(err));
210 }
211 }
212 }
213 }
214 }
215
216 #[inline]
218 async fn fetch_new_messages(&mut self) -> Result<()> {
219 loop {
220 self.stream.readable().await?;
221 let mut frame = [0_u8; READ_FRAME_SIZE];
222 match self.stream.try_read(&mut frame) {
223 Ok(0) => {
224 return Err(Error::eof("premature EOF"));
225 }
226 Ok(count) => {
227 if enqueue_messages(
228 &frame[..count],
229 &mut self.unparsed_bytes,
230 &mut self.unsent_messages,
231 )? == 0
232 {
233 continue;
236 } else {
237 return Ok(());
238 }
239 }
240 Err(err) => {
241 if err.kind() != ErrorKind::WouldBlock {
242 return Err(Error::IoError(err));
243 }
244 }
245 }
246 }
247 }
248}
249
250pub struct SyncConnection {
252 stream: std::os::unix::net::UnixStream,
253 unparsed_bytes: Vec<u8>,
254 unsent_messages: VecDeque<Message>,
255 request_in_progress: bool,
256}
257
258impl SyncConnection {
259 pub(crate) fn new<P: AsRef<Path>>(unix_socket: P) -> Result<Self> {
262 let stream = std::os::unix::net::UnixStream::connect(unix_socket)?;
263
264 let mut connection = SyncConnection {
265 stream,
266 unparsed_bytes: Vec::with_capacity(2 * READ_FRAME_SIZE),
267 unsent_messages: VecDeque::with_capacity(20),
268 request_in_progress: true,
269 };
270
271 if let Message::Welcome(ref greeting) = connection.next_message()? {
272 log::trace!("received greeting {greeting}");
273 if let Message::Ok = connection.next_message()? {
274 log::trace!("handshake completed. connection active");
275 connection.allow_new_requests();
276 return Ok(connection);
277 }
278 }
279 Err(Error::InvalidToken(InvalidTokenError::Other(
280 "did not find greeting".into(),
281 )))
282 }
283
284 #[inline]
287 fn allow_new_requests(&mut self) {
288 self.request_in_progress = false;
289 }
290
291 pub fn send_request(&mut self, request: &str) -> Result<Vec<Message>> {
294 if self.request_in_progress {
295 return Err(Error::OperationInProgress);
296 }
297
298 self.unparsed_bytes.clear();
299 self.unsent_messages.clear();
300
301 let request = if request.ends_with('\n') {
302 request.to_owned()
303 } else {
304 format!("{}\n", &request)
305 };
306 let mut result: Vec<Message> = Vec::new();
307 self.write_to_server(&request)?;
308 self.request_in_progress = true;
309
310 loop {
311 let message = self.next_message()?;
312 if let Message::Ok = message {
313 self.allow_new_requests();
314 return Ok(result);
315 } else {
316 result.push(message);
317 }
318 }
319 }
320
321 pub fn show_interfaces_summary(&mut self) -> Result<Vec<InterfaceSummary>> {
324 let messages = self.send_request("show interfaces summary")?;
325
326 for message in &messages {
327 if let Message::InterfaceSummary(_) = message {
328 return if let Some(ifcs) = InterfaceSummary::from_enum(message) {
329 Ok(ifcs)
330 } else {
331 Err(Error::ParseError(messages))
332 };
333 }
334 }
335
336 Err(Error::ParseError(messages))
337 }
338
339 pub fn show_interfaces(&mut self) -> Result<Vec<ShowInterfacesMessage>> {
342 let messages = self.send_request("show interfaces")?;
343 handle_show_interfaces(messages)
344 }
345
346 pub fn show_protocols(&mut self, pattern: Option<&str>) -> Result<Vec<Protocol>> {
352 let cmd = if let Some(pattern) = pattern {
353 format!("show protocols \"{pattern}\"")
354 } else {
355 "show protocols".into()
356 };
357 let messages = self.send_request(&cmd)?;
358 handle_show_protocols(messages)
359 }
360
361 pub fn show_protocols_details(
367 &mut self,
368 pattern: Option<&str>,
369 ) -> Result<Vec<ShowProtocolDetailsMessage>> {
370 let cmd = if let Some(pattern) = pattern {
371 format!("show protocols all \"{pattern}\"")
372 } else {
373 "show protocols all".into()
374 };
375 let messages = self.send_request(&cmd)?;
376 handle_show_protocols_details(messages)
377 }
378
379 pub fn show_status(&mut self) -> Result<ShowStatusMessage> {
382 let messages = self.send_request("show status")?;
383
384 match ShowStatusMessage::from_messages(&messages) {
385 Some(ssm) => Ok(ssm),
386 None => Err(Error::ParseError(messages)),
387 }
388 }
389
390 fn next_message(&mut self) -> Result<Message> {
392 if let Some(pending_message) = self.unsent_messages.pop_front() {
393 return Ok(pending_message);
394 }
395
396 self.fetch_new_messages()?;
397 if let Some(new_message) = self.unsent_messages.pop_front() {
398 Ok(new_message)
399 } else {
400 Err(Error::eof("was expecting more messages"))
401 }
402 }
403
404 fn write_to_server(&mut self, request: &str) -> Result<()> {
407 let data = request.as_bytes();
408 let total_size = data.len();
409 let mut written_size = 0;
410 loop {
411 match self.stream.write(data) {
412 Ok(n) => {
413 written_size += n;
414 if written_size >= total_size {
415 return Ok(());
416 }
417 }
418 Err(err) => {
419 if err.kind() != ErrorKind::WouldBlock {
420 return Err(Error::from(err));
421 }
422 }
423 }
424 }
425 }
426
427 #[inline]
429 fn fetch_new_messages(&mut self) -> Result<()> {
430 loop {
431 let mut frame = [0_u8; READ_FRAME_SIZE];
432 match self.stream.read(&mut frame) {
433 Ok(0) => {
434 return Err(Error::eof("premature EOF"));
435 }
436 Ok(count) => {
437 if enqueue_messages(
438 &frame[..count],
439 &mut self.unparsed_bytes,
440 &mut self.unsent_messages,
441 )? == 0
442 {
443 continue;
444 } else {
445 return Ok(());
446 }
447 }
448 Err(err) => {
449 if err.kind() != ErrorKind::WouldBlock {
450 return Err(Error::IoError(err));
451 }
452 }
453 }
454 }
455 }
456}
457
458fn handle_show_interfaces(messages: Vec<Message>) -> Result<Vec<ShowInterfacesMessage>> {
459 let mut result = vec![];
460
461 let mut idx = 0;
463 loop {
464 if idx >= messages.len() {
468 return Ok(result);
469 }
470
471 let first_msg = &messages[idx];
473 idx += 1;
474
475 if let Some(msg_1001) = Interface::from_enum(first_msg) {
477 let next_1001_idx = messages[idx..]
479 .iter()
480 .position(|x| matches!(x, Message::InterfaceList(_)))
481 .unwrap_or(messages.len() - idx)
482 + idx;
483 let delta = next_1001_idx - idx;
484 if delta == 0 || delta > 2 {
485 log::error!(
486 "conn: parse failed: a 1001 entry without (or more than one) 1003/1004",
487 );
488 return Err(Error::ParseError(messages));
489 }
490 let mut msg_1004: Option<InterfaceProperties> = None;
491 let mut msg_1003: Option<Vec<InterfaceAddress>> = None;
492 while idx < next_1001_idx {
493 let cur_msg = &messages[idx];
494 idx += 1;
495 match cur_msg {
496 Message::InterfaceFlags(_) => {
497 if let Some(props) = InterfaceProperties::from_enum(cur_msg) {
498 msg_1004 = Some(props);
499 } else {
500 return Err(Error::ParseError(messages));
501 }
502 }
503 Message::InterfaceAddress(_) => {
504 if let Some(addrs) = InterfaceAddress::from_enum(cur_msg) {
505 msg_1003 = Some(addrs);
506 } else {
507 return Err(Error::ParseError(messages));
508 }
509 }
510 _ => {
511 log::error!(
512 "conn: parse failed: found invalid code {}",
513 messages[idx].code()
514 );
515 return Err(Error::ParseError(messages));
516 }
517 }
518 }
519 if let Some(msg_1004) = msg_1004 {
520 result.push(ShowInterfacesMessage {
521 interface: msg_1001,
522 properties: msg_1004,
523 addresses: msg_1003.unwrap_or_default(),
524 });
525 } else {
526 log::error!("conn: parse failed: found a 1001 without a 1004");
527 return Err(Error::ParseError(messages));
528 }
529 } else {
530 return Err(Error::ParseError(messages));
531 }
532 }
533}
534
535fn handle_show_protocols(messages: Vec<Message>) -> Result<Vec<Protocol>> {
536 for message in &messages {
538 if let Message::ProtocolList(_) = message {
540 return if let Some(protocols) = Protocol::from_enum(message) {
541 Ok(protocols)
542 } else {
543 Err(Error::ParseError(messages))
544 };
545 }
546 }
547
548 Err(Error::ParseError(messages))
550}
551
552fn handle_show_protocols_details(
553 messages: Vec<Message>,
554) -> Result<Vec<ShowProtocolDetailsMessage>> {
555 let mut result = vec![];
556 if let Some(mut idx) = messages
558 .iter()
559 .position(|x| matches!(x, Message::ProtocolList(_)))
560 {
561 while idx < messages.len() {
562 if let Some(protocol) = Protocol::from_enum(&messages[idx]) {
565 idx += 1;
567 if let Some(protocol) = protocol.first() {
569 if idx == messages.len()
571 || !matches!(messages[idx], Message::ProtocolDetails(_))
572 {
573 result.push(ShowProtocolDetailsMessage {
576 protocol: protocol.clone(),
577 detail: None,
578 });
579 continue;
580 }
581 if let Some(detail) = ProtocolDetail::from_enum(&messages[idx]) {
583 idx += 1;
585 result.push(ShowProtocolDetailsMessage {
586 protocol: protocol.clone(),
587 detail: Some(detail),
588 });
589 } else {
590 log::error!("conn: failed to parse 1006 message");
591 return Err(Error::ParseError(messages));
592 }
593 }
594 } else {
595 log::error!("conn: failed to parse 1002 message: {:?}", messages[idx]);
596 return Err(Error::ParseError(messages));
597 }
598 }
599
600 Ok(result)
601 } else {
602 Ok(Vec::new())
604 }
605}
606
607#[inline]
620fn enqueue_messages(
621 frame: &[u8],
622 unparsed_bytes: &mut Vec<u8>,
623 unsent_messages: &mut VecDeque<Message>,
624) -> Result<usize> {
625 let num_unparsed = unparsed_bytes.len();
626 let has_unparsed = num_unparsed > 0;
627 if has_unparsed {
628 let mut new_vec: Vec<u8> = Vec::with_capacity(num_unparsed + frame.len());
631 new_vec.extend_from_slice(unparsed_bytes);
632 new_vec.extend_from_slice(frame);
633 enqueue_messages_from_buffer(&new_vec, unparsed_bytes, unsent_messages)
634 } else {
635 enqueue_messages_from_buffer(frame, unparsed_bytes, unsent_messages)
639 }
640}
641
642#[inline]
656fn enqueue_messages_from_buffer(
657 buffer: &[u8],
658 unparsed_bytes: &mut Vec<u8>,
659 unsent_messages: &mut VecDeque<Message>,
660) -> Result<usize> {
661 let bsize = buffer.len();
662 let mut num_messages = 0;
663 let mut pos: usize = 0;
664 let mut code: u32 = 0;
665 let mut msg_start_pos = 0;
666 let mut message_size: usize = 0;
667 let mut last_msg_added_epos = 0;
668
669 loop {
672 let line_start_pos = pos;
673 log::trace!("conn: checking if we can start processing a new line");
674 if pos >= bsize {
676 log::trace!(" need more data: position is larger than buffer size");
677 break;
678 }
679
680 let nl_pos: usize = match buffer[pos..].iter().position(|it| *it == b'\n') {
683 Some(it) => pos + it,
684 None => {
685 log::trace!(" need more data: buffer is not terminated by newline");
686 break;
687 }
688 };
689 let next_line_pos = nl_pos + 1;
690
691 log::trace!(
692 "conn: processing line: {}",
693 String::from_utf8_lossy(&buffer[pos..nl_pos])
694 );
695
696 if buffer[pos] == b' ' {
697 log::trace!(" no code present, we're a continuation of prev line");
698 pos += 1; message_size += nl_pos - pos + 1; } else {
701 log::trace!(" line has a code, need to check if same as prev or not");
702 if pos + 5 >= bsize {
705 log::trace!(" need more data: trying to parse bird code but buffer does not contain all of it");
706 break;
707 }
708 let new_code = parse_code(&buffer[pos..(pos + 4)])?;
709 let separator = buffer[pos + 4];
710 log::trace!(
711 " encountered code {} and separator '{}'",
712 new_code,
713 separator as char
714 );
715 let is_last = match separator {
716 b' ' => true,
717 b'-' => false,
718 _ => {
719 return Err(Error::InvalidToken(InvalidTokenError::Other(format!(
720 "unknown separator {separator} after code {new_code}"
721 ))));
722 }
723 };
724 pos += 5; let mut ok_added = false;
727 if is_last {
728 if new_code == code {
730 log::trace!(
731 " determined to be the last line, but has same code as before {code}"
732 );
733 message_size += nl_pos - pos + 1;
735 let message = parse_message(code, buffer, msg_start_pos, message_size)?;
736 log::trace!(" pushing last message {message:?}");
737 unsent_messages.push_back(message);
738 num_messages += 1;
739 last_msg_added_epos = nl_pos + 1;
740 } else {
741 log::trace!(" determined to be the last line, has new code {code}");
742 if message_size > 0 {
745 let message = parse_message(code, buffer, msg_start_pos, message_size)?;
746 log::trace!(" pushing prev to last message {message:?}");
747 unsent_messages.push_back(message);
748 num_messages += 1;
749 }
751 code = new_code;
753 msg_start_pos = pos;
754 let message = parse_message(code, buffer, msg_start_pos, message_size)?;
755 log::trace!(" pushing new message {message:?}");
756 if let Message::Ok = message {
757 ok_added = true;
758 }
759 unsent_messages.push_back(message);
760 num_messages += 1;
761 last_msg_added_epos = nl_pos + 1;
762 }
763 if !ok_added {
764 unsent_messages.push_back(Message::Ok);
765 }
766 break;
767 } else {
768 if new_code == code {
771 log::trace!(" not the last line, continuing from prev line");
772 message_size += nl_pos - pos + 1;
774 } else {
775 log::trace!(" not the last line, but new code");
776 if message_size > 0 {
779 let message = parse_message(code, buffer, msg_start_pos, message_size)?;
780 log::trace!(" pushing new message {message:?}");
781 unsent_messages.push_back(message);
782 num_messages += 1;
783 last_msg_added_epos = line_start_pos;
784 }
785 log::trace!(" resetting markers for a new message with code {new_code}");
787 code = new_code;
788 message_size = nl_pos - pos;
789 msg_start_pos = pos;
790 }
791 }
792 }
793 pos = next_line_pos; }
795
796 let remaining = buffer.len() - last_msg_added_epos;
798 log::trace!("conn: found {remaining} pending bytes");
799 if remaining > 0 {
800 unparsed_bytes.clear();
801 let src = &buffer[(buffer.len() - remaining)..];
802 log::trace!("conn: enqueuing pending: {}", &String::from_utf8_lossy(src));
803 unparsed_bytes.extend_from_slice(src);
804 }
805
806 log::trace!(" already enqueued {num_messages} messages");
807 Ok(num_messages)
808}
809
810#[inline]
812fn parse_code(buffer: &[u8]) -> Result<u32> {
813 let text = std::str::from_utf8(&buffer[0..4])?;
814 match text.parse() {
815 Ok(num) => Ok(num),
816 Err(err) => {
817 log::error!(
818 "failed to parse string to u32. this is not a u32: '{}'\n{}",
819 text,
820 err
821 );
822 Err(err.into())
823 }
824 }
825}
826
827#[inline]
829fn parse_message(code: u32, buffer: &[u8], start_pos: usize, msg_size: usize) -> Result<Message> {
830 let mut v: Vec<u8> = Vec::with_capacity(msg_size);
831 let mut idx = 0;
832 let mut pos = start_pos;
833 while pos < buffer.len() {
834 if idx > 0 {
835 pos += match buffer[pos] {
836 b' ' => 1,
837 _ => 5,
838 };
839 v.push(b'\n'); }
841 idx += 1;
842
843 if let Some(nl_pos) = buffer[pos..].iter().position(|it| *it == b'\n') {
844 let src = &buffer[pos..(pos + nl_pos)];
845 v.extend_from_slice(src);
846 pos += src.len() + 1;
847 if v.len() == msg_size {
848 break;
849 }
850 } else {
851 let src = &buffer[pos..];
854 v.extend_from_slice(src);
855 break;
856 }
857 }
858 Ok(Message::from_code(code, std::str::from_utf8(&v)?))
859}
860
861pub const READ_FRAME_SIZE: usize = 2048;
863
864#[cfg(test)]
866mod tests {
867 use super::*;
868 use crate::*;
869
870 fn heredoc(s: &str) -> String {
872 let indent = if let Some(line2) = s.split('\n').nth(2) {
873 line2.find(char::is_alphanumeric).unwrap_or(0)
874 } else {
875 0
876 };
877 s.lines()
878 .map(|x| (if x.starts_with(' ') { &x[indent..] } else { x }).into())
879 .collect::<Vec<String>>()
880 .join("\n")
881 }
882
883 fn get_test_text() -> String {
885 let _ = env_logger::try_init();
886
887 heredoc(
888 "0001 BIRD 2.0.7 ready.
889 show interfaces
890 1001-lo up (index=1)
891 1004-\tMultiAccess AdminUp LinkUp Loopback Ignored MTU=65536
892 1003-\t127.0.0.1/8 (Preferred, scope host)
893 \t::1/128 (Preferred, scope host)
894 1001-eth0 up (index=2)
895 1004-\tMultiAccess Broadcast Multicast AdminUp LinkUp MTU=9000
896 1003-\t172.30.0.12/16 (Preferred, scope site)
897 \t172.29.1.15/32 (scope univ)
898 \t172.29.1.16/32 (scope univ)
899 \t172.29.1.17/32 (scope univ)
900 \tfe80::4495:80ff:fe71:a791/64 (Preferred, scope link)
901 \tfe80::4490::72/64 (scope univ)
902 1001-eth1 up (index=3)
903 1004-\tMultiAccess Broadcast Multicast AdminUp LinkUp MTU=1500
904 1003-\t169.254.199.2/30 (Preferred, opposite 169.254.199.1, scope univ)
905 \tfe80::a06f:7ff:fea7:c662/64 (Preferred, scope link)
906 \tfe80:169:254:199::2/126 (scope link)
907 0000
908 ",
909 )
910 }
911
912 fn get_nth_pos<F>(text: &str, n: u32, op: F) -> usize
914 where
915 F: Fn(&str) -> Option<usize>,
916 {
917 let mut pos: usize = 0;
918 let mut num_match: u32 = 0;
919 for line in text.lines() {
920 if let Some(x) = op(line) {
921 num_match += 1;
922 if num_match == n {
923 pos += x;
924 break;
925 }
926 }
927 pos += line.len() + 1;
928 }
929 pos
930 }
931
932 #[test]
934 fn test_single_line_parsing() {
935 let text = get_test_text();
936 let needle = "lo up (index=1)";
937 let buffer = text.as_bytes();
938 let start_pos = text.find(needle).unwrap();
939 let message = parse_message(1001, buffer, start_pos, needle.len())
940 .expect("should not have failed parsing");
941 if let Message::InterfaceList(s) = message {
942 assert_eq!(s, needle);
943 } else {
944 panic!("incorrect message type {message:?}");
945 }
946 }
947
948 #[test]
950 fn test_multi_line_parsing() {
951 let text = get_test_text();
952
953 let start_pos = get_nth_pos(&text, 1, |x| {
954 if x.ends_with("MTU=9000") {
955 Some(x.len())
956 } else {
957 None
958 }
959 }) + 6;
960 let end_pos = get_nth_pos(&text, 3, |x| {
961 if x.starts_with("1001-") {
962 Some(0)
963 } else {
964 None
965 }
966 }) - 1;
967 let buffer = text.as_bytes();
968 let msg_size = end_pos
969 - start_pos
970 - unsafe {
971 std::str::from_utf8_unchecked(&buffer[start_pos..end_pos])
972 .matches('\n')
973 .count()
974 };
975 let message = parse_message(1003, buffer, start_pos, msg_size)
976 .expect("should not have failed parsing");
977 if let Message::InterfaceAddress(s) = message {
978 assert!(s.starts_with("\t172.30.0.12"));
979 assert!(s.contains("\n\t172.29.1.15/32 (scope univ)\n"));
980 assert!(s.contains("\n\t172.29.1.16/32 (scope univ)\n"));
981 assert!(s.contains("\n\t172.29.1.17/32 (scope univ)\n"));
982 assert!(s.ends_with("fe80::4490::72/64 (scope univ)"));
983 assert!(!s.ends_with('\n'));
984 } else {
985 panic!("incorrect message type {message:?}");
986 }
987 }
988
989 #[test]
991 fn test_multi_line_parsing_at_end() {
992 let text = get_test_text().replace("\n0000 \n", "");
993 let start_pos = get_nth_pos(&text, 3, |x| {
994 if x.starts_with("1003-") {
995 Some(0)
996 } else {
997 None
998 }
999 }) + 5;
1000 let end_pos = text.len();
1001 let buffer = text.as_bytes();
1002 let msg_size = end_pos
1003 - start_pos
1004 - unsafe {
1005 std::str::from_utf8_unchecked(&buffer[start_pos..end_pos])
1006 .matches('\n')
1007 .count()
1008 };
1009 let message = parse_message(1003, buffer, start_pos, msg_size)
1010 .expect("should not have failed parsing");
1011 if let Message::InterfaceAddress(s) = message {
1012 assert!(s.starts_with("\t169.254.199.2"));
1013 assert!(s.contains("\n\tfe80::a06f:7ff:fea7:c662/64 (Preferred, scope link)\n"));
1014 assert!(s.contains("\n\tfe80:169:254:199::2/126 (scope link)"));
1015 assert!(!s.ends_with('\n'));
1016 } else {
1017 panic!("incorrect message type {message:?}");
1018 }
1019 }
1020}