/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- make_hostent
- Scm_GetHostByName
- Scm_GetHostByAddr
- hostent_name
- hostent_aliases
- hostent_addresses
- make_protoent
- Scm_GetProtoByName
- Scm_GetProtoByNumber
- protoent_name
- protoent_aliases
- protoent_proto
- make_servent
- Scm_GetServByName
- Scm_GetServByPort
- servent_name
- servent_aliases
- servent_port
- servent_proto
- addrinfo_allocate
- make_addrinfo
- Scm_GetAddrinfo
- Scm_GetNameinfo
- Scm_Init_NetDB
1 /*
2 * netdb.c - obtain information about network
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: netdb.c,v 1.12 2005/11/02 06:03:26 shirok Exp $
34 */
35
36 #include "gauche/net.h"
37 #include <gauche/class.h>
38
39 #define DATA_BUFSIZ 980
40
41 static struct netdb_data_rec {
42 int dummy; /* dummy var to make sure this structure is
43 placed in data area. */
44 ScmInternalMutex hostent_mutex;
45 ScmInternalMutex protoent_mutex;
46 ScmInternalMutex servent_mutex;
47 } netdb_data = { 1 };
48
49 #define WITH_GLOBAL_LOCK(mutex, body) \
50 SCM_UNWIND_PROTECT { \
51 SCM_INTERNAL_MUTEX_LOCK(mutex); \
52 body; \
53 } SCM_WHEN_ERROR { \
54 SCM_INTERNAL_MUTEX_UNLOCK(mutex); \
55 SCM_NEXT_HANDLER; \
56 } SCM_END_PROTECT; \
57 SCM_INTERNAL_MUTEX_UNLOCK(mutex)
58
59 /*-------------------------------------------------------------
60 * Hostent
61 */
62
63 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_SysHostentClass, NULL);
64
65 static ScmSysHostent *make_hostent(struct hostent *he)
66 {
67 ScmSysHostent *entry = SCM_NEW(ScmSysHostent);
68 ScmObj h = SCM_NIL, t = SCM_NIL;
69 char **p;
70
71 SCM_SET_CLASS(entry, SCM_CLASS_SYS_HOSTENT);
72 entry->name = SCM_MAKE_STR_COPYING(he->h_name);
73 for (p = he->h_aliases; *p; p++) {
74 SCM_APPEND1(h, t, SCM_MAKE_STR_COPYING(*p));
75 }
76 entry->aliases = h;
77 h = t = SCM_NIL;
78 if (he->h_addrtype == AF_INET) {
79 for (p = he->h_addr_list; *p; p++) {
80 char buf[50];
81 struct in_addr *addr = (struct in_addr*)*p;
82 unsigned long addrval = ntohl(addr->s_addr);
83 /* avoid using inet_ntoa, for it is not reentrant */
84 snprintf(buf, 50, "%d.%d.%d.%d",
85 ((addrval >> 24)& 0xff), ((addrval >> 16) & 0xff),
86 ((addrval >> 8) & 0xff), (addrval & 0xff));
87 SCM_APPEND1(h, t, SCM_MAKE_STR_COPYING(buf));
88 }
89 } else {
90 /* soon AF_INET6 will be supported (hopefully) */
91 Scm_Error("unknown address type (%d)", he->h_addrtype);
92 }
93 entry->addresses = h;
94 return entry;
95 }
96
97 ScmObj Scm_GetHostByName(const char *name)
98 {
99 #if defined(GETHOSTBYNAME_R_NUMARGS)
100 ScmObj entry = SCM_FALSE;
101 {
102 int herr = 0, bufsiz = DATA_BUFSIZ;
103 struct hostent he;
104 char staticbuf[DATA_BUFSIZ], *buf = staticbuf;
105
106 for (;;) {
107 #if GETHOSTBYNAME_R_NUMARGS == 5
108 if (gethostbyname_r(name, &he, buf, bufsiz, &herr) != NULL) break;
109 if (herr != ERANGE) return SCM_FALSE;
110 bufsiz *= 2;
111 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
112 #elif GETHOSTBYNAME_R_NUMARGS == 6
113 struct hostent *rhe;
114 gethostbyname_r(name, &he, buf, bufsiz, &rhe, &herr);
115 if (rhe != 0) break;
116 else if (herr != ERANGE) return SCM_FALSE;
117 bufsiz *= 2;
118 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
119 #else
120 #error unsupported gethostbyname_r variant (configuration error?)
121 #endif
122 }
123 entry = SCM_OBJ(make_hostent(&he));
124 }
125 return entry;
126 #else /* !defined(GETHOSTBYNAME_R_NUMARGS) */
127 /* We don't have thread-safe call. Using global lock. */
128 volatile ScmObj entry = SCM_FALSE;
129 WITH_GLOBAL_LOCK(netdb_data.hostent_mutex,
130 do {
131 struct hostent *he;
132 he = gethostbyname(name);
133 if (he != NULL) entry = SCM_OBJ(make_hostent(he));
134 } while (0));
135 return entry;
136 #endif /* !defined(GETHOSTBYNAME_R_NUMARGS) */
137 }
138
139 ScmObj Scm_GetHostByAddr(const char *addr, int type)
140 {
141 struct in_addr iaddr;
142 if (type != AF_INET) {
143 Scm_Error("unsupported address type: %d", type);
144 }
145 if (!inet_aton(addr, &iaddr)) {
146 Scm_Error("bad inet address format: %s", addr);
147 }
148
149 #if defined(GETHOSTBYADDR_R_NUMARGS)
150 {
151 ScmObj entry = SCM_FALSE;
152 int herr = 0, bufsiz = DATA_BUFSIZ;
153 struct hostent he;
154 char staticbuf[DATA_BUFSIZ], *buf = staticbuf;
155 for (;;) {
156 #if GETHOSTBYADDR_R_NUMARGS == 7
157 if (gethostbyaddr_r((void *)&iaddr, sizeof(struct in_addr),
158 AF_INET, &he, buf, bufsiz, &herr) != NULL) {
159 break;
160 }
161 if (herr != ERANGE) return SCM_FALSE;
162 bufsiz *= 2;
163 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
164 #elif GETHOSTBYADDR_R_NUMARGS == 8
165 struct hostent *rhe;
166 gethostbyaddr_r((void *)&iaddr, sizeof(struct in_addr),
167 AF_INET, &he, buf, bufsiz, &rhe, &herr);
168 if (rhe != NULL) break;
169 if (herr != ERANGE) return SCM_FALSE;
170 bufsiz *= 2;
171 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
172 #else
173 #error unsupported gethostbyaddr_r variant (configuration error?)
174 #endif
175 }
176 entry = SCM_OBJ(make_hostent(&he));
177 return entry;
178 }
179 #else /* !defined(GETHOSTBYADDR_R_NUMARGS) */
180 {
181 volatile ScmObj entry = SCM_FALSE;
182 struct hostent *he;
183
184 WITH_GLOBAL_LOCK(netdb_data.hostent_mutex,
185 do {
186 he = gethostbyaddr((void*)&iaddr,
187 sizeof(struct in_addr),
188 AF_INET);
189 if (he != NULL) {
190 entry = SCM_OBJ(make_hostent(he));
191 }
192 } while (0));
193 return entry;
194 }
195 #endif /* !defined(GETHOSTBYADDR_R_NUMARGS) */
196 }
197
198 static ScmObj hostent_name(ScmSysHostent *entry)
199 {
200 return entry->name;
201 }
202
203 static ScmObj hostent_aliases(ScmSysHostent *entry)
204 {
205 return entry->aliases;
206 }
207
208 static ScmObj hostent_addresses(ScmSysHostent *entry)
209 {
210 return entry->addresses;
211 }
212
213 static ScmClassStaticSlotSpec hostent_slots[] = {
214 SCM_CLASS_SLOT_SPEC("name", hostent_name, NULL),
215 SCM_CLASS_SLOT_SPEC("aliases", hostent_aliases, NULL),
216 SCM_CLASS_SLOT_SPEC("addresses", hostent_addresses, NULL),
217 { NULL }
218 };
219
220 /*-------------------------------------------------------------
221 * Protoent
222 */
223
224 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_SysProtoentClass, NULL);
225
226 static ScmSysProtoent *make_protoent(struct protoent *pe)
227 {
228 ScmSysProtoent *entry = SCM_NEW(ScmSysProtoent);
229 ScmObj h = SCM_NIL, t = SCM_NIL;
230 char **p;
231
232 SCM_SET_CLASS(entry, SCM_CLASS_SYS_PROTOENT);
233 entry->name = SCM_MAKE_STR_COPYING(pe->p_name);
234 for (p = pe->p_aliases; *p; p++) {
235 SCM_APPEND1(h, t, SCM_MAKE_STR_COPYING(*p));
236 }
237 entry->aliases = h;
238 entry->proto = Scm_MakeInteger(pe->p_proto);
239 return entry;
240 }
241
242 ScmObj Scm_GetProtoByName(const char *name)
243 {
244 #if defined(GETPROTOBYNAME_R_NUMARGS)
245 ScmObj entry = SCM_FALSE;
246 {
247 int bufsiz = DATA_BUFSIZ;
248 struct protoent pe;
249 char staticbuf[DATA_BUFSIZ], *buf = staticbuf;
250
251 for (;;) {
252 #if GETPROTOBYNAME_R_NUMARGS == 4
253 if (getprotobyname_r(name, &pe, buf, bufsiz) != NULL) break;
254 if (errno != ERANGE) return SCM_FALSE;
255 bufsiz *= 2;
256 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
257 #elif GETPROTOBYNAME_R_NUMARGS == 5
258 struct protoent *rpe;
259 getprotobyname_r(name, &pe, buf, bufsiz, &rpe);
260 if (rpe != 0) break;
261 else if (errno != ERANGE) return SCM_FALSE;
262 bufsiz *= 2;
263 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
264 #else
265 #error unsupported getprotobyname_r variant (configuration error?)
266 #endif
267 }
268 entry = SCM_OBJ(make_protoent(&pe));
269 }
270 return entry;
271 #else /* !defined(GETPROTOBYNAME_R_NUMARGS) */
272 volatile ScmObj entry = SCM_FALSE;
273 WITH_GLOBAL_LOCK(netdb_data.protoent_mutex,
274 do {
275 struct protoent *pe = getprotobyname(name);
276 if (pe != NULL) entry = SCM_OBJ(make_protoent(pe));
277 } while (0));
278 return entry;
279 #endif /* !defined(GETPROTOBYNAME_R_NUMARGS) */
280 }
281
282 ScmObj Scm_GetProtoByNumber(int number)
283 {
284 #if defined(GETPROTOBYNUMBER_R_NUMARGS)
285 ScmObj entry = SCM_FALSE;
286 {
287 int bufsiz = DATA_BUFSIZ;
288 struct protoent pe;
289 char staticbuf[DATA_BUFSIZ], *buf = staticbuf;
290
291 for (;;) {
292 #if GETPROTOBYNUMBER_R_NUMARGS == 4
293 if (getprotobynumber_r(number, &pe, buf, bufsiz) != NULL) break;
294 if (errno != ERANGE) return SCM_FALSE;
295 bufsiz *= 2;
296 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
297 #elif GETPROTOBYNUMBER_R_NUMARGS == 5
298 struct protoent *rpe;
299 getprotobynumber_r(number, &pe, buf, bufsiz, &rpe);
300 if (rpe != 0) break;
301 else if (errno != ERANGE) return SCM_FALSE;
302 bufsiz *= 2;
303 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
304 #else
305 #error unsupported getprotobyname_r variant (configuration error?)
306 #endif
307 }
308 entry = SCM_OBJ(make_protoent(&pe));
309 }
310 return entry;
311 #else /* !defined(GETPROTOBYNUMBER_R_NUMARGS) */
312 volatile ScmObj entry = SCM_FALSE;
313 WITH_GLOBAL_LOCK(netdb_data.protoent_mutex,
314 do {
315 struct protoent *pe = getprotobynumber(number);
316 if (pe != NULL) entry = SCM_OBJ(make_protoent(pe));
317 } while (0));
318 return entry;
319 #endif /* !defined(GETPROTOBYNUMBER_R_NUMARGS) */
320 }
321
322 static ScmObj protoent_name(ScmSysProtoent *entry)
323 {
324 return entry->name;
325 }
326
327 static ScmObj protoent_aliases(ScmSysProtoent *entry)
328 {
329 return entry->aliases;
330 }
331
332 static ScmObj protoent_proto(ScmSysProtoent *entry)
333 {
334 return entry->proto;
335 }
336
337 static ScmClassStaticSlotSpec protoent_slots[] = {
338 SCM_CLASS_SLOT_SPEC("name", protoent_name, NULL),
339 SCM_CLASS_SLOT_SPEC("aliases", protoent_aliases, NULL),
340 SCM_CLASS_SLOT_SPEC("proto", protoent_proto, NULL),
341 { NULL }
342 };
343
344 /*-------------------------------------------------------------
345 * Servent
346 */
347
348 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_SysServentClass, NULL);
349
350 static ScmSysServent *make_servent(struct servent *se)
351 {
352 ScmSysServent *entry = SCM_NEW(ScmSysServent);
353 ScmObj h = SCM_NIL, t = SCM_NIL;
354 char **p;
355
356 SCM_SET_CLASS(entry, SCM_CLASS_SYS_SERVENT);
357 entry->name = SCM_MAKE_STR_COPYING(se->s_name);
358 for (p = se->s_aliases; *p; p++) {
359 SCM_APPEND1(h, t, SCM_MAKE_STR_COPYING(*p));
360 }
361 entry->aliases = h;
362 entry->port = Scm_MakeInteger(ntohs(se->s_port));
363 entry->proto = SCM_MAKE_STR_COPYING(se->s_proto);
364 return entry;
365 }
366
367 ScmObj Scm_GetServByName(const char *name, const char *proto)
368 {
369 #if defined(GETSERVBYNAME_R_NUMARGS)
370 ScmObj entry = SCM_FALSE;
371 {
372 int bufsiz = DATA_BUFSIZ;
373 struct servent se;
374 char staticbuf[DATA_BUFSIZ], *buf = staticbuf;
375
376 for (;;) {
377 #if GETSERVBYNAME_R_NUMARGS == 5
378 if (getservbyname_r(name, proto, &se, buf, bufsiz) != NULL) break;
379 if (errno != ERANGE) return SCM_FALSE;
380 bufsiz *= 2;
381 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
382 #elif GETSERVBYNAME_R_NUMARGS == 6
383 struct servent *rse;
384 getservbyname_r(name, proto, &se, buf, bufsiz, &rse);
385 if (rse != 0) break;
386 else if (errno != ERANGE) return SCM_FALSE;
387 bufsiz *= 2;
388 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
389 #else
390 #error unsupported getservbyname_r variant (configuration error?)
391 #endif
392 }
393 entry = SCM_OBJ(make_servent(&se));
394 }
395 return entry;
396 #else /* !defined(GETSERVBYNAME_R_NUMARGS) */
397 volatile ScmObj entry = SCM_FALSE;
398 WITH_GLOBAL_LOCK(netdb_data.servent_mutex,
399 do {
400 struct servent *se = getservbyname(name, proto);
401 if (se != NULL) entry = SCM_OBJ(make_servent(se));
402 } while (0));
403 return entry;
404 #endif /* !defined(GETSERVBYNAME_R_NUMARGS) */
405 }
406
407 ScmObj Scm_GetServByPort(int port, const char *proto)
408 {
409 #if defined(GETSERVBYPORT_R_NUMARGS)
410 ScmObj entry = SCM_FALSE;
411 {
412 int bufsiz = DATA_BUFSIZ;
413 struct servent se;
414 char staticbuf[DATA_BUFSIZ], *buf = staticbuf;
415
416 for (;;) {
417 #if GETSERVBYPORT_R_NUMARGS == 5
418 if (getservbyport_r(htons(port), proto, &se, buf, bufsiz) != NULL)
419 break;
420 if (errno != ERANGE) return SCM_FALSE;
421 bufsiz *= 2;
422 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
423 #elif GETSERVBYPORT_R_NUMARGS == 6
424 struct servent *rse;
425 getservbyport_r(htons(port), proto, &se, buf, bufsiz, &rse);
426 if (rse != 0) break;
427 else if (errno != ERANGE) return SCM_FALSE;
428 bufsiz *= 2;
429 buf = SCM_NEW_ATOMIC2(char*, bufsiz);
430 #else
431 #error unsupported getservbyport_r variant (configuration error?)
432 #endif
433 }
434 entry = SCM_OBJ(make_servent(&se));
435 }
436 return entry;
437 #else /* !defined(GETSERVBYPORT_R_NUMARGS) */
438 volatile ScmObj entry = SCM_FALSE;
439 WITH_GLOBAL_LOCK(netdb_data.servent_mutex,
440 do {
441 struct servent *se =
442 getservbyport(htons(port), proto);
443 if (se != NULL) entry = SCM_OBJ(make_servent(se));
444 } while (0));
445 return entry;
446 #endif /* !defined(GETSERVBYPORT_R_NUMARGS) */
447 }
448
449 static ScmObj servent_name(ScmSysServent *entry)
450 {
451 return entry->name;
452 }
453
454 static ScmObj servent_aliases(ScmSysServent *entry)
455 {
456 return entry->aliases;
457 }
458
459 static ScmObj servent_port(ScmSysServent *entry)
460 {
461 return entry->port;
462 }
463
464 static ScmObj servent_proto(ScmSysServent *entry)
465 {
466 return entry->proto;
467 }
468
469 static ScmClassStaticSlotSpec servent_slots[] = {
470 SCM_CLASS_SLOT_SPEC("name", servent_name, NULL),
471 SCM_CLASS_SLOT_SPEC("aliases", servent_aliases, NULL),
472 SCM_CLASS_SLOT_SPEC("port", servent_port, NULL),
473 SCM_CLASS_SLOT_SPEC("proto", servent_proto, NULL),
474 { NULL }
475 };
476
477 /*-------------------------------------------------------------
478 * Addrinfo
479 */
480
481 #ifdef HAVE_IPV6
482
483 ScmObj addrinfo_allocate(ScmClass *klass, ScmObj initargs)
484 {
485 ScmSysAddrinfo *info = SCM_ALLOCATE(ScmSysAddrinfo, klass);
486 SCM_SET_CLASS(info, klass);
487 info->canonname = NULL;
488 info->addr = NULL;
489 return SCM_OBJ(info);
490 }
491
492 static ScmSysAddrinfo *make_addrinfo(struct addrinfo *ai)
493 {
494 ScmSysAddrinfo *info = SCM_NEW(ScmSysAddrinfo);
495
496 SCM_SET_CLASS(info, SCM_CLASS_SYS_ADDRINFO);
497 info->flags = ai->ai_flags;
498 info->family = ai->ai_family;
499 info->socktype = ai->ai_socktype;
500 info->protocol = ai->ai_protocol;
501 info->addrlen = ai->ai_addrlen;
502 if (ai->ai_canonname != NULL)
503 info->canonname = SCM_STRING(SCM_MAKE_STR_COPYING(ai->ai_canonname));
504 if (ai->ai_addr != NULL)
505 info->addr = SCM_SOCKADDR(Scm_MakeSockAddr(NULL,
506 ai->ai_addr,
507 ai->ai_addrlen));
508 return info;
509 }
510
511 ScmObj Scm_GetAddrinfo(const char *nodename,
512 const char *servname,
513 struct addrinfo *hints)
514 {
515 ScmObj h = SCM_NIL, t = SCM_NIL;
516 struct addrinfo *res, *res0;
517 int r;
518
519 r = getaddrinfo(nodename, servname, hints, &res0);
520 if (r) Scm_Error("getaddrinfo failed: %s", gai_strerror(r));
521
522 for (res = res0; res != NULL; res = res->ai_next) {
523 SCM_APPEND1(h, t, SCM_OBJ(make_addrinfo(res)));
524 }
525 freeaddrinfo(res0);
526 return h;
527 }
528
529 ScmObj Scm_GetNameinfo(ScmSockAddr *addr, int flags)
530 {
531 char host[NI_MAXHOST], serv[NI_MAXSERV];
532 int r;
533
534 r = getnameinfo(&addr->addr, addr->addrlen,
535 host, sizeof(host), serv, sizeof(serv), flags);
536 if (r) Scm_Error("getnameinfo failed: %s", gai_strerror(r));
537 return Scm_Values2(SCM_MAKE_STR_COPYING(host), SCM_MAKE_STR_COPYING(serv));
538 }
539
540 #endif /* HAVE_IPV6 */
541
542 /*-------------------------------------------------------------
543 * Initialize
544 */
545
546 void Scm_Init_NetDB(ScmModule *mod)
547 {
548 Scm_InitStaticClass(&Scm_SysHostentClass, "<sys-hostent>", mod,
549 hostent_slots, 0);
550 Scm_InitStaticClass(&Scm_SysProtoentClass, "<sys-protoent>", mod,
551 protoent_slots, 0);
552 Scm_InitStaticClass(&Scm_SysServentClass, "<sys-servent>", mod,
553 servent_slots, 0);
554 SCM_INTERNAL_MUTEX_INIT(netdb_data.hostent_mutex);
555 SCM_INTERNAL_MUTEX_INIT(netdb_data.protoent_mutex);
556 SCM_INTERNAL_MUTEX_INIT(netdb_data.servent_mutex);
557 }