mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
Port core:net to FreeBSD
This commit is contained in:
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
// +build windows, linux, darwin
|
// +build windows, linux, darwin, freebsd
|
||||||
package net
|
package net
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -743,4 +743,4 @@ parse_ip_component :: proc(input: string, max_value := u64(max(u32)), bases := D
|
|||||||
get_network_interfaces :: proc() -> []Address {
|
get_network_interfaces :: proc() -> []Address {
|
||||||
// TODO: Implement using `enumerate_interfaces` and returning only the addresses of active interfaces.
|
// TODO: Implement using `enumerate_interfaces` and returning only the addresses of active interfaces.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build windows, linux, darwin
|
// +build windows, linux, darwin, freebsd
|
||||||
package net
|
package net
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -413,4 +413,4 @@ DNS_Record_Header :: struct #packed {
|
|||||||
DNS_Host_Entry :: struct {
|
DNS_Host_Entry :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
addr: Address,
|
addr: Address,
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
// +build windows, linux, darwin
|
// +build windows, linux, darwin, freebsd
|
||||||
package net
|
package net
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -30,7 +30,7 @@ when ODIN_OS == .Windows {
|
|||||||
resolv_conf = "",
|
resolv_conf = "",
|
||||||
hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts",
|
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{
|
DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{
|
||||||
resolv_conf = "/etc/resolv.conf",
|
resolv_conf = "/etc/resolv.conf",
|
||||||
hosts_file = "/etc/hosts",
|
hosts_file = "/etc/hosts",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build linux, darwin
|
//+build linux, darwin, freebsd
|
||||||
package net
|
package net
|
||||||
/*
|
/*
|
||||||
Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
|
Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
//+build freebsd
|
||||||
|
package net
|
||||||
|
|
||||||
|
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,
|
||||||
|
Insufficient_Buffer_Space = 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.
|
||||||
|
Refused = cast(c.int)freebsd.Errno.ECONNREFUSED,
|
||||||
|
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,
|
||||||
|
|
||||||
|
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,
|
||||||
|
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.
|
||||||
|
/* The system was unable to allocate an internal buffer.
|
||||||
|
The operation may succeed when buffers become available. */
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
Cannot_Install_Accept_Filter_On_Non_Listening_Socket = 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,
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build windows, linux, darwin
|
// +build windows, linux, darwin, freebsd
|
||||||
package net
|
package net
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
//+build freebsd
|
||||||
|
package net
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build windows, linux, darwin
|
// +build windows, linux, darwin, freebsd
|
||||||
package net
|
package net
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -0,0 +1,365 @@
|
|||||||
|
//+build freebsd
|
||||||
|
package net
|
||||||
|
|
||||||
|
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
|
||||||
|
// ACCEPTCONN
|
||||||
|
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,
|
||||||
|
// REUSEPORT
|
||||||
|
// TIMESTAMP
|
||||||
|
// NOSIGPIPE
|
||||||
|
// ACCEPTFILTER
|
||||||
|
// BINTIME
|
||||||
|
// NO_OFFLOAD
|
||||||
|
// NO_DDP
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 cast(int)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 cast(int)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 += cast(int)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 += cast(int)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
|
||||||
|
|
||||||
|
// TODO: Verify that these options perform adequately.
|
||||||
|
switch option {
|
||||||
|
case
|
||||||
|
.Reuse_Address,
|
||||||
|
.Keep_Alive,
|
||||||
|
.Broadcast,
|
||||||
|
.Use_Loopback,
|
||||||
|
.Out_Of_Bounds_Data_Inline:
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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 = transmute(IP4_Address)addr.addr.addr8,
|
||||||
|
port = cast(int)addr.port,
|
||||||
|
}
|
||||||
|
case .INET6:
|
||||||
|
addr := cast(^freebsd.Socket_Address_Internet6)native_addr
|
||||||
|
ep = {
|
||||||
|
address = transmute(IP6_Address)addr.addr.addr16,
|
||||||
|
port = cast(int)addr.port,
|
||||||
|
}
|
||||||
|
case:
|
||||||
|
panic("native_addr is neither an IP4 or IP6 address")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user