mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-09 20:08:11 +00:00
Merge pull request #3810 from Feoramund/freebsd-core-net
Port `core:net` to FreeBSD
This commit is contained in:
@@ -12,7 +12,7 @@ IMPORTING_TIME :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED)
|
||||
|
||||
// Override support for parsing `net` types.
|
||||
// TODO: Update this when the BSDs are supported.
|
||||
IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin)
|
||||
IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD)
|
||||
|
||||
TAG_ARGS :: "args"
|
||||
SUBTAG_NAME :: "name"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build freebsd, netbsd, openbsd
|
||||
//+build netbsd, openbsd
|
||||
package flags
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build !freebsd !netbsd !openbsd
|
||||
//+build !netbsd !openbsd
|
||||
package flags
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//+private
|
||||
//+build !freebsd !netbsd !openbsd
|
||||
//+build !netbsd !openbsd
|
||||
package flags
|
||||
|
||||
import "core:net"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows, linux, darwin
|
||||
// +build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:strconv"
|
||||
@@ -742,4 +744,4 @@ parse_ip_component :: proc(input: string, max_value := u64(max(u32)), bases := D
|
||||
get_network_interfaces :: proc() -> []Address {
|
||||
// TODO: Implement using `enumerate_interfaces` and returning only the addresses of active interfaces.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows, linux, darwin
|
||||
// +build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -13,12 +13,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "base:runtime"
|
||||
@@ -413,4 +415,4 @@ DNS_Record_Header :: struct #packed {
|
||||
DNS_Host_Entry :: struct {
|
||||
name: string,
|
||||
addr: Address,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows, linux, darwin
|
||||
// +build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
@@ -30,7 +32,7 @@ when ODIN_OS == .Windows {
|
||||
resolv_conf = "",
|
||||
hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts",
|
||||
}
|
||||
} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{
|
||||
resolv_conf = "/etc/resolv.conf",
|
||||
hosts_file = "/etc/hosts",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build linux, darwin
|
||||
//+build linux, darwin, freebsd
|
||||
package net
|
||||
/*
|
||||
Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
|
||||
@@ -9,12 +9,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
import "core:strings"
|
||||
|
||||
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:strings"
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
|
||||
217
core/net/errors_freebsd.odin
Normal file
217
core/net/errors_freebsd.odin
Normal file
@@ -0,0 +1,217 @@
|
||||
//+build freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
|
||||
For other protocols and their features, see subdirectories of this package.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import "core:sys/freebsd"
|
||||
|
||||
Create_Socket_Error :: enum c.int {
|
||||
None = 0,
|
||||
Access_Denied = cast(c.int)freebsd.Errno.EACCES,
|
||||
Family_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
|
||||
Full_Per_Process_Descriptor_Table = cast(c.int)freebsd.Errno.EMFILE,
|
||||
Full_System_File_Table = cast(c.int)freebsd.Errno.ENFILE,
|
||||
No_Buffer_Space_Available = cast(c.int)freebsd.Errno.ENOBUFS,
|
||||
Insufficient_Permission = cast(c.int)freebsd.Errno.EPERM,
|
||||
Protocol_Unsupported_In_Family = cast(c.int)freebsd.Errno.EPROTONOSUPPORT,
|
||||
Socket_Type_Unsupported_By_Protocol = cast(c.int)freebsd.Errno.EPROTOTYPE,
|
||||
}
|
||||
|
||||
Dial_Error :: enum c.int {
|
||||
None = 0,
|
||||
Port_Required = -1,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Invalid_Namelen = cast(c.int)freebsd.Errno.EINVAL,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
Address_Unavailable = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
|
||||
Wrong_Family_For_Socket = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
|
||||
Already_Connected = cast(c.int)freebsd.Errno.EISCONN,
|
||||
Timeout = cast(c.int)freebsd.Errno.ETIMEDOUT,
|
||||
Refused_By_Remote_Host = cast(c.int)freebsd.Errno.ECONNREFUSED,
|
||||
// `Refused` alias for `core:net` tests.
|
||||
// The above default name `Refused_By_Remote_Host` is more explicit.
|
||||
Refused = Refused_By_Remote_Host,
|
||||
Reset_By_Remote_Host = cast(c.int)freebsd.Errno.ECONNRESET,
|
||||
Network_Unreachable = cast(c.int)freebsd.Errno.ENETUNREACH,
|
||||
Host_Unreachable = cast(c.int)freebsd.Errno.EHOSTUNREACH,
|
||||
Address_In_Use = cast(c.int)freebsd.Errno.EADDRINUSE,
|
||||
Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
|
||||
In_Progress = cast(c.int)freebsd.Errno.EINPROGRESS,
|
||||
Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
|
||||
Previous_Attempt_Incomplete = cast(c.int)freebsd.Errno.EALREADY,
|
||||
Broadcast_Unavailable = cast(c.int)freebsd.Errno.EACCES,
|
||||
Auto_Port_Unavailable = cast(c.int)freebsd.Errno.EAGAIN,
|
||||
|
||||
// NOTE: There are additional connect() error possibilities, but they are
|
||||
// strictly for addresses in the UNIX domain.
|
||||
}
|
||||
|
||||
Bind_Error :: enum c.int {
|
||||
None = 0,
|
||||
Kernel_Resources_Unavailable = cast(c.int)freebsd.Errno.EAGAIN,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
|
||||
// NOTE: bind() can also return EINVAL if the underlying `addrlen` is an
|
||||
// invalid length for the address family. This shouldn't happen for the net
|
||||
// package, but it's worth noting.
|
||||
Already_Bound = cast(c.int)freebsd.Errno.EINVAL,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
Given_Nonlocal_Address = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
|
||||
Address_In_Use = cast(c.int)freebsd.Errno.EADDRINUSE,
|
||||
Address_Family_Mismatch = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
|
||||
Protected_Address = cast(c.int)freebsd.Errno.EACCES,
|
||||
Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
|
||||
|
||||
// NOTE: There are additional bind() error possibilities, but they are
|
||||
// strictly for addresses in the UNIX domain.
|
||||
}
|
||||
|
||||
Listen_Error :: enum c.int {
|
||||
None = 0,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Socket_Not_Bound = cast(c.int)freebsd.Errno.EDESTADDRREQ,
|
||||
Already_Connected = cast(c.int)freebsd.Errno.EINVAL,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
Listening_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EOPNOTSUPP,
|
||||
}
|
||||
|
||||
Accept_Error :: enum c.int {
|
||||
None = 0,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Interrupted = cast(c.int)freebsd.Errno.EINTR,
|
||||
Full_Per_Process_Descriptor_Table = cast(c.int)freebsd.Errno.EMFILE,
|
||||
Full_System_File_Table = cast(c.int)freebsd.Errno.ENFILE,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
Listen_Not_Called_On_Socket_Yet = cast(c.int)freebsd.Errno.EINVAL,
|
||||
Address_Not_Writable = cast(c.int)freebsd.Errno.EFAULT,
|
||||
|
||||
// NOTE: This is the same as EWOULDBLOCK.
|
||||
No_Connections_Available = cast(c.int)freebsd.Errno.EAGAIN,
|
||||
// `Would_Block` alias for `core:net` tests.
|
||||
Would_Block = cast(c.int)freebsd.Errno.EAGAIN,
|
||||
|
||||
New_Connection_Aborted = cast(c.int)freebsd.Errno.ECONNABORTED,
|
||||
}
|
||||
|
||||
TCP_Recv_Error :: enum c.int {
|
||||
None = 0,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
|
||||
Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
|
||||
// NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
|
||||
// but I'm including them for completeness's sake.
|
||||
Full_Table_And_Pending_Data = cast(c.int)freebsd.Errno.EMFILE,
|
||||
Invalid_Message_Size = cast(c.int)freebsd.Errno.EMSGSIZE,
|
||||
|
||||
Timeout = cast(c.int)freebsd.Errno.EAGAIN,
|
||||
Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
|
||||
Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
|
||||
}
|
||||
|
||||
UDP_Recv_Error :: enum c.int {
|
||||
None = 0,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
|
||||
Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
|
||||
// NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
|
||||
// but I'm including them for completeness's sake.
|
||||
Full_Table_And_Data_Discarded = cast(c.int)freebsd.Errno.EMFILE,
|
||||
Invalid_Message_Size = cast(c.int)freebsd.Errno.EMSGSIZE,
|
||||
|
||||
Timeout = cast(c.int)freebsd.Errno.EAGAIN,
|
||||
Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
|
||||
Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
|
||||
}
|
||||
|
||||
TCP_Send_Error :: enum c.int {
|
||||
None = 0,
|
||||
Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Broadcast_Status_Mismatch = cast(c.int)freebsd.Errno.EACCES,
|
||||
Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
|
||||
|
||||
Message_Size_Breaks_Atomicity = cast(c.int)freebsd.Errno.EMSGSIZE,
|
||||
|
||||
/* The socket is marked non-blocking, or MSG_DONTWAIT is
|
||||
specified, and the requested operation would block. */
|
||||
Would_Block = cast(c.int)freebsd.Errno.EAGAIN,
|
||||
|
||||
/* NOTE: This error arises for two distinct reasons:
|
||||
|
||||
1. The system was unable to allocate an internal buffer.
|
||||
The operation may succeed when buffers become available.
|
||||
|
||||
2. The output queue for a network interface was full.
|
||||
This generally indicates that the interface has stopped
|
||||
sending, but may be caused by transient congestion.
|
||||
*/
|
||||
No_Buffer_Space_Available = cast(c.int)freebsd.Errno.ENOBUFS,
|
||||
|
||||
Host_Unreachable = cast(c.int)freebsd.Errno.EHOSTUNREACH,
|
||||
Already_Connected = cast(c.int)freebsd.Errno.EISCONN,
|
||||
ICMP_Unreachable = cast(c.int)freebsd.Errno.ECONNREFUSED,
|
||||
Host_Down = cast(c.int)freebsd.Errno.EHOSTDOWN,
|
||||
Network_Down = cast(c.int)freebsd.Errno.ENETDOWN,
|
||||
Jailed_Socket_Tried_To_Escape = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
|
||||
Cannot_Send_More_Data = cast(c.int)freebsd.Errno.EPIPE,
|
||||
}
|
||||
|
||||
// NOTE(Feoramund): The same as TCP errors go, as far as I'm aware.
|
||||
UDP_Send_Error :: distinct TCP_Send_Error
|
||||
|
||||
Shutdown_Manner :: enum c.int {
|
||||
Receive = cast(c.int)freebsd.Shutdown_Method.RD,
|
||||
Send = cast(c.int)freebsd.Shutdown_Method.WR,
|
||||
Both = cast(c.int)freebsd.Shutdown_Method.RDWR,
|
||||
}
|
||||
|
||||
Shutdown_Error :: enum c.int {
|
||||
None = 0,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Invalid_Manner = cast(c.int)freebsd.Errno.EINVAL,
|
||||
Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
}
|
||||
|
||||
Socket_Option_Error :: enum c.int {
|
||||
None = 0,
|
||||
Value_Out_Of_Range = -1,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
|
||||
Unknown_Option_For_Level = cast(c.int)freebsd.Errno.ENOPROTOOPT,
|
||||
Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
|
||||
// This error can arise for many different reasons.
|
||||
Invalid_Value = cast(c.int)freebsd.Errno.EINVAL,
|
||||
System_Memory_Allocation_Failed = cast(c.int)freebsd.Errno.ENOMEM,
|
||||
Insufficient_System_Resources = cast(c.int)freebsd.Errno.ENOBUFS,
|
||||
}
|
||||
|
||||
Set_Blocking_Error :: enum c.int {
|
||||
None = 0,
|
||||
Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
|
||||
Wrong_Descriptor = cast(c.int)freebsd.Errno.ENOTTY,
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
@@ -17,6 +18,7 @@ package net
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
flysand: Move dependency from core:linux.Errno to core:sys/linux
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows, linux, darwin
|
||||
// +build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:strings"
|
||||
|
||||
@@ -10,13 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
|
||||
177
core/net/interface_freebsd.odin
Normal file
177
core/net/interface_freebsd.odin
Normal file
@@ -0,0 +1,177 @@
|
||||
//+build freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
|
||||
For other protocols and their features, see subdirectories of this package.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import "core:strings"
|
||||
import "core:sys/freebsd"
|
||||
|
||||
@(private)
|
||||
_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
|
||||
// This is a simplified implementation of `getifaddrs` from the FreeBSD
|
||||
// libc using only Odin and syscalls.
|
||||
context.allocator = allocator
|
||||
|
||||
mib := [6]freebsd.MIB_Identifier {
|
||||
.CTL_NET,
|
||||
cast(freebsd.MIB_Identifier)freebsd.Protocol_Family.ROUTE,
|
||||
freebsd.MIB_Identifier(0),
|
||||
freebsd.MIB_Identifier(0),
|
||||
.NET_RT_IFLISTL,
|
||||
freebsd.MIB_Identifier(0),
|
||||
}
|
||||
|
||||
// Figure out how much space we need.
|
||||
needed: c.size_t = ---
|
||||
|
||||
errno := freebsd.sysctl(mib[:], nil, &needed, nil, 0)
|
||||
if errno != nil {
|
||||
return nil, .Unable_To_Enumerate_Network_Interfaces
|
||||
}
|
||||
|
||||
// Allocate and get the entries.
|
||||
buf, alloc_err := make([]byte, needed)
|
||||
if alloc_err != nil {
|
||||
return nil, .Unable_To_Enumerate_Network_Interfaces
|
||||
}
|
||||
defer delete(buf)
|
||||
|
||||
errno = freebsd.sysctl(mib[:], &buf[0], &needed, nil, 0)
|
||||
if errno != nil {
|
||||
return nil, .Unable_To_Enumerate_Network_Interfaces
|
||||
}
|
||||
|
||||
// Build the interfaces with each message.
|
||||
if_builder: [dynamic]Network_Interface
|
||||
for message_pointer: uintptr = 0; message_pointer < cast(uintptr)needed; /**/ {
|
||||
rtm := cast(^freebsd.Route_Message_Header)&buf[message_pointer]
|
||||
if rtm.version != freebsd.RTM_VERSION {
|
||||
continue
|
||||
}
|
||||
|
||||
#partial switch rtm.type {
|
||||
case .IFINFO:
|
||||
ifm := cast(^freebsd.Interface_Message_Header_Len)&buf[message_pointer]
|
||||
if .IFP not_in ifm.addrs {
|
||||
// No name available.
|
||||
break
|
||||
}
|
||||
|
||||
dl := cast(^freebsd.Socket_Address_Data_Link)&buf[message_pointer + cast(uintptr)ifm.len]
|
||||
|
||||
if_data := cast(^freebsd.Interface_Data)&buf[message_pointer + cast(uintptr)ifm.data_off]
|
||||
|
||||
// This is done this way so the different message types can
|
||||
// dynamically build a `Network_Interface`.
|
||||
resize(&if_builder, max(len(if_builder), 1 + cast(int)ifm.index))
|
||||
interface := if_builder[ifm.index]
|
||||
|
||||
interface.adapter_name = strings.clone_from_bytes(dl.data[0:dl.nlen])
|
||||
interface.mtu = if_data.mtu
|
||||
|
||||
switch if_data.link_state {
|
||||
case .UNKNOWN: /* Do nothing; the default value is valid. */
|
||||
case .UP: interface.link.state |= { .Up }
|
||||
case .DOWN: interface.link.state |= { .Down }
|
||||
}
|
||||
|
||||
// TODO: Uncertain if these are equivalent:
|
||||
// interface.link.transmit_speed = if_data.baudrate
|
||||
// interface.link.receive_speed = if_data.baudrate
|
||||
|
||||
if dl.type == .LOOP {
|
||||
interface.link.state |= { .Loopback }
|
||||
} else {
|
||||
interface.physical_address = physical_address_to_string(dl.data[dl.nlen:][:6])
|
||||
}
|
||||
|
||||
if_builder[ifm.index] = interface
|
||||
|
||||
case .NEWADDR:
|
||||
RTA_MASKS :: freebsd.Route_Address_Flags { .IFA, .NETMASK }
|
||||
ifam := cast(^freebsd.Interface_Address_Message_Header_Len)&buf[message_pointer]
|
||||
if ifam.addrs & RTA_MASKS == {} {
|
||||
break
|
||||
}
|
||||
|
||||
resize(&if_builder, max(len(if_builder), 1 + cast(int)ifam.index))
|
||||
interface := if_builder[ifam.index]
|
||||
|
||||
address_pointer := message_pointer + cast(uintptr)ifam.len
|
||||
|
||||
lease: Lease
|
||||
address_set: bool
|
||||
for address_type in ifam.addrs {
|
||||
ptr := cast(^freebsd.Socket_Address_Basic)&buf[address_pointer]
|
||||
|
||||
#partial switch address_type {
|
||||
case .IFA:
|
||||
#partial switch ptr.family {
|
||||
case .INET:
|
||||
real := cast(^freebsd.Socket_Address_Internet)ptr
|
||||
lease.address = cast(IP4_Address)real.addr.addr8
|
||||
address_set = true
|
||||
case .INET6:
|
||||
real := cast(^freebsd.Socket_Address_Internet6)ptr
|
||||
lease.address = cast(IP6_Address)real.addr.addr16
|
||||
address_set = true
|
||||
}
|
||||
case .NETMASK:
|
||||
#partial switch ptr.family {
|
||||
case .INET:
|
||||
real := cast(^freebsd.Socket_Address_Internet)ptr
|
||||
lease.netmask = cast(Netmask)cast(IP4_Address)real.addr.addr8
|
||||
case .INET6:
|
||||
real := cast(^freebsd.Socket_Address_Internet6)ptr
|
||||
lease.netmask = cast(Netmask)cast(IP6_Address)real.addr.addr16
|
||||
}
|
||||
}
|
||||
|
||||
SALIGN : u8 : size_of(c.long) - 1
|
||||
address_advance: uintptr = ---
|
||||
if ptr.len > 0 {
|
||||
address_advance = cast(uintptr)((ptr.len + SALIGN) & ~SALIGN)
|
||||
} else {
|
||||
address_advance = cast(uintptr)(SALIGN + 1)
|
||||
}
|
||||
|
||||
address_pointer += address_advance
|
||||
}
|
||||
|
||||
if address_set {
|
||||
append(&interface.unicast, lease)
|
||||
}
|
||||
|
||||
if_builder[ifam.index] = interface
|
||||
}
|
||||
|
||||
message_pointer += cast(uintptr)rtm.msglen
|
||||
}
|
||||
|
||||
// Remove any interfaces that were allocated but had no name.
|
||||
#no_bounds_check for i := len(if_builder) - 1; i >= 0; i -= 1 {
|
||||
if len(if_builder[i].adapter_name) == 0 {
|
||||
ordered_remove(&if_builder, i)
|
||||
}
|
||||
}
|
||||
|
||||
return if_builder[:], nil
|
||||
}
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
|
||||
This file uses `getifaddrs` libc call to enumerate interfaces.
|
||||
TODO: When we have raw sockets, split off into its own file for Linux so we can use the NETLINK protocol and bypass libc.
|
||||
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import sys "core:sys/windows"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows, linux, darwin
|
||||
// +build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022-2023 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022-2023 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022-2023 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
any_socket_to_socket :: proc "contextless" (socket: Any_Socket) -> Socket {
|
||||
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
|
||||
404
core/net/socket_freebsd.odin
Normal file
404
core/net/socket_freebsd.odin
Normal file
@@ -0,0 +1,404 @@
|
||||
//+build freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
|
||||
For other protocols and their features, see subdirectories of this package.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import "core:sys/freebsd"
|
||||
import "core:time"
|
||||
|
||||
Fd :: freebsd.Fd
|
||||
|
||||
Socket_Option :: enum c.int {
|
||||
// TODO: Test and implement more socket options.
|
||||
// DEBUG
|
||||
Reuse_Address = cast(c.int)freebsd.Socket_Option.REUSEADDR,
|
||||
Keep_Alive = cast(c.int)freebsd.Socket_Option.KEEPALIVE,
|
||||
// DONTROUTE
|
||||
Broadcast = cast(c.int)freebsd.Socket_Option.BROADCAST,
|
||||
Use_Loopback = cast(c.int)freebsd.Socket_Option.USELOOPBACK,
|
||||
Linger = cast(c.int)freebsd.Socket_Option.LINGER,
|
||||
Out_Of_Bounds_Data_Inline = cast(c.int)freebsd.Socket_Option.OOBINLINE,
|
||||
Reuse_Port = cast(c.int)freebsd.Socket_Option.REUSEPORT,
|
||||
// TIMESTAMP
|
||||
No_SIGPIPE_From_EPIPE = cast(c.int)freebsd.Socket_Option.NOSIGPIPE,
|
||||
// ACCEPTFILTER
|
||||
// BINTIME
|
||||
// NO_OFFLOAD
|
||||
// NO_DDP
|
||||
Reuse_Port_Load_Balancing = cast(c.int)freebsd.Socket_Option.REUSEPORT_LB,
|
||||
// RERROR
|
||||
|
||||
Send_Buffer_Size = cast(c.int)freebsd.Socket_Option.SNDBUF,
|
||||
Receive_Buffer_Size = cast(c.int)freebsd.Socket_Option.RCVBUF,
|
||||
// SNDLOWAT
|
||||
// RCVLOWAT
|
||||
Send_Timeout = cast(c.int)freebsd.Socket_Option.SNDTIMEO,
|
||||
Receive_Timeout = cast(c.int)freebsd.Socket_Option.RCVTIMEO,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
|
||||
sys_family: freebsd.Protocol_Family = ---
|
||||
sys_protocol: freebsd.Protocol = ---
|
||||
sys_socket_type: freebsd.Socket_Type = ---
|
||||
|
||||
switch family {
|
||||
case .IP4: sys_family = .INET
|
||||
case .IP6: sys_family = .INET6
|
||||
}
|
||||
|
||||
switch protocol {
|
||||
case .TCP: sys_protocol = .TCP; sys_socket_type = .STREAM
|
||||
case .UDP: sys_protocol = .UDP; sys_socket_type = .DGRAM
|
||||
}
|
||||
|
||||
new_socket, errno := freebsd.socket(sys_family, sys_socket_type, sys_protocol)
|
||||
if errno != nil {
|
||||
err = cast(Create_Socket_Error)errno
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE(Feoramund): By default, FreeBSD will generate SIGPIPE if an EPIPE
|
||||
// error is raised during the writing of a socket that may be closed.
|
||||
// This behavior is unlikely to be expected by general users.
|
||||
//
|
||||
// There are two workarounds. One is to apply the .NOSIGNAL flag when using
|
||||
// the `sendto` syscall. However, that would prevent users of this library
|
||||
// from re-enabling the SIGPIPE-raising functionality, if they really
|
||||
// wanted it.
|
||||
//
|
||||
// So I have disabled it here with this socket option for all sockets.
|
||||
truth: b32 = true
|
||||
errno = freebsd.setsockopt(new_socket, .SOCKET, .NOSIGPIPE, &truth, size_of(truth))
|
||||
if errno != nil {
|
||||
err = cast(Socket_Option_Error)errno
|
||||
return
|
||||
}
|
||||
|
||||
switch protocol {
|
||||
case .TCP: return cast(TCP_Socket)new_socket, nil
|
||||
case .UDP: return cast(UDP_Socket)new_socket, nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
|
||||
if endpoint.port == 0 {
|
||||
return 0, .Port_Required
|
||||
}
|
||||
|
||||
family := family_from_endpoint(endpoint)
|
||||
new_socket := create_socket(family, .TCP) or_return
|
||||
socket = new_socket.(TCP_Socket)
|
||||
|
||||
sockaddr := _endpoint_to_sockaddr(endpoint)
|
||||
errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
|
||||
if errno != nil {
|
||||
err = cast(Dial_Error)errno
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
|
||||
sockaddr := _endpoint_to_sockaddr(ep)
|
||||
real_socket := any_socket_to_socket(socket)
|
||||
errno := freebsd.bind(cast(Fd)real_socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
|
||||
if errno != nil {
|
||||
err = cast(Bind_Error)errno
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
|
||||
family := family_from_endpoint(interface_endpoint)
|
||||
new_socket := create_socket(family, .TCP) or_return
|
||||
socket = new_socket.(TCP_Socket)
|
||||
|
||||
bind(socket, interface_endpoint) or_return
|
||||
|
||||
errno := freebsd.listen(cast(Fd)socket, backlog)
|
||||
if errno != nil {
|
||||
err = cast(Listen_Error)errno
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
|
||||
sockaddr: freebsd.Socket_Address_Storage
|
||||
|
||||
result, errno := freebsd.accept(cast(Fd)sock, &sockaddr)
|
||||
if errno != nil {
|
||||
err = cast(Accept_Error)errno
|
||||
return
|
||||
}
|
||||
|
||||
client = cast(TCP_Socket)result
|
||||
source = _sockaddr_to_endpoint(&sockaddr)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_close :: proc(socket: Any_Socket) {
|
||||
real_socket := cast(Fd)any_socket_to_socket(socket)
|
||||
// TODO: This returns an error number, but the `core:net` interface does not handle it.
|
||||
_ = freebsd.close(real_socket)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
result, errno := freebsd.recv(cast(Fd)socket, buf, .NONE)
|
||||
if errno != nil {
|
||||
err = cast(TCP_Recv_Error)errno
|
||||
return
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
from: freebsd.Socket_Address_Storage
|
||||
|
||||
result, errno := freebsd.recvfrom(cast(Fd)socket, buf, .NONE, &from)
|
||||
if errno != nil {
|
||||
err = cast(UDP_Recv_Error)errno
|
||||
return
|
||||
}
|
||||
return result, _sockaddr_to_endpoint(&from), nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
|
||||
for bytes_written < len(buf) {
|
||||
limit := min(int(max(i32)), len(buf) - bytes_written)
|
||||
remaining := buf[bytes_written:][:limit]
|
||||
|
||||
result, errno := freebsd.send(cast(Fd)socket, remaining, .NONE)
|
||||
if errno != nil {
|
||||
err = cast(TCP_Send_Error)errno
|
||||
return
|
||||
}
|
||||
bytes_written += result
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
|
||||
toaddr := _endpoint_to_sockaddr(to)
|
||||
for bytes_written < len(buf) {
|
||||
limit := min(int(max(i32)), len(buf) - bytes_written)
|
||||
remaining := buf[bytes_written:][:limit]
|
||||
|
||||
result, errno := freebsd.sendto(cast(Fd)socket, remaining, .NONE, &toaddr)
|
||||
if errno != nil {
|
||||
err = cast(UDP_Send_Error)errno
|
||||
return
|
||||
}
|
||||
bytes_written += result
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
|
||||
real_socket := cast(Fd)any_socket_to_socket(socket)
|
||||
errno := freebsd.shutdown(real_socket, cast(freebsd.Shutdown_Method)manner)
|
||||
if errno != nil {
|
||||
return cast(Shutdown_Error)errno
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
|
||||
// NOTE(Feoramund): I found that FreeBSD, like Linux, requires at least 32
|
||||
// bits for a boolean socket option value. Nothing less will work.
|
||||
bool_value: b32
|
||||
// TODO: Assuming no larger than i32, but the system may accept i64.
|
||||
int_value: i32
|
||||
timeval_value: freebsd.timeval
|
||||
|
||||
ptr: rawptr
|
||||
len: freebsd.socklen_t
|
||||
|
||||
switch option {
|
||||
case
|
||||
.Reuse_Address,
|
||||
.Keep_Alive,
|
||||
.Broadcast,
|
||||
.Use_Loopback,
|
||||
.Out_Of_Bounds_Data_Inline,
|
||||
.Reuse_Port,
|
||||
.No_SIGPIPE_From_EPIPE,
|
||||
.Reuse_Port_Load_Balancing:
|
||||
switch real in value {
|
||||
case bool: bool_value = cast(b32)real
|
||||
case b8: bool_value = cast(b32)real
|
||||
case b16: bool_value = cast(b32)real
|
||||
case b32: bool_value = real
|
||||
case b64: bool_value = cast(b32)real
|
||||
case:
|
||||
panic("set_option() value must be a boolean here", loc)
|
||||
}
|
||||
ptr = &bool_value
|
||||
len = size_of(bool_value)
|
||||
case
|
||||
.Linger,
|
||||
.Send_Timeout,
|
||||
.Receive_Timeout:
|
||||
t, ok := value.(time.Duration)
|
||||
if !ok {
|
||||
panic("set_option() value must be a time.Duration here", loc)
|
||||
}
|
||||
|
||||
micros := cast(freebsd.time_t)time.duration_microseconds(t)
|
||||
timeval_value.usec = cast(freebsd.suseconds_t)micros % 1e6
|
||||
timeval_value.sec = (micros - cast(freebsd.time_t)timeval_value.usec) / 1e6
|
||||
|
||||
ptr = &timeval_value
|
||||
len = size_of(timeval_value)
|
||||
case
|
||||
.Receive_Buffer_Size,
|
||||
.Send_Buffer_Size:
|
||||
switch real in value {
|
||||
case i8: int_value = cast(i32)real
|
||||
case u8: int_value = cast(i32)real
|
||||
case i16: int_value = cast(i32)real
|
||||
case u16: int_value = cast(i32)real
|
||||
case i32: int_value = real
|
||||
case u32:
|
||||
if real > u32(max(i32)) { return .Value_Out_Of_Range }
|
||||
int_value = cast(i32)real
|
||||
case i64:
|
||||
if real > i64(max(i32)) || real < i64(min(i32)) { return .Value_Out_Of_Range }
|
||||
int_value = cast(i32)real
|
||||
case u64:
|
||||
if real > u64(max(i32)) { return .Value_Out_Of_Range }
|
||||
int_value = cast(i32)real
|
||||
case i128:
|
||||
if real > i128(max(i32)) || real < i128(min(i32)) { return .Value_Out_Of_Range }
|
||||
int_value = cast(i32)real
|
||||
case u128:
|
||||
if real > u128(max(i32)) { return .Value_Out_Of_Range }
|
||||
int_value = cast(i32)real
|
||||
case int:
|
||||
if real > int(max(i32)) || real < int(min(i32)) { return .Value_Out_Of_Range }
|
||||
int_value = cast(i32)real
|
||||
case uint:
|
||||
if real > uint(max(i32)) { return .Value_Out_Of_Range }
|
||||
int_value = cast(i32)real
|
||||
case:
|
||||
panic("set_option() value must be an integer here", loc)
|
||||
}
|
||||
ptr = &int_value
|
||||
len = size_of(int_value)
|
||||
case:
|
||||
unimplemented("set_option() option not yet implemented", loc)
|
||||
}
|
||||
|
||||
real_socket := any_socket_to_socket(socket)
|
||||
errno := freebsd.setsockopt(cast(Fd)real_socket, .SOCKET, cast(freebsd.Socket_Option)option, ptr, len)
|
||||
if errno != nil {
|
||||
return cast(Socket_Option_Error)errno
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
|
||||
real_socket := any_socket_to_socket(socket)
|
||||
|
||||
flags, errno := freebsd.fcntl_getfl(cast(freebsd.Fd)real_socket)
|
||||
if errno != nil {
|
||||
return cast(Set_Blocking_Error)errno
|
||||
}
|
||||
|
||||
if should_block {
|
||||
flags &= ~{ .NONBLOCK }
|
||||
} else {
|
||||
flags |= { .NONBLOCK }
|
||||
}
|
||||
|
||||
errno = freebsd.fcntl_setfl(cast(freebsd.Fd)real_socket, flags)
|
||||
if errno != nil {
|
||||
return cast(Set_Blocking_Error)errno
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: freebsd.Socket_Address_Storage) {
|
||||
switch addr in ep.address {
|
||||
case IP4_Address:
|
||||
(cast(^freebsd.Socket_Address_Internet)(&sockaddr))^ = {
|
||||
len = size_of(freebsd.Socket_Address_Internet),
|
||||
family = .INET,
|
||||
port = cast(freebsd.in_port_t)ep.port,
|
||||
addr = transmute(freebsd.IP4_Address)addr,
|
||||
}
|
||||
case IP6_Address:
|
||||
(cast(^freebsd.Socket_Address_Internet6)(&sockaddr))^ = {
|
||||
len = size_of(freebsd.Socket_Address_Internet),
|
||||
family = .INET6,
|
||||
port = cast(freebsd.in_port_t)ep.port,
|
||||
addr = transmute(freebsd.IP6_Address)addr,
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_sockaddr_to_endpoint :: proc(native_addr: ^freebsd.Socket_Address_Storage) -> (ep: Endpoint) {
|
||||
#partial switch native_addr.family {
|
||||
case .INET:
|
||||
addr := cast(^freebsd.Socket_Address_Internet)native_addr
|
||||
ep = {
|
||||
address = cast(IP4_Address)addr.addr.addr8,
|
||||
port = cast(int)addr.port,
|
||||
}
|
||||
case .INET6:
|
||||
addr := cast(^freebsd.Socket_Address_Internet6)native_addr
|
||||
ep = {
|
||||
address = cast(IP6_Address)addr.addr.addr16,
|
||||
port = cast(int)addr.port,
|
||||
}
|
||||
case:
|
||||
panic("native_addr is neither an IP4 or IP6 address")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
@@ -17,6 +18,7 @@ package net
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
flysand: Move dependency from core:os to core:sys/linux
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -10,12 +10,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -8,12 +8,14 @@ package net
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Tetralux: Initial implementation
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:strings"
|
||||
|
||||
@@ -3,35 +3,27 @@
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:sys/freebsd"
|
||||
import "core:time"
|
||||
|
||||
UMTX_OP_WAIT :: 2
|
||||
UMTX_OP_WAKE :: 3
|
||||
|
||||
ETIMEDOUT :: 60
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
foreign libc {
|
||||
_umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int ---
|
||||
__error :: proc "c" () -> ^c.int ---
|
||||
}
|
||||
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
timeout := [2]i64{14400, 0} // 4 hours
|
||||
for {
|
||||
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
|
||||
timeout := freebsd.timespec {14400, 0} // 4 hours
|
||||
timeout_size := cast(rawptr)cast(uintptr)size_of(timeout)
|
||||
|
||||
if res != -1 {
|
||||
for {
|
||||
errno := freebsd._umtx_op(f, .WAIT_UINT, cast(c.ulong)expected, timeout_size, &timeout)
|
||||
|
||||
if errno == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if __error()^ == ETIMEDOUT {
|
||||
if errno == .ETIMEDOUT {
|
||||
continue
|
||||
}
|
||||
|
||||
_panic("_futex_wait failure")
|
||||
}
|
||||
|
||||
unreachable()
|
||||
}
|
||||
|
||||
@@ -40,14 +32,15 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
|
||||
return false
|
||||
}
|
||||
|
||||
timeout := [2]i64{i64(duration/1e9), i64(duration%1e9)}
|
||||
timeout := freebsd.timespec {cast(freebsd.time_t)duration / 1e9, cast(c.long)duration % 1e9}
|
||||
timeout_size := cast(rawptr)cast(uintptr)size_of(timeout)
|
||||
|
||||
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
|
||||
if res != -1 {
|
||||
errno := freebsd._umtx_op(f, .WAIT_UINT, cast(c.ulong)expected, timeout_size, &timeout)
|
||||
if errno == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if __error()^ == ETIMEDOUT {
|
||||
if errno == .ETIMEDOUT {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -55,17 +48,17 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
|
||||
}
|
||||
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil)
|
||||
errno := freebsd._umtx_op(f, .WAKE, 1, nil, nil)
|
||||
|
||||
if res == -1 {
|
||||
if errno != nil {
|
||||
_panic("_futex_signal failure")
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil)
|
||||
errno := freebsd._umtx_op(f, .WAKE, cast(c.ulong)max(i32), nil, nil)
|
||||
|
||||
if res == -1 {
|
||||
if errno != nil {
|
||||
_panic("_futex_broadcast failure")
|
||||
}
|
||||
}
|
||||
|
||||
515
core/sys/freebsd/syscalls.odin
Normal file
515
core/sys/freebsd/syscalls.odin
Normal file
@@ -0,0 +1,515 @@
|
||||
package sys_freebsd
|
||||
|
||||
/*
|
||||
(c) Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Feoramund: Initial implementation.
|
||||
*/
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:c"
|
||||
|
||||
// FreeBSD 15 syscall numbers
|
||||
// See: https://alfonsosiciliano.gitlab.io/posts/2023-08-28-freebsd-15-system-calls.html
|
||||
|
||||
SYS_open : uintptr : 5
|
||||
SYS_close : uintptr : 6
|
||||
SYS_getpid : uintptr : 20
|
||||
SYS_recvfrom : uintptr : 29
|
||||
SYS_accept : uintptr : 30
|
||||
SYS_fcntl : uintptr : 92
|
||||
SYS_socket : uintptr : 97
|
||||
SYS_connect : uintptr : 98
|
||||
SYS_bind : uintptr : 104
|
||||
SYS_listen : uintptr : 106
|
||||
SYS_sendto : uintptr : 133
|
||||
SYS_shutdown : uintptr : 134
|
||||
SYS_setsockopt : uintptr : 105
|
||||
SYS_sysctl : uintptr : 202
|
||||
SYS__umtx_op : uintptr : 454
|
||||
SYS_accept4 : uintptr : 541
|
||||
|
||||
//
|
||||
// Odin syscall wrappers
|
||||
//
|
||||
|
||||
// Open or create a file for reading, writing or executing.
|
||||
//
|
||||
// The open() function appeared in Version 1 AT&T UNIX.
|
||||
// The openat() function was introduced in FreeBSD 8.0.
|
||||
open :: proc "contextless" (path: string, flags: File_Status_Flags, mode: int = 0o000) -> (Fd, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_open,
|
||||
cast(uintptr)raw_data(path),
|
||||
cast(uintptr)transmute(c.int)flags,
|
||||
cast(uintptr)mode)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(Fd)result, nil
|
||||
}
|
||||
|
||||
// Delete a descriptor.
|
||||
//
|
||||
// The open() function appeared in Version 1 AT&T UNIX.
|
||||
close :: proc "contextless" (fd: Fd) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_close,
|
||||
cast(uintptr)fd)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
// Get parent or calling process identification.
|
||||
//
|
||||
// The getpid() function appeared in Version 7 AT&T UNIX.
|
||||
getpid :: proc "contextless" () -> pid_t {
|
||||
// This always succeeds.
|
||||
result, _ := intrinsics.syscall_bsd(SYS_getpid)
|
||||
return cast(pid_t)result
|
||||
}
|
||||
|
||||
// Receive message(s) from a socket.
|
||||
//
|
||||
// The recv() function appeared in 4.2BSD.
|
||||
// The recvmmsg() function appeared in FreeBSD 11.0.
|
||||
recvfrom :: proc "contextless" (s: Fd, buf: []u8, flags: Recv_Flags, from: ^$T) -> (int, Errno)
|
||||
where
|
||||
intrinsics.type_is_subtype_of(T, Socket_Address_Header)
|
||||
{
|
||||
fromlen: socklen_t = size_of(T)
|
||||
|
||||
result, ok := intrinsics.syscall_bsd(SYS_recvfrom,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)raw_data(buf),
|
||||
cast(uintptr)len(buf),
|
||||
cast(uintptr)flags,
|
||||
cast(uintptr)from,
|
||||
cast(uintptr)&fromlen)
|
||||
|
||||
// `from.len` will be modified by the syscall, so we shouldn't need to pass
|
||||
// `fromlen` back from this API.
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(int)result, nil
|
||||
}
|
||||
|
||||
// Receive message(s) from a socket.
|
||||
//
|
||||
// The recv() function appeared in 4.2BSD.
|
||||
// The recvmmsg() function appeared in FreeBSD 11.0.
|
||||
recv :: proc "contextless" (s: Fd, buf: []u8, flags: Recv_Flags) -> (int, Errno) {
|
||||
// This is a wrapper over recvfrom().
|
||||
result, ok := intrinsics.syscall_bsd(SYS_recvfrom,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)raw_data(buf),
|
||||
cast(uintptr)len(buf),
|
||||
cast(uintptr)flags,
|
||||
0,
|
||||
0)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(int)result, nil
|
||||
}
|
||||
|
||||
// Accept a connection on a socket.
|
||||
//
|
||||
// The accept() system call appeared in 4.2BSD.
|
||||
accept_T :: proc "contextless" (s: Fd, sockaddr: ^$T) -> (Fd, Errno)
|
||||
where
|
||||
intrinsics.type_is_subtype_of(T, Socket_Address_Header)
|
||||
{
|
||||
// sockaddr must contain a valid pointer, or this will segfault because
|
||||
// we're telling the syscall that there's memory available to write to.
|
||||
addrlen: socklen_t = size_of(T)
|
||||
|
||||
result, ok := intrinsics.syscall_bsd(SYS_accept,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)sockaddr,
|
||||
cast(uintptr)&addrlen)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
sockaddr.len = cast(u8)addrlen
|
||||
|
||||
return cast(Fd)result, nil
|
||||
}
|
||||
|
||||
|
||||
// Accept a connection on a socket.
|
||||
//
|
||||
// The accept() system call appeared in 4.2BSD.
|
||||
accept_nil :: proc "contextless" (s: Fd) -> (Fd, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_accept,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)0,
|
||||
cast(uintptr)0)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(Fd)result, nil
|
||||
}
|
||||
|
||||
accept :: proc { accept_T, accept_nil }
|
||||
|
||||
// File control.
|
||||
//
|
||||
// The fcntl() system call appeared in 4.2BSD.
|
||||
// The F_DUP2FD constant first appeared in FreeBSD 7.1.
|
||||
//
|
||||
// NOTE: If you know at compile-time what command you're calling, use one of the
|
||||
// `fcntl_*` procedures instead to preserve type safety.
|
||||
fcntl :: proc "contextless" (fd: Fd, cmd: File_Control_Command, arg: c.int) -> (int, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)cmd,
|
||||
cast(uintptr)arg)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(int)result, nil
|
||||
}
|
||||
|
||||
// TODO: Implement more type-safe fcntl commands.
|
||||
|
||||
fcntl_dupfd :: proc "contextless" (fd: Fd, newfd: Fd) -> (Fd, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.DUPFD,
|
||||
cast(uintptr)newfd)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(Fd)result, nil
|
||||
}
|
||||
|
||||
fcntl_getfd :: proc "contextless" (fd: Fd) -> (bool, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.GETFD)
|
||||
|
||||
if !ok {
|
||||
return false, cast(Errno)result
|
||||
}
|
||||
|
||||
return result & FD_CLOEXEC > 0, nil
|
||||
}
|
||||
|
||||
fcntl_setfd :: proc "contextless" (fd: Fd, close_on_exec: bool) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.SETFD,
|
||||
(close_on_exec ? FD_CLOEXEC : 0))
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
fcntl_getfl :: proc "contextless" (fd: Fd) -> (File_Status_Flags, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.GETFL)
|
||||
|
||||
if !ok {
|
||||
return nil, cast(Errno)result
|
||||
}
|
||||
|
||||
return transmute(File_Status_Flags)cast(c.int)result, nil
|
||||
}
|
||||
|
||||
fcntl_setfl :: proc "contextless" (fd: Fd, flags: File_Status_Flags) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.SETFL,
|
||||
cast(uintptr)transmute(c.int)flags)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
fcntl_getown :: proc "contextless" (fd: Fd) -> (pid_t, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.GETOWN)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(pid_t)result, nil
|
||||
}
|
||||
|
||||
fcntl_setown :: proc "contextless" (fd: Fd, pid: pid_t) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.SETOWN,
|
||||
cast(uintptr)pid)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
fcntl_getlk :: proc "contextless" (fd: Fd, flock: ^File_Lock) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.GETLK,
|
||||
cast(uintptr)flock)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
fcntl_setlk :: proc "contextless" (fd: Fd, flock: ^File_Lock) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.SETLK,
|
||||
cast(uintptr)flock)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
fcntl_add_seals :: proc "contextless" (fd: Fd, seals: File_Seals) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.ADD_SEALS,
|
||||
cast(uintptr)transmute(c.int)seals)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
fcntl_get_seals :: proc "contextless" (fd: Fd) -> (File_Seals, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_fcntl,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)File_Control_Command.GET_SEALS)
|
||||
|
||||
if !ok {
|
||||
return nil, cast(Errno)result
|
||||
}
|
||||
|
||||
return transmute(File_Seals)cast(c.int)result, nil
|
||||
}
|
||||
|
||||
//
|
||||
// End type-safe fcntl commands.
|
||||
//
|
||||
|
||||
// Create an endpoint for communication.
|
||||
//
|
||||
// The socket() system call appeared in 4.2BSD.
|
||||
socket :: proc "contextless" (domain: Protocol_Family, type: Socket_Type, protocol: Protocol) -> (Fd, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_socket,
|
||||
cast(uintptr)domain,
|
||||
cast(uintptr)type,
|
||||
cast(uintptr)protocol)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(Fd)result, nil
|
||||
}
|
||||
|
||||
// Initiate a connection on a socket.
|
||||
//
|
||||
// The connect() system call appeared in 4.2BSD.
|
||||
connect :: proc "contextless" (fd: Fd, sockaddr: ^$T, addrlen: socklen_t) -> Errno
|
||||
where
|
||||
intrinsics.type_is_subtype_of(T, Socket_Address_Header)
|
||||
{
|
||||
result, _ := intrinsics.syscall_bsd(SYS_connect,
|
||||
cast(uintptr)fd,
|
||||
cast(uintptr)sockaddr,
|
||||
cast(uintptr)addrlen)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
|
||||
// Assign a local protocol address to a socket.
|
||||
//
|
||||
// The bind() system call appeared in 4.2BSD.
|
||||
bind :: proc "contextless" (s: Fd, sockaddr: ^$T, addrlen: socklen_t) -> Errno
|
||||
where
|
||||
intrinsics.type_is_subtype_of(T, Socket_Address_Header)
|
||||
{
|
||||
result, _ := intrinsics.syscall_bsd(SYS_bind,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)sockaddr,
|
||||
cast(uintptr)addrlen)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
// Listen for connections on a socket.
|
||||
//
|
||||
// The listen() system call appeared in 4.2BSD.
|
||||
listen :: proc "contextless" (s: Fd, backlog: int) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_listen,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)backlog)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
// Send message(s) from a socket.
|
||||
//
|
||||
// The send() function appeared in 4.2BSD.
|
||||
// The sendmmsg() function appeared in FreeBSD 11.0.
|
||||
sendto :: proc "contextless" (s: Fd, msg: []u8, flags: Send_Flags, to: ^$T) -> (int, Errno)
|
||||
where
|
||||
intrinsics.type_is_subtype_of(T, Socket_Address_Header)
|
||||
{
|
||||
result, ok := intrinsics.syscall_bsd(SYS_sendto,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)raw_data(msg),
|
||||
cast(uintptr)len(msg),
|
||||
cast(uintptr)flags,
|
||||
cast(uintptr)to,
|
||||
cast(uintptr)to.len)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(int)result, nil
|
||||
}
|
||||
|
||||
// Send message(s) from a socket.
|
||||
//
|
||||
// The send() function appeared in 4.2BSD.
|
||||
// The sendmmsg() function appeared in FreeBSD 11.0.
|
||||
send :: proc "contextless" (s: Fd, msg: []u8, flags: Send_Flags) -> (int, Errno) {
|
||||
// This is a wrapper over sendto().
|
||||
result, ok := intrinsics.syscall_bsd(SYS_sendto,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)raw_data(msg),
|
||||
cast(uintptr)len(msg),
|
||||
cast(uintptr)flags,
|
||||
0,
|
||||
0)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(int)result, nil
|
||||
}
|
||||
|
||||
// Disable sends and/or receives on a socket.
|
||||
//
|
||||
// The shutdown() system call appeared in 4.2BSD.
|
||||
shutdown :: proc "contextless" (s: Fd, how: Shutdown_Method) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_shutdown,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)how)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
// Get and set options on sockets.
|
||||
//
|
||||
// The getsockopt() and setsockopt() system calls appeared in 4.2BSD.
|
||||
setsockopt :: proc "contextless" (s: Fd, level: Valid_Socket_Option_Level, optname: Socket_Option, optval: rawptr, optlen: socklen_t) -> Errno {
|
||||
real_level: uintptr
|
||||
switch which in level {
|
||||
case Protocol_Family: real_level = cast(uintptr)which
|
||||
case Socket_Option_Level: real_level = cast(uintptr)which
|
||||
}
|
||||
|
||||
result, _ := intrinsics.syscall_bsd(SYS_setsockopt,
|
||||
cast(uintptr)s,
|
||||
real_level,
|
||||
cast(uintptr)optname,
|
||||
cast(uintptr)optval,
|
||||
cast(uintptr)optlen)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
// Get or set system information.
|
||||
//
|
||||
// The sysctl() function first appeared in 4.4BSD.
|
||||
sysctl :: proc "contextless" (mib: []MIB_Identifier, oldp: rawptr, oldlenp: ^c.size_t, newp: rawptr, newlen: c.size_t) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS_sysctl,
|
||||
cast(uintptr)raw_data(mib),
|
||||
cast(uintptr)len(mib),
|
||||
cast(uintptr)oldp,
|
||||
cast(uintptr)oldlenp,
|
||||
cast(uintptr)newp,
|
||||
cast(uintptr)newlen)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
// Interface for implementation of userspace threading synchronization primitives.
|
||||
//
|
||||
// The _umtx_op() system call is non-standard and is used by the 1:1 Threading
|
||||
// Library (libthr, -lthr) to implement IEEE Std 1003.1-2001 (“POSIX.1”)
|
||||
// pthread(3) functionality.
|
||||
_umtx_op :: proc "contextless" (obj: rawptr, op: Userland_Mutex_Operation, val: c.ulong, uaddr, uaddr2: rawptr) -> Errno {
|
||||
result, _ := intrinsics.syscall_bsd(SYS__umtx_op,
|
||||
cast(uintptr)obj,
|
||||
cast(uintptr)op,
|
||||
cast(uintptr)val,
|
||||
cast(uintptr)uaddr,
|
||||
cast(uintptr)uaddr2)
|
||||
|
||||
return cast(Errno)result
|
||||
}
|
||||
|
||||
// Accept a connection on a socket.
|
||||
//
|
||||
// The accept4() system call appeared in FreeBSD 10.0.
|
||||
accept4_T :: proc "contextless" (s: Fd, sockaddr: ^$T, flags: Socket_Flags = {}) -> (Fd, Errno)
|
||||
where
|
||||
intrinsics.type_is_subtype_of(T, Socket_Address_Header)
|
||||
{
|
||||
// `sockaddr` must contain a valid pointer, or this will segfault because
|
||||
// we're telling the syscall that there's memory available to write to.
|
||||
addrlen: u32 = size_of(T)
|
||||
|
||||
result, ok := intrinsics.syscall_bsd(SYS_accept4,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)sockaddr,
|
||||
cast(uintptr)&addrlen,
|
||||
cast(uintptr)transmute(c.int)flags)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
sockaddr.len = cast(u8)addrlen
|
||||
|
||||
return cast(Fd)result, nil
|
||||
}
|
||||
|
||||
// Accept a connection on a socket.
|
||||
//
|
||||
// The accept4() system call appeared in FreeBSD 10.0.
|
||||
accept4_nil :: proc "contextless" (s: Fd, flags: Socket_Flags = {}) -> (Fd, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_accept4,
|
||||
cast(uintptr)s,
|
||||
cast(uintptr)0,
|
||||
cast(uintptr)0,
|
||||
cast(uintptr)transmute(c.int)flags)
|
||||
|
||||
if !ok {
|
||||
return 0, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(Fd)result, nil
|
||||
}
|
||||
|
||||
accept4 :: proc { accept4_nil, accept4_T }
|
||||
1587
core/sys/freebsd/types.odin
Normal file
1587
core/sys/freebsd/types.odin
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,16 @@
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
graphitemaster: pton/ntop IANA test vectors
|
||||
Feoramund: FreeBSD-specific tests.
|
||||
|
||||
A test suite for `core:net`
|
||||
*/
|
||||
//+build !netbsd !freebsd !openbsd
|
||||
//+build !netbsd !openbsd
|
||||
package test_core_net
|
||||
|
||||
import "core:testing"
|
||||
|
||||
89
tests/core/net/test_core_net_freebsd.odin
Normal file
89
tests/core/net/test_core_net_freebsd.odin
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
graphitemaster: pton/ntop IANA test vectors
|
||||
Feoramund: FreeBSD-specific tests.
|
||||
|
||||
A test suite for `core:net`
|
||||
*/
|
||||
//+build freebsd
|
||||
package test_core_net
|
||||
|
||||
import "core:net"
|
||||
import "core:time"
|
||||
import "core:testing"
|
||||
|
||||
ENDPOINT_DUPLICATE_BINDING := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 11000}
|
||||
ENDPOINT_EPIPE_TEST := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 11001}
|
||||
|
||||
@test
|
||||
test_duplicate_binding :: proc(t: ^testing.T) {
|
||||
// FreeBSD has the capacity to permit multiple processes and sockets to
|
||||
// bind on the same port with the right option.
|
||||
|
||||
raw_socket1, err_create1 := net.create_socket(.IP4, .TCP)
|
||||
if !testing.expect_value(t, err_create1, nil) {
|
||||
return
|
||||
}
|
||||
defer net.close(raw_socket1)
|
||||
tcp_socket1 := raw_socket1.(net.TCP_Socket)
|
||||
err_set1 := net.set_option(tcp_socket1, .Reuse_Port, true)
|
||||
if !testing.expect_value(t, err_set1, nil) {
|
||||
return
|
||||
}
|
||||
err_bind1 := net.bind(tcp_socket1, ENDPOINT_DUPLICATE_BINDING)
|
||||
if !testing.expect_value(t, err_bind1, nil) {
|
||||
return
|
||||
}
|
||||
|
||||
raw_socket2, err_create2 := net.create_socket(.IP4, .TCP)
|
||||
if !testing.expect_value(t, err_create2, nil) {
|
||||
return
|
||||
}
|
||||
defer net.close(raw_socket2)
|
||||
tcp_socket2 := raw_socket2.(net.TCP_Socket)
|
||||
err_set2 := net.set_option(tcp_socket2, .Reuse_Port, true)
|
||||
if !testing.expect_value(t, err_set2, nil) {
|
||||
return
|
||||
}
|
||||
err_bind2 := net.bind(tcp_socket2, ENDPOINT_DUPLICATE_BINDING)
|
||||
if !testing.expect_value(t, err_bind2, nil) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_sigpipe_bypass :: proc(t: ^testing.T) {
|
||||
// If the internals aren't working as expected, this test will fail by raising SIGPIPE.
|
||||
|
||||
server_socket, listen_err := net.listen_tcp(ENDPOINT_EPIPE_TEST)
|
||||
if !testing.expect_value(t, listen_err, nil) {
|
||||
return
|
||||
}
|
||||
defer net.close(server_socket)
|
||||
|
||||
client_socket, dial_err := net.dial_tcp(ENDPOINT_EPIPE_TEST)
|
||||
if !testing.expect_value(t, dial_err, nil) {
|
||||
return
|
||||
}
|
||||
defer net.close(client_socket)
|
||||
|
||||
time.sleep(10 * time.Millisecond)
|
||||
|
||||
net.close(server_socket)
|
||||
|
||||
time.sleep(10 * time.Millisecond)
|
||||
|
||||
data := "Hellope!"
|
||||
bytes_written, err_send := net.send(client_socket, transmute([]u8)data)
|
||||
if !testing.expect_value(t, err_send, net.TCP_Send_Error.Cannot_Send_More_Data) {
|
||||
return
|
||||
}
|
||||
if !testing.expect_value(t, bytes_written, 0) {
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user