summaryrefslogtreecommitdiff
path: root/libguile/net_db.c
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2010-02-14 20:56:31 +0100
committerLudovic Courtès <ludo@gnu.org>2010-02-14 20:56:47 +0100
commit55ae00ea73e5698d64c31118717c15e6dd9cafe7 (patch)
tree514b45f0da93acaeb3c66b5e75328c479fb6bbc6 /libguile/net_db.c
parent61cd9dc907b8a09990b14e9aeac7e20fe77cecc6 (diff)
downloadguile-55ae00ea73e5698d64c31118717c15e6dd9cafe7.tar.gz
Add `getaddrinfo' and related procedures.
* libguile/net_db.c (sym_getaddrinfo_error, sym_ai_passive, sym_ai_canonname, sym_ai_numerichost, sym_ai_numericserv, sym_ai_v4mapped, sym_ai_all, sym_ai_addrconfig, sym_eai_badflags, sym_eai_noname, sym_eai_again, sym_eai_fail, sym_eai_family, sym_eai_socktype, sym_eai_service, sym_eai_memory, sym_eai_system, sym_eai_overflow, sym_eai_nodata, sym_eai_addrfamily, sym_eai_inprogress, sym_eai_canceled, sym_eai_notcanceled, sym_eai_alldone, sym_eai_intr, sym_eai_idn_encode): New variables. (scm_from_addrinfo, scm_getaddrinfo, scm_gai_strerror): New functions. * libguile/net_db.h (scm_getaddrinfo, scm_gai_strerror): New functions. * module/ice-9/networking.scm (addrinfo:flags, addrinfo:fam, addrinfo:socktype, addrinfo:protocol, addrinfo:addr, addrinfo:canonname): New procedures. * test-suite/tests/net-db.test: New file. * test-suite/Makefile.am (SCM_TESTS): Add `tests/net-db.test'. * doc/ref/posix.texi (Network Databases)[getaddrinfo]: New subsection. [The Host Database]: Suggest using `getaddrinfo'.
Diffstat (limited to 'libguile/net_db.c')
-rw-r--r--libguile/net_db.c292
1 files changed, 291 insertions, 1 deletions
diff --git a/libguile/net_db.c b/libguile/net_db.c
index 4307091f7..ca6dafb28 100644
--- a/libguile/net_db.c
+++ b/libguile/net_db.c
@@ -30,6 +30,7 @@
# include <config.h>
#endif
+#include <verify.h>
#include <errno.h>
#include "libguile/_scm.h"
@@ -40,6 +41,7 @@
#include "libguile/validate.h"
#include "libguile/net_db.h"
+#include "libguile/socket.h"
#ifdef HAVE_STRING_H
#include <string.h>
@@ -449,8 +451,296 @@ SCM_DEFINE (scm_setserv, "setserv", 0, 1, 0,
#undef FUNC_NAME
#endif
+
+/* Protocol-independent name resolution with getaddrinfo(3) & co. */
+
+SCM_SYMBOL (sym_getaddrinfo_error, "getaddrinfo-error");
+
+/* Make sure the `AI_*' flags can be stored as INUMs. */
+verify (SCM_I_INUM (SCM_I_MAKINUM (AI_ALL)) == AI_ALL);
+
+/* Valid values for the `ai_flags' to `struct addrinfo'. */
+SCM_VARIABLE_INIT (sym_ai_passive, "AI_PASSIVE",
+ SCM_I_MAKINUM (AI_PASSIVE));
+SCM_VARIABLE_INIT (sym_ai_canonname, "AI_CANONNAME",
+ SCM_I_MAKINUM (AI_CANONNAME));
+SCM_VARIABLE_INIT (sym_ai_numerichost, "AI_NUMERICHOST",
+ SCM_I_MAKINUM (AI_NUMERICHOST));
+SCM_VARIABLE_INIT (sym_ai_numericserv, "AI_NUMERICSERV",
+ SCM_I_MAKINUM (AI_NUMERICSERV));
+SCM_VARIABLE_INIT (sym_ai_v4mapped, "AI_V4MAPPED",
+ SCM_I_MAKINUM (AI_V4MAPPED));
+SCM_VARIABLE_INIT (sym_ai_all, "AI_ALL",
+ SCM_I_MAKINUM (AI_ALL));
+SCM_VARIABLE_INIT (sym_ai_addrconfig, "AI_ADDRCONFIG",
+ SCM_I_MAKINUM (AI_ADDRCONFIG));
+
+/* Return a Scheme vector whose elements correspond to the fields of C_AI,
+ ignoring the `ai_next' field. This function is not exported because the
+ definition of `struct addrinfo' is provided by Gnulib. */
+static SCM
+scm_from_addrinfo (const struct addrinfo *c_ai)
+{
+ SCM ai;
+
+ /* Note: The indices here must be kept synchronized with those used by the
+ `addrinfo:' procedures in `networking.scm'. */
+
+ ai = scm_c_make_vector (6, SCM_UNDEFINED);
+ SCM_SIMPLE_VECTOR_SET (ai, 0, scm_from_int (c_ai->ai_flags));
+ SCM_SIMPLE_VECTOR_SET (ai, 1, scm_from_int (c_ai->ai_family));
+ SCM_SIMPLE_VECTOR_SET (ai, 2, scm_from_int (c_ai->ai_socktype));
+ SCM_SIMPLE_VECTOR_SET (ai, 3, scm_from_int (c_ai->ai_protocol));
+ SCM_SIMPLE_VECTOR_SET (ai, 4,
+ scm_from_sockaddr (c_ai->ai_addr, c_ai->ai_addrlen));
+ SCM_SIMPLE_VECTOR_SET (ai, 5, scm_from_locale_string (c_ai->ai_canonname));
+
+ return ai;
+}
+
+SCM_DEFINE (scm_getaddrinfo, "getaddrinfo", 1, 5, 0,
+ (SCM name, SCM service, SCM hint_flags, SCM hint_family,
+ SCM hint_socktype, SCM hint_protocol),
+ "Return a list of @code{addrinfo} structures containing "
+ "a socket address and associated information for host @var{name} "
+ "and/or @var{service} to be used in creating a socket with "
+ "which to address the specified service.\n\n"
+ "@example\n"
+ "(let* ((ai (car (getaddrinfo \"www.gnu.org\" \"http\")))\n"
+ " (s (socket (addrinfo:fam ai) (addrinfo:socktype ai)\n"
+ " (addrinfo:protocol ai))))\n"
+ " (connect s (addrinfo:addr ai))\n"
+ " s)\n"
+ "@end example\n\n"
+ "When @var{service} is omitted or is @code{#f}, return "
+ "network-level addresses for @var{name}. When @var{name} "
+ "is @code{#f} @var{service} must be provided and service "
+ "locations local to the caller are returned.\n"
+ "\n"
+ "Additional hints can be provided. When specified, "
+ "@var{hint_flags} should be a bitwise-or of zero or more "
+ "constants among the following:\n\n"
+ "@table @code\n"
+ "@item AI_PASSIVE\n"
+ "Socket address is intended for @code{bind}.\n\n"
+ "@item AI_CANONNAME\n"
+ "Request for canonical host name, available via "
+ "@code{addrinfo:canonname}. This makes sense mainly when "
+ "DNS lookups are involved.\n\n"
+ "@item AI_NUMERICHOST\n"
+ "Specifies that @var{name} is a numeric host address string "
+ "(e.g., @code{\"127.0.0.1\"}), meaning that name resolution "
+ "will not be used.\n\n"
+ "@item AI_NUMERICSERV\n"
+ "Likewise, specifies that @var{service} is a numeric port "
+ "string (e.g., @code{\"80\"}).\n\n"
+ "@item AI_ADDRCONFIG\n"
+ "Return only addresses configured on the local system. It is "
+ "highly recommended to provide this flag when the returned "
+ "socket addresses are to be used to make connections; "
+ "otherwise, some of the returned addresses could be unreachable "
+ "or use a protocol that is not supported.\n\n"
+ "@item AI_V4MAPPED\n"
+ "When looking up IPv6 addresses, return mapped "
+ "IPv4 addresses if there is no IPv6 address available at all.\n\n"
+ "@item AI_ALL\n"
+ "If this flag is set along with @code{AI_V4MAPPED} when looking "
+ "up IPv6 addresses, return all IPv6 addresses "
+ "as well as all IPv4 addresses, the latter mapped to IPv6 "
+ "format.\n"
+ "@end table\n\n"
+ "When given, @var{hint_family} should specify the requested "
+ "address family, e.g., @code{AF_INET6}. Similarly, "
+ "@var{hint_socktype} should specify the requested socket type "
+ "(e.g., @code{SOCK_DGRAM}), and @var{hint_protocol} should "
+ "specify the requested protocol (its value is interpretered "
+ "as in calls to @code{socket}).\n"
+ "\n"
+ "On error, an exception with key @code{getaddrinfo-error} is "
+ "thrown, with an error code (an integer) as its argument:\n\n"
+ "@example\n"
+ "(catch 'getaddrinfo-error\n"
+ " (lambda ()\n"
+ " (getaddrinfo \"www.gnu.org\" \"gopher\"))\n"
+ " (lambda (key errcode)\n"
+ " (cond ((= errcode EAI_SERVICE)\n"
+ " (display \"doesn't know about Gopher!\\n\"))\n"
+ " ((= errcode EAI_NONAME)\n"
+ " (display \"www.gnu.org not found\\n\"))\n"
+ " (else\n"
+ " (format #t \"something wrong: ~a\\n\"\n"
+ " (gai-strerror errcode))))))\n"
+ "@end example\n"
+ "\n"
+ "Error codes are:\n\n"
+ "@table @code\n"
+ "@item EAI_AGAIN\n"
+ "The name or service could not be resolved at this time. Future "
+ "attempts may succeed.\n\n"
+ "@item EAI_BADFLAGS\n"
+ "@var{hint_flags} contains an invalid value.\n\n"
+ "@item EAI_FAIL\n"
+ "A non-recoverable error occurred when attempting to "
+ "resolve the name.\n\n"
+ "@item EAI_FAMILY\n"
+ "@var{hint_family} was not recognized.\n\n"
+ "@item EAI_NONAME\n"
+ "Either @var{name} does not resolve for the supplied parameters, "
+ "or neither @var{name} nor @var{service} were supplied.\n\n"
+ "@item EAI_SERVICE\n"
+ "@var{service} was not recognized for the specified socket type.\n\n"
+ "@item EAI_SOCKTYPE\n"
+ "@var{hint_socktype} was not recognized.\n\n"
+ "@item EAI_SYSTEM\n"
+ "A system error occurred; the error code can be found in "
+ "@code{errno}.\n"
+ "@end table\n"
+ "\n"
+ "Users are encouraged to read the "
+ "@url{http://www.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html,"
+ "POSIX specification} for more details.\n")
+#define FUNC_NAME s_scm_getaddrinfo
+{
+ int err;
+ char *c_name, *c_service;
+ struct addrinfo c_hints, *c_result;
+ SCM result = SCM_EOL;
+
+ if (scm_is_true (name))
+ SCM_VALIDATE_STRING (SCM_ARG1, name);
+
+ if (!SCM_UNBNDP (service) && scm_is_true (service))
+ SCM_VALIDATE_STRING (SCM_ARG2, service);
+
+ scm_dynwind_begin (0);
+
+ if (scm_is_string (name))
+ {
+ c_name = scm_to_locale_string (name);
+ scm_dynwind_free (c_name);
+ }
+ else
+ c_name = NULL;
+
+ if (scm_is_string (service))
+ {
+ c_service = scm_to_locale_string (service);
+ scm_dynwind_free (c_service);
+ }
+ else
+ c_service = NULL;
+
+ memset (&c_hints, 0, sizeof (c_hints));
+ if (!SCM_UNBNDP (hint_flags))
+ {
+ c_hints.ai_flags = scm_to_int (hint_flags);
+ if (!SCM_UNBNDP (hint_family))
+ {
+ c_hints.ai_family = scm_to_int (hint_family);
+ if (!SCM_UNBNDP (hint_socktype))
+ {
+ c_hints.ai_socktype = scm_to_int (hint_socktype);
+ if (!SCM_UNBNDP (hint_family))
+ c_hints.ai_family = scm_to_int (hint_family);
+ }
+ }
+ }
-void
+ err = getaddrinfo (c_name, c_service, &c_hints, &c_result);
+ if (err == 0)
+ {
+ SCM *prev_addr;
+ struct addrinfo *a;
+
+ for (prev_addr = &result, a = c_result;
+ a != NULL;
+ a = a->ai_next, prev_addr = SCM_CDRLOC (*prev_addr))
+ *prev_addr = scm_list_1 (scm_from_addrinfo (a));
+
+ freeaddrinfo (c_result);
+ }
+ else
+ scm_throw (sym_getaddrinfo_error, scm_list_1 (scm_from_int (err)));
+
+ scm_dynwind_end ();
+
+ return result;
+}
+#undef FUNC_NAME
+
+/* Make sure the `EAI_*' flags can be stored as INUMs. */
+verify (SCM_I_INUM (SCM_I_MAKINUM (EAI_BADFLAGS)) == EAI_BADFLAGS);
+
+/* Error codes returned by `getaddrinfo'. */
+SCM_VARIABLE_INIT (sym_eai_badflags, "EAI_BADFLAGS",
+ SCM_I_MAKINUM (EAI_BADFLAGS));
+SCM_VARIABLE_INIT (sym_eai_noname, "EAI_NONAME",
+ SCM_I_MAKINUM (EAI_NONAME));
+SCM_VARIABLE_INIT (sym_eai_again, "EAI_AGAIN",
+ SCM_I_MAKINUM (EAI_AGAIN));
+SCM_VARIABLE_INIT (sym_eai_fail, "EAI_FAIL",
+ SCM_I_MAKINUM (EAI_FAIL));
+SCM_VARIABLE_INIT (sym_eai_family, "EAI_FAMILY",
+ SCM_I_MAKINUM (EAI_FAMILY));
+SCM_VARIABLE_INIT (sym_eai_socktype, "EAI_SOCKTYPE",
+ SCM_I_MAKINUM (EAI_SOCKTYPE));
+SCM_VARIABLE_INIT (sym_eai_service, "EAI_SERVICE",
+ SCM_I_MAKINUM (EAI_SERVICE));
+SCM_VARIABLE_INIT (sym_eai_memory, "EAI_MEMORY",
+ SCM_I_MAKINUM (EAI_MEMORY));
+SCM_VARIABLE_INIT (sym_eai_system, "EAI_SYSTEM",
+ SCM_I_MAKINUM (EAI_SYSTEM));
+SCM_VARIABLE_INIT (sym_eai_overflow, "EAI_OVERFLOW",
+ SCM_I_MAKINUM (EAI_OVERFLOW));
+
+/* The following values are GNU extensions. */
+#ifdef EAI_NODATA
+SCM_VARIABLE_INIT (sym_eai_nodata, "EAI_NODATA",
+ SCM_I_MAKINUM (EAI_NODATA));
+#endif
+#ifdef EAI_ADDRFAMILY
+SCM_VARIABLE_INIT (sym_eai_addrfamily, "EAI_ADDRFAMILY",
+ SCM_I_MAKINUM (EAI_ADDRFAMILY));
+#endif
+#ifdef EAI_INPROGRESS
+SCM_VARIABLE_INIT (sym_eai_inprogress, "EAI_INPROGRESS",
+ SCM_I_MAKINUM (EAI_INPROGRESS));
+#endif
+#ifdef EAI_CANCELED
+SCM_VARIABLE_INIT (sym_eai_canceled, "EAI_CANCELED",
+ SCM_I_MAKINUM (EAI_CANCELED));
+#endif
+#ifdef EAI_NOTCANCELED
+SCM_VARIABLE_INIT (sym_eai_notcanceled, "EAI_NOTCANCELED",
+ SCM_I_MAKINUM (EAI_NOTCANCELED));
+#endif
+#ifdef EAI_ALLDONE
+SCM_VARIABLE_INIT (sym_eai_alldone, "EAI_ALLDONE",
+ SCM_I_MAKINUM (EAI_ALLDONE));
+#endif
+#ifdef EAI_INTR
+SCM_VARIABLE_INIT (sym_eai_intr, "EAI_INTR",
+ SCM_I_MAKINUM (EAI_INTR));
+#endif
+#ifdef EAI_IDN_ENCODE
+SCM_VARIABLE_INIT (sym_eai_idn_encode, "EAI_IDN_ENCODE",
+ SCM_I_MAKINUM (EAI_IDN_ENCODE));
+#endif
+
+SCM_DEFINE (scm_gai_strerror, "gai-strerror", 1, 0, 0,
+ (SCM error),
+ "Return a string describing @var{error}, an integer error code "
+ "returned by @code{getaddrinfo}.")
+#define FUNC_NAME s_scm_gai_strerror
+{
+ return scm_from_locale_string (gai_strerror (scm_to_int (error)));
+}
+#undef FUNC_NAME
+
+/* TODO: Add a getnameinfo(3) wrapper. */
+
+
+void
scm_init_net_db ()
{
scm_add_feature ("net-db");