/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- sockaddr_print
- Scm_SockAddrP
- Scm_SockAddrName
- Scm_SockAddrFamily
- sockaddr_allocate
- Scm_MakeSockAddr
- sockaddr_un_allocate
- sockaddr_in_allocate
- sockaddr_in6_allocate
- Scm_Init_NetAddr
1 /*
2 * addr.c - socket address
3 *
4 * Copyright(C) 2001-2004 by Shiro Kawai (shiro@acm.org)
5 *
6 * Permission to use, copy, modify, distribute this software and
7 * accompanying documentation for any purpose is hereby granted,
8 * provided that existing copyright notices are retained in all
9 * copies and that this notice is included verbatim in all
10 * distributions.
11 * This software is provided as is, without express or implied
12 * warranty. In no circumstances the author(s) shall be liable
13 * for any damages arising out of the use of this software.
14 *
15 * $Id: addr.c,v 1.22 2005/10/13 08:14:13 shirok Exp $
16 */
17
18 #include "gauche/net.h"
19 #include <string.h>
20
21 static ScmObj key_path = SCM_FALSE;
22 static ScmObj key_host = SCM_FALSE;
23 static ScmObj key_port = SCM_FALSE;
24 static ScmObj key_any = SCM_FALSE;
25 static ScmObj key_broadcast = SCM_FALSE;
26 static ScmObj key_loopback = SCM_FALSE;
27
28 /*==================================================================
29 * Generic Socket Address
30 */
31
32 static void sockaddr_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx);
33 static ScmObj sockaddr_allocate(ScmClass *, ScmObj);
34
35 ScmClass *Scm_SockAddrCPL[] = {
36 SCM_CLASS_STATIC_PTR(Scm_SockAddrClass),
37 SCM_CLASS_STATIC_PTR(Scm_TopClass),
38 NULL
39 };
40
41 SCM_DEFINE_BUILTIN_CLASS(Scm_SockAddrClass, sockaddr_print,
42 NULL, NULL, sockaddr_allocate,
43 NULL);
44
45 void sockaddr_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
46 {
47 Scm_Printf(port, "#<sockaddr %S %S>",
48 Scm_SockAddrFamily(SCM_SOCKADDR(obj)),
49 Scm_SockAddrName(SCM_SOCKADDR(obj)));
50 }
51
52 int Scm_SockAddrP(ScmObj obj)
53 {
54 return Scm_SubtypeP(Scm_ClassOf(obj), SCM_CLASS_SOCKADDR);
55 }
56
57 /* C interface of sockaddr-name and sockaddr-family */
58 ScmObj Scm_SockAddrName(ScmSockAddr *addr)
59 {
60 return Scm_Apply(SCM_OBJ(&Scm_GenericSockAddrName),
61 SCM_LIST1(SCM_OBJ(addr)));
62 }
63
64 ScmObj Scm_SockAddrFamily(ScmSockAddr *addr)
65 {
66 return Scm_Apply(SCM_OBJ(&Scm_GenericSockAddrFamily),
67 SCM_LIST1(SCM_OBJ(addr)));
68 }
69
70 /* Fallback of allocation method */
71 static ScmObj sockaddr_allocate(ScmClass *klass, ScmObj initargs)
72 {
73 Scm_Error("you can't directly instantiate the abstract class <sockaddr>");
74 return SCM_UNDEFINED; /* dummy */
75 }
76
77 /* creates sockaddr from struct sockaddr. */
78 ScmObj Scm_MakeSockAddr(ScmClass *klass, struct sockaddr *saddr, int len)
79 {
80 ScmSockAddr *addr;
81 addr = SCM_NEW2(ScmSockAddr*,
82 sizeof(ScmSockAddr) - sizeof(struct sockaddr) + len);
83 if (klass == NULL) {
84 switch (saddr->sa_family) {
85 case AF_UNIX:
86 klass = SCM_CLASS_SOCKADDR_UN;
87 break;
88 case AF_INET:
89 klass = SCM_CLASS_SOCKADDR_IN;
90 break;
91 #ifdef HAVE_IPV6
92 case AF_INET6:
93 klass = SCM_CLASS_SOCKADDR_IN6;
94 break;
95 #endif
96 default:
97 Scm_Error("unknown address type (%d)", saddr->sa_family);
98 break;
99 }
100 }
101 SCM_SET_CLASS(addr, klass);
102 addr->addrlen = len;
103 memset(&addr->addr, 0, len);
104 memcpy(&addr->addr, saddr, len);
105 return SCM_OBJ(addr);
106 }
107
108 /*==================================================================
109 * Unix domain socket
110 */
111
112 #define UNIX_ADDRESS_PATH_MAX 108
113 static ScmObj sockaddr_un_allocate(ScmClass *klass, ScmObj initargs);
114
115 SCM_DEFINE_BUILTIN_CLASS(Scm_SockAddrUnClass, sockaddr_print,
116 NULL, NULL, sockaddr_un_allocate, Scm_SockAddrCPL);
117
118 static ScmObj sockaddr_un_allocate(ScmClass *klass, ScmObj initargs)
119 {
120 ScmObj path = Scm_GetKeyword(key_path, initargs, SCM_FALSE);
121 ScmSockAddrUn *addr;
122
123 if (!SCM_FALSEP(path) && !SCM_STRINGP(path)) {
124 Scm_Error(":path parameter must be a string, but got %S", path);
125 }
126 addr = SCM_NEW(ScmSockAddrUn);
127 SCM_SET_CLASS(addr, &Scm_SockAddrUnClass);
128 memset(&addr->addr, 0, sizeof(struct sockaddr_un));
129 addr->addr.sun_family = AF_UNIX;
130 if (SCM_STRINGP(path)) {
131 int size;
132 const char *cpath = Scm_GetStringContent(SCM_STRING(path), &size,
133 NULL, NULL);
134 if (size >= UNIX_ADDRESS_PATH_MAX-1) {
135 Scm_Error("path too long: %S", path);
136 }
137 memcpy(addr->addr.sun_path, cpath, size);
138 addr->addr.sun_path[size] = '\0';
139 }
140 addr->addrlen = sizeof(struct sockaddr_un);
141 return SCM_OBJ(addr);
142 }
143
144 /*==================================================================
145 * Inet domain socket
146 */
147
148 static ScmObj sockaddr_in_allocate(ScmClass *klass, ScmObj initargs);
149
150 SCM_DEFINE_BUILTIN_CLASS(Scm_SockAddrInClass, sockaddr_print,
151 NULL, NULL, sockaddr_in_allocate, Scm_SockAddrCPL);
152
153 static ScmObj sockaddr_in_allocate(ScmClass *klass, ScmObj initargs)
154 {
155 ScmObj host = Scm_GetKeyword(key_host, initargs, key_any);
156 ScmObj port = Scm_GetKeyword(key_port, initargs, SCM_MAKE_INT(0));
157 ScmSockAddrIn *addr;
158
159 if (!SCM_INTP(port)) {
160 Scm_Error(":port parameter must be a small exact integer, but got %S",
161 port);
162 }
163 addr = SCM_NEW(ScmSockAddrIn);
164 SCM_SET_CLASS(addr, &Scm_SockAddrInClass);
165 memset(&addr->addr, 0, sizeof(struct sockaddr_in));
166 #ifdef HAVE_SIN_LEN
167 addr->addr.sin_len = sizeof(struct sockaddr_in);
168 #endif
169 addr->addr.sin_family = AF_INET;
170 addr->addr.sin_port = htons(SCM_INT_VALUE(port));
171 if (SCM_STRINGP(host)) {
172 const char *hname = Scm_GetStringConst(SCM_STRING(host));
173 /* First, see if host is dotted number notation. */
174 if (inet_aton(hname, &addr->addr.sin_addr) == 0) {
175 /* Now, we need to look up the host name.
176 Call MT-safe Scm_GetHostByName */
177 ScmObj ap, hent = Scm_GetHostByName(hname);
178 if (!SCM_SYS_HOSTENT_P(hent)) {
179 Scm_Error("unknown host: %S", host);
180 }
181 ap = SCM_SYS_HOSTENT(hent)->addresses;
182 if (SCM_NULLP(ap) || !SCM_STRINGP(SCM_CAR(ap))) {
183 Scm_Error("host have unknown address type: %S", host);
184 }
185 hname = Scm_GetStringConst(SCM_STRING(SCM_CAR(ap)));
186 if (inet_aton(hname, &addr->addr.sin_addr) == 0) {
187 Scm_Error("host name lookup failure: %S", host);
188 }
189 }
190 } else if (host == key_any) {
191 addr->addr.sin_addr.s_addr = htonl(INADDR_ANY);
192 } else if (host == key_broadcast) {
193 addr->addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
194 } else if (host == key_loopback) {
195 addr->addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
196 } else {
197 Scm_Error("bad :host parameter: %S", host);
198 }
199 addr->addrlen = sizeof(struct sockaddr_in);
200 return SCM_OBJ(addr);
201 }
202
203 /*==================================================================
204 * Inet6 domain socket
205 */
206
207 #ifdef HAVE_IPV6
208
209 static ScmObj sockaddr_in6_allocate(ScmClass *klass, ScmObj initargs);
210
211 SCM_DEFINE_BUILTIN_CLASS(Scm_SockAddrIn6Class, sockaddr_print,
212 NULL, NULL, sockaddr_in6_allocate, Scm_SockAddrCPL);
213
214 static ScmObj sockaddr_in6_allocate(ScmClass *klass, ScmObj initargs)
215 {
216 ScmObj host = Scm_GetKeyword(key_host, initargs, key_any);
217 ScmObj port = Scm_GetKeyword(key_port, initargs, SCM_MAKE_INT(0));
218 ScmSockAddrIn6 *addr;
219
220 if (!SCM_INTP(port)) {
221 Scm_Error(":port parameter must be a small exact integer, but got %S",
222 port);
223 }
224 addr = SCM_NEW(ScmSockAddrIn6);
225 SCM_SET_CLASS(addr, &Scm_SockAddrIn6Class);
226 memset(&addr->addr, 0, sizeof(struct sockaddr_in6));
227 #ifdef HAVE_SIN6_LEN
228 addr->addr.sin6_len = sizeof(struct sockaddr_in6);
229 #endif
230 addr->addr.sin6_family = AF_INET6;
231 addr->addr.sin6_port = htons(SCM_INT_VALUE(port));
232 if (SCM_STRINGP(host)) {
233 const char *hname = Scm_GetStringConst(SCM_STRING(host));
234 struct addrinfo hints, *res;
235 int r;
236 memset(&hints, 0, sizeof(hints));
237 hints.ai_family = AF_INET6;
238 hints.ai_socktype = SOCK_STREAM;
239 r = getaddrinfo(hname, NULL, &hints, &res);
240 if (r) Scm_Error("getaddrinfo: %s", gai_strerror(r));
241 addr->addr.sin6_addr = ((struct sockaddr_in6*)res->ai_addr)->sin6_addr;
242 } else if (host == key_any) {
243 addr->addr.sin6_addr = in6addr_any;
244 } else if (host == key_loopback) {
245 addr->addr.sin6_addr = in6addr_loopback;
246 } else {
247 Scm_Error("bad :host parameter: %S", host);
248 }
249 addr->addrlen = sizeof(struct sockaddr_in6);
250 return SCM_OBJ(addr);
251 }
252
253 #endif /* HAVE_IPV6 */
254
255 /*==================================================================
256 * Initialization stuff
257 */
258
259 void Scm_Init_NetAddr(ScmModule *mod)
260 {
261 key_path = SCM_MAKE_KEYWORD("path");
262 key_host = SCM_MAKE_KEYWORD("host");
263 key_port = SCM_MAKE_KEYWORD("port");
264 key_any = SCM_MAKE_KEYWORD("any");
265 key_broadcast = SCM_MAKE_KEYWORD("broadcast");
266 key_loopback = SCM_MAKE_KEYWORD("loopback");
267
268 Scm_InitStaticClass(&Scm_SockAddrClass, "<sockaddr>", mod, NULL, 0);
269 Scm_InitStaticClass(&Scm_SockAddrUnClass, "<sockaddr-un>", mod, NULL, 0);
270 Scm_InitStaticClass(&Scm_SockAddrInClass, "<sockaddr-in>", mod, NULL, 0);
271 #ifdef HAVE_IPV6
272 Scm_InitStaticClass(&Scm_SockAddrIn6Class, "<sockaddr-in6>", mod, NULL, 0);
273 #endif /* HAVE_IPV6 */
274 }
275