ref: 87b9d6012af6af199b8304c67d2dbcf2365fd649
dir: /libstd/dial.myr/
use "alloc.use"
use "chartype.use"
use "die.use"
use "error.use"
use "sys.use"
use "sleq.use"
use "option.use"
use "ipparse.use"
use "resolve.use"
use "fmt.use"
use "endian.use"
use "intparse.use"
use "hasprefix.use"
use "utf.use"
pkg std =
	const dial	: (dialstr : byte[:] -> error(fd, byte[:]))
;;
/*
 a map from service name to a list of (port,proto)
 pairs in order of preference
*/
/* FIXME: implement
var services : htab(byte[:], [int, byte[:]][:])#
var inited = false
*/
/* takes a plan 9 style dial string */
const dial = {str
	var proto, host, port
	var socktype, portnum
	var sa : sockaddr_in	/* we only support inet sockets right now.. ugh. */
	var sock
	(proto, str) = nameseg(str)
	(host, str) = nameseg(str)
	(port, str) = nameseg(str)
	if proto.len == 0
		-> `Failure "missing proto"
	elif host.len == 0
		-> `Failure "missing host"
	elif port.len == 0
		-> `Failure "missing port"
	;;
	if sleq(proto, "net")
		-> `Failure "net wildcard proto not yet supported\n"
	elif sleq(proto, "unix")
		-> `Failure "net unix proto not yet supported\n"
	elif sleq(proto, "tcp")
		socktype = Sockstream
	elif sleq(proto, "udp")
		socktype = Sockdgram
	;;
	match parseport(port)
	| `Some n:	portnum = n
	| `None:	-> `Failure "bad port"
	;;
	match getaddr(host)
	| `Ipv4 bits:
		put("connecting to %ub.%ub.%ub.%ub:%i\n", bits[0], bits[1], bits[2], bits[3], portnum)
		sa.fam = Afinet
		sa.addr = bits
		sa.port = hosttonet(portnum)
	| `Ipv6 bits:
		-> `Failure "ipv6 not yet supported"
	;;
	put("socketing...\n")
	sock = socket(sa.fam, socktype, 0)
	put("socketed\n")
	if sock < 0
		-> `Failure "failed to connect to socket"
	;;
	var err
	put("connecting...\n")
	err = connect(sock, (&sa) castto(sockaddr#), sizeof(sockaddr_in))
	put("connected\n")
	if err < 0
		put("Errno %i\n", -err)
		close(sock)
		-> `Failure "Failed to bind socket"
	;;
	-> `Success sock
}
const parseport = {port
	match intparse(port)
	| `Some n:	-> `Some n
	| `None:
		/* a small number of hardcoded ports */
		if sleq(port, "http")
			-> `Some 80
		elif sleq(port, "https")
			-> `Some 443
		elif sleq(port, "ircd")
			-> `Some 6667
		elif sleq(port, "dns")
			-> `Some 53
		;;
	;;
	-> `None
}
const getaddr = {addr
	var ip
	match ipparse(addr)
	| `Some a:	ip = a
	| `None:
		match resolve(addr)
		| `Success hi:
			ip = hi[0].addr
			slfree(hi)
		| `Failure m:
		;;
	;;
	-> ip
}
const nameseg = {str
	var len
	for len = 0; len < str.len; len++
		if str[len] == '!' castto(byte)
			-> (str[:len], str[len+1:])
		;;
	;;
	-> (str[:], str[len:])
}