diff --git a/src/safeposix/syscalls/net_calls.rs b/src/safeposix/syscalls/net_calls.rs index 22ebf9af..2b9eb236 100644 --- a/src/safeposix/syscalls/net_calls.rs +++ b/src/safeposix/syscalls/net_calls.rs @@ -3363,14 +3363,48 @@ impl Cage { } } + /// ## ------------------GETPEERNAME SYSCALL------------------ + /// ### Description + /// The `getpeername_syscall()` returns the address of the peer connected to + /// the socket fd, in the buffer pointed to by ret_addr + /// + /// ### Function Arguments + /// The `getpeername_syscall()` receives two arguments: + /// * `fd` - The file descriptor of the socket + /// * `ret_addr` - A buffer of GenSockaddr type to store the return value + /// + /// ### Returns + /// On success, zero is returned. Otherwise, errors or panics are returned + /// for different scenarios. + /// + /// ### Errors + /// * EBADF - The argument fd is not a valid file descriptor. + /// * ENOTSOCK - The file descriptor sockfd does not refer to a socket. + /// * ENOTCONN - The socket is not connected. + /// + /// ### Panics + /// No Panic is expected from this syscall. + /// + /// more details at https://man7.org/linux/man-pages/man2/getpeername.2.html pub fn getpeername_syscall(&self, fd: i32, ret_addr: &mut interface::GenSockaddr) -> i32 { + // first let's check the fd range + if fd < 0 || fd >= MAXFD { + return syscall_error( + Errno::EBADF, + "getpeername", + "the provided file descriptor is not valid", + ); + } + + // get the file descriptor object let checkedfd = self.get_filedescriptor(fd).unwrap(); let unlocked_fd = checkedfd.read(); if let Some(filedesc_enum) = &*unlocked_fd { if let Socket(sockfdobj) = filedesc_enum { - //if the socket is not connected, then we should return an error + // get the read lock of sockhandle let sock_tmp = sockfdobj.handle.clone(); let sockhandle = sock_tmp.read(); + // if the socket is not connected, then we should return an error if sockhandle.remoteaddr == None { return syscall_error( Errno::ENOTCONN, @@ -3378,9 +3412,12 @@ impl Cage { "the socket is not connected", ); } + // remoteaddr stores the value we want so we just return the remoteaddr stored + // in sockhandle *ret_addr = sockhandle.remoteaddr.unwrap(); return 0; } else { + // if the fd is not socket object return syscall_error( Errno::ENOTSOCK, "getpeername", @@ -3388,6 +3425,7 @@ impl Cage { ); } } else { + // if the fd is not valid return syscall_error( Errno::EBADF, "getpeername", @@ -3396,15 +3434,56 @@ impl Cage { } } + /// ## ------------------GETSOCKNAME SYSCALL------------------ + /// ### Description + /// The `getsockname_syscall()` returns the current address to which the + /// socket fd is bound, in the buffer pointed to by ret_addr. If the socket + /// hasn't bound to any address, it returns an empty address. + /// + /// ### Function Arguments + /// The `getsockname_syscall()` receives two arguments: + /// * `fd` - The file descriptor of the socket + /// * `ret_addr` - A buffer of GenSockaddr type to store the return value + /// + /// ### Returns + /// On success, zero is returned. Otherwise, errors or panics are returned + /// for different scenarios. + /// + /// ### Errors + /// * EBADF - The argument fd is not a valid file descriptor. + /// * ENOTSOCK - The file descriptor sockfd does not refer to a socket. + /// + /// ### Panics + /// No Panic is expected from this syscall. + /// + /// more details at https://man7.org/linux/man-pages/man2/getsockname.2.html pub fn getsockname_syscall(&self, fd: i32, ret_addr: &mut interface::GenSockaddr) -> i32 { + // first let's check the fd range + if fd < 0 || fd >= MAXFD { + return syscall_error( + Errno::EBADF, + "getsockname", + "the provided file descriptor is not valid", + ); + } + + // get the file descriptor object let checkedfd = self.get_filedescriptor(fd).unwrap(); let unlocked_fd = checkedfd.read(); if let Some(filedesc_enum) = &*unlocked_fd { if let Socket(sockfdobj) = filedesc_enum { + // must be a socket file descriptor + + // get the read lock of socket handler let sock_tmp = sockfdobj.handle.clone(); let sockhandle = sock_tmp.read(); + // each socket type has different structure + // so we must handle them seperately if sockhandle.domain == AF_UNIX { + // in case of AF_UNIX socket if sockhandle.localaddr == None { + // if hasn't bound to any address, + // return an empty address let null_path: &[u8] = &[]; *ret_addr = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( sockhandle.domain as u16, @@ -3412,13 +3491,19 @@ impl Cage { )); return 0; } - //if the socket is not none, then return the socket + // if the socket address is not none, then return the socket address *ret_addr = sockhandle.localaddr.unwrap(); return 0; } else { + // in case of AF_INET/AF_INET6 if sockhandle.localaddr == None { - //sets the address to 0.0.0.0 if the address is not initialized yet - //setting the family as well based on the domain + // if the socket hasn't bound to any address, we'd return an empty address + // with both ip and port set to 0. But family should be set since it is + // something that was already specified when the socket was created + + // for ipv4, set the address to 0.0.0.0 to indicate uninitialized address + // for ipv6, set the address to 0:0:0:0:0:0:0:0 + // (::) to indicate uninitialized address let addr = match sockhandle.domain { AF_INET => interface::GenIpaddr::V4(interface::V4Addr::default()), AF_INET6 => interface::GenIpaddr::V6(interface::V6Addr::default()), @@ -3428,13 +3513,16 @@ impl Cage { }; ret_addr.set_addr(addr); ret_addr.set_port(0); + // set the family ret_addr.set_family(sockhandle.domain as u16); return 0; } + // if the socket address is not none, then return the socket address *ret_addr = sockhandle.localaddr.unwrap(); return 0; } } else { + // the fd is not a socket return syscall_error( Errno::ENOTSOCK, "getsockname", @@ -3442,6 +3530,7 @@ impl Cage { ); } } else { + // invalid fd return syscall_error( Errno::EBADF, "getsockname", @@ -3450,9 +3539,32 @@ impl Cage { } } - //we only return the default host name because we do not allow for the user to - // change the host name right now + /// ## ------------------GETHOSTNAME SYSCALL------------------ + /// ### Description + /// The `gethostname_syscall()` returns the null-terminated hostname in the + /// address_ptr, which has length bytes. If the null-terminated + /// hostname is too large to fit, then the name is truncated, and no error + /// is returned + /// + /// ### Function Arguments + /// The `gethostname_syscall()` receives two arguments: + /// * `address_ptr` - The buffer to hold the returned host name + /// * `length` - The length of the buffer + /// + /// ### Returns + /// On success, zero is returned. Otherwise, errors or panics are returned + /// for different scenarios. + /// + /// ### Errors + /// * EINVAL - length is negative + /// + /// ### Panics + /// No Panic is expected from this syscall. + /// + /// more details at https://www.man7.org/linux/man-pages/man2/gethostname.2.html pub fn gethostname_syscall(&self, address_ptr: *mut u8, length: isize) -> i32 { + // we only return the default host name (Lind) because we do not allow for the + // user to change the host name right now if length < 0 { return syscall_error( Errno::EINVAL, @@ -3461,15 +3573,19 @@ impl Cage { ); } + // DEFAULT_HOSTNAME is "Lind" + // we convert the string to vector with a null terminator let mut bytes: Vec = DEFAULT_HOSTNAME.as_bytes().to_vec(); bytes.push(0u8); //Adding a null terminator to the end of the string let name_length = bytes.len(); + // take the min between name_length and length from argument let mut len = name_length; if (length as usize) < len { len = length as usize; } + // fill up the address_ptr interface::fill(address_ptr, len, &bytes); return 0; @@ -4251,9 +4367,32 @@ impl Cage { return 0; } - // all this does is send the net_devs data in a string to libc, where we will - // later parse and alloc into getifaddrs structs + /// ## ------------------GETIFADDRS SYSCALL------------------ + /// ### Description + /// The `getifaddrs_syscall()` function creates a linked list of structures + /// describing the network interfaces of the local system, and stores the + /// address of the first item of the list in buf. + /// + /// ### Function Arguments + /// The `getifaddrs_syscall()` receives two arguments: + /// * `buf` - The buffer to hold the returned address + /// * `count` - The length of the buffer + /// + /// ### Returns + /// On success, zero is returned. Otherwise, errors or panics are returned + /// for different scenarios. + /// + /// ### Errors + /// * EOPNOTSUPP - buf length is too small to hold the return value + /// + /// ### Panics + /// No Panic is expected from this syscall. + /// + /// more details at https://www.man7.org/linux/man-pages/man3/getifaddrs.3.html pub fn getifaddrs_syscall(&self, buf: *mut u8, count: usize) -> i32 { + // all this does is returning the net_devs data in a string, where we will later + // parse and alloc into getifaddrs structs in libc + if NET_IFADDRS_STR.len() < count { interface::fill( buf, diff --git a/src/tests/networking_tests.rs b/src/tests/networking_tests.rs index da94213b..f11c3945 100644 --- a/src/tests/networking_tests.rs +++ b/src/tests/networking_tests.rs @@ -575,8 +575,190 @@ pub mod net_tests { lindrustfinalize(); } - pub fn ut_lind_net_getpeername() { - //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + #[test] + pub fn ut_lind_net_getpeername_bad_input() { + // this test is used for testing getpeername with invalid input + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let filefd = cage.open_syscall("/getpeernametest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + + // test for invalid file descriptor + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //127.0.0.1 + // fd that is out of range + assert_eq!( + cage.getpeername_syscall(-1, &mut retsocket), + -(Errno::EBADF as i32) + ); + // fd that does not exist + assert_eq!( + cage.getpeername_syscall(10, &mut retsocket), + -(Errno::EBADF as i32) + ); + // fd that is not socket + assert_eq!( + cage.getpeername_syscall(filefd, &mut retsocket), + -(Errno::ENOTSOCK as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getpeername_inet() { + // this test is used for testing getpeername on AF_INET sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + let port: u16 = generate_random_port(); + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); // 127.0.0.1 + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + // we havn't connected yet, so it should return ENOTCONN error + assert_eq!( + cage.getpeername_syscall(sockfd, &mut retsocket), + -(Errno::ENOTCONN as i32) + ); + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage2.getpeername_syscall(clientsockfd1, &mut retsocket), 0); + // the retsocket should be exactly the same as the address of the server + assert_eq!(retsocket, socket); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 4), 0); + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + assert!(fd > 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getpeername_syscall(fd, &mut retsocket), 0); + // peer's port should not be zero + assert_ne!(retsocket.port(), 0); + // peer's address should be 127.0.0.1 + assert_eq!( + retsocket.addr(), + interface::GenIpaddr::V4(interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }) + ); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getpeername_unix() { + // this test is used for testing getpeername on AF_UNIX sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_getpeername".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + assert_eq!(cage.bind_syscall(sockfd, &serversocket_unix), 0); + + assert_eq!(cage.fork_syscall(2), 0); + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + let clientsockfd1 = cage2.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &serversocket_unix), 0); + + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage2.getpeername_syscall(clientsockfd1, &mut retsocket), 0); + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // client's peer address should be exactly same as the server address + assert_eq!(unixinfo, serversockaddr_unix); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 1), 0); + let mut sockgarbage = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( + AF_UNIX as u16, + "".as_bytes(), + )); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + assert!(fd > 0); + + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage.getpeername_syscall(fd, &mut retsocket), 0); + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // server's peer address should have family of AF_UNIX + assert_eq!(unixinfo.sun_family, AF_UNIX as u16); + // and a path that is not empty + assert_ne!(unixinfo.sun_path[0], 0); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getpeername_udp() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, // and also performs clean env setup let _thelock = setup::lock_and_init(); @@ -617,22 +799,103 @@ pub mod net_tests { } #[test] - pub fn ut_lind_net_getsockname() { - //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + pub fn ut_lind_net_getsockname_bad_input() { + // this test is used for testing getsockname with invalid input + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // test for passing invalid fd + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!( + cage.getsockname_syscall(10, &mut retsocket), + -(Errno::EBADF as i32) + ); + + // test for passing fd that is not socket + let filefd = cage.open_syscall("/getsocknametest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + assert!(filefd > 0); + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!( + cage.getsockname_syscall(filefd, &mut retsocket), + -(Errno::ENOTSOCK as i32) + ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockname_empty() { + // this test is used for testing getsockname with socket that hasn't bound to + // any address + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, // and also performs clean env setup let _thelock = setup::lock_and_init(); let cage = interface::cagetable_getref(1); + // test for ipv4 + let emptysocket_ipv4 = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: 0, + sin_addr: interface::V4Addr::default(), + padding: 0, + }); + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + assert!(sockfd >= 0); let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, emptysocket_ipv4); + + // test for unix socket + let sockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + assert!(sockfd >= 0); + let mut retsocket = + interface::GenSockaddr::Unix(interface::new_sockaddr_unix(SOCK_STREAM as u16, &[])); + let emptysocket_unix = + interface::GenSockaddr::Unix(interface::new_sockaddr_unix(SOCK_STREAM as u16, &[])); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + assert_eq!(retsocket, emptysocket_unix); + // test for ipv6 + let sockfd = cage.socket_syscall(AF_INET6, SOCK_STREAM, 0); + assert!(sockfd >= 0); + let mut retsocket = interface::GenSockaddr::V6(interface::SockaddrV6::default()); assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + // port should be 0 assert_eq!(retsocket.port(), 0); + // address should be :: assert_eq!( retsocket.addr(), - interface::GenIpaddr::V4(interface::V4Addr::default()) + interface::GenIpaddr::V6(interface::V6Addr::default()) ); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + #[ignore] + pub fn ut_lind_net_getsockname_inet_connect() { + // temporary test derived from ut_lind_net_getsockname_inet due to a bug + // once the bug is fixed, ideally should merge back into + // ut_lind_net_getsockname_inet + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let port: u16 = generate_random_port(); let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { sin_family: AF_INET as u16, @@ -641,17 +904,219 @@ pub mod net_tests { s_addr: u32::from_ne_bytes([127, 0, 0, 1]), }, padding: 0, - }); //127.0.0.1 + }); // 127.0.0.1 + + assert_eq!(cage.bind_syscall(sockfd, &socket), 0); + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage2.getsockname_syscall(clientsockfd1, &mut retsocket), 0); + + // BUG: when calling connect without binding to any address, an new address will + // be automatically assigned to the socket, and its ip address is supposed to be + // 127.0.0.1, and port is not supposed to be 0. But we failed this test + assert_ne!(retsocket.port(), 0); + assert_eq!( + retsocket.addr(), + interface::GenIpaddr::V4(interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }) + ); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 4), 0); + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + assert!(fd > 0); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockname_inet() { + // this test is used for testing getsockname on AF_INET sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + let port: u16 = generate_random_port(); + let socket = interface::GenSockaddr::V4(interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }); // 127.0.0.1 + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); assert_eq!(cage.bind_syscall(sockfd, &socket), 0); assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); assert_eq!(retsocket, socket); - //checking that we cannot rebind the socket + // checking that we cannot rebind the socket assert_eq!(cage.bind_syscall(sockfd, &socket), -(Errno::EINVAL as i32)); //already bound so should fail assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); assert_eq!(retsocket, socket); + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client + + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage2.getsockname_syscall(clientsockfd1, &mut retsocket), 0); + + // assert_ne!(retsocket.port(), 0); + // assert_eq!(retsocket.addr(), interface::GenIpaddr::V4(interface::V4Addr { + // s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + // })); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + assert_eq!(cage.listen_syscall(sockfd, 4), 0); + let mut sockgarbage = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + + let mut retsocket = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + assert_eq!(cage.getsockname_syscall(fd, &mut retsocket), 0); + assert_ne!(retsocket.port(), 0); + assert_eq!( + retsocket.addr(), + interface::GenIpaddr::V4(interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }) + ); + + threadclient1.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getsockname_unix() { + // this test is used for testing getsockname on AF_UNIX sockets + + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // create a AF_UNIX socket + let sockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_getsockname".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + assert_eq!(cage.bind_syscall(sockfd, &serversocket_unix), 0); + + // now the socket is bound to an address, check if the address returned from + // getsockname_syscall is correct + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage.getsockname_syscall(sockfd, &mut retsocket), 0); + // retrieve the unix info + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + assert_eq!(serversockaddr_unix, unixinfo); + + assert_eq!(cage.fork_syscall(2), 0); + + // this barrier is to coordinate server and client + let barrier = Arc::new(Barrier::new(2)); + let barrier_clone = barrier.clone(); + + let threadclient1 = interface::helper_thread(move || { + // client thread + let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(sockfd), 0); + + let clientsockfd1 = cage2.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + barrier_clone.wait(); + // connect to server + assert_eq!(cage2.connect_syscall(clientsockfd1, &serversocket_unix), 0); + + // the client address hasn't bound to an address before connect + // so a new address must be automatically assigned to it + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage2.getsockname_syscall(clientsockfd1, &mut retsocket), 0); + // retrieve the unix info + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // check the family + assert_eq!(unixinfo.sun_family, AF_UNIX as u16); + // its path should not be empty + // since we do not know the assigned address exactly + // so we just check if the first character of the address it not null + // to verify if the address is not empty + assert_ne!(unixinfo.sun_path[0], 0); + + cage2.exit_syscall(EXIT_SUCCESS); + }); + + // server listen + assert_eq!(cage.listen_syscall(sockfd, 1), 0); + let mut sockgarbage = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( + AF_UNIX as u16, + "".as_bytes(), + )); + barrier.wait(); + let fd = cage.accept_syscall(sockfd as i32, &mut sockgarbage); + + // the socket created from accept should be automatically assigned an address as + // well + let mut retsocket = interface::GenSockaddr::Unix(interface::new_sockaddr_unix(0, &[])); + assert_eq!(cage.getsockname_syscall(fd, &mut retsocket), 0); + let unixinfo = match retsocket { + interface::GenSockaddr::Unix(value) => value, + _ => unreachable!(), + }; + // same check as client + assert_eq!(unixinfo.sun_family, AF_UNIX as u16); + assert_ne!(unixinfo.sun_path[0], 0); + + threadclient1.join().unwrap(); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } @@ -3255,11 +3720,11 @@ pub mod net_tests { #[test] pub fn ut_lind_net_gethostname() { - //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, // and also performs clean env setup let _thelock = setup::lock_and_init(); - //Assuming DEFAULT_HOSTNAME == "Lind" and change of hostname is not allowed + // Assuming DEFAULT_HOSTNAME == "Lind" and change of hostname is not allowed let cage = interface::cagetable_getref(1); @@ -3292,6 +3757,38 @@ pub mod net_tests { assert_eq!(cage.gethostname_syscall(bufptr, 2), 0); assert_eq!(std::str::from_utf8(&buf).unwrap(), "Li"); + let mut buf = vec![0u8; 2]; + let bufptr: *mut u8 = &mut buf[0]; + assert_eq!(cage.gethostname_syscall(bufptr, 0), 0); + assert_eq!(std::str::from_utf8(&buf).unwrap(), "\0\0"); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + #[test] + pub fn ut_lind_net_getifaddrs() { + // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + + let cage = interface::cagetable_getref(1); + + // get the address + let mut buf = vec![0u8; 200]; + let bufptr: *mut u8 = &mut buf[0]; + // not enough space would cause EOPNOTSUPP error + assert_eq!( + cage.getifaddrs_syscall(bufptr, 1), + -(Errno::EOPNOTSUPP as i32) + ); + assert_eq!(cage.getifaddrs_syscall(bufptr, 200), 0); + // split the address string into vector + let result = std::str::from_utf8(&buf).unwrap().replace("\0", ""); + let addrs = result.trim().split("\n").collect::>(); + // we should have some addresses returned + assert!(addrs.len() > 0); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); }