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