From 704d0d62b4ab901e349e01a3638a3d845c1192d0 Mon Sep 17 00:00:00 2001 From: Mats Erik Andersson Date: Tue, 18 Jan 2011 12:05:41 +0100 Subject: [PATCH] src/logger: Implement support for IPv6. --- ChangeLog | 13 +++++ doc/inetutils.texi | 49 ++++++++++++----- src/logger.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 194 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index c474101..b2df64d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2011-01-18 Mats Erik Andersson + + * src/logger.c (host_family): New variable. + (struct logger_sockaddr): New component SINET6. + (open_socket): Implement new code based on getaddrinfo. + These are conditioned on HAVE_DECL_GETADDRINFO and HAVE_IPV6. + Reorganize old handler of port specification. + (argp_options): New options `--ipv4' and `--ipv6` conditioned + on HAVE_IPV6. + (parse_opt): Implement parsing of `--ipv4' and `--ipv6'. + * doc/inetutils.texi (logger): Describe implications of + IPv6-support. Small corrections to example usage. + 2010-12-21 Mats Erik Andersson * src/inetd.c (pidfile_option): New variable. diff --git a/doc/inetutils.texi b/doc/inetutils.texi index 7249bbf..740dd8b 100644 --- a/doc/inetutils.texi +++ b/doc/inetutils.texi @@ -287,6 +287,24 @@ logger address@hidden@dots{}] address@hidden @end example @table @option address@hidden -4 address@hidden --ipv4 address@hidden -4 address@hidden --ipv4 +Use IPv4 as transport when logging to a host. The default behaviour +is to use whatever IP version that matches the host. + address@hidden -6 address@hidden --ipv6 address@hidden -6 address@hidden --ipv6 +Use IPv6 as transport when loggin to a host. + +The above two options are present on systems supporting IPv6. +Both options are influencial only when the target host is +named using a symbolic name, but numerical addresses must also +match if either of @option{--ipv4} or @option{--ipv6} is stated. + @item address@hidden @itemx address@hidden @opindex -i @@ -303,26 +321,29 @@ must be separated from it by exactly one equals sign. @opindex -h @opindex --host Send messages to the given host or socket. The @var{host} argument -can be either a local UNIX socket name (always starting with a address@hidden/} or: +can be either a local UNIX socket name (starting with a dash @samp{/}), +or: @smallexample @var{host}[:@var{port}] @end smallexample @noindent -where @var{host} is the remote host name or IP address (only IPv4 is -supported so far), and optional @var{port} is a decimal port number or -symbolic service name from @file{/etc/services}. If @var{port} is not -specified, the port number corresponding to the @samp{syslog} service -is used. +where @var{host} is the remote host name or IP address, and the +optional @var{port} is a decimal port number or symbolic service +name from @file{/etc/services}. If @var{port} is not specified, +the port number corresponding to the @samp{syslog} service is used. +If a numerical IPv6 address is given without a port specification, +then the address must be enclosed within brackets (like [::1]). @item -S @var{addr} @itemx address@hidden @opindex -S @opindex --source Supply the source IP address for INET connections. This option is -useful in conjunction with @option{--host} (see above). +useful in conjunction with @option{--host} (see above). The kind of +address specified here (IPv4 or IPv6) will propagate to influence +the resolution of the host address, if it is a symbolic name. @item -s @itemx --stderr @@ -366,23 +387,23 @@ The following examples illustrate the usage of the @command{logger} command: @enumerate 1 address@hidden Log the @samp{System rebooted} message to the local syslog. Use -the default facility and priority: address@hidden Log the message @samp{System rebooted} to the local syslog. +Use default facility and priority: @example logger System rebooted @end example @item Run command and send its error output to the channel address@hidden channel. Mark each message with tag @samp{cmd}: address@hidden Mark each message with tag @samp{cmd}: @example command 2>&1 | logger -p local0.notice -t cmd @end example address@hidden Log each line from file @file{warnings} to @samp{daemon.warn} -channel on host @samp{logger.runasimi.org}, using the source IP address@hidden: address@hidden Log each line from file @file{warnings} to channel address@hidden on host @samp{logger.runasimi.org}, +using the source IP @samp{10.10.10.1}: @example logger -p daemon.warn -h logger.runasimi.org -S 10.10.10.1 --file diff --git a/src/logger.c b/src/logger.c index f112334..505af04 100644 --- a/src/logger.c +++ b/src/logger.c @@ -56,6 +56,10 @@ static char *host = PATH_LOG; static char *source; static char *pidstr; +#if HAVE_DECL_GETADDRINFO +static int host_family = AF_UNSPEC; +#endif + int @@ -104,6 +108,9 @@ union logger_sockaddr { struct sockaddr sa; struct sockaddr_in sinet; +#if HAVE_IPV6 + struct sockaddr_in6 sinet6; +#endif struct sockaddr_un sunix; }; @@ -115,6 +122,9 @@ open_socket (void) union logger_sockaddr sockaddr; socklen_t socklen; int family; +#if HAVE_DECL_GETADDRINFO + int ret; +#endif if (host[0] == '/') { @@ -128,14 +138,131 @@ open_socket (void) } else { +#if HAVE_DECL_GETADDRINFO + struct addrinfo hints, *ai, *res; +#else struct hostent *hp; struct servent *sp; unsigned short port; +#endif /* !HAVE_DECL_GETADDRINFO */ char *p; +#if HAVE_IPV6 + /* Bare, numeric IPv6 addresses must be contained + * in brackets in order that an appended port not + * be read by mistake. */ + if (*host == '[') + { + ++host; + p = strchr (host, ']'); + if (p) + { + *p++ = '\0'; + if (*p == ':') + ++p; + else + p = NULL; + } + } + else + { + /* When no bracket was detected, then seek the + * right-most colon character in order to correctly + * parse IPv6 addresses. */ + p = strrchr (host, ':'); + if (p) + *p++ = 0; + } +#else /* !HAVE_IPV6 */ p = strchr (host, ':'); if (p) *p++ = 0; +#endif /* !HAVE_IPV6 */ + + if (!p) + p = "syslog"; + +#if HAVE_DECL_GETADDRINFO + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_DGRAM; + +# if HAVE_IPV6 + hints.ai_family = host_family; +# else /* !HAVE_IPV6 */ + hints.ai_family = AF_INET; +# endif /* !HAVE_IPV6 */ + +# ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +# endif + + /* The complete handshake is attempted within + * a single while-loop, since the answers from + * getaddrinfo() need to be checked in detail. */ + ret = getaddrinfo (host, p, &hints, &res); + if (ret < 0) + error (EXIT_FAILURE, 0, "cannot resolve host"); + + + for (ai = res; ai; ai = ai->ai_next) + { + fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) + continue; + + if (source) + { + /* Make the assumption that the source address + * encodes a desired domain family and that it + * be numeric. */ + int ret; + struct addrinfo tips, *a; + + memset (&tips, 0, sizeof (tips)); + tips.ai_family = ai->ai_family; + tips.ai_flags = AI_NUMERICHOST; + + ret = getaddrinfo(source, NULL, &tips, &a); + if (ret) + { + close (fd); + continue; + } + + if (bind (fd, a->ai_addr, a->ai_addrlen)) + { + freeaddrinfo (a); + close (fd); + continue; + } + + freeaddrinfo (a); + } + + if (connect (fd, ai->ai_addr, ai->ai_addrlen)) + { + close (fd); + continue; + } + + /* Socket standing, bound and connected. */ + break; + } + + freeaddrinfo (res); + + if (ai == NULL) + error (EXIT_FAILURE, 0, "cannot connect to host"); + + /* Existing socket can be returned now. + * This handles AF_INET and AF_INET6 in case + * HAVE_DECL_GETADDRINFO is true. */ + return; + +#else /* !HAVE_DECL_GETADDRINFO */ + + sockaddr.sinet.sin_family = AF_INET; + family = PF_INET; hp = gethostbyname (host); if (hp) @@ -144,11 +271,6 @@ open_socket (void) != 1) error (EXIT_FAILURE, 0, "unknown host name"); - sockaddr.sinet.sin_family = AF_INET; - family = PF_INET; - if (!p) - p = "syslog"; - if (isdigit (*p)) { char *end; @@ -163,9 +285,14 @@ open_socket (void) error (EXIT_FAILURE, 0, "%s: unknown service name", p); sockaddr.sinet.sin_port = port; +#endif /* !HAVE_DECL_GETADDRINFO */ + socklen = sizeof (sockaddr.sinet); } + /* Execution arrives here for AF_UNIX and for + * situations with !HAVE_DECL_GETADDRINFO. */ + fd = socket (family, SOCK_DGRAM, 0); if (fd < 0) error (EXIT_FAILURE, errno, "cannot create socket"); @@ -244,6 +371,10 @@ const char args_doc[] = "[MESSAGE]"; const char doc[] = "Send messages to syslog"; static struct argp_option argp_options[] = { +#if HAVE_IPV6 + {"ipv4", '4', NULL, 0, "use IPv4 for logging to host" }, + {"ipv6", '6', NULL, 0, "use IPv6 with a host target" }, +#endif { "host", 'h', "HOST", 0, "log to host instead of the default " PATH_LOG }, { "source", 'S', "IP", 0, @@ -264,6 +395,16 @@ parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { +#if HAVE_IPV6 + case '4': + host_family = AF_INET; + break; + + case '6': + host_family = AF_INET6; + break; +#endif /* HAVE_IPV6 */ + case 'h': host = arg; break; -- 1.7.2.3