* _scm.h: Removed #include <errno.h>.
[bpt/guile.git] / libguile / net_db.c
1 /* "net_db.c" network database support
2 * Copyright (C) 1995, 96, 97, 98, 99, 2000, 2001 Free Software Foundation, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307 USA
18 *
19 * As a special exception, the Free Software Foundation gives permission
20 * for additional uses of the text contained in its release of GUILE.
21 *
22 * The exception is that, if you link the GUILE library with other files
23 * to produce an executable, this does not by itself cause the
24 * resulting executable to be covered by the GNU General Public License.
25 * Your use of that executable is in no way restricted on account of
26 * linking the GUILE library code into it.
27 *
28 * This exception does not however invalidate any other reasons why
29 * the executable file might be covered by the GNU General Public License.
30 *
31 * This exception applies only to the code released by the
32 * Free Software Foundation under the name GUILE. If you copy
33 * code from other Free Software Foundation releases into a copy of
34 * GUILE, as the General Public License permits, the exception does
35 * not apply to the code that you add in this way. To avoid misleading
36 * anyone as to the status of such modified files, you must delete
37 * this exception notice from them.
38 *
39 * If you write modifications of your own for GUILE, it is your choice
40 * whether to permit this exception to apply to your modifications.
41 * If you do not wish that, delete this exception notice. */
42
43 /* Software engineering face-lift by Greg J. Badros, 11-Dec-1999,
44 gjb@cs.washington.edu, http://www.cs.washington.edu/homes/gjb */
45
46
47 /* Written in 1994 by Aubrey Jaffer.
48 * Thanks to Hallvard.Tretteberg@si.sintef.no for inspiration and discussion.
49 * Rewritten by Gary Houston to be a closer interface to the C socket library.
50 * Split into net_db.c and socket.c.
51 */
52 \f
53
54 #include <errno.h>
55
56 #include "libguile/_scm.h"
57 #include "libguile/feature.h"
58 #include "libguile/strings.h"
59 #include "libguile/vectors.h"
60
61 #include "libguile/validate.h"
62 #include "libguile/net_db.h"
63
64 #ifdef HAVE_STRING_H
65 #include <string.h>
66 #endif
67
68 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <netdb.h>
71 #include <netinet/in.h>
72 #include <arpa/inet.h>
73
74 \f
75
76 #ifndef STDC_HEADERS
77 int close ();
78 #endif /* STDC_HEADERS */
79
80 #ifndef HAVE_INET_ATON
81 extern int inet_aton ();
82 #endif
83
84 SCM_DEFINE (scm_inet_aton, "inet-aton", 1, 0, 0,
85 (SCM address),
86 "Converts a string containing an Internet host address in the traditional\n"
87 "dotted decimal notation into an integer.\n\n"
88 "@smalllisp\n"
89 "(inet-aton \"127.0.0.1\") @result{} 2130706433\n\n"
90 "@end smalllisp")
91 #define FUNC_NAME s_scm_inet_aton
92 {
93 struct in_addr soka;
94
95 SCM_VALIDATE_STRING (1, address);
96 SCM_STRING_COERCE_0TERMINATION_X (address);
97 if (inet_aton (SCM_STRING_CHARS (address), &soka) == 0)
98 SCM_MISC_ERROR ("bad address", SCM_EOL);
99 return scm_ulong2num (ntohl (soka.s_addr));
100 }
101 #undef FUNC_NAME
102
103
104 SCM_DEFINE (scm_inet_ntoa, "inet-ntoa", 1, 0, 0,
105 (SCM inetid),
106 "Converts an integer Internet host address into a string with the\n"
107 "traditional dotted decimal representation.\n\n"
108 "@smalllisp\n"
109 "(inet-ntoa 2130706433) @result{} \"127.0.0.1\"\n"
110 "@end smalllisp")
111 #define FUNC_NAME s_scm_inet_ntoa
112 {
113 struct in_addr addr;
114 char *s;
115 SCM answer;
116 addr.s_addr = htonl (SCM_NUM2ULONG (1,inetid));
117 s = inet_ntoa (addr);
118 answer = scm_makfromstr (s, strlen (s), 0);
119 return answer;
120 }
121 #undef FUNC_NAME
122
123 #ifdef HAVE_INET_NETOF
124 SCM_DEFINE (scm_inet_netof, "inet-netof", 1, 0, 0,
125 (SCM address),
126 "Returns the network number part of the given integer Internet address.\n\n"
127 "@smalllisp\n"
128 "(inet-netof 2130706433) @result{} 127\n"
129 "@end smalllisp")
130 #define FUNC_NAME s_scm_inet_netof
131 {
132 struct in_addr addr;
133 addr.s_addr = htonl (SCM_NUM2ULONG (1,address));
134 return scm_ulong2num ((unsigned long) inet_netof (addr));
135 }
136 #undef FUNC_NAME
137 #endif
138
139 #ifdef HAVE_INET_LNAOF
140 SCM_DEFINE (scm_lnaof, "inet-lnaof", 1, 0, 0,
141 (SCM address),
142 "Returns the local-address-with-network part of the given Internet\n"
143 "address.\n\n"
144 "@smalllisp\n"
145 "(inet-lnaof 2130706433) @result{} 1\n"
146 "@end smalllisp")
147 #define FUNC_NAME s_scm_lnaof
148 {
149 struct in_addr addr;
150 addr.s_addr = htonl (SCM_NUM2ULONG (1,address));
151 return scm_ulong2num ((unsigned long) inet_lnaof (addr));
152 }
153 #undef FUNC_NAME
154 #endif
155
156 #ifdef HAVE_INET_MAKEADDR
157 SCM_DEFINE (scm_inet_makeaddr, "inet-makeaddr", 2, 0, 0,
158 (SCM net, SCM lna),
159 "Makes an Internet host address by combining the network number @var{net}\n"
160 "with the local-address-within-network number @var{lna}.\n\n"
161 "@smalllisp\n"
162 "(inet-makeaddr 127 1) @result{} 2130706433\n"
163 "@end smalllisp")
164 #define FUNC_NAME s_scm_inet_makeaddr
165 {
166 struct in_addr addr;
167 unsigned long netnum;
168 unsigned long lnanum;
169
170 #if 0 /* GJB:FIXME:: */
171 SCM_VALIDATE_INUM_COPY (1,net,netnum);
172 SCM_VALIDATE_INUM_COPY (2,lna,lnanum);
173 #else
174 netnum = SCM_NUM2ULONG (1, net);
175 lnanum = SCM_NUM2ULONG (2, lna);
176 #endif
177 addr = inet_makeaddr (netnum, lnanum);
178 return scm_ulong2num (ntohl (addr.s_addr));
179 }
180 #undef FUNC_NAME
181 #endif
182
183 SCM_SYMBOL (scm_host_not_found_key, "host-not-found");
184 SCM_SYMBOL (scm_try_again_key, "try-again");
185 SCM_SYMBOL (scm_no_recovery_key, "no-recovery");
186 SCM_SYMBOL (scm_no_data_key, "no-data");
187
188 static void scm_resolv_error (const char *subr, SCM bad_value)
189 {
190 #ifdef NETDB_INTERNAL
191 if (h_errno == NETDB_INTERNAL)
192 {
193 /* errno supposedly contains a useful value. */
194 scm_syserror (subr);
195 }
196 else
197 #endif
198 {
199 SCM key;
200 const char *errmsg;
201
202 switch (h_errno)
203 {
204 case HOST_NOT_FOUND:
205 key = scm_host_not_found_key;
206 errmsg = "Unknown host";
207 break;
208 case TRY_AGAIN:
209 key = scm_try_again_key;
210 errmsg = "Host name lookup failure";
211 break;
212 case NO_RECOVERY:
213 key = scm_no_recovery_key;
214 errmsg = "Unknown server error";
215 break;
216 case NO_DATA:
217 key = scm_no_data_key;
218 errmsg = "No address associated with name";
219 break;
220 default:
221 scm_misc_error (subr, "Unknown resolver error", SCM_EOL);
222 errmsg = NULL;
223 }
224
225 #ifdef HAVE_HSTRERROR
226 errmsg = (const char *) hstrerror (h_errno);
227 #endif
228 scm_error (key, subr, errmsg, scm_cons (bad_value, SCM_EOL), SCM_EOL);
229 }
230 }
231
232 /* Should take an extra arg for address format (will be needed for IPv6).
233 Should use reentrant facilities if available.
234 */
235
236 SCM_DEFINE (scm_gethost, "gethost", 0, 1, 0,
237 (SCM host),
238 "@deffnx procedure gethostbyname hostname\n"
239 "@deffnx procedure gethostbyaddr address\n"
240 "Look up a host by name or address, returning a host object. The\n"
241 "@code{gethost} procedure will accept either a string name or an integer\n"
242 "address; if given no arguments, it behaves like @code{gethostent} (see\n"
243 "below). If a name or address is supplied but the address can not be\n"
244 "found, an error will be thrown to one of the keys:\n"
245 "@code{host-not-found}, @code{try-again}, @code{no-recovery} or\n"
246 "@code{no-data}, corresponding to the equivalent @code{h_error} values.\n"
247 "Unusual conditions may result in errors thrown to the\n"
248 "@code{system-error} or @code{misc_error} keys.")
249 #define FUNC_NAME s_scm_gethost
250 {
251 SCM ans = scm_c_make_vector (5, SCM_UNSPECIFIED);
252 SCM *ve = SCM_VELTS (ans);
253 SCM lst = SCM_EOL;
254 struct hostent *entry;
255 struct in_addr inad;
256 char **argv;
257 int i = 0;
258 if (SCM_UNBNDP (host))
259 {
260 #ifdef HAVE_GETHOSTENT
261 entry = gethostent ();
262 #else
263 entry = NULL;
264 #endif
265 if (! entry)
266 {
267 /* As far as I can tell, there's no good way to tell whether
268 zero means an error or end-of-file. The trick of
269 clearing errno before calling gethostent and checking it
270 afterwards doesn't cut it, because, on Linux, it seems to
271 try to contact some other server (YP?) and fails, which
272 is a benign failure. */
273 return SCM_BOOL_F;
274 }
275 }
276 else if (SCM_STRINGP (host))
277 {
278 SCM_STRING_COERCE_0TERMINATION_X (host);
279 entry = gethostbyname (SCM_STRING_CHARS (host));
280 }
281 else
282 {
283 inad.s_addr = htonl (SCM_NUM2ULONG (1,host));
284 entry = gethostbyaddr ((char *) &inad, sizeof (inad), AF_INET);
285 }
286 if (!entry)
287 scm_resolv_error (FUNC_NAME, host);
288
289 ve[0] = scm_makfromstr (entry->h_name,
290 (scm_sizet) strlen (entry->h_name), 0);
291 ve[1] = scm_makfromstrs (-1, entry->h_aliases);
292 ve[2] = SCM_MAKINUM (entry->h_addrtype + 0L);
293 ve[3] = SCM_MAKINUM (entry->h_length + 0L);
294 if (sizeof (struct in_addr) != entry->h_length)
295 {
296 ve[4] = SCM_BOOL_F;
297 return ans;
298 }
299 for (argv = entry->h_addr_list; argv[i]; i++);
300 while (i--)
301 {
302 inad = *(struct in_addr *) argv[i];
303 lst = scm_cons (scm_ulong2num (ntohl (inad.s_addr)), lst);
304 }
305 ve[4] = lst;
306 return ans;
307 }
308 #undef FUNC_NAME
309
310
311 /* In all subsequent getMUMBLE functions, when we're called with no
312 arguments, we're supposed to traverse the tables entry by entry.
313 However, there doesn't seem to be any documented way to distinguish
314 between end-of-table and an error; in both cases the functions
315 return zero. Gotta love Unix. For the time being, we clear errno,
316 and if we get a zero and errno is set, we signal an error. This
317 doesn't seem quite right (what if errno gets set as part of healthy
318 operation?), but it seems to work okay. We'll see. */
319
320 #if defined(HAVE_GETNETENT) && defined(HAVE_GETNETBYNAME) && defined(HAVE_GETNETBYADDR)
321 SCM_DEFINE (scm_getnet, "getnet", 0, 1, 0,
322 (SCM net),
323 "@deffnx procedure getnetbyname net-name\n"
324 "@deffnx procedure getnetbyaddr net-number\n"
325 "Look up a network by name or net number in the network database. The\n"
326 "@var{net-name} argument must be a string, and the @var{net-number}\n"
327 "argument must be an integer. @code{getnet} will accept either type of\n"
328 "argument, behaving like @code{getnetent} (see below) if no arguments are\n"
329 "given.")
330 #define FUNC_NAME s_scm_getnet
331 {
332 SCM ans;
333 SCM *ve;
334 struct netent *entry;
335
336 ans = scm_c_make_vector (4, SCM_UNSPECIFIED);
337 ve = SCM_VELTS (ans);
338 if (SCM_UNBNDP (net))
339 {
340 entry = getnetent ();
341 if (! entry)
342 {
343 /* There's no good way to tell whether zero means an error
344 or end-of-file, so we always return #f. See `gethost'
345 for details. */
346 return SCM_BOOL_F;
347 }
348 }
349 else if (SCM_STRINGP (net))
350 {
351 SCM_STRING_COERCE_0TERMINATION_X (net);
352 entry = getnetbyname (SCM_STRING_CHARS (net));
353 }
354 else
355 {
356 unsigned long netnum;
357 netnum = SCM_NUM2ULONG (1, net);
358 entry = getnetbyaddr (netnum, AF_INET);
359 }
360 if (!entry)
361 SCM_SYSERROR_MSG ("no such network ~A",
362 scm_listify (net, SCM_UNDEFINED), errno);
363 ve[0] = scm_makfromstr (entry->n_name, (scm_sizet) strlen (entry->n_name), 0);
364 ve[1] = scm_makfromstrs (-1, entry->n_aliases);
365 ve[2] = SCM_MAKINUM (entry->n_addrtype + 0L);
366 ve[3] = scm_ulong2num (entry->n_net + 0L);
367 return ans;
368 }
369 #undef FUNC_NAME
370 #endif
371
372 #ifdef HAVE_GETPROTOENT
373 SCM_DEFINE (scm_getproto, "getproto", 0, 1, 0,
374 (SCM protocol),
375 "@deffnx procedure getprotobyname name\n"
376 "@deffnx procedure getprotobynumber number\n"
377 "Look up a network protocol by name or by number. @code{getprotobyname}\n"
378 "takes a string argument, and @code{getprotobynumber} takes an integer\n"
379 "argument. @code{getproto} will accept either type, behaving like\n"
380 "@code{getprotoent} (see below) if no arguments are supplied.")
381 #define FUNC_NAME s_scm_getproto
382 {
383 SCM ans;
384 SCM *ve;
385 struct protoent *entry;
386
387 ans = scm_c_make_vector (3, SCM_UNSPECIFIED);
388 ve = SCM_VELTS (ans);
389 if (SCM_UNBNDP (protocol))
390 {
391 entry = getprotoent ();
392 if (! entry)
393 {
394 /* There's no good way to tell whether zero means an error
395 or end-of-file, so we always return #f. See `gethost'
396 for details. */
397 return SCM_BOOL_F;
398 }
399 }
400 else if (SCM_STRINGP (protocol))
401 {
402 SCM_STRING_COERCE_0TERMINATION_X (protocol);
403 entry = getprotobyname (SCM_STRING_CHARS (protocol));
404 }
405 else
406 {
407 unsigned long protonum;
408 protonum = SCM_NUM2ULONG (1,protocol);
409 entry = getprotobynumber (protonum);
410 }
411 if (!entry)
412 SCM_SYSERROR_MSG ("no such protocol ~A",
413 scm_listify (protocol, SCM_UNDEFINED), errno);
414 ve[0] = scm_makfromstr (entry->p_name, (scm_sizet) strlen (entry->p_name), 0);
415 ve[1] = scm_makfromstrs (-1, entry->p_aliases);
416 ve[2] = SCM_MAKINUM (entry->p_proto + 0L);
417 return ans;
418 }
419 #undef FUNC_NAME
420 #endif
421
422 static SCM
423 scm_return_entry (struct servent *entry)
424 {
425 SCM ans;
426 SCM *ve;
427
428 ans = scm_c_make_vector (4, SCM_UNSPECIFIED);
429 ve = SCM_VELTS (ans);
430 ve[0] = scm_makfromstr (entry->s_name, (scm_sizet) strlen (entry->s_name), 0);
431 ve[1] = scm_makfromstrs (-1, entry->s_aliases);
432 ve[2] = SCM_MAKINUM (ntohs (entry->s_port) + 0L);
433 ve[3] = scm_makfromstr (entry->s_proto, (scm_sizet) strlen (entry->s_proto), 0);
434 return ans;
435 }
436
437 #ifdef HAVE_GETSERVENT
438 SCM_DEFINE (scm_getserv, "getserv", 0, 2, 0,
439 (SCM name, SCM protocol),
440 "@deffnx procedure getservbyname name protocol\n"
441 "@deffnx procedure getservbyport port protocol\n"
442 "Look up a network service by name or by service number, and return a\n"
443 "network service object. The @var{protocol} argument specifies the name\n"
444 "of the desired protocol; if the protocol found in the network service\n"
445 "database does not match this name, a system error is signalled.\n\n"
446 "The @code{getserv} procedure will take either a service name or number\n"
447 "as its first argument; if given no arguments, it behaves like\n"
448 "@code{getservent} (see below).")
449 #define FUNC_NAME s_scm_getserv
450 {
451 struct servent *entry;
452 if (SCM_UNBNDP (name))
453 {
454 entry = getservent ();
455 if (!entry)
456 {
457 /* There's no good way to tell whether zero means an error
458 or end-of-file, so we always return #f. See `gethost'
459 for details. */
460 return SCM_BOOL_F;
461 }
462 return scm_return_entry (entry);
463 }
464 SCM_VALIDATE_STRING (2, protocol);
465 SCM_STRING_COERCE_0TERMINATION_X (protocol);
466 if (SCM_STRINGP (name))
467 {
468 SCM_STRING_COERCE_0TERMINATION_X (name);
469 entry = getservbyname (SCM_STRING_CHARS (name), SCM_STRING_CHARS (protocol));
470 }
471 else
472 {
473 SCM_VALIDATE_INUM (1,name);
474 entry = getservbyport (htons (SCM_INUM (name)), SCM_STRING_CHARS (protocol));
475 }
476 if (!entry)
477 SCM_SYSERROR_MSG("no such service ~A",
478 scm_listify (name, SCM_UNDEFINED), errno);
479 return scm_return_entry (entry);
480 }
481 #undef FUNC_NAME
482 #endif
483
484 #if defined(HAVE_SETHOSTENT) && defined(HAVE_ENDHOSTENT)
485 SCM_DEFINE (scm_sethost, "sethost", 0, 1, 0,
486 (SCM stayopen),
487 "If @var{stayopen} is omitted, this is equivalent to @code{endhostent}.\n"
488 "Otherwise it is equivalent to @code{sethostent stayopen}.")
489 #define FUNC_NAME s_scm_sethost
490 {
491 if (SCM_UNBNDP (stayopen))
492 endhostent ();
493 else
494 sethostent (SCM_NFALSEP (stayopen));
495 return SCM_UNSPECIFIED;
496 }
497 #undef FUNC_NAME
498 #endif
499
500 #if defined(HAVE_SETNETENT) && defined(HAVE_ENDNETENT)
501 SCM_DEFINE (scm_setnet, "setnet", 0, 1, 0,
502 (SCM stayopen),
503 "If @var{stayopen} is omitted, this is equivalent to @code{endnetent}.\n"
504 "Otherwise it is equivalent to @code{setnetent stayopen}.")
505 #define FUNC_NAME s_scm_setnet
506 {
507 if (SCM_UNBNDP (stayopen))
508 endnetent ();
509 else
510 setnetent (SCM_NFALSEP (stayopen));
511 return SCM_UNSPECIFIED;
512 }
513 #undef FUNC_NAME
514 #endif
515
516 #if defined(HAVE_SETPROTOENT) && defined(HAVE_ENDPROTOENT)
517 SCM_DEFINE (scm_setproto, "setproto", 0, 1, 0,
518 (SCM stayopen),
519 "If @var{stayopen} is omitted, this is equivalent to @code{endprotoent}.\n"
520 "Otherwise it is equivalent to @code{setprotoent stayopen}.")
521 #define FUNC_NAME s_scm_setproto
522 {
523 if (SCM_UNBNDP (stayopen))
524 endprotoent ();
525 else
526 setprotoent (SCM_NFALSEP (stayopen));
527 return SCM_UNSPECIFIED;
528 }
529 #undef FUNC_NAME
530 #endif
531
532 #if defined(HAVE_SETSERVENT) && defined(HAVE_ENDSERVENT)
533 SCM_DEFINE (scm_setserv, "setserv", 0, 1, 0,
534 (SCM stayopen),
535 "If @var{stayopen} is omitted, this is equivalent to @code{endservent}.\n"
536 "Otherwise it is equivalent to @code{setservent stayopen}.")
537 #define FUNC_NAME s_scm_setserv
538 {
539 if (SCM_UNBNDP (stayopen))
540 endservent ();
541 else
542 setservent (SCM_NFALSEP (stayopen));
543 return SCM_UNSPECIFIED;
544 }
545 #undef FUNC_NAME
546 #endif
547
548
549 void
550 scm_init_net_db ()
551 {
552 #ifdef INADDR_ANY
553 scm_sysintern ("INADDR_ANY", scm_ulong2num (INADDR_ANY));
554 #endif
555 #ifdef INADDR_BROADCAST
556 scm_sysintern ("INADDR_BROADCAST", scm_ulong2num (INADDR_BROADCAST));
557 #endif
558 #ifdef INADDR_NONE
559 scm_sysintern ("INADDR_NONE", scm_ulong2num (INADDR_NONE));
560 #endif
561 #ifdef INADDR_LOOPBACK
562 scm_sysintern ("INADDR_LOOPBACK", scm_ulong2num (INADDR_LOOPBACK));
563 #endif
564
565 scm_add_feature ("net-db");
566 #ifndef SCM_MAGIC_SNARFER
567 #include "libguile/net_db.x"
568 #endif
569 }
570
571 /*
572 Local Variables:
573 c-file-style: "gnu"
574 End:
575 */