259 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			4.5 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))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	copy(a.addr.Ip6.addr[0..], addr[0..])
 | |
| 
 | |
| 	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))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	copy(a.addr.Ip6.addr[0..], addr[0..])
 | |
| 
 | |
| 	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 := []byte{len: net.max_ip_len, init: 0}
 | |
| 
 | |
| 	res := &char(C.inet_ntop(.ip, &a.addr, buf.data, buf.len))
 | |
| 
 | |
| 	if res == 0 {
 | |
| 		return '<Unknown>'
 | |
| 	}
 | |
| 
 | |
| 	saddr := buf.bytestr()
 | |
| 	port := C.ntohs(a.port)
 | |
| 
 | |
| 	return '$saddr:$port'
 | |
| }
 | |
| 
 | |
| fn (a Ip6) str() string {
 | |
| 	buf := []byte{len: net.max_ip6_len, init: 0}
 | |
| 
 | |
| 	res := &char(C.inet_ntop(.ip6, &a.addr, buf.data, buf.len))
 | |
| 
 | |
| 	if res == 0 {
 | |
| 		return '<Unknown>'
 | |
| 	}
 | |
| 
 | |
| 	saddr := buf.bytestr()
 | |
| 	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] == `:` {
 | |
| 		// Use in6addr_any
 | |
| 		return [new_ip6(port, net.addr_ip6_any)]
 | |
| 	}
 | |
| 
 | |
| 	mut hints := C.addrinfo{
 | |
| 		// ai_family: int(family)
 | |
| 		// ai_socktype: int(typ)
 | |
| 		// ai_flags: C.AI_PASSIVE
 | |
| 	}
 | |
| 	hints.ai_family = int(family)
 | |
| 	hints.ai_socktype = int(typ)
 | |
| 	hints.ai_flags = C.AI_PASSIVE
 | |
| 	hints.ai_protocol = 0
 | |
| 	hints.ai_addrlen = 0
 | |
| 	hints.ai_addr = voidptr(0)
 | |
| 	hints.ai_canonname = voidptr(0)
 | |
| 	hints.ai_next = voidptr(0)
 | |
| 	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, .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
 | |
| }
 |