262 lines
7.0 KiB
V
262 lines
7.0 KiB
V
module net
|
|
|
|
import time
|
|
|
|
const raw_default_read_timeout = time.second / 10
|
|
const raw_default_write_timeout = time.second / 10
|
|
|
|
struct RawSocket {
|
|
Socket
|
|
l Addr
|
|
mut:
|
|
has_r bool
|
|
r Addr
|
|
protocol Protocol
|
|
}
|
|
|
|
// RawConn represents a raw socket connection for low-level network access.
|
|
// Raw sockets allow sending and receiving packets at the IP layer,
|
|
// bypassing the TCP/UDP transport layer.
|
|
pub struct RawConn {
|
|
pub mut:
|
|
sock RawSocket
|
|
mut:
|
|
write_deadline time.Time
|
|
read_deadline time.Time
|
|
read_timeout time.Duration
|
|
write_timeout time.Duration
|
|
}
|
|
|
|
// RawSocketConfig configures the creation of a raw socket.
|
|
@[params]
|
|
pub struct RawSocketConfig {
|
|
pub:
|
|
family AddrFamily = .ip
|
|
protocol Protocol = .icmp
|
|
}
|
|
|
|
// new_raw_socket creates a new raw socket with the given configuration.
|
|
// Raw sockets typically require elevated privileges (root/administrator).
|
|
pub fn new_raw_socket(config RawSocketConfig) !&RawConn {
|
|
sockfd := socket_error(C.socket(i32(config.family), i32(SocketType.raw), i32(config.protocol)))!
|
|
mut s := &RawSocket{
|
|
handle: sockfd
|
|
protocol: config.protocol
|
|
l: Addr{
|
|
addr: AddrData{
|
|
Ip6: Ip6{}
|
|
}
|
|
}
|
|
r: Addr{
|
|
addr: AddrData{
|
|
Ip6: Ip6{}
|
|
}
|
|
}
|
|
}
|
|
|
|
$if net_nonblocking_sockets ? {
|
|
set_blocking(sockfd, false)!
|
|
}
|
|
|
|
return &RawConn{
|
|
sock: s
|
|
read_timeout: raw_default_read_timeout
|
|
write_timeout: raw_default_write_timeout
|
|
}
|
|
}
|
|
|
|
// write_ptr writes data from `b` of length `len` to the connected remote address.
|
|
pub fn (mut c RawConn) write_ptr(b &u8, len int) !int {
|
|
remote := c.sock.remote() or { return error('no remote address set for raw socket') }
|
|
return c.write_to_ptr(remote, b, len)
|
|
}
|
|
|
|
// write writes `buf` to the connected remote address.
|
|
pub fn (mut c RawConn) write(buf []u8) !int {
|
|
return c.write_ptr(buf.data, buf.len)
|
|
}
|
|
|
|
// write_string writes string `s` to the connected remote address.
|
|
pub fn (mut c RawConn) write_string(s string) !int {
|
|
return c.write_ptr(s.str, s.len)
|
|
}
|
|
|
|
// write_to_ptr writes data from `b` of length `len` to the specified `addr`.
|
|
pub fn (mut c RawConn) write_to_ptr(addr Addr, b &u8, len int) !int {
|
|
res := C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len())
|
|
if res >= 0 {
|
|
return res
|
|
}
|
|
code := error_code()
|
|
if code == int(error_ewouldblock) {
|
|
c.wait_for_write()!
|
|
socket_error(C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len()))!
|
|
} else {
|
|
wrap_error(code)!
|
|
}
|
|
return error('write failed')
|
|
}
|
|
|
|
// write_to writes `buf` to the specified `addr`.
|
|
pub fn (mut c RawConn) write_to(addr Addr, buf []u8) !int {
|
|
return c.write_to_ptr(addr, buf.data, buf.len)
|
|
}
|
|
|
|
// write_to_string writes string `s` to the specified `addr`.
|
|
pub fn (mut c RawConn) write_to_string(addr Addr, s string) !int {
|
|
return c.write_to_ptr(addr, s.str, s.len)
|
|
}
|
|
|
|
// read_ptr reads from the socket into `buf_ptr` up to `len` bytes,
|
|
// returning the number of bytes read and the source `Addr`.
|
|
pub fn (c &RawConn) read_ptr(buf_ptr &u8, len int) !(int, Addr) {
|
|
mut addr := Addr{
|
|
addr: AddrData{
|
|
Ip6: Ip6{}
|
|
}
|
|
}
|
|
addr_len := sizeof(Addr)
|
|
mut res := wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf_ptr), len, 0, voidptr(&addr),
|
|
&addr_len))!
|
|
if res > 0 {
|
|
return res, addr
|
|
}
|
|
code := error_code()
|
|
if code == int(error_ewouldblock) {
|
|
c.wait_for_read()!
|
|
res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf_ptr), len, 0, voidptr(&addr),
|
|
&addr_len))!
|
|
res2 := socket_error(res)!
|
|
return res2, addr
|
|
} else {
|
|
wrap_error(code)!
|
|
}
|
|
return error('read failed')
|
|
}
|
|
|
|
// read reads from the socket into `buf`, returning the number of bytes read and the source `Addr`.
|
|
pub fn (mut c RawConn) read(mut buf []u8) !(int, Addr) {
|
|
return c.read_ptr(buf.data, buf.len)!
|
|
}
|
|
|
|
// read_deadline returns the current read deadline.
|
|
pub fn (c &RawConn) read_deadline() !time.Time {
|
|
if c.read_deadline.unix() == 0 {
|
|
return c.read_deadline
|
|
}
|
|
return error('none')
|
|
}
|
|
|
|
// set_read_deadline sets the read deadline.
|
|
pub fn (mut c RawConn) set_read_deadline(deadline time.Time) {
|
|
c.read_deadline = deadline
|
|
}
|
|
|
|
// write_deadline returns the current write deadline.
|
|
pub fn (c &RawConn) write_deadline() !time.Time {
|
|
if c.write_deadline.unix() == 0 {
|
|
return c.write_deadline
|
|
}
|
|
return error('none')
|
|
}
|
|
|
|
// set_write_deadline sets the write deadline.
|
|
pub fn (mut c RawConn) set_write_deadline(deadline time.Time) {
|
|
c.write_deadline = deadline
|
|
}
|
|
|
|
// read_timeout returns the current read timeout duration.
|
|
pub fn (c &RawConn) read_timeout() time.Duration {
|
|
return c.read_timeout
|
|
}
|
|
|
|
// set_read_timeout sets the read timeout duration.
|
|
pub fn (mut c RawConn) set_read_timeout(t time.Duration) {
|
|
c.read_timeout = t
|
|
}
|
|
|
|
// write_timeout returns the current write timeout duration.
|
|
pub fn (c &RawConn) write_timeout() time.Duration {
|
|
return c.write_timeout
|
|
}
|
|
|
|
// set_write_timeout sets the write timeout duration.
|
|
pub fn (mut c RawConn) set_write_timeout(t time.Duration) {
|
|
c.write_timeout = t
|
|
}
|
|
|
|
// wait_for_read waits for a read operation to be available.
|
|
@[inline]
|
|
pub fn (c &RawConn) wait_for_read() ! {
|
|
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
|
|
}
|
|
|
|
// wait_for_write waits for a write operation to be available.
|
|
@[inline]
|
|
pub fn (mut c RawConn) wait_for_write() ! {
|
|
return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
|
|
}
|
|
|
|
// str returns a string representation of the RawConn.
|
|
pub fn (c &RawConn) str() string {
|
|
return 'RawConn'
|
|
}
|
|
|
|
// close closes the raw socket connection.
|
|
pub fn (mut c RawConn) close() ! {
|
|
return c.sock.close()
|
|
}
|
|
|
|
// set_remote sets the remote address for write operations.
|
|
pub fn (mut c RawConn) set_remote(addr Addr) {
|
|
c.sock.has_r = true
|
|
c.sock.r = addr
|
|
}
|
|
|
|
// protocol returns the protocol used by this socket.
|
|
pub fn (c &RawConn) protocol() Protocol {
|
|
return c.sock.protocol
|
|
}
|
|
|
|
// set_option_bool sets a boolean socket option.
|
|
pub fn (mut s RawSocket) set_option_bool(opt SocketOption, value bool) ! {
|
|
x := int(value)
|
|
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int)))!
|
|
}
|
|
|
|
// set_option_int sets an integer socket option.
|
|
pub fn (mut s RawSocket) set_option_int(opt SocketOption, value int) ! {
|
|
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(int)))!
|
|
}
|
|
|
|
// set_ip_header_included enables or disables the IP_HDRINCL option.
|
|
// When enabled, the user must provide the complete IP header.
|
|
pub fn (mut s RawSocket) set_ip_header_included(on bool) ! {
|
|
x := int(on)
|
|
socket_error(C.setsockopt(s.handle, C.IPPROTO_IP, C.IP_HDRINCL, &x, sizeof(int)))!
|
|
}
|
|
|
|
// close shuts down and closes the socket.
|
|
pub fn (mut s RawSocket) close() ! {
|
|
shutdown(s.handle)
|
|
return close(s.handle)
|
|
}
|
|
|
|
// select waits for no more than `timeout` for the IO operation, defined by `test`, to be available.
|
|
pub fn (mut s RawSocket) select(test Select, timeout time.Duration) !bool {
|
|
return select(s.handle, test, timeout)
|
|
}
|
|
|
|
// remote returns the remote `Addr` of the socket or `none` if not set.
|
|
pub fn (s &RawSocket) remote() ?Addr {
|
|
if s.has_r {
|
|
return s.r
|
|
}
|
|
return none
|
|
}
|
|
|
|
// addr returns the local address of the socket.
|
|
pub fn (c &RawConn) addr() !Addr {
|
|
return c.sock.address()
|
|
}
|