hermit/syscalls/
socket.rs

1#![allow(dead_code)]
2#![allow(nonstandard_style)]
3use alloc::sync::Arc;
4use core::ffi::{c_char, c_void};
5use core::mem::size_of;
6#[allow(unused_imports)]
7use core::ops::DerefMut;
8
9use cfg_if::cfg_if;
10#[cfg(any(feature = "tcp", feature = "udp"))]
11use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint};
12
13use crate::errno::*;
14#[cfg(any(feature = "tcp", feature = "udp"))]
15use crate::executor::network::{NIC, NetworkState};
16#[cfg(feature = "tcp")]
17use crate::fd::socket::tcp;
18#[cfg(feature = "udp")]
19use crate::fd::socket::udp;
20#[cfg(feature = "vsock")]
21use crate::fd::socket::vsock::{self, VsockEndpoint, VsockListenEndpoint};
22use crate::fd::{
23	Endpoint, ListenEndpoint, ObjectInterface, SocketOption, get_object, insert_object,
24};
25use crate::syscalls::{IoCtl, block_on};
26
27pub const AF_INET: i32 = 0;
28pub const AF_INET6: i32 = 1;
29pub const AF_VSOCK: i32 = 2;
30pub const IPPROTO_IP: i32 = 0;
31pub const IPPROTO_IPV6: i32 = 41;
32pub const IPPROTO_TCP: i32 = 6;
33pub const IPPROTO_UDP: i32 = 17;
34pub const IPV6_ADD_MEMBERSHIP: i32 = 12;
35pub const IPV6_DROP_MEMBERSHIP: i32 = 13;
36pub const IPV6_MULTICAST_LOOP: i32 = 19;
37pub const IPV6_V6ONLY: i32 = 27;
38pub const IP_TOS: i32 = 1;
39pub const IP_TTL: i32 = 2;
40pub const IP_MULTICAST_TTL: i32 = 5;
41pub const IP_MULTICAST_LOOP: i32 = 7;
42pub const IP_ADD_MEMBERSHIP: i32 = 3;
43pub const IP_DROP_MEMBERSHIP: i32 = 4;
44pub const SOL_SOCKET: i32 = 4095;
45pub const SO_REUSEADDR: i32 = 0x0004;
46pub const SO_KEEPALIVE: i32 = 0x0008;
47pub const SO_BROADCAST: i32 = 0x0020;
48pub const SO_LINGER: i32 = 0x0080;
49pub const SO_SNDBUF: i32 = 0x1001;
50pub const SO_RCVBUF: i32 = 0x1002;
51pub const SO_SNDTIMEO: i32 = 0x1005;
52pub const SO_RCVTIMEO: i32 = 0x1006;
53pub const SO_ERROR: i32 = 0x1007;
54pub const TCP_NODELAY: i32 = 1;
55pub const MSG_PEEK: i32 = 1;
56pub const EAI_AGAIN: i32 = 2;
57pub const EAI_BADFLAGS: i32 = 3;
58pub const EAI_FAIL: i32 = 4;
59pub const EAI_FAMILY: i32 = 5;
60pub const EAI_MEMORY: i32 = 6;
61pub const EAI_NODATA: i32 = 7;
62pub const EAI_NONAME: i32 = 8;
63pub const EAI_SERVICE: i32 = 9;
64pub const EAI_SOCKTYPE: i32 = 10;
65pub const EAI_SYSTEM: i32 = 11;
66pub const EAI_OVERFLOW: i32 = 14;
67pub type sa_family_t = u8;
68pub type socklen_t = u32;
69pub type in_addr_t = u32;
70pub type in_port_t = u16;
71
72bitflags! {
73	#[derive(Debug, Copy, Clone)]
74	#[repr(C)]
75	pub struct SockType: i32 {
76		const SOCK_DGRAM = 2;
77		const SOCK_STREAM = 1;
78		const SOCK_NONBLOCK = 0o4000;
79		const SOCK_CLOEXEC = 0o40000;
80	}
81}
82
83#[repr(C)]
84#[derive(Debug, Default, Copy, Clone)]
85pub struct in_addr {
86	pub s_addr: in_addr_t,
87}
88
89#[repr(C, align(4))]
90#[derive(Debug, Default, Copy, Clone)]
91pub struct in6_addr {
92	pub s6_addr: [u8; 16],
93}
94
95#[repr(C)]
96#[derive(Debug, Default, Copy, Clone)]
97pub struct sockaddr {
98	pub sa_len: u8,
99	pub sa_family: sa_family_t,
100	pub sa_data: [c_char; 14],
101}
102
103#[cfg(feature = "vsock")]
104#[repr(C)]
105#[derive(Debug, Copy, Clone, Default)]
106pub struct sockaddr_vm {
107	pub svm_len: u8,
108	pub svm_family: sa_family_t,
109	pub svm_reserved1: u16,
110	pub svm_port: u32,
111	pub svm_cid: u32,
112	pub svm_zero: [u8; 4],
113}
114
115#[cfg(feature = "vsock")]
116impl From<sockaddr_vm> for VsockListenEndpoint {
117	fn from(addr: sockaddr_vm) -> VsockListenEndpoint {
118		let port = addr.svm_port;
119		let cid = if addr.svm_cid < u32::MAX {
120			Some(addr.svm_cid)
121		} else {
122			None
123		};
124
125		VsockListenEndpoint::new(port, cid)
126	}
127}
128
129#[cfg(feature = "vsock")]
130impl From<sockaddr_vm> for VsockEndpoint {
131	fn from(addr: sockaddr_vm) -> VsockEndpoint {
132		let port = addr.svm_port;
133		let cid = addr.svm_cid;
134
135		VsockEndpoint::new(port, cid)
136	}
137}
138
139#[cfg(feature = "vsock")]
140impl From<VsockEndpoint> for sockaddr_vm {
141	fn from(endpoint: VsockEndpoint) -> Self {
142		Self {
143			svm_len: core::mem::size_of::<sockaddr_vm>().try_into().unwrap(),
144			svm_family: AF_VSOCK.try_into().unwrap(),
145			svm_port: endpoint.port,
146			svm_cid: endpoint.cid,
147			..Default::default()
148		}
149	}
150}
151
152#[repr(C)]
153#[derive(Debug, Default, Copy, Clone)]
154pub struct sockaddr_in {
155	pub sin_len: u8,
156	pub sin_family: sa_family_t,
157	pub sin_port: in_port_t,
158	pub sin_addr: in_addr,
159	pub sin_zero: [c_char; 8],
160}
161
162#[cfg(any(feature = "tcp", feature = "udp"))]
163impl From<sockaddr_in> for IpListenEndpoint {
164	fn from(addr: sockaddr_in) -> IpListenEndpoint {
165		let port = u16::from_be(addr.sin_port);
166		if addr.sin_addr.s_addr == 0 {
167			IpListenEndpoint { addr: None, port }
168		} else {
169			let s_addr = addr.sin_addr.s_addr.to_ne_bytes();
170
171			let address = IpAddress::v4(s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
172
173			IpListenEndpoint::from((address, port))
174		}
175	}
176}
177
178#[cfg(any(feature = "tcp", feature = "udp"))]
179impl From<sockaddr_in> for IpEndpoint {
180	fn from(addr: sockaddr_in) -> IpEndpoint {
181		let port = u16::from_be(addr.sin_port);
182		let s_addr = addr.sin_addr.s_addr.to_ne_bytes();
183		let address = IpAddress::v4(s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
184
185		IpEndpoint::from((address, port))
186	}
187}
188
189#[cfg(any(feature = "tcp", feature = "udp"))]
190impl From<IpEndpoint> for sockaddr_in {
191	fn from(endpoint: IpEndpoint) -> Self {
192		match endpoint.addr {
193			IpAddress::Ipv4(ip) => {
194				let sin_addr = in_addr {
195					s_addr: u32::from_ne_bytes(ip.octets()),
196				};
197
198				Self {
199					sin_len: core::mem::size_of::<sockaddr_in>().try_into().unwrap(),
200					sin_port: endpoint.port.to_be(),
201					sin_family: AF_INET.try_into().unwrap(),
202					sin_addr,
203					..Default::default()
204				}
205			}
206			IpAddress::Ipv6(_) => panic!("Unable to convert IPv6 address to sockadd_in"),
207		}
208	}
209}
210
211#[repr(C)]
212#[derive(Debug, Default, Copy, Clone)]
213pub struct sockaddr_in6 {
214	pub sin6_len: u8,
215	pub sin6_family: sa_family_t,
216	pub sin6_port: in_port_t,
217	pub sin6_flowinfo: u32,
218	pub sin6_addr: in6_addr,
219	pub sin6_scope_id: u32,
220}
221
222#[cfg(any(feature = "tcp", feature = "udp"))]
223impl From<sockaddr_in6> for IpListenEndpoint {
224	fn from(addr: sockaddr_in6) -> IpListenEndpoint {
225		let port = u16::from_be(addr.sin6_port);
226		if addr.sin6_addr.s6_addr.into_iter().all(|b| b == 0) {
227			IpListenEndpoint { addr: None, port }
228		} else {
229			let s6_addr = addr.sin6_addr.s6_addr;
230			let a0 = (u16::from(s6_addr[0]) << 8) | u16::from(s6_addr[1]);
231			let a1 = (u16::from(s6_addr[2]) << 8) | u16::from(s6_addr[3]);
232			let a2 = (u16::from(s6_addr[4]) << 8) | u16::from(s6_addr[5]);
233			let a3 = (u16::from(s6_addr[6]) << 8) | u16::from(s6_addr[7]);
234			let a4 = (u16::from(s6_addr[8]) << 8) | u16::from(s6_addr[9]);
235			let a5 = (u16::from(s6_addr[10]) << 8) | u16::from(s6_addr[11]);
236			let a6 = (u16::from(s6_addr[12]) << 8) | u16::from(s6_addr[13]);
237			let a7 = (u16::from(s6_addr[14]) << 8) | u16::from(s6_addr[15]);
238			let address = IpAddress::v6(a0, a1, a2, a3, a4, a5, a6, a7);
239
240			IpListenEndpoint::from((address, port))
241		}
242	}
243}
244
245#[cfg(any(feature = "tcp", feature = "udp"))]
246impl From<sockaddr_in6> for IpEndpoint {
247	fn from(addr: sockaddr_in6) -> IpEndpoint {
248		let port = u16::from_be(addr.sin6_port);
249		let s6_addr = addr.sin6_addr.s6_addr;
250		let a0 = (u16::from(s6_addr[0]) << 8) | u16::from(s6_addr[1]);
251		let a1 = (u16::from(s6_addr[2]) << 8) | u16::from(s6_addr[3]);
252		let a2 = (u16::from(s6_addr[4]) << 8) | u16::from(s6_addr[5]);
253		let a3 = (u16::from(s6_addr[6]) << 8) | u16::from(s6_addr[7]);
254		let a4 = (u16::from(s6_addr[8]) << 8) | u16::from(s6_addr[9]);
255		let a5 = (u16::from(s6_addr[10]) << 8) | u16::from(s6_addr[11]);
256		let a6 = (u16::from(s6_addr[12]) << 8) | u16::from(s6_addr[13]);
257		let a7 = (u16::from(s6_addr[14]) << 8) | u16::from(s6_addr[15]);
258		let address = IpAddress::v6(a0, a1, a2, a3, a4, a5, a6, a7);
259
260		IpEndpoint::from((address, port))
261	}
262}
263
264#[cfg(any(feature = "tcp", feature = "udp"))]
265impl From<IpEndpoint> for sockaddr_in6 {
266	fn from(endpoint: IpEndpoint) -> Self {
267		match endpoint.addr {
268			IpAddress::Ipv6(ip) => {
269				let mut in6_addr = in6_addr::default();
270				in6_addr.s6_addr.copy_from_slice(&ip.octets());
271
272				Self {
273					sin6_len: core::mem::size_of::<sockaddr_in6>().try_into().unwrap(),
274					sin6_port: endpoint.port.to_be(),
275					sin6_family: AF_INET6.try_into().unwrap(),
276					sin6_addr: in6_addr,
277					..Default::default()
278				}
279			}
280			IpAddress::Ipv4(_) => panic!("Unable to convert IPv4 address to sockadd_in6"),
281		}
282	}
283}
284
285#[repr(C)]
286#[derive(Debug, Copy, Clone)]
287pub struct ip_mreq {
288	pub imr_multiaddr: in_addr,
289	pub imr_interface: in_addr,
290}
291
292#[repr(C)]
293#[derive(Debug, Copy, Clone)]
294pub struct ipv6_mreq {
295	pub ipv6mr_multiaddr: in6_addr,
296	pub ipv6mr_interface: u32,
297}
298
299#[repr(C)]
300#[derive(Debug, Copy, Clone)]
301pub struct addrinfo {
302	pub ai_flags: i32,
303	pub ai_family: i32,
304	pub ai_socktype: i32,
305	pub ai_protocol: i32,
306	pub ai_addrlen: socklen_t,
307	pub ai_canonname: *mut c_char,
308	pub ai_addr: *mut sockaddr,
309	pub ai_next: *mut addrinfo,
310}
311
312#[repr(C)]
313#[derive(Debug, Copy, Clone)]
314pub struct linger {
315	pub l_onoff: i32,
316	pub l_linger: i32,
317}
318
319#[cfg(not(feature = "dns"))]
320#[hermit_macro::system]
321#[unsafe(no_mangle)]
322pub unsafe extern "C" fn sys_getaddrbyname(
323	_name: *const c_char,
324	_inaddr: *mut u8,
325	_len: usize,
326) -> i32 {
327	error!("Please enable the feature 'dns' to determine the network ip by name.");
328	-ENOSYS
329}
330
331/// The system call `sys_getaddrbyname` determine the network host entry.
332/// It expects an array of u8 with a size of in_addr or of in6_addr.
333/// The result of the DNS request will be stored in this array.
334///
335/// # Example
336///
337/// ```
338/// use hermit_abi::in_addr;
339/// let c_string = std::ffi::CString::new("rust-lang.org").expect("CString::new failed");
340/// let name = c_string.into_raw();
341/// let mut inaddr: in_addr = Default::default();
342/// let _ = unsafe {
343///         hermit_abi::getaddrbyname(
344///                 name,
345///                 &mut inaddr as *mut _ as *mut u8,
346///                 std::mem::size_of::<in_addr>(),
347///         )
348/// };
349///
350/// // retake pointer to free memory
351/// let _ = CString::from_raw(name);
352/// ```
353#[cfg(feature = "dns")]
354#[hermit_macro::system]
355#[unsafe(no_mangle)]
356pub unsafe extern "C" fn sys_getaddrbyname(
357	name: *const c_char,
358	inaddr: *mut u8,
359	len: usize,
360) -> i32 {
361	use alloc::borrow::ToOwned;
362
363	use smoltcp::wire::DnsQueryType;
364
365	use crate::executor::block_on;
366	use crate::executor::network::get_query_result;
367
368	if len != size_of::<in_addr>() && len != size_of::<in6_addr>() {
369		return -EINVAL;
370	}
371
372	if inaddr.is_null() {
373		return -EINVAL;
374	}
375
376	let query_type = if len == size_of::<in6_addr>() {
377		DnsQueryType::Aaaa
378	} else {
379		DnsQueryType::A
380	};
381
382	let name = unsafe { core::ffi::CStr::from_ptr(name) };
383	let name = if let Ok(name) = name.to_str() {
384		name.to_owned()
385	} else {
386		return -EINVAL;
387	};
388
389	let query = {
390		let mut guard = NIC.lock();
391		let nic = guard.as_nic_mut().unwrap();
392		let query = nic.start_query(&name, query_type).unwrap();
393		nic.poll_common(crate::executor::network::now());
394
395		query
396	};
397
398	match block_on(get_query_result(query), None) {
399		Ok(addr_vec) => {
400			let slice = unsafe { core::slice::from_raw_parts_mut(inaddr, len) };
401
402			match addr_vec[0] {
403				IpAddress::Ipv4(ipv4_addr) => slice.copy_from_slice(&ipv4_addr.octets()),
404				IpAddress::Ipv6(ipv6_addr) => slice.copy_from_slice(&ipv6_addr.octets()),
405			}
406
407			0
408		}
409		Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(),
410	}
411}
412
413#[hermit_macro::system]
414#[unsafe(no_mangle)]
415pub extern "C" fn sys_socket(domain: i32, type_: SockType, protocol: i32) -> i32 {
416	debug!(
417		"sys_socket: domain {}, type {:?}, protocol {}",
418		domain, type_, protocol
419	);
420
421	if protocol != 0 {
422		return -EINVAL;
423	}
424
425	#[cfg(feature = "vsock")]
426	if domain == AF_VSOCK && type_.intersects(SockType::SOCK_STREAM) {
427		let socket = Arc::new(async_lock::RwLock::new(vsock::Socket::new()));
428
429		if type_.contains(SockType::SOCK_NONBLOCK) {
430			block_on(socket.ioctl(IoCtl::NonBlocking, true), None).unwrap();
431		}
432
433		let fd = insert_object(socket).expect("FD is already used");
434
435		return fd;
436	}
437
438	#[cfg(any(feature = "tcp", feature = "udp"))]
439	if (domain == AF_INET || domain == AF_INET6)
440		&& type_.intersects(SockType::SOCK_STREAM | SockType::SOCK_DGRAM)
441	{
442		let mut guard = NIC.lock();
443
444		if let NetworkState::Initialized(nic) = &mut *guard {
445			#[cfg(feature = "udp")]
446			if type_.contains(SockType::SOCK_DGRAM) {
447				let handle = nic.create_udp_handle().unwrap();
448				drop(guard);
449				let socket = Arc::new(async_lock::RwLock::new(udp::Socket::new(handle)));
450
451				if type_.contains(SockType::SOCK_NONBLOCK) {
452					block_on(socket.ioctl(IoCtl::NonBlocking, true), None).unwrap();
453				}
454
455				let fd = insert_object(socket).expect("FD is already used");
456
457				return fd;
458			}
459
460			#[cfg(feature = "tcp")]
461			if type_.contains(SockType::SOCK_STREAM) {
462				let handle = nic.create_tcp_handle().unwrap();
463				drop(guard);
464				let socket = Arc::new(async_lock::RwLock::new(tcp::Socket::new(handle)));
465
466				if type_.contains(SockType::SOCK_NONBLOCK) {
467					block_on(socket.ioctl(IoCtl::NonBlocking, true), None).unwrap();
468				}
469
470				let fd = insert_object(socket).expect("FD is already used");
471
472				return fd;
473			}
474		}
475	}
476
477	-EINVAL
478}
479
480#[hermit_macro::system]
481#[unsafe(no_mangle)]
482pub unsafe extern "C" fn sys_accept(fd: i32, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 {
483	let obj = get_object(fd);
484	obj.map_or_else(
485		|e| -num::ToPrimitive::to_i32(&e).unwrap(),
486		|v| {
487			block_on((*v).accept(), None).map_or_else(
488				|e| -num::ToPrimitive::to_i32(&e).unwrap(),
489				|(obj, endpoint)| match endpoint {
490					#[cfg(any(feature = "tcp", feature = "udp"))]
491					Endpoint::Ip(endpoint) => {
492						let new_fd = insert_object(obj).unwrap();
493
494						if !addr.is_null() && !addrlen.is_null() {
495							let addrlen = unsafe { &mut *addrlen };
496
497							match endpoint.addr {
498								IpAddress::Ipv4(_) => {
499									if *addrlen >= size_of::<sockaddr_in>().try_into().unwrap() {
500										let addr = unsafe { &mut *addr.cast() };
501										*addr = sockaddr_in::from(endpoint);
502										*addrlen = size_of::<sockaddr_in>().try_into().unwrap();
503									}
504								}
505								IpAddress::Ipv6(_) => {
506									if *addrlen >= size_of::<sockaddr_in6>().try_into().unwrap() {
507										let addr = unsafe { &mut *addr.cast() };
508										*addr = sockaddr_in6::from(endpoint);
509										*addrlen = size_of::<sockaddr_in6>().try_into().unwrap();
510									}
511								}
512							}
513						}
514
515						new_fd
516					}
517					#[cfg(feature = "vsock")]
518					Endpoint::Vsock(endpoint) => {
519						let new_fd = insert_object(v.clone()).unwrap();
520
521						if !addr.is_null() && !addrlen.is_null() {
522							let addrlen = unsafe { &mut *addrlen };
523
524							if *addrlen >= size_of::<sockaddr_vm>().try_into().unwrap() {
525								let addr = unsafe { &mut *addr.cast() };
526								*addr = sockaddr_vm::from(endpoint);
527								*addrlen = size_of::<sockaddr_vm>().try_into().unwrap();
528							}
529						}
530
531						new_fd
532					}
533				},
534			)
535		},
536	)
537}
538
539#[hermit_macro::system]
540#[unsafe(no_mangle)]
541pub extern "C" fn sys_listen(fd: i32, backlog: i32) -> i32 {
542	let obj = get_object(fd);
543	obj.map_or_else(
544		|e| -num::ToPrimitive::to_i32(&e).unwrap(),
545		|v| {
546			block_on((*v).listen(backlog), None)
547				.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
548		},
549	)
550}
551
552#[hermit_macro::system]
553#[unsafe(no_mangle)]
554pub unsafe extern "C" fn sys_bind(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 {
555	if name.is_null() {
556		return -crate::errno::EINVAL;
557	}
558
559	let family: i32 = unsafe { (*name).sa_family.into() };
560
561	let obj = get_object(fd);
562	obj.map_or_else(
563		|e| -num::ToPrimitive::to_i32(&e).unwrap(),
564		|v| match family {
565			#[cfg(any(feature = "tcp", feature = "udp"))]
566			AF_INET => {
567				if namelen < size_of::<sockaddr_in>().try_into().unwrap() {
568					return -crate::errno::EINVAL;
569				}
570				let endpoint = IpListenEndpoint::from(unsafe { *name.cast::<sockaddr_in>() });
571				block_on((*v).bind(ListenEndpoint::Ip(endpoint)), None)
572					.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
573			}
574			#[cfg(any(feature = "tcp", feature = "udp"))]
575			AF_INET6 => {
576				if namelen < size_of::<sockaddr_in6>().try_into().unwrap() {
577					return -crate::errno::EINVAL;
578				}
579				let endpoint = IpListenEndpoint::from(unsafe { *name.cast::<sockaddr_in6>() });
580				block_on((*v).bind(ListenEndpoint::Ip(endpoint)), None)
581					.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
582			}
583			#[cfg(feature = "vsock")]
584			AF_VSOCK => {
585				if namelen < size_of::<sockaddr_vm>().try_into().unwrap() {
586					return -crate::errno::EINVAL;
587				}
588				let endpoint = VsockListenEndpoint::from(unsafe { *name.cast::<sockaddr_vm>() });
589				block_on((*v).bind(ListenEndpoint::Vsock(endpoint)), None)
590					.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
591			}
592			_ => -crate::errno::EINVAL,
593		},
594	)
595}
596
597#[hermit_macro::system]
598#[unsafe(no_mangle)]
599pub unsafe extern "C" fn sys_connect(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 {
600	if name.is_null() {
601		return -crate::errno::EINVAL;
602	}
603
604	let sa_family = unsafe { i32::from((*name).sa_family) };
605
606	let endpoint = match sa_family {
607		#[cfg(any(feature = "tcp", feature = "udp"))]
608		AF_INET => {
609			if namelen < size_of::<sockaddr_in>().try_into().unwrap() {
610				return -crate::errno::EINVAL;
611			}
612			Endpoint::Ip(IpEndpoint::from(unsafe { *name.cast::<sockaddr_in>() }))
613		}
614		#[cfg(any(feature = "tcp", feature = "udp"))]
615		AF_INET6 => {
616			if namelen < size_of::<sockaddr_in6>().try_into().unwrap() {
617				return -crate::errno::EINVAL;
618			}
619			Endpoint::Ip(IpEndpoint::from(unsafe { *name.cast::<sockaddr_in6>() }))
620		}
621		#[cfg(feature = "vsock")]
622		AF_VSOCK => {
623			if namelen < size_of::<sockaddr_vm>().try_into().unwrap() {
624				return -crate::errno::EINVAL;
625			}
626			Endpoint::Vsock(VsockEndpoint::from(unsafe { *name.cast::<sockaddr_vm>() }))
627		}
628		_ => {
629			return -crate::errno::EINVAL;
630		}
631	};
632
633	let obj = get_object(fd);
634	obj.map_or_else(
635		|e| -num::ToPrimitive::to_i32(&e).unwrap(),
636		|v| {
637			block_on((*v).connect(endpoint), None)
638				.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
639		},
640	)
641}
642
643#[hermit_macro::system]
644#[unsafe(no_mangle)]
645pub unsafe extern "C" fn sys_getsockname(
646	fd: i32,
647	addr: *mut sockaddr,
648	addrlen: *mut socklen_t,
649) -> i32 {
650	let obj = get_object(fd);
651	obj.map_or_else(
652		|e| -num::ToPrimitive::to_i32(&e).unwrap(),
653		|v| {
654			if let Ok(Some(endpoint)) = block_on((*v).getsockname(), None) {
655				if !addr.is_null() && !addrlen.is_null() {
656					let addrlen = unsafe { &mut *addrlen };
657
658					match endpoint {
659						#[cfg(any(feature = "tcp", feature = "udp"))]
660						Endpoint::Ip(endpoint) => match endpoint.addr {
661							IpAddress::Ipv4(_) => {
662								if *addrlen >= size_of::<sockaddr_in>().try_into().unwrap() {
663									let addr = unsafe { &mut *addr.cast() };
664									*addr = sockaddr_in::from(endpoint);
665									*addrlen = size_of::<sockaddr_in>().try_into().unwrap();
666								} else {
667									return -crate::errno::EINVAL;
668								}
669							}
670							#[cfg(any(feature = "tcp", feature = "udp"))]
671							IpAddress::Ipv6(_) => {
672								if *addrlen >= size_of::<sockaddr_in6>().try_into().unwrap() {
673									let addr = unsafe { &mut *addr.cast() };
674									*addr = sockaddr_in6::from(endpoint);
675									*addrlen = size_of::<sockaddr_in6>().try_into().unwrap();
676								} else {
677									return -crate::errno::EINVAL;
678								}
679							}
680						},
681						#[cfg(feature = "vsock")]
682						Endpoint::Vsock(_) => {
683							if *addrlen >= size_of::<sockaddr_vm>().try_into().unwrap() {
684								warn!("unsupported device");
685							} else {
686								return -crate::errno::EINVAL;
687							}
688						}
689					}
690				} else {
691					return -crate::errno::EINVAL;
692				}
693			}
694
695			0
696		},
697	)
698}
699
700#[hermit_macro::system]
701#[unsafe(no_mangle)]
702pub unsafe extern "C" fn sys_setsockopt(
703	fd: i32,
704	level: i32,
705	optname: i32,
706	optval: *const c_void,
707	optlen: socklen_t,
708) -> i32 {
709	debug!(
710		"sys_setsockopt: {}, level {}, optname {}",
711		fd, level, optname
712	);
713
714	if level == IPPROTO_TCP
715		&& optname == TCP_NODELAY
716		&& optlen == size_of::<i32>().try_into().unwrap()
717	{
718		if optval.is_null() {
719			return -crate::errno::EINVAL;
720		}
721
722		let value = unsafe { *optval.cast::<i32>() };
723		let obj = get_object(fd);
724		obj.map_or_else(
725			|e| -num::ToPrimitive::to_i32(&e).unwrap(),
726			|v| {
727				block_on((*v).setsockopt(SocketOption::TcpNoDelay, value != 0), None)
728					.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
729			},
730		)
731	} else if level == SOL_SOCKET && optname == SO_REUSEADDR {
732		0
733	} else {
734		-crate::errno::EINVAL
735	}
736}
737
738#[hermit_macro::system]
739#[unsafe(no_mangle)]
740pub unsafe extern "C" fn sys_getsockopt(
741	fd: i32,
742	level: i32,
743	optname: i32,
744	optval: *mut c_void,
745	optlen: *mut socklen_t,
746) -> i32 {
747	debug!(
748		"sys_getsockopt: {}, level {}, optname {}",
749		fd, level, optname
750	);
751
752	if level == IPPROTO_TCP && optname == TCP_NODELAY {
753		if optval.is_null() || optlen.is_null() {
754			return -crate::errno::EINVAL;
755		}
756
757		let optval = unsafe { &mut *optval.cast::<i32>() };
758		let optlen = unsafe { &mut *optlen };
759		let obj = get_object(fd);
760		obj.map_or_else(
761			|e| -num::ToPrimitive::to_i32(&e).unwrap(),
762			|v| {
763				block_on((*v).getsockopt(SocketOption::TcpNoDelay), None).map_or_else(
764					|e| -num::ToPrimitive::to_i32(&e).unwrap(),
765					|value| {
766						if value {
767							*optval = 1;
768						} else {
769							*optval = 0;
770						}
771						*optlen = core::mem::size_of::<i32>().try_into().unwrap();
772
773						0
774					},
775				)
776			},
777		)
778	} else {
779		-crate::errno::EINVAL
780	}
781}
782
783#[hermit_macro::system]
784#[unsafe(no_mangle)]
785pub unsafe extern "C" fn sys_getpeername(
786	fd: i32,
787	addr: *mut sockaddr,
788	addrlen: *mut socklen_t,
789) -> i32 {
790	let obj = get_object(fd);
791	obj.map_or_else(
792		|e| -num::ToPrimitive::to_i32(&e).unwrap(),
793		|v| {
794			if let Ok(Some(endpoint)) = block_on((*v).getpeername(), None) {
795				if !addr.is_null() && !addrlen.is_null() {
796					let addrlen = unsafe { &mut *addrlen };
797
798					match endpoint {
799						#[cfg(any(feature = "tcp", feature = "udp"))]
800						Endpoint::Ip(endpoint) => match endpoint.addr {
801							IpAddress::Ipv4(_) => {
802								if *addrlen >= size_of::<sockaddr_in>().try_into().unwrap() {
803									let addr = unsafe { &mut *addr.cast() };
804									*addr = sockaddr_in::from(endpoint);
805									*addrlen = size_of::<sockaddr_in>().try_into().unwrap();
806								} else {
807									return -crate::errno::EINVAL;
808								}
809							}
810							IpAddress::Ipv6(_) => {
811								if *addrlen >= size_of::<sockaddr_in6>().try_into().unwrap() {
812									let addr = unsafe { &mut *addr.cast() };
813									*addr = sockaddr_in6::from(endpoint);
814									*addrlen = size_of::<sockaddr_in6>().try_into().unwrap();
815								} else {
816									return -crate::errno::EINVAL;
817								}
818							}
819						},
820						#[cfg(feature = "vsock")]
821						Endpoint::Vsock(_) => {
822							if *addrlen >= size_of::<sockaddr_vm>().try_into().unwrap() {
823								warn!("unsupported device");
824							} else {
825								return -crate::errno::EINVAL;
826							}
827						}
828					}
829				} else {
830					return -crate::errno::EINVAL;
831				}
832			}
833
834			0
835		},
836	)
837}
838
839#[hermit_macro::system]
840#[unsafe(no_mangle)]
841pub unsafe extern "C" fn sys_freeaddrinfo(_ai: *mut addrinfo) {}
842
843#[hermit_macro::system]
844#[unsafe(no_mangle)]
845pub unsafe extern "C" fn sys_getaddrinfo(
846	_nodename: *const c_char,
847	_servname: *const c_char,
848	_hints: *const addrinfo,
849	_res: *mut *mut addrinfo,
850) -> i32 {
851	-EINVAL
852}
853
854#[hermit_macro::system]
855#[unsafe(no_mangle)]
856pub unsafe extern "C" fn sys_send(s: i32, mem: *const c_void, len: usize, _flags: i32) -> isize {
857	unsafe { super::write(s, mem.cast(), len) }
858}
859
860fn shutdown(sockfd: i32, how: i32) -> i32 {
861	let obj = get_object(sockfd);
862	obj.map_or_else(
863		|e| -num::ToPrimitive::to_i32(&e).unwrap(),
864		|v| {
865			block_on((*v).shutdown(how), None)
866				.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
867		},
868	)
869}
870
871#[hermit_macro::system]
872#[unsafe(no_mangle)]
873pub extern "C" fn sys_shutdown(sockfd: i32, how: i32) -> i32 {
874	shutdown(sockfd, how)
875}
876
877#[hermit_macro::system]
878#[unsafe(no_mangle)]
879pub extern "C" fn sys_shutdown_socket(fd: i32, how: i32) -> i32 {
880	shutdown(fd, how)
881}
882
883#[hermit_macro::system]
884#[unsafe(no_mangle)]
885pub unsafe extern "C" fn sys_recv(fd: i32, buf: *mut u8, len: usize, flags: i32) -> isize {
886	if flags == 0 {
887		let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) };
888		crate::fd::read(fd, slice).map_or_else(
889			|e| -num::ToPrimitive::to_isize(&e).unwrap(),
890			|v| v.try_into().unwrap(),
891		)
892	} else {
893		(-crate::errno::EINVAL).try_into().unwrap()
894	}
895}
896
897#[hermit_macro::system]
898#[unsafe(no_mangle)]
899pub unsafe extern "C" fn sys_sendto(
900	fd: i32,
901	buf: *const u8,
902	len: usize,
903	_flags: i32,
904	addr: *const sockaddr,
905	addr_len: socklen_t,
906) -> isize {
907	let endpoint;
908
909	if addr.is_null() || addr_len == 0 {
910		return (-crate::errno::EINVAL).try_into().unwrap();
911	}
912
913	cfg_if! {
914		if #[cfg(any(feature = "tcp", feature = "udp"))] {
915			let sa_family = unsafe { i32::from((*addr).sa_family) };
916
917			if sa_family == AF_INET {
918				if addr_len < size_of::<sockaddr_in>().try_into().unwrap() {
919					return (-crate::errno::EINVAL).try_into().unwrap();
920				}
921
922				endpoint = Some(Endpoint::Ip(IpEndpoint::from(unsafe {*(addr.cast::<sockaddr_in>())})));
923			} else if sa_family == AF_INET6 {
924				if addr_len < size_of::<sockaddr_in6>().try_into().unwrap() {
925					return (-crate::errno::EINVAL).try_into().unwrap();
926				}
927
928				endpoint = Some(Endpoint::Ip(IpEndpoint::from(unsafe { *(addr.cast::<sockaddr_in6>()) })));
929			} else {
930				endpoint = None;
931			}
932		} else {
933			endpoint = None;
934		}
935	}
936
937	if let Some(endpoint) = endpoint {
938		let slice = unsafe { core::slice::from_raw_parts(buf, len) };
939		let obj = get_object(fd);
940
941		obj.map_or_else(
942			|e| -num::ToPrimitive::to_isize(&e).unwrap(),
943			|v| {
944				block_on((*v).sendto(slice, endpoint), None).map_or_else(
945					|e| -num::ToPrimitive::to_isize(&e).unwrap(),
946					|v| v.try_into().unwrap(),
947				)
948			},
949		)
950	} else {
951		(-crate::errno::EINVAL).try_into().unwrap()
952	}
953}
954
955#[hermit_macro::system]
956#[unsafe(no_mangle)]
957pub unsafe extern "C" fn sys_recvfrom(
958	fd: i32,
959	buf: *mut u8,
960	len: usize,
961	_flags: i32,
962	addr: *mut sockaddr,
963	addrlen: *mut socklen_t,
964) -> isize {
965	let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) };
966	let obj = get_object(fd);
967	obj.map_or_else(
968		|e| -num::ToPrimitive::to_isize(&e).unwrap(),
969		|v| {
970			block_on((*v).recvfrom(slice), None).map_or_else(
971				|e| -num::ToPrimitive::to_isize(&e).unwrap(),
972				|(len, endpoint)| {
973					if !addr.is_null() && !addrlen.is_null() {
974						#[allow(unused_variables)]
975						let addrlen = unsafe { &mut *addrlen };
976
977						match endpoint {
978							#[cfg(any(feature = "tcp", feature = "udp"))]
979							Endpoint::Ip(endpoint) => match endpoint.addr {
980								IpAddress::Ipv4(_) => {
981									if *addrlen >= size_of::<sockaddr_in>().try_into().unwrap() {
982										let addr = unsafe { &mut *addr.cast() };
983										*addr = sockaddr_in::from(endpoint);
984										*addrlen = size_of::<sockaddr_in>().try_into().unwrap();
985									} else {
986										return (-crate::errno::EINVAL).try_into().unwrap();
987									}
988								}
989								IpAddress::Ipv6(_) => {
990									if *addrlen >= size_of::<sockaddr_in6>().try_into().unwrap() {
991										let addr = unsafe { &mut *addr.cast() };
992										*addr = sockaddr_in6::from(endpoint);
993										*addrlen = size_of::<sockaddr_in6>().try_into().unwrap();
994									} else {
995										return (-crate::errno::EINVAL).try_into().unwrap();
996									}
997								}
998							},
999							#[cfg(feature = "vsock")]
1000							_ => {
1001								return (-crate::errno::EINVAL).try_into().unwrap();
1002							}
1003						}
1004					}
1005
1006					len.try_into().unwrap()
1007				},
1008			)
1009		},
1010	)
1011}