Files
Odin/core/sys/posix/sys_socket.odin
Harold Brenes 219b0fe535 Replace system:System.framework imports with system:System
This makes the linker work for both macOS and iOS targets
2025-07-13 15:45:21 -04:00

619 lines
16 KiB
Odin

#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import libc "system:System"
} else {
foreign import libc "system:c"
}
// sys/socket.h - main sockets header
#assert(Protocol.IP == Protocol(0), "socket() assumes this")
foreign libc {
/*
Creates a socket.
Returns: -1 (setting errno) on failure, file descriptor of socket otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html ]]
*/
@(link_name=LSOCKET)
socket :: proc(domain: AF, type: Sock, protocol: Protocol = .IP) -> FD ---
/*
Extracts the first connection on the queue of pending connections.
Blocks (if not O_NONBLOCK) if there is no pending connection.
Returns: -1 (setting errno) on failure, file descriptor of accepted socket otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html ]]
*/
accept :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> FD ---
/*
Assigns a local socket address to the socket.
Example:
sfd := posix.socket(.UNIX, .STREAM)
if sfd == -1 {
/* Handle error */
}
addr: posix.sockaddr_un
addr.sun_family = .UNIX
copy(addr.sun_path[:], "/somepath\x00")
/*
unlink the socket before binding in case
of previous runs not cleaning up the socket
*/
posix.unlink("/somepath")
if posix.bind(sfd, (^posix.sockaddr)(&addr), size_of(addr)) != .OK {
/* Handle error */
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html ]]
*/
bind :: proc(socket: FD, address: ^sockaddr, address_len: socklen_t) -> result ---
/*
Attempt to make a connection.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html ]]
*/
connect :: proc(socket: FD, address: ^sockaddr, address_len: socklen_t) -> result ---
/*
Get the peer address of the specified socket.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html ]]
*/
getpeername :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> result ---
/*
Get the socket name.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html ]]
*/
getsockname :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> result ---
/*
Retrieves the value for the option specified by option_name.
level: either `c.int(posix.Protocol(...))` to specify a protocol level or `posix.SOL_SOCKET`
to specify the socket local level.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html ]]
*/
getsockopt :: proc(
socket: FD,
level: c.int,
option_name: Sock_Option,
option_value: rawptr,
option_len: ^socklen_t,
) -> result ---
/*
Sets the specified option.
level: either `c.int(posix.Protocol(...))` to specify a protocol level or `posix.SOL_SOCKET`
to specify the socket local level.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html ]]
*/
setsockopt :: proc(
socket: FD,
level: c.int,
option_name: Sock_Option,
option_value: rawptr,
option_len: socklen_t,
) -> result ---
/*
Mark the socket as a socket accepting connections.
backlog provides a hint to limit the number of connections on the listen queue.
Implementation may silently reduce the backlog, additionally `SOMAXCONN` specifies the maximum
an implementation has to support.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html ]]
*/
listen :: proc(socket: FD, backlog: c.int) -> result ---
/*
Receives a message from a socket.
Blocks (besides with O_NONBLOCK) if there is nothing to receive.
Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html ]]
*/
recv :: proc(socket: FD, buffer: rawptr, length: c.size_t, flags: Msg_Flags) -> c.ssize_t ---
/*
Receives a message from a socket.
Equivalent to recv() but retrieves the source address too.
Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html ]]
*/
recvfrom :: proc(
socket: FD,
buffer: rawptr,
length: c.size_t,
flags: Msg_Flags,
address: ^sockaddr,
address_len: ^socklen_t,
) -> c.ssize_t ---
/*
Receives a message from a socket.
Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html ]]
*/
recvmsg :: proc(socket: FD, message: ^msghdr, flags: Msg_Flags) -> c.ssize_t ---
/*
Sends a message on a socket.
Returns: -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html ]]
*/
send :: proc(socket: FD, buffer: rawptr, length: c.size_t, flags: Msg_Flags) -> c.ssize_t ---
/*
Sends a message on a socket.
Returns: -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html ]]
*/
sendmsg :: proc(socket: FD, message: ^msghdr, flags: Msg_Flags) -> c.ssize_t ---
/*
Sends a message on a socket.
If the socket is connectionless, the dest_addr is used to send to.
Returns: -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html ]]
*/
sendto :: proc(
socket: FD,
message: rawptr,
length: c.size_t,
flags: Msg_Flags,
dest_addr: ^sockaddr,
dest_len: socklen_t,
) -> c.ssize_t ---
/*
Shuts down a socket end or both.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html ]]
*/
shutdown :: proc(socket: FD, how: Shut) -> result ---
/*
Determine wheter a socket is at the out-of-band mark.
Returns: -1 (setting errno) on failure, 0 if not at the mark, 1 if it is
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sockatmark.html ]]
*/
sockatmark :: proc(socket: FD) -> c.int ---
/*
Create a pair of connected sockets.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html ]]
*/
socketpair :: proc(domain: AF, type: Sock, protocol: Protocol, socket_vector: ^[2]FD) -> result ---
}
AF_UNSPEC :: 0
AF :: enum c.int {
// Unspecified.
UNSPEC = AF_UNSPEC,
// Internet domain sockets for use with IPv4 addresses.
INET = AF_INET,
// Internet domain sockets for use with IPv6 addresses.
INET6 = AF_INET6,
// UNIX domain sockets.
UNIX = AF_UNIX,
}
sa_family_t :: enum _sa_family_t {
// Unspecified.
UNSPEC = AF_UNSPEC,
// Internet domain sockets for use with IPv4 addresses.
INET = AF_INET,
// Internet domain sockets for use with IPv6 addresses.
INET6 = AF_INET6,
// UNIX domain sockets.
UNIX = AF_UNIX,
}
Sock :: enum c.int {
// Datagram socket.
DGRAM = SOCK_DGRAM,
// Raw Protocol Interface.
RAW = SOCK_RAW,
// Sequenced-packet socket.
SEQPACKET = SOCK_SEQPACKET,
// Byte-stream socket.
STREAM = SOCK_STREAM,
}
Shut :: enum c.int {
// Disables further receive operations.
RD = SHUT_RD,
// Disables further send and receive operations.
RDWR = SHUT_RDWR,
// Disables further send operations.
WR = SHUT_WR,
}
Msg_Flag_Bits :: enum c.int {
// Control data truncated.
CTRUNC = log2(MSG_CTRUNC),
// Send without using routing table.
DONTROUTE = log2(MSG_DONTROUTE),
// Terminates a record (if supported by protocol).
EOR = log2(MSG_EOR),
// Out-of-band data.
OOB = log2(MSG_OOB),
// No SIGPIPE is generated when an attempt to send is made on a stream-oriented socket that is
// no longer connected.
NOSIGNAL = log2(MSG_NOSIGNAL),
// Leave received data in queue.
PEEK = log2(MSG_PEEK),
// Normal data truncated.
TRUNC = log2(MSG_TRUNC),
// Attempt to fill the read buffer.
WAITALL = log2(MSG_WAITALL),
}
Msg_Flags :: bit_set[Msg_Flag_Bits; c.int]
Sock_Option :: enum c.int {
// Transmission of broadcast message is supported.
BROADCAST = SO_BROADCAST,
// Debugging information is being recorded.
DEBUG = SO_DEBUG,
// Bypass normal routing.
DONTROUTE = SO_DONTROUTE,
// Socket error status.
ERROR = SO_ERROR,
// Connections are kept alive with periodic messages.
KEEPALIVE = SO_KEEPALIVE,
// Socket lingers on close.
LINGER = SO_LINGER,
// Out-of-band data is transmitted in line.
OOBINLINE = SO_OOBINLINE,
// Receive buffer size.
RCVBUF = SO_RCVBUF,
// Receive low water mark.
RCVLOWAT = SO_RCVLOWAT,
// Receive timeout.
RCVTIMEO = SO_RCVTIMEO,
// Reuse of local addresses is supported.
REUSEADDR = SO_REUSEADDR,
// Send buffer size.
SNDBUF = SO_SNDBUF,
// Send low water mark.
SNDLOWAT = SO_SNDLOWAT,
// Send timeout.
SNDTIMEO = SO_SNDTIMEO,
// Socket type.
TYPE = SO_TYPE,
}
when ODIN_OS == .NetBSD {
@(private) LSOCKET :: "__socket30"
} else {
@(private) LSOCKET :: "socket"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
socklen_t :: distinct c.uint
when ODIN_OS == .Haiku {
@(private)
_SA_DATASIZE :: 30
} else {
@(private)
_SA_DATASIZE :: 14
}
when ODIN_OS == .Linux {
_sa_family_t :: distinct c.ushort
sockaddr :: struct {
sa_family: sa_family_t, /* [PSX] address family */
sa_data: [_SA_DATASIZE]c.char, /* [PSX] socket address */
}
} else {
_sa_family_t :: distinct c.uint8_t
sockaddr :: struct {
sa_len: c.uint8_t, /* total length */
sa_family: sa_family_t, /* [PSX] address family */
sa_data: [_SA_DATASIZE]c.char, /* [PSX] socket address */
}
}
when ODIN_OS == .OpenBSD {
@(private)
_SS_PAD1SIZE :: 6
@(private)
_SS_PAD2SIZE :: 240
} else when ODIN_OS == .Haiku {
@(private)
_SS_PAD1SIZE :: 6
@(private)
_SS_PAD2SIZE :: 112
} else when ODIN_OS == .Linux {
@(private)
_SS_SIZE :: 128
@(private)
_SS_PADSIZE :: _SS_SIZE - size_of(c.uint16_t) - size_of(c.uint64_t)
} else {
@(private)
_SS_MAXSIZE :: 128
@(private)
_SS_ALIGNSIZE :: size_of(c.int64_t)
@(private)
_SS_PAD1SIZE :: _SS_ALIGNSIZE - size_of(c.uint8_t) - size_of(sa_family_t)
@(private)
_SS_PAD2SIZE :: _SS_MAXSIZE - size_of(c.uint8_t) - size_of(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE
}
when ODIN_OS == .Linux {
sockaddr_storage :: struct {
ss_family: sa_family_t, /* [PSX] address family */
__ss_padding: [_SS_PADSIZE]c.char,
__ss_align: c.uint64_t, /* force structure storage alignment */
}
msghdr :: struct {
msg_name: rawptr, /* [PSX] optional address */
msg_namelen: socklen_t, /* [PSX] size of address */
msg_iov: [^]iovec, /* [PSX] scatter/gather array */
msg_iovlen: c.size_t, /* [PSX] members in msg_iov */
msg_control: rawptr, /* [PSX] ancillary data */
msg_controllen: c.size_t, /* [PSX] ancillary data buffer length */
msg_flags: Msg_Flags, /* [PSX] flags on received message */
}
cmsghdr :: struct {
cmsg_len: c.size_t, /* [PSX] data byte count, including cmsghdr */
cmsg_level: c.int, /* [PSX] originating protocol */
cmsg_type: c.int, /* [PSX] protocol-specific type */
}
} else {
sockaddr_storage :: struct {
ss_len: c.uint8_t, /* address length */
ss_family: sa_family_t, /* [PSX] address family */
__ss_pad1: [_SS_PAD1SIZE]c.char,
__ss_align: c.int64_t, /* force structure storage alignment */
__ss_pad2: [_SS_PAD2SIZE]c.char,
}
msghdr :: struct {
msg_name: rawptr, /* [PSX] optional address */
msg_namelen: socklen_t, /* [PSX] size of address */
msg_iov: [^]iovec, /* [PSX] scatter/gather array */
msg_iovlen: c.int, /* [PSX] members in msg_iov */
msg_control: rawptr, /* [PSX] ancillary data */
msg_controllen: socklen_t, /* [PSX] ancillary data buffer length */
msg_flags: Msg_Flags, /* [PSX] flags on received message */
}
cmsghdr :: struct {
cmsg_len: socklen_t, /* [PSX] data byte count, including cmsghdr */
cmsg_level: c.int, /* [PSX] originating protocol */
cmsg_type: c.int, /* [PSX] protocol-specific type */
}
}
SCM_RIGHTS :: 0x01
@(private)
__ALIGN32 :: #force_inline proc "contextless" (p: uintptr) -> uintptr {
__ALIGNBYTES32 :: size_of(c.uint32_t) - 1
return (p + __ALIGNBYTES32) &~ __ALIGNBYTES32
}
// Returns a pointer to the data array.
CMSG_DATA :: #force_inline proc "contextless" (cmsg: ^cmsghdr) -> [^]c.uchar {
return ([^]c.uchar)(uintptr(cmsg) + __ALIGN32(size_of(cmsghdr)))
}
// Returns a pointer to the next cmsghdr or nil.
CMSG_NXTHDR :: #force_inline proc "contextless" (mhdr: ^msghdr, cmsg: ^cmsghdr) -> ^cmsghdr {
if cmsg == nil {
return CMSG_FIRSTHDR(mhdr)
}
ptr := uintptr(cmsg) + __ALIGN32(uintptr(cmsg.cmsg_len))
if ptr + __ALIGN32(size_of(cmsghdr)) > uintptr(mhdr.msg_control) + uintptr(mhdr.msg_controllen) {
return nil
}
return (^cmsghdr)(ptr)
}
// Returns a pointer to the first cmsghdr or nil.
CMSG_FIRSTHDR :: #force_inline proc "contextless" (mhdr: ^msghdr) -> ^cmsghdr {
if mhdr.msg_controllen >= size_of(cmsghdr) {
return (^cmsghdr)(mhdr.msg_control)
}
return nil
}
linger :: struct {
l_onoff: c.int, /* [PSX] indicates whether linger option is enabled */
l_linger: c.int, /* [PSX] linger time in seconds */
}
SOCK_DGRAM :: 2
SOCK_RAW :: 3
SOCK_SEQPACKET :: 5
SOCK_STREAM :: 1
// Options to be accessed at socket level, not protocol level.
when ODIN_OS == .Linux {
SOL_SOCKET :: 1
SO_ACCEPTCONN :: 30
SO_BROADCAST :: 6
SO_DEBUG :: 1
SO_DONTROUTE :: 5
SO_ERROR :: 4
SO_KEEPALIVE :: 9
SO_OOBINLINE :: 10
SO_RCVBUF :: 8
SO_RCVLOWAT :: 18
SO_REUSEADDR :: 2
SO_SNDBUF :: 7
SO_SNDLOWAT :: 19
SO_TYPE :: 3
SO_LINGER :: 13
SO_RCVTIMEO :: 66
SO_SNDTIMEO :: 67
} else when ODIN_OS == .Haiku {
SOL_SOCKET :: -1
SO_ACCEPTCONN :: 0x00000001
SO_BROADCAST :: 0x00000002
SO_DEBUG :: 0x00000004
SO_DONTROUTE :: 0x00000008
SO_ERROR :: 0x40000007
SO_KEEPALIVE :: 0x00000010
SO_OOBINLINE :: 0x00000020
SO_RCVBUF :: 0x40000004
SO_RCVLOWAT :: 0x40000005
SO_REUSEADDR :: 0x00000040
SO_SNDBUF :: 0x40000001
SO_SNDLOWAT :: 0x40000002
SO_TYPE :: 0x40000008
SO_LINGER :: 0x00000200
SO_RCVTIMEO :: 0x40000006
SO_SNDTIMEO :: 0x40000003
} else {
SOL_SOCKET :: 0xffff
SO_ACCEPTCONN :: 0x0002
SO_BROADCAST :: 0x0020
SO_DEBUG :: 0x0001
SO_DONTROUTE :: 0x0010
SO_ERROR :: 0x1007
SO_KEEPALIVE :: 0x0008
SO_OOBINLINE :: 0x0100
SO_RCVBUF :: 0x1002
SO_RCVLOWAT :: 0x1004
SO_REUSEADDR :: 0x0004
SO_SNDBUF :: 0x1001
SO_SNDLOWAT :: 0x1003
SO_TYPE :: 0x1008
when ODIN_OS == .Darwin {
SO_LINGER :: 0x1080
SO_RCVTIMEO :: 0x1006
SO_SNDTIMEO :: 0x1005
} else when ODIN_OS == .FreeBSD {
SO_LINGER :: 0x0080
SO_RCVTIMEO :: 0x1006
SO_SNDTIMEO :: 0x1005
} else when ODIN_OS == .NetBSD {
SO_LINGER :: 0x0080
SO_RCVTIMEO :: 0x100c
SO_SNDTIMEO :: 0x100b
} else when ODIN_OS == .OpenBSD {
SO_LINGER :: 0x0080
SO_RCVTIMEO :: 0x1006
SO_SNDTIMEO :: 0x1005
}
}
// The maximum backlog queue length for listen().
when ODIN_OS == .Haiku {
SOMAXCONN :: 32
} else {
SOMAXCONN :: 128
}
when ODIN_OS == .Linux {
MSG_CTRUNC :: 0x008
MSG_DONTROUTE :: 0x004
MSG_EOR :: 0x080
MSG_OOB :: 0x001
MSG_PEEK :: 0x002
MSG_TRUNC :: 0x020
MSG_WAITALL :: 0x100
MSG_NOSIGNAL :: 0x4000
} else {
MSG_CTRUNC :: 0x20
MSG_DONTROUTE :: 0x4
MSG_EOR :: 0x8
MSG_OOB :: 0x1
MSG_PEEK :: 0x2
MSG_TRUNC :: 0x10
MSG_WAITALL :: 0x40
when ODIN_OS == .Darwin {
MSG_NOSIGNAL :: 0x80000
} else when ODIN_OS == .FreeBSD {
MSG_NOSIGNAL :: 0x00020000
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
MSG_NOSIGNAL :: 0x0400
} else when ODIN_OS == .Haiku {
MSG_NOSIGNAL :: 0x800
}
}
when ODIN_OS == .Haiku {
AF_INET :: 1
AF_UNIX :: 9
} else {
AF_INET :: 2
AF_UNIX :: 1
}
when ODIN_OS == .Darwin {
AF_INET6 :: 30
} else when ODIN_OS == .FreeBSD {
AF_INET6 :: 28
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
AF_INET6 :: 24
} else when ODIN_OS == .Linux {
AF_INET6 :: 10
} else when ODIN_OS == .Haiku {
AF_INET6 :: 5
}
SHUT_RD :: 0
SHUT_RDWR :: 2
SHUT_WR :: 1
}