v / vlib / net
Raw file | 294 loc (251 sloc) | 6.35 KB | Latest commit hash d2e5c721a
1module net
2
3import time
4
5const (
6 udp_default_read_timeout = time.second / 10
7 udp_default_write_timeout = time.second / 10
8)
9
10struct UdpSocket {
11 Socket
12 l Addr
13 // TODO(emily): replace with option again
14 // when i figure out how to coerce it properly
15mut:
16 has_r bool
17 r Addr
18}
19
20pub struct UdpConn {
21pub mut:
22 sock UdpSocket
23mut:
24 write_deadline time.Time
25 read_deadline time.Time
26 read_timeout time.Duration
27 write_timeout time.Duration
28}
29
30pub fn dial_udp(raddr string) !&UdpConn {
31 addrs := resolve_addrs_fuzzy(raddr, .udp)!
32
33 for addr in addrs {
34 // create a local socket for this
35 // bind to any port (or file) (we dont care in this
36 // case because we only care about the remote)
37 if sock := new_udp_socket_for_remote(addr) {
38 return &UdpConn{
39 sock: sock
40 read_timeout: net.udp_default_read_timeout
41 write_timeout: net.udp_default_write_timeout
42 }
43 }
44 }
45
46 return error('none')
47}
48
49// pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
50// local := resolve_addr(laddr, .inet, .udp)?
51
52// sbase := new_udp_socket()?
53
54// sock := UdpSocket{
55// handle: sbase.handle
56// l: local
57// r: resolve_wrapper(raddr)
58// }
59// }
60
61pub fn (mut c UdpConn) write_ptr(b &u8, len int) !int {
62 remote := c.sock.remote() or { return err_no_udp_remote }
63 return c.write_to_ptr(remote, b, len)
64}
65
66pub fn (mut c UdpConn) write(buf []u8) !int {
67 return c.write_ptr(buf.data, buf.len)
68}
69
70pub fn (mut c UdpConn) write_string(s string) !int {
71 return c.write_ptr(s.str, s.len)
72}
73
74pub fn (mut c UdpConn) write_to_ptr(addr Addr, b &u8, len int) !int {
75 res := C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len())
76 if res >= 0 {
77 return res
78 }
79 code := error_code()
80 if code == int(error_ewouldblock) {
81 c.wait_for_write()!
82 socket_error(C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len()))!
83 } else {
84 wrap_error(code)!
85 }
86 return error('none')
87}
88
89// write_to blocks and writes the buf to the remote addr specified
90pub fn (mut c UdpConn) write_to(addr Addr, buf []u8) !int {
91 return c.write_to_ptr(addr, buf.data, buf.len)
92}
93
94// write_to_string blocks and writes the buf to the remote addr specified
95pub fn (mut c UdpConn) write_to_string(addr Addr, s string) !int {
96 return c.write_to_ptr(addr, s.str, s.len)
97}
98
99// read reads from the socket into buf up to buf.len returning the number of bytes read
100pub fn (mut c UdpConn) read(mut buf []u8) !(int, Addr) {
101 mut addr := Addr{
102 addr: AddrData{
103 Ip6: Ip6{}
104 }
105 }
106 len := sizeof(Addr)
107 mut res := wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf.data), buf.len,
108 0, voidptr(&addr), &len))!
109 if res > 0 {
110 return res, addr
111 }
112 code := error_code()
113 if code == int(error_ewouldblock) {
114 c.wait_for_read()!
115 // same setup as in tcp
116 res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf.data), buf.len, 0,
117 voidptr(&addr), &len))!
118 res2 := socket_error(res)!
119 return res2, addr
120 } else {
121 wrap_error(code)!
122 }
123 return error('none')
124}
125
126pub fn (c &UdpConn) read_deadline() !time.Time {
127 if c.read_deadline.unix == 0 {
128 return c.read_deadline
129 }
130 return error('none')
131}
132
133pub fn (mut c UdpConn) set_read_deadline(deadline time.Time) {
134 c.read_deadline = deadline
135}
136
137pub fn (c &UdpConn) write_deadline() !time.Time {
138 if c.write_deadline.unix == 0 {
139 return c.write_deadline
140 }
141 return error('none')
142}
143
144pub fn (mut c UdpConn) set_write_deadline(deadline time.Time) {
145 c.write_deadline = deadline
146}
147
148pub fn (c &UdpConn) read_timeout() time.Duration {
149 return c.read_timeout
150}
151
152pub fn (mut c UdpConn) set_read_timeout(t time.Duration) {
153 c.read_timeout = t
154}
155
156pub fn (c &UdpConn) write_timeout() time.Duration {
157 return c.write_timeout
158}
159
160pub fn (mut c UdpConn) set_write_timeout(t time.Duration) {
161 c.write_timeout = t
162}
163
164[inline]
165pub fn (mut c UdpConn) wait_for_read() ! {
166 return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
167}
168
169[inline]
170pub fn (mut c UdpConn) wait_for_write() ! {
171 return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
172}
173
174pub fn (c &UdpConn) str() string {
175 // TODO
176 return 'UdpConn'
177}
178
179pub fn (mut c UdpConn) close() ! {
180 return c.sock.close()
181}
182
183pub fn listen_udp(laddr string) !&UdpConn {
184 addrs := resolve_addrs_fuzzy(laddr, .udp)!
185 // TODO(emily):
186 // here we are binding to the first address
187 // and that is probably not ideal
188 addr := addrs[0]
189 return &UdpConn{
190 sock: new_udp_socket(addr)!
191 read_timeout: net.udp_default_read_timeout
192 write_timeout: net.udp_default_write_timeout
193 }
194}
195
196fn new_udp_socket(local_addr Addr) !&UdpSocket {
197 family := local_addr.family()
198
199 sockfd := socket_error(C.socket(family, SocketType.udp, 0))!
200 mut s := &UdpSocket{
201 handle: sockfd
202 l: local_addr
203 r: Addr{
204 addr: AddrData{
205 Ip6: Ip6{}
206 }
207 }
208 }
209
210 s.set_option_bool(.reuse_addr, true)!
211
212 if family == .ip6 {
213 s.set_dualstack(true)!
214 }
215
216 $if !net_blocking_sockets ? {
217 // NOTE: refer to comments in tcp.v
218 $if windows {
219 t := u32(1) // true
220 socket_error(C.ioctlsocket(sockfd, fionbio, &t))!
221 } $else {
222 socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK))!
223 }
224 }
225
226 // cast to the correct type
227 socket_error(C.bind(s.handle, voidptr(&local_addr), local_addr.len()))!
228 return s
229}
230
231fn new_udp_socket_for_remote(raddr Addr) !&UdpSocket {
232 // Invent a sutible local address for this remote addr
233 // Appease compiler
234 mut addr := Addr{
235 addr: AddrData{
236 Ip6: Ip6{}
237 }
238 }
239 match raddr.family() {
240 .ip {
241 // Use ip dualstack
242 addr = new_ip(0, addr_ip_any)
243 }
244 .ip6 {
245 // Use ip6 dualstack
246 addr = new_ip6(0, addr_ip6_any)
247 }
248 .unix {
249 addr = temp_unix()!
250 }
251 else {
252 panic('Invalid family')
253 }
254 }
255 mut sock := new_udp_socket(addr)!
256 sock.has_r = true
257 sock.r = raddr
258
259 return sock
260}
261
262pub fn (mut s UdpSocket) set_option_bool(opt SocketOption, value bool) ! {
263 // TODO reenable when this `in` operation works again
264 // if opt !in opts_can_set {
265 // return err_option_not_settable
266 // }
267 // if opt !in opts_bool {
268 // return err_option_wrong_type
269 // }
270 x := int(value)
271 socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int)))!
272}
273
274pub fn (mut s UdpSocket) set_dualstack(on bool) ! {
275 x := int(!on)
276 socket_error(C.setsockopt(s.handle, C.IPPROTO_IPV6, int(SocketOption.ipv6_only), &x,
277 sizeof(int)))!
278}
279
280fn (mut s UdpSocket) close() ! {
281 shutdown(s.handle)
282 return close(s.handle)
283}
284
285fn (mut s UdpSocket) @select(test Select, timeout time.Duration) !bool {
286 return @select(s.handle, test, timeout)
287}
288
289fn (s &UdpSocket) remote() !Addr {
290 if s.has_r {
291 return s.r
292 }
293 return error('none')
294}