From 20713b875316528a662bead44fb7e8d8112bdce1 Mon Sep 17 00:00:00 2001 From: Juan Jose Nicola Date: Tue, 10 Dec 2024 11:50:37 -0300 Subject: [PATCH 1/3] Update to new FnError. Also, fix conflicts after merging upstream. --- .../src/nasl/builtin/raw_ip/packet_forgery.rs | 2462 +++++++++++++++-- 1 file changed, 2269 insertions(+), 193 deletions(-) diff --git a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs index 30c47036b..1a117291a 100644 --- a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs +++ b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs @@ -5,7 +5,7 @@ //! Defines NASL packet forgery functions use std::{ - net::{Ipv4Addr, SocketAddr}, + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, str::FromStr, }; @@ -25,8 +25,15 @@ use pnet::packet::{ self, ethernet::EthernetPacket, icmp::*, + icmpv6::{ + ndp::{ + MutableNeighborAdvertPacket, MutableNeighborSolicitPacket, MutableRouterAdvertPacket, + }, + Icmpv6Packet, Icmpv6Types, + }, ip::{IpNextHeaderProtocol, IpNextHeaderProtocols}, ipv4::{checksum, Ipv4Packet, MutableIpv4Packet}, + ipv6::MutableIpv6Packet, tcp::{TcpOption, TcpOptionNumbers, TcpPacket, *}, udp::UdpPacket, Packet, PrimitiveValues, @@ -34,7 +41,6 @@ use pnet::packet::{ use pnet_macros_support::types::u9be; use socket2::{Domain, Protocol, Socket}; - use thiserror::Error; use tracing::debug; @@ -100,7 +106,7 @@ pub fn display_packet(vector: &[u8]) { s.push('\n'); } } - println!("packet = {}", s); + println!("packet = {}", &s); } /// Copy from a slice in safe way, performing the necessary test to avoid panicking @@ -129,7 +135,7 @@ fn safe_copy_from_slice( } /// Forge an IP datagram inside the block of data. It takes following arguments: -/// +/// /// - data: is the payload. /// - ip_hl: is the IP header length in 32 bits words. 5 by default. /// - ip_id: is the datagram ID; by default, it is random. @@ -270,7 +276,7 @@ fn forge_ip_packet(register: &Register, configs: &Context) -> Result Result Result { @@ -361,7 +367,7 @@ fn set_ip_elements(register: &Register, _configs: &Context) -> Result Result String { + let protocol = pkt.get_next_level_protocol(); + let byte = protocol.to_primitive_values().0; + let protocol_name = match protocol { + IpNextHeaderProtocols::Tcp => "IPPROTO_TCP", + IpNextHeaderProtocols::Udp => "IPPROTO_UDP", + _ => "IPPROTO_ICMP", + }; + format!("{} ({})", protocol_name, byte) +} + /// Receive a list of IP packets and print them in a readable format in the screen. #[nasl_function] fn dump_ip_packet(register: &Register) -> Result { @@ -445,19 +462,8 @@ fn dump_ip_packet(register: &Register) -> Result { Ok(NaslValue::Null) } -fn dump_protocol(pkt: &Ipv4Packet) -> String { - let protocol = pkt.get_next_level_protocol(); - let byte = protocol.to_primitive_values().0; - let protocol_name = match protocol { - IpNextHeaderProtocols::Tcp => "IPPROTO_TCP", - IpNextHeaderProtocols::Udp => "IPPROTO_UDP", - _ => "IPPROTO_ICMP", - }; - format!("{} ({})", protocol_name, byte) -} - -/// Add a option to a specified IP datagram. -/// +/// Add an option to a specified IP datagram. +/// /// - ip: is the IP datagram /// - code: is the identifier of the option to add /// - length: is the length of the option data @@ -540,7 +546,7 @@ fn insert_ip_options(register: &Register, _configs: &Context) -> Result Result Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - // Missingarguments return Err(FnError::missing_argument("ip")); } }; @@ -657,7 +662,7 @@ fn forge_tcp_packet(register: &Register, _configs: &Context) -> Result Result Result { @@ -708,13 +713,13 @@ fn get_tcp_element(register: &Register, _configs: &Context) -> Result Result { @@ -777,7 +782,7 @@ fn get_tcp_option(register: &Register, _configs: &Context) -> Result Result Result Result { - let positional = register.positional(); - if positional.is_empty() { - return Err(error( - "Missing arguments. It needs at least one tcp packet".to_string(), - )); - } - - for tcp_seg in positional.iter() { - match tcp_seg { - NaslValue::Data(data) => { - let ip = match packet::ipv4::Ipv4Packet::new(data) { - Some(ip) => ip, - None => { - return Err( - ArgumentError::WrongArgument("Invalid TCP packet".to_string()).into(), - ); - } - }; - - match packet::tcp::TcpPacket::new(ip.payload()) { - Some(pkt) => { - let th_flags = format_flags(&pkt); - println!("\tth_sport = {}", pkt.get_source()); - println!("\tth_dport = {}", pkt.get_destination()); - println!("\tth_seq = {}", pkt.get_sequence()); - println!("\tth_ack = {}", pkt.get_acknowledgement()); - println!("\tth_x2 = {}", pkt.get_reserved()); - println!("\tth_off = {}", pkt.get_data_offset()); - println!("\tth_win = {}", pkt.get_window()); - println!("\tth_sum = {}", pkt.get_checksum()); - println!("\tth_urp = {}", pkt.get_urgent_ptr()); - println!("\tth_flags = {}", th_flags); - - display_opts(&pkt); - display_packet(data); - } - None => { - return Err( - ArgumentError::WrongArgument("Invalid TCP packet".to_string()).into(), - ); - } - } - } - _ => { - return Err(ArgumentError::WrongArgument("Invalid ip packet".to_string()).into()); - } - } - } - - Ok(NaslValue::Null) -} fn display_opts(pkt: &TcpPacket) { for o in pkt.get_options_iter() { @@ -1200,8 +1151,69 @@ fn format_flags(pkt: &TcpPacket) -> String { } } +fn print_tcp_packet(tcp: &Option) -> Result<(), FnError> { + match tcp { + Some(pkt) => { + let th_flags = format_flags(&pkt); + println!("------\n"); + println!("\tth_sport = {}", pkt.get_source()); + println!("\tth_dport = {}", pkt.get_destination()); + println!("\tth_seq = {}", pkt.get_sequence()); + println!("\tth_ack = {}", pkt.get_acknowledgement()); + println!("\tth_x2 = {}", pkt.get_reserved()); + println!("\tth_off = {}", pkt.get_data_offset()); + println!("\tth_flags = {}", th_flags); + println!("\tth_win = {}", pkt.get_window()); + println!("\tth_sum = {}", pkt.get_checksum()); + println!("\tth_urp = {}", pkt.get_urgent_ptr()); + println!("\tTCP Options:"); + display_opts(&pkt); + Ok(()) + } + None => Err(ArgumentError::WrongArgument( + "Invalid TPC packet".to_string(), + ).into()), + } +} + +/// Receive a list of IPv4 datagrams and print their TCP part in a readable format in the screen. +#[nasl_function] +fn dump_tcp_packet(register: &Register) -> Result { + let positional = register.positional(); + if positional.is_empty() { + return Err(error( + "Missing arguments. It needs at least one tcp packet".to_string(), + )); + } + + for tcp_seg in positional.iter() { + match tcp_seg { + NaslValue::Data(data) => { + let ip = match packet::ipv4::Ipv4Packet::new(data) { + Some(ip) => ip, + None => { + return Err(ArgumentError::WrongArgument( + "Invalid TCP packet".to_string(), + ).into()); + } + }; + let pkt = packet::tcp::TcpPacket::new(ip.payload()); + print_tcp_packet(&pkt)?; + display_packet(data); + } + _ => { + return Err(ArgumentError::WrongArgument( + "Invalid ip packet".to_string(), + ).into()); + } + } + } + + Ok(NaslValue::Null) +} + /// Fills an IP datagram with UDP data. Note that the ip_p field is not updated. It returns the modified IP datagram. Its arguments are: -/// +/// /// - data: is the payload. /// - ip: is the IP datagram to be filled. /// - uh_dport: is the destination port. NASL will convert it into network order if necessary. 0 by default. @@ -1209,7 +1221,7 @@ fn format_flags(pkt: &TcpPacket) -> String { /// - uh_sum: is the UDP checksum. Although it is not compulsory, the right value is computed by default. /// - uh_ulen: is the data length. By default it is set to the length the data argument plus the size of the UDP header. /// - update_ip_len: is a flag (TRUE by default). If set, NASL will recompute the size field of the IP datagram. -/// + /// Returns the modified IP datagram or NULL on error. #[nasl_function] fn forge_udp_packet(register: &Register, _configs: &Context) -> Result { @@ -1283,7 +1295,7 @@ fn forge_udp_packet(register: &Register, _configs: &Context) -> Result Result, + data: &[u8], +) -> Result { + match datagram { + Some(pkt) => { + println!("------\n"); + println!("\tuh_sport : {}", pkt.get_source()); + println!("\tuh_sport : {:?}", pkt.get_source()); + println!("\tuh_dport : {:?}", pkt.get_destination()); + println!("\tuh_len : {:?}", pkt.get_length()); + println!("\tuh_sum : {:?}", pkt.get_checksum()); + display_packet(data); + } + None => { + return Err(ArgumentError::WrongArgument( + "Invalid UDP packet".to_string()).into()); + } + } + Ok(NaslValue::Null) +} + /// Receive a list of IPv4 datagrams and print their UDP part in a readable format in the screen. #[nasl_function] fn dump_udp_packet(register: &Register) -> Result { @@ -1413,18 +1447,8 @@ fn dump_udp_packet(register: &Register) -> Result { } }; - match packet::udp::UdpPacket::new(ip.payload()) { - Some(pkt) => { - println!("\tuh_sport = {}", pkt.get_source()); - println!("\tuh_dport = {}", pkt.get_destination()); - println!("\tuh_len = {}", pkt.get_length()); - println!("\tuh_sum = {}", pkt.get_checksum()); - display_packet(data); - } - None => { - return invalid_udp_packet_error(); - } - } + let datagram = packet::udp::UdpPacket::new(ip.payload()); + dump_udp(&datagram, data)?; } _ => { return invalid_udp_packet_error(); @@ -1435,10 +1459,27 @@ fn dump_udp_packet(register: &Register) -> Result { Ok(NaslValue::Null) } +fn get_udp_element_from_datagram( + udp: packet::udp::UdpPacket, + register: &Register, +) -> Result { + match register.named("element") { + Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { + "uh_sport" => Ok(NaslValue::Number(udp.get_source() as i64)), + "uh_dport" => Ok(NaslValue::Number(udp.get_destination() as i64)), + "uh_len" => Ok(NaslValue::Number(udp.get_length() as i64)), + "uh_sum" => Ok(NaslValue::Number(udp.get_checksum() as i64)), + "data" => Ok(NaslValue::Data(udp.payload().to_vec())), + _ => Err(ArgumentError::WrongArgument("element".to_string()).into()), + }, + _ => Err(ArgumentError::WrongArgument("element".to_string()).into()), + } +} + /// Get an UDP element from a IP datagram. It returns a data block or an integer, according to the type of the element. Its arguments are: /// - udp: is the IP datagram. /// - element: is the name of the field to get -/// +/// /// Valid IP elements to get are: /// - uh_sport /// - uh_dport @@ -1565,7 +1606,7 @@ fn forge_icmp_packet(register: &Register, _configs: &Context) -> Result Result { data = buf[4..].to_vec(); } - println!("\ticmp_id = {}", icmp_id); - println!("\ticmp_code = {:?}", icmp.get_icmp_code()); - println!("\ticmp_type = {:?}", icmp.get_icmp_type()); - println!("\ticmp_seq = {}", icmp_seq); - println!("\ticmp_cksum = {}", icmp.get_checksum()); - println!("\tdata = {:?}", data); + println!("------"); + println!("\ticmp_id : {}", icmp_id); + println!("\ticmp_code : {:?}", icmp.get_icmp_code()); + println!("\ticmp_type : {:?}", icmp.get_icmp_type()); + println!("\ticmp_seq : {}", icmp_seq); + println!("\ticmp_cksum : {}", icmp.get_checksum()); + println!("\tData : {:?}", data); + println!("\n"); } Ok(NaslValue::Null) } @@ -1711,8 +1754,8 @@ pub mod igmp { } /// Represents a generic IGMP packet. - #[packet] #[allow(dead_code)] + #[packet] pub struct Igmp { #[construct_with(u8)] pub igmp_type: IgmpType, @@ -1846,8 +1889,22 @@ fn new_raw_socket() -> Result { } } +fn new_raw_ipv6_socket() -> Result { + match Socket::new( + Domain::IPV6, // 10 + socket2::Type::RAW, // 3 + Some(Protocol::from(IPPROTO_RAW)), // 255 + ) { + Ok(s) => Ok(s), + Err(e) => Err(error(format!( + "new_raw_ipv6_socket: Not possible to create a raw socket: {}", + e + ))), + } +} + /// This function tries to open a TCP connection and sees if anything comes back (SYN/ACK or RST). -/// +/// /// Its argument is: /// - port: port for the ping #[nasl_function] @@ -1887,7 +1944,7 @@ fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result { return Err(FnError::wrong_unnamed_argument( "Number", - "Invalid length value", + "Invalid port value", )) } }; @@ -1979,7 +2036,7 @@ fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result Result { return Err(FnError::wrong_unnamed_argument( "Integer", - "Invalid timeout value", + "Invalid pcap_timeout value", )) } }; @@ -2122,8 +2179,17 @@ fn nasl_send_packet(register: &Register, configs: &Context) -> Result Result { +// nasl_send_capture(register, configs) +//} + /// Read the next packet. -/// +/// /// - interface: network interface name, by default NASL will try to find the best one /// - pcap_filter: BPF filter, by default it listens to everything /// - timeout: timeout in seconds, 5 by default @@ -2194,87 +2260,2097 @@ fn nasl_send_capture(register: &Register, configs: &Context) -> Result NaslVars<'static> { - let builtin_vars: NaslVars = [ - ( - "IPPROTO_TCP", - NaslValue::Number(IpNextHeaderProtocols::Tcp.to_primitive_values().0.into()), - ), - ( - "IPPROTO_UDP", - NaslValue::Number(IpNextHeaderProtocols::Udp.to_primitive_values().0.into()), - ), - ( - "IPPROTO_ICMP", - NaslValue::Number(IpNextHeaderProtocols::Icmp.to_primitive_values().0.into()), - ), - ( - "IPPROTO_IGMP", - NaslValue::Number(IpNextHeaderProtocols::Igmp.to_primitive_values().0.into()), - ), - ("IPPROTO_IP", NaslValue::Number(IPPROTO_IP.into())), - ("TH_FIN", NaslValue::Number(TcpFlags::FIN.into())), - ("TH_SYN", NaslValue::Number(TcpFlags::SYN.into())), - ("TH_RST", NaslValue::Number(TcpFlags::RST.into())), - ("TH_PUSH", NaslValue::Number(TcpFlags::PSH.into())), - ("TH_ACK", NaslValue::Number(TcpFlags::ACK.into())), - ("TH_URG", NaslValue::Number(TcpFlags::URG.into())), - ("IP_RF", NaslValue::Number(IP_RF)), - ("IP_DF", NaslValue::Number(IP_DF)), - ("IP_MF", NaslValue::Number(IP_MF)), - ("IP_OFFMASK", NaslValue::Number(IP_OFFMASK)), - ( - "TCPOPT_MAXSEG", - NaslValue::Number(TcpOptionNumbers::MSS.to_primitive_values().0 as i64), - ), - ( - "TCPOPT_WINDOW", - NaslValue::Number(TcpOptionNumbers::WSCALE.to_primitive_values().0 as i64), - ), - ( - "TCPOPT_SACK_PERMITTED", - NaslValue::Number(TcpOptionNumbers::SACK_PERMITTED.to_primitive_values().0 as i64), - ), - ( - "TCPOPT_TIMESTAMP", - NaslValue::Number(TcpOptionNumbers::TIMESTAMPS.to_primitive_values().0 as i64), - ), - ] - .iter() - .cloned() - .collect(); - builtin_vars -} +// IP v6 functions -pub struct PacketForgery; +/// Forge an IP datagram inside the block of data. It takes following arguments: +/// +/// The arguments are: +/// - data: Data payload +/// - ip6_v: Version. 6 by default. +/// - ip6_tc: Traffic class. 0 by default. +/// - ip6_fl: Flow label. 0 by default. +/// - ip6_p: IP protocol. 0 by default. +/// - ip6_hlim: Hop limit. Max. 255. 64 by default. +/// - ip6_src: Source address. +/// - ip6_dst: Destination address. +/// +/// Return an IPv6 datagram or Null on error. +#[nasl_function] +fn forge_ip_v6_packet( + register: &Register, + configs: &Context, +) -> Result { + let dst_addr = get_host_ip(configs)?; -function_set! { - PacketForgery, - ( - forge_ip_packet, - set_ip_elements, - get_ip_element, - dump_ip_packet, - insert_ip_options, - forge_tcp_packet, - get_tcp_element, - get_tcp_option, - set_tcp_elements, - insert_tcp_options, - dump_tcp_packet, - forge_udp_packet, - set_udp_elements, - dump_udp_packet, - get_udp_element, - forge_icmp_packet, - get_icmp_element, - dump_icmp_packet, - forge_igmp_packet, - (nasl_tcp_ping, "tcp_ping"), - (nasl_send_packet, "send_packet"), - // These two functions are the same - (nasl_send_capture, "pcap_next"), - (nasl_send_capture, "send_capture"), + if dst_addr.is_ipv4() { + return Err(FnError::wrong_unnamed_argument( + "IPv6", + "forge_ip_v6_packet: No valid dst_addr could be determined via call to get_host_ip()")); + } + + let data = match register.named("data") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), + Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), + _ => Vec::::new(), + }; + + let total_length = 40 + data.len(); + let mut buf = vec![0; total_length]; + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + pkt.set_payload_length(data.len() as u16); + + if !data.is_empty() { + pkt.set_payload(&data); + } + + let ip_v = match register.named("ip6_v") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as u8, + _ => 6_u8, + }; + pkt.set_version(ip_v); + + let ip_tc = match register.named("ip6_tc") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as u8, + _ => 0_u8, + }; + pkt.set_traffic_class(ip_tc); + + let ip_flow = match register.named("ip6_fl") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as u32, + _ => 0_u32, + }; + pkt.set_flow_label(ip_flow); + + let ip_p = match register.named("ip6_p") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as u8, + _ => 0_u8, + }; + pkt.set_next_header(IpNextHeaderProtocol::new(ip_p)); + + let ip_hlim = match register.named("ip6_hlim") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as u8, + _ => 64_u8, + }; + pkt.set_hop_limit(ip_hlim); + + match register.named("ip6_src") { + Some(ContextType::Value(NaslValue::String(x))) => { + match x.parse::() { + Ok(ip) => { + pkt.set_source(ip); + } + Err(e) => { + return Err( + ArgumentError::WrongArgument(format!( + "Invalid ip_src: {}", e + )).into()); + } + }; + x.to_string() + } + _ => String::new(), + }; + + match register.named("ip6_dst") { + Some(ContextType::Value(NaslValue::String(x))) => { + match x.parse::() { + Ok(ip) => { + pkt.set_destination(ip); + } + Err(e) => { + return Err(ArgumentError::WrongArgument(format!( + "Invalid ip_dst: {}", + e + )).into()); + } + }; + x.to_string() + } + _ => { + match dst_addr.to_string().parse::() { + Ok(ip) => { + pkt.set_destination(ip); + } + Err(e) => { + return Err(ArgumentError::WrongArgument(format!( + "Invalid ip: {}", + e + )).into()); + } + }; + dst_addr.to_string() + } + }; + + // There is no checksum for ipv6. Only upper layer + // calculates a checksum using pseudoheader + + Ok(NaslValue::Data(buf)) +} + +/// Get an IP element from a IP v6 datagram. It returns a data block or an integer, according to the type of the element. Its arguments are: +/// - ip6: is the IP v6 datagram. +/// - element: is the name of the field to get +/// +/// Valid IP elements to get are: +/// - ip6_v +/// - ip6_tc +/// - ip6_fl +/// - ip6_plen +/// - ip6_nxt +/// - ip6_hlim +/// - ip6_src +/// - ip6_dst +#[nasl_function] +fn get_ip_v6_element( + register: &Register, +) -> Result { + let buf = match register.named("ip6") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("ip6").into()); + } + }; + + let pkt = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + match register.named("element") { + Some(ContextType::Value(NaslValue::String(e))) => match e.as_str() { + "ip6_v" => Ok(NaslValue::Number(pkt.get_version() as i64)), + "ip6_tc" => Ok(NaslValue::Number(pkt.get_traffic_class() as i64)), + "ip6_fl" => Ok(NaslValue::Number(pkt.get_flow_label() as i64)), + "ip6_plen" => Ok(NaslValue::Number(pkt.get_payload_length() as i64)), + "ip6_nxt" => Ok(NaslValue::Number(i64::from( + pkt.get_next_header().to_primitive_values().0, + ))), + "ip6_src" => Ok(NaslValue::String(pkt.get_source().to_string())), + "ip6_dst" => Ok(NaslValue::String(pkt.get_destination().to_string())), + _ => Err(ArgumentError::WrongArgument( + "Invalid element".to_string(), + ).into()), + }, + _ => Err(FnError::missing_argument("element").into()), + } +} + +/// Set an IP element from a IP v6 datagram. It returns a data block or an integer, according to the type of the element. Its arguments are: +/// - ip6: is the IP v6 datagram. +/// - element: is the name of the field to get +/// +/// Valid IP elements to get are: +/// - ip6_plen +/// - ip6_nxt +/// - ip6_hlim +/// - ip6_src +#[nasl_function] +fn set_ip_v6_elements( + register: &Register, +) -> Result { + let mut buf = match register.named("ip6") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("ip").into()); + } + }; + + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut buf).ok_or_else(|| { + error( + "No possible to create a packet from buffer".to_string()) + })?; + + let ip6_plen = match register.named("ip6_plen") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as u16, + _ => pkt.get_payload_length(), + }; + pkt.set_payload_length(ip6_plen); + + let ip6_nxt = match register.named("ip6_nxt") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as u8, + _ => pkt.get_next_header().0, + }; + pkt.set_next_header(IpNextHeaderProtocol::new(ip6_nxt)); + + if let Some(ContextType::Value(NaslValue::String(x))) = register.named("ip6_src") { + match x.parse::() { + Ok(ip) => { + pkt.set_source(ip); + } + Err(e) => { + return Err(ArgumentError::WrongArgument(format!( + "Invalid ip_src: {}", + e + )).into()); + } + }; + }; + + Ok(NaslValue::Data(buf)) +} + +/// Adds an IPv6 option to the datagram. +/// +/// - ip6 IPv6 packet. +/// - data Data payload. +/// - code Code of option. +/// - length Length of value. +/// - value Value of the option. +/// +/// Return the modified datagram. +/// +#[nasl_function] +fn insert_ip_v6_options( + register: &Register, +) -> Result { + let buf = match register.named("ip6") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("ip6")); + } + }; + + let code = match register.named("code") { + Some(ContextType::Value(NaslValue::Number(x))) => *x, + _ => { + return Err(FnError::missing_argument("code")); + } + }; + let length = match register.named("length") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as usize, + _ => { + return Err(FnError::missing_argument("length")); + } + }; + let value = match register.named("value") { + Some(ContextType::Value(NaslValue::String(x))) => x.as_bytes(), + Some(ContextType::Value(NaslValue::Data(x))) => x, + _ => { + return Err(FnError::missing_argument("value")); + } + }; + + // The pnet library does not have the implementation for create/modify TcpOptions. + // The TcpOption struct members are even private. Therefore, creating it manually. + // This is not possible: + //let opt = Ipv4Option{ + // copied: 1, + // class: 0, + // number: Ipv4OptionNumber(3), + // length: opt_len.to_vec(), + // data: opt_buf.to_vec(), + //}; + + // Get the first byte from an i64 + let codebyte = code.to_le_bytes()[0]; + // Length is 2 bytes. Get the 2 bytes from the i64 + let opt_len = &length.to_le_bytes()[..2]; + let mut opt_buf = vec![0u8; length]; + + //opt_buf[..1].copy_from_slice(&vec![codebyte][..]); + safe_copy_from_slice(&mut opt_buf[..], 0, 1, &[codebyte], 0, 1)?; + //opt_buf[1..3].copy_from_slice(opt_len); + safe_copy_from_slice(&mut opt_buf[..], 1, 3, opt_len, 0, opt_len.len())?; + //opt_buf[3..].copy_from_slice(value); + safe_copy_from_slice(&mut opt_buf[..], 3, length, value, 0, value.len())?; + + let hl_valid_data = 40 + opt_buf.len(); + let padding = 32 - hl_valid_data % 32; + let hl = hl_valid_data + padding; + let mut new_buf = vec![0u8; hl]; + //new_buf[..40].copy_from_slice(&buf[..40]); + safe_copy_from_slice(&mut new_buf[..], 0, 40, &buf, 0, 40)?; + //new_buf[40..hl_valid_data].copy_from_slice(&opt_buf[..opt_buf.len()]); + safe_copy_from_slice( + &mut new_buf[..], + 40, + hl_valid_data, + &opt_buf, + 0, + opt_buf.len(), + )?; + + let mut new_pkt = MutableIpv6Packet::new(&mut new_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + new_pkt.set_payload_length((hl / 4) as u16); + Ok(NaslValue::Data(new_pkt.packet().to_vec())) +} + +/// Receive a list of IP v6 packets and print them in a readable format in the screen. +#[nasl_function] +fn dump_ip_v6_packet(register: &Register) -> Result { + let positional = register.positional(); + if positional.is_empty() { + return Err(ArgumentError::MissingPositionals { + expected: 1, + got: 0, + }.into()); + } + + for ip in positional.iter() { + match ip { + NaslValue::Data(data) => { + let pkt = packet::ipv6::Ipv6Packet::new(data).ok_or_else(|| { + error( + "No possible to create a packet from buffer".to_string(), + ) + })?; + println!("------\n"); + println!("\tip6_v : {:?}", pkt.get_version()); + println!("\tip6_tc : {:?}", pkt.get_traffic_class()); + println!("\tip6_fl : {:?}", pkt.get_flow_label()); + println!("\tip6_plen : {:?}", pkt.get_payload_length()); + println!("\tip6_hlim : {:?}", pkt.get_hop_limit()); + + match pkt.get_next_header() { + IpNextHeaderProtocols::Tcp => println!( + "\tip6_nxt : IPPROTO_TCP ({:?})", + pkt.get_next_header().to_primitive_values().0 + ), + IpNextHeaderProtocols::Udp => println!( + "\tip6_nxt : IPPROTO_UDP ({:?})", + pkt.get_next_header().to_primitive_values().0 + ), + IpNextHeaderProtocols::Icmpv6 => println!( + "\tip6_nxt : IPPROTO_ICMP ({:?})", + pkt.get_next_header().to_primitive_values().0 + ), + _ => println!( + "\tip6_nxt : {:?}", + pkt.get_next_header().to_primitive_values().0 + ), + }; + + println!("\tip6_src : {}", pkt.get_source().to_string()); + println!("\tip6_dst : {:?}", pkt.get_destination().to_string()); + display_packet(data); + } + _ => { + return Err(FnError::wrong_unnamed_argument( + "Data", + "Invalid ip packet", + )); + } + } + } + + Ok(NaslValue::Null) +} + +// TCP over IPv6 + +/// Fills an IP datagram with TCP data. Note that the ip_p field is not updated. It returns the modified IP datagram. Its arguments are: +/// +/// - ip6: is the IP datagram to be filled. +/// - data: is the TCP data payload. +/// - th_sport: is the source port. NASL will convert it into network order if necessary. 0 by default. +/// - th_dport: is the destination port. NASL will convert it into network order if necessary. 0 by default. +/// - th_seq: is the TCP sequence number. NASL will convert it into network order if necessary. Random by default. +/// - th_ack: is the acknowledge number. NASL will convert it into network order if necessary. 0 by default. +/// - th_x2: is a reserved field and should probably be left unchanged. 0 by default. +/// - th_off: is the size of the TCP header in 32 bits words. By default, 5. +/// - th_flags: are the TCP flags. 0 by default. +/// - th_win: is the TCP window size. NASL will convert it into network order if necessary. 0 by default. +/// - th_sum: is the TCP checksum. By default, the right value is computed. +/// - th_urp: is the urgent pointer. 0 by default. +/// - update_ip_len: is a flag (TRUE by default). If set, NASL will recompute the size field of the IP datagram. +/// +/// The modified IP datagram or NULL on error. +#[nasl_function] +fn forge_tcp_v6_packet( + register: &Register, +) -> Result { + let mut ip_buf = match register.named("ip6") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("ip6")); + } + }; + let original_ip_len = ip_buf.len(); + + let data = match register.named("data") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), + Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), + _ => Vec::::new(), + }; + + //tcp length + data length + let total_length = 20 + data.len(); + let mut buf = vec![0; total_length]; + let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + if !data.is_empty() { + tcp_seg.set_payload(&data); + } + + match register.named("th_sport") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_source(*x as u16), + _ => tcp_seg.set_source(0_u16), + }; + match register.named("th_dport") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_destination(*x as u16), + _ => tcp_seg.set_destination(0_u16), + }; + + match register.named("th_seq") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_sequence(*x as u32), + _ => tcp_seg.set_sequence(random_impl()? as u32), + }; + match register.named("th_ack") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_acknowledgement(*x as u32), + _ => tcp_seg.set_acknowledgement(0_u32), + }; + match register.named("th_x2") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_reserved(*x as u8), + _ => tcp_seg.set_reserved(0_u8), + }; + match register.named("th_off") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_data_offset(*x as u8), + _ => tcp_seg.set_data_offset(5_u8), + }; + match register.named("th_flags") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_flags(*x as u16), + _ => tcp_seg.set_flags(0_u16), + }; + match register.named("th_win") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_window(*x as u16), + _ => tcp_seg.set_window(0_u16), + }; + match register.named("th_urp") { + Some(ContextType::Value(NaslValue::Number(x))) => tcp_seg.set_urgent_ptr(*x as u16), + _ => tcp_seg.set_urgent_ptr(0_u16), + }; + + let chksum = match register.named("th_sum") { + Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), + _ => { + let pkt = packet::ipv6::Ipv6Packet::new(&ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let tcp_aux = TcpPacket::new(tcp_seg.packet()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pnet::packet::tcp::ipv6_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) + } + }; + + let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + tcp_seg.set_checksum(chksum); + + ip_buf.append(&mut buf); + let l = ip_buf.len(); + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pkt.set_payload_length(l as u16); + match register.named("update_ip_len") { + Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { + pkt.set_payload_length(original_ip_len as u16); + } + _ => (), + }; + + Ok(NaslValue::Data(ip_buf)) +} + +/// Get an TCP element from a IP datagram. It returns a data block or an integer, according to the type of the element. Its arguments are: +/// - tcp: is the IP datagram. +/// - element: is the name of the field to get +/// +/// Valid IP elements to get are: +/// - th_sport +/// - th_dsport +/// - th_seq +/// - th_ack +/// - th_x2 +/// - th_off +/// - th_flags +/// - th_win +/// - th_sum +/// - th_urp +/// - data +/// +/// Returns an TCP element from a IP datagram. +#[nasl_function] +fn get_tcp_v6_element( + register: &Register, +) -> Result { + let buf = match register.named("tcp") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("tcp")); + } + }; + + let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + let tcp = packet::tcp::TcpPacket::new(ip.payload()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + match register.named("element") { + Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { + "th_sport" => Ok(NaslValue::Number(tcp.get_source() as i64)), + "th_dport" => Ok(NaslValue::Number(tcp.get_destination() as i64)), + "th_seq" => Ok(NaslValue::Number(tcp.get_sequence() as i64)), + "th_ack" => Ok(NaslValue::Number(tcp.get_acknowledgement() as i64)), + "th_x2" => Ok(NaslValue::Number(tcp.get_reserved() as i64)), + "th_off" => Ok(NaslValue::Number(tcp.get_data_offset() as i64)), + "th_flags" => Ok(NaslValue::Number(tcp.get_flags() as i64)), + "th_win" => Ok(NaslValue::Number(tcp.get_window() as i64)), + "th_sum" => Ok(NaslValue::Number(tcp.get_checksum() as i64)), + "th_urp" => Ok(NaslValue::Number(tcp.get_urgent_ptr() as i64)), + "th_data" => Ok(NaslValue::Data(tcp.payload().to_vec())), + _ => Err(ArgumentError::WrongArgument("element".to_string()).into()), + }, + _ => Err(FnError::missing_argument("element")), + } +} + +/// Get a TCP option from a IPv6 datagram. Its arguments are: +/// - tcp: is the IP datagram. +/// - option: is the name of the field to get +/// +/// Valid IP options to get are: +/// - 2: TCPOPT_MAXSEG, values between 536 and 65535 +/// - 3: TCPOPT_WINDOW, with values between 0 and 14 +/// - 4: TCPOPT_SACK_PERMITTED, no value required. +/// - 8: TCPOPT_TIMESTAMP, 8 bytes value for timestamp and echo timestamp, 4 bytes each one. +/// +/// The returned option depends on the given *option* parameter. It is either an int for option 2, 3 and 4 or an array containing the two values for option 8. +fn get_tcp_v6_option( + register: &Register, + _configs: &Context, +) -> Result { + let buf = match register.named("tcp") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("tcp")); + } + }; + + let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let tcp = packet::tcp::TcpPacket::new(ip.payload()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + let mut max_seg: i64 = 0; + let mut window: i64 = 0; + let mut sack_permitted: i64 = 0; + let mut timestamps: Vec = vec![NaslValue::Number(0), NaslValue::Number(0)]; + for opt in tcp.get_options_iter() { + if opt.get_number() == TcpOptionNumbers::MSS { + let mut val = [0u8; 2]; + //val[..2].copy_from_slice(&opt.payload()[..2]); + safe_copy_from_slice(&mut val, 0, 2, opt.payload(), 0, 2)?; + max_seg = i16::from_be_bytes(val) as i64; + } + if opt.get_number() == TcpOptionNumbers::WSCALE { + let mut val = [0u8; 1]; + //val[..1].copy_from_slice(&opt.payload()[..1]); + safe_copy_from_slice(&mut val, 0, 1, opt.payload(), 0, 1)?; + window = val[0] as i64; + } + if opt.get_number() == TcpOptionNumbers::SACK_PERMITTED { + sack_permitted = 1; + } + if opt.get_number() == TcpOptionNumbers::TIMESTAMPS { + let mut t1 = [0u8; 4]; + let mut t2 = [0u8; 4]; + //t1[..4].copy_from_slice(&opt.payload()[..4]); + safe_copy_from_slice(&mut t1, 0, 4, opt.payload(), 0, 4)?; + //t2[..4].copy_from_slice(&opt.payload()[4..]); + safe_copy_from_slice(&mut t2, 0, 4, opt.payload(), 4, opt.payload().len())?; + let t1_val = i32::from_be_bytes(t1) as i64; + let t2_val = i32::from_be_bytes(t2) as i64; + + timestamps = vec![NaslValue::Number(t1_val), NaslValue::Number(t2_val)]; + } + } + + match register.named("option") { + Some(ContextType::Value(NaslValue::Number(el))) => match el { + 2 => Ok(NaslValue::Number(max_seg)), + 3 => Ok(NaslValue::Number(window)), + 4 => Ok(NaslValue::Number(sack_permitted)), + 8 => Ok(NaslValue::Array(timestamps)), + _ => Err(ArgumentError::WrongArgument( + "Invalid option".to_string(), + ).into()), + }, + _ => Err(FnError::missing_argument("option")), + } +} + +/// This function modifies the TCP fields of an IP datagram. Its arguments are: +/// +/// - data: is the TCP data payload. +/// - tcp: is the IP datagram to be filled. +/// - th_ack: is the acknowledge number. NASL will convert it into network order if necessary. 0 by default. +/// - th_dport: is the destination port. NASL will convert it into network order if necessary. 0 by default. +/// - th_flags: are the TCP flags. 0 by default. +/// - th_off: is the size of the TCP header in 32 bits words. By default, 5. +/// - th_seq: is the TCP sequence number. NASL will convert it into network order if necessary. Random by default. +/// - th_sport: is the source port. NASL will convert it into network order if necessary. 0 by default. +/// - th_sum: is the TCP checksum. By default, the right value is computed. +/// - th_urp: is the urgent pointer. 0 by default. +/// - th_win: is the TCP window size. NASL will convert it into network order if necessary. 0 by default. +/// - th_x2: is a reserved field and should probably be left unchanged. 0 by default. +/// - update_ip_len: is a flag (TRUE by default). If set, NASL will recompute the size field of the IP datagram. +fn set_tcp_v6_elements( + register: &Register, + _configs: &Context, +) -> Result { + let buf = match register.named("tcp") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("tcp")); + } + }; + + let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let iph_len = ip.get_payload_length() as usize * 4; // the header length is given in 32-bits words + + let data = match register.named("data") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), + Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), + _ => Vec::::new(), + }; + + let ori_tcp_buf = <&[u8]>::clone(&ip.payload()).to_owned(); + let mut ori_tcp: packet::tcp::MutableTcpPacket; + + let mut new_buf: Vec; + let tcp_total_length: usize; + if !data.is_empty() { + //Prepare a new buffer with new size, copy the tcp header and set the new data + tcp_total_length = 20 + data.len(); + new_buf = vec![0u8; tcp_total_length]; + //new_buf[..20].copy_from_slice(&ori_tcp_buf[..20]); + safe_copy_from_slice(&mut new_buf[..], 0, 20, &ori_tcp_buf, 0, 20)?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + ori_tcp.set_payload(&data); + } else { + // Copy the original tcp buffer into the new buffer + tcp_total_length = ip.payload().len(); + new_buf = vec![0u8; tcp_total_length]; + //new_buf[..].copy_from_slice(&ori_tcp_buf); + safe_copy_from_slice( + &mut new_buf[..], + 0, + tcp_total_length, + &ori_tcp_buf, + 0, + ori_tcp_buf.len(), + )?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + } + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_sport") { + ori_tcp.set_source(*x as u16); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_dport") { + ori_tcp.set_destination(*x as u16); + }; + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_seq") { + ori_tcp.set_sequence(*x as u32); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_ack") { + ori_tcp.set_acknowledgement(*x as u32); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_x2") { + ori_tcp.set_reserved(*x as u8); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_off") { + ori_tcp.set_data_offset(*x as u8); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_flags") { + ori_tcp.set_flags(*x as u16); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_win") { + ori_tcp.set_window(*x as u16); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_urp") { + ori_tcp.set_urgent_ptr(*x as u16); + }; + + // Set the checksum for the tcp segment + let chksum = match register.named("th_sum") { + Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), + _ => { + let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let tcp_aux = TcpPacket::new(ori_tcp.packet()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pnet::packet::tcp::ipv4_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) + } + }; + ori_tcp.set_checksum(chksum); + + // Create a owned copy of the final tcp segment, which will be appended as payload to the IP packet. + let mut fin_tcp_buf: Vec = vec![0u8; tcp_total_length]; + let buf_aux = <&[u8]>::clone(&ori_tcp.packet()).to_owned(); + fin_tcp_buf.clone_from_slice(&buf_aux); + + // Create a new IP packet with the original IP header, and the new TCP payload + let mut new_ip_buf = vec![0u8; iph_len]; + //new_ip_buf[..].copy_from_slice(&buf[..iph_len]); + safe_copy_from_slice(&mut new_ip_buf[..], 0, iph_len, &buf, 0, iph_len)?; + + new_ip_buf.append(&mut fin_tcp_buf.to_vec()); + + let l = new_ip_buf.len(); + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut new_ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + // pnet will panic if the total length set in the ip datagram field does not much with the total length. + // Therefore, the total length is set to the right one before setting the payload. + // By default it was always updated, but if desired, the original length is set again after setting the payload. + let original_ip_len = pkt.get_payload_length(); + pkt.set_payload_length(l as u16); + pkt.set_payload(&fin_tcp_buf); + match register.named("update_ip_len") { + Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { + pkt.set_payload_length(original_ip_len); + } + _ => (), + }; + + Ok(NaslValue::Data(pkt.packet().to_vec())) +} + +/// This function adds TCP options to a IP datagram. The options are given as key value(s) pair with the positional argument list. The first positional argument is the identifier of the option, the next positional argument is the value for the option. For the option TCPOPT_TIMESTAMP (8) two values must be given. +/// +/// Available options are: +/// +/// - 2: TCPOPT_MAXSEG, values between 536 and 65535 +/// - 3: TCPOPT_WINDOW, with values between 0 and 14 +/// - 4: TCPOPT_SACK_PERMITTED, no value required. +/// - 8: TCPOPT_TIMESTAMP, 8 bytes value for timestamp and echo timestamp, 4 bytes each one. +fn insert_tcp_v6_options( + register: &Register, + _configs: &Context, +) -> Result { + let buf = match register.named("tcp") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(error( + "insert_tcp_options: missing field".to_string(), + )); + } + }; + + let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let iph_len = ip.get_header_length() as usize * 4; // the header length is given in 32-bits words + let ori_tcp_buf = <&[u8]>::clone(&ip.payload()).to_owned(); + let mut ori_tcp: packet::tcp::MutableTcpPacket; + + // Get the new data or use the existing one. + let data = match register.named("data") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), + Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), + _ => { + let tcp = TcpPacket::new(&ori_tcp_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + tcp.payload().to_vec() + } + }; + + // Forge the options field + let positional = register.positional(); + if positional.is_empty() { + return Err(error( + "Missing optional arguments. At least one optional porsitional argument followed by its value must be given".to_string() + )); + } + + let mut opts: Vec = vec![]; + let mut opts_len = 0; + let mut opts_iter = positional.iter(); + loop { + match opts_iter.next() { + Some(NaslValue::Number(o)) if *o == 2 => { + if let Some(NaslValue::Number(val)) = opts_iter.next() { + let v = *val as u16; + opts.push(TcpOption::mss(v)); + opts_len += 4; + } else { + return Err(ArgumentError::WrongArgument( + "Invalid value for tcp option TCPOPT_MAXSEG".to_string(), + ).into()); + } + } + Some(NaslValue::Number(o)) if *o == 3 => { + if let Some(NaslValue::Number(val)) = opts_iter.next() { + let v = *val as u8; + opts.push(TcpOption::wscale(v)); + opts_len += 3; + } else { + return Err(ArgumentError::WrongArgument( + "Invalid value for tcp option TCPOPT_WINDOW".to_string(), + ).into()); + } + } + + Some(NaslValue::Number(o)) if *o == 4 => { + opts.push(TcpOption::sack_perm()); + opts_len += 2; + } + Some(NaslValue::Number(o)) if *o == 8 => { + if let Some(NaslValue::Number(val1)) = opts_iter.next() { + if let Some(NaslValue::Number(val2)) = opts_iter.next() { + let v1 = *val1 as u32; + let v2 = *val2 as u32; + opts.push(TcpOption::timestamp(v1, v2)); + opts_len += 10; + } else { + return Err(ArgumentError::WrongArgument( + "Invalid value for tcp option TCPOPT_TIMESTAMP".to_string(), + ).into()); + } + } else { + return Err(ArgumentError::WrongArgument( + "Invalid value for tcp option TCPOPT_TIMESTAMP".to_string(), + ).into()); + } + } + None => break, + _ => { + return Err(ArgumentError::WrongArgument( + "insert_tcp_options: invalid tcp option".to_string(), + ).into()); + } + } + } + + // Padding for completing a 32-bit word + if opts_len > 0 { + //Since there are options, we add 1 for the EOL + opts_len += 1; + let padding = 4 - (opts_len % 4); + for _i in 0..padding { + opts.push(TcpOption::nop()); + opts_len += 1; + } + } + + assert_eq!(opts_len % 4, 0); + + let mut new_buf: Vec; + //Prepare a new buffer with new size, copy the tcp header and set the new data + let tcp_total_length = 20 + opts_len + data.len(); + new_buf = vec![0u8; tcp_total_length]; + //new_buf[..20].copy_from_slice(&ori_tcp_buf[..20]); + safe_copy_from_slice(&mut new_buf[..], 0, 20, &ori_tcp_buf, 0, 20)?; + + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + // At this point, opts len is a 4bytes multiple and the ofset is expressed in 32bits words + ori_tcp.set_data_offset(5 + opts_len as u8 / 4); + if !opts.is_empty() { + ori_tcp.set_options(&opts); + } + if !data.is_empty() { + ori_tcp.set_payload(&data); + } + + // Set the checksum for the tcp segment + let chksum = match register.named("th_sum") { + Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), + _ => { + let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let tcp_aux = TcpPacket::new(ori_tcp.packet()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pnet::packet::tcp::ipv4_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) + } + }; + ori_tcp.set_checksum(chksum); + + // Create a owned copy of the final tcp segment, which will be appended as payload to the IP packet. + let mut fin_tcp_buf: Vec = vec![0u8; tcp_total_length]; + let buf_aux = <&[u8]>::clone(&ori_tcp.packet()).to_owned(); + fin_tcp_buf.clone_from_slice(&buf_aux); + + // Create a new IP packet with the original IP header, and the new TCP payload + let mut new_ip_buf = vec![0u8; iph_len]; + //new_ip_buf[..].copy_from_slice(&buf[..iph_len]); + safe_copy_from_slice(&mut new_ip_buf[..], 0, iph_len, &buf, 0, iph_len)?; + new_ip_buf.append(&mut fin_tcp_buf.to_vec()); + + let l = new_ip_buf.len(); + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + // pnet will panic if the total length set in the ip datagram field does not much with the total length. + // Therefore, the total length is set to the right one before setting the payload. + // By default it was always updated, but if desired, the original length is set again after setting the payload. + let original_ip_len = pkt.get_total_length(); + pkt.set_total_length(l as u16); + pkt.set_payload(&fin_tcp_buf); + match register.named("update_ip_len") { + Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { + pkt.set_total_length(original_ip_len); + } + _ => (), + }; + + // New IP checksum + let chksum = checksum(&pkt.to_immutable()); + pkt.set_checksum(chksum); + + Ok(NaslValue::Data(pkt.packet().to_vec())) +} + +/// Receive a list of IPv6 datagrams and print their TCP part in a readable format in the screen. +fn dump_tcp_v6_packet(register: &Register, _: &Context) -> Result { + let positional = register.positional(); + if positional.is_empty() { + return Err(error( + "Missing arguments. It needs at least one tcp packet".to_string(), + )); + } + + for tcp_seg in positional.iter() { + match tcp_seg { + NaslValue::Data(data) => { + let ip = match packet::ipv6::Ipv6Packet::new(data) { + Some(ip) => ip, + None => { + return Err(FnError::wrong_unnamed_argument( + "IPv6", + "Invalid TCP packet", + )); + } + }; + + let pkt = packet::tcp::TcpPacket::new(ip.payload()); + print_tcp_packet(&pkt)?; + display_packet(data); + } + _ => { + return Err(FnError::wrong_unnamed_argument( + "Data", + "Invalid ip packet", + )); + } + } + } + Ok(NaslValue::Null) +} + +// UDP over IPv6 + +/// Fills an IP v6 packet with UDP datagram. Note that the ip_p field is not updated. It returns the modified IP datagram. Its arguments are: +/// +/// - data: is the payload. +/// - ip6: is the IP datagram to be filled. +/// - uh_dport: is the destination port. NASL will convert it into network order if necessary. 0 by default. +/// - uh_sport: is the source port. NASL will convert it into network order if necessary. 0 by default. +/// - uh_sum: is the UDP checksum. Although it is not compulsory, the right value is computed by default. +/// - uh_ulen: is the data length. By default it is set to the length the data argument plus the size of the UDP header. +/// - update_ip6_len: is a flag (TRUE by default). If set, NASL will recompute the size field of the IP datagram. + +/// Returns the modified IP datagram or NULL on error. +fn forge_udp_v6_packet(register: &Register, _: &Context) -> Result { + let mut ip_buf = match register.named("ip6") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => return Err(FnError::missing_argument("ip6")), + }; + let original_ip_len = ip_buf.len(); + + let data = match register.named("data") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), + Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), + _ => Vec::::new(), + }; + + //udp length + data length + let total_length = 8 + data.len(); + let mut buf = vec![0; total_length]; + let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + if !data.is_empty() { + udp_datagram.set_payload(&data); + } + + match register.named("uh_sport") { + Some(ContextType::Value(NaslValue::Number(x))) => udp_datagram.set_source(*x as u16), + _ => udp_datagram.set_source(0_u16), + }; + match register.named("uh_dport") { + Some(ContextType::Value(NaslValue::Number(x))) => udp_datagram.set_destination(*x as u16), + _ => udp_datagram.set_destination(0_u16), + }; + + match register.named("uh_len") { + Some(ContextType::Value(NaslValue::Number(x))) => udp_datagram.set_length(*x as u16), + _ => udp_datagram.set_length(8u16), + }; + + let chksum = match register.named("th_sum") { + Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), + _ => { + let pkt = packet::ipv6::Ipv6Packet::new(&ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let udp_aux = UdpPacket::new(udp_datagram.packet()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pnet::packet::udp::ipv6_checksum(&udp_aux, &pkt.get_source(), &pkt.get_destination()) + } + }; + + let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + udp_datagram.set_checksum(chksum); + + ip_buf.append(&mut buf); + let l = ip_buf.len(); + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pkt.set_payload_length(l as u16); + match register.named("update_ip_len") { + Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { + pkt.set_payload_length(original_ip_len as u16); + } + _ => (), + }; + + Ok(NaslValue::Data(ip_buf)) +} + +/// Get an UDP element from a IP packet. It returns a data block or an integer, according to the type of the element. Its arguments are: +/// - udp: is the IP datagram. +/// - element: is the name of the field to get +/// +/// Valid IP elements to get are: +/// - uh_sport +/// - uh_dport +/// - uh_ulen +/// - uh_sum +/// - data +fn get_udp_v6_element( + register: &Register, + _configs: &Context, +) -> Result { + let buf = match register.named("udp") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("udp")); + } + }; + + let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let udp = packet::udp::UdpPacket::new(ip.payload()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + get_udp_element_from_datagram(udp, register) +} + +/// This function modifies the UDP fields of an IPv6 packet. Its arguments are: +/// +/// - udp: is the IP v6 packet to be filled. +/// - data: is the payload. +/// - uh_dport: is the destination port. NASL will convert it into network order if necessary. 0 by default. +/// - uh_sport: is the source port. NASL will convert it into network order if necessary. 0 by default. +/// - uh_sum: is the UDP checksum. Although it is not compulsory, the right value is computed by default. +/// - uh_ulen: is the data length. By default it is set to the length the data argument plus the size of the UDP header. +fn set_udp_v6_elements( + register: &Register, + _configs: &Context, +) -> Result { + let buf = match register.named("udp") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("udp")); + } + }; + + let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let iph_len = ip.get_payload_length() as usize * 4; // the header length is given in 32-bits words + + let data = match register.named("data") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), + Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), + _ => Vec::::new(), + }; + + let ori_udp_buf = <&[u8]>::clone(&ip.payload()).to_owned(); + let mut ori_udp: packet::udp::MutableUdpPacket; + + let mut new_buf: Vec; + let udp_total_length: usize; + if !data.is_empty() { + //Prepare a new buffer with new size, copy the udp header and set the new data + udp_total_length = 8 + data.len(); + new_buf = vec![0u8; udp_total_length]; + //new_buf[..8].copy_from_slice(&ori_udp_buf[..8]); + safe_copy_from_slice(&mut new_buf[..], 0, 8, &ori_udp_buf, 0, 8)?; + + ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + ori_udp.set_payload(&data); + } else { + // Copy the original udp buffer into the new buffer + udp_total_length = ip.payload().len(); + new_buf = vec![0u8; udp_total_length]; + //new_buf[..].copy_from_slice(&ori_udp_buf); + safe_copy_from_slice( + &mut new_buf[..], + 0, + udp_total_length, + &ori_udp_buf, + 0, + ori_udp_buf.len(), + )?; + ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + } + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("uh_sport") { + ori_udp.set_source(*x as u16); + }; + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("uh_dport") { + ori_udp.set_destination(*x as u16); + }; + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("uh_len") { + ori_udp.set_length(*x as u16); + }; + + // Set the checksum for the udp segment + let chksum = match register.named("uh_sum") { + Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), + _ => { + let pkt = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let udp_aux = UdpPacket::new(ori_udp.packet()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pnet::packet::udp::ipv6_checksum(&udp_aux, &pkt.get_source(), &pkt.get_destination()) + } + }; + ori_udp.set_checksum(chksum); + + // Create a owned copy of the final udp segment, which will be appended as payload to the IP packet. + let mut fin_udp_buf: Vec = vec![0u8; udp_total_length]; + let buf_aux = <&[u8]>::clone(&ori_udp.packet()).to_owned(); + fin_udp_buf.clone_from_slice(&buf_aux); + + // Create a new IP packet with the original IP header, and the new UDP payload + let mut new_ip_buf = vec![0u8; iph_len]; + //new_ip_buf[..].copy_from_slice(&buf[..iph_len]); + safe_copy_from_slice(&mut new_ip_buf[..], 0, iph_len, &buf, 0, iph_len)?; + new_ip_buf.append(&mut fin_udp_buf.to_vec()); + + let l = new_ip_buf.len(); + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut new_ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + pkt.set_payload_length(l as u16); + pkt.set_payload(&fin_udp_buf); + + Ok(NaslValue::Data(pkt.packet().to_vec())) +} + +/// Receive a list of IPv4 datagrams and print their UDP part in a readable format in the screen. +fn dump_udp_v6_packet(register: &Register, _: &Context) -> Result { + let positional = register.positional(); + if positional.is_empty() { + return Err(error( + "Missing arguments. It needs at least one UDP packet".to_string(), + )); + } + + for udp_datagram in positional.iter() { + match udp_datagram { + NaslValue::Data(data) => { + let ip = match packet::ipv6::Ipv6Packet::new(data) { + Some(ip) => ip, + None => { + return Err(ArgumentError::WrongArgument( + "Invalid UDP packet".to_string(), + ).into()); + } + }; + + let datagram = packet::udp::UdpPacket::new(ip.payload()); + dump_udp(&datagram, data)?; + } + _ => { + return Err(ArgumentError::WrongArgument( + "Invalid UDP packet".to_string(), + ).into()); + } + } + } + + Ok(NaslValue::Null) +} + +// ICMP6 + +/// Fills an IPv6 packet with ICMPv6 data. Note that the ip_p field is not updated. It returns the modified IP datagram. Its arguments are: +/// - *ip6*: IP datagram that is updated. +/// - *data*: Payload. +/// - *icmp_cksum*: Checksum, computed by default. +/// - *icmp_code*: ICMP code. 0 by default. +/// - *icmp_id*: ICMP ID. 0 by default. +/// - *icmp_seq*: ICMP sequence number. +/// - *icmp_type*: ICMP type. 0 by default. +/// - *reachable_time*: +/// - *retransmit_time*: +/// - *flags*: +/// - *target*: +/// - *update_ip_len*: If this flag is set, NASL will recompute the size field of the IP datagram. Default: True. +#[nasl_function] +fn forge_icmp_v6_packet( + register: &Register, +) -> Result { + let mut ip_buf = match register.named("ip6") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("icmp6")); + } + }; + + let original_ip_len = ip_buf.len(); + // to extract the max hop limit, ip6_dst. + let pkt_aux = packet::ipv6::Ipv6Packet::new(&ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + let data = match register.named("data") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), + Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), + _ => Vec::::new(), + }; + + let icmp_code = match register.named("icmp_code") { + Some(ContextType::Value(NaslValue::Number(x))) => packet::icmpv6::Icmpv6Code::new(*x as u8), + _ => packet::icmpv6::Icmpv6Code::new(0u8), + }; + + let total_length: usize; + let icmp_pkt_size: usize; + let mut icmp_buf: Vec; + + let icmp_type = match register.named("icmp_type") { + Some(ContextType::Value(NaslValue::Number(x))) => { + if *x < 0 || *x > 255 { + return Err(error(format!( + "forge_icmp_v6_packet: illegal type {}", + x + ))); + } + packet::icmpv6::Icmpv6Type::new(*x as u8) + } + _ => { + return Err(error( + "forge_icmp_v6_packet: illegal type".to_string(), + )); + } + }; + + match icmp_type { + Icmpv6Types::EchoRequest => { + icmp_pkt_size = 8; + total_length = 8 + data.len(); + icmp_buf = vec![0; total_length]; + let mut icmp_pkt = + packet::icmpv6::MutableIcmpv6Packet::new(&mut icmp_buf).ok_or_else(|| { + error( + "No possible to create a packet from buffer".to_string(), + ) + })?; + + icmp_pkt.set_icmpv6_type(icmp_type); + icmp_pkt.set_icmpv6_code(icmp_code); + + if !data.is_empty() { + //buf[8..].copy_from_slice(&data[0..]); + safe_copy_from_slice(&mut icmp_buf, 8, total_length, &data[..], 0, data.len())?; + } + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("icmp_id") { + //buf[4..6].copy_from_slice(&x.to_le_bytes()[0..2]); + safe_copy_from_slice(&mut icmp_buf, 4, 6, &x.to_le_bytes()[..], 0, 2)?; + } + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("icmp_seq") { + //buf[6..8].copy_from_slice(&x.to_le_bytes()[0..2]); + safe_copy_from_slice(&mut icmp_buf, 6, 8, &x.to_le_bytes()[..], 0, 2)?; + } + } + Icmpv6Types::RouterSolicit => { + icmp_pkt_size = 8; + total_length = icmp_pkt_size + data.len(); + icmp_buf = vec![0; total_length]; + let mut icmp_pkt = packet::icmpv6::ndp::MutableRouterSolicitPacket::new(&mut icmp_buf) + .ok_or_else(|| { + error( + "No possible to create a packet from buffer".to_string(), + ) + })?; + + icmp_pkt.set_icmpv6_type(icmp_type); + icmp_pkt.set_icmpv6_code(icmp_code); + + if !data.is_empty() { + //buf[8..].copy_from_slice(&data[0..]); + safe_copy_from_slice(&mut icmp_buf, 8, total_length, &data[..], 0, data.len())?; + } + } + Icmpv6Types::RouterAdvert => { + icmp_pkt_size = MutableRouterAdvertPacket::minimum_packet_size(); + total_length = icmp_pkt_size + data.len(); + icmp_buf = vec![0; total_length]; + let mut icmp_pkt = MutableRouterAdvertPacket::new(&mut icmp_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + icmp_pkt.set_icmpv6_type(icmp_type); + icmp_pkt.set_icmpv6_code(icmp_code); + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("reachable_time") + { + icmp_pkt.set_reachable_time(*x as u32); + } + + if let Some(ContextType::Value(NaslValue::Number(x))) = + register.named("retransmit_time") + { + icmp_pkt.set_retrans_time(*x as u32); + } + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("flags") { + icmp_pkt.set_flags(*x as u8); + } + + icmp_pkt.set_hop_limit(pkt_aux.get_hop_limit()); + + if !data.is_empty() { + //buf[8..].copy_from_slice(&data[0..]); + safe_copy_from_slice( + &mut icmp_buf, + icmp_pkt_size, + total_length, + &data[..], + 0, + data.len(), + )?; + } + } + Icmpv6Types::NeighborSolicit => { + icmp_pkt_size = MutableNeighborSolicitPacket::minimum_packet_size(); + total_length = icmp_pkt_size + data.len(); + icmp_buf = vec![0; total_length]; + let mut icmp_pkt = + MutableNeighborSolicitPacket::new(&mut icmp_buf).ok_or_else(|| { + error( + "No possible to create a packet from buffer".to_string(), + ) + })?; + + icmp_pkt.set_icmpv6_type(icmp_type); + icmp_pkt.set_icmpv6_code(icmp_code); + icmp_pkt.set_target_addr(pkt_aux.get_destination()); + + if !data.is_empty() { + //buf[8..].copy_from_slice(&data[0..]); + safe_copy_from_slice( + &mut icmp_buf, + icmp_pkt_size, + total_length, + &data[..], + 0, + data.len(), + )?; + } + } + Icmpv6Types::NeighborAdvert => { + icmp_pkt_size = MutableNeighborAdvertPacket::minimum_packet_size(); + total_length = icmp_pkt_size + data.len(); + icmp_buf = vec![0; total_length]; + let mut icmp_pkt = + MutableNeighborAdvertPacket::new(&mut icmp_buf).ok_or_else(|| { + error( + "No possible to create a packet from buffer".to_string(), + ) + })?; + + icmp_pkt.set_icmpv6_type(icmp_type); + icmp_pkt.set_icmpv6_code(icmp_code); + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("flags") { + icmp_pkt.set_flags(*x as u8); + if (*x as u8) & 0b10000000_u8 == 0b10000000 { + icmp_pkt.set_target_addr(pkt_aux.get_source()); + } else if let Some(ContextType::Value(NaslValue::String(x))) = + register.named("target") + { + if let Ok(ip) = Ipv6Addr::from_str(x) { + icmp_pkt.set_target_addr(ip); + } + } else { + return Err( + error( + "forge_icmp_v6_package: missing 'target' parameter required for constructing response to a Neighbor Solicitation".to_string())); + } + } + } + _ => { + icmp_pkt_size = 8; + total_length = icmp_pkt_size + data.len(); + icmp_buf = vec![0; total_length]; + let mut icmp_pkt = + packet::icmpv6::MutableIcmpv6Packet::new(&mut icmp_buf).ok_or_else(|| { + error( + "No possible to create a packet from buffer".to_string(), + ) + })?; + + icmp_pkt.set_icmpv6_type(icmp_type); + icmp_pkt.set_icmpv6_code(icmp_code); + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("icmp_id") { + //buf[4..6].copy_from_slice(&x.to_le_bytes()[0..2]); + safe_copy_from_slice(&mut icmp_buf, 4, 6, &x.to_le_bytes()[..], 0, 2)?; + } + + if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("icmp_seq") { + //buf[6..8].copy_from_slice(&x.to_le_bytes()[0..2]); + safe_copy_from_slice(&mut icmp_buf, 6, 8, &x.to_le_bytes()[..], 0, 2)?; + } + } + } + + if !data.is_empty() { + //buf[8..].copy_from_slice(&data[0..]); + safe_copy_from_slice( + &mut icmp_buf, + icmp_pkt_size, + total_length, + &data[..], + 0, + data.len(), + )?; + } + + ip_buf.append(&mut icmp_buf); + let l = ip_buf.len(); + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + let chksum = match register.named("icmp_cksum") { + Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), + _ => { + let icmp_aux = Icmpv6Packet::new(&icmp_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + pnet::packet::icmpv6::checksum(&icmp_aux, &pkt.get_source(), &pkt.get_destination()) + } + }; + + let mut icmp_pkt = + packet::icmpv6::MutableIcmpv6Packet::new(&mut icmp_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + icmp_pkt.set_checksum(chksum); + + pkt.set_payload_length(l as u16); + match register.named("update_ip_len") { + Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { + pkt.set_payload_length(original_ip_len as u16); + } + _ => (), + }; + + Ok(NaslValue::Data(ip_buf)) +} + +/// Get an ICMPv6 element from a IPv6 packet. It returns a data block or an integer, according to the type of the element. Its arguments are: +/// - icmp: is the IP datagram (not the ICMP part only). +/// - element: is the name of the field to get +/// +/// Valid ICMP elements to get are: +/// - icmp_id +/// - icmp_code +/// - icmp_type +/// - icmp_seq +/// - icmp_chsum +/// - icmp_data +#[nasl_function] +fn get_icmp_v6_element( + register: &Register, +) -> Result { + let buf = match register.named("icmp") { + Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), + _ => { + return Err(FnError::missing_argument("icmp")); + } + }; + + let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let icmp = packet::icmpv6::Icmpv6Packet::new(ip.payload()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + match register.named("element") { + Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { + "icmp_code" => Ok(NaslValue::Number(icmp.get_icmpv6_code().0 as i64)), + "icmp_type" => Ok(NaslValue::Number(icmp.get_icmpv6_type().0 as i64)), + "icmp_cksum" => Ok(NaslValue::Number(icmp.get_checksum() as i64)), + "icmp_id" => { + if icmp.payload().len() >= 4 { + let pl = icmp.payload(); + let mut id = [0u8; 8]; + //id[..2].copy_from_slice(&pl[..2]); + safe_copy_from_slice(&mut id, 0, 2, pl, 0, 2)?; + Ok(NaslValue::Number(i64::from_le_bytes(id))) + } else { + Ok(NaslValue::Number(0)) + } + } + "icmp_seq" => { + if icmp.payload().len() >= 4 { + let pl = icmp.payload(); + let mut seq = [0u8; 8]; + //seq[0..2].copy_from_slice(&pl[2..4]); + safe_copy_from_slice(&mut seq, 0, 2, pl, 2, 4)?; + + Ok(NaslValue::Number(i64::from_le_bytes(seq))) + } else { + Ok(NaslValue::Number(0)) + } + } + "data" => { + if icmp.payload().len() > 4 { + let buf = icmp.payload(); + Ok(NaslValue::Data(buf[4..].to_vec())) + } else { + Ok(NaslValue::Null) + } + } + _ => Ok(NaslValue::Null), + }, + _ => Err(FnError::missing_argument("element")), + } +} +/// Receive a list of IPv4 ICMP packets and print them in a readable format in the screen. +#[nasl_function] +fn dump_icmp_v6_packet(register: &Register) -> Result { + let positional = register.positional(); + if positional.is_empty() { + return Err(FnError::missing_argument("icmp")); + } + + for icmp_pkt in positional.iter() { + let buf = match icmp_pkt { + NaslValue::Data(d) => d.clone(), + _ => { + continue; + } + }; + + let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let icmp = packet::icmp::IcmpPacket::new(ip.payload()).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + let mut icmp_seq = 0; + if icmp.payload().len() >= 4 { + let pl = icmp.payload(); + let mut seq = [0u8; 8]; + //seq[0..2].copy_from_slice(&pl[2..4]); + safe_copy_from_slice(&mut seq, 0, 2, pl, 2, 4)?; + icmp_seq = i64::from_le_bytes(seq); + } + + let mut icmp_id = 0; + if icmp.payload().len() >= 4 { + let pl = icmp.payload(); + let mut id = [0u8; 8]; + //id[..2].copy_from_slice(&pl[..2]); + safe_copy_from_slice(&mut id, 0, 2, pl, 0, 2)?; + icmp_id = i64::from_le_bytes(id); + } + + let mut data = vec![]; + if icmp.payload().len() > 4 { + let buf = icmp.payload(); + data = buf[4..].to_vec(); + } + + println!("------"); + println!("\ticmp6_id : {:?}", icmp_id); + println!("\ticmp6_code : {:?}", icmp.get_icmp_code()); + println!("\ticmp_type : {:?}", icmp.get_icmp_type()); + println!("\ticmp6_seq : {:?}", icmp_seq); + println!("\ticmp6_cksum : {:?}", icmp.get_checksum()); + println!("\tData : {:?}", data); + println!("\n"); + } + Ok(NaslValue::Null) +} + +#[nasl_function] +fn forge_igmp_v6_packet( +) -> Result { + // TODO: not implemented. Multicast management on IPv6 networks is handled by Multicast + // Listener Discovery (MLD) which is a part of ICMPv6 in contrast to IGMP's bare IP encapsulation. + // Currently, pnet does not support MDL. + Ok(NaslValue::Null) +} + +/// This function tries to open a TCP connection and sees if anything comes back (SYN/ACK or RST). +/// +/// Its argument is: +/// - port: port for the ping +#[nasl_function] +fn nasl_tcp_v6_ping( + register: &Register, + configs: &Context +) -> Result { + let rnd_tcp_port = || -> u16 { (random_impl().unwrap_or(0) % 65535 + 1024) as u16 }; + + let sports_ori: Vec = vec![ + 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 0, 20, 0, 25, 0, 0, 0, + ]; + let mut sports: Vec = vec![]; + let ports = [ + 139, 135, 445, 80, 22, 515, 23, 21, 6000, 1025, 25, 111, 1028, 9100, 1029, 79, 497, 548, + 5000, 1917, 53, 161, 9001, 65535, 443, 113, 993, 8080, + ]; + + for p in sports_ori.iter() { + if *p == 0u16 { + sports.push(rnd_tcp_port()); + } else { + sports.push(*p); + } + } + + let soc = new_raw_ipv6_socket()?; + if let Err(e) = soc.set_header_included(true) { + return Err(error(format!( + "Not possible to create a raw socket: {}", + e + )).into()); + }; + + // Get the iface name, to set the capture device. + let target_ip = get_host_ip(configs)?; + let local_ip = get_source_ip(target_ip, 50000u16)?; + let iface = get_interface_by_local_ip(local_ip)?; + + let port = match register.named("port") { + Some(ContextType::Value(NaslValue::Number(x))) => *x, + None => 0, //TODO: implement plug_get_host_open_port() + _ => { + return Err(ArgumentError::WrongArgument( + "Invalid length value".to_string(), + ).into()) + } + }; + + if islocalhost(target_ip) { + return Ok(NaslValue::Number(1)); + } + + let mut capture_dev = match Capture::from_device(iface) { + Ok(c) => match c.promisc(true).timeout(100).open() { + Ok(capture) => capture, + Err(e) => return custom_error!("send_packet: {}", e), + }, + Err(e) => return custom_error!("send_packet: {}", e), + }; + let filter = format!("ip and src host {}", target_ip); + + let mut ip_buf = [0u8; 40]; + let mut ip = MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + ip.set_flow_label(0); + ip.set_traffic_class(0); + ip.set_version(6); + ip.set_next_header(IpNextHeaderProtocol(6)); + ip.set_payload_length(40); + let ipv6_src = Ipv6Addr::from_str(&local_ip.to_string()) + .map_err(|_| ArgumentError::WrongArgument("invalid IP".to_string()))?; + ip.set_source(ipv6_src); + + let ipv6_dst = Ipv6Addr::from_str(&target_ip.to_string()) + .map_err(|_| ArgumentError::WrongArgument("invalid IP".to_string()))?; + ip.set_destination(ipv6_dst); + + let mut tcp_buf = [0u8; 20]; + let mut tcp = MutableTcpPacket::new(&mut tcp_buf).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + tcp.set_flags(0x02); //TH_SYN + tcp.set_sequence(random_impl()? as u32); + tcp.set_acknowledgement(0); + tcp.set_data_offset(5); + tcp.set_window(2048); + tcp.set_urgent_ptr(0); + + for (i, _) in sports.iter().enumerate() { + // TODO: the port is fixed since the function to get open ports is not implemented. + let mut sport = rnd_tcp_port(); + let mut dport = port as u16; + if port == 0 { + sport = sports[i]; + dport = ports[i] as u16; + } + + tcp.set_source(sport); + tcp.set_destination(dport); + let chksum = pnet::packet::tcp::ipv6_checksum( + &tcp.to_immutable(), + &ip.get_source(), + &ip.get_destination(), + ); + tcp.set_checksum(chksum); + ip.set_payload(tcp.packet()); + + let sockaddr = socket2::SockAddr::from(SocketAddr::new(target_ip, 0)); + match soc.send_to(ip.packet(), &sockaddr) { + Ok(b) => { + debug!("Sent {} bytes", b); + } + Err(e) => { + return Err(error(format!("send_packet: {}", e))); + } + } + + let p = match capture_dev.filter(&filter, true) { + Ok(_) => capture_dev.next_packet(), + Err(e) => Err(pcap::Error::PcapError(e.to_string())), + }; + + if p.is_ok() { + return Ok(NaslValue::Number(1)); + } + } + + Ok(NaslValue::Null) +} + +/// Send a list of packets, passed as unnamed arguments, with the option to listen to the answers. +/// +/// The arguments are: +/// - Any number of packets to send +/// - length: default length of each every packet, if a packet does not fit, its actual size is taken instead +/// - pcap_active: option to capture the answers, TRUE by default +/// - pcap_filter: BPF filter used for the answers +/// - pcap_timeout: time to wait for the answers in seconds, 5 by default +/// - allow_broadcast: default FALSE +#[nasl_function] +fn nasl_send_v6packet( + register: &Register, + configs: &Context, +) -> Result { + let use_pcap = match register.named("pcap_active") { + Some(ContextType::Value(NaslValue::Boolean(x))) => *x, + None => true, + _ => { + return Err(ArgumentError::wrong_argument( + "pcap_active", + "Boolean", + "Invalid pcap_active value", + ).into()) + } + }; + + let filter = match register.named("pcap_filter") { + Some(ContextType::Value(NaslValue::String(x))) => x.to_string(), + None => String::new(), + _ => { + return Err(ArgumentError::wrong_argument( + "pcap_filter", + "String", + "Invalid pcap_filter value", + ).into()) + } + }; + + let timeout = match register.named("pcap_timeout") { + Some(ContextType::Value(NaslValue::Number(x))) => *x as i32 * 1000i32, // to milliseconds + None => DEFAULT_TIMEOUT, + _ => { + return Err(ArgumentError::wrong_argument( + "pcap_timeout", + "Number", + "Invalid timeout value", + ).into()) + } + }; + + let positional = register.positional(); + if positional.is_empty() { + return Ok(NaslValue::Null); + } + + let soc = new_raw_ipv6_socket()?; + + if let Err(e) = soc.set_header_included_v6(true) { + return Err(error(format!( + "send_v6packet: Not possible to create a raw socket: {}", + e + )).into()); + }; + + let _dflt_packet_sz = match register.named("length") { + Some(ContextType::Value(NaslValue::Number(x))) => *x, + None => 0, + _ => { + return Err(ArgumentError::wrong_argument( + "length", + "Number", + "Invalid length value", + ).into()) + } + }; + + // Get the iface name, to set the capture device. + let target_ip = get_host_ip(configs)?; + print!("\nAAAAAAAAAAA target_ip {:?}\n", target_ip); + + let local_ip = get_source_ip(target_ip, 50000u16)?; + println!("\nAAAAAAAAAAA local_ip {:?}\n", local_ip); + let iface = get_interface_by_local_ip(local_ip)?; + println!("\nAAAAAAAAAAA iface {:?}\n", iface); + + let mut capture_dev = match Capture::from_device(iface) { + Ok(c) => match c.promisc(true).timeout(timeout).open() { + Ok(capture) => capture, + Err(e) => return custom_error!("send_packet: {}", e), + }, + Err(e) => return custom_error!("send_packet: {}", e), + }; + + print!("\nAAAAAAAAAAA antes de pkt\n"); + for pkt in positional.iter() { + print!("AAAAAAAAAAA 1"); + let packet_raw = match pkt { + NaslValue::Data(data) => data as &[u8], + _ => { + return Err(FnError::wrong_unnamed_argument( + "Data", + "Invalid packet", + )) + } + }; + let packet = packet::ipv6::Ipv6Packet::new(packet_raw).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + + // No broadcast destination and dst ip address inside the IP packet + // differs from target IP, is consider a malicious or buggy script. + if packet.get_destination() != target_ip { + return Err(error( + format!("send_packet: malicious or buggy script is trying to send packet to {} instead of designated target {}", + packet.get_destination(), target_ip) + )); + } + + let sock_str = format!("{}:{}", &packet.get_destination().to_string().as_str(), 0); + let sockaddr = match SocketAddr::from_str(&sock_str) { + Ok(addr) => socket2::SockAddr::from(addr), + Err(e) => { + return Err(error( + format!("send_packet: {}", e) + ).into()); + } + }; + + match soc.send_to(packet_raw, &sockaddr) { + Ok(b) => { + debug!("Sent {} bytes", b); + } + Err(e) => { + return Err(error( + format!("send_packet: {}", e) + ).into()); + } + } + + if use_pcap { + let p = match capture_dev.filter(&filter, true) { + Ok(_) => capture_dev.next_packet(), + Err(e) => Err(pcap::Error::PcapError(e.to_string())), + }; + + match p { + Ok(packet) => return Ok(NaslValue::Data(packet.data.to_vec())), + Err(_) => return Ok(NaslValue::Null), + }; + } + } + Ok(NaslValue::Null) +} + +/// Returns a NaslVars with all predefined variables which must be expose to nasl script +pub fn expose_vars() -> NaslVars<'static> { + let builtin_vars: NaslVars = [ + ( + "IPPROTO_TCP", + NaslValue::Number(IpNextHeaderProtocols::Tcp.to_primitive_values().0.into()), + ), + ( + "IPPROTO_UDP", + NaslValue::Number(IpNextHeaderProtocols::Udp.to_primitive_values().0.into()), + ), + ( + "IPPROTO_ICMP", + NaslValue::Number(IpNextHeaderProtocols::Icmp.to_primitive_values().0.into()), + ), + ( + "IPPROTO_IGMP", + NaslValue::Number(IpNextHeaderProtocols::Igmp.to_primitive_values().0.into()), + ), + ("IPPROTO_IP", NaslValue::Number(IPPROTO_IP.into())), + ("TH_FIN", NaslValue::Number(TcpFlags::FIN.into())), + ("TH_SYN", NaslValue::Number(TcpFlags::SYN.into())), + ("TH_RST", NaslValue::Number(TcpFlags::RST.into())), + ("TH_PUSH", NaslValue::Number(TcpFlags::PSH.into())), + ("TH_ACK", NaslValue::Number(TcpFlags::ACK.into())), + ("TH_URG", NaslValue::Number(TcpFlags::URG.into())), + ("IP_RF", NaslValue::Number(IP_RF)), + ("IP_DF", NaslValue::Number(IP_DF)), + ("IP_MF", NaslValue::Number(IP_MF)), + ("IP_OFFMASK", NaslValue::Number(IP_OFFMASK)), + ( + "TCPOPT_MAXSEG", + NaslValue::Number(TcpOptionNumbers::MSS.to_primitive_values().0 as i64), + ), + ( + "TCPOPT_WINDOW", + NaslValue::Number(TcpOptionNumbers::WSCALE.to_primitive_values().0 as i64), + ), + ( + "TCPOPT_SACK_PERMITTED", + NaslValue::Number(TcpOptionNumbers::SACK_PERMITTED.to_primitive_values().0 as i64), + ), + ( + "TCPOPT_TIMESTAMP", + NaslValue::Number(TcpOptionNumbers::TIMESTAMPS.to_primitive_values().0 as i64), + ), + ] + .iter() + .cloned() + .collect(); + builtin_vars +} + +pub struct PacketForgery; + +function_set! { + PacketForgery, + ( + forge_ip_packet, + set_ip_elements, + get_ip_element, + dump_ip_packet, + insert_ip_options, + forge_tcp_packet, + get_tcp_element, + get_tcp_option, + set_tcp_elements, + insert_tcp_options, + dump_tcp_packet, + forge_udp_packet, + set_udp_elements, + dump_udp_packet, + get_udp_element, + forge_icmp_packet, + get_icmp_element, + dump_icmp_packet, + forge_igmp_packet, + (nasl_tcp_ping, "tcp_ping"), + (nasl_send_packet, "send_packet"), + // These two functions are the same + (nasl_send_capture, "pcap_next"), + (nasl_send_capture, "send_capture"), + + //IPv6 + forge_ip_v6_packet, + get_ip_v6_element, + set_ip_v6_elements, + insert_ip_v6_options, + dump_ip_v6_packet, + forge_tcp_v6_packet, + get_tcp_v6_element, + get_tcp_v6_option, + set_tcp_v6_elements, + insert_tcp_v6_options, + dump_tcp_v6_packet, + (nasl_tcp_v6_ping, "tcp_v6_ping"), + forge_udp_v6_packet, + get_udp_v6_element, + set_udp_v6_elements, + dump_udp_v6_packet, + forge_icmp_v6_packet, + get_icmp_v6_element, + dump_icmp_v6_packet, + forge_igmp_v6_packet, + (nasl_send_v6packet, "send_v6packet"), ) } From a028783d686dc9f601e265e9ebf46083f8c08298 Mon Sep 17 00:00:00 2001 From: Juan Jose Nicola Date: Mon, 16 Dec 2024 14:48:01 -0300 Subject: [PATCH 2/3] fix send_v6packet, icmpv6, add tests --- rust/Cargo.lock | 14 +- rust/Cargo.toml | 2 +- rust/examples/forge_icmp_v6.nasl | 41 ++ rust/examples/forge_tcp_v6.nasl | 56 ++ .../src/nasl/builtin/raw_ip/packet_forgery.rs | 611 +++++++----------- rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs | 2 +- .../builtin/raw_ip/tests/packet_forgery.rs | 27 + 7 files changed, 378 insertions(+), 375 deletions(-) create mode 100644 rust/examples/forge_icmp_v6.nasl create mode 100644 rust/examples/forge_tcp_v6.nasl diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 8e274e6a0..0bca63028 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1002,7 +1002,7 @@ checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" dependencies = [ "cfg-if", "libc", - "socket2 0.5.7", + "socket2 0.5.8", "windows-sys 0.48.0", ] @@ -1580,7 +1580,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -1654,7 +1654,7 @@ dependencies = [ "http-body 1.0.1", "hyper 1.5.1", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -3509,7 +3509,7 @@ dependencies = [ "serde_json", "sha1", "sha2", - "socket2 0.5.7", + "socket2 0.5.8", "sysinfo", "thiserror", "time", @@ -3850,9 +3850,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4168,7 +4168,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.5.8", "tokio-macros", "windows-sys 0.52.0", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f04ba6f8e..f5d72896b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -66,7 +66,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" sha1 = "0.10.5" sha2 = "0.10.7" -socket2 = "0.5.7" +socket2 = "0.5.8" sysinfo = "0.30.5" thiserror = "1.0.62" time = { version = "0", features = ["parsing"] } diff --git a/rust/examples/forge_icmp_v6.nasl b/rust/examples/forge_icmp_v6.nasl new file mode 100644 index 000000000..3047a07fc --- /dev/null +++ b/rust/examples/forge_icmp_v6.nasl @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2023 Greenbone AG +# +# SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception + +if(description) { + script_oid("1.2.3"); + exit(0); +} + +include("misc_func.inc"); + +# ICMPv6 +IP6_v = 0x60; +IP6_P = 0x3a;#ICMPv6 +IP6_HLIM = 0x40; +ICMP_ID = rand() % 65536; + +ori = "5858::1"; +dst = "5858::1"; + +ip6_packet = forge_ip_v6_packet( ip6_v: 6, # IP6_v, + ip6_p: IP6_P, + ip6_plen:40, + ip6_hlim:IP6_HLIM, + ip6_src: ori, + ip6_dst: dst ); + +dump_ip_v6_packet(ip6_packet); + +d = "123456"; +icmp = forge_icmp_v6_packet( ip6:ip6_packet, + icmp_type:128, + icmp_code:1, + icmp_seq:2, + icmp_id:ICMP_ID, + icmp_cksum: 0 + ); +dump_icmp_v6_packet(icmpv6); +filter = string("icmp6"); +ret = send_v6packet( icmp, pcap_active:TRUE, pcap_filter:filter, pcap_timeout: 2); +display(ret); diff --git a/rust/examples/forge_tcp_v6.nasl b/rust/examples/forge_tcp_v6.nasl new file mode 100644 index 000000000..1f7a942bc --- /dev/null +++ b/rust/examples/forge_tcp_v6.nasl @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: 2023 Greenbone AG +# +# SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception + +# This script forges an IPv6 packet with a TCP segment including data. Sends it and captures the packet. +# For running with openvas-nasl and scannerctl, run the following commands respectively +# sudo openvas-nasl -X -d -i $PLUGINSPATH ~/my_nasl/forge_tcp_v6.nasl -t 5858::2 +# sudo target/debug/scannerctl execute script ~/my_nasl/forge_tcp_v6.nasl -t 5858::2 +# +# Set the correct IPv6 addresses and routes in the origin and destination hosts with the right address on each. +# sudo ip addr add 5858::1/64 dev wlp6s0 +# sudo ip -6 route add 5858::1 dev wlp6s0 + +if(description) { + script_oid("1.2.3"); + exit(0); +} + +include("misc_func.inc"); + + +src = "5858::1"; +dst = "5858::2"; +sport = 63321; +dport = 63322; + +filter = string("tcp and src ", src, " and dst ", dst); + +ip6 = forge_ip_v6_packet( ip6_v: 6, # IP6_v, + ip6_p: 6, #IP6_P, + ip6_plen:40, + ip6_hlim:IP6_HLIM, + ip6_src: src, + ip6_dst: dst); + + +tcp = forge_tcp_v6_packet(ip6 : ip6, + th_ack : 0, + th_dport : dport, + th_flags : TH_SYN, + #th_seq : tcp_seq + 1024, + th_sport : sport, + th_x2 : 0, + th_off : 5, + th_win : 1024, + th_urp : 0, + tcp_opt : 3, + tcp_opt_val : 7, + data: "123456", + update_ip_len: TRUE + ); + +dump_tcp_v6_packet(tcp); + +res = send_v6packet(tcp, pcap_filter: filter, pcap_timeout: 20, pcap_active: TRUE); +display(res); diff --git a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs index 1a117291a..11706dab1 100644 --- a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs +++ b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs @@ -26,10 +26,12 @@ use pnet::packet::{ ethernet::EthernetPacket, icmp::*, icmpv6::{ + echo_request::MutableEchoRequestPacket, ndp::{ MutableNeighborAdvertPacket, MutableNeighborSolicitPacket, MutableRouterAdvertPacket, + MutableRouterSolicitPacket, }, - Icmpv6Packet, Icmpv6Types, + Icmpv6Types, }, ip::{IpNextHeaderProtocol, IpNextHeaderProtocols}, ipv4::{checksum, Ipv4Packet, MutableIpv4Packet}, @@ -291,7 +293,7 @@ fn forge_ip_packet(register: &Register, configs: &Context) -> Result Result { +fn set_ip_elements(register: &Register) -> Result { let mut buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -381,7 +383,7 @@ fn set_ip_elements(register: &Register, _configs: &Context) -> Result Result { +fn get_ip_element(register: &Register) -> Result { let buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -469,7 +471,7 @@ fn dump_ip_packet(register: &Register) -> Result { /// - length: is the length of the option data /// - value: is the option data #[nasl_function] -fn insert_ip_options(register: &Register, _configs: &Context) -> Result { +fn insert_ip_options(register: &Register) -> Result { let buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -563,7 +565,7 @@ fn insert_ip_options(register: &Register, _configs: &Context) -> Result Result { +fn forge_tcp_packet(register: &Register) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -678,7 +680,7 @@ fn forge_tcp_packet(register: &Register, _configs: &Context) -> Result Result { +fn get_tcp_element(register: &Register) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -722,7 +724,7 @@ fn get_tcp_element(register: &Register, _configs: &Context) -> Result Result { +fn get_tcp_option(register: &Register) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -797,7 +799,7 @@ fn get_tcp_option(register: &Register, _configs: &Context) -> Result Result { +fn set_tcp_elements(register: &Register) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -934,7 +936,7 @@ fn set_tcp_elements(register: &Register, _configs: &Context) -> Result Result { +fn insert_tcp_options(register: &Register) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -1112,7 +1114,6 @@ fn insert_tcp_options(register: &Register, _configs: &Context) -> Result String { fn print_tcp_packet(tcp: &Option) -> Result<(), FnError> { match tcp { Some(pkt) => { - let th_flags = format_flags(&pkt); + let th_flags = format_flags(pkt); println!("------\n"); println!("\tth_sport = {}", pkt.get_source()); println!("\tth_dport = {}", pkt.get_destination()); @@ -1167,12 +1168,10 @@ fn print_tcp_packet(tcp: &Option) -> Result<(), FnError> println!("\tth_sum = {}", pkt.get_checksum()); println!("\tth_urp = {}", pkt.get_urgent_ptr()); println!("\tTCP Options:"); - display_opts(&pkt); + display_opts(pkt); Ok(()) } - None => Err(ArgumentError::WrongArgument( - "Invalid TPC packet".to_string(), - ).into()), + None => Err(ArgumentError::WrongArgument("Invalid TPC packet".to_string()).into()), } } @@ -1192,9 +1191,9 @@ fn dump_tcp_packet(register: &Register) -> Result { let ip = match packet::ipv4::Ipv4Packet::new(data) { Some(ip) => ip, None => { - return Err(ArgumentError::WrongArgument( - "Invalid TCP packet".to_string(), - ).into()); + return Err( + ArgumentError::WrongArgument("Invalid TCP packet".to_string()).into(), + ); } }; let pkt = packet::tcp::TcpPacket::new(ip.payload()); @@ -1202,9 +1201,7 @@ fn dump_tcp_packet(register: &Register) -> Result { display_packet(data); } _ => { - return Err(ArgumentError::WrongArgument( - "Invalid ip packet".to_string(), - ).into()); + return Err(ArgumentError::WrongArgument("Invalid ip packet".to_string()).into()); } } } @@ -1224,7 +1221,7 @@ fn dump_tcp_packet(register: &Register) -> Result { /// Returns the modified IP datagram or NULL on error. #[nasl_function] -fn forge_udp_packet(register: &Register, _configs: &Context) -> Result { +fn forge_udp_packet(register: &Register) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => return Err(FnError::missing_argument("ip")), @@ -1303,7 +1300,7 @@ fn forge_udp_packet(register: &Register, _configs: &Context) -> Result Result { +fn set_udp_elements(register: &Register) -> Result { let buf = match register.named("udp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -1403,10 +1400,7 @@ fn set_udp_elements(register: &Register, _configs: &Context) -> Result, - data: &[u8], -) -> Result { +fn dump_udp(datagram: &Option, data: &[u8]) -> Result { match datagram { Some(pkt) => { println!("------\n"); @@ -1418,8 +1412,7 @@ fn dump_udp( display_packet(data); } None => { - return Err(ArgumentError::WrongArgument( - "Invalid UDP packet".to_string()).into()); + return Err(ArgumentError::WrongArgument("Invalid UDP packet".to_string()).into()); } } Ok(NaslValue::Null) @@ -1487,7 +1480,7 @@ fn get_udp_element_from_datagram( /// - uh_sum /// - data #[nasl_function] -fn get_udp_element(register: &Register, _configs: &Context) -> Result { +fn get_udp_element(register: &Register) -> Result { let buf = match register.named("udp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -1523,7 +1516,7 @@ fn get_udp_element(register: &Register, _configs: &Context) -> Result Result { +fn forge_icmp_packet(register: &Register) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -1615,7 +1608,7 @@ fn forge_icmp_packet(register: &Register, _configs: &Context) -> Result Result { +fn get_icmp_element(register: &Register) -> Result { let buf = match register.named("icmp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -1801,7 +1794,7 @@ pub mod igmp { /// - type: IGMP type. 0 by default. /// - update_ip_len: If this flag is set, NASL will recompute the size field of the IP datagram. Default: True. #[nasl_function] -fn forge_igmp_packet(register: &Register, _configs: &Context) -> Result { +fn forge_igmp_packet(register: &Register) -> Result { let mut ip_buf = match register.named("ip") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -1929,7 +1922,7 @@ fn nasl_tcp_ping(register: &Register, configs: &Context) -> Result Result Result Result { -// nasl_send_capture(register, configs) +// nasl_send_capture(register, configs) //} /// Read the next packet. @@ -2276,16 +2269,14 @@ fn nasl_send_capture(register: &Register, configs: &Context) -> Result Result { +fn forge_ip_v6_packet(register: &Register, configs: &Context) -> Result { let dst_addr = get_host_ip(configs)?; if dst_addr.is_ipv4() { return Err(FnError::wrong_unnamed_argument( "IPv6", - "forge_ip_v6_packet: No valid dst_addr could be determined via call to get_host_ip()")); + "forge_ip_v6_packet: No valid dst_addr could be determined via call to get_host_ip()", + )); } let data = match register.named("data") { @@ -2297,9 +2288,8 @@ fn forge_ip_v6_packet( let total_length = 40 + data.len(); let mut buf = vec![0; total_length]; - let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_payload_length(data.len() as u16); @@ -2345,9 +2335,8 @@ fn forge_ip_v6_packet( } Err(e) => { return Err( - ArgumentError::WrongArgument(format!( - "Invalid ip_src: {}", e - )).into()); + ArgumentError::WrongArgument(format!("Invalid ip_src: {}", e)).into(), + ); } }; x.to_string() @@ -2362,10 +2351,9 @@ fn forge_ip_v6_packet( pkt.set_destination(ip); } Err(e) => { - return Err(ArgumentError::WrongArgument(format!( - "Invalid ip_dst: {}", - e - )).into()); + return Err( + ArgumentError::WrongArgument(format!("Invalid ip_dst: {}", e)).into(), + ); } }; x.to_string() @@ -2376,10 +2364,7 @@ fn forge_ip_v6_packet( pkt.set_destination(ip); } Err(e) => { - return Err(ArgumentError::WrongArgument(format!( - "Invalid ip: {}", - e - )).into()); + return Err(ArgumentError::WrongArgument(format!("Invalid ip: {}", e)).into()); } }; dst_addr.to_string() @@ -2406,19 +2391,16 @@ fn forge_ip_v6_packet( /// - ip6_src /// - ip6_dst #[nasl_function] -fn get_ip_v6_element( - register: &Register, -) -> Result { +fn get_ip_v6_element(register: &Register) -> Result { let buf = match register.named("ip6") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FnError::missing_argument("ip6").into()); + return Err(FnError::missing_argument("ip6")); } }; - let pkt = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("element") { Some(ContextType::Value(NaslValue::String(e))) => match e.as_str() { @@ -2431,11 +2413,9 @@ fn get_ip_v6_element( ))), "ip6_src" => Ok(NaslValue::String(pkt.get_source().to_string())), "ip6_dst" => Ok(NaslValue::String(pkt.get_destination().to_string())), - _ => Err(ArgumentError::WrongArgument( - "Invalid element".to_string(), - ).into()), + _ => Err(ArgumentError::WrongArgument("Invalid element".to_string()).into()), }, - _ => Err(FnError::missing_argument("element").into()), + _ => Err(FnError::missing_argument("element")), } } @@ -2449,20 +2429,16 @@ fn get_ip_v6_element( /// - ip6_hlim /// - ip6_src #[nasl_function] -fn set_ip_v6_elements( - register: &Register, -) -> Result { +fn set_ip_v6_elements(register: &Register) -> Result { let mut buf = match register.named("ip6") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FnError::missing_argument("ip").into()); + return Err(FnError::missing_argument("ip")); } }; - let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut buf).ok_or_else(|| { - error( - "No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let ip6_plen = match register.named("ip6_plen") { Some(ContextType::Value(NaslValue::Number(x))) => *x as u16, @@ -2482,10 +2458,7 @@ fn set_ip_v6_elements( pkt.set_source(ip); } Err(e) => { - return Err(ArgumentError::WrongArgument(format!( - "Invalid ip_src: {}", - e - )).into()); + return Err(ArgumentError::WrongArgument(format!("Invalid ip_src: {}", e)).into()); } }; }; @@ -2504,9 +2477,7 @@ fn set_ip_v6_elements( /// Return the modified datagram. /// #[nasl_function] -fn insert_ip_v6_options( - register: &Register, -) -> Result { +fn insert_ip_v6_options(register: &Register) -> Result { let buf = match register.named("ip6") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -2574,9 +2545,8 @@ fn insert_ip_v6_options( opt_buf.len(), )?; - let mut new_pkt = MutableIpv6Packet::new(&mut new_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut new_pkt = MutableIpv6Packet::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; new_pkt.set_payload_length((hl / 4) as u16); Ok(NaslValue::Data(new_pkt.packet().to_vec())) @@ -2590,16 +2560,15 @@ fn dump_ip_v6_packet(register: &Register) -> Result { return Err(ArgumentError::MissingPositionals { expected: 1, got: 0, - }.into()); + } + .into()); } for ip in positional.iter() { match ip { NaslValue::Data(data) => { let pkt = packet::ipv6::Ipv6Packet::new(data).ok_or_else(|| { - error( - "No possible to create a packet from buffer".to_string(), - ) + error("No possible to create a packet from buffer".to_string()) })?; println!("------\n"); println!("\tip6_v : {:?}", pkt.get_version()); @@ -2627,15 +2596,12 @@ fn dump_ip_v6_packet(register: &Register) -> Result { ), }; - println!("\tip6_src : {}", pkt.get_source().to_string()); + println!("\tip6_src : {:?}", pkt.get_source().to_string()); println!("\tip6_dst : {:?}", pkt.get_destination().to_string()); display_packet(data); } _ => { - return Err(FnError::wrong_unnamed_argument( - "Data", - "Invalid ip packet", - )); + return Err(FnError::wrong_unnamed_argument("Data", "Invalid ip packet")); } } } @@ -2663,9 +2629,7 @@ fn dump_ip_v6_packet(register: &Register) -> Result { /// /// The modified IP datagram or NULL on error. #[nasl_function] -fn forge_tcp_v6_packet( - register: &Register, -) -> Result { +fn forge_tcp_v6_packet(register: &Register) -> Result { let mut ip_buf = match register.named("ip6") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -2684,9 +2648,8 @@ fn forge_tcp_v6_packet( //tcp length + data length let total_length = 20 + data.len(); let mut buf = vec![0; total_length]; - let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; if !data.is_empty() { tcp_seg.set_payload(&data); @@ -2733,26 +2696,22 @@ fn forge_tcp_v6_packet( let chksum = match register.named("th_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv6::Ipv6Packet::new(&ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let tcp_aux = TcpPacket::new(tcp_seg.packet()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv6::Ipv6Packet::new(&ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp_aux = TcpPacket::new(tcp_seg.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::tcp::ipv6_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) } }; - let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut tcp_seg = packet::tcp::MutableTcpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; tcp_seg.set_checksum(chksum); ip_buf.append(&mut buf); let l = ip_buf.len(); - let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_payload_length(l as u16); match register.named("update_ip_len") { Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { @@ -2783,9 +2742,7 @@ fn forge_tcp_v6_packet( /// /// Returns an TCP element from a IP datagram. #[nasl_function] -fn get_tcp_v6_element( - register: &Register, -) -> Result { +fn get_tcp_v6_element(register: &Register) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -2793,13 +2750,11 @@ fn get_tcp_v6_element( } }; - let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; - let tcp = packet::tcp::TcpPacket::new(ip.payload()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let tcp = packet::tcp::TcpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("element") { Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { @@ -2831,10 +2786,8 @@ fn get_tcp_v6_element( /// - 8: TCPOPT_TIMESTAMP, 8 bytes value for timestamp and echo timestamp, 4 bytes each one. /// /// The returned option depends on the given *option* parameter. It is either an int for option 2, 3 and 4 or an array containing the two values for option 8. -fn get_tcp_v6_option( - register: &Register, - _configs: &Context, -) -> Result { +#[nasl_function] +fn get_tcp_v6_option(register: &Register, _configs: &Context) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -2842,12 +2795,10 @@ fn get_tcp_v6_option( } }; - let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let tcp = packet::tcp::TcpPacket::new(ip.payload()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp = packet::tcp::TcpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let mut max_seg: i64 = 0; let mut window: i64 = 0; @@ -2889,9 +2840,7 @@ fn get_tcp_v6_option( 3 => Ok(NaslValue::Number(window)), 4 => Ok(NaslValue::Number(sack_permitted)), 8 => Ok(NaslValue::Array(timestamps)), - _ => Err(ArgumentError::WrongArgument( - "Invalid option".to_string(), - ).into()), + _ => Err(ArgumentError::WrongArgument("Invalid option".to_string()).into()), }, _ => Err(FnError::missing_argument("option")), } @@ -2912,10 +2861,8 @@ fn get_tcp_v6_option( /// - th_win: is the TCP window size. NASL will convert it into network order if necessary. 0 by default. /// - th_x2: is a reserved field and should probably be left unchanged. 0 by default. /// - update_ip_len: is a flag (TRUE by default). If set, NASL will recompute the size field of the IP datagram. -fn set_tcp_v6_elements( - register: &Register, - _configs: &Context, -) -> Result { +#[nasl_function] +fn set_tcp_v6_elements(register: &Register, _configs: &Context) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -2923,9 +2870,8 @@ fn set_tcp_v6_elements( } }; - let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let iph_len = ip.get_payload_length() as usize * 4; // the header length is given in 32-bits words let data = match register.named("data") { @@ -2946,9 +2892,8 @@ fn set_tcp_v6_elements( new_buf = vec![0u8; tcp_total_length]; //new_buf[..20].copy_from_slice(&ori_tcp_buf[..20]); safe_copy_from_slice(&mut new_buf[..], 0, 20, &ori_tcp_buf, 0, 20)?; - ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; ori_tcp.set_payload(&data); } else { // Copy the original tcp buffer into the new buffer @@ -2963,9 +2908,8 @@ fn set_tcp_v6_elements( 0, ori_tcp_buf.len(), )?; - ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; } if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("th_sport") { @@ -3001,12 +2945,10 @@ fn set_tcp_v6_elements( let chksum = match register.named("th_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let tcp_aux = TcpPacket::new(ori_tcp.packet()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp_aux = TcpPacket::new(ori_tcp.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::tcp::ipv4_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) } }; @@ -3025,9 +2967,8 @@ fn set_tcp_v6_elements( new_ip_buf.append(&mut fin_tcp_buf.to_vec()); let l = new_ip_buf.len(); - let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut new_ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut new_ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; // pnet will panic if the total length set in the ip datagram field does not much with the total length. // Therefore, the total length is set to the right one before setting the payload. @@ -3053,22 +2994,17 @@ fn set_tcp_v6_elements( /// - 3: TCPOPT_WINDOW, with values between 0 and 14 /// - 4: TCPOPT_SACK_PERMITTED, no value required. /// - 8: TCPOPT_TIMESTAMP, 8 bytes value for timestamp and echo timestamp, 4 bytes each one. -fn insert_tcp_v6_options( - register: &Register, - _configs: &Context, -) -> Result { +#[nasl_function] +fn insert_tcp_v6_options(register: &Register, _configs: &Context) -> Result { let buf = match register.named("tcp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(error( - "insert_tcp_options: missing field".to_string(), - )); + return Err(error("insert_tcp_options: missing field".to_string())); } }; - let ip = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let iph_len = ip.get_header_length() as usize * 4; // the header length is given in 32-bits words let ori_tcp_buf = <&[u8]>::clone(&ip.payload()).to_owned(); let mut ori_tcp: packet::tcp::MutableTcpPacket; @@ -3079,9 +3015,8 @@ fn insert_tcp_v6_options( Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), _ => { - let tcp = TcpPacket::new(&ori_tcp_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let tcp = TcpPacket::new(&ori_tcp_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; tcp.payload().to_vec() } }; @@ -3107,7 +3042,8 @@ fn insert_tcp_v6_options( } else { return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_MAXSEG".to_string(), - ).into()); + ) + .into()); } } Some(NaslValue::Number(o)) if *o == 3 => { @@ -3118,7 +3054,8 @@ fn insert_tcp_v6_options( } else { return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_WINDOW".to_string(), - ).into()); + ) + .into()); } } @@ -3136,19 +3073,22 @@ fn insert_tcp_v6_options( } else { return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_TIMESTAMP".to_string(), - ).into()); + ) + .into()); } } else { return Err(ArgumentError::WrongArgument( "Invalid value for tcp option TCPOPT_TIMESTAMP".to_string(), - ).into()); + ) + .into()); } } None => break, _ => { return Err(ArgumentError::WrongArgument( "insert_tcp_options: invalid tcp option".to_string(), - ).into()); + ) + .into()); } } } @@ -3173,9 +3113,8 @@ fn insert_tcp_v6_options( //new_buf[..20].copy_from_slice(&ori_tcp_buf[..20]); safe_copy_from_slice(&mut new_buf[..], 0, 20, &ori_tcp_buf, 0, 20)?; - ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + ori_tcp = packet::tcp::MutableTcpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; // At this point, opts len is a 4bytes multiple and the ofset is expressed in 32bits words ori_tcp.set_data_offset(5 + opts_len as u8 / 4); @@ -3190,12 +3129,10 @@ fn insert_tcp_v6_options( let chksum = match register.named("th_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv4::Ipv4Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let tcp_aux = TcpPacket::new(ori_tcp.packet()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv4::Ipv4Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let tcp_aux = TcpPacket::new(ori_tcp.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::tcp::ipv4_checksum(&tcp_aux, &pkt.get_source(), &pkt.get_destination()) } }; @@ -3213,9 +3150,8 @@ fn insert_tcp_v6_options( new_ip_buf.append(&mut fin_tcp_buf.to_vec()); let l = new_ip_buf.len(); - let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv4::MutableIpv4Packet::new(&mut new_ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; // pnet will panic if the total length set in the ip datagram field does not much with the total length. // Therefore, the total length is set to the right one before setting the payload. @@ -3238,7 +3174,8 @@ fn insert_tcp_v6_options( } /// Receive a list of IPv6 datagrams and print their TCP part in a readable format in the screen. -fn dump_tcp_v6_packet(register: &Register, _: &Context) -> Result { +#[nasl_function] +fn dump_tcp_v6_packet(register: &Register) -> Result { let positional = register.positional(); if positional.is_empty() { return Err(error( @@ -3264,10 +3201,7 @@ fn dump_tcp_v6_packet(register: &Register, _: &Context) -> Result { - return Err(FnError::wrong_unnamed_argument( - "Data", - "Invalid ip packet", - )); + return Err(FnError::wrong_unnamed_argument("Data", "Invalid ip packet")); } } } @@ -3287,7 +3221,8 @@ fn dump_tcp_v6_packet(register: &Register, _: &Context) -> Result Result { +#[nasl_function] +fn forge_udp_v6_packet(register: &Register) -> Result { let mut ip_buf = match register.named("ip6") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => return Err(FnError::missing_argument("ip6")), @@ -3304,9 +3239,8 @@ fn forge_udp_v6_packet(register: &Register, _: &Context) -> Result Result (*x as u16).to_be(), _ => { - let pkt = packet::ipv6::Ipv6Packet::new(&ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let udp_aux = UdpPacket::new(udp_datagram.packet()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv6::Ipv6Packet::new(&ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let udp_aux = UdpPacket::new(udp_datagram.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::udp::ipv6_checksum(&udp_aux, &pkt.get_source(), &pkt.get_destination()) } }; - let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut udp_datagram = packet::udp::MutableUdpPacket::new(&mut buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; udp_datagram.set_checksum(chksum); ip_buf.append(&mut buf); let l = ip_buf.len(); - let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_payload_length(l as u16); match register.named("update_ip_len") { Some(ContextType::Value(NaslValue::Boolean(l))) if !(*l) => { @@ -3370,10 +3300,8 @@ fn forge_udp_v6_packet(register: &Register, _: &Context) -> Result Result { +#[nasl_function] +fn get_udp_v6_element(register: &Register, _configs: &Context) -> Result { let buf = match register.named("udp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -3381,12 +3309,10 @@ fn get_udp_v6_element( } }; - let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let udp = packet::udp::UdpPacket::new(ip.payload()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let udp = packet::udp::UdpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; get_udp_element_from_datagram(udp, register) } @@ -3399,10 +3325,8 @@ fn get_udp_v6_element( /// - uh_sport: is the source port. NASL will convert it into network order if necessary. 0 by default. /// - uh_sum: is the UDP checksum. Although it is not compulsory, the right value is computed by default. /// - uh_ulen: is the data length. By default it is set to the length the data argument plus the size of the UDP header. -fn set_udp_v6_elements( - register: &Register, - _configs: &Context, -) -> Result { +#[nasl_function] +fn set_udp_v6_elements(register: &Register) -> Result { let buf = match register.named("udp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -3410,9 +3334,8 @@ fn set_udp_v6_elements( } }; - let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let iph_len = ip.get_payload_length() as usize * 4; // the header length is given in 32-bits words let data = match register.named("data") { @@ -3434,9 +3357,8 @@ fn set_udp_v6_elements( //new_buf[..8].copy_from_slice(&ori_udp_buf[..8]); safe_copy_from_slice(&mut new_buf[..], 0, 8, &ori_udp_buf, 0, 8)?; - ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; ori_udp.set_payload(&data); } else { // Copy the original udp buffer into the new buffer @@ -3451,9 +3373,8 @@ fn set_udp_v6_elements( 0, ori_udp_buf.len(), )?; - ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + ori_udp = packet::udp::MutableUdpPacket::new(&mut new_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; } if let Some(ContextType::Value(NaslValue::Number(x))) = register.named("uh_sport") { @@ -3471,12 +3392,10 @@ fn set_udp_v6_elements( let chksum = match register.named("uh_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let udp_aux = UdpPacket::new(ori_udp.packet()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let pkt = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let udp_aux = UdpPacket::new(ori_udp.packet()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pnet::packet::udp::ipv6_checksum(&udp_aux, &pkt.get_source(), &pkt.get_destination()) } }; @@ -3494,9 +3413,8 @@ fn set_udp_v6_elements( new_ip_buf.append(&mut fin_udp_buf.to_vec()); let l = new_ip_buf.len(); - let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut new_ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut new_ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_payload_length(l as u16); pkt.set_payload(&fin_udp_buf); @@ -3505,7 +3423,8 @@ fn set_udp_v6_elements( } /// Receive a list of IPv4 datagrams and print their UDP part in a readable format in the screen. -fn dump_udp_v6_packet(register: &Register, _: &Context) -> Result { +#[nasl_function] +fn dump_udp_v6_packet(register: &Register) -> Result { let positional = register.positional(); if positional.is_empty() { return Err(error( @@ -3519,9 +3438,9 @@ fn dump_udp_v6_packet(register: &Register, _: &Context) -> Result ip, None => { - return Err(ArgumentError::WrongArgument( - "Invalid UDP packet".to_string(), - ).into()); + return Err( + ArgumentError::WrongArgument("Invalid UDP packet".to_string()).into(), + ); } }; @@ -3529,9 +3448,7 @@ fn dump_udp_v6_packet(register: &Register, _: &Context) -> Result { - return Err(ArgumentError::WrongArgument( - "Invalid UDP packet".to_string(), - ).into()); + return Err(ArgumentError::WrongArgument("Invalid UDP packet".to_string()).into()); } } } @@ -3555,27 +3472,24 @@ fn dump_udp_v6_packet(register: &Register, _: &Context) -> Result Result { +fn forge_icmp_v6_packet(register: &Register) -> Result { let mut ip_buf = match register.named("ip6") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { - return Err(FnError::missing_argument("icmp6")); + return Err(FnError::missing_argument("ip6")); } }; let original_ip_len = ip_buf.len(); // to extract the max hop limit, ip6_dst. - let pkt_aux = packet::ipv6::Ipv6Packet::new(&ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let pkt_aux = packet::ipv6::Ipv6Packet::new(&ip_buf) + .ok_or_else(|| error("No possible to create a ipv6 packet from buffer".to_string()))?; - let data = match register.named("data") { + let data: Vec = match register.named("data") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), Some(ContextType::Value(NaslValue::String(d))) => d.as_bytes().to_vec(), Some(ContextType::Value(NaslValue::Number(d))) => d.to_be_bytes().to_vec(), - _ => Vec::::new(), + _ => vec![], }; let icmp_code = match register.named("icmp_code") { @@ -3590,29 +3504,24 @@ fn forge_icmp_v6_packet( let icmp_type = match register.named("icmp_type") { Some(ContextType::Value(NaslValue::Number(x))) => { if *x < 0 || *x > 255 { - return Err(error(format!( - "forge_icmp_v6_packet: illegal type {}", - x - ))); + return Err(error(format!("forge_icmp_v6_packet: illegal type {}", x))); } packet::icmpv6::Icmpv6Type::new(*x as u8) } _ => { - return Err(error( - "forge_icmp_v6_packet: illegal type".to_string(), - )); + return Err(error("forge_icmp_v6_packet: illegal type".to_string())); } }; match icmp_type { Icmpv6Types::EchoRequest => { - icmp_pkt_size = 8; - total_length = 8 + data.len(); + icmp_pkt_size = MutableEchoRequestPacket::minimum_packet_size(); + total_length = icmp_pkt_size + data.len(); icmp_buf = vec![0; total_length]; let mut icmp_pkt = packet::icmpv6::MutableIcmpv6Packet::new(&mut icmp_buf).ok_or_else(|| { error( - "No possible to create a packet from buffer".to_string(), + "EchoRequest: No possible to create an icmp packet from buffer".to_string(), ) })?; @@ -3635,14 +3544,12 @@ fn forge_icmp_v6_packet( } } Icmpv6Types::RouterSolicit => { - icmp_pkt_size = 8; + icmp_pkt_size = MutableRouterSolicitPacket::minimum_packet_size(); total_length = icmp_pkt_size + data.len(); icmp_buf = vec![0; total_length]; let mut icmp_pkt = packet::icmpv6::ndp::MutableRouterSolicitPacket::new(&mut icmp_buf) .ok_or_else(|| { - error( - "No possible to create a packet from buffer".to_string(), - ) + error("RouterSolicit: No possible to create a packet from buffer".to_string()) })?; icmp_pkt.set_icmpv6_type(icmp_type); @@ -3658,7 +3565,7 @@ fn forge_icmp_v6_packet( total_length = icmp_pkt_size + data.len(); icmp_buf = vec![0; total_length]; let mut icmp_pkt = MutableRouterAdvertPacket::new(&mut icmp_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) + error("RouterAdvert: No possible to create a packet from buffer".to_string()) })?; icmp_pkt.set_icmpv6_type(icmp_type); @@ -3697,9 +3604,7 @@ fn forge_icmp_v6_packet( icmp_buf = vec![0; total_length]; let mut icmp_pkt = MutableNeighborSolicitPacket::new(&mut icmp_buf).ok_or_else(|| { - error( - "No possible to create a packet from buffer".to_string(), - ) + error("NeighborSolicit: No possible to create a packet from buffer".to_string()) })?; icmp_pkt.set_icmpv6_type(icmp_type); @@ -3724,9 +3629,7 @@ fn forge_icmp_v6_packet( icmp_buf = vec![0; total_length]; let mut icmp_pkt = MutableNeighborAdvertPacket::new(&mut icmp_buf).ok_or_else(|| { - error( - "No possible to create a packet from buffer".to_string(), - ) + error("NeighborAdvert: No possible to create a packet from buffer".to_string()) })?; icmp_pkt.set_icmpv6_type(icmp_type); @@ -3755,9 +3658,7 @@ fn forge_icmp_v6_packet( icmp_buf = vec![0; total_length]; let mut icmp_pkt = packet::icmpv6::MutableIcmpv6Packet::new(&mut icmp_buf).ok_or_else(|| { - error( - "No possible to create a packet from buffer".to_string(), - ) + error("No possible to create a icmp packet from buffer".to_string()) })?; icmp_pkt.set_icmpv6_type(icmp_type); @@ -3787,27 +3688,40 @@ fn forge_icmp_v6_packet( )?; } - ip_buf.append(&mut icmp_buf); - let l = ip_buf.len(); - let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - - let chksum = match register.named("icmp_cksum") { - Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), - _ => { - let icmp_aux = Icmpv6Packet::new(&icmp_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - pnet::packet::icmpv6::checksum(&icmp_aux, &pkt.get_source(), &pkt.get_destination()) - } - }; + let chksum: u16; + { + let mut ip_buf_aux = ip_buf.clone(); + ip_buf_aux.append(&mut icmp_buf.clone()); + chksum = match register.named("icmp_cksum") { + Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), + _ => { + let pkt = packet::ipv6::Ipv6Packet::new(&ip_buf_aux).ok_or_else(|| { + error("No possible to create a packet from buffer".to_string()) + })?; + let icmp_aux = packet::icmpv6::Icmpv6Packet::new(&icmp_buf).ok_or_else(|| { + error( + "No possible to create a packet from buffer for chksum calculation" + .to_string(), + ) + })?; + pnet::packet::icmpv6::checksum(&icmp_aux, &pkt.get_source(), &pkt.get_destination()) + } + }; + } + dbg!(&chksum); let mut icmp_pkt = packet::icmpv6::MutableIcmpv6Packet::new(&mut icmp_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) + error( + "No possible to create a packet from buffer while setting the checksum".to_string(), + ) })?; + icmp_pkt.set_checksum(chksum); + ip_buf.append(&mut icmp_buf.clone()); + let l = icmp_buf.len(); + let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; pkt.set_payload_length(l as u16); match register.named("update_ip_len") { @@ -3832,9 +3746,7 @@ fn forge_icmp_v6_packet( /// - icmp_chsum /// - icmp_data #[nasl_function] -fn get_icmp_v6_element( - register: &Register, -) -> Result { +fn get_icmp_v6_element(register: &Register) -> Result { let buf = match register.named("icmp") { Some(ContextType::Value(NaslValue::Data(d))) => d.clone(), _ => { @@ -3842,12 +3754,10 @@ fn get_icmp_v6_element( } }; - let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let icmp = packet::icmpv6::Icmpv6Packet::new(ip.payload()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let icmp = packet::icmpv6::Icmpv6Packet::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; match register.named("element") { Some(ContextType::Value(NaslValue::String(el))) => match el.as_str() { @@ -3906,12 +3816,10 @@ fn dump_icmp_v6_packet(register: &Register) -> Result { } }; - let ip = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; - let icmp = packet::icmp::IcmpPacket::new(ip.payload()).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let ip = packet::ipv6::Ipv6Packet::new(&buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let icmp = packet::icmp::IcmpPacket::new(ip.payload()) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; let mut icmp_seq = 0; if icmp.payload().len() >= 4 { @@ -3950,8 +3858,7 @@ fn dump_icmp_v6_packet(register: &Register) -> Result { } #[nasl_function] -fn forge_igmp_v6_packet( -) -> Result { +fn forge_igmp_v6_packet() -> Result { // TODO: not implemented. Multicast management on IPv6 networks is handled by Multicast // Listener Discovery (MLD) which is a part of ICMPv6 in contrast to IGMP's bare IP encapsulation. // Currently, pnet does not support MDL. @@ -3963,10 +3870,7 @@ fn forge_igmp_v6_packet( /// Its argument is: /// - port: port for the ping #[nasl_function] -fn nasl_tcp_v6_ping( - register: &Register, - configs: &Context -) -> Result { +fn nasl_tcp_v6_ping(register: &Register, configs: &Context) -> Result { let rnd_tcp_port = || -> u16 { (random_impl().unwrap_or(0) % 65535 + 1024) as u16 }; let sports_ori: Vec = vec![ @@ -3987,11 +3891,8 @@ fn nasl_tcp_v6_ping( } let soc = new_raw_ipv6_socket()?; - if let Err(e) = soc.set_header_included(true) { - return Err(error(format!( - "Not possible to create a raw socket: {}", - e - )).into()); + if let Err(e) = soc.set_header_included_v4(true) { + return Err(error(format!("Not possible to create a raw socket: {}", e))); }; // Get the iface name, to set the capture device. @@ -4002,11 +3903,7 @@ fn nasl_tcp_v6_ping( let port = match register.named("port") { Some(ContextType::Value(NaslValue::Number(x))) => *x, None => 0, //TODO: implement plug_get_host_open_port() - _ => { - return Err(ArgumentError::WrongArgument( - "Invalid length value".to_string(), - ).into()) - } + _ => return Err(ArgumentError::WrongArgument("Invalid length value".to_string()).into()), }; if islocalhost(target_ip) { @@ -4023,9 +3920,8 @@ fn nasl_tcp_v6_ping( let filter = format!("ip and src host {}", target_ip); let mut ip_buf = [0u8; 40]; - let mut ip = MutableIpv6Packet::new(&mut ip_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut ip = MutableIpv6Packet::new(&mut ip_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; ip.set_flow_label(0); ip.set_traffic_class(0); @@ -4041,9 +3937,8 @@ fn nasl_tcp_v6_ping( ip.set_destination(ipv6_dst); let mut tcp_buf = [0u8; 20]; - let mut tcp = MutableTcpPacket::new(&mut tcp_buf).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + let mut tcp = MutableTcpPacket::new(&mut tcp_buf) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; tcp.set_flags(0x02); //TH_SYN tcp.set_sequence(random_impl()? as u32); tcp.set_acknowledgement(0); @@ -4103,10 +3998,7 @@ fn nasl_tcp_v6_ping( /// - pcap_timeout: time to wait for the answers in seconds, 5 by default /// - allow_broadcast: default FALSE #[nasl_function] -fn nasl_send_v6packet( - register: &Register, - configs: &Context, -) -> Result { +fn nasl_send_v6packet(register: &Register, configs: &Context) -> Result { let use_pcap = match register.named("pcap_active") { Some(ContextType::Value(NaslValue::Boolean(x))) => *x, None => true, @@ -4115,7 +4007,8 @@ fn nasl_send_v6packet( "pcap_active", "Boolean", "Invalid pcap_active value", - ).into()) + ) + .into()) } }; @@ -4127,7 +4020,8 @@ fn nasl_send_v6packet( "pcap_filter", "String", "Invalid pcap_filter value", - ).into()) + ) + .into()) } }; @@ -4139,7 +4033,8 @@ fn nasl_send_v6packet( "pcap_timeout", "Number", "Invalid timeout value", - ).into()) + ) + .into()) } }; @@ -4154,29 +4049,23 @@ fn nasl_send_v6packet( return Err(error(format!( "send_v6packet: Not possible to create a raw socket: {}", e - )).into()); + ))); }; let _dflt_packet_sz = match register.named("length") { Some(ContextType::Value(NaslValue::Number(x))) => *x, None => 0, _ => { - return Err(ArgumentError::wrong_argument( - "length", - "Number", - "Invalid length value", - ).into()) + return Err( + ArgumentError::wrong_argument("length", "Number", "Invalid length value").into(), + ) } }; // Get the iface name, to set the capture device. let target_ip = get_host_ip(configs)?; - print!("\nAAAAAAAAAAA target_ip {:?}\n", target_ip); - let local_ip = get_source_ip(target_ip, 50000u16)?; - println!("\nAAAAAAAAAAA local_ip {:?}\n", local_ip); let iface = get_interface_by_local_ip(local_ip)?; - println!("\nAAAAAAAAAAA iface {:?}\n", iface); let mut capture_dev = match Capture::from_device(iface) { Ok(c) => match c.promisc(true).timeout(timeout).open() { @@ -4186,21 +4075,14 @@ fn nasl_send_v6packet( Err(e) => return custom_error!("send_packet: {}", e), }; - print!("\nAAAAAAAAAAA antes de pkt\n"); for pkt in positional.iter() { - print!("AAAAAAAAAAA 1"); let packet_raw = match pkt { NaslValue::Data(data) => data as &[u8], - _ => { - return Err(FnError::wrong_unnamed_argument( - "Data", - "Invalid packet", - )) - } + _ => return Err(FnError::wrong_unnamed_argument("Data", "Invalid packet")), }; - let packet = packet::ipv6::Ipv6Packet::new(packet_raw).ok_or_else(|| { - error("No possible to create a packet from buffer".to_string()) - })?; + + let packet = packet::ipv6::Ipv6Packet::new(packet_raw) + .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; // No broadcast destination and dst ip address inside the IP packet // differs from target IP, is consider a malicious or buggy script. @@ -4211,13 +4093,12 @@ fn nasl_send_v6packet( )); } - let sock_str = format!("{}:{}", &packet.get_destination().to_string().as_str(), 0); + let sock_str = format!("[{}]:{}", &packet.get_destination().to_string().as_str(), 0); + let sockaddr = match SocketAddr::from_str(&sock_str) { Ok(addr) => socket2::SockAddr::from(addr), Err(e) => { - return Err(error( - format!("send_packet: {}", e) - ).into()); + return Err(error(format!("send_packet: {}", e))); } }; @@ -4226,9 +4107,7 @@ fn nasl_send_v6packet( debug!("Sent {} bytes", b); } Err(e) => { - return Err(error( - format!("send_packet: {}", e) - ).into()); + return Err(error(format!("send_packet: {}", e))); } } diff --git a/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs b/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs index 94e67ae83..df38e31f7 100644 --- a/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs +++ b/rust/src/nasl/builtin/raw_ip/raw_ip_utils.rs @@ -74,7 +74,7 @@ pub fn get_interface_by_local_ip(local_address: IpAddr) -> Result Result { match dst { SocketAddr::V4(_) => UdpSocket::bind("0.0.0.0:0"), - SocketAddr::V6(_) => UdpSocket::bind(" 0:0:0:0:0:0:0:0:0"), + SocketAddr::V6(_) => UdpSocket::bind("[0:0:0:0:0:0:0:0]:0"), } .map_err(RawIpError::FailedToBind) } diff --git a/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs b/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs index cf3a20b53..7ac9f2952 100644 --- a/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs +++ b/rust/src/nasl/builtin/raw_ip/tests/packet_forgery.rs @@ -297,4 +297,31 @@ mod tests { let _r = safe_copy_from_slice(&mut a, 0, 2, &b, 0, 2); assert_eq!(a, [b'a', b'b', 3u8, 4u8]); } + + #[test] + fn forge_icmp_v6_packet() { + let mut t = setup(); + t.ok( + r#"ip6_packet = forge_ip_v6_packet( ip6_v: 6, # IP6_v, + ip6_p: 0x3a, #ICMPv6 + ip6_plen:40, + ip6_hlim:IP6_HLIM, + ip6_src: "5858::1", + ip6_dst: "5858::2");"#, + vec![6, 0, 0, 58, 40, 58, 58, 0, 0, 0, 0, 0, 1, 58,58, 0, 0, 0, 0, 0, 2], + ); + t.ok( + r#"icmp = forge_icmp_v6_packet( ip6:ip6_packet, + icmp_type:128, + icmp_code:1, + icmp_seq:2, + icmp_id: 1, + icmp_cksum: 0 + );"#, + vec![ + 6, 0, 0, 58, 40, 58, 58, 0, 0, 0, 0, 0, 1, 58, 58, 0, 0, 0, 0, 0, 2, + 128, 1, 64, 38, 140, 227, 2, 0 + ], + ); + } } From ee83e30c9647ff47e9d77d77cab6a194853f09e1 Mon Sep 17 00:00:00 2001 From: Juan Jose Nicola Date: Tue, 17 Dec 2024 12:11:51 -0300 Subject: [PATCH 3/3] fix udp v6 forgery and add examples --- rust/examples/forge_tcp_v6.nasl | 2 +- rust/examples/packet_forgery_udp_v6.nasl | 31 +++++++++++++++++++ .../src/nasl/builtin/raw_ip/packet_forgery.rs | 26 ++++++++++------ 3 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 rust/examples/packet_forgery_udp_v6.nasl diff --git a/rust/examples/forge_tcp_v6.nasl b/rust/examples/forge_tcp_v6.nasl index 1f7a942bc..576281c15 100644 --- a/rust/examples/forge_tcp_v6.nasl +++ b/rust/examples/forge_tcp_v6.nasl @@ -7,7 +7,7 @@ # sudo openvas-nasl -X -d -i $PLUGINSPATH ~/my_nasl/forge_tcp_v6.nasl -t 5858::2 # sudo target/debug/scannerctl execute script ~/my_nasl/forge_tcp_v6.nasl -t 5858::2 # -# Set the correct IPv6 addresses and routes in the origin and destination hosts with the right address on each. +# Set the correct IPv6 addresses and routes in the orgin and destination hosts with the right address on each. # sudo ip addr add 5858::1/64 dev wlp6s0 # sudo ip -6 route add 5858::1 dev wlp6s0 diff --git a/rust/examples/packet_forgery_udp_v6.nasl b/rust/examples/packet_forgery_udp_v6.nasl new file mode 100644 index 000000000..7e6a6f212 --- /dev/null +++ b/rust/examples/packet_forgery_udp_v6.nasl @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2023 Greenbone AG +# +# SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception + +IP6_HLIM = 128; + +src = "5858::1"; +dst = "5858::2"; + +ip6 = forge_ip_v6_packet( ip6_v: 6, # IP6_v, + ip6_p: IPPROTO_UDP, #0x11 + ip6_plen:40, + ip6_hlim:IP6_HLIM, + ip6_src: src, + ip6_dst: dst); + +dump_ip_v6_packet (ip6); + +udp6_packet = forge_udp_v6_packet(ip: ip6, + uh_sport: 5080, + uh_dport: 80, + uh_len: 12, + th_sum: 0, + data: "1234"); +display(get_udp_v6_element(udp:udp6_packet, element:"uh_sport")); +udp6_packet = set_udp_v6_elements(udp: udp6_packet, uh_sport: 33000); +display(get_udp_v6_element(udp:udp6_packet, element:"uh_sport")); + +dump_ip_v6_packet (udp6_packet); + +send_v6packet(udp6_packet); diff --git a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs index 11706dab1..0d5cc278c 100644 --- a/rust/src/nasl/builtin/raw_ip/packet_forgery.rs +++ b/rust/src/nasl/builtin/raw_ip/packet_forgery.rs @@ -3116,7 +3116,7 @@ fn insert_tcp_v6_options(register: &Register, _configs: &Context) -> Result Result { let chksum = match register.named("uh_sum") { Some(ContextType::Value(NaslValue::Number(x))) if *x != 0 => (*x as u16).to_be(), _ => { - let pkt = packet::ipv6::Ipv6Packet::new(&buf) - .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; - let udp_aux = UdpPacket::new(ori_udp.packet()) - .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; + let pkt = packet::ipv6::Ipv6Packet::new(&buf).ok_or_else(|| { + error("No possible to create an IPv6 segment from buffer".to_string()) + })?; + let udp_aux = UdpPacket::new(ori_udp.packet()).ok_or_else(|| { + error("No possible to create an UDP datagram from buffer".to_string()) + })?; pnet::packet::udp::ipv6_checksum(&udp_aux, &pkt.get_source(), &pkt.get_destination()) } }; ori_udp.set_checksum(chksum); - // Create a owned copy of the final udp segment, which will be appended as payload to the IP packet. let mut fin_udp_buf: Vec = vec![0u8; udp_total_length]; let buf_aux = <&[u8]>::clone(&ori_udp.packet()).to_owned(); @@ -3409,13 +3410,18 @@ fn set_udp_v6_elements(register: &Register) -> Result { // Create a new IP packet with the original IP header, and the new UDP payload let mut new_ip_buf = vec![0u8; iph_len]; //new_ip_buf[..].copy_from_slice(&buf[..iph_len]); - safe_copy_from_slice(&mut new_ip_buf[..], 0, iph_len, &buf, 0, iph_len)?; - new_ip_buf.append(&mut fin_udp_buf.to_vec()); + safe_copy_from_slice( + &mut new_ip_buf[..], + 0, + buf.len() - 1, + &buf, + 0, + buf.len() - 1, + )?; - let l = new_ip_buf.len(); + let l = fin_udp_buf.len(); let mut pkt = packet::ipv6::MutableIpv6Packet::new(&mut new_ip_buf) .ok_or_else(|| error("No possible to create a packet from buffer".to_string()))?; - pkt.set_payload_length(l as u16); pkt.set_payload(&fin_udp_buf);