init
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
module netio
|
||||
|
||||
import dlmalloc
|
||||
import encoding.binary
|
||||
import os
|
||||
|
||||
#include <netdb.h>
|
||||
|
||||
struct C.addrinfo {
|
||||
mut:
|
||||
ai_flags i32
|
||||
ai_family i32
|
||||
ai_socktype i32
|
||||
ai_protocol i32
|
||||
ai_addrlen i32
|
||||
ai_addr voidptr
|
||||
ai_canonname voidptr
|
||||
ai_next voidptr
|
||||
}
|
||||
|
||||
fn C.getaddrinfo(node &char, srvc &char, hints &C.addrinfo, res &&C.addrinfo) int
|
||||
fn C.freeaddrinfo(info &C.addrinfo)
|
||||
|
||||
const max_unix_path_size = $if linux {
|
||||
108
|
||||
} $else $if windows {
|
||||
108
|
||||
} $else {
|
||||
104
|
||||
}
|
||||
|
||||
// AddrInfo represents the [addrinfo](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html).
|
||||
pub struct AddrInfo {
|
||||
pub:
|
||||
flags int
|
||||
family AddrFamily
|
||||
sock_type SocketType
|
||||
protocol Protocol
|
||||
addr SocketAddr
|
||||
canonical string
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct TranslateAddrHints {
|
||||
pub:
|
||||
node ?string
|
||||
service ?string
|
||||
family AddrFamily = af_unspec
|
||||
sock_type SocketType
|
||||
protocol Protocol
|
||||
flags int
|
||||
}
|
||||
|
||||
// translate_addr translates the network address.
|
||||
// See also [getaddrinfo(3)](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html).
|
||||
// Example:
|
||||
// ```v
|
||||
// import os
|
||||
// import netio
|
||||
//
|
||||
// // Resolve the host FQND
|
||||
// addrs := netio.translate_addr(node: os.hostname()!, flags: C.AI_CANONNAME)!
|
||||
// for addr in addrs {
|
||||
// println(addr.canonical)
|
||||
// }
|
||||
// ```
|
||||
pub fn translate_addr(hints TranslateAddrHints) ![]AddrInfo {
|
||||
mut hints_ := C.addrinfo{}
|
||||
unsafe { vmemset(&hints_, 0, int(sizeof(hints_))) }
|
||||
hints_.ai_family = i32(hints.family)
|
||||
hints_.ai_socktype = i32(hints.sock_type)
|
||||
hints_.ai_protocol = i32(hints.protocol)
|
||||
hints_.ai_flags = i32(hints.flags)
|
||||
mut node := unsafe { nil }
|
||||
if hints.node != none {
|
||||
node = &char(hints.node.str)
|
||||
}
|
||||
mut service := unsafe { nil }
|
||||
if hints.service != none {
|
||||
service = &char(hints.service.str)
|
||||
}
|
||||
mut results := &C.addrinfo(unsafe { nil })
|
||||
if C.getaddrinfo(node, service, &hints_, &results) == -1 {
|
||||
return os.last_error()
|
||||
}
|
||||
defer {
|
||||
C.freeaddrinfo(results)
|
||||
}
|
||||
mut addrs := []AddrInfo{}
|
||||
for result := unsafe { results }; !isnil(result); result = result.ai_next {
|
||||
addrs << AddrInfo{
|
||||
flags: int(result.ai_flags)
|
||||
family: int(result.ai_family)
|
||||
sock_type: int(result.ai_socktype)
|
||||
protocol: int(result.ai_protocol)
|
||||
addr: unsafe { SocketAddr.from_ptr(result.ai_addr, result.ai_addrlen)! }
|
||||
canonical: if isnil(result.ai_canonname) {
|
||||
''
|
||||
} else {
|
||||
unsafe {
|
||||
cstring_to_vstring(result.ai_canonname)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
// SocketAddr.ipv4 creates new AF_INET socket address.
|
||||
// addr must be in big-endian byte order.
|
||||
pub fn SocketAddr.ipv4(addr [4]u8, port u16) SocketAddr {
|
||||
mut sock_addr := unsafe { SocketAddr.empty(af_inet, 16) }
|
||||
unsafe {
|
||||
sock_addr.push(binary.little_endian_get_u16(u16(af_inet))) or {}
|
||||
sock_addr.push(binary.big_endian_get_u16(port)) or {}
|
||||
sock_addr.push(addr[..]) or {}
|
||||
}
|
||||
return sock_addr
|
||||
}
|
||||
|
||||
// SocketAddr.ipv6 creates new AF_INET6 socket address.
|
||||
// addr must be in big-endian byte order.
|
||||
pub fn SocketAddr.ipv6(addr [16]u8, port u16, flow_info u32, scope_id u32) SocketAddr {
|
||||
mut sock_addr := unsafe { SocketAddr.empty(af_inet6, 28) }
|
||||
unsafe {
|
||||
sock_addr.push(binary.big_endian_get_u16(port)) or {}
|
||||
sock_addr.push(binary.big_endian_get_u32(flow_info)) or {}
|
||||
sock_addr.push(addr[..]) or {}
|
||||
sock_addr.push(binary.big_endian_get_u32(scope_id)) or {}
|
||||
}
|
||||
return sock_addr
|
||||
}
|
||||
|
||||
// SocketAddr.unix creates new AF_UNIX socket address.
|
||||
pub fn SocketAddr.unix(path string) SocketAddr {
|
||||
mut sock_addr := unsafe { SocketAddr.empty(af_unix, usize(max_unix_path_size) + 2) }
|
||||
unsafe {
|
||||
sock_addr.push(path.bytes()) or {}
|
||||
}
|
||||
return sock_addr
|
||||
}
|
||||
|
||||
// SocketAddr.empty creates new instance of SocketAddr with specified address family and size.
|
||||
//
|
||||
// Note: This function only allocates memory (zero filled) for the address, but **does not**
|
||||
// initialize the address itself, you need to do that manually. The benefit is that you can
|
||||
// create the any type of address.
|
||||
//
|
||||
// Note: This function sets the address family for you.
|
||||
// This field is always first and have 2-byte size (`u16` type).
|
||||
//
|
||||
// SocketAddr is a "constructor" for
|
||||
// [sockaddr(3type)](https://www.man7.org/linux/man-pages/man3/sockaddr.3type.html) objects.
|
||||
// Use this function only if you understand what you do. You must manually write the data
|
||||
// for the desired socket address, ensuring the correct sizes of all types, the order of
|
||||
// the fields in the struct, the byte order, and the total size of the struct. The sizes
|
||||
// and byte order may vary by platform, so you'll need to keep an eye on that as well.
|
||||
// A mistake while creating an address will crash your application. So this function is
|
||||
// marked as `unsafe`.
|
||||
//
|
||||
// The example below creates a `sockaddr_in` struct describing the loopback IPv4-address
|
||||
// 127.0.0.1 with port number 1080. Note the comment in the example. This is a fragment
|
||||
// of [sockaddr_in(3type)](https://www.man7.org/linux/man-pages/man3/sockaddr.3type.html)
|
||||
// manual page, which shows the target C struct. Summing the field sizes yields 8
|
||||
// bytes, but we need to allocate 16 bytes according to the <netinet/in.h>.
|
||||
// Data must be padded to sockaddr struct size which is 16 bytes. Each field is then
|
||||
// written in turn, from top to bottom. Keep in mind that address family field (sin_family
|
||||
// in this case) are already written. According to the manual page, the address and port
|
||||
// are written using the network (big endian) byte order.
|
||||
//
|
||||
// Example:
|
||||
// ```v
|
||||
// import encoding.binary
|
||||
// import netio
|
||||
//
|
||||
// // struct sockaddr_in {
|
||||
// // sa_family_t sin_family; /* AF_INET */
|
||||
// // in_port_t sin_port; /* Port number */
|
||||
// // struct in_addr sin_addr; /* IPv4 address */
|
||||
// // };
|
||||
// //
|
||||
// // struct in_addr {
|
||||
// // in_addr_t s_addr;
|
||||
// // };
|
||||
// //
|
||||
// // typedef uint32_t in_addr_t;
|
||||
// // typedef uint16_t in_port_t;
|
||||
//
|
||||
// mut sa := unsafe { netio.SocketAddr.empty(u16(C.AF_INET), 16) }
|
||||
// unsafe {
|
||||
// sa.push(binary.big_endian_get_u16(u16(1080)))!
|
||||
// sa.push([u8(127), 0, 0, 1])!
|
||||
// }
|
||||
// ```
|
||||
@[unsafe]
|
||||
pub fn SocketAddr.empty(af AddrFamily, size isize) SocketAddr {
|
||||
ptr := unsafe { dlmalloc.calloc(usize(size)) }
|
||||
mut sock_addr := SocketAddr{
|
||||
data: ptr
|
||||
len: int(size)
|
||||
}
|
||||
unsafe {
|
||||
$if little_endian {
|
||||
sock_addr.push(binary.little_endian_get_u16(u16(af))) or {}
|
||||
} $else {
|
||||
sock_addr.push(binary.big_endian_get_u16(u16(af))) or {}
|
||||
}
|
||||
}
|
||||
return sock_addr
|
||||
}
|
||||
|
||||
// SocketAddr.from_ptr creates new socket address by copying data from specified pointer.
|
||||
@[unsafe]
|
||||
pub fn SocketAddr.from_ptr(ptr voidptr, size isize) !SocketAddr {
|
||||
data := unsafe { dlmalloc.calloc(usize(size)) }
|
||||
unsafe {
|
||||
vmemcpy(data, ptr, size)
|
||||
}
|
||||
return SocketAddr{
|
||||
data: data
|
||||
len: int(size)
|
||||
}
|
||||
}
|
||||
|
||||
struct SocketAddr {
|
||||
mut:
|
||||
data &u8 = unsafe { nil }
|
||||
len int
|
||||
pos int
|
||||
}
|
||||
|
||||
// family returns the socket address family.
|
||||
pub fn (a SocketAddr) family() AddrFamily {
|
||||
mut f := u16(-1)
|
||||
unsafe { vmemcpy(&f, a.data, isize(2)) }
|
||||
return f
|
||||
}
|
||||
|
||||
// push appends the `inp` bytes into internal data buffer.
|
||||
@[unsafe]
|
||||
pub fn (mut a SocketAddr) push(inp []u8) ! {
|
||||
if a.pos + inp.len > a.len {
|
||||
return error('push: data overflow')
|
||||
}
|
||||
mut i := 0
|
||||
for a.pos + 1 < a.len {
|
||||
unsafe {
|
||||
a.data[a.pos + i] = inp[i]
|
||||
}
|
||||
i++
|
||||
if i >= inp.len {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.pos += inp.len
|
||||
}
|
||||
|
||||
// ptr returns the pointer to sockaddr data.
|
||||
pub fn (a SocketAddr) ptr() &u8 {
|
||||
return a.data
|
||||
}
|
||||
|
||||
// size reports the size of sockaddr data.
|
||||
pub fn (a SocketAddr) size() u32 {
|
||||
return u32(a.len)
|
||||
}
|
||||
|
||||
pub fn (a SocketAddr) str() string {
|
||||
family := a.family()
|
||||
match family {
|
||||
af_inet {
|
||||
mut addr := [4]u8{}
|
||||
mut port := [2]u8{}
|
||||
unsafe {
|
||||
vmemcpy(port, a.ptr() + 2, 2)
|
||||
vmemcpy(addr, a.ptr() + 4, 4)
|
||||
}
|
||||
port_int := binary.big_endian_u16_fixed(port)
|
||||
return '${addr[0]}.${addr[1]}.${addr[2]}.${addr[3]}:${port_int}'
|
||||
}
|
||||
af_inet6 {
|
||||
mut addr := [16]u8{}
|
||||
mut port := [2]u8{}
|
||||
mut res := ''
|
||||
unsafe {
|
||||
vmemcpy(port, a.ptr() + 2, 2)
|
||||
vmemcpy(addr, a.ptr() + 8, 16)
|
||||
}
|
||||
for i := 0; i < 16; i += 2 {
|
||||
res += addr[i..i + 2].hex()
|
||||
if i < 14 {
|
||||
res += ':'
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
af_unix {
|
||||
mut path := [max_unix_path_size]u8{}
|
||||
mut res := ''
|
||||
unsafe {
|
||||
vmemcpy(path, a.ptr() + 2, max_unix_path_size)
|
||||
res = tos_clone(&u8(path[..].data))
|
||||
}
|
||||
return res
|
||||
}
|
||||
else {
|
||||
return 'SocketAddr(${a.data.str()})'
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user