Hi Arjen,
in server/upsd.c r731, you moved the call conf_load() from after
check_perms() (~ l.1020) to before setupsignals() (~ l.989).
The problem is that conf_load() needs to open the ups driver socket,
and it assumes that STATEPATH is the current working directory. The
directory is only set in l.1016. Therefore, the first attempt to open
a socket will always fail. From the user's point of view:
Can't connect to UPS [mge] (mge): No such file or directory
The socket is later opened on the second attempt, but only after upsd
has forked into the background, so the command-line user is left with
no further information after the first error message.
Why was it necessary to move conf_load()? In this case,
chdir(statepath) should be moved before it. But the problem is that
become_user() has to be before chdir (to check permission), and
chroot_start() has to be before become_user(), and this might perhaps
mess up listen_add() (does listen_add() require root access)?
This looks like a catch-22 to me, but I don't understand the code as
well as you do. Is there some sequence of events that will work?
Moreover, conf_load() is now called before the chroot() and
become_user(), which means that ups.conf is read by root and in the
raw directory tree. This is not how it was intended; perhaps even a
potential security problem.
-- Peter
Arjen de Korte wrote:>
> Author: adkorte-guest
> Date: Sat Jan 13 21:22:28 2007
> New Revision: 731
>
> Added:
> trunk/server/stype.h
> Modified:
> trunk/ChangeLog
> trunk/clients/upsclient.c
> trunk/clients/upsclient.h
> trunk/conf/upsd.conf.sample
> trunk/m4/nut_check_ipv6.m4
> trunk/server/conf.c
> trunk/server/upsd.c
> trunk/server/upsd.h
> trunk/server/upstype.h
> Log:
> * clients/upsclient.ch:
> - hostname can now be specified as a fully.qualified.domain.name or an
[address literal]
> - cleaned up IPv6 code
> * m4/nut_check_ipv6.m4:
> - removed check for AI_ADDRCONFIG, since it is no longer used (for
portability reasons, wasn't really needed anyway)
> * server/upsd.ch, server/conf.c, server/stype.h:
> - listen on multiple TCP sockets (to be configured in upsd.conf)
> - removed -i and -p options which are obsoleted by the above
> - listening address can be specified as a fully.qualified.domain.name or
an [address literal]
> - cleaned up IPv6 code
> * conf/upsd.conf.sample:
> - added new LISTEN parameter
>
> IPv6 code needs testing. From initial tests, it looks like the
ACCESS/REJECT doesn't work in the way expected, so this might need some
refining.
>
> Modified: trunk/ChangeLog
>
=============================================================================>
--- trunk/ChangeLog (original)
> +++ trunk/ChangeLog Sat Jan 13 21:22:28 2007
> @@ -1,3 +1,21 @@
> +Sat Jan 13 20:12:16 UTC 2007 / Arjen de Korte <arjen@de-korte.org>
> +
> + * clients/upsclient.ch:
> + - hostname can now be specified as a fully.qualified.domain.name or an
> + [address literal]
> + - cleaned up IPv6 code
> + * m4/nut_check_ipv6.m4:
> + - removed check for AI_ADDRCONFIG, since it is no longer used (for
> + portability reasons, wasn't really needed anyway)
> + * server/upsd.ch, server/conf.c, server/stype.h:
> + - listen on multiple TCP sockets (to be configured in upsd.conf)
> + - removed -i and -p options which are obsoleted by the above
> + - listening address can be specified as a fully.qualified.domain.name
> + or an [address literal]
> + - cleaned up IPv6 code
> + * conf/upsd.conf.sample:
> + - added new LISTEN parameter
> +
> Wed Jan 10 15:42:20 UTC 2007 / Arjen de Korte <arjen@de-korte.org>
>
> - server/upsd.c: set a default for the listenaddress if we're using
>
> Modified: trunk/clients/upsclient.c
>
=============================================================================>
--- trunk/clients/upsclient.c (original)
> +++ trunk/clients/upsclient.c Sat Jan 13 21:22:28 2007
> @@ -27,6 +27,7 @@
> #include <unistd.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> +#include <arpa/inet.h>
>
> #include "upsclient.h"
>
> @@ -418,15 +419,15 @@
> }
>
> #endif /* HAVE_SSL */
> -
> +
> int upscli_connect(UPSCONN *ups, const char *host, int port, int flags)
> {
> #ifndef HAVE_IPV6
> - struct sockaddr_in local, server;
> - struct hostent *serv;
> + struct sockaddr_in local, server;
> + struct hostent *serv;
> #else
> - struct addrinfo hints, *r, *rtmp;
> - char *service;
> + struct addrinfo hints, *res, *ai;
> + char sport[NI_MAXSERV];
> #endif
>
> /* clear out any lingering junk */
> @@ -437,11 +438,10 @@
> ups->syserrno = 0;
> ups->upsclient_magic = UPSCLIENT_MAGIC;
>
> - ups->pc_ctx = malloc(sizeof(PCONF_CTX));
> -
> - if (!ups->pc_ctx) {
> + if ((ups->pc_ctx = malloc(sizeof(PCONF_CTX))) == NULL)
> + {
> ups->upserror = UPSCLI_ERR_NOMEM;
> - return -1;
> + return -1;
> }
>
> pconf_init(ups->pc_ctx, NULL);
> @@ -449,19 +449,27 @@
> ups->ssl_ctx = NULL;
> ups->ssl = NULL;
>
> - if (!host) {
> + if (!host)
> + {
> ups->upserror = UPSCLI_ERR_NOSUCHHOST;
> return -1;
> }
>
> #ifndef HAVE_IPV6
> - if ((serv = gethostbyname(host)) == (struct hostent *) NULL) {
> -
> - ups->upserror = UPSCLI_ERR_NOSUCHHOST;
> - return -1;
> + if ((serv = gethostbyname(host)) == (struct hostent *)NULL)
> + {
> + struct in_addr listenaddr;
> +
> + if (!inet_aton(host, &listenaddr) || ((serv =
gethostbyaddr(&listenaddr,
> + sizeof(listenaddr), AF_INET)) == (struct hostent *)NULL))
> + {
> + ups->upserror = UPSCLI_ERR_NOSUCHHOST;
> + return -1;
> + }
> }
>
> - if ((ups->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
> + if ((ups->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
> + {
> ups->upserror = UPSCLI_ERR_SOCKFAILURE;
> ups->syserrno = errno;
> return -1;
> @@ -477,140 +485,123 @@
>
> memcpy(&server.sin_addr, serv->h_addr, serv->h_length);
>
> - if (bind(ups->fd, (struct sockaddr *) &local,
> - sizeof(struct sockaddr_in)) == -1) {
> + if (bind(ups->fd, (struct sockaddr *) &local, sizeof(struct
sockaddr_in)) == -1)
> + {
> ups->upserror = UPSCLI_ERR_BINDFAILURE;
> ups->syserrno = errno;
> close(ups->fd);
> ups->fd = -1;
> -
> return -1;
> }
>
> - if (connect(ups->fd, (struct sockaddr *) &server,
> - sizeof(struct sockaddr_in)) == -1) {
> + if (connect(ups->fd, (struct sockaddr *) &server, sizeof(struct
sockaddr_in)) == -1)
> + {
> ups->upserror = UPSCLI_ERR_CONNFAILURE;
> ups->syserrno = errno;
> close(ups->fd);
> ups->fd = -1;
> -
> return -1;
> }
> -
> - /* don't use xstrdup for cleaner linking (fewer dependencies) */
> - ups->host = strdup(host);
> -
> - if (!ups->host) {
> - close(ups->fd);
> - ups->fd = -1;
> -
> - ups->upserror = UPSCLI_ERR_NOMEM;
> - return -1;
> - }
> -
> - ups->port = port;
> -
> - if (flags & UPSCLI_CONN_TRYSSL) {
> - upscli_sslinit(ups);
> -
> - /* see if something made us die inside sslinit */
> - if (ups->upserror != 0)
> - return -1;
> - }
> -
> - if (flags & UPSCLI_CONN_REQSSL) {
> - if (upscli_sslinit(ups) != 1) {
> - ups->upserror = UPSCLI_ERR_SSLFAIL;
> - upscli_closefd(ups);
> - return -1;
> - }
> - }
> -
> - return 0;
> #else
> - service = malloc (sizeof (char) * 6);
> - if (service == NULL) {
> - ups->upserror = UPSCLI_ERR_NOMEM;
> - return -1;
> - }
> -
> - if (snprintf (service, 6, "%hu", (unsigned short int)port) <
1) {
> - return -1;
> - }
> + snprintf(sport, NI_MAXSERV, "%hu", (unsigned short int)port);
>
> memset (&hints, 0, sizeof (struct addrinfo));
> - hints.ai_family = flags & UPSCLI_CONN_INET ? AF_INET : (flags &
UPSCLI_CONN_INET6 ? AF_INET6 : AF_UNSPEC);
> + hints.ai_family = AF_UNSPEC;
> hints.ai_socktype = SOCK_STREAM;
> hints.ai_protocol = IPPROTO_TCP;
> - hints.ai_flags = AI_ADDRCONFIG;
>
> - if (getaddrinfo (host, service, &hints, &r) != 0) {
> + if (getaddrinfo(host, sport, &hints, &res) != 0)
> + {
> ups->upserror = UPSCLI_ERR_NOSUCHHOST;
> - free (service);
> return -1;
> }
> - free (service);
>
> - for (rtmp = r; r != NULL; r = r->ai_next) {
> - ups->fd = socket (r->ai_family, r->ai_socktype,
r->ai_protocol);
> - if (ups->fd < 0) {
> - if (r->ai_next == NULL) {
> + for (ai = res; ai != NULL; ai = ai->ai_next)
> + {
> + int sock_fd, v;
> +
> + if (ai->ai_socktype != hints.ai_socktype || ai->ai_protocol !=
hints.ai_protocol)
> + continue;
> +
> + if ((sock_fd = socket(ai->ai_family, ai->ai_socktype,
ai->ai_protocol)) < 0)
> + {
> + switch (errno)
> + {
> + case EAFNOSUPPORT:
> + case EINVAL:
> + continue;
> + default:
> ups->upserror = UPSCLI_ERR_SOCKFAILURE;
> ups->syserrno = errno;
> - break;
> + continue;
> }
> - continue;
> }
>
> - if (connect (ups->fd, r->ai_addr, r->ai_addrlen) == -1) {
> - close (ups->fd);
> - ups->fd = -1;
> - if (r->ai_next == NULL) {
> + while ((v = connect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0)
> + {
> + switch (errno)
> + {
> + case EAFNOSUPPORT:
> + break;
> + case EINTR:
> + case EAGAIN:
> + continue;
> + default:
> ups->upserror = UPSCLI_ERR_CONNFAILURE;
> ups->syserrno = errno;
> break;
> }
> - continue;
> }
> - freeaddrinfo (rtmp);
>
> - /* don't use xstrdup for cleaner linking (fewer dependencies) */
> - ups->host = strdup(host);
> + if (v < 0)
> + {
> + close(sock_fd);
> + continue;
> + }
>
> - if (!ups->host) {
> - close(ups->fd);
> - ups->fd = -1;
> + ups->fd = sock_fd;
> + ups->upserror = 0;
> + ups->syserrno = 0;
> + break;
> + }
>
> - ups->upserror = UPSCLI_ERR_NOMEM;
> - return -1;
> - }
> + freeaddrinfo(res);
>
> - ups->port = port;
> + if (ups->fd < 0)
> + return -1;
> +#endif
>
> - if (flags & UPSCLI_CONN_TRYSSL) {
> - upscli_sslinit(ups);
> + if ((ups->host = strdup(host)) == NULL)
> + {
> + ups->upserror = UPSCLI_ERR_NOMEM;
> + close(ups->fd);
> + ups->fd = -1;
> + return -1;
> + }
>
> - /* see if something made us die inside sslinit */
> - if (ups->upserror != 0)
> - return -1;
> - }
> + ups->port = port;
>
> - if (flags & UPSCLI_CONN_REQSSL) {
> - if (upscli_sslinit(ups) != 1) {
> - ups->upserror = UPSCLI_ERR_SSLFAIL;
> - upscli_closefd(ups);
> - return -1;
> - }
> - }
> + if (flags & UPSCLI_CONN_TRYSSL)
> + {
> + upscli_sslinit(ups);
>
> - return 0;
> + /* see if something made us die inside sslinit */
> + if (ups->upserror != 0)
> + return -1;
> }
> - freeaddrinfo (rtmp);
>
> - return -1;
> -#endif
> + if (flags & UPSCLI_CONN_REQSSL)
> + {
> + if (upscli_sslinit(ups) != 1)
> + {
> + ups->upserror = UPSCLI_ERR_SSLFAIL;
> + upscli_closefd(ups);
> + return -1;
> + }
> + }
> +
> + return 0;
> }
> -
> /* map upsd error strings back to upsclient internal numbers */
> static struct {
> int errnum;
> @@ -921,106 +912,60 @@
> return 0;
> }
>
> -/* split [upsname@]hostname[:port] into separate components */
> -int upscli_splitname(const char *buf, char **upsname, char **hostname,
> - int *port)
> +/* split upsname@hostname[:port] into separate components */
> +int upscli_splitname(const char *buf, char **upsname, char **hostname, int
*port)
> {
> - char tmp[SMALLBUF], *ptr, *ap, *cp;
> + char tmp[SMALLBUF], *s;
>
> + /* paranoia */
> if ((!buf) || (!upsname) || (!hostname) || (!port))
> return -1;
>
> snprintf(tmp, sizeof(tmp), "%s", buf);
>
> - ap = strchr(tmp, '@');
> -
> - if (!ap) {
> + /* split at the '@' character */
> + if ((s = strtok(tmp, "@")) == NULL)
> + {
> fprintf(stderr, "upscli_splitname: no UPS name specified
(upsname@hostname)\n");
> return -1;
> }
>
> - *ap++ = '\0';
> - *upsname = strdup(tmp);
> -
> - if (!*upsname) {
> + if ((*upsname = strdup(s)) == NULL)
> + {
> fprintf(stderr, "upscli_splitname: strdup failed\n");
> return -1;
> }
>
> - ptr = ap;
> -
> -#ifndef HAVE_IPV6
> - cp = strchr(ptr, ':');
> -
> - if (cp) {
> - *cp++ = '\0';
> - *hostname = strdup(ptr);
> -
> - if (!*hostname) {
> - fprintf(stderr, "upscli_splitname: strdup failed\n");
> - return -1;
> - }
> -
> - ptr = cp;
> -
> - *port = strtol(ptr, (char **) NULL, 10);
> -
> - } else {
> -
> - *hostname = strdup(ptr);
> -
> - if (!*hostname) {
> - fprintf(stderr, "upscli_splitname: strdup failed\n");
> - return -1;
> - }
> -
> - *port = PORT;
> + if ((s = strtok(NULL, "\0")) == NULL)
> + {
> + fprintf(stderr, "upscli_splitname: no hostname specified
(upsname@hostname)\n");
> + return -1;
> }
> -#else
> - if (*ptr != '[') {
> - cp = strchr(ptr, ':');
> - if (cp) {
> - *cp++ = '\0';
> - *hostname = strdup(ptr);
> -
> - if (!*hostname) {
> - fprintf(stderr, "upscli_splitname: strdup failed\n");
> - return -1;
> - }
>
> - ptr = cp;
> -
> - *port = strtol(ptr, (char **) NULL, 10);
> -
> - } else {
> + if (*s == '[')
> + s = strtok(s+1, "]"); /* address literal */
> + else
> + s = strtok(s, ":\0"); /* hostname */
> +
> + if (s == NULL)
> + {
> + fprintf(stderr, "upscli_splitname: no hostname specified
(upsname@hostname)\n");
> + return -1;
> + }
>
> - *hostname = strdup(ptr);
> + if ((*hostname = strdup(s)) == NULL)
> + {
> + fprintf(stderr, "upscli_splitname: strdup failed\n");
> + return -1;
> + }
>
> - if (!*hostname) {
> - fprintf(stderr, "upscli_splitname: strdup failed\n");
> - return -1;
> - }
> + /* skip the separator between hostname and port (if any) */
> + if (((s = strtok(NULL, "\0")) != NULL) && (*s ==
':')) s++;
>
> - *port = PORT;
> - }
> - } else {
> - ptr++;
> - cp = strchr(ptr, ']');
> - if (cp) {
> - *cp = '\0';
> - *hostname = strdup (ptr);
> - ptr = ++cp;
> - cp = strchr (ptr, ':');
> - if (cp != NULL)
> - *port = strtol (++cp, (char **)NULL, 10);
> - else
> - *port = PORT;
> - } else {
> - fprintf (stderr, "upscli_splitname: strchr(']')
failed\n");
> - return -1;
> - }
> - }
> -#endif
> + if (s == NULL)
> + *port = PORT;
> + else
> + *port = strtol(s, NULL, 10);
>
> return 0;
> }
>
> Modified: trunk/clients/upsclient.h
>
=============================================================================>
--- trunk/clients/upsclient.h (original)
> +++ trunk/clients/upsclient.h Sat Jan 13 21:22:28 2007
> @@ -149,11 +149,6 @@
> #define UPSCLI_CONN_TRYSSL 0x0001 /* try SSL, OK if not supported */
> #define UPSCLI_CONN_REQSSL 0x0002 /* try SSL, fail if not supported */
>
> -#ifdef HAVE_IPV6
> -#define UPSCLI_CONN_INET 0x0004 /* IPv4 only */
> -#define UPSCLI_CONN_INET6 0x0008 /* IPv6 only */
> -#endif
> -
> #ifdef __cplusplus
> }
> #endif
>
> Modified: trunk/conf/upsd.conf.sample
>
=============================================================================>
--- trunk/conf/upsd.conf.sample (original)
> +++ trunk/conf/upsd.conf.sample Sat Jan 13 21:22:28 2007
> @@ -40,3 +40,15 @@
> # You should only use this if your driver has difficulties keeping
> # the data fresh within the normal 15 second interval. Watch the syslog
> # for notifications from upsd about staleness.
> +
> +#
======================================================================> +#
LISTEN <address> [<port>]
> +# LISTEN 0.0.0.0 3493
> +#
> +# This defaults to the global IPv4 listening address and port 3493. You
> +# may specify each interface you want upsd to listen on for connections,
> +# optionally with a port number.
> +#
> +# You may need this if you have multiple interfaces on your machine and
> +# you don't want upsd to listen to all interfaces (for instance on a
> +# firewall, you may not want to listen to the external interface).
>
> Modified: trunk/m4/nut_check_ipv6.m4
>
=============================================================================>
--- trunk/m4/nut_check_ipv6.m4 (original)
> +++ trunk/m4/nut_check_ipv6.m4 Sat Jan 13 21:22:28 2007
> @@ -29,17 +29,17 @@
> dnl [nut_have_ipv6=no],
> dnl [#include <netdb.h>])
>
> - AC_MSG_CHECKING([for AI_ADDRCONFIG])
> - AC_COMPILE_IFELSE(
> - [AC_LANG_PROGRAM(
> - [[#include <netdb.h>]],
> - [[int flag = AI_ADDRCONFIG]]
> - )],
> - [AC_DEFINE(HAVE_AI_ADDRCONFIG, 1, [Define if `addrinfo'
structure allows AI_ADDRCONFIG flag])
> - AC_MSG_RESULT(yes)],
> - [AC_MSG_RESULT(no)
> - nut_have_ipv6=no]
> - )
> +dnl AC_MSG_CHECKING([for AI_ADDRCONFIG])
> +dnl AC_COMPILE_IFELSE(
> +dnl [AC_LANG_PROGRAM(
> +dnl [[#include <netdb.h>]],
> +dnl [[int flag = AI_ADDRCONFIG]]
> +dnl )],
> +dnl [AC_DEFINE(HAVE_AI_ADDRCONFIG, 1, [Define if `addrinfo'
structure allows AI_ADDRCONFIG flag])
> +dnl AC_MSG_RESULT(yes)],
> +dnl [AC_MSG_RESULT(no)
> +dnl nut_have_ipv6=no]
> +dnl)
>
> AC_MSG_CHECKING([for IN6_IS_ADDR_V4MAPPED])
> AC_LINK_IFELSE(
>
> Modified: trunk/server/conf.c
>
=============================================================================>
--- trunk/server/conf.c (original)
> +++ trunk/server/conf.c Sat Jan 13 21:22:28 2007
> @@ -25,6 +25,8 @@
> #include "user.h"
> #include "access.h"
>
> +#define QUOTED(x) #x
> +
> extern int maxage;
> extern char *statepath, *datapath, *certfile;
> extern upstype *firstups;
> @@ -197,6 +199,15 @@
> return 1;
> }
>
> + /* LISTEN <address> [<port>] */
> + if (!strcmp(arg[0], "LISTEN")) {
> + if (numargs < 3)
> + listen_add(arg[1], "3493");
> + else
> + listen_add(arg[1], arg[2]);
> + return 1;
> + }
> +
> /* everything below here uses up through arg[2] */
> if (numargs < 3)
> return 0;
>
> Added: trunk/server/stype.h
>
=============================================================================>
--- (empty file)
> +++ trunk/server/stype.h Sat Jan 13 21:22:28 2007
> @@ -0,0 +1,40 @@
> +/* stype.h - server data definitions for upsd
> +
> + Copyright (C) 2007 Arjen de Korte <arjen@de-korte.org>
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 2 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> +*/
> +
> +#ifndef STYPE_H_SEEN
> +#define STYPE_H_SEEN 1
> +
> +#include <netdb.h>
> +
> +#ifndef NI_MAXHOST
> +#define NI_MAXHOST 1025
> +#endif
> +
> +#ifndef NI_MAXSERV
> +#define NI_MAXSERV 32
> +#endif
> +
> +typedef struct {
> + char *addr;
> + char *port;
> + int sock_fd;
> + void *next;
> +} stype;
> +
> +#endif /* STYPE_H_SEEN */
>
> Modified: trunk/server/upsd.c
>
=============================================================================>
--- trunk/server/upsd.c (original)
> +++ trunk/server/upsd.c Sat Jan 13 21:22:28 2007
> @@ -36,11 +36,14 @@
> #include "user.h"
> #include "access.h"
> #include "ctype.h"
> +#include "stype.h"
> #include "ssl.h"
> #include "sstate.h"
> #include "desc.h"
> #include "neterr.h"
>
> +#define QUOTED(x) #x
> +
> /* externally-visible settings and pointers */
>
> upstype *firstups = NULL;
> @@ -56,20 +59,12 @@
>
> /* everything else */
>
> -static ctype *firstclient = NULL;
> -
> -static int listenfd, net_port = PORT;
> +static ctype *firstclient = NULL;
>
> /* default is to listen on all local interfaces */
> -#ifndef HAVE_IPV6
> -static struct in_addr listenaddr;
> -#else
> -static char *listenaddr = "0.0.0.0";
> -
> -/* AF_ */
> +static stype *firstaddr = NULL;
>
> static int opt_af = AF_UNSPEC;
> -#endif
>
> /* signal handlers */
> static struct sigaction sa;
> @@ -153,99 +148,172 @@
> ups_data_ok(ups);
> }
>
> -/* create a listening socket for tcp connections */
> -static void setuptcp(void)
> +/* add another listening address */
> +void listen_add(const char *addr, const char *port)
> {
> + stype *stmp, *last;
> +
> + stmp = last = firstaddr;
> +
> + /* find end of linked list */
> + while (stmp != NULL) {
> + last = stmp;
> + stmp = stmp->next;
> + }
> +
> + /* grab some memory and add the info */
> + stmp = xmalloc(sizeof(stype));
> + stmp->addr = xstrdup(addr);
> + stmp->port = xstrdup(port);
> + stmp->sock_fd = -1;
> + stmp->next = NULL;
> +
> + if (last == NULL)
> + firstaddr = stmp;
> + else
> + last->next = stmp;
> +
> + upsdebugx(3, "listen_add: added %s:%s", stmp->addr,
stmp->port);
> +}
> +
> +/* create a listening socket for tcp connections */
> +static void setuptcp(stype *serv)
> #ifndef HAVE_IPV6
> - struct sockaddr_in server;
> +{
> + struct hostent *host;
> + struct sockaddr_in server;
> int res, one = 1;
>
> - if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
> + if ((host = gethostbyname(serv->addr)) == (struct hostent *)NULL) {
> + struct in_addr listenaddr;
> +
> + if (!inet_aton(serv->addr, &listenaddr) || ((host =
gethostbyaddr(&listenaddr,
> + sizeof(listenaddr), AF_INET)) == (struct hostent *)NULL))
> + fatal_with_errno("gethost");
> + }
> +
> + if ((serv->sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
> fatal_with_errno("socket");
>
> - res = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
> + res = setsockopt(serv->sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)
&one,
> sizeof(one));
>
> if (res != 0)
> fatal_with_errno("setsockopt(SO_REUSEADDR)");
>
> memset(&server, '\0', sizeof(server));
> - server.sin_addr = listenaddr;
> server.sin_family = AF_INET;
> - server.sin_port = htons(net_port);
> + server.sin_port = htons(atoi(serv->port));
>
> - if (bind(listenfd, (struct sockaddr *) &server, sizeof(server)) ==
-1)
> - fatal_with_errno("Can't bind TCP port number %d",
net_port);
> + memcpy(&server.sin_addr, host->h_addr, host->h_length);
>
> - if ((res = fcntl(listenfd, F_GETFL, 0)) == -1)
> + if (bind(serv->sock_fd, (struct sockaddr *) &server,
sizeof(server)) == -1)
> + fatal_with_errno("Can't bind TCP port %s", serv->port);
> +
> + if ((res = fcntl(serv->sock_fd, F_GETFL, 0)) == -1)
> fatal_with_errno("fcntl(get)");
>
> - if (fcntl(listenfd, F_SETFL, res | O_NDELAY) == -1)
> + if (fcntl(serv->sock_fd, F_SETFL, res | O_NDELAY) == -1)
> fatal_with_errno("fcntl(set)");
>
> - if (listen(listenfd, 16))
> + if (listen(serv->sock_fd, 16))
> fatal_with_errno("listen");
>
> return;
> +}
> #else
> - struct addrinfo hints, *r, *rtmp;
> - char *service;
> - int res, one = 1;
> +{
> + struct addrinfo hints, *res, *ai;
> + struct sockaddr_storage sas;
> + socklen_t sas_len = sizeof(sas);
> + int v = 0, one = 1;
> + char saddr[NI_MAXHOST];
> + char sport[NI_MAXSERV];
> +
> + upsdebugx(3, "setuptcp: try to bind to %s port %s",
serv->addr, serv->port);
> +
> + /* memset(&hints, 0, sizeof(hints)); */
> + hints.ai_flags = AI_PASSIVE;
> + hints.ai_family = PF_UNSPEC;
> + hints.ai_socktype = SOCK_STREAM;
> + hints.ai_protocol = IPPROTO_TCP;
> + hints.ai_addrlen = 0;
> + hints.ai_addr = NULL;
> + hints.ai_canonname = NULL;
> + hints.ai_next = NULL;
> +
> + if ((v = getaddrinfo(serv->addr, serv->port, &hints,
&res))) {
> + if (v == EAI_SYSTEM)
> + fatal_with_errno("getaddrinfo");
> +
> + fatalx("getaddrinfo: %s\n", gai_strerror(v));
> + }
> +
> + for (ai = res; ai != NULL; ai = ai->ai_next) {
> + int sock_fd;
> +
> + if ((ai->ai_socktype != hints.ai_socktype) || (ai->ai_protocol !=
hints.ai_protocol))
> + continue;
> +
> + if ((sock_fd = socket(ai->ai_family, ai->ai_socktype,
ai->ai_protocol)) < 0) {
> + upsdebug_with_errno(3, "setuptcp: socket");
> + continue;
> + }
> +
> +
> + if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one,
sizeof(one)) != 0)
> + fatal_with_errno("setuptcp: setsockopt");
> +
> + if (bind(sock_fd, ai->ai_addr, ai->ai_addrlen) < 0) {
> + upsdebug_with_errno(3, "setuptcp: bind");
> + close(sock_fd);
> + continue;
> + }
> +
> + if ((v = fcntl(sock_fd, F_GETFL, 0)) == -1)
> + fatal_with_errno("setuptcp: fcntl(get)");
>
> - memset (&hints, 0, sizeof (struct addrinfo));
> - hints.ai_family = opt_af;
> - hints.ai_flags = AI_PASSIVE;
> - hints.ai_protocol = IPPROTO_TCP;
> - hints.ai_socktype = SOCK_STREAM;
> -
> - service = xmalloc (sizeof (char) * 6);
> -
> - if (snprintf (service, 6, "%hu", (unsigned short int)net_port)
< 1)
> - fatal_with_errno("snprintf");
> -
> - if (getaddrinfo (listenaddr, service, &hints, &r) != 0) {
> - free (service);
> - fatal_with_errno("getaddrinfo");
> - }
> - free (service);
> -
> - for (rtmp = r; r != NULL; r = r->ai_next) {
> - listenfd = socket(r->ai_family, r->ai_socktype,
r->ai_protocol);
> - if (listenfd < 0) {
> - if (r->ai_next == NULL)
> - fatal_with_errno("socket");
> + if (fcntl(sock_fd, F_SETFL, v | O_NDELAY) == -1)
> + fatal_with_errno("setuptcp: fcntl(set)");
> +
> + if (listen(sock_fd, 1) < 0) {
> + upsdebug_with_errno(3, "setuptcp: listen");
> + close(sock_fd);
> continue;
> }
>
> - res = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
sizeof(one));
> - if (res != 0)
> - fatal_with_errno("setsockopt(SO_REUSEADDR)");
> -
> - if (bind (listenfd, r->ai_addr, r->ai_addrlen) == -1) {
> - if (r->ai_next == NULL)
> - fatal_with_errno("Can't bind TCP port number %u",
net_port);
> - close (listenfd);
> + if (getsockname(sock_fd, (struct sockaddr *)&sas, &sas_len) <
0) {
> + upsdebug_with_errno(3, "setuptcp: getsockname");
> + close(sock_fd);
> continue;
> }
>
> - if ((res = fcntl(listenfd, F_GETFL, 0)) == -1)
> - fatal_with_errno("fcntl(get)");
> + if ((v = getnameinfo((struct sockaddr *)&sas, sas_len,
> + saddr, NI_MAXHOST, sport, NI_MAXSERV, 0))) {
> +
> + upslogx(LOG_ERR, "setuptcp: getaddrinfo: %s\n",
gai_strerror(v));
>
> - if (fcntl(listenfd, F_SETFL, res | O_NDELAY) == -1)
> - fatal_with_errno("fcntl(set)");
> + if (v == EAI_SYSTEM)
> + upsdebug_with_errno(3, "setuptcp: getaddrinfo");
>
> - if (listen(listenfd, 16) == -1) {
> - if (r->ai_next == NULL)
> - fatal_with_errno("listen");
> - close (listenfd);
> + close(sock_fd);
> continue;
> }
> +
> + serv->sock_fd = sock_fd;
> break;
> }
> - freeaddrinfo (rtmp);
> +
> + freeaddrinfo(res);
> +
> + if (serv->sock_fd < 0)
> + upslogx(LOG_WARNING, "upsd: not listening on %s:%s",
serv->addr, serv->port);
> + else
> + upslogx(LOG_INFO, "upsd: listening on %s:%s", saddr, sport);
> +
> return;
> -#endif
> }
> +#endif
>
> /* decrement the login counter for this ups */
> static void declogins(const char *upsname)
> @@ -493,19 +561,20 @@
> }
>
> /* answer incoming tcp connections */
> -static void answertcp(void)
> -{
> - int acc;
> +static void answertcp(stype *serv)
> #ifndef HAVE_IPV6
> +{
> struct sockaddr_in csock;
> #else
> +{
> struct sockaddr_storage csock;
> #endif
> + int acc;
> ctype *tmp, *last;
> socklen_t clen;
>
> clen = sizeof(csock);
> - acc = accept(listenfd, (struct sockaddr *) &csock, &clen);
> + acc = accept(serv->sock_fd, (struct sockaddr *) &csock,
&clen);
>
> if (acc < 0)
> return;
> @@ -531,16 +600,14 @@
>
> tmp = xmalloc(sizeof(ctype));
>
> -#ifndef HAVE_IPV6
> - tmp->addr = xstrdup(inet_ntoa(csock.sin_addr));
> -#else
> - tmp->addr = xstrdup(inet_ntopW(&csock));
> -#endif
> tmp->fd = acc;
> tmp->delete = 0;
> +
> #ifndef HAVE_IPV6
> + tmp->addr = xstrdup(inet_ntoa(csock.sin_addr));
> memcpy(&tmp->sock, &csock, sizeof(struct sockaddr_in));
> #else
> + tmp->addr = xstrdup(inet_ntopW(&csock));
> memcpy(&tmp->sock, &csock, sizeof(struct sockaddr_storage));
> #endif
>
> @@ -563,11 +630,7 @@
> else
> last->next = tmp;
>
> -#ifndef HAVE_IPV6
> - upslogx(LOG_INFO, "Connection from %s",
inet_ntoa(csock.sin_addr));
> -#else
> upslogx(LOG_INFO, "Connection from %s", tmp->addr);
> -#endif
> }
>
> /* read tcp messages and handle them */
> @@ -625,6 +688,24 @@
> {
> ctype *tmpcli, *tmpnext;
> upstype *ups, *unext;
> + stype *stmp, *snext;
> +
> + /* cleanup server fds */
> + stmp = firstaddr;
> +
> + while (stmp) {
> + snext = stmp->next;
> +
> + if (stmp->sock_fd != -1)
> + close(stmp->sock_fd);
> + if (stmp->addr != NULL)
> + free(stmp->addr);
> + if (stmp->port != NULL)
> + free(stmp->port);
> + free(stmp);
> +
> + stmp = snext;
> + }
>
> /* cleanup client fds */
> tmpcli = firstclient;
> @@ -681,22 +762,30 @@
> {
> fd_set rfds;
> struct timeval tv;
> - int res, maxfd;
> + int res, maxfd = -1;
> ctype *tmpcli, *tmpnext;
> upstype *utmp, *unext;
> + stype *stmp, *snext;
>
> if (reload_flag) {
> conf_reload();
> reload_flag = 0;
> }
> -
> - FD_ZERO(&rfds);
> - FD_SET(listenfd, &rfds);
>
> tv.tv_sec = 2;
> tv.tv_usec = 0;
>
> - maxfd = listenfd;
> + FD_ZERO(&rfds);
> +
> + /* scan through servers and add to FD_SET */
> + for (stmp = firstaddr; stmp != NULL; stmp = stmp->next) {
> + if (stmp->sock_fd != -1) {
> + FD_SET(stmp->sock_fd, &rfds);
> +
> + if (stmp->sock_fd > maxfd)
> + maxfd = stmp->sock_fd;
> + }
> + }
>
> /* scan through clients and add to FD_SET */
> for (tmpcli = firstclient; tmpcli != NULL; tmpcli = tmpcli->next) {
> @@ -718,11 +807,20 @@
> res = select(maxfd + 1, &rfds, NULL, NULL, &tv);
>
> if (res > 0) {
> - if (FD_ISSET(listenfd, &rfds))
> - answertcp();
> + /* scan servers for activity */
> + stmp = firstaddr;
>
> - /* scan clients for activity */
> + while (stmp) {
> + snext = stmp->next;
> +
> + if (stmp->sock_fd != -1)
> + if (FD_ISSET(stmp->sock_fd, &rfds))
> + answertcp(stmp);
>
> + stmp = snext;
> + }
> +
> + /* scan clients for activity */
> tmpcli = firstclient;
>
> while (tmpcli != NULL) {
> @@ -766,9 +864,6 @@
> printf(" -D raise debugging level\n");
> printf(" -f stay in the foreground for testing\n");
> printf(" -h display this help\n");
> - printf(" -i <address> binds to interface
<address>\n");
> - printf(" -p <port> sets network port (default: TCP port
%d)\n",
> - PORT);
> printf(" -r <dir> chroots to <dir>\n");
> printf(" -u <user> switch to <user> (if started as
root)\n");
> printf(" -V display the version of this software\n");
> @@ -834,6 +929,7 @@
> const char *user = NULL;
> char *progname, *chroot_path = NULL;
> struct passwd *new_uid = NULL;
> + stype *serv;
>
> progname = argv[0];
>
> @@ -845,34 +941,20 @@
> datapath = xstrdup(DATADIR);
>
> /* set up some things for later */
> -#ifndef HAVE_IPV6
> -
> - listenaddr.s_addr = INADDR_ANY;
> -#endif
> snprintf(pidfn, sizeof(pidfn), "%s/upsd.pid", altpidpath());
>
> printf("Network UPS Tools upsd %s\n", UPS_VERSION);
>
> -#ifndef HAVE_IPV6
> - while ((i = getopt(argc, argv, "+hp:r:i:fu:Vc:D")) != EOF) {
> -#else
> while ((i = getopt(argc, argv, "+h46p:r:i:fu:Vc:D")) != EOF) {
> -#endif
> switch (i) {
> case 'h':
> help(progname);
> break;
> case 'p':
> - net_port = atoi(optarg);
> - break;
> case 'i':
> -#ifndef HAVE_IPV6
> - if (!inet_aton(optarg, &listenaddr))
> - fatal_with_errno("Invalid IP address");
> -#else
> - listenaddr = xstrdup(optarg);
> -#endif
> - break;
> + fatalx("Specifying a listening addresses with '-i
<address>' and '-p <port>'\n"
> + "is deprecated. Use 'LISTEN <address>
[<port>]' in 'upsd.conf' instead.\n"
> + "See 'man 8 upsd.conf' for more information.");
> case 'r':
> chroot_path = optarg;
> break;
> @@ -930,8 +1012,22 @@
> if (argc != 0)
> help(progname);
>
> + /* read data from config files - upsd.conf, ups.conf, upsd.users */
> + conf_load();
> +
> setupsignals();
> - setuptcp();
> +
> + /* default behaviour if no LISTEN addres has been specified */
> + if (firstaddr == NULL) {
> + firstaddr = (stype *)xmalloc(sizeof(stype));
> + firstaddr->addr = xstrdup("0.0.0.0");
> + firstaddr->port = xstrdup("3493");
> + firstaddr->sock_fd = -1;
> + firstaddr->next = NULL;
> + }
> +
> + for (serv = firstaddr; serv != NULL; serv = serv->next)
> + setuptcp(serv);
>
> open_syslog("upsd");
>
> @@ -955,9 +1051,6 @@
> /* check statepath perms */
> check_perms(statepath);
>
> - /* read data from config files - upsd.conf, ups.conf, upsd.users */
> - conf_load();
> -
> if (num_ups == 0)
> fatalx("Fatal error: at least one UPS must be defined in
ups.conf");
>
>
> Modified: trunk/server/upsd.h
>
=============================================================================>
--- trunk/server/upsd.h (original)
> +++ trunk/server/upsd.h Sat Jan 13 21:22:28 2007
> @@ -50,6 +50,8 @@
> upstype *get_ups_ptr(const char *upsname);
> int ups_available(const upstype *ups, ctype *client);
>
> +void listen_add(const char *addr, const char *port);
> +
> void kick_login_clients(const char *upsname);
> int sendback(ctype *client, const char *fmt, ...)
> __attribute__ ((__format__ (__printf__, 2, 3)));
>
> Modified: trunk/server/upstype.h
>
=============================================================================>
--- trunk/server/upstype.h (original)
> +++ trunk/server/upstype.h Sat Jan 13 21:22:28 2007
> @@ -46,4 +46,6 @@
> void *next;
> } upstype;
>
> +extern upstype *firstups;
> +
> #endif /* UPSTYPE_H_SEEN */
>
> _______________________________________________
> nut-commits mailing list
> nut-commits@lists.alioth.debian.org
> http://lists.alioth.debian.org/mailman/listinfo/nut-commits
>