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  *   require 'socket'
23  *
24  *   UDPSocket.new                   #=> #<UDPSocket:fd 3>
25  *   UDPSocket.new(Socket::AF_INET6) #=> #<UDPSocket:fd 4>
26  *
27  */
28 static VALUE
udp_init(int argc,VALUE * argv,VALUE sock)29 udp_init(int argc, VALUE *argv, VALUE sock)
30 {
31     VALUE arg;
32     int family = AF_INET;
33     int fd;
34 
35     if (rb_scan_args(argc, argv, "01", &arg) == 1) {
36 	family = rsock_family_arg(arg);
37     }
38     fd = rsock_socket(family, SOCK_DGRAM, 0);
39     if (fd < 0) {
40 	rb_sys_fail("socket(2) - udp");
41     }
42 
43     return rsock_init_sock(sock, fd);
44 }
45 
46 struct udp_arg
47 {
48     struct rb_addrinfo *res;
49     rb_io_t *fptr;
50 };
51 
52 static VALUE
udp_connect_internal(struct udp_arg * arg)53 udp_connect_internal(struct udp_arg *arg)
54 {
55     rb_io_t *fptr;
56     int fd;
57     struct addrinfo *res;
58 
59     rb_io_check_closed(fptr = arg->fptr);
60     fd = fptr->fd;
61     for (res = arg->res->ai; res; res = res->ai_next) {
62 	if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0) >= 0) {
63 	    return Qtrue;
64 	}
65     }
66     return Qfalse;
67 }
68 
69 /*
70  * call-seq:
71  *   udpsocket.connect(host, port) => 0
72  *
73  * Connects _udpsocket_ to _host_:_port_.
74  *
75  * This makes possible to send without destination address.
76  *
77  *   u1 = UDPSocket.new
78  *   u1.bind("127.0.0.1", 4913)
79  *   u2 = UDPSocket.new
80  *   u2.connect("127.0.0.1", 4913)
81  *   u2.send "uuuu", 0
82  *   p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
83  *
84  */
85 static VALUE
udp_connect(VALUE sock,VALUE host,VALUE port)86 udp_connect(VALUE sock, VALUE host, VALUE port)
87 {
88     struct udp_arg arg;
89     VALUE ret;
90 
91     GetOpenFile(sock, arg.fptr);
92     arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0);
93     ret = rb_ensure(udp_connect_internal, (VALUE)&arg,
94 		    rsock_freeaddrinfo, (VALUE)arg.res);
95     if (!ret) rsock_sys_fail_host_port("connect(2)", host, port);
96     return INT2FIX(0);
97 }
98 
99 static VALUE
udp_bind_internal(struct udp_arg * arg)100 udp_bind_internal(struct udp_arg *arg)
101 {
102     rb_io_t *fptr;
103     int fd;
104     struct addrinfo *res;
105 
106     rb_io_check_closed(fptr = arg->fptr);
107     fd = fptr->fd;
108     for (res = arg->res->ai; res; res = res->ai_next) {
109 	if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
110 	    continue;
111 	}
112 	return Qtrue;
113     }
114     return Qfalse;
115 }
116 
117 /*
118  * call-seq:
119  *   udpsocket.bind(host, port) #=> 0
120  *
121  * Binds _udpsocket_ to _host_:_port_.
122  *
123  *   u1 = UDPSocket.new
124  *   u1.bind("127.0.0.1", 4913)
125  *   u1.send "message-to-self", 0, "127.0.0.1", 4913
126  *   p u1.recvfrom(10) #=> ["message-to", ["AF_INET", 4913, "localhost", "127.0.0.1"]]
127  *
128  */
129 static VALUE
udp_bind(VALUE sock,VALUE host,VALUE port)130 udp_bind(VALUE sock, VALUE host, VALUE port)
131 {
132     struct udp_arg arg;
133     VALUE ret;
134 
135     GetOpenFile(sock, arg.fptr);
136     arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0);
137     ret = rb_ensure(udp_bind_internal, (VALUE)&arg,
138 		    rsock_freeaddrinfo, (VALUE)arg.res);
139     if (!ret) rsock_sys_fail_host_port("bind(2)", host, port);
140     return INT2FIX(0);
141 }
142 
143 struct udp_send_arg {
144     struct rb_addrinfo *res;
145     rb_io_t *fptr;
146     struct rsock_send_arg sarg;
147 };
148 
149 static VALUE
udp_send_internal(struct udp_send_arg * arg)150 udp_send_internal(struct udp_send_arg *arg)
151 {
152     rb_io_t *fptr;
153     int n;
154     struct addrinfo *res;
155 
156     rb_io_check_closed(fptr = arg->fptr);
157     for (res = arg->res->ai; res; res = res->ai_next) {
158       retry:
159 	arg->sarg.fd = fptr->fd;
160 	arg->sarg.to = res->ai_addr;
161 	arg->sarg.tolen = res->ai_addrlen;
162 	rsock_maybe_fd_writable(arg->sarg.fd);
163 	n = (int)BLOCKING_REGION_FD(rsock_sendto_blocking, &arg->sarg);
164 	if (n >= 0) {
165 	    return INT2FIX(n);
166 	}
167 	if (rb_io_wait_writable(fptr->fd)) {
168 	    goto retry;
169 	}
170     }
171     return Qfalse;
172 }
173 
174 /*
175  * call-seq:
176  *   udpsocket.send(mesg, flags, host, port)  => numbytes_sent
177  *   udpsocket.send(mesg, flags, sockaddr_to) => numbytes_sent
178  *   udpsocket.send(mesg, flags)              => numbytes_sent
179  *
180  * Sends _mesg_ via _udpsocket_.
181  *
182  * _flags_ should be a bitwise OR of Socket::MSG_* constants.
183  *
184  *   u1 = UDPSocket.new
185  *   u1.bind("127.0.0.1", 4913)
186  *
187  *   u2 = UDPSocket.new
188  *   u2.send "hi", 0, "127.0.0.1", 4913
189  *
190  *   mesg, addr = u1.recvfrom(10)
191  *   u1.send mesg, 0, addr[3], addr[1]
192  *
193  *   p u2.recv(100) #=> "hi"
194  *
195  */
196 static VALUE
udp_send(int argc,VALUE * argv,VALUE sock)197 udp_send(int argc, VALUE *argv, VALUE sock)
198 {
199     VALUE flags, host, port;
200     struct udp_send_arg arg;
201     VALUE ret;
202 
203     if (argc == 2 || argc == 3) {
204 	return rsock_bsock_send(argc, argv, sock);
205     }
206     rb_scan_args(argc, argv, "4", &arg.sarg.mesg, &flags, &host, &port);
207 
208     StringValue(arg.sarg.mesg);
209     GetOpenFile(sock, arg.fptr);
210     arg.sarg.fd = arg.fptr->fd;
211     arg.sarg.flags = NUM2INT(flags);
212     arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0);
213     ret = rb_ensure(udp_send_internal, (VALUE)&arg,
214 		    rsock_freeaddrinfo, (VALUE)arg.res);
215     if (!ret) rsock_sys_fail_host_port("sendto(2)", host, port);
216     return ret;
217 }
218 
219 /* :nodoc: */
220 static VALUE
udp_recvfrom_nonblock(VALUE sock,VALUE len,VALUE flg,VALUE str,VALUE ex)221 udp_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex)
222 {
223     return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_IP);
224 }
225 
226 void
rsock_init_udpsocket(void)227 rsock_init_udpsocket(void)
228 {
229     /*
230      * Document-class: UDPSocket < IPSocket
231      *
232      * UDPSocket represents a UDP/IP socket.
233      *
234      */
235     rb_cUDPSocket = rb_define_class("UDPSocket", rb_cIPSocket);
236     rb_define_method(rb_cUDPSocket, "initialize", udp_init, -1);
237     rb_define_method(rb_cUDPSocket, "connect", udp_connect, 2);
238     rb_define_method(rb_cUDPSocket, "bind", udp_bind, 2);
239     rb_define_method(rb_cUDPSocket, "send", udp_send, -1);
240 
241     /* for ext/socket/lib/socket.rb use only: */
242     rb_define_private_method(rb_cUDPSocket,
243 			     "__recvfrom_nonblock", udp_recvfrom_nonblock, 4);
244 }
245