/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- socket_finalize
- socket_print
- make_socket
- Scm_MakeSocket
- Scm_SocketShutdown
- Scm_SocketClose
- Scm_SocketInputPort
- Scm_SocketOutputPort
- Scm_SocketBind
- Scm_SocketListen
- Scm_SocketAccept
- Scm_SocketConnect
- Scm_SocketGetSockName
- Scm_SocketGetPeerName
- Scm_SocketSend
- Scm_SocketSendTo
- Scm_SocketRecv
- Scm_SocketRecvFrom
- Scm_SocketSetOpt
- Scm_SocketGetOpt
- inet_aton
- Scm_Init_libnet
1 /*
2 * net.c - network interface
3 *
4 * Copyright (c) 2000-2004 Shiro Kawai, All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the authors nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $Id: net.c,v 1.41 2005/10/13 08:14:13 shirok Exp $
34 */
35
36 #include "gauche/net.h"
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <gauche/extend.h>
40
41 /*==================================================================
42 * Socket
43 */
44
45 static void socket_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx);
46
47 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_SocketClass, socket_print);
48
49 static void socket_finalize(ScmObj obj, void *data)
50 {
51 ScmSocket *sock = (ScmSocket*)obj;
52 /* NB: at this point, sock->inPort and sock->outPort may already
53 be GC-ed and finalized, so we don't flush them here. */
54 if (!(SOCKET_CLOSED(sock->fd))) {
55 closeSocket(sock->fd);
56 sock->fd = INVALID_SOCKET;
57 sock->status = SCM_SOCKET_STATUS_CLOSED;
58 }
59 }
60
61 static void socket_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
62 {
63 ScmSocket *sock = SCM_SOCKET(obj);
64 Scm_Printf(port, "#<socket");
65 switch (sock->status) {
66 case SCM_SOCKET_STATUS_NONE:
67 break;
68 case SCM_SOCKET_STATUS_BOUND:
69 Scm_Printf(port, " (bound %S)", Scm_SockAddrName(sock->address));
70 break;
71 case SCM_SOCKET_STATUS_LISTENING:
72 Scm_Printf(port, " (listen %S)", Scm_SockAddrName(sock->address));
73 break;
74 case SCM_SOCKET_STATUS_CONNECTED:
75 Scm_Printf(port, " (connect %S)", Scm_SockAddrName(sock->address));
76 break;
77 case SCM_SOCKET_STATUS_SHUTDOWN:
78 Scm_Printf(port, " (shutdown)");
79 break;
80 case SCM_SOCKET_STATUS_CLOSED:
81 Scm_Printf(port, " (closed)");
82 break;
83 default:
84 Scm_Printf(port, " (unknown status)");
85 break;
86 }
87 Scm_Printf(port, ">");
88 }
89
90 ScmSocket *make_socket(Socket fd, int type)
91 {
92 ScmSocket *s = SCM_NEW(ScmSocket);
93 SCM_SET_CLASS(s, SCM_CLASS_SOCKET);
94 s->fd = fd;
95 s->status = SCM_SOCKET_STATUS_NONE;
96 s->inPort = s->outPort = NULL;
97 s->address = NULL;
98 s->name = NULL;
99 s->type = type;
100 Scm_RegisterFinalizer(SCM_OBJ(s), socket_finalize, NULL);
101 return s;
102 }
103
104 ScmObj Scm_MakeSocket(int domain, int type, int protocol)
105 {
106 int sock;
107 SCM_SYSCALL(sock, socket(domain, type, protocol));
108 if (SOCKET_INVALID(sock)) Scm_SysError("couldn't create socket");
109 return SCM_OBJ(make_socket(sock, type));
110 }
111
112 ScmObj Scm_SocketShutdown(ScmSocket *s, int how)
113 {
114 int r;
115 if (s->status != SCM_SOCKET_STATUS_CONNECTED) {
116 return SCM_FALSE;
117 }
118 SCM_SYSCALL(r, shutdown(s->fd, how));
119 if (r < 0) {
120 Scm_SysError("socket shutdown failed for %S", SCM_OBJ(s));
121 }
122 s->status = SCM_SOCKET_STATUS_SHUTDOWN;
123 return SCM_TRUE;
124 }
125
126 ScmObj Scm_SocketClose(ScmSocket *s)
127 {
128 if (s->status == SCM_SOCKET_STATUS_CLOSED) {
129 return SCM_FALSE;
130 }
131 /* We don't shutdown the connection; forked process may have
132 reference to the same socket. */
133 if (s->inPort) Scm_ClosePort(s->inPort); /* ignore errors */
134 if (s->outPort) Scm_ClosePort(s->outPort); /* ignore errors */
135 closeSocket(s->fd);
136 s->fd = INVALID_SOCKET;
137 s->status = SCM_SOCKET_STATUS_CLOSED;
138 return SCM_TRUE;
139 }
140
141 ScmObj Scm_SocketInputPort(ScmSocket *sock, int buffering)
142 {
143 if (sock->inPort == NULL) {
144 ScmObj sockname;
145 int infd;
146 if (sock->type != SOCK_DGRAM &&
147 sock->status < SCM_SOCKET_STATUS_CONNECTED) {
148 Scm_Error("attempt to obtain an input port from unconnected socket: %S",
149 SCM_OBJ(sock));
150 }
151 #ifndef __MINGW32__
152 infd = sock->fd;
153 #else /*__MINGW32__*/
154 infd = _open_osfhandle(sock->fd, O_RDONLY);
155 #endif /*__MINGW32__*/
156 /* NB: I keep the socket itself in the port name, in order to avoid
157 the socket from GCed prematurely if application doesn't keep
158 pointer to the socket. */
159 sockname = SCM_LIST2(SCM_MAKE_STR("socket input"), SCM_OBJ(sock));
160 sock->inPort = SCM_PORT(Scm_MakePortWithFd(sockname, SCM_PORT_INPUT,
161 infd, buffering,
162 FALSE));
163 }
164 return SCM_OBJ(sock->inPort);
165 }
166
167 ScmObj Scm_SocketOutputPort(ScmSocket *sock, int buffering)
168 {
169 if (sock->outPort == NULL) {
170 ScmObj sockname;
171 int outfd;
172 if (sock->type != SOCK_DGRAM &&
173 sock->status < SCM_SOCKET_STATUS_CONNECTED) {
174 Scm_Error("attempt to obtain an output port from an unconnected socket: %S",
175 SCM_OBJ(sock));
176 }
177 #ifndef __MINGW32__
178 outfd = sock->fd;
179 #else /*__MINGW32__*/
180 outfd = _open_osfhandle(sock->fd, 0);
181 #endif /*__MINGW32__*/
182
183 /* NB: I keep the socket itself in the port name, in order to avoid
184 the socket from GCed prematurely if application doesn't keep
185 pointer to the socket. */
186 sockname = SCM_LIST2(SCM_MAKE_STR("socket output"), SCM_OBJ(sock));
187 sock->outPort = SCM_PORT(Scm_MakePortWithFd(sockname, SCM_PORT_OUTPUT,
188 outfd, buffering, FALSE));
189 }
190 return SCM_OBJ(sock->outPort);
191 }
192
193 /*==================================================================
194 * Low-level library
195 */
196
197 ScmObj Scm_SocketBind(ScmSocket *sock, ScmSockAddr *addr)
198 {
199 ScmSockAddr *naddr;
200 int r;
201
202 if (SOCKET_CLOSED(sock->fd)) {
203 Scm_Error("attempt to bind a closed socket: %S", sock);
204 }
205 SCM_SYSCALL(r, bind(sock->fd, &addr->addr, addr->addrlen));
206 if (r < 0) {
207 Scm_SysError("bind failed to %S", addr);
208 }
209 /* The system may assign different address than <addr>, especially when
210 <addr> contains some 'wild card' (e.g. port=0). We call getsockname
211 to obtain the exact address. Patch provided by ODA Hideo */
212 naddr = SCM_SOCKADDR(Scm_MakeSockAddr(SCM_CLASS_OF(addr),
213 &addr->addr, addr->addrlen));
214 SCM_SYSCALL(r, getsockname(sock->fd, &naddr->addr, &naddr->addrlen));
215 if (r < 0) {
216 Scm_SysError("getsockname failed to %S", addr);
217 }
218 sock->address = naddr;
219 sock->status = SCM_SOCKET_STATUS_BOUND;
220 return SCM_OBJ(sock);
221 }
222
223 ScmObj Scm_SocketListen(ScmSocket *sock, int backlog)
224 {
225 int r;
226 if (SOCKET_CLOSED(sock->fd)) {
227 Scm_Error("attempt to listen a closed socket: %S", sock);
228 }
229 SCM_SYSCALL(r, listen(sock->fd, backlog));
230 if (r < 0) {
231 Scm_SysError("listen(2) failed");
232 }
233 sock->status = SCM_SOCKET_STATUS_LISTENING;
234 return SCM_OBJ(sock);
235 }
236
237 ScmObj Scm_SocketAccept(ScmSocket *sock)
238 {
239 const char addrbuf[SCM_SOCKADDR_MAXLEN];
240 int newfd;
241 socklen_t addrlen = SCM_SOCKADDR_MAXLEN;
242 ScmSocket *newsock;
243 ScmClass *addrClass = Scm_ClassOf(SCM_OBJ(sock->address));
244
245 if (SOCKET_CLOSED(sock->fd)) {
246 Scm_Error("attempt to accept a closed socket: %S", sock);
247 }
248 SCM_SYSCALL(newfd, accept(sock->fd, (struct sockaddr *)addrbuf, &addrlen));
249 if (SOCKET_INVALID(newfd)) {
250 if (errno == EAGAIN) {
251 return SCM_FALSE;
252 } else {
253 Scm_SysError("accept(2) failed");
254 }
255 }
256 newsock = make_socket(newfd, sock->type);
257 newsock->address =
258 SCM_SOCKADDR(Scm_MakeSockAddr(addrClass,
259 (struct sockaddr *)addrbuf,
260 addrlen));
261 newsock->status = SCM_SOCKET_STATUS_CONNECTED;
262 return SCM_OBJ(newsock);
263 }
264
265 ScmObj Scm_SocketConnect(ScmSocket *sock, ScmSockAddr *addr)
266 {
267 int r;
268 if (SOCKET_CLOSED(sock->fd)) {
269 Scm_Error("attempt to connect a closed socket: %S", sock);
270 }
271 SCM_SYSCALL(r, connect(sock->fd, &addr->addr, addr->addrlen));
272 if (r < 0) {
273 Scm_SysError("connect failed to %S", addr);
274 }
275 sock->address = addr;
276 sock->status = SCM_SOCKET_STATUS_CONNECTED;
277 return SCM_OBJ(sock);
278 }
279
280 ScmObj Scm_SocketGetSockName(ScmSocket *sock)
281 {
282 const char addrbuf[SCM_SOCKADDR_MAXLEN];
283 int r;
284 socklen_t addrlen = SCM_SOCKADDR_MAXLEN;
285
286 if (SOCKET_CLOSED(sock->fd)) {
287 Scm_Error("attempt to get the name of a closed socket: %S", sock);
288 }
289 SCM_SYSCALL(r, getsockname(sock->fd, (struct sockaddr *)addrbuf, &addrlen));
290 if (r < 0) {
291 Scm_SysError("getsockname(2) failed");
292 }
293 return SCM_OBJ(Scm_MakeSockAddr(NULL, (struct sockaddr *)addrbuf, addrlen));
294 }
295
296 ScmObj Scm_SocketGetPeerName(ScmSocket *sock)
297 {
298 const char addrbuf[SCM_SOCKADDR_MAXLEN];
299 int r;
300 socklen_t addrlen = SCM_SOCKADDR_MAXLEN;
301
302 if (SOCKET_CLOSED(sock->fd)) {
303 Scm_Error("attempt to get the name of a closed socket: %S", sock);
304 }
305 SCM_SYSCALL(r, getpeername(sock->fd, (struct sockaddr *)addrbuf, &addrlen));
306 if (r < 0) {
307 Scm_SysError("getpeername(2) failed");
308 }
309 return SCM_OBJ(Scm_MakeSockAddr(NULL, (struct sockaddr *)addrbuf, addrlen));
310 }
311
312 ScmObj Scm_SocketSend(ScmSocket *sock, ScmString *msg, int flags)
313 {
314 int r; u_int size;
315 const char *cmsg;
316 if (SOCKET_CLOSED(sock->fd)) {
317 Scm_Error("attempt to send to a closed socket: %S", sock);
318 }
319 cmsg = Scm_GetStringContent(msg, &size, NULL, NULL);
320 SCM_SYSCALL(r, send(sock->fd, cmsg, size, flags));
321 if (r < 0) {
322 Scm_SysError("send(2) failed");
323 }
324 return SCM_MAKE_INT(r);
325 }
326
327 ScmObj Scm_SocketSendTo(ScmSocket *sock, ScmString *msg, ScmSockAddr *to,
328 int flags)
329 {
330 int r; u_int size;
331 const char *cmsg;
332 if (SOCKET_CLOSED(sock->fd)) {
333 Scm_Error("attempt to send to a closed socket: %S", sock);
334 }
335 cmsg = Scm_GetStringContent(msg, &size, NULL, NULL);
336 SCM_SYSCALL(r, sendto(sock->fd, cmsg, size, flags,
337 &SCM_SOCKADDR(to)->addr, SCM_SOCKADDR(to)->addrlen));
338 if (r < 0) {
339 Scm_SysError("sendto(2) failed");
340 }
341 return SCM_MAKE_INT(r);
342 }
343
344 ScmObj Scm_SocketRecv(ScmSocket *sock, int bytes, int flags)
345 {
346 int r;
347 char *buf;
348 if (SOCKET_CLOSED(sock->fd)) {
349 Scm_Error("attempt to recv from a closed socket: %S", sock);
350 }
351 buf = SCM_NEW_ATOMIC2(char*, bytes);
352 SCM_SYSCALL(r, recv(sock->fd, buf, bytes, flags));
353 if (r < 0) {
354 Scm_SysError("recv(2) failed");
355 }
356 return Scm_MakeString(buf, r, r, SCM_MAKSTR_INCOMPLETE);
357 }
358
359 ScmObj Scm_SocketRecvFrom(ScmSocket *sock, int bytes, int flags)
360 {
361 int r;
362 char *buf;
363 struct sockaddr from;
364 socklen_t fromlen = sizeof(from);
365 if (SOCKET_CLOSED(sock->fd)) {
366 Scm_Error("attempt to recv from a closed socket: %S", sock);
367 }
368 buf = SCM_NEW_ATOMIC2(char*, bytes);
369 SCM_SYSCALL(r, recvfrom(sock->fd, buf, bytes, flags, &from, &fromlen));
370 if (r < 0) {
371 Scm_SysError("recvfrom(2) failed");
372 }
373 return Scm_Values2(Scm_MakeString(buf, r, r, SCM_MAKSTR_INCOMPLETE),
374 Scm_MakeSockAddr(NULL, &from, fromlen));
375 }
376
377 /* Low-level setsockopt() and getsockopt() interface. */
378 /* for getsockopt(), we need to know the size of the result.
379 if rtype > 0, it is used as the size of result buffer and
380 a string value is returned. if rtype == 0, the result value
381 assumed to be an integer. */
382
383 ScmObj Scm_SocketSetOpt(ScmSocket *s, int level, int option, ScmObj value)
384 {
385 int r = 0;
386 if (SOCKET_CLOSED(s->fd)) {
387 Scm_Error("attempt to set a socket option of a closed socket: %S", s);
388 }
389 if (SCM_STRINGP(value)) {
390 u_int size;
391 const char *cvalue = Scm_GetStringContent(SCM_STRING(value), &size,
392 NULL, NULL);
393 SCM_SYSCALL(r, setsockopt(s->fd, level, option, cvalue, size));
394 } else if (SCM_INTP(value) || SCM_BIGNUMP(value)) {
395 int v = Scm_GetInteger(value);
396 SCM_SYSCALL(r, setsockopt(s->fd, level, option, &v, sizeof(int)));
397 } else {
398 Scm_Error("socket option must be a string or an integer: %S", value);
399 }
400 if (r < 0) Scm_SysError("setsockopt failed");
401 return SCM_TRUE;
402 }
403
404 ScmObj Scm_SocketGetOpt(ScmSocket *s, int level, int option, int rsize)
405 {
406 int r = 0;
407 socklen_t rrsize = rsize;
408 if (SOCKET_CLOSED(s->fd)) {
409 Scm_Error("attempt to get a socket option of a closed socket: %S", s);
410 }
411 if (rsize > 0) {
412 char *buf = SCM_NEW_ATOMIC2(char *, rrsize);
413 SCM_SYSCALL(r, getsockopt(s->fd, level, option, buf, &rrsize));
414 if (r < 0) Scm_SysError("getsockopt failed");
415 return Scm_MakeString(buf, rrsize, rrsize, SCM_MAKSTR_INCOMPLETE);
416 } else {
417 int val;
418 rrsize = sizeof(int);
419 SCM_SYSCALL(r, getsockopt(s->fd, level, option, &val, &rrsize));
420 if (r < 0) Scm_SysError("getsockopt failed");
421 return Scm_MakeInteger(val);
422 }
423 }
424
425 /*==================================================================
426 * Windows/MinGW compatibility layer
427 */
428 #ifdef __MINGW32__
429
430 /*
431 * I should use WSAStringToAddress, but just for the time being...
432 */
433 int inet_aton(const char *cp, struct in_addr *inp)
434 {
435 unsigned long r = inet_addr(cp);
436 if (r == (unsigned long)-1) {
437 return 0;
438 } else {
439 inp->s_addr = r;
440 return 1;
441 }
442 }
443
444 /* winsock requires some obscure initialization */
445 static WSADATA wsaData;
446
447 #endif /*__MINGW32__*/
448
449 /*==================================================================
450 * Initialization
451 */
452
453 extern void Scm_Init_NetAddr(ScmModule *mod);
454 extern void Scm_Init_NetDB(ScmModule *mod);
455 extern void Scm_Init_netlib(ScmModule *mod);
456 extern void Scm_Init_netaux(void);
457
458 void Scm_Init_libnet(void)
459 {
460 ScmModule *mod;
461
462 SCM_INIT_EXTENSION(net);
463 mod = SCM_FIND_MODULE("gauche.net", SCM_FIND_MODULE_CREATE);
464 #ifdef __MINGW32__
465 /* NB: I'm supposed to call WSACleanup when application shuts down,
466 or resource leak would happen, according to the Windows document.
467 It's just ridiculous---why can't OS itself clean up the dead process?
468 Anyway, to behave politically correctly, I need a generic callback
469 mechanism for Gauche core to call cleanup routines in Scm_Exit.
470 For now, let us behave like a wild kid.
471 */
472 {
473 int opt;
474 int r = WSAStartup(MAKEWORD(2,2), &wsaData);
475 if (r != 0) {
476 SetLastError(r);
477 Scm_SysError("WSAStartup failed");
478 }
479 /* windows voodoo to make _open_osfhandle magic work */
480 opt = SO_SYNCHRONOUS_NONALERT;
481 r = setsockopt(INVALID_SOCKET, SOL_SOCKET,
482 SO_OPENTYPE, (char*)&opt, sizeof(opt));
483 if (r == SOCKET_ERROR) {
484 Scm_SysError("winsock initialization failed");
485 }
486 }
487 #endif /*__MINGW32__*/
488 Scm_InitStaticClass(&Scm_SocketClass, "<socket>", mod, NULL, 0);
489 Scm_Init_NetAddr(mod);
490 Scm_Init_NetDB(mod);
491 Scm_Init_netlib(mod);
492 Scm_Init_netaux();
493 }