276 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			V
		
	
	
module net
 | 
						|
 | 
						|
import io.util
 | 
						|
import os
 | 
						|
 | 
						|
union AddrData {
 | 
						|
	Unix
 | 
						|
	Ip
 | 
						|
	Ip6
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	addr_ip6_any = [16]byte{init: byte(0)}
 | 
						|
	addr_ip_any  = [4]byte{init: byte(0)}
 | 
						|
)
 | 
						|
 | 
						|
fn new_ip6(port u16, addr [16]byte) Addr {
 | 
						|
	a := Addr{
 | 
						|
		f: u16(AddrFamily.ip6)
 | 
						|
		addr: AddrData{
 | 
						|
			Ip6: Ip6{
 | 
						|
				port: u16(C.htons(port))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	unsafe { vmemcpy(&a.addr.Ip6.addr[0], &addr[0], 16) }
 | 
						|
 | 
						|
	return a
 | 
						|
}
 | 
						|
 | 
						|
fn new_ip(port u16, addr [4]byte) Addr {
 | 
						|
	a := Addr{
 | 
						|
		f: u16(AddrFamily.ip)
 | 
						|
		addr: AddrData{
 | 
						|
			Ip: Ip{
 | 
						|
				port: u16(C.htons(port))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	unsafe { vmemcpy(&a.addr.Ip.addr[0], &addr[0], 4) }
 | 
						|
 | 
						|
	return a
 | 
						|
}
 | 
						|
 | 
						|
fn temp_unix() ?Addr {
 | 
						|
	// create a temp file to get a filename
 | 
						|
	// close it
 | 
						|
	// remove it
 | 
						|
	// then reuse the filename
 | 
						|
	mut file, filename := util.temp_file() ?
 | 
						|
	file.close()
 | 
						|
	os.rm(filename) ?
 | 
						|
	addrs := resolve_addrs(filename, .unix, .udp) ?
 | 
						|
	return addrs[0]
 | 
						|
}
 | 
						|
 | 
						|
pub fn (a Addr) family() AddrFamily {
 | 
						|
	return AddrFamily(a.f)
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	max_ip_len  = 24
 | 
						|
	max_ip6_len = 46
 | 
						|
)
 | 
						|
 | 
						|
fn (a Ip) str() string {
 | 
						|
	buf := [net.max_ip_len]char{}
 | 
						|
 | 
						|
	res := &char(C.inet_ntop(.ip, &a.addr, &buf[0], buf.len))
 | 
						|
 | 
						|
	if res == 0 {
 | 
						|
		return '<Unknown>'
 | 
						|
	}
 | 
						|
 | 
						|
	saddr := unsafe { cstring_to_vstring(&buf[0]) }
 | 
						|
	port := C.ntohs(a.port)
 | 
						|
 | 
						|
	return '$saddr:$port'
 | 
						|
}
 | 
						|
 | 
						|
fn (a Ip6) str() string {
 | 
						|
	buf := [net.max_ip6_len]char{}
 | 
						|
 | 
						|
	res := &char(C.inet_ntop(.ip6, &a.addr, &buf[0], buf.len))
 | 
						|
 | 
						|
	if res == 0 {
 | 
						|
		return '<Unknown>'
 | 
						|
	}
 | 
						|
 | 
						|
	saddr := unsafe { cstring_to_vstring(&buf[0]) }
 | 
						|
	port := C.ntohs(a.port)
 | 
						|
 | 
						|
	return '[$saddr]:$port'
 | 
						|
}
 | 
						|
 | 
						|
const aoffset = __offsetof(Addr, addr)
 | 
						|
 | 
						|
fn (a Addr) len() u32 {
 | 
						|
	match a.family() {
 | 
						|
		.ip {
 | 
						|
			return sizeof(Ip) + net.aoffset
 | 
						|
		}
 | 
						|
		.ip6 {
 | 
						|
			return sizeof(Ip6) + net.aoffset
 | 
						|
		}
 | 
						|
		.unix {
 | 
						|
			return sizeof(Unix) + net.aoffset
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			panic('Unknown address family')
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pub fn resolve_addrs(addr string, family AddrFamily, @type SocketType) ?[]Addr {
 | 
						|
	match family {
 | 
						|
		.ip, .ip6, .unspec {
 | 
						|
			return resolve_ipaddrs(addr, family, @type)
 | 
						|
		}
 | 
						|
		.unix {
 | 
						|
			resolved := Unix{}
 | 
						|
 | 
						|
			if addr.len > max_unix_path {
 | 
						|
				return error('net: resolve_addrs Unix socket address is too long')
 | 
						|
			}
 | 
						|
 | 
						|
			// Copy the unix path into the address struct
 | 
						|
			unsafe {
 | 
						|
				C.memcpy(&resolved.path, addr.str, addr.len)
 | 
						|
			}
 | 
						|
 | 
						|
			return [
 | 
						|
				Addr{
 | 
						|
					f: u16(AddrFamily.unix)
 | 
						|
					addr: AddrData{
 | 
						|
						Unix: resolved
 | 
						|
					}
 | 
						|
				},
 | 
						|
			]
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pub fn resolve_addrs_fuzzy(addr string, @type SocketType) ?[]Addr {
 | 
						|
	if addr.len == 0 {
 | 
						|
		return none
 | 
						|
	}
 | 
						|
 | 
						|
	// Use a small heuristic to figure out what address family this is
 | 
						|
	// (out of the ones that we support)
 | 
						|
 | 
						|
	if addr.contains(':') {
 | 
						|
		// Colon is a reserved character in unix paths
 | 
						|
		// so this must be an ip address
 | 
						|
		return resolve_addrs(addr, .unspec, @type)
 | 
						|
	}
 | 
						|
 | 
						|
	return resolve_addrs(addr, .unix, @type)
 | 
						|
}
 | 
						|
 | 
						|
pub fn resolve_ipaddrs(addr string, family AddrFamily, typ SocketType) ?[]Addr {
 | 
						|
	address, port := split_address(addr) ?
 | 
						|
 | 
						|
	if addr[0] == `:` {
 | 
						|
		match family {
 | 
						|
			.ip6 {
 | 
						|
				return [new_ip6(port, net.addr_ip6_any)]
 | 
						|
			}
 | 
						|
			.ip, .unspec {
 | 
						|
				return [new_ip(port, net.addr_ip_any)]
 | 
						|
			}
 | 
						|
			else {}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mut hints := C.addrinfo{
 | 
						|
		// ai_family: int(family)
 | 
						|
		// ai_socktype: int(typ)
 | 
						|
		// ai_flags: C.AI_PASSIVE
 | 
						|
	}
 | 
						|
	unsafe { vmemset(&hints, 0, int(sizeof(hints))) }
 | 
						|
	hints.ai_family = int(family)
 | 
						|
	hints.ai_socktype = int(typ)
 | 
						|
	hints.ai_flags = C.AI_PASSIVE
 | 
						|
 | 
						|
	results := &C.addrinfo(0)
 | 
						|
 | 
						|
	sport := '$port'
 | 
						|
 | 
						|
	// This might look silly but is recommended by MSDN
 | 
						|
	$if windows {
 | 
						|
		socket_error(0 - C.getaddrinfo(&char(address.str), &char(sport.str), &hints, &results)) ?
 | 
						|
	} $else {
 | 
						|
		x := C.getaddrinfo(&char(address.str), &char(sport.str), &hints, &results)
 | 
						|
		wrap_error(x) ?
 | 
						|
	}
 | 
						|
 | 
						|
	defer {
 | 
						|
		C.freeaddrinfo(results)
 | 
						|
	}
 | 
						|
 | 
						|
	// Now that we have our linked list of addresses
 | 
						|
	// convert them into an array
 | 
						|
	mut addresses := []Addr{}
 | 
						|
 | 
						|
	for result := results; !isnil(result); result = result.ai_next {
 | 
						|
		match AddrFamily(result.ai_family) {
 | 
						|
			.ip {
 | 
						|
				new_addr := Addr{
 | 
						|
					addr: AddrData{
 | 
						|
						Ip: Ip{}
 | 
						|
					}
 | 
						|
				}
 | 
						|
				unsafe {
 | 
						|
					C.memcpy(&new_addr, result.ai_addr, result.ai_addrlen)
 | 
						|
				}
 | 
						|
				addresses << new_addr
 | 
						|
			}
 | 
						|
			.ip6 {
 | 
						|
				new_addr := Addr{
 | 
						|
					addr: AddrData{
 | 
						|
						Ip6: Ip6{}
 | 
						|
					}
 | 
						|
				}
 | 
						|
				unsafe {
 | 
						|
					C.memcpy(&new_addr, result.ai_addr, result.ai_addrlen)
 | 
						|
				}
 | 
						|
				addresses << new_addr
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				panic('Unexpected address family $result.ai_family')
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return addresses
 | 
						|
}
 | 
						|
 | 
						|
fn (a Addr) str() string {
 | 
						|
	match AddrFamily(a.f) {
 | 
						|
		.ip {
 | 
						|
			unsafe {
 | 
						|
				return a.addr.Ip.str()
 | 
						|
			}
 | 
						|
		}
 | 
						|
		.ip6 {
 | 
						|
			unsafe {
 | 
						|
				return a.addr.Ip6.str()
 | 
						|
			}
 | 
						|
		}
 | 
						|
		.unix {
 | 
						|
			unsafe {
 | 
						|
				return tos_clone(a.addr.Unix.path[0..max_unix_path].data)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		.unspec {
 | 
						|
			return '<.unspec>'
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pub fn addr_from_socket_handle(handle int) Addr {
 | 
						|
	addr := Addr{
 | 
						|
		addr: AddrData{
 | 
						|
			Ip6: Ip6{}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	size := sizeof(addr)
 | 
						|
 | 
						|
	C.getsockname(handle, voidptr(&addr), &size)
 | 
						|
 | 
						|
	return addr
 | 
						|
}
 |