module netio import encoding.binary struct C.sockaddr_storage {} // max_unix_path_size value is used to pad the sockaddr_un struct. const max_unix_path_size = $if linux { 108 } $else $if windows { 108 } $else { 104 } pub struct SocketAddr { mut: data &u8 = unsafe { nil } len int pos int } // SocketAddr.ipv4 creates new AF_INET socket address. // addr must be set in network (big-endian) byte order. pub fn SocketAddr.ipv4(addr [4]u8, port u16) SocketAddr { mut sock_addr := unsafe { SocketAddr.new(af_inet, 16) } unsafe { 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 set in network (big-endian) byte order. // Use if_nametoindex(3) to get an integer scope_id from its string representation. pub fn SocketAddr.ipv6(addr [16]u8, port u16, flow_info u32, scope_id u32) SocketAddr { mut sock_addr := unsafe { SocketAddr.new(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 { if path.len > max_unix_path_size { return error('Too long path to socket') } mut sock_addr := unsafe { SocketAddr.new(af_unix, usize(max_unix_path_size) + 2) } unsafe { sock_addr.push(path.bytes()) or {} } return sock_addr } // SocketAddr.new creates new instance of SocketAddr with specified address family and size. // // This function 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 kind of socket address. // // SocketAddr is a builder 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. Using the `push()` method you must // 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 . // 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 two-byte address family field // (sin_family in this case) is 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.new(netio.af_inet, 16) } // unsafe { // sa.push(binary.big_endian_get_u16(u16(1080)))! // sa.push([u8(127), 0, 0, 1])! // } // println(sa) // ``` @[unsafe] pub fn SocketAddr.new(af AddrFamily, size isize) SocketAddr { ptr := unsafe { vcalloc(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 { if isnil(ptr) { return error('${@METHOD}: cannot accept nil ptr') } data := unsafe { vcalloc(usize(size)) } unsafe { vmemcpy(data, ptr, size) } return SocketAddr{ data: data len: int(size) } } // family returns the socket address family. // Note: It returns 0 if socket address is nil, see also `is_empty()`. pub fn (a SocketAddr) family() AddrFamily { if isnil(a.data) { return 0 } mut f := 0 unsafe { vmemcpy(&f, a.data, isize(2)) } return f } // is_empty returns true if socket address is unspecified — the data pointer is nil or // data contains only zeros. Empty address cannot be used in `bind` and `connect` calls. pub fn (a SocketAddr) is_empty() bool { if isnil(a.data) { return true } if a.u8_array().all(|e| e == 0) { return true } return false } // push appends the `inp` bytes into internal data buffer. @[unsafe] pub fn (mut a SocketAddr) push(inp []u8) ! { if isnil(a.data) { return error('${@METHOD}: SocketAddr is nil') } if a.pos + inp.len > a.len { return error('${@METHOD}: 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) } // u8_array returns the socket address data as is as bytes array. pub fn (a SocketAddr) u8_array() []u8 { mut addr := []u8{len: int(a.size()), init: 0} unsafe { vmemcpy(addr.data, a.ptr(), a.size()) } return addr } // str returns the string representation of socket address. // Supported address families are AF_INET, AF_INET6, and AF_UNIX. // Examples: '172.16.16.132:1080', '[fdf1:72d1:0033:0000:0000:0000:0000:0247]:25535', // '/run/app.sock'. For others a string like 'SocketAddr(0x00000000)' will be returned. // See also `translate_name()`. pub fn (a SocketAddr) str() string { match a.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) // vfmt off return addr[0].str() + '.' + addr[1].str() + '.' + addr[2].str() + '.' + addr[3].str() + ':' + port_int.str() // vfmt on } 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 += ':' } } port_int := binary.big_endian_u16_fixed(port) return '[' + res + ']:' + port_int.str() } 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(0x${a.data:08x})' } } }