1/************************************************
2
3  udpsocket.c -
4
5  created at: Thu Mar 31 12:21:29 JST 1994
6
7  Copyright (C) 1993-2007 Yukihiro Matsumoto
8
9************************************************/
10
11#include "rubysocket.h"
12
13/*
14 * call-seq:
15 *   UDPSocket.new([address_family]) => socket
16 *
17 * Creates a new UDPSocket object.
18 *
19 * _address_family_ should be an integer, a string or a symbol:
20 * Socket::AF_INET, "AF_INET", :INET, etc.
21 *
22 *   UDPSocket.new                   #=> #<UDPSocket:fd 3>
23 *   UDPSocket.new(Socket::AF_INET6) #=> #<UDPSocket:fd 4>
24 *
25 */
26static VALUE
27udp_init(int argc, VALUE *argv, VALUE sock)
28{
29    VALUE arg;
30    int family = AF_INET;
31    int fd;
32
33    rb_secure(3);
34    if (rb_scan_args(argc, argv, "01", &arg) == 1) {
35	family = rsock_family_arg(arg);
36    }
37    fd = rsock_socket(family, SOCK_DGRAM, 0);
38    if (fd < 0) {
39	rb_sys_fail("socket(2) - udp");
40    }
41
42    return rsock_init_sock(sock, fd);
43}
44
45struct udp_arg
46{
47    struct addrinfo *res;
48    int fd;
49};
50
51static VALUE
52udp_connect_internal(struct udp_arg *arg)
53{
54    int fd = arg->fd;
55    struct addrinfo *res;
56
57    for (res = arg->res; res; res = res->ai_next) {
58	if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0) >= 0) {
59	    return Qtrue;
60	}
61    }
62    return Qfalse;
63}
64
65VALUE rsock_freeaddrinfo(struct addrinfo *addr);
66
67/*
68 * call-seq:
69 *   udpsocket.connect(host, port) => 0
70 *
71 * Connects _udpsocket_ to _host_:_port_.
72 *
73 * This makes possible to send without destination address.
74 *
75 *   u1 = UDPSocket.new
76 *   u1.bind("127.0.0.1", 4913)
77 *   u2 = UDPSocket.new
78 *   u2.connect("127.0.0.1", 4913)
79 *   u2.send "uuuu", 0
80 *   p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
81 *
82 */
83static VALUE
84udp_connect(VALUE sock, VALUE host, VALUE port)
85{
86    rb_io_t *fptr;
87    struct udp_arg arg;
88    VALUE ret;
89
90    rb_secure(3);
91    arg.res = rsock_addrinfo(host, port, SOCK_DGRAM, 0);
92    GetOpenFile(sock, fptr);
93    arg.fd = fptr->fd;
94    ret = rb_ensure(udp_connect_internal, (VALUE)&arg,
95		    rsock_freeaddrinfo, (VALUE)arg.res);
96    if (!ret) rb_sys_fail("connect(2)");
97    return INT2FIX(0);
98}
99
100/*
101 * call-seq:
102 *   udpsocket.bind(host, port) #=> 0
103 *
104 * Binds _udpsocket_ to _host_:_port_.
105 *
106 *   u1 = UDPSocket.new
107 *   u1.bind("127.0.0.1", 4913)
108 *   u1.send "message-to-self", 0, "127.0.0.1", 4913
109 *   p u1.recvfrom(10) #=> ["message-to", ["AF_INET", 4913, "localhost", "127.0.0.1"]]
110 *
111 */
112static VALUE
113udp_bind(VALUE sock, VALUE host, VALUE port)
114{
115    rb_io_t *fptr;
116    struct addrinfo *res0, *res;
117
118    rb_secure(3);
119    res0 = rsock_addrinfo(host, port, SOCK_DGRAM, 0);
120    GetOpenFile(sock, fptr);
121    for (res = res0; res; res = res->ai_next) {
122	if (bind(fptr->fd, res->ai_addr, res->ai_addrlen) < 0) {
123	    continue;
124	}
125	freeaddrinfo(res0);
126	return INT2FIX(0);
127    }
128    freeaddrinfo(res0);
129    rb_sys_fail("bind(2)");
130    return INT2FIX(0);
131}
132
133/*
134 * call-seq:
135 *   udpsocket.send(mesg, flags, host, port)  => numbytes_sent
136 *   udpsocket.send(mesg, flags, sockaddr_to) => numbytes_sent
137 *   udpsocket.send(mesg, flags)              => numbytes_sent
138 *
139 * Sends _mesg_ via _udpsocket_.
140 *
141 * _flags_ should be a bitwise OR of Socket::MSG_* constants.
142 *
143 *   u1 = UDPSocket.new
144 *   u1.bind("127.0.0.1", 4913)
145 *
146 *   u2 = UDPSocket.new
147 *   u2.send "hi", 0, "127.0.0.1", 4913
148 *
149 *   mesg, addr = u1.recvfrom(10)
150 *   u1.send mesg, 0, addr[3], addr[1]
151 *
152 *   p u2.recv(100) #=> "hi"
153 *
154 */
155static VALUE
156udp_send(int argc, VALUE *argv, VALUE sock)
157{
158    VALUE flags, host, port;
159    rb_io_t *fptr;
160    int n;
161    struct addrinfo *res0, *res;
162    struct rsock_send_arg arg;
163
164    if (argc == 2 || argc == 3) {
165	return rsock_bsock_send(argc, argv, sock);
166    }
167    rb_secure(4);
168    rb_scan_args(argc, argv, "4", &arg.mesg, &flags, &host, &port);
169
170    StringValue(arg.mesg);
171    res0 = rsock_addrinfo(host, port, SOCK_DGRAM, 0);
172    GetOpenFile(sock, fptr);
173    arg.fd = fptr->fd;
174    arg.flags = NUM2INT(flags);
175    for (res = res0; res; res = res->ai_next) {
176      retry:
177	arg.to = res->ai_addr;
178	arg.tolen = res->ai_addrlen;
179	rb_thread_fd_writable(arg.fd);
180	n = (int)BLOCKING_REGION_FD(rsock_sendto_blocking, &arg);
181	if (n >= 0) {
182	    freeaddrinfo(res0);
183	    return INT2FIX(n);
184	}
185	if (rb_io_wait_writable(fptr->fd)) {
186	    goto retry;
187	}
188    }
189    freeaddrinfo(res0);
190    rb_sys_fail("sendto(2)");
191    return INT2FIX(n);
192}
193
194/*
195 * call-seq:
196 * 	udpsocket.recvfrom_nonblock(maxlen) => [mesg, sender_inet_addr]
197 * 	udpsocket.recvfrom_nonblock(maxlen, flags) => [mesg, sender_inet_addr]
198 *
199 * Receives up to _maxlen_ bytes from +udpsocket+ using recvfrom(2) after
200 * O_NONBLOCK is set for the underlying file descriptor.
201 * If _maxlen_ is omitted, its default value is 65536.
202 * _flags_ is zero or more of the +MSG_+ options.
203 * The first element of the results, _mesg_, is the data received.
204 * The second element, _sender_inet_addr_, is an array to represent the sender address.
205 *
206 * When recvfrom(2) returns 0,
207 * Socket#recvfrom_nonblock returns an empty string as data.
208 * It means an empty packet.
209 *
210 * === Parameters
211 * * +maxlen+ - the number of bytes to receive from the socket
212 * * +flags+ - zero or more of the +MSG_+ options
213 *
214 * === Example
215 * 	require 'socket'
216 * 	s1 = UDPSocket.new
217 * 	s1.bind("127.0.0.1", 0)
218 * 	s2 = UDPSocket.new
219 * 	s2.bind("127.0.0.1", 0)
220 * 	s2.connect(*s1.addr.values_at(3,1))
221 * 	s1.connect(*s2.addr.values_at(3,1))
222 * 	s1.send "aaa", 0
223 * 	begin # emulate blocking recvfrom
224 * 	  p s2.recvfrom_nonblock(10)  #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]]
225 * 	rescue IO::WaitReadable
226 * 	  IO.select([s2])
227 * 	  retry
228 * 	end
229 *
230 * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
231 * to _recvfrom_nonblock_ fails.
232 *
233 * UDPSocket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure,
234 * including Errno::EWOULDBLOCK.
235 *
236 * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN,
237 * it is extended by IO::WaitReadable.
238 * So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock.
239 *
240 * === See
241 * * Socket#recvfrom
242 */
243static VALUE
244udp_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock)
245{
246    return rsock_s_recvfrom_nonblock(sock, argc, argv, RECV_IP);
247}
248
249void
250rsock_init_udpsocket(void)
251{
252    /*
253     * Document-class: UDPSocket < IPSocket
254     *
255     * UDPSocket represents a UDP/IP socket.
256     *
257     */
258    rb_cUDPSocket = rb_define_class("UDPSocket", rb_cIPSocket);
259    rb_define_method(rb_cUDPSocket, "initialize", udp_init, -1);
260    rb_define_method(rb_cUDPSocket, "connect", udp_connect, 2);
261    rb_define_method(rb_cUDPSocket, "bind", udp_bind, 2);
262    rb_define_method(rb_cUDPSocket, "send", udp_send, -1);
263    rb_define_method(rb_cUDPSocket, "recvfrom_nonblock", udp_recvfrom_nonblock, -1);
264}
265
266