Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,513 @@
|
||||
module net
|
||||
|
||||
import time
|
||||
|
||||
const udp_default_read_timeout = time.second / 10
|
||||
const udp_default_write_timeout = time.second / 10
|
||||
|
||||
struct UdpSocket {
|
||||
Socket
|
||||
l Addr
|
||||
// TODO(emily): replace with option again
|
||||
// when i figure out how to coerce it properly
|
||||
mut:
|
||||
has_r bool
|
||||
r Addr
|
||||
}
|
||||
|
||||
pub struct UdpConn {
|
||||
pub mut:
|
||||
sock UdpSocket
|
||||
mut:
|
||||
write_deadline time.Time
|
||||
read_deadline time.Time
|
||||
read_timeout time.Duration
|
||||
write_timeout time.Duration
|
||||
is_blocking bool = true
|
||||
}
|
||||
|
||||
@[_pack: '1']
|
||||
struct Ipv4MulticastRequest {
|
||||
multiaddr [4]u8
|
||||
interface_ip [4]u8
|
||||
}
|
||||
|
||||
@[_pack: '1']
|
||||
struct Ipv6MulticastRequest {
|
||||
multiaddr [16]u8
|
||||
interface_index u32
|
||||
}
|
||||
|
||||
pub fn dial_udp(raddr string) !&UdpConn {
|
||||
addrs := resolve_addrs_fuzzy(raddr, .udp)!
|
||||
|
||||
for addr in addrs {
|
||||
// create a local socket for this
|
||||
// bind to any port (or file) (we dont care in this
|
||||
// case because we only care about the remote)
|
||||
if sock := new_udp_socket_for_remote(addr) {
|
||||
mut conn := &UdpConn{
|
||||
sock: sock
|
||||
read_timeout: udp_default_read_timeout
|
||||
write_timeout: udp_default_write_timeout
|
||||
}
|
||||
$if net_nonblocking_sockets ? {
|
||||
conn.is_blocking = false
|
||||
}
|
||||
return conn
|
||||
}
|
||||
}
|
||||
|
||||
return error('none')
|
||||
}
|
||||
|
||||
// pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
|
||||
// local := resolve_addr(laddr, .inet, .udp)?
|
||||
|
||||
// sbase := new_udp_socket()?
|
||||
|
||||
// sock := UdpSocket{
|
||||
// handle: sbase.handle
|
||||
// l: local
|
||||
// r: resolve_wrapper(raddr)
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn (mut c UdpConn) write_ptr(b &u8, len int) !int {
|
||||
remote := c.sock.remote() or { return err_no_udp_remote }
|
||||
return c.write_to_ptr(remote, b, len)
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) write(buf []u8) !int {
|
||||
return c.write_ptr(buf.data, buf.len)
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) write_string(s string) !int {
|
||||
return c.write_ptr(s.str, s.len)
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) 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('none')
|
||||
}
|
||||
|
||||
// write_to blocks and writes the buf to the remote addr specified
|
||||
pub fn (mut c UdpConn) write_to(addr Addr, buf []u8) !int {
|
||||
return c.write_to_ptr(addr, buf.data, buf.len)
|
||||
}
|
||||
|
||||
// write_to_string blocks and writes the buf to the remote addr specified
|
||||
pub fn (mut c UdpConn) 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 `Addr` read from.
|
||||
pub fn (c &UdpConn) read_ptr(buf_ptr &u8, len int) !(int, Addr) {
|
||||
mut addr := Addr{
|
||||
addr: AddrData{
|
||||
Ip6: Ip6{}
|
||||
}
|
||||
}
|
||||
addr_len := sizeof(Addr)
|
||||
mut res := 0
|
||||
if c.is_blocking {
|
||||
// Honor read deadlines/timeouts before entering a blocking recvfrom call.
|
||||
c.wait_for_read()!
|
||||
res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf_ptr), len, 0, voidptr(&addr),
|
||||
&addr_len))!
|
||||
} else {
|
||||
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 in [int(error_ewouldblock), int(error_eagain), C.EINTR] {
|
||||
c.wait_for_read()!
|
||||
// same setup as in tcp
|
||||
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('none')
|
||||
}
|
||||
|
||||
// read reads from the socket into buf up to buf.len returning the number of bytes read
|
||||
pub fn (mut c UdpConn) read(mut buf []u8) !(int, Addr) {
|
||||
return c.read_ptr(buf.data, buf.len)!
|
||||
}
|
||||
|
||||
pub fn (c &UdpConn) read_deadline() !time.Time {
|
||||
if c.read_deadline.unix() == 0 {
|
||||
return c.read_deadline
|
||||
}
|
||||
return error('none')
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) set_read_deadline(deadline time.Time) {
|
||||
c.read_deadline = deadline
|
||||
}
|
||||
|
||||
pub fn (c &UdpConn) write_deadline() !time.Time {
|
||||
if c.write_deadline.unix() == 0 {
|
||||
return c.write_deadline
|
||||
}
|
||||
return error('none')
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) set_write_deadline(deadline time.Time) {
|
||||
c.write_deadline = deadline
|
||||
}
|
||||
|
||||
pub fn (c &UdpConn) read_timeout() time.Duration {
|
||||
return c.read_timeout
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) set_read_timeout(t time.Duration) {
|
||||
c.read_timeout = t
|
||||
}
|
||||
|
||||
pub fn (c &UdpConn) write_timeout() time.Duration {
|
||||
return c.write_timeout
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) set_write_timeout(t time.Duration) {
|
||||
c.write_timeout = t
|
||||
}
|
||||
|
||||
@[inline]
|
||||
pub fn (c &UdpConn) wait_for_read() ! {
|
||||
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
|
||||
}
|
||||
|
||||
@[inline]
|
||||
pub fn (mut c UdpConn) wait_for_write() ! {
|
||||
return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
|
||||
}
|
||||
|
||||
pub fn (c &UdpConn) str() string {
|
||||
// TODO
|
||||
return 'UdpConn'
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) close() ! {
|
||||
return c.sock.close()
|
||||
}
|
||||
|
||||
// join_multicast_group joins the UDP socket to an IPv4 or IPv6 multicast group.
|
||||
// For IPv4 sockets, `iface_addr` must be an IPv4 address or `''` to use the default interface.
|
||||
// For IPv6 sockets, `iface_addr` must be a numeric interface index or `''` to use the default interface.
|
||||
pub fn (mut c UdpConn) join_multicast_group(multicast_addr string, iface_addr string) ! {
|
||||
family := c.multicast_family()!
|
||||
match family {
|
||||
.ip {
|
||||
mreq := Ipv4MulticastRequest{
|
||||
multiaddr: parse_ipv4_multicast_addr(multicast_addr)!
|
||||
interface_ip: parse_ipv4_interface_addr(iface_addr)!
|
||||
}
|
||||
c.sock.set_option_raw(C.IPPROTO_IP, C.IP_ADD_MEMBERSHIP, &mreq,
|
||||
sizeof(Ipv4MulticastRequest))!
|
||||
}
|
||||
.ip6 {
|
||||
mreq := Ipv6MulticastRequest{
|
||||
multiaddr: parse_ipv6_multicast_addr(multicast_addr)!
|
||||
interface_index: parse_ipv6_interface_index(iface_addr)!
|
||||
}
|
||||
c.sock.set_option_raw(C.IPPROTO_IPV6, ipv6_membership_socket_option(true), &mreq,
|
||||
sizeof(Ipv6MulticastRequest))!
|
||||
}
|
||||
else {
|
||||
return error('net: udp multicast is only supported on ip and ip6 sockets')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// leave_multicast_group leaves an IPv4 or IPv6 multicast group previously joined by the socket.
|
||||
// `iface_addr` follows the same rules as `join_multicast_group`.
|
||||
pub fn (mut c UdpConn) leave_multicast_group(multicast_addr string, iface_addr string) ! {
|
||||
family := c.multicast_family()!
|
||||
match family {
|
||||
.ip {
|
||||
mreq := Ipv4MulticastRequest{
|
||||
multiaddr: parse_ipv4_multicast_addr(multicast_addr)!
|
||||
interface_ip: parse_ipv4_interface_addr(iface_addr)!
|
||||
}
|
||||
c.sock.set_option_raw(C.IPPROTO_IP, C.IP_DROP_MEMBERSHIP, &mreq,
|
||||
sizeof(Ipv4MulticastRequest))!
|
||||
}
|
||||
.ip6 {
|
||||
mreq := Ipv6MulticastRequest{
|
||||
multiaddr: parse_ipv6_multicast_addr(multicast_addr)!
|
||||
interface_index: parse_ipv6_interface_index(iface_addr)!
|
||||
}
|
||||
c.sock.set_option_raw(C.IPPROTO_IPV6, ipv6_membership_socket_option(false), &mreq,
|
||||
sizeof(Ipv6MulticastRequest))!
|
||||
}
|
||||
else {
|
||||
return error('net: udp multicast is only supported on ip and ip6 sockets')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set_multicast_ttl sets the IPv4 TTL or IPv6 hop limit used for outgoing multicast traffic.
|
||||
// Valid values are between 0 and 255 inclusive.
|
||||
pub fn (mut c UdpConn) set_multicast_ttl(ttl int) ! {
|
||||
if ttl < 0 || ttl > 255 {
|
||||
return error('net: multicast ttl must be between 0 and 255')
|
||||
}
|
||||
match c.multicast_family()! {
|
||||
.ip {
|
||||
c.sock.set_option_int(C.IPPROTO_IP, C.IP_MULTICAST_TTL, ttl)!
|
||||
}
|
||||
.ip6 {
|
||||
c.sock.set_option_int(C.IPPROTO_IPV6, C.IPV6_MULTICAST_HOPS, ttl)!
|
||||
}
|
||||
else {
|
||||
return error('net: udp multicast is only supported on ip and ip6 sockets')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set_multicast_loop enables or disables local loopback for outgoing multicast packets.
|
||||
pub fn (mut c UdpConn) set_multicast_loop(enable bool) ! {
|
||||
value := int(enable)
|
||||
match c.multicast_family()! {
|
||||
.ip {
|
||||
c.sock.set_option_int(C.IPPROTO_IP, C.IP_MULTICAST_LOOP, value)!
|
||||
}
|
||||
.ip6 {
|
||||
c.sock.set_option_int(C.IPPROTO_IPV6, C.IPV6_MULTICAST_LOOP, value)!
|
||||
}
|
||||
else {
|
||||
return error('net: udp multicast is only supported on ip and ip6 sockets')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set_multicast_interface sets the outgoing multicast interface for the UDP socket.
|
||||
// For IPv4 sockets, `iface_addr` must be an IPv4 address or `''` to clear it to the default interface.
|
||||
// For IPv6 sockets, `iface_addr` must be a numeric interface index or `''` to use the default interface.
|
||||
pub fn (mut c UdpConn) set_multicast_interface(iface_addr string) ! {
|
||||
match c.multicast_family()! {
|
||||
.ip {
|
||||
addr := parse_ipv4_interface_addr(iface_addr)!
|
||||
c.sock.set_option_raw(C.IPPROTO_IP, C.IP_MULTICAST_IF, &addr[0], sizeof([4]u8))!
|
||||
}
|
||||
.ip6 {
|
||||
index := parse_ipv6_interface_index(iface_addr)!
|
||||
c.sock.set_option_int(C.IPPROTO_IPV6, C.IPV6_MULTICAST_IF, int(index))!
|
||||
}
|
||||
else {
|
||||
return error('net: udp multicast is only supported on ip and ip6 sockets')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listen_udp(laddr string) !&UdpConn {
|
||||
addrs := resolve_addrs_fuzzy(laddr, .udp)!
|
||||
// TODO(emily):
|
||||
// here we are binding to the first address
|
||||
// and that is probably not ideal
|
||||
addr := addrs[0]
|
||||
mut conn := &UdpConn{
|
||||
sock: new_udp_socket(addr)!
|
||||
read_timeout: udp_default_read_timeout
|
||||
write_timeout: udp_default_write_timeout
|
||||
}
|
||||
$if net_nonblocking_sockets ? {
|
||||
conn.is_blocking = false
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
fn new_udp_socket(local_addr Addr) !&UdpSocket {
|
||||
family := local_addr.family()
|
||||
|
||||
sockfd := socket_error(C.socket(i32(family), i32(SocketType.udp), 0))!
|
||||
mut s := &UdpSocket{
|
||||
handle: sockfd
|
||||
l: local_addr
|
||||
r: Addr{
|
||||
addr: AddrData{
|
||||
Ip6: Ip6{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.set_option_bool(.reuse_addr, true)!
|
||||
|
||||
if family == .ip6 {
|
||||
s.set_dualstack(true)!
|
||||
}
|
||||
|
||||
$if net_nonblocking_sockets ? {
|
||||
set_blocking(sockfd, false)!
|
||||
}
|
||||
|
||||
// cast to the correct type
|
||||
socket_error(C.bind(s.handle, voidptr(&local_addr), local_addr.len()))!
|
||||
return s
|
||||
}
|
||||
|
||||
fn new_udp_socket_for_remote(raddr Addr) !&UdpSocket {
|
||||
// Invent a sutible local address for this remote addr
|
||||
// Appease compiler
|
||||
mut addr := Addr{
|
||||
addr: AddrData{
|
||||
Ip6: Ip6{}
|
||||
}
|
||||
}
|
||||
match raddr.family() {
|
||||
.ip {
|
||||
// Use ip dualstack
|
||||
addr = new_ip(0, addr_ip_any)
|
||||
}
|
||||
.ip6 {
|
||||
// Use ip6 dualstack
|
||||
addr = new_ip6(0, addr_ip6_any)
|
||||
}
|
||||
.unix {
|
||||
addr = temp_unix()!
|
||||
}
|
||||
else {
|
||||
panic('Invalid family')
|
||||
}
|
||||
}
|
||||
|
||||
mut sock := new_udp_socket(addr)!
|
||||
sock.has_r = true
|
||||
sock.r = raddr
|
||||
|
||||
return sock
|
||||
}
|
||||
|
||||
fn (c &UdpConn) multicast_family() !AddrFamily {
|
||||
family := c.sock.l.family()
|
||||
if family !in [.ip, .ip6] {
|
||||
return error('net: udp multicast is only supported on ip and ip6 sockets')
|
||||
}
|
||||
return family
|
||||
}
|
||||
|
||||
fn normalize_ip_literal(address string) string {
|
||||
if address.len >= 2 && address[0] == `[` && address[address.len - 1] == `]` {
|
||||
return address[1..address.len - 1]
|
||||
}
|
||||
return address
|
||||
}
|
||||
|
||||
fn parse_ipv4_interface_addr(iface_addr string) ![4]u8 {
|
||||
if iface_addr.len == 0 {
|
||||
return [4]u8{}
|
||||
}
|
||||
address := normalize_ip_literal(iface_addr)
|
||||
mut parsed := [4]u8{}
|
||||
if C.inet_pton(i32(AddrFamily.ip), &char(address.str), &parsed[0]) != 1 {
|
||||
return error('net: ipv4 multicast interface must be an ipv4 address')
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
fn parse_ipv4_multicast_addr(multicast_addr string) ![4]u8 {
|
||||
address := normalize_ip_literal(multicast_addr)
|
||||
mut parsed := [4]u8{}
|
||||
if C.inet_pton(i32(AddrFamily.ip), &char(address.str), &parsed[0]) != 1 {
|
||||
return error('net: invalid ipv4 multicast address `${multicast_addr}`')
|
||||
}
|
||||
if parsed[0] < 224 || parsed[0] > 239 {
|
||||
return error('net: `${multicast_addr}` is not an ipv4 multicast address')
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
fn parse_ipv6_multicast_addr(multicast_addr string) ![16]u8 {
|
||||
address := normalize_ip_literal(multicast_addr)
|
||||
mut parsed := [16]u8{}
|
||||
if C.inet_pton(i32(AddrFamily.ip6), &char(address.str), &parsed[0]) != 1 {
|
||||
return error('net: invalid ipv6 multicast address `${multicast_addr}`')
|
||||
}
|
||||
if parsed[0] != 0xff {
|
||||
return error('net: `${multicast_addr}` is not an ipv6 multicast address')
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
fn parse_ipv6_interface_index(iface_addr string) !u32 {
|
||||
if iface_addr.len == 0 {
|
||||
return 0
|
||||
}
|
||||
for ch in iface_addr {
|
||||
if ch < `0` || ch > `9` {
|
||||
return error('net: ipv6 multicast interface must be a numeric interface index')
|
||||
}
|
||||
}
|
||||
return iface_addr.u32()
|
||||
}
|
||||
|
||||
fn ipv6_membership_socket_option(join bool) int {
|
||||
$if windows {
|
||||
return if join { C.IPV6_ADD_MEMBERSHIP } else { C.IPV6_DROP_MEMBERSHIP }
|
||||
} $else {
|
||||
return if join { C.IPV6_JOIN_GROUP } else { C.IPV6_LEAVE_GROUP }
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut s UdpSocket) set_option_raw(level int, opt int, value voidptr, value_len u32) ! {
|
||||
socket_error(C.setsockopt(s.handle, level, opt, value, value_len))!
|
||||
}
|
||||
|
||||
fn (mut s UdpSocket) set_option_int(level int, opt int, value int) ! {
|
||||
s.set_option_raw(level, opt, &value, sizeof(int))!
|
||||
}
|
||||
|
||||
// set_option_bool sets a boolean socket option on the UDP socket.
|
||||
pub fn (mut s UdpSocket) set_option_bool(opt SocketOption, value bool) ! {
|
||||
// TODO: reenable when this `in` operation works again
|
||||
// if opt !in opts_can_set {
|
||||
// return err_option_not_settable
|
||||
// }
|
||||
// if opt !in opts_bool {
|
||||
// return err_option_wrong_type
|
||||
// }
|
||||
x := int(value)
|
||||
s.set_option_int(C.SOL_SOCKET, int(opt), x)!
|
||||
}
|
||||
|
||||
// set_dualstack enables or disables dual-stack behavior for IPv6 UDP sockets.
|
||||
pub fn (mut s UdpSocket) set_dualstack(on bool) ! {
|
||||
x := int(!on)
|
||||
s.set_option_int(C.IPPROTO_IPV6, int(SocketOption.ipv6_only), x)!
|
||||
}
|
||||
|
||||
// close shuts down and closes the socket for communication.
|
||||
pub fn (mut s UdpSocket) 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 UdpSocket) select(test Select, timeout time.Duration) !bool {
|
||||
return select(s.handle, test, timeout)
|
||||
}
|
||||
|
||||
// remote returns the remote `Addr` address of the socket or `none` if no remote is has been resolved.
|
||||
pub fn (s &UdpSocket) remote() ?Addr {
|
||||
if s.has_r {
|
||||
return s.r
|
||||
}
|
||||
return none
|
||||
}
|
||||
Reference in new issue
Block a user