summaryrefslogtreecommitdiff
path: root/lib/std/dial+posixy.myr
blob: 13f02578de58a74943527c8e9e1a13100e7b24d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use sys

use "alloc.use"
use "chartype.use"
use "die.use"
use "endian.use"
use "hasprefix.use"
use "intparse.use"
use "ipparse.use"
use "option.use"
use "resolve.use"
use "result.use"
use "sleq.use"
use "strfind.use"
use "syswrap.use"
use "utf.use"

pkg std =
	const dial	: (dialstr : byte[:] -> result(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 sa4 : sys.sockaddr_in
	var sa6 : sys.sockaddr_in6
	var sa	: sys.sockaddr#
	var sock

	match parsedial(str)
	| `Ok val:	(proto, host, port) = val
	| `Fail m:	-> `Fail m
	;;

	match getaddr(host)
	| `Ipv4 bits:
		sa4.fam = sys.Afinet
		sa4.addr = bits
		sa4.port = hosttonet(port)
		sa = &sa4 castto(sys.sockaddr#)
	| `Ipv6 bits:
		sa6.fam = sys.Afinet6
		sa6.addr = bits
		sa6.port = hosttonet(port)
		sa = &sa6 castto(sys.sockaddr#)
	;;
	sock = sys.socket(sa.fam, proto, 0)

	if sock < 0
		-> `Fail "failed to connect to socket"
	;;
	var err
	err = sys.connect(sock, sa, sizeof(sys.sockaddr_in))
	if err < 0
		sys.close(sock)
		-> `Fail "Failed to bind socket"
	;;

	-> `Ok (sock castto(fd))
}

const parsedial = {str
	var proto, host, port
	var socktype, portnum

	(proto, str) = nameseg(str)
	(host, str) = nameseg(str)
	(port, str) = nameseg(str)

	if proto.len == 0
		-> `Fail "missing proto"
	elif host.len == 0
		-> `Fail "missing host"
	elif port.len == 0
		-> `Fail "missing port"
	;;

	if sleq(proto, "net")
		-> `Fail "net wildcard proto not yet supported\n"
	elif sleq(proto, "unix")
		-> `Fail "net unix proto not yet supported\n"
	elif sleq(proto, "tcp")
		socktype = sys.Sockstream
	elif sleq(proto, "udp")
		socktype = sys.Sockdgram
	;;

	match parseport(port)
	| `Some n:	portnum = n
	| `None:	-> `Fail "bad port"
	;;

	-> `Ok (socktype, host, portnum)
}

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)
		| `Ok hi:
			ip = hi[0].addr
			slfree(hi)
		| `Fail m:
		;;
	;;
	-> ip
}

const nameseg = {str
	match strfind(str, "!")
	| `Some idx:
		-> (str[:idx], str[idx+1:])
	| `None:
		-> (str, "")
	;;
}