1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci   Copyright (C) 1997 Andreas Beck
3141cc406Sopenharmony_ci   Copyright (C) 2001 - 2004 Henning Meier-Geinitz
4141cc406Sopenharmony_ci   Copyright (C) 2003, 2008 Julien BLACHE <jb@jblache.org>
5141cc406Sopenharmony_ci       AF-independent + IPv6 code, standalone mode
6141cc406Sopenharmony_ci
7141cc406Sopenharmony_ci   This file is part of the SANE package.
8141cc406Sopenharmony_ci
9141cc406Sopenharmony_ci   SANE is free software; you can redistribute it and/or modify it under
10141cc406Sopenharmony_ci   the terms of the GNU General Public License as published by the Free
11141cc406Sopenharmony_ci   Software Foundation; either version 2 of the License, or (at your
12141cc406Sopenharmony_ci   option) any later version.
13141cc406Sopenharmony_ci
14141cc406Sopenharmony_ci   SANE is distributed in the hope that it will be useful, but WITHOUT
15141cc406Sopenharmony_ci   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16141cc406Sopenharmony_ci   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17141cc406Sopenharmony_ci   for more details.
18141cc406Sopenharmony_ci
19141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
20141cc406Sopenharmony_ci   along with sane; see the file COPYING.
21141cc406Sopenharmony_ci   If not, see <https://www.gnu.org/licenses/>.
22141cc406Sopenharmony_ci
23141cc406Sopenharmony_ci   The SANE network daemon.  This is the counterpart to the NET
24141cc406Sopenharmony_ci   backend.
25141cc406Sopenharmony_ci*/
26141cc406Sopenharmony_ci
27141cc406Sopenharmony_ci#ifdef _AIX
28141cc406Sopenharmony_ci# include "../include/lalloca.h"		/* MUST come first for AIX! */
29141cc406Sopenharmony_ci#endif
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_ci#include "../include/sane/config.h"
32141cc406Sopenharmony_ci#include "../include/lalloca.h"
33141cc406Sopenharmony_ci#include <sys/types.h>
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci#if defined(HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO)
36141cc406Sopenharmony_ci# define SANED_USES_AF_INDEP
37141cc406Sopenharmony_ci# ifdef HAS_SS_FAMILY
38141cc406Sopenharmony_ci#  define SS_FAMILY(ss) ss.ss_family
39141cc406Sopenharmony_ci# elif defined(HAS___SS_FAMILY)
40141cc406Sopenharmony_ci#  define SS_FAMILY(ss) ss.__ss_family
41141cc406Sopenharmony_ci# else /* fallback to the old, IPv4-only code */
42141cc406Sopenharmony_ci#  undef SANED_USES_AF_INDEP
43141cc406Sopenharmony_ci#  undef ENABLE_IPV6
44141cc406Sopenharmony_ci# endif
45141cc406Sopenharmony_ci#else
46141cc406Sopenharmony_ci# undef ENABLE_IPV6
47141cc406Sopenharmony_ci#endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */
48141cc406Sopenharmony_ci
49141cc406Sopenharmony_ci#include <assert.h>
50141cc406Sopenharmony_ci#include <errno.h>
51141cc406Sopenharmony_ci#include <fcntl.h>
52141cc406Sopenharmony_ci#include <netdb.h>
53141cc406Sopenharmony_ci#include <signal.h>
54141cc406Sopenharmony_ci#include <stdio.h>
55141cc406Sopenharmony_ci#include <stdlib.h>
56141cc406Sopenharmony_ci#include <string.h>
57141cc406Sopenharmony_ci#include <syslog.h>
58141cc406Sopenharmony_ci#include <time.h>
59141cc406Sopenharmony_ci#include <unistd.h>
60141cc406Sopenharmony_ci#include <limits.h>
61141cc406Sopenharmony_ci#ifdef HAVE_LIBC_H
62141cc406Sopenharmony_ci# include <libc.h>		/* NeXTStep/OpenStep */
63141cc406Sopenharmony_ci#endif
64141cc406Sopenharmony_ci
65141cc406Sopenharmony_ci#ifdef HAVE_SYS_SELECT_H
66141cc406Sopenharmony_ci# include <sys/select.h>
67141cc406Sopenharmony_ci#endif
68141cc406Sopenharmony_ci
69141cc406Sopenharmony_ci#include <netinet/in.h>
70141cc406Sopenharmony_ci
71141cc406Sopenharmony_ci#include <stdarg.h>
72141cc406Sopenharmony_ci
73141cc406Sopenharmony_ci#include <sys/param.h>
74141cc406Sopenharmony_ci#include <sys/socket.h>
75141cc406Sopenharmony_ci
76141cc406Sopenharmony_ci#include <sys/time.h>
77141cc406Sopenharmony_ci#include <sys/types.h>
78141cc406Sopenharmony_ci#include <arpa/inet.h>
79141cc406Sopenharmony_ci
80141cc406Sopenharmony_ci#include <sys/wait.h>
81141cc406Sopenharmony_ci
82141cc406Sopenharmony_ci#include <pwd.h>
83141cc406Sopenharmony_ci#include <grp.h>
84141cc406Sopenharmony_ci
85141cc406Sopenharmony_ci#include "lgetopt.h"
86141cc406Sopenharmony_ci
87141cc406Sopenharmony_ci#if defined(HAVE_POLL_H) && defined(HAVE_POLL)
88141cc406Sopenharmony_ci# include <poll.h>
89141cc406Sopenharmony_ci#else
90141cc406Sopenharmony_ci/*
91141cc406Sopenharmony_ci * This replacement poll() using select() is only designed to cover
92141cc406Sopenharmony_ci * our needs in run_standalone(). It should probably be extended...
93141cc406Sopenharmony_ci */
94141cc406Sopenharmony_cistruct pollfd
95141cc406Sopenharmony_ci{
96141cc406Sopenharmony_ci  int fd;
97141cc406Sopenharmony_ci  short events;
98141cc406Sopenharmony_ci  short revents;
99141cc406Sopenharmony_ci};
100141cc406Sopenharmony_ci
101141cc406Sopenharmony_ci#define POLLIN 0x0001
102141cc406Sopenharmony_ci#define POLLERR 0x0002
103141cc406Sopenharmony_ci
104141cc406Sopenharmony_ciint
105141cc406Sopenharmony_cipoll (struct pollfd *ufds, unsigned int nfds, int timeout);
106141cc406Sopenharmony_ci
107141cc406Sopenharmony_ciint
108141cc406Sopenharmony_cipoll (struct pollfd *ufds, unsigned int nfds, int timeout)
109141cc406Sopenharmony_ci{
110141cc406Sopenharmony_ci  struct pollfd *fdp;
111141cc406Sopenharmony_ci
112141cc406Sopenharmony_ci  fd_set rfds;
113141cc406Sopenharmony_ci  fd_set efds;
114141cc406Sopenharmony_ci  struct timeval tv;
115141cc406Sopenharmony_ci  int maxfd = 0;
116141cc406Sopenharmony_ci  unsigned int i;
117141cc406Sopenharmony_ci  int ret;
118141cc406Sopenharmony_ci
119141cc406Sopenharmony_ci  tv.tv_sec = timeout / 1000;
120141cc406Sopenharmony_ci  tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
121141cc406Sopenharmony_ci
122141cc406Sopenharmony_ci  FD_ZERO (&rfds);
123141cc406Sopenharmony_ci  FD_ZERO (&efds);
124141cc406Sopenharmony_ci
125141cc406Sopenharmony_ci  for (i = 0, fdp = ufds; i < nfds; i++, fdp++)
126141cc406Sopenharmony_ci    {
127141cc406Sopenharmony_ci      fdp->revents = 0;
128141cc406Sopenharmony_ci
129141cc406Sopenharmony_ci      if (fdp->events & POLLIN)
130141cc406Sopenharmony_ci	FD_SET (fdp->fd, &rfds);
131141cc406Sopenharmony_ci
132141cc406Sopenharmony_ci      FD_SET (fdp->fd, &efds);
133141cc406Sopenharmony_ci
134141cc406Sopenharmony_ci      maxfd = (fdp->fd > maxfd) ? fdp->fd : maxfd;
135141cc406Sopenharmony_ci    }
136141cc406Sopenharmony_ci
137141cc406Sopenharmony_ci  maxfd++;
138141cc406Sopenharmony_ci
139141cc406Sopenharmony_ci  ret = select (maxfd, &rfds, NULL, &efds, &tv);
140141cc406Sopenharmony_ci
141141cc406Sopenharmony_ci  if (ret < 0)
142141cc406Sopenharmony_ci    return ret;
143141cc406Sopenharmony_ci
144141cc406Sopenharmony_ci  for (i = 0, fdp = ufds; i < nfds; i++, fdp++)
145141cc406Sopenharmony_ci    {
146141cc406Sopenharmony_ci      if (fdp->events & POLLIN)
147141cc406Sopenharmony_ci	if (FD_ISSET (fdp->fd, &rfds))
148141cc406Sopenharmony_ci	  fdp->revents |= POLLIN;
149141cc406Sopenharmony_ci
150141cc406Sopenharmony_ci      if (FD_ISSET (fdp->fd, &efds))
151141cc406Sopenharmony_ci	fdp->revents |= POLLERR;
152141cc406Sopenharmony_ci    }
153141cc406Sopenharmony_ci
154141cc406Sopenharmony_ci  return ret;
155141cc406Sopenharmony_ci}
156141cc406Sopenharmony_ci#endif /* HAVE_SYS_POLL_H && HAVE_POLL */
157141cc406Sopenharmony_ci
158141cc406Sopenharmony_ci#if WITH_AVAHI
159141cc406Sopenharmony_ci# include <avahi-client/client.h>
160141cc406Sopenharmony_ci# include <avahi-client/publish.h>
161141cc406Sopenharmony_ci
162141cc406Sopenharmony_ci# include <avahi-common/alternative.h>
163141cc406Sopenharmony_ci# include <avahi-common/simple-watch.h>
164141cc406Sopenharmony_ci# include <avahi-common/malloc.h>
165141cc406Sopenharmony_ci# include <avahi-common/error.h>
166141cc406Sopenharmony_ci
167141cc406Sopenharmony_ci# define SANED_SERVICE_DNS "_sane-port._tcp"
168141cc406Sopenharmony_ci# define SANED_NAME "saned"
169141cc406Sopenharmony_ci
170141cc406Sopenharmony_cipid_t avahi_pid = -1;
171141cc406Sopenharmony_ci
172141cc406Sopenharmony_cichar *avahi_svc_name;
173141cc406Sopenharmony_ci
174141cc406Sopenharmony_cistatic AvahiClient *avahi_client = NULL;
175141cc406Sopenharmony_cistatic AvahiSimplePoll *avahi_poll = NULL;
176141cc406Sopenharmony_cistatic AvahiEntryGroup *avahi_group = NULL;
177141cc406Sopenharmony_ci#endif /* WITH_AVAHI */
178141cc406Sopenharmony_ci
179141cc406Sopenharmony_ci#ifdef HAVE_SYSTEMD
180141cc406Sopenharmony_ci#include <systemd/sd-daemon.h>
181141cc406Sopenharmony_ci#endif
182141cc406Sopenharmony_ci
183141cc406Sopenharmony_ci
184141cc406Sopenharmony_ci#include "../include/sane/sane.h"
185141cc406Sopenharmony_ci#include "../include/sane/sanei.h"
186141cc406Sopenharmony_ci#include "../include/sane/sanei_net.h"
187141cc406Sopenharmony_ci#include "../include/sane/sanei_codec_bin.h"
188141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
189141cc406Sopenharmony_ci
190141cc406Sopenharmony_ci#include "../include/sane/sanei_auth.h"
191141cc406Sopenharmony_ci
192141cc406Sopenharmony_ci#ifndef EXIT_SUCCESS
193141cc406Sopenharmony_ci# define EXIT_SUCCESS   0
194141cc406Sopenharmony_ci#endif
195141cc406Sopenharmony_ci
196141cc406Sopenharmony_ci#ifndef IN_LOOPBACK
197141cc406Sopenharmony_ci# define IN_LOOPBACK(addr) (addr == 0x7f000001L)
198141cc406Sopenharmony_ci#endif
199141cc406Sopenharmony_ci
200141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
201141cc406Sopenharmony_ci# ifndef IN6_IS_ADDR_LOOPBACK
202141cc406Sopenharmony_ci# define IN6_IS_ADDR_LOOPBACK(a) \
203141cc406Sopenharmony_ci        (((const uint32_t *) (a))[0] == 0                                   \
204141cc406Sopenharmony_ci         && ((const uint32_t *) (a))[1] == 0                                \
205141cc406Sopenharmony_ci         && ((const uint32_t *) (a))[2] == 0                                \
206141cc406Sopenharmony_ci         && ((const uint32_t *) (a))[3] == htonl (1))
207141cc406Sopenharmony_ci# endif
208141cc406Sopenharmony_ci# ifndef IN6_IS_ADDR_V4MAPPED
209141cc406Sopenharmony_ci# define IN6_IS_ADDR_V4MAPPED(a) \
210141cc406Sopenharmony_ci((((const uint32_t *) (a))[0] == 0)                                 \
211141cc406Sopenharmony_ci && (((const uint32_t *) (a))[1] == 0)                              \
212141cc406Sopenharmony_ci && (((const uint32_t *) (a))[2] == htonl (0xffff)))
213141cc406Sopenharmony_ci# endif
214141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
215141cc406Sopenharmony_ci
216141cc406Sopenharmony_ci#ifndef MAXHOSTNAMELEN
217141cc406Sopenharmony_ci# define MAXHOSTNAMELEN 120
218141cc406Sopenharmony_ci#endif
219141cc406Sopenharmony_ci
220141cc406Sopenharmony_ci#ifndef PATH_MAX
221141cc406Sopenharmony_ci# define PATH_MAX 1024
222141cc406Sopenharmony_ci#endif
223141cc406Sopenharmony_ci
224141cc406Sopenharmony_cistruct saned_child {
225141cc406Sopenharmony_ci  pid_t pid;
226141cc406Sopenharmony_ci  struct saned_child *next;
227141cc406Sopenharmony_ci};
228141cc406Sopenharmony_cistruct saned_child *children;
229141cc406Sopenharmony_ciint numchildren;
230141cc406Sopenharmony_ci
231141cc406Sopenharmony_ci#define SANED_CONFIG_FILE "saned.conf"
232141cc406Sopenharmony_ci#define SANED_PID_FILE    "/var/run/saned.pid"
233141cc406Sopenharmony_ci
234141cc406Sopenharmony_ci#define SANED_SERVICE_NAME   "sane-port"
235141cc406Sopenharmony_ci#define SANED_SERVICE_PORT   6566
236141cc406Sopenharmony_ci#define SANED_SERVICE_PORT_S "6566"
237141cc406Sopenharmony_ci
238141cc406Sopenharmony_citypedef struct
239141cc406Sopenharmony_ci{
240141cc406Sopenharmony_ci  u_int inuse:1;		/* is this handle in use? */
241141cc406Sopenharmony_ci  u_int scanning:1;		/* are we scanning? */
242141cc406Sopenharmony_ci  u_int docancel:1;		/* cancel the current scan */
243141cc406Sopenharmony_ci  SANE_Handle handle;		/* backends handle */
244141cc406Sopenharmony_ci}
245141cc406Sopenharmony_ciHandle;
246141cc406Sopenharmony_ci
247141cc406Sopenharmony_cistatic SANE_Net_Procedure_Number current_request;
248141cc406Sopenharmony_cistatic const char *prog_name;
249141cc406Sopenharmony_cistatic int can_authorize;
250141cc406Sopenharmony_cistatic Wire wire;
251141cc406Sopenharmony_cistatic int num_handles;
252141cc406Sopenharmony_cistatic int debug;
253141cc406Sopenharmony_cistatic int run_mode;
254141cc406Sopenharmony_cistatic int run_foreground;
255141cc406Sopenharmony_cistatic int run_once;
256141cc406Sopenharmony_cistatic int data_connect_timeout = 4000;
257141cc406Sopenharmony_cistatic Handle *handle;
258141cc406Sopenharmony_cistatic char *bind_addr;
259141cc406Sopenharmony_cistatic short bind_port = -1;
260141cc406Sopenharmony_cistatic union
261141cc406Sopenharmony_ci{
262141cc406Sopenharmony_ci  int w;
263141cc406Sopenharmony_ci  u_char ch;
264141cc406Sopenharmony_ci}
265141cc406Sopenharmony_cibyte_order;
266141cc406Sopenharmony_ci
267141cc406Sopenharmony_ci/* The default-user name.  This is not used to imply any rights.  All
268141cc406Sopenharmony_ci   it does is save a remote user some work by reducing the amount of
269141cc406Sopenharmony_ci   text s/he has to type when authentication is requested.  */
270141cc406Sopenharmony_cistatic const char *default_username = "saned-user";
271141cc406Sopenharmony_cistatic char *remote_ip;
272141cc406Sopenharmony_ci
273141cc406Sopenharmony_ci/* data port range */
274141cc406Sopenharmony_cistatic in_port_t data_port_lo;
275141cc406Sopenharmony_cistatic in_port_t data_port_hi;
276141cc406Sopenharmony_ci
277141cc406Sopenharmony_ci#ifdef SANED_USES_AF_INDEP
278141cc406Sopenharmony_cistatic union {
279141cc406Sopenharmony_ci  struct sockaddr_storage ss;
280141cc406Sopenharmony_ci  struct sockaddr sa;
281141cc406Sopenharmony_ci  struct sockaddr_in sin;
282141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
283141cc406Sopenharmony_ci  struct sockaddr_in6 sin6;
284141cc406Sopenharmony_ci#endif
285141cc406Sopenharmony_ci} remote_address;
286141cc406Sopenharmony_cistatic int remote_address_len;
287141cc406Sopenharmony_ci#else
288141cc406Sopenharmony_cistatic struct in_addr remote_address;
289141cc406Sopenharmony_ci#endif /* SANED_USES_AF_INDEP */
290141cc406Sopenharmony_ci
291141cc406Sopenharmony_ci#ifndef _PATH_HEQUIV
292141cc406Sopenharmony_ci# define _PATH_HEQUIV   "/etc/hosts.equiv"
293141cc406Sopenharmony_ci#endif
294141cc406Sopenharmony_ci
295141cc406Sopenharmony_cistatic const char *config_file_names[] = {
296141cc406Sopenharmony_ci  _PATH_HEQUIV, SANED_CONFIG_FILE
297141cc406Sopenharmony_ci};
298141cc406Sopenharmony_ci
299141cc406Sopenharmony_cistatic SANE_Bool log_to_syslog = SANE_TRUE;
300141cc406Sopenharmony_ci
301141cc406Sopenharmony_ci/* forward declarations: */
302141cc406Sopenharmony_cistatic int process_request (Wire * w);
303141cc406Sopenharmony_ci
304141cc406Sopenharmony_ci#define SANED_RUN_INETD  0
305141cc406Sopenharmony_ci#define SANED_RUN_ALONE  1
306141cc406Sopenharmony_ci
307141cc406Sopenharmony_ci#define DBG_ERR  1
308141cc406Sopenharmony_ci#define DBG_WARN 2
309141cc406Sopenharmony_ci#define DBG_MSG  3
310141cc406Sopenharmony_ci#define DBG_INFO 4
311141cc406Sopenharmony_ci#define DBG_DBG  5
312141cc406Sopenharmony_ci
313141cc406Sopenharmony_ci#define DBG	saned_debug_call
314141cc406Sopenharmony_ci
315141cc406Sopenharmony_cistatic void
316141cc406Sopenharmony_cisaned_debug_call (int level, const char *fmt, ...)
317141cc406Sopenharmony_ci{
318141cc406Sopenharmony_ci#ifndef NDEBUG
319141cc406Sopenharmony_ci  va_list ap;
320141cc406Sopenharmony_ci  va_start (ap, fmt);
321141cc406Sopenharmony_ci  if (debug >= level)
322141cc406Sopenharmony_ci    {
323141cc406Sopenharmony_ci      if (log_to_syslog)
324141cc406Sopenharmony_ci	{
325141cc406Sopenharmony_ci	  /* print to syslog */
326141cc406Sopenharmony_ci	  vsyslog (LOG_DEBUG, fmt, ap);
327141cc406Sopenharmony_ci	}
328141cc406Sopenharmony_ci      else
329141cc406Sopenharmony_ci	{
330141cc406Sopenharmony_ci	  /* print to stderr */
331141cc406Sopenharmony_ci	  fprintf (stderr, "[saned] ");
332141cc406Sopenharmony_ci	  vfprintf (stderr, fmt, ap);
333141cc406Sopenharmony_ci	}
334141cc406Sopenharmony_ci    }
335141cc406Sopenharmony_ci  va_end (ap);
336141cc406Sopenharmony_ci#endif
337141cc406Sopenharmony_ci}
338141cc406Sopenharmony_ci
339141cc406Sopenharmony_ci
340141cc406Sopenharmony_cistatic void
341141cc406Sopenharmony_cireset_watchdog (void)
342141cc406Sopenharmony_ci{
343141cc406Sopenharmony_ci  if (!debug)
344141cc406Sopenharmony_ci    alarm (3600);
345141cc406Sopenharmony_ci}
346141cc406Sopenharmony_ci
347141cc406Sopenharmony_cistatic void
348141cc406Sopenharmony_ciauth_callback (SANE_String_Const res,
349141cc406Sopenharmony_ci	       SANE_Char *username,
350141cc406Sopenharmony_ci	       SANE_Char *password)
351141cc406Sopenharmony_ci{
352141cc406Sopenharmony_ci  SANE_Net_Procedure_Number procnum;
353141cc406Sopenharmony_ci  SANE_Authorization_Req req;
354141cc406Sopenharmony_ci  SANE_Word word, ack = 0;
355141cc406Sopenharmony_ci
356141cc406Sopenharmony_ci  memset (username, 0, SANE_MAX_USERNAME_LEN);
357141cc406Sopenharmony_ci  memset (password, 0, SANE_MAX_PASSWORD_LEN);
358141cc406Sopenharmony_ci
359141cc406Sopenharmony_ci  if (!can_authorize)
360141cc406Sopenharmony_ci    {
361141cc406Sopenharmony_ci      DBG (DBG_WARN,
362141cc406Sopenharmony_ci	   "auth_callback: called during non-authorizable RPC (resource=%s)\n",
363141cc406Sopenharmony_ci	   res);
364141cc406Sopenharmony_ci      return;
365141cc406Sopenharmony_ci    }
366141cc406Sopenharmony_ci
367141cc406Sopenharmony_ci  if (wire.status)
368141cc406Sopenharmony_ci    {
369141cc406Sopenharmony_ci      DBG(DBG_ERR, "auth_callback: bad status %d\n", wire.status);
370141cc406Sopenharmony_ci      return;
371141cc406Sopenharmony_ci    }
372141cc406Sopenharmony_ci
373141cc406Sopenharmony_ci  switch (current_request)
374141cc406Sopenharmony_ci    {
375141cc406Sopenharmony_ci    case SANE_NET_OPEN:
376141cc406Sopenharmony_ci      {
377141cc406Sopenharmony_ci	SANE_Open_Reply reply;
378141cc406Sopenharmony_ci
379141cc406Sopenharmony_ci	memset (&reply, 0, sizeof (reply));
380141cc406Sopenharmony_ci	reply.resource_to_authorize = (char *) res;
381141cc406Sopenharmony_ci	sanei_w_reply (&wire, (WireCodecFunc) sanei_w_open_reply, &reply);
382141cc406Sopenharmony_ci      }
383141cc406Sopenharmony_ci      break;
384141cc406Sopenharmony_ci
385141cc406Sopenharmony_ci    case SANE_NET_CONTROL_OPTION:
386141cc406Sopenharmony_ci      {
387141cc406Sopenharmony_ci	SANE_Control_Option_Reply reply;
388141cc406Sopenharmony_ci
389141cc406Sopenharmony_ci	memset (&reply, 0, sizeof (reply));
390141cc406Sopenharmony_ci	reply.resource_to_authorize = (char *) res;
391141cc406Sopenharmony_ci	sanei_w_reply (&wire,
392141cc406Sopenharmony_ci		       (WireCodecFunc) sanei_w_control_option_reply, &reply);
393141cc406Sopenharmony_ci      }
394141cc406Sopenharmony_ci      break;
395141cc406Sopenharmony_ci
396141cc406Sopenharmony_ci    case SANE_NET_START:
397141cc406Sopenharmony_ci      {
398141cc406Sopenharmony_ci	SANE_Start_Reply reply;
399141cc406Sopenharmony_ci
400141cc406Sopenharmony_ci	memset (&reply, 0, sizeof (reply));
401141cc406Sopenharmony_ci	reply.resource_to_authorize = (char *) res;
402141cc406Sopenharmony_ci	sanei_w_reply (&wire, (WireCodecFunc) sanei_w_start_reply, &reply);
403141cc406Sopenharmony_ci      }
404141cc406Sopenharmony_ci      break;
405141cc406Sopenharmony_ci
406141cc406Sopenharmony_ci    default:
407141cc406Sopenharmony_ci      DBG (DBG_WARN,
408141cc406Sopenharmony_ci	   "auth_callback: called for unexpected request %d (resource=%s)\n",
409141cc406Sopenharmony_ci	   current_request, res);
410141cc406Sopenharmony_ci      break;
411141cc406Sopenharmony_ci    }
412141cc406Sopenharmony_ci
413141cc406Sopenharmony_ci  if (wire.status)
414141cc406Sopenharmony_ci    {
415141cc406Sopenharmony_ci      DBG(DBG_ERR, "auth_callback: bad status %d\n", wire.status);
416141cc406Sopenharmony_ci      return;
417141cc406Sopenharmony_ci    }
418141cc406Sopenharmony_ci
419141cc406Sopenharmony_ci  reset_watchdog ();
420141cc406Sopenharmony_ci
421141cc406Sopenharmony_ci  sanei_w_set_dir (&wire, WIRE_DECODE);
422141cc406Sopenharmony_ci  sanei_w_word (&wire, &word);
423141cc406Sopenharmony_ci
424141cc406Sopenharmony_ci  if (wire.status)
425141cc406Sopenharmony_ci    {
426141cc406Sopenharmony_ci      DBG(DBG_ERR, "auth_callback: bad status %d\n", wire.status);
427141cc406Sopenharmony_ci      return;
428141cc406Sopenharmony_ci    }
429141cc406Sopenharmony_ci
430141cc406Sopenharmony_ci  procnum = word;
431141cc406Sopenharmony_ci  if (procnum != SANE_NET_AUTHORIZE)
432141cc406Sopenharmony_ci    {
433141cc406Sopenharmony_ci      DBG (DBG_WARN,
434141cc406Sopenharmony_ci	   "auth_callback: bad procedure number %d "
435141cc406Sopenharmony_ci	   "(expected: %d, resource=%s)\n", procnum, SANE_NET_AUTHORIZE,
436141cc406Sopenharmony_ci	   res);
437141cc406Sopenharmony_ci      return;
438141cc406Sopenharmony_ci    }
439141cc406Sopenharmony_ci
440141cc406Sopenharmony_ci  sanei_w_authorization_req (&wire, &req);
441141cc406Sopenharmony_ci  if (wire.status)
442141cc406Sopenharmony_ci    {
443141cc406Sopenharmony_ci      DBG(DBG_ERR, "auth_callback: bad status %d\n", wire.status);
444141cc406Sopenharmony_ci      return;
445141cc406Sopenharmony_ci    }
446141cc406Sopenharmony_ci
447141cc406Sopenharmony_ci  if (req.username)
448141cc406Sopenharmony_ci    strcpy (username, req.username);
449141cc406Sopenharmony_ci  if (req.password)
450141cc406Sopenharmony_ci    strcpy (password, req.password);
451141cc406Sopenharmony_ci  if (!req.resource || strcmp (req.resource, res) != 0)
452141cc406Sopenharmony_ci    {
453141cc406Sopenharmony_ci      DBG (DBG_MSG,
454141cc406Sopenharmony_ci	   "auth_callback: got auth for resource %s (expected resource=%s)\n",
455141cc406Sopenharmony_ci	   res, req.resource);
456141cc406Sopenharmony_ci    }
457141cc406Sopenharmony_ci  sanei_w_free (&wire, (WireCodecFunc) sanei_w_authorization_req, &req);
458141cc406Sopenharmony_ci  sanei_w_reply (&wire, (WireCodecFunc) sanei_w_word, &ack);
459141cc406Sopenharmony_ci}
460141cc406Sopenharmony_ci
461141cc406Sopenharmony_cistatic void
462141cc406Sopenharmony_ciquit (int signum)
463141cc406Sopenharmony_ci{
464141cc406Sopenharmony_ci  static int running = 0;
465141cc406Sopenharmony_ci  int i;
466141cc406Sopenharmony_ci
467141cc406Sopenharmony_ci  if (signum)
468141cc406Sopenharmony_ci    DBG (DBG_ERR, "quit: received signal %d\n", signum);
469141cc406Sopenharmony_ci
470141cc406Sopenharmony_ci  if (running)
471141cc406Sopenharmony_ci    {
472141cc406Sopenharmony_ci      DBG (DBG_ERR, "quit: already active, returning\n");
473141cc406Sopenharmony_ci      return;
474141cc406Sopenharmony_ci    }
475141cc406Sopenharmony_ci  running = 1;
476141cc406Sopenharmony_ci
477141cc406Sopenharmony_ci  for (i = 0; i < num_handles; ++i)
478141cc406Sopenharmony_ci    if (handle[i].inuse)
479141cc406Sopenharmony_ci      sane_close (handle[i].handle);
480141cc406Sopenharmony_ci
481141cc406Sopenharmony_ci  sane_exit ();
482141cc406Sopenharmony_ci  sanei_w_exit (&wire);
483141cc406Sopenharmony_ci  if (handle)
484141cc406Sopenharmony_ci    free (handle);
485141cc406Sopenharmony_ci  DBG (DBG_WARN, "quit: exiting\n");
486141cc406Sopenharmony_ci  if (log_to_syslog)
487141cc406Sopenharmony_ci    closelog ();
488141cc406Sopenharmony_ci  exit (EXIT_SUCCESS);		/* This is a nowait-daemon. */
489141cc406Sopenharmony_ci}
490141cc406Sopenharmony_ci
491141cc406Sopenharmony_cistatic SANE_Word
492141cc406Sopenharmony_ciget_free_handle (void)
493141cc406Sopenharmony_ci{
494141cc406Sopenharmony_ci# define ALLOC_INCREMENT        16
495141cc406Sopenharmony_ci  static int h, last_handle_checked = -1;
496141cc406Sopenharmony_ci
497141cc406Sopenharmony_ci  if (num_handles > 0)
498141cc406Sopenharmony_ci    {
499141cc406Sopenharmony_ci      h = last_handle_checked + 1;
500141cc406Sopenharmony_ci      do
501141cc406Sopenharmony_ci	{
502141cc406Sopenharmony_ci	  if (h >= num_handles)
503141cc406Sopenharmony_ci	    h = 0;
504141cc406Sopenharmony_ci	  if (!handle[h].inuse)
505141cc406Sopenharmony_ci	    {
506141cc406Sopenharmony_ci	      last_handle_checked = h;
507141cc406Sopenharmony_ci	      memset (handle + h, 0, sizeof (handle[0]));
508141cc406Sopenharmony_ci	      handle[h].inuse = 1;
509141cc406Sopenharmony_ci	      return h;
510141cc406Sopenharmony_ci	    }
511141cc406Sopenharmony_ci	  ++h;
512141cc406Sopenharmony_ci	}
513141cc406Sopenharmony_ci      while (h != last_handle_checked);
514141cc406Sopenharmony_ci    }
515141cc406Sopenharmony_ci
516141cc406Sopenharmony_ci  /* we're out of handles---alloc some more: */
517141cc406Sopenharmony_ci  last_handle_checked = num_handles - 1;
518141cc406Sopenharmony_ci  num_handles += ALLOC_INCREMENT;
519141cc406Sopenharmony_ci  if (handle)
520141cc406Sopenharmony_ci    handle = realloc (handle, num_handles * sizeof (handle[0]));
521141cc406Sopenharmony_ci  else
522141cc406Sopenharmony_ci    handle = malloc (num_handles * sizeof (handle[0]));
523141cc406Sopenharmony_ci  if (!handle)
524141cc406Sopenharmony_ci    return -1;
525141cc406Sopenharmony_ci  memset (handle + last_handle_checked + 1, 0,
526141cc406Sopenharmony_ci	  ALLOC_INCREMENT * sizeof (handle[0]));
527141cc406Sopenharmony_ci  return get_free_handle ();
528141cc406Sopenharmony_ci# undef ALLOC_INCREMENT
529141cc406Sopenharmony_ci}
530141cc406Sopenharmony_ci
531141cc406Sopenharmony_cistatic void
532141cc406Sopenharmony_ciclose_handle (int h)
533141cc406Sopenharmony_ci{
534141cc406Sopenharmony_ci  if (h >= 0 && handle[h].inuse)
535141cc406Sopenharmony_ci    {
536141cc406Sopenharmony_ci      sane_close (handle[h].handle);
537141cc406Sopenharmony_ci      handle[h].inuse = 0;
538141cc406Sopenharmony_ci    }
539141cc406Sopenharmony_ci}
540141cc406Sopenharmony_ci
541141cc406Sopenharmony_cistatic SANE_Word
542141cc406Sopenharmony_cidecode_handle (Wire * w, const char *op)
543141cc406Sopenharmony_ci{
544141cc406Sopenharmony_ci  SANE_Word h;
545141cc406Sopenharmony_ci
546141cc406Sopenharmony_ci  sanei_w_word (w, &h);
547141cc406Sopenharmony_ci  if (w->status || (unsigned) h >= (unsigned) num_handles || !handle[h].inuse)
548141cc406Sopenharmony_ci    {
549141cc406Sopenharmony_ci      DBG (DBG_ERR,
550141cc406Sopenharmony_ci	   "decode_handle: %s: error while decoding handle argument "
551141cc406Sopenharmony_ci	   "(h=%d, %s)\n", op, h, strerror (w->status));
552141cc406Sopenharmony_ci      return -1;
553141cc406Sopenharmony_ci    }
554141cc406Sopenharmony_ci  return h;
555141cc406Sopenharmony_ci}
556141cc406Sopenharmony_ci
557141cc406Sopenharmony_ci
558141cc406Sopenharmony_ci
559141cc406Sopenharmony_ci/* Convert a number of bits to an 8-bit bitmask */
560141cc406Sopenharmony_cistatic unsigned int cidrtomask[9] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0,
561141cc406Sopenharmony_ci				      0xF8, 0xFC, 0xFE, 0xFF };
562141cc406Sopenharmony_ci
563141cc406Sopenharmony_ci#ifdef SANED_USES_AF_INDEP
564141cc406Sopenharmony_cistatic SANE_Bool
565141cc406Sopenharmony_cicheck_v4_in_range (struct sockaddr_in *sin, char *base_ip, char *netmask)
566141cc406Sopenharmony_ci{
567141cc406Sopenharmony_ci  int cidr;
568141cc406Sopenharmony_ci  int i, err;
569141cc406Sopenharmony_ci  char *end;
570141cc406Sopenharmony_ci  uint32_t mask;
571141cc406Sopenharmony_ci  struct sockaddr_in *base;
572141cc406Sopenharmony_ci  struct addrinfo hints;
573141cc406Sopenharmony_ci  struct addrinfo *res;
574141cc406Sopenharmony_ci  SANE_Bool ret = SANE_FALSE;
575141cc406Sopenharmony_ci
576141cc406Sopenharmony_ci  cidr = -1;
577141cc406Sopenharmony_ci  cidr = strtol (netmask, &end, 10);
578141cc406Sopenharmony_ci
579141cc406Sopenharmony_ci  /* Sanity check on the cidr value */
580141cc406Sopenharmony_ci  if ((cidr < 0) || (cidr > 32) || (end == netmask))
581141cc406Sopenharmony_ci    {
582141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_v4_in_range: invalid CIDR value (%s) !\n", netmask);
583141cc406Sopenharmony_ci      return SANE_FALSE;
584141cc406Sopenharmony_ci    }
585141cc406Sopenharmony_ci
586141cc406Sopenharmony_ci  mask = 0;
587141cc406Sopenharmony_ci  cidr -= 8;
588141cc406Sopenharmony_ci
589141cc406Sopenharmony_ci  /* Build a bitmask out of the CIDR value */
590141cc406Sopenharmony_ci  for (i = 3; cidr >= 0; i--)
591141cc406Sopenharmony_ci    {
592141cc406Sopenharmony_ci      mask |= (0xff << (8 * i));
593141cc406Sopenharmony_ci      cidr -= 8;
594141cc406Sopenharmony_ci    }
595141cc406Sopenharmony_ci
596141cc406Sopenharmony_ci  if (cidr < 0)
597141cc406Sopenharmony_ci    mask |= (cidrtomask[cidr + 8] << (8 * i));
598141cc406Sopenharmony_ci
599141cc406Sopenharmony_ci  mask = htonl (mask);
600141cc406Sopenharmony_ci
601141cc406Sopenharmony_ci  /* get a sockaddr_in representing the base IP address */
602141cc406Sopenharmony_ci  memset (&hints, 0, sizeof (struct addrinfo));
603141cc406Sopenharmony_ci  hints.ai_flags = AI_NUMERICHOST;
604141cc406Sopenharmony_ci  hints.ai_family = PF_INET;
605141cc406Sopenharmony_ci
606141cc406Sopenharmony_ci  err = getaddrinfo (base_ip, NULL, &hints, &res);
607141cc406Sopenharmony_ci  if (err)
608141cc406Sopenharmony_ci    {
609141cc406Sopenharmony_ci      DBG (DBG_DBG, "check_v4_in_range: getaddrinfo() failed: %s\n", gai_strerror (err));
610141cc406Sopenharmony_ci      return SANE_FALSE;
611141cc406Sopenharmony_ci    }
612141cc406Sopenharmony_ci
613141cc406Sopenharmony_ci  base = (struct sockaddr_in *) res->ai_addr;
614141cc406Sopenharmony_ci
615141cc406Sopenharmony_ci  /*
616141cc406Sopenharmony_ci   * Check that the address belongs to the specified subnet, using the bitmask.
617141cc406Sopenharmony_ci   * The address is represented by a 32bit integer.
618141cc406Sopenharmony_ci   */
619141cc406Sopenharmony_ci  if ((base->sin_addr.s_addr & mask) == (sin->sin_addr.s_addr & mask))
620141cc406Sopenharmony_ci    ret = SANE_TRUE;
621141cc406Sopenharmony_ci
622141cc406Sopenharmony_ci  freeaddrinfo (res);
623141cc406Sopenharmony_ci
624141cc406Sopenharmony_ci  return ret;
625141cc406Sopenharmony_ci}
626141cc406Sopenharmony_ci
627141cc406Sopenharmony_ci
628141cc406Sopenharmony_ci# ifdef ENABLE_IPV6
629141cc406Sopenharmony_ci
630141cc406Sopenharmony_cistatic SANE_Bool
631141cc406Sopenharmony_cicheck_v6_in_range (struct sockaddr_in6 *sin6, char *base_ip, char *netmask)
632141cc406Sopenharmony_ci{
633141cc406Sopenharmony_ci  int cidr;
634141cc406Sopenharmony_ci  int i, err;
635141cc406Sopenharmony_ci  unsigned int mask[16];
636141cc406Sopenharmony_ci  char *end;
637141cc406Sopenharmony_ci  struct sockaddr_in6 *base;
638141cc406Sopenharmony_ci  struct addrinfo hints;
639141cc406Sopenharmony_ci  struct addrinfo *res;
640141cc406Sopenharmony_ci  SANE_Bool ret = SANE_TRUE;
641141cc406Sopenharmony_ci
642141cc406Sopenharmony_ci  cidr = -1;
643141cc406Sopenharmony_ci  cidr = strtol (netmask, &end, 10);
644141cc406Sopenharmony_ci
645141cc406Sopenharmony_ci  /* Sanity check on the cidr value */
646141cc406Sopenharmony_ci  if ((cidr < 0) || (cidr > 128) || (end == netmask))
647141cc406Sopenharmony_ci    {
648141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_v6_in_range: invalid CIDR value (%s) !\n", netmask);
649141cc406Sopenharmony_ci      return SANE_FALSE;
650141cc406Sopenharmony_ci    }
651141cc406Sopenharmony_ci
652141cc406Sopenharmony_ci  memset (mask, 0, (16 * sizeof (unsigned int)));
653141cc406Sopenharmony_ci  cidr -= 8;
654141cc406Sopenharmony_ci
655141cc406Sopenharmony_ci  /* Build a bitmask out of the CIDR value */
656141cc406Sopenharmony_ci  for (i = 0; cidr >= 0; i++)
657141cc406Sopenharmony_ci    {
658141cc406Sopenharmony_ci      mask[i] = 0xff;
659141cc406Sopenharmony_ci      cidr -= 8;
660141cc406Sopenharmony_ci    }
661141cc406Sopenharmony_ci
662141cc406Sopenharmony_ci  if (cidr < 0)
663141cc406Sopenharmony_ci    mask[i] = cidrtomask[cidr + 8];
664141cc406Sopenharmony_ci
665141cc406Sopenharmony_ci  /* get a sockaddr_in6 representing the base IP address */
666141cc406Sopenharmony_ci  memset (&hints, 0, sizeof (struct addrinfo));
667141cc406Sopenharmony_ci  hints.ai_flags = AI_NUMERICHOST;
668141cc406Sopenharmony_ci  hints.ai_family = PF_INET6;
669141cc406Sopenharmony_ci
670141cc406Sopenharmony_ci  err = getaddrinfo (base_ip, NULL, &hints, &res);
671141cc406Sopenharmony_ci  if (err)
672141cc406Sopenharmony_ci    {
673141cc406Sopenharmony_ci      DBG (DBG_DBG, "check_v6_in_range: getaddrinfo() failed: %s\n", gai_strerror (err));
674141cc406Sopenharmony_ci      return SANE_FALSE;
675141cc406Sopenharmony_ci    }
676141cc406Sopenharmony_ci
677141cc406Sopenharmony_ci  base = (struct sockaddr_in6 *) res->ai_addr;
678141cc406Sopenharmony_ci
679141cc406Sopenharmony_ci  /*
680141cc406Sopenharmony_ci   * Check that the address belongs to the specified subnet.
681141cc406Sopenharmony_ci   * The address is reprensented by an array of 16 8bit integers.
682141cc406Sopenharmony_ci   */
683141cc406Sopenharmony_ci  for (i = 0; i < 16; i++)
684141cc406Sopenharmony_ci    {
685141cc406Sopenharmony_ci      if ((base->sin6_addr.s6_addr[i] & mask[i]) != (sin6->sin6_addr.s6_addr[i] & mask[i]))
686141cc406Sopenharmony_ci	{
687141cc406Sopenharmony_ci	  ret = SANE_FALSE;
688141cc406Sopenharmony_ci	  break;
689141cc406Sopenharmony_ci	}
690141cc406Sopenharmony_ci    }
691141cc406Sopenharmony_ci
692141cc406Sopenharmony_ci  freeaddrinfo (res);
693141cc406Sopenharmony_ci
694141cc406Sopenharmony_ci  return ret;
695141cc406Sopenharmony_ci}
696141cc406Sopenharmony_ci# endif /* ENABLE_IPV6 */
697141cc406Sopenharmony_ci#else /* !SANED_USES_AF_INDEP */
698141cc406Sopenharmony_cistatic SANE_Bool
699141cc406Sopenharmony_cicheck_v4_in_range (struct in_addr *inaddr, struct in_addr *base, char *netmask)
700141cc406Sopenharmony_ci{
701141cc406Sopenharmony_ci  int cidr;
702141cc406Sopenharmony_ci  int i;
703141cc406Sopenharmony_ci  char *end;
704141cc406Sopenharmony_ci  uint32_t mask;
705141cc406Sopenharmony_ci  SANE_Bool ret = SANE_FALSE;
706141cc406Sopenharmony_ci
707141cc406Sopenharmony_ci  cidr = -1;
708141cc406Sopenharmony_ci  cidr = strtol (netmask, &end, 10);
709141cc406Sopenharmony_ci
710141cc406Sopenharmony_ci  /* sanity check on the cidr value */
711141cc406Sopenharmony_ci  if ((cidr < 0) || (cidr > 32) || (end == netmask))
712141cc406Sopenharmony_ci    {
713141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_v4_in_range: invalid CIDR value (%s) !\n", netmask);
714141cc406Sopenharmony_ci      return SANE_FALSE;
715141cc406Sopenharmony_ci    }
716141cc406Sopenharmony_ci
717141cc406Sopenharmony_ci  mask = 0;
718141cc406Sopenharmony_ci  cidr -= 8;
719141cc406Sopenharmony_ci
720141cc406Sopenharmony_ci  /* Build a bitmask out of the CIDR value */
721141cc406Sopenharmony_ci  for (i = 3; cidr >= 0; i--)
722141cc406Sopenharmony_ci    {
723141cc406Sopenharmony_ci      mask |= (0xff << (8 * i));
724141cc406Sopenharmony_ci      cidr -= 8;
725141cc406Sopenharmony_ci    }
726141cc406Sopenharmony_ci
727141cc406Sopenharmony_ci  if (cidr < 0)
728141cc406Sopenharmony_ci    mask |= (cidrtomask[cidr + 8] << (8 * i));
729141cc406Sopenharmony_ci
730141cc406Sopenharmony_ci  mask = htonl (mask);
731141cc406Sopenharmony_ci
732141cc406Sopenharmony_ci  /*
733141cc406Sopenharmony_ci   * Check that the address belongs to the specified subnet, using the bitmask.
734141cc406Sopenharmony_ci   * The address is represented by a 32bit integer.
735141cc406Sopenharmony_ci   */
736141cc406Sopenharmony_ci  if ((base->s_addr & mask) == (inaddr->s_addr & mask))
737141cc406Sopenharmony_ci    ret = SANE_TRUE;
738141cc406Sopenharmony_ci
739141cc406Sopenharmony_ci  return ret;
740141cc406Sopenharmony_ci}
741141cc406Sopenharmony_ci#endif /* SANED_USES_AF_INDEP */
742141cc406Sopenharmony_ci
743141cc406Sopenharmony_ci
744141cc406Sopenharmony_ci
745141cc406Sopenharmony_ci/* Access control */
746141cc406Sopenharmony_ci#ifdef SANED_USES_AF_INDEP
747141cc406Sopenharmony_cistatic SANE_Status
748141cc406Sopenharmony_cicheck_host (int fd)
749141cc406Sopenharmony_ci{
750141cc406Sopenharmony_ci  struct sockaddr_in *sin = NULL;
751141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
752141cc406Sopenharmony_ci  struct sockaddr_in6 *sin6;
753141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
754141cc406Sopenharmony_ci  struct addrinfo hints;
755141cc406Sopenharmony_ci  struct addrinfo *res;
756141cc406Sopenharmony_ci  struct addrinfo *resp;
757141cc406Sopenharmony_ci  int j, access_ok = 0;
758141cc406Sopenharmony_ci  int err;
759141cc406Sopenharmony_ci  char text_addr[64];
760141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
761141cc406Sopenharmony_ci  SANE_Bool IPv4map = SANE_FALSE;
762141cc406Sopenharmony_ci  char *remote_ipv4 = NULL; /* in case we have an IPv4-mapped address (eg ::ffff:127.0.0.1) */
763141cc406Sopenharmony_ci  char *tmp;
764141cc406Sopenharmony_ci  struct addrinfo *remote_ipv4_addr = NULL;
765141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
766141cc406Sopenharmony_ci  char config_line_buf[1024];
767141cc406Sopenharmony_ci  char *config_line;
768141cc406Sopenharmony_ci  char *netmask;
769141cc406Sopenharmony_ci  char hostname[MAXHOSTNAMELEN];
770141cc406Sopenharmony_ci
771141cc406Sopenharmony_ci  int len;
772141cc406Sopenharmony_ci  FILE *fp;
773141cc406Sopenharmony_ci
774141cc406Sopenharmony_ci  /* Get address of remote host */
775141cc406Sopenharmony_ci  remote_address_len = sizeof (remote_address.ss);
776141cc406Sopenharmony_ci  if (getpeername (fd, &remote_address.sa, (socklen_t *) &remote_address_len) < 0)
777141cc406Sopenharmony_ci    {
778141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_host: getpeername failed: %s\n", strerror (errno));
779141cc406Sopenharmony_ci      remote_ip = strdup ("[error]");
780141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
781141cc406Sopenharmony_ci    }
782141cc406Sopenharmony_ci
783141cc406Sopenharmony_ci  err = getnameinfo (&remote_address.sa, remote_address_len,
784141cc406Sopenharmony_ci		     hostname, sizeof (hostname), NULL, 0, NI_NUMERICHOST);
785141cc406Sopenharmony_ci  if (err)
786141cc406Sopenharmony_ci    {
787141cc406Sopenharmony_ci      DBG (DBG_DBG, "check_host: getnameinfo failed: %s\n", gai_strerror(err));
788141cc406Sopenharmony_ci      remote_ip = strdup ("[error]");
789141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
790141cc406Sopenharmony_ci    }
791141cc406Sopenharmony_ci  else
792141cc406Sopenharmony_ci    remote_ip = strdup (hostname);
793141cc406Sopenharmony_ci
794141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
795141cc406Sopenharmony_ci  sin6 = &remote_address.sin6;
796141cc406Sopenharmony_ci
797141cc406Sopenharmony_ci  if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)sin6->sin6_addr.s6_addr))
798141cc406Sopenharmony_ci    {
799141cc406Sopenharmony_ci      DBG (DBG_DBG, "check_host: detected an IPv4-mapped address\n");
800141cc406Sopenharmony_ci      remote_ipv4 = remote_ip + 7;
801141cc406Sopenharmony_ci      IPv4map = SANE_TRUE;
802141cc406Sopenharmony_ci
803141cc406Sopenharmony_ci      memset (&hints, 0, sizeof (struct addrinfo));
804141cc406Sopenharmony_ci      hints.ai_flags = AI_NUMERICHOST;
805141cc406Sopenharmony_ci      hints.ai_family = PF_INET;
806141cc406Sopenharmony_ci
807141cc406Sopenharmony_ci      err = getaddrinfo (remote_ipv4, NULL, &hints, &res);
808141cc406Sopenharmony_ci      if (err)
809141cc406Sopenharmony_ci	{
810141cc406Sopenharmony_ci	  DBG (DBG_DBG, "check_host: getaddrinfo() failed: %s\n", gai_strerror (err));
811141cc406Sopenharmony_ci	  IPv4map = SANE_FALSE; /* we failed, remote_ipv4_addr points to nothing */
812141cc406Sopenharmony_ci	}
813141cc406Sopenharmony_ci      else
814141cc406Sopenharmony_ci	{
815141cc406Sopenharmony_ci	  remote_ipv4_addr = res;
816141cc406Sopenharmony_ci	  sin = (struct sockaddr_in *)res->ai_addr;
817141cc406Sopenharmony_ci	}
818141cc406Sopenharmony_ci    }
819141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
820141cc406Sopenharmony_ci
821141cc406Sopenharmony_ci  DBG (DBG_WARN, "check_host: access by remote host: %s\n", remote_ip);
822141cc406Sopenharmony_ci
823141cc406Sopenharmony_ci  /* Always allow access from local host. Do it here to avoid DNS lookups
824141cc406Sopenharmony_ci     and reading saned.conf. */
825141cc406Sopenharmony_ci
826141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
827141cc406Sopenharmony_ci  if (IPv4map == SANE_TRUE)
828141cc406Sopenharmony_ci    {
829141cc406Sopenharmony_ci      if (IN_LOOPBACK (ntohl (sin->sin_addr.s_addr)))
830141cc406Sopenharmony_ci	{
831141cc406Sopenharmony_ci	  DBG (DBG_MSG,
832141cc406Sopenharmony_ci	       "check_host: remote host is IN_LOOPBACK: access granted\n");
833141cc406Sopenharmony_ci	  freeaddrinfo (remote_ipv4_addr);
834141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
835141cc406Sopenharmony_ci	}
836141cc406Sopenharmony_ci      freeaddrinfo (remote_ipv4_addr);
837141cc406Sopenharmony_ci    }
838141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
839141cc406Sopenharmony_ci
840141cc406Sopenharmony_ci  sin = &remote_address.sin;
841141cc406Sopenharmony_ci
842141cc406Sopenharmony_ci  switch (SS_FAMILY(remote_address.ss))
843141cc406Sopenharmony_ci    {
844141cc406Sopenharmony_ci      case AF_INET:
845141cc406Sopenharmony_ci	if (IN_LOOPBACK (ntohl (sin->sin_addr.s_addr)))
846141cc406Sopenharmony_ci	  {
847141cc406Sopenharmony_ci	    DBG (DBG_MSG,
848141cc406Sopenharmony_ci		 "check_host: remote host is IN_LOOPBACK: access granted\n");
849141cc406Sopenharmony_ci	    return SANE_STATUS_GOOD;
850141cc406Sopenharmony_ci	  }
851141cc406Sopenharmony_ci	break;
852141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
853141cc406Sopenharmony_ci      case AF_INET6:
854141cc406Sopenharmony_ci	if (IN6_IS_ADDR_LOOPBACK ((struct in6_addr *)sin6->sin6_addr.s6_addr))
855141cc406Sopenharmony_ci	  {
856141cc406Sopenharmony_ci	    DBG (DBG_MSG,
857141cc406Sopenharmony_ci		 "check_host: remote host is IN6_LOOPBACK: access granted\n");
858141cc406Sopenharmony_ci	    return SANE_STATUS_GOOD;
859141cc406Sopenharmony_ci	  }
860141cc406Sopenharmony_ci	break;
861141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
862141cc406Sopenharmony_ci      default:
863141cc406Sopenharmony_ci	break;
864141cc406Sopenharmony_ci    }
865141cc406Sopenharmony_ci
866141cc406Sopenharmony_ci  DBG (DBG_DBG, "check_host: remote host is not IN_LOOPBACK"
867141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
868141cc406Sopenharmony_ci       " nor IN6_LOOPBACK"
869141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
870141cc406Sopenharmony_ci       "\n");
871141cc406Sopenharmony_ci
872141cc406Sopenharmony_ci
873141cc406Sopenharmony_ci  /* Get name of local host */
874141cc406Sopenharmony_ci  if (gethostname (hostname, sizeof (hostname)) < 0)
875141cc406Sopenharmony_ci    {
876141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_host: gethostname failed: %s\n", strerror (errno));
877141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
878141cc406Sopenharmony_ci    }
879141cc406Sopenharmony_ci  DBG (DBG_DBG, "check_host: local hostname: %s\n", hostname);
880141cc406Sopenharmony_ci
881141cc406Sopenharmony_ci  /* Get local addresses */
882141cc406Sopenharmony_ci  memset (&hints, 0, sizeof (hints));
883141cc406Sopenharmony_ci  hints.ai_flags = AI_CANONNAME;
884141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
885141cc406Sopenharmony_ci  hints.ai_family = PF_UNSPEC;
886141cc406Sopenharmony_ci#else
887141cc406Sopenharmony_ci  hints.ai_family = PF_INET;
888141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
889141cc406Sopenharmony_ci
890141cc406Sopenharmony_ci  err = getaddrinfo (hostname, NULL, &hints, &res);
891141cc406Sopenharmony_ci  if (err)
892141cc406Sopenharmony_ci    {
893141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_host: getaddrinfo for local hostname failed: %s\n",
894141cc406Sopenharmony_ci	   gai_strerror (err));
895141cc406Sopenharmony_ci
896141cc406Sopenharmony_ci      /* Proceed even if the local hostname does not resolve */
897141cc406Sopenharmony_ci      if (err != EAI_NONAME)
898141cc406Sopenharmony_ci	return SANE_STATUS_INVAL;
899141cc406Sopenharmony_ci    }
900141cc406Sopenharmony_ci  else
901141cc406Sopenharmony_ci    {
902141cc406Sopenharmony_ci      for (resp = res; resp != NULL; resp = resp->ai_next)
903141cc406Sopenharmony_ci	{
904141cc406Sopenharmony_ci	  DBG (DBG_DBG, "check_host: local hostname(s) (from DNS): %s\n",
905141cc406Sopenharmony_ci	       resp->ai_canonname);
906141cc406Sopenharmony_ci
907141cc406Sopenharmony_ci	  err = getnameinfo (resp->ai_addr, resp->ai_addrlen, text_addr,
908141cc406Sopenharmony_ci			     sizeof (text_addr), NULL, 0, NI_NUMERICHOST);
909141cc406Sopenharmony_ci	  if (err)
910141cc406Sopenharmony_ci		strncpy (text_addr, "[error]", 8);
911141cc406Sopenharmony_ci
912141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
913141cc406Sopenharmony_ci	  if ((strcasecmp (text_addr, remote_ip) == 0) ||
914141cc406Sopenharmony_ci	      ((IPv4map == SANE_TRUE) && (strcmp (text_addr, remote_ipv4) == 0)))
915141cc406Sopenharmony_ci#else
916141cc406Sopenharmony_ci	  if (strcmp (text_addr, remote_ip) == 0)
917141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
918141cc406Sopenharmony_ci	    {
919141cc406Sopenharmony_ci	      DBG (DBG_MSG, "check_host: remote host has same addr as local: access granted\n");
920141cc406Sopenharmony_ci
921141cc406Sopenharmony_ci	      freeaddrinfo (res);
922141cc406Sopenharmony_ci	      res = NULL;
923141cc406Sopenharmony_ci
924141cc406Sopenharmony_ci	      return SANE_STATUS_GOOD;
925141cc406Sopenharmony_ci	    }
926141cc406Sopenharmony_ci	}
927141cc406Sopenharmony_ci
928141cc406Sopenharmony_ci      freeaddrinfo (res);
929141cc406Sopenharmony_ci      res = NULL;
930141cc406Sopenharmony_ci
931141cc406Sopenharmony_ci      DBG (DBG_DBG,
932141cc406Sopenharmony_ci	   "check_host: remote host doesn't have same addr as local\n");
933141cc406Sopenharmony_ci    }
934141cc406Sopenharmony_ci
935141cc406Sopenharmony_ci  /* must be a remote host: check contents of PATH_NET_CONFIG or
936141cc406Sopenharmony_ci     /etc/hosts.equiv if former doesn't exist: */
937141cc406Sopenharmony_ci  for (j = 0; j < NELEMS (config_file_names); ++j)
938141cc406Sopenharmony_ci    {
939141cc406Sopenharmony_ci      DBG (DBG_DBG, "check_host: opening config file: %s\n",
940141cc406Sopenharmony_ci	   config_file_names[j]);
941141cc406Sopenharmony_ci      if (config_file_names[j][0] == '/')
942141cc406Sopenharmony_ci	fp = fopen (config_file_names[j], "r");
943141cc406Sopenharmony_ci      else
944141cc406Sopenharmony_ci	fp = sanei_config_open (config_file_names[j]);
945141cc406Sopenharmony_ci      if (!fp)
946141cc406Sopenharmony_ci	{
947141cc406Sopenharmony_ci	  DBG (DBG_MSG,
948141cc406Sopenharmony_ci	       "check_host: can't open config file: %s (%s)\n",
949141cc406Sopenharmony_ci	       config_file_names[j], strerror (errno));
950141cc406Sopenharmony_ci	  continue;
951141cc406Sopenharmony_ci	}
952141cc406Sopenharmony_ci
953141cc406Sopenharmony_ci      while (!access_ok && sanei_config_read (config_line_buf,
954141cc406Sopenharmony_ci					      sizeof (config_line_buf), fp))
955141cc406Sopenharmony_ci	{
956141cc406Sopenharmony_ci	  config_line = config_line_buf; /* from now on, use a pointer */
957141cc406Sopenharmony_ci	  DBG (DBG_DBG, "check_host: config file line: `%s'\n", config_line);
958141cc406Sopenharmony_ci	  if (config_line[0] == '#')
959141cc406Sopenharmony_ci	    continue;           /* ignore comments */
960141cc406Sopenharmony_ci
961141cc406Sopenharmony_ci	  if (strchr (config_line, '='))
962141cc406Sopenharmony_ci	    continue;           /* ignore lines with an = sign */
963141cc406Sopenharmony_ci
964141cc406Sopenharmony_ci	  len = strlen (config_line);
965141cc406Sopenharmony_ci	  if (!len)
966141cc406Sopenharmony_ci	    continue;		/* ignore empty lines */
967141cc406Sopenharmony_ci
968141cc406Sopenharmony_ci	  /* look for a subnet specification */
969141cc406Sopenharmony_ci	  netmask = strchr (config_line, '/');
970141cc406Sopenharmony_ci	  if (netmask != NULL)
971141cc406Sopenharmony_ci	    {
972141cc406Sopenharmony_ci	      *netmask = '\0';
973141cc406Sopenharmony_ci	      netmask++;
974141cc406Sopenharmony_ci	      DBG (DBG_DBG, "check_host: subnet with base IP = %s, CIDR netmask = %s\n",
975141cc406Sopenharmony_ci		   config_line, netmask);
976141cc406Sopenharmony_ci	    }
977141cc406Sopenharmony_ci
978141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
979141cc406Sopenharmony_ci	  /* IPv6 addresses are enclosed in [] */
980141cc406Sopenharmony_ci	  if (*config_line == '[')
981141cc406Sopenharmony_ci	    {
982141cc406Sopenharmony_ci	      config_line++;
983141cc406Sopenharmony_ci	      tmp = strchr (config_line, ']');
984141cc406Sopenharmony_ci	      if (tmp == NULL)
985141cc406Sopenharmony_ci		{
986141cc406Sopenharmony_ci		  DBG (DBG_ERR,
987141cc406Sopenharmony_ci		       "check_host: malformed IPv6 address in config file, skipping: [%s\n",
988141cc406Sopenharmony_ci		       config_line);
989141cc406Sopenharmony_ci		  continue;
990141cc406Sopenharmony_ci		}
991141cc406Sopenharmony_ci	      *tmp = '\0';
992141cc406Sopenharmony_ci	    }
993141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
994141cc406Sopenharmony_ci
995141cc406Sopenharmony_ci	  if (strcmp (config_line, "+") == 0)
996141cc406Sopenharmony_ci	    {
997141cc406Sopenharmony_ci	      access_ok = 1;
998141cc406Sopenharmony_ci	      DBG (DBG_DBG,
999141cc406Sopenharmony_ci		   "check_host: access granted from any host (`+')\n");
1000141cc406Sopenharmony_ci	    }
1001141cc406Sopenharmony_ci	  /* compare remote_ip (remote IP address) to the config_line */
1002141cc406Sopenharmony_ci	  else if (strcasecmp (config_line, remote_ip) == 0)
1003141cc406Sopenharmony_ci	    {
1004141cc406Sopenharmony_ci	      access_ok = 1;
1005141cc406Sopenharmony_ci	      DBG (DBG_DBG,
1006141cc406Sopenharmony_ci		   "check_host: access granted from IP address %s\n", remote_ip);
1007141cc406Sopenharmony_ci	    }
1008141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1009141cc406Sopenharmony_ci	  else if ((IPv4map == SANE_TRUE) && (strcmp (config_line, remote_ipv4) == 0))
1010141cc406Sopenharmony_ci	    {
1011141cc406Sopenharmony_ci	      access_ok = 1;
1012141cc406Sopenharmony_ci	      DBG (DBG_DBG,
1013141cc406Sopenharmony_ci		   "check_host: access granted from IP address %s (IPv4-mapped)\n", remote_ip);
1014141cc406Sopenharmony_ci	    }
1015141cc406Sopenharmony_ci	  /* handle IP ranges, take care of the IPv4map stuff */
1016141cc406Sopenharmony_ci	  else if (netmask != NULL)
1017141cc406Sopenharmony_ci	    {
1018141cc406Sopenharmony_ci	      if (strchr (config_line, ':') != NULL) /* is a v6 address */
1019141cc406Sopenharmony_ci		{
1020141cc406Sopenharmony_ci		  if (SS_FAMILY(remote_address.ss) == AF_INET6)
1021141cc406Sopenharmony_ci		    {
1022141cc406Sopenharmony_ci		      if (check_v6_in_range (sin6, config_line, netmask))
1023141cc406Sopenharmony_ci			{
1024141cc406Sopenharmony_ci			  access_ok = 1;
1025141cc406Sopenharmony_ci			  DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet [%s]/%s)\n",
1026141cc406Sopenharmony_ci			       remote_ip, config_line, netmask);
1027141cc406Sopenharmony_ci			}
1028141cc406Sopenharmony_ci		    }
1029141cc406Sopenharmony_ci		}
1030141cc406Sopenharmony_ci	      else /* is a v4 address */
1031141cc406Sopenharmony_ci		{
1032141cc406Sopenharmony_ci		  if (IPv4map == SANE_TRUE)
1033141cc406Sopenharmony_ci		    {
1034141cc406Sopenharmony_ci		      /* get a sockaddr_in representing the v4-mapped IP address */
1035141cc406Sopenharmony_ci		      memset (&hints, 0, sizeof (struct addrinfo));
1036141cc406Sopenharmony_ci		      hints.ai_flags = AI_NUMERICHOST;
1037141cc406Sopenharmony_ci		      hints.ai_family = PF_INET;
1038141cc406Sopenharmony_ci
1039141cc406Sopenharmony_ci		      err = getaddrinfo (remote_ipv4, NULL, &hints, &res);
1040141cc406Sopenharmony_ci		      if (err)
1041141cc406Sopenharmony_ci			DBG (DBG_DBG, "check_host: getaddrinfo() failed: %s\n", gai_strerror (err));
1042141cc406Sopenharmony_ci		      else
1043141cc406Sopenharmony_ci			sin = (struct sockaddr_in *)res->ai_addr;
1044141cc406Sopenharmony_ci		    }
1045141cc406Sopenharmony_ci
1046141cc406Sopenharmony_ci		  if ((SS_FAMILY(remote_address.ss) == AF_INET) ||
1047141cc406Sopenharmony_ci		      (IPv4map == SANE_TRUE))
1048141cc406Sopenharmony_ci		    {
1049141cc406Sopenharmony_ci
1050141cc406Sopenharmony_ci		      if (check_v4_in_range (sin, config_line, netmask))
1051141cc406Sopenharmony_ci			{
1052141cc406Sopenharmony_ci			  DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet %s/%s)\n",
1053141cc406Sopenharmony_ci			       ((IPv4map == SANE_TRUE) ? remote_ipv4 : remote_ip), config_line, netmask);
1054141cc406Sopenharmony_ci			  access_ok = 1;
1055141cc406Sopenharmony_ci			}
1056141cc406Sopenharmony_ci		      else
1057141cc406Sopenharmony_ci			{
1058141cc406Sopenharmony_ci			  /* restore the old sin pointer */
1059141cc406Sopenharmony_ci			  sin = &remote_address.sin;
1060141cc406Sopenharmony_ci			}
1061141cc406Sopenharmony_ci
1062141cc406Sopenharmony_ci		      if (res != NULL)
1063141cc406Sopenharmony_ci			{
1064141cc406Sopenharmony_ci			  freeaddrinfo (res);
1065141cc406Sopenharmony_ci			  res = NULL;
1066141cc406Sopenharmony_ci			}
1067141cc406Sopenharmony_ci		    }
1068141cc406Sopenharmony_ci		}
1069141cc406Sopenharmony_ci	    }
1070141cc406Sopenharmony_ci#else /* !ENABLE_IPV6 */
1071141cc406Sopenharmony_ci	  /* handle IP ranges */
1072141cc406Sopenharmony_ci	  else if (netmask != NULL)
1073141cc406Sopenharmony_ci	    {
1074141cc406Sopenharmony_ci	      if (check_v4_in_range (sin, config_line, netmask))
1075141cc406Sopenharmony_ci		{
1076141cc406Sopenharmony_ci		  access_ok = 1;
1077141cc406Sopenharmony_ci		  DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet %s/%s)\n",
1078141cc406Sopenharmony_ci		       remote_ip, config_line, netmask);
1079141cc406Sopenharmony_ci		}
1080141cc406Sopenharmony_ci	    }
1081141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1082141cc406Sopenharmony_ci	  else
1083141cc406Sopenharmony_ci	    {
1084141cc406Sopenharmony_ci	      memset (&hints, 0, sizeof (hints));
1085141cc406Sopenharmony_ci	      hints.ai_flags = AI_CANONNAME;
1086141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1087141cc406Sopenharmony_ci	      hints.ai_family = PF_UNSPEC;
1088141cc406Sopenharmony_ci#else
1089141cc406Sopenharmony_ci	      hints.ai_family = PF_INET;
1090141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1091141cc406Sopenharmony_ci
1092141cc406Sopenharmony_ci	      err = getaddrinfo (config_line, NULL, &hints, &res);
1093141cc406Sopenharmony_ci	      if (err)
1094141cc406Sopenharmony_ci		{
1095141cc406Sopenharmony_ci		  DBG (DBG_DBG,
1096141cc406Sopenharmony_ci		       "check_host: getaddrinfo for `%s' failed: %s\n",
1097141cc406Sopenharmony_ci		       config_line, gai_strerror (err));
1098141cc406Sopenharmony_ci		  DBG (DBG_MSG, "check_host: entry isn't an IP address "
1099141cc406Sopenharmony_ci		       "and can't be found in DNS\n");
1100141cc406Sopenharmony_ci		  continue;
1101141cc406Sopenharmony_ci		}
1102141cc406Sopenharmony_ci	      else
1103141cc406Sopenharmony_ci		{
1104141cc406Sopenharmony_ci		  for (resp = res; resp != NULL; resp = resp->ai_next)
1105141cc406Sopenharmony_ci		    {
1106141cc406Sopenharmony_ci		      err = getnameinfo (resp->ai_addr, resp->ai_addrlen, text_addr,
1107141cc406Sopenharmony_ci					 sizeof (text_addr), NULL, 0, NI_NUMERICHOST);
1108141cc406Sopenharmony_ci		      if (err)
1109141cc406Sopenharmony_ci			strncpy (text_addr, "[error]", 8);
1110141cc406Sopenharmony_ci
1111141cc406Sopenharmony_ci		      DBG (DBG_MSG,
1112141cc406Sopenharmony_ci			   "check_host: DNS lookup returns IP address: %s\n",
1113141cc406Sopenharmony_ci			   text_addr);
1114141cc406Sopenharmony_ci
1115141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1116141cc406Sopenharmony_ci		      if ((strcasecmp (text_addr, remote_ip) == 0) ||
1117141cc406Sopenharmony_ci			  ((IPv4map == SANE_TRUE) && (strcmp (text_addr, remote_ipv4) == 0)))
1118141cc406Sopenharmony_ci#else
1119141cc406Sopenharmony_ci		      if (strcmp (text_addr, remote_ip) == 0)
1120141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1121141cc406Sopenharmony_ci			access_ok = 1;
1122141cc406Sopenharmony_ci
1123141cc406Sopenharmony_ci		      if (access_ok)
1124141cc406Sopenharmony_ci			break;
1125141cc406Sopenharmony_ci		    }
1126141cc406Sopenharmony_ci		  freeaddrinfo (res);
1127141cc406Sopenharmony_ci		  res = NULL;
1128141cc406Sopenharmony_ci		}
1129141cc406Sopenharmony_ci	    }
1130141cc406Sopenharmony_ci	}
1131141cc406Sopenharmony_ci      fclose (fp);
1132141cc406Sopenharmony_ci    }
1133141cc406Sopenharmony_ci
1134141cc406Sopenharmony_ci  if (access_ok)
1135141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1136141cc406Sopenharmony_ci
1137141cc406Sopenharmony_ci  return SANE_STATUS_ACCESS_DENIED;
1138141cc406Sopenharmony_ci}
1139141cc406Sopenharmony_ci
1140141cc406Sopenharmony_ci#else /* !SANED_USES_AF_INDEP */
1141141cc406Sopenharmony_ci
1142141cc406Sopenharmony_cistatic SANE_Status
1143141cc406Sopenharmony_cicheck_host (int fd)
1144141cc406Sopenharmony_ci{
1145141cc406Sopenharmony_ci  struct sockaddr_in sin;
1146141cc406Sopenharmony_ci  int j, access_ok = 0;
1147141cc406Sopenharmony_ci  struct hostent *he;
1148141cc406Sopenharmony_ci  char text_addr[64];
1149141cc406Sopenharmony_ci  char config_line_buf[1024];
1150141cc406Sopenharmony_ci  char *config_line;
1151141cc406Sopenharmony_ci  char *netmask;
1152141cc406Sopenharmony_ci  char hostname[MAXHOSTNAMELEN];
1153141cc406Sopenharmony_ci  char *r_hostname;
1154141cc406Sopenharmony_ci  static struct in_addr config_line_address;
1155141cc406Sopenharmony_ci
1156141cc406Sopenharmony_ci  int len;
1157141cc406Sopenharmony_ci  FILE *fp;
1158141cc406Sopenharmony_ci
1159141cc406Sopenharmony_ci  /* Get address of remote host */
1160141cc406Sopenharmony_ci  len = sizeof (sin);
1161141cc406Sopenharmony_ci  if (getpeername (fd, (struct sockaddr *) &sin, (socklen_t *) &len) < 0)
1162141cc406Sopenharmony_ci    {
1163141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_host: getpeername failed: %s\n", strerror (errno));
1164141cc406Sopenharmony_ci      remote_ip = strdup ("[error]");
1165141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1166141cc406Sopenharmony_ci    }
1167141cc406Sopenharmony_ci  r_hostname = inet_ntoa (sin.sin_addr);
1168141cc406Sopenharmony_ci  remote_ip = strdup (r_hostname);
1169141cc406Sopenharmony_ci  DBG (DBG_WARN, "check_host: access by remote host: %s\n",
1170141cc406Sopenharmony_ci       remote_ip);
1171141cc406Sopenharmony_ci  /* Save remote address for check of control and data connections */
1172141cc406Sopenharmony_ci  memcpy (&remote_address, &sin.sin_addr, sizeof (remote_address));
1173141cc406Sopenharmony_ci
1174141cc406Sopenharmony_ci  /* Always allow access from local host. Do it here to avoid DNS lookups
1175141cc406Sopenharmony_ci     and reading saned.conf. */
1176141cc406Sopenharmony_ci  if (IN_LOOPBACK (ntohl (sin.sin_addr.s_addr)))
1177141cc406Sopenharmony_ci    {
1178141cc406Sopenharmony_ci      DBG (DBG_MSG,
1179141cc406Sopenharmony_ci	   "check_host: remote host is IN_LOOPBACK: access accepted\n");
1180141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
1181141cc406Sopenharmony_ci    }
1182141cc406Sopenharmony_ci  DBG (DBG_DBG, "check_host: remote host is not IN_LOOPBACK\n");
1183141cc406Sopenharmony_ci
1184141cc406Sopenharmony_ci  /* Get name of local host */
1185141cc406Sopenharmony_ci  if (gethostname (hostname, sizeof (hostname)) < 0)
1186141cc406Sopenharmony_ci    {
1187141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_host: gethostname failed: %s\n", strerror (errno));
1188141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1189141cc406Sopenharmony_ci    }
1190141cc406Sopenharmony_ci  DBG (DBG_DBG, "check_host: local hostname: %s\n", hostname);
1191141cc406Sopenharmony_ci
1192141cc406Sopenharmony_ci  /* Get local address */
1193141cc406Sopenharmony_ci  he = gethostbyname (hostname);
1194141cc406Sopenharmony_ci
1195141cc406Sopenharmony_ci  if (!he)
1196141cc406Sopenharmony_ci    {
1197141cc406Sopenharmony_ci      DBG (DBG_ERR, "check_host: gethostbyname for local hostname failed: %s\n",
1198141cc406Sopenharmony_ci	   hstrerror (h_errno));
1199141cc406Sopenharmony_ci
1200141cc406Sopenharmony_ci      /* Proceed even if the local hostname doesn't resolve */
1201141cc406Sopenharmony_ci      if (h_errno != HOST_NOT_FOUND)
1202141cc406Sopenharmony_ci	return SANE_STATUS_INVAL;
1203141cc406Sopenharmony_ci    }
1204141cc406Sopenharmony_ci  else
1205141cc406Sopenharmony_ci    {
1206141cc406Sopenharmony_ci      DBG (DBG_DBG, "check_host: local hostname (from DNS): %s\n",
1207141cc406Sopenharmony_ci	   he->h_name);
1208141cc406Sopenharmony_ci
1209141cc406Sopenharmony_ci      if ((he->h_length == 4) || (he->h_addrtype == AF_INET))
1210141cc406Sopenharmony_ci	{
1211141cc406Sopenharmony_ci	  if (!inet_ntop (he->h_addrtype, he->h_addr_list[0], text_addr,
1212141cc406Sopenharmony_ci			  sizeof (text_addr)))
1213141cc406Sopenharmony_ci	    strcpy (text_addr, "[error]");
1214141cc406Sopenharmony_ci	  DBG (DBG_DBG, "check_host: local host address (from DNS): %s\n",
1215141cc406Sopenharmony_ci	       text_addr);
1216141cc406Sopenharmony_ci	  if (memcmp (he->h_addr_list[0], &remote_address.s_addr, 4) == 0)
1217141cc406Sopenharmony_ci	    {
1218141cc406Sopenharmony_ci	      DBG (DBG_MSG,
1219141cc406Sopenharmony_ci		   "check_host: remote host has same addr as local: "
1220141cc406Sopenharmony_ci		   "access accepted\n");
1221141cc406Sopenharmony_ci	      return SANE_STATUS_GOOD;
1222141cc406Sopenharmony_ci	    }
1223141cc406Sopenharmony_ci	}
1224141cc406Sopenharmony_ci      else
1225141cc406Sopenharmony_ci	{
1226141cc406Sopenharmony_ci	  DBG (DBG_ERR, "check_host: can't get local address "
1227141cc406Sopenharmony_ci	       "(only IPv4 is supported)\n");
1228141cc406Sopenharmony_ci	}
1229141cc406Sopenharmony_ci
1230141cc406Sopenharmony_ci      DBG (DBG_DBG,
1231141cc406Sopenharmony_ci	   "check_host: remote host doesn't have same addr as local\n");
1232141cc406Sopenharmony_ci    }
1233141cc406Sopenharmony_ci
1234141cc406Sopenharmony_ci  /* must be a remote host: check contents of PATH_NET_CONFIG or
1235141cc406Sopenharmony_ci     /etc/hosts.equiv if former doesn't exist: */
1236141cc406Sopenharmony_ci  for (j = 0; j < NELEMS (config_file_names); ++j)
1237141cc406Sopenharmony_ci    {
1238141cc406Sopenharmony_ci      DBG (DBG_DBG, "check_host: opening config file: %s\n",
1239141cc406Sopenharmony_ci	   config_file_names[j]);
1240141cc406Sopenharmony_ci      if (config_file_names[j][0] == '/')
1241141cc406Sopenharmony_ci	fp = fopen (config_file_names[j], "r");
1242141cc406Sopenharmony_ci      else
1243141cc406Sopenharmony_ci	fp = sanei_config_open (config_file_names[j]);
1244141cc406Sopenharmony_ci      if (!fp)
1245141cc406Sopenharmony_ci	{
1246141cc406Sopenharmony_ci	  DBG (DBG_MSG,
1247141cc406Sopenharmony_ci	       "check_host: can't open config file: %s (%s)\n",
1248141cc406Sopenharmony_ci	       config_file_names[j], strerror (errno));
1249141cc406Sopenharmony_ci	  continue;
1250141cc406Sopenharmony_ci	}
1251141cc406Sopenharmony_ci
1252141cc406Sopenharmony_ci      while (!access_ok && sanei_config_read (config_line_buf,
1253141cc406Sopenharmony_ci					      sizeof (config_line_buf), fp))
1254141cc406Sopenharmony_ci	{
1255141cc406Sopenharmony_ci	  config_line = config_line_buf; /* from now on, use a pointer */
1256141cc406Sopenharmony_ci	  DBG (DBG_DBG, "check_host: config file line: `%s'\n", config_line);
1257141cc406Sopenharmony_ci	  if (config_line[0] == '#')
1258141cc406Sopenharmony_ci	    continue;           /* ignore comments */
1259141cc406Sopenharmony_ci
1260141cc406Sopenharmony_ci	  if (strchr (config_line, '='))
1261141cc406Sopenharmony_ci	    continue;           /* ignore lines with an = sign */
1262141cc406Sopenharmony_ci
1263141cc406Sopenharmony_ci	  len = strlen (config_line);
1264141cc406Sopenharmony_ci	  if (!len)
1265141cc406Sopenharmony_ci	    continue;		/* ignore empty lines */
1266141cc406Sopenharmony_ci
1267141cc406Sopenharmony_ci	  /* look for a subnet specification */
1268141cc406Sopenharmony_ci	  netmask = strchr (config_line, '/');
1269141cc406Sopenharmony_ci	  if (netmask != NULL)
1270141cc406Sopenharmony_ci	    {
1271141cc406Sopenharmony_ci	      *netmask = '\0';
1272141cc406Sopenharmony_ci	      netmask++;
1273141cc406Sopenharmony_ci	      DBG (DBG_DBG, "check_host: subnet with base IP = %s, CIDR netmask = %s\n",
1274141cc406Sopenharmony_ci		   config_line, netmask);
1275141cc406Sopenharmony_ci	    }
1276141cc406Sopenharmony_ci
1277141cc406Sopenharmony_ci	  if (strcmp (config_line, "+") == 0)
1278141cc406Sopenharmony_ci	    {
1279141cc406Sopenharmony_ci	      access_ok = 1;
1280141cc406Sopenharmony_ci	      DBG (DBG_DBG,
1281141cc406Sopenharmony_ci		   "check_host: access accepted from any host (`+')\n");
1282141cc406Sopenharmony_ci	    }
1283141cc406Sopenharmony_ci	  else
1284141cc406Sopenharmony_ci	    {
1285141cc406Sopenharmony_ci	      if (inet_pton (AF_INET, config_line, &config_line_address) > 0)
1286141cc406Sopenharmony_ci		{
1287141cc406Sopenharmony_ci		  if (memcmp (&remote_address.s_addr,
1288141cc406Sopenharmony_ci			      &config_line_address.s_addr, 4) == 0)
1289141cc406Sopenharmony_ci		    access_ok = 1;
1290141cc406Sopenharmony_ci		  else if (netmask != NULL)
1291141cc406Sopenharmony_ci		    {
1292141cc406Sopenharmony_ci		      if (check_v4_in_range (&remote_address, &config_line_address, netmask))
1293141cc406Sopenharmony_ci			{
1294141cc406Sopenharmony_ci			  access_ok = 1;
1295141cc406Sopenharmony_ci			  DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet %s/%s)\n",
1296141cc406Sopenharmony_ci			       remote_ip, config_line, netmask);
1297141cc406Sopenharmony_ci			}
1298141cc406Sopenharmony_ci		    }
1299141cc406Sopenharmony_ci		}
1300141cc406Sopenharmony_ci	      else
1301141cc406Sopenharmony_ci		{
1302141cc406Sopenharmony_ci		  DBG (DBG_DBG,
1303141cc406Sopenharmony_ci		       "check_host: inet_pton for `%s' failed\n",
1304141cc406Sopenharmony_ci		       config_line);
1305141cc406Sopenharmony_ci		  he = gethostbyname (config_line);
1306141cc406Sopenharmony_ci		  if (!he)
1307141cc406Sopenharmony_ci		    {
1308141cc406Sopenharmony_ci		      DBG (DBG_DBG,
1309141cc406Sopenharmony_ci			   "check_host: gethostbyname for `%s' failed: %s\n",
1310141cc406Sopenharmony_ci			   config_line, hstrerror (h_errno));
1311141cc406Sopenharmony_ci		      DBG (DBG_MSG, "check_host: entry isn't an IP address "
1312141cc406Sopenharmony_ci			   "and can't be found in DNS\n");
1313141cc406Sopenharmony_ci		      continue;
1314141cc406Sopenharmony_ci		    }
1315141cc406Sopenharmony_ci		  if (!inet_ntop (he->h_addrtype, he->h_addr_list[0],
1316141cc406Sopenharmony_ci				  text_addr, sizeof (text_addr)))
1317141cc406Sopenharmony_ci		    strcpy (text_addr, "[error]");
1318141cc406Sopenharmony_ci		  DBG (DBG_MSG,
1319141cc406Sopenharmony_ci		       "check_host: DNS lookup returns IP address: %s\n",
1320141cc406Sopenharmony_ci		       text_addr);
1321141cc406Sopenharmony_ci		  if (memcmp (&remote_address.s_addr,
1322141cc406Sopenharmony_ci			      he->h_addr_list[0], 4) == 0)
1323141cc406Sopenharmony_ci		    access_ok = 1;
1324141cc406Sopenharmony_ci		}
1325141cc406Sopenharmony_ci	    }
1326141cc406Sopenharmony_ci	}
1327141cc406Sopenharmony_ci      fclose (fp);
1328141cc406Sopenharmony_ci      if (access_ok)
1329141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1330141cc406Sopenharmony_ci    }
1331141cc406Sopenharmony_ci  return SANE_STATUS_ACCESS_DENIED;
1332141cc406Sopenharmony_ci}
1333141cc406Sopenharmony_ci
1334141cc406Sopenharmony_ci#endif /* SANED_USES_AF_INDEP */
1335141cc406Sopenharmony_ci
1336141cc406Sopenharmony_cistatic int
1337141cc406Sopenharmony_ciinit (Wire * w)
1338141cc406Sopenharmony_ci{
1339141cc406Sopenharmony_ci  SANE_Word word, be_version_code;
1340141cc406Sopenharmony_ci  SANE_Init_Reply reply;
1341141cc406Sopenharmony_ci  SANE_Status status;
1342141cc406Sopenharmony_ci  SANE_Init_Req req;
1343141cc406Sopenharmony_ci
1344141cc406Sopenharmony_ci  reset_watchdog ();
1345141cc406Sopenharmony_ci
1346141cc406Sopenharmony_ci  status = check_host (w->io.fd);
1347141cc406Sopenharmony_ci  if (status != SANE_STATUS_GOOD)
1348141cc406Sopenharmony_ci    {
1349141cc406Sopenharmony_ci      DBG (DBG_WARN, "init: access by host %s denied\n", remote_ip);
1350141cc406Sopenharmony_ci      return -1;
1351141cc406Sopenharmony_ci    }
1352141cc406Sopenharmony_ci  else
1353141cc406Sopenharmony_ci    DBG (DBG_MSG, "init: access granted\n");
1354141cc406Sopenharmony_ci
1355141cc406Sopenharmony_ci  sanei_w_set_dir (w, WIRE_DECODE);
1356141cc406Sopenharmony_ci  if (w->status)
1357141cc406Sopenharmony_ci    {
1358141cc406Sopenharmony_ci      DBG (DBG_ERR, "init: bad status after sanei_w_set_dir: %d\n", w->status);
1359141cc406Sopenharmony_ci      return -1;
1360141cc406Sopenharmony_ci    }
1361141cc406Sopenharmony_ci
1362141cc406Sopenharmony_ci  sanei_w_word (w, &word);	/* decode procedure number */
1363141cc406Sopenharmony_ci  if (w->status || word != SANE_NET_INIT)
1364141cc406Sopenharmony_ci    {
1365141cc406Sopenharmony_ci      DBG (DBG_ERR, "init: bad status=%d or procnum=%d\n",
1366141cc406Sopenharmony_ci	   w->status, word);
1367141cc406Sopenharmony_ci      return -1;
1368141cc406Sopenharmony_ci    }
1369141cc406Sopenharmony_ci
1370141cc406Sopenharmony_ci  sanei_w_init_req (w, &req);
1371141cc406Sopenharmony_ci  if (w->status)
1372141cc406Sopenharmony_ci    {
1373141cc406Sopenharmony_ci      DBG (DBG_ERR, "init: bad status after sanei_w_init_req: %d\n", w->status);
1374141cc406Sopenharmony_ci      return -1;
1375141cc406Sopenharmony_ci    }
1376141cc406Sopenharmony_ci
1377141cc406Sopenharmony_ci  w->version = SANEI_NET_PROTOCOL_VERSION;
1378141cc406Sopenharmony_ci  if (req.username)
1379141cc406Sopenharmony_ci    default_username = strdup (req.username);
1380141cc406Sopenharmony_ci
1381141cc406Sopenharmony_ci  sanei_w_free (w, (WireCodecFunc) sanei_w_init_req, &req);
1382141cc406Sopenharmony_ci  if (w->status)
1383141cc406Sopenharmony_ci    {
1384141cc406Sopenharmony_ci      DBG (DBG_ERR, "init: bad status after sanei_w_free: %d\n", w->status);
1385141cc406Sopenharmony_ci      return -1;
1386141cc406Sopenharmony_ci    }
1387141cc406Sopenharmony_ci
1388141cc406Sopenharmony_ci  reply.version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR,
1389141cc406Sopenharmony_ci					  SANEI_NET_PROTOCOL_VERSION);
1390141cc406Sopenharmony_ci
1391141cc406Sopenharmony_ci  DBG (DBG_WARN, "init: access granted to %s@%s\n",
1392141cc406Sopenharmony_ci       default_username, remote_ip);
1393141cc406Sopenharmony_ci
1394141cc406Sopenharmony_ci  if (status == SANE_STATUS_GOOD)
1395141cc406Sopenharmony_ci    {
1396141cc406Sopenharmony_ci      status = sane_init (&be_version_code, auth_callback);
1397141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
1398141cc406Sopenharmony_ci	DBG (DBG_ERR, "init: failed to initialize backend (%s)\n",
1399141cc406Sopenharmony_ci	     sane_strstatus (status));
1400141cc406Sopenharmony_ci
1401141cc406Sopenharmony_ci      if (SANE_VERSION_MAJOR (be_version_code) != V_MAJOR)
1402141cc406Sopenharmony_ci	{
1403141cc406Sopenharmony_ci	  DBG (DBG_ERR,
1404141cc406Sopenharmony_ci	       "init: unexpected backend major version %d (expected %d)\n",
1405141cc406Sopenharmony_ci	       SANE_VERSION_MAJOR (be_version_code), V_MAJOR);
1406141cc406Sopenharmony_ci	  status = SANE_STATUS_INVAL;
1407141cc406Sopenharmony_ci	}
1408141cc406Sopenharmony_ci    }
1409141cc406Sopenharmony_ci  reply.status = status;
1410141cc406Sopenharmony_ci  if (status != SANE_STATUS_GOOD)
1411141cc406Sopenharmony_ci    reply.version_code = 0;
1412141cc406Sopenharmony_ci  sanei_w_reply (w, (WireCodecFunc) sanei_w_init_reply, &reply);
1413141cc406Sopenharmony_ci
1414141cc406Sopenharmony_ci  if (w->status || status != SANE_STATUS_GOOD)
1415141cc406Sopenharmony_ci    return -1;
1416141cc406Sopenharmony_ci
1417141cc406Sopenharmony_ci  return 0;
1418141cc406Sopenharmony_ci}
1419141cc406Sopenharmony_ci
1420141cc406Sopenharmony_ci#ifdef SANED_USES_AF_INDEP
1421141cc406Sopenharmony_cistatic int
1422141cc406Sopenharmony_cistart_scan (Wire * w, int h, SANE_Start_Reply * reply)
1423141cc406Sopenharmony_ci{
1424141cc406Sopenharmony_ci  union {
1425141cc406Sopenharmony_ci    struct sockaddr_storage ss;
1426141cc406Sopenharmony_ci    struct sockaddr sa;
1427141cc406Sopenharmony_ci    struct sockaddr_in sin;
1428141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1429141cc406Sopenharmony_ci    struct sockaddr_in6 sin6;
1430141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1431141cc406Sopenharmony_ci  } data_addr;
1432141cc406Sopenharmony_ci  struct sockaddr_in *sin;
1433141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1434141cc406Sopenharmony_ci  struct sockaddr_in6 *sin6;
1435141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1436141cc406Sopenharmony_ci  SANE_Handle be_handle;
1437141cc406Sopenharmony_ci  int fd, len;
1438141cc406Sopenharmony_ci  in_port_t data_port;
1439141cc406Sopenharmony_ci  int ret = -1;
1440141cc406Sopenharmony_ci
1441141cc406Sopenharmony_ci  be_handle = handle[h].handle;
1442141cc406Sopenharmony_ci
1443141cc406Sopenharmony_ci  len = sizeof (data_addr.ss);
1444141cc406Sopenharmony_ci  if (getsockname (w->io.fd, &data_addr.sa, (socklen_t *) &len) < 0)
1445141cc406Sopenharmony_ci    {
1446141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to obtain socket address (%s)\n",
1447141cc406Sopenharmony_ci	   strerror (errno));
1448141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1449141cc406Sopenharmony_ci      return -1;
1450141cc406Sopenharmony_ci    }
1451141cc406Sopenharmony_ci
1452141cc406Sopenharmony_ci  fd = socket (SS_FAMILY(data_addr.ss), SOCK_STREAM, 0);
1453141cc406Sopenharmony_ci  if (fd < 0)
1454141cc406Sopenharmony_ci    {
1455141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to obtain data socket (%s)\n",
1456141cc406Sopenharmony_ci	   strerror (errno));
1457141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1458141cc406Sopenharmony_ci      return -1;
1459141cc406Sopenharmony_ci    }
1460141cc406Sopenharmony_ci
1461141cc406Sopenharmony_ci  switch (SS_FAMILY(data_addr.ss))
1462141cc406Sopenharmony_ci    {
1463141cc406Sopenharmony_ci      case AF_INET:
1464141cc406Sopenharmony_ci	sin = &data_addr.sin;
1465141cc406Sopenharmony_ci	break;
1466141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1467141cc406Sopenharmony_ci      case AF_INET6:
1468141cc406Sopenharmony_ci	sin6 = &data_addr.sin6;
1469141cc406Sopenharmony_ci	break;
1470141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1471141cc406Sopenharmony_ci      default:
1472141cc406Sopenharmony_ci	break;
1473141cc406Sopenharmony_ci    }
1474141cc406Sopenharmony_ci
1475141cc406Sopenharmony_ci  /* Try to bind a port between data_port_lo and data_port_hi for the data connection */
1476141cc406Sopenharmony_ci  for (data_port = data_port_lo; data_port <= data_port_hi; data_port++)
1477141cc406Sopenharmony_ci    {
1478141cc406Sopenharmony_ci      switch (SS_FAMILY(data_addr.ss))
1479141cc406Sopenharmony_ci        {
1480141cc406Sopenharmony_ci          case AF_INET:
1481141cc406Sopenharmony_ci            sin->sin_port = htons(data_port);
1482141cc406Sopenharmony_ci            break;
1483141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1484141cc406Sopenharmony_ci          case AF_INET6:
1485141cc406Sopenharmony_ci            sin6->sin6_port = htons(data_port);
1486141cc406Sopenharmony_ci            break;
1487141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1488141cc406Sopenharmony_ci          default:
1489141cc406Sopenharmony_ci            break;
1490141cc406Sopenharmony_ci       }
1491141cc406Sopenharmony_ci
1492141cc406Sopenharmony_ci      DBG (DBG_INFO, "start_scan: trying to bind data port %d\n", data_port);
1493141cc406Sopenharmony_ci
1494141cc406Sopenharmony_ci      ret = bind (fd, &data_addr.sa, len);
1495141cc406Sopenharmony_ci      if (ret == 0)
1496141cc406Sopenharmony_ci        break;
1497141cc406Sopenharmony_ci    }
1498141cc406Sopenharmony_ci
1499141cc406Sopenharmony_ci  if (ret < 0)
1500141cc406Sopenharmony_ci    {
1501141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to bind address (%s)\n",
1502141cc406Sopenharmony_ci	   strerror (errno));
1503141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1504141cc406Sopenharmony_ci      return -1;
1505141cc406Sopenharmony_ci    }
1506141cc406Sopenharmony_ci
1507141cc406Sopenharmony_ci  if (listen (fd, 1) < 0)
1508141cc406Sopenharmony_ci    {
1509141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to make socket listen (%s)\n",
1510141cc406Sopenharmony_ci	   strerror (errno));
1511141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1512141cc406Sopenharmony_ci      return -1;
1513141cc406Sopenharmony_ci    }
1514141cc406Sopenharmony_ci
1515141cc406Sopenharmony_ci  if (getsockname (fd, &data_addr.sa, (socklen_t *) &len) < 0)
1516141cc406Sopenharmony_ci    {
1517141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to obtain socket address (%s)\n",
1518141cc406Sopenharmony_ci	   strerror (errno));
1519141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1520141cc406Sopenharmony_ci      return -1;
1521141cc406Sopenharmony_ci    }
1522141cc406Sopenharmony_ci
1523141cc406Sopenharmony_ci  switch (SS_FAMILY(data_addr.ss))
1524141cc406Sopenharmony_ci    {
1525141cc406Sopenharmony_ci      case AF_INET:
1526141cc406Sopenharmony_ci	sin = &data_addr.sin;
1527141cc406Sopenharmony_ci	reply->port = ntohs (sin->sin_port);
1528141cc406Sopenharmony_ci	break;
1529141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
1530141cc406Sopenharmony_ci      case AF_INET6:
1531141cc406Sopenharmony_ci	sin6 = &data_addr.sin6;
1532141cc406Sopenharmony_ci	reply->port = ntohs (sin6->sin6_port);
1533141cc406Sopenharmony_ci	break;
1534141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
1535141cc406Sopenharmony_ci      default:
1536141cc406Sopenharmony_ci	break;
1537141cc406Sopenharmony_ci    }
1538141cc406Sopenharmony_ci
1539141cc406Sopenharmony_ci  DBG (DBG_MSG, "start_scan: using port %d for data\n", reply->port);
1540141cc406Sopenharmony_ci
1541141cc406Sopenharmony_ci  reply->status = sane_start (be_handle);
1542141cc406Sopenharmony_ci  if (reply->status == SANE_STATUS_GOOD)
1543141cc406Sopenharmony_ci    {
1544141cc406Sopenharmony_ci      handle[h].scanning = 1;
1545141cc406Sopenharmony_ci      handle[h].docancel = 0;
1546141cc406Sopenharmony_ci    }
1547141cc406Sopenharmony_ci
1548141cc406Sopenharmony_ci  return fd;
1549141cc406Sopenharmony_ci}
1550141cc406Sopenharmony_ci
1551141cc406Sopenharmony_ci#else /* !SANED_USES_AF_INDEP */
1552141cc406Sopenharmony_ci
1553141cc406Sopenharmony_cistatic int
1554141cc406Sopenharmony_cistart_scan (Wire * w, int h, SANE_Start_Reply * reply)
1555141cc406Sopenharmony_ci{
1556141cc406Sopenharmony_ci  struct sockaddr_in sin;
1557141cc406Sopenharmony_ci  SANE_Handle be_handle;
1558141cc406Sopenharmony_ci  int fd, len;
1559141cc406Sopenharmony_ci  in_port_t data_port;
1560141cc406Sopenharmony_ci  int ret;
1561141cc406Sopenharmony_ci
1562141cc406Sopenharmony_ci  be_handle = handle[h].handle;
1563141cc406Sopenharmony_ci
1564141cc406Sopenharmony_ci  len = sizeof (sin);
1565141cc406Sopenharmony_ci  if (getsockname (w->io.fd, (struct sockaddr *) &sin, (socklen_t *) &len) < 0)
1566141cc406Sopenharmony_ci    {
1567141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to obtain socket address (%s)\n",
1568141cc406Sopenharmony_ci	   strerror (errno));
1569141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1570141cc406Sopenharmony_ci      return -1;
1571141cc406Sopenharmony_ci    }
1572141cc406Sopenharmony_ci
1573141cc406Sopenharmony_ci  fd = socket (AF_INET, SOCK_STREAM, 0);
1574141cc406Sopenharmony_ci  if (fd < 0)
1575141cc406Sopenharmony_ci    {
1576141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to obtain data socket (%s)\n",
1577141cc406Sopenharmony_ci	   strerror (errno));
1578141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1579141cc406Sopenharmony_ci      return -1;
1580141cc406Sopenharmony_ci    }
1581141cc406Sopenharmony_ci
1582141cc406Sopenharmony_ci  /* Try to bind a port between data_port_lo and data_port_hi for the data connection */
1583141cc406Sopenharmony_ci  for (data_port = data_port_lo; data_port <= data_port_hi; data_port++)
1584141cc406Sopenharmony_ci    {
1585141cc406Sopenharmony_ci      sin.sin_port = htons(data_port);
1586141cc406Sopenharmony_ci
1587141cc406Sopenharmony_ci      DBG(DBG_INFO, "start_scan: trying to bind data port %d\n", data_port);
1588141cc406Sopenharmony_ci
1589141cc406Sopenharmony_ci      ret = bind (fd, (struct sockaddr *) &sin, len);
1590141cc406Sopenharmony_ci      if (ret == 0)
1591141cc406Sopenharmony_ci        break;
1592141cc406Sopenharmony_ci    }
1593141cc406Sopenharmony_ci
1594141cc406Sopenharmony_ci  if (ret < 0)
1595141cc406Sopenharmony_ci    {
1596141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to bind address (%s)\n",
1597141cc406Sopenharmony_ci	   strerror (errno));
1598141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1599141cc406Sopenharmony_ci      return -1;
1600141cc406Sopenharmony_ci    }
1601141cc406Sopenharmony_ci
1602141cc406Sopenharmony_ci  if (listen (fd, 1) < 0)
1603141cc406Sopenharmony_ci    {
1604141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to make socket listen (%s)\n",
1605141cc406Sopenharmony_ci	   strerror (errno));
1606141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1607141cc406Sopenharmony_ci      return -1;
1608141cc406Sopenharmony_ci    }
1609141cc406Sopenharmony_ci
1610141cc406Sopenharmony_ci  if (getsockname (fd, (struct sockaddr *) &sin, (socklen_t *) &len) < 0)
1611141cc406Sopenharmony_ci    {
1612141cc406Sopenharmony_ci      DBG (DBG_ERR, "start_scan: failed to obtain socket address (%s)\n",
1613141cc406Sopenharmony_ci	   strerror (errno));
1614141cc406Sopenharmony_ci      reply->status = SANE_STATUS_IO_ERROR;
1615141cc406Sopenharmony_ci      return -1;
1616141cc406Sopenharmony_ci    }
1617141cc406Sopenharmony_ci
1618141cc406Sopenharmony_ci  reply->port = ntohs (sin.sin_port);
1619141cc406Sopenharmony_ci
1620141cc406Sopenharmony_ci  DBG (DBG_MSG, "start_scan: using port %d for data\n", reply->port);
1621141cc406Sopenharmony_ci
1622141cc406Sopenharmony_ci  reply->status = sane_start (be_handle);
1623141cc406Sopenharmony_ci  if (reply->status == SANE_STATUS_GOOD)
1624141cc406Sopenharmony_ci    {
1625141cc406Sopenharmony_ci      handle[h].scanning = 1;
1626141cc406Sopenharmony_ci      handle[h].docancel = 0;
1627141cc406Sopenharmony_ci    }
1628141cc406Sopenharmony_ci
1629141cc406Sopenharmony_ci  return fd;
1630141cc406Sopenharmony_ci}
1631141cc406Sopenharmony_ci#endif /* SANED_USES_AF_INDEP */
1632141cc406Sopenharmony_ci
1633141cc406Sopenharmony_cistatic int
1634141cc406Sopenharmony_cistore_reclen (SANE_Byte * buf, size_t buf_size, int i, size_t reclen)
1635141cc406Sopenharmony_ci{
1636141cc406Sopenharmony_ci  buf[i++] = (reclen >> 24) & 0xff;
1637141cc406Sopenharmony_ci  if (i >= (int) buf_size)
1638141cc406Sopenharmony_ci    i = 0;
1639141cc406Sopenharmony_ci  buf[i++] = (reclen >> 16) & 0xff;
1640141cc406Sopenharmony_ci  if (i >= (int) buf_size)
1641141cc406Sopenharmony_ci    i = 0;
1642141cc406Sopenharmony_ci  buf[i++] = (reclen >> 8) & 0xff;
1643141cc406Sopenharmony_ci  if (i >= (int) buf_size)
1644141cc406Sopenharmony_ci    i = 0;
1645141cc406Sopenharmony_ci  buf[i++] = (reclen >> 0) & 0xff;
1646141cc406Sopenharmony_ci  if (i >= (int) buf_size)
1647141cc406Sopenharmony_ci    i = 0;
1648141cc406Sopenharmony_ci  return i;
1649141cc406Sopenharmony_ci}
1650141cc406Sopenharmony_ci
1651141cc406Sopenharmony_cistatic void
1652141cc406Sopenharmony_cido_scan (Wire * w, int h, int data_fd)
1653141cc406Sopenharmony_ci{
1654141cc406Sopenharmony_ci  int num_fds, be_fd = -1, reader, writer, bytes_in_buf, status_dirty = 0;
1655141cc406Sopenharmony_ci  SANE_Handle be_handle = handle[h].handle;
1656141cc406Sopenharmony_ci  struct timeval tv, *timeout = 0;
1657141cc406Sopenharmony_ci  fd_set rd_set, rd_mask, wr_set, wr_mask;
1658141cc406Sopenharmony_ci  SANE_Byte buf[8192];
1659141cc406Sopenharmony_ci  SANE_Status status;
1660141cc406Sopenharmony_ci  long int nwritten;
1661141cc406Sopenharmony_ci  SANE_Int length;
1662141cc406Sopenharmony_ci  size_t nbytes;
1663141cc406Sopenharmony_ci
1664141cc406Sopenharmony_ci  DBG (3, "do_scan: start\n");
1665141cc406Sopenharmony_ci
1666141cc406Sopenharmony_ci  FD_ZERO (&rd_mask);
1667141cc406Sopenharmony_ci  FD_SET (w->io.fd, &rd_mask);
1668141cc406Sopenharmony_ci  num_fds = w->io.fd + 1;
1669141cc406Sopenharmony_ci
1670141cc406Sopenharmony_ci  FD_ZERO (&wr_mask);
1671141cc406Sopenharmony_ci  FD_SET (data_fd, &wr_mask);
1672141cc406Sopenharmony_ci  if (data_fd >= num_fds)
1673141cc406Sopenharmony_ci    num_fds = data_fd + 1;
1674141cc406Sopenharmony_ci
1675141cc406Sopenharmony_ci  sane_set_io_mode (be_handle, SANE_TRUE);
1676141cc406Sopenharmony_ci  if (sane_get_select_fd (be_handle, &be_fd) == SANE_STATUS_GOOD)
1677141cc406Sopenharmony_ci    {
1678141cc406Sopenharmony_ci      FD_SET (be_fd, &rd_mask);
1679141cc406Sopenharmony_ci      if (be_fd >= num_fds)
1680141cc406Sopenharmony_ci	num_fds = be_fd + 1;
1681141cc406Sopenharmony_ci    }
1682141cc406Sopenharmony_ci  else
1683141cc406Sopenharmony_ci    {
1684141cc406Sopenharmony_ci      memset (&tv, 0, sizeof (tv));
1685141cc406Sopenharmony_ci      timeout = &tv;
1686141cc406Sopenharmony_ci    }
1687141cc406Sopenharmony_ci
1688141cc406Sopenharmony_ci  status = SANE_STATUS_GOOD;
1689141cc406Sopenharmony_ci  reader = writer = bytes_in_buf = 0;
1690141cc406Sopenharmony_ci  do
1691141cc406Sopenharmony_ci    {
1692141cc406Sopenharmony_ci      rd_set = rd_mask;
1693141cc406Sopenharmony_ci      wr_set = wr_mask;
1694141cc406Sopenharmony_ci      if (select (num_fds, &rd_set, &wr_set, 0, timeout) < 0)
1695141cc406Sopenharmony_ci	{
1696141cc406Sopenharmony_ci	  if (be_fd >= 0 && errno == EBADF)
1697141cc406Sopenharmony_ci	    {
1698141cc406Sopenharmony_ci	      /* This normally happens when a backend closes a select
1699141cc406Sopenharmony_ci		 filedescriptor when reaching the end of file.  So
1700141cc406Sopenharmony_ci		 pass back this status to the client: */
1701141cc406Sopenharmony_ci	      FD_CLR (be_fd, &rd_mask);
1702141cc406Sopenharmony_ci	      be_fd = -1;
1703141cc406Sopenharmony_ci	      /* only set status_dirty if EOF hasn't been already detected */
1704141cc406Sopenharmony_ci	      if (status == SANE_STATUS_GOOD)
1705141cc406Sopenharmony_ci		status_dirty = 1;
1706141cc406Sopenharmony_ci	      status = SANE_STATUS_EOF;
1707141cc406Sopenharmony_ci	      DBG (DBG_INFO, "do_scan: select_fd was closed --> EOF\n");
1708141cc406Sopenharmony_ci	      continue;
1709141cc406Sopenharmony_ci	    }
1710141cc406Sopenharmony_ci	  else
1711141cc406Sopenharmony_ci	    {
1712141cc406Sopenharmony_ci	      status = SANE_STATUS_IO_ERROR;
1713141cc406Sopenharmony_ci	      DBG (DBG_ERR, "do_scan: select failed (%s)\n", strerror (errno));
1714141cc406Sopenharmony_ci	      break;
1715141cc406Sopenharmony_ci	    }
1716141cc406Sopenharmony_ci	}
1717141cc406Sopenharmony_ci
1718141cc406Sopenharmony_ci      if (bytes_in_buf)
1719141cc406Sopenharmony_ci	{
1720141cc406Sopenharmony_ci	  if (FD_ISSET (data_fd, &wr_set))
1721141cc406Sopenharmony_ci	    {
1722141cc406Sopenharmony_ci	      if (bytes_in_buf > 0)
1723141cc406Sopenharmony_ci		{
1724141cc406Sopenharmony_ci		  /* write more input data */
1725141cc406Sopenharmony_ci		  nbytes = bytes_in_buf;
1726141cc406Sopenharmony_ci		  if (writer + nbytes > sizeof (buf))
1727141cc406Sopenharmony_ci		    nbytes = sizeof (buf) - writer;
1728141cc406Sopenharmony_ci		  DBG (DBG_INFO,
1729141cc406Sopenharmony_ci		       "do_scan: trying to write %d bytes to client\n",
1730141cc406Sopenharmony_ci		       nbytes);
1731141cc406Sopenharmony_ci		  nwritten = write (data_fd, buf + writer, nbytes);
1732141cc406Sopenharmony_ci		  DBG (DBG_INFO,
1733141cc406Sopenharmony_ci		       "do_scan: wrote %ld bytes to client\n", nwritten);
1734141cc406Sopenharmony_ci		  if (nwritten < 0)
1735141cc406Sopenharmony_ci		    {
1736141cc406Sopenharmony_ci		      DBG (DBG_ERR, "do_scan: write failed (%s)\n",
1737141cc406Sopenharmony_ci			   strerror (errno));
1738141cc406Sopenharmony_ci		      status = SANE_STATUS_CANCELLED;
1739141cc406Sopenharmony_ci	              handle[h].docancel = 1;
1740141cc406Sopenharmony_ci		      break;
1741141cc406Sopenharmony_ci		    }
1742141cc406Sopenharmony_ci		  bytes_in_buf -= nwritten;
1743141cc406Sopenharmony_ci		  writer += nwritten;
1744141cc406Sopenharmony_ci		  if (writer == sizeof (buf))
1745141cc406Sopenharmony_ci		    writer = 0;
1746141cc406Sopenharmony_ci		}
1747141cc406Sopenharmony_ci	    }
1748141cc406Sopenharmony_ci	}
1749141cc406Sopenharmony_ci      else if (status == SANE_STATUS_GOOD
1750141cc406Sopenharmony_ci	       && (timeout || FD_ISSET (be_fd, &rd_set)))
1751141cc406Sopenharmony_ci	{
1752141cc406Sopenharmony_ci	  int i;
1753141cc406Sopenharmony_ci
1754141cc406Sopenharmony_ci	  /* get more input data */
1755141cc406Sopenharmony_ci
1756141cc406Sopenharmony_ci	  /* reserve 4 bytes to store the length of the data record: */
1757141cc406Sopenharmony_ci	  i = reader;
1758141cc406Sopenharmony_ci	  reader += 4;
1759141cc406Sopenharmony_ci	  if (reader >= (int) sizeof (buf))
1760141cc406Sopenharmony_ci	    reader -= sizeof(buf);
1761141cc406Sopenharmony_ci
1762141cc406Sopenharmony_ci	  assert (bytes_in_buf == 0);
1763141cc406Sopenharmony_ci	  nbytes = sizeof (buf) - 4;
1764141cc406Sopenharmony_ci	  if (reader + nbytes > sizeof (buf))
1765141cc406Sopenharmony_ci	    nbytes = sizeof (buf) - reader;
1766141cc406Sopenharmony_ci
1767141cc406Sopenharmony_ci	  DBG (DBG_INFO,
1768141cc406Sopenharmony_ci	       "do_scan: trying to read %d bytes from scanner\n", nbytes);
1769141cc406Sopenharmony_ci	  status = sane_read (be_handle, buf + reader, nbytes, &length);
1770141cc406Sopenharmony_ci	  DBG (DBG_INFO,
1771141cc406Sopenharmony_ci	       "do_scan: read %d bytes from scanner\n", length);
1772141cc406Sopenharmony_ci
1773141cc406Sopenharmony_ci	  reset_watchdog ();
1774141cc406Sopenharmony_ci
1775141cc406Sopenharmony_ci	  reader += length;
1776141cc406Sopenharmony_ci	  if (reader >= (int) sizeof (buf))
1777141cc406Sopenharmony_ci	    reader = 0;
1778141cc406Sopenharmony_ci	  bytes_in_buf += length + 4;
1779141cc406Sopenharmony_ci
1780141cc406Sopenharmony_ci	  if (status != SANE_STATUS_GOOD)
1781141cc406Sopenharmony_ci	    {
1782141cc406Sopenharmony_ci	      reader = i;	/* restore reader index */
1783141cc406Sopenharmony_ci	      status_dirty = 1;
1784141cc406Sopenharmony_ci	      DBG (DBG_MSG,
1785141cc406Sopenharmony_ci		   "do_scan: status = `%s'\n", sane_strstatus(status));
1786141cc406Sopenharmony_ci	    }
1787141cc406Sopenharmony_ci	  else
1788141cc406Sopenharmony_ci	    store_reclen (buf, sizeof (buf), i, length);
1789141cc406Sopenharmony_ci	}
1790141cc406Sopenharmony_ci
1791141cc406Sopenharmony_ci      if (status_dirty && sizeof (buf) - bytes_in_buf >= 5)
1792141cc406Sopenharmony_ci	{
1793141cc406Sopenharmony_ci	  status_dirty = 0;
1794141cc406Sopenharmony_ci	  reader = store_reclen (buf, sizeof (buf), reader, 0xffffffff);
1795141cc406Sopenharmony_ci	  buf[reader] = status;
1796141cc406Sopenharmony_ci	  bytes_in_buf += 5;
1797141cc406Sopenharmony_ci	  DBG (DBG_MSG, "do_scan: statuscode `%s' was added to buffer\n",
1798141cc406Sopenharmony_ci	       sane_strstatus(status));
1799141cc406Sopenharmony_ci	}
1800141cc406Sopenharmony_ci
1801141cc406Sopenharmony_ci      if (FD_ISSET (w->io.fd, &rd_set))
1802141cc406Sopenharmony_ci	{
1803141cc406Sopenharmony_ci	  DBG (DBG_MSG,
1804141cc406Sopenharmony_ci	       "do_scan: processing RPC request on fd %d\n", w->io.fd);
1805141cc406Sopenharmony_ci	  if(process_request (w) < 0)
1806141cc406Sopenharmony_ci	    handle[h].docancel = 1;
1807141cc406Sopenharmony_ci
1808141cc406Sopenharmony_ci	  if (handle[h].docancel)
1809141cc406Sopenharmony_ci	    break;
1810141cc406Sopenharmony_ci	}
1811141cc406Sopenharmony_ci    }
1812141cc406Sopenharmony_ci  while (status == SANE_STATUS_GOOD || bytes_in_buf > 0 || status_dirty);
1813141cc406Sopenharmony_ci  DBG (DBG_MSG, "do_scan: done, status=%s\n", sane_strstatus (status));
1814141cc406Sopenharmony_ci
1815141cc406Sopenharmony_ci  if(handle[h].docancel)
1816141cc406Sopenharmony_ci    sane_cancel (handle[h].handle);
1817141cc406Sopenharmony_ci
1818141cc406Sopenharmony_ci  handle[h].docancel = 0;
1819141cc406Sopenharmony_ci  handle[h].scanning = 0;
1820141cc406Sopenharmony_ci}
1821141cc406Sopenharmony_ci
1822141cc406Sopenharmony_cistatic int
1823141cc406Sopenharmony_ciprocess_request (Wire * w)
1824141cc406Sopenharmony_ci{
1825141cc406Sopenharmony_ci  SANE_Handle be_handle;
1826141cc406Sopenharmony_ci  SANE_Word h, word;
1827141cc406Sopenharmony_ci  int i;
1828141cc406Sopenharmony_ci
1829141cc406Sopenharmony_ci  DBG (DBG_DBG, "process_request: waiting for request\n");
1830141cc406Sopenharmony_ci  sanei_w_set_dir (w, WIRE_DECODE);
1831141cc406Sopenharmony_ci  sanei_w_word (w, &word);	/* decode procedure number */
1832141cc406Sopenharmony_ci
1833141cc406Sopenharmony_ci  if (w->status)
1834141cc406Sopenharmony_ci    {
1835141cc406Sopenharmony_ci      DBG (DBG_ERR,
1836141cc406Sopenharmony_ci	   "process_request: bad status %d\n", w->status);
1837141cc406Sopenharmony_ci      return -1;
1838141cc406Sopenharmony_ci    }
1839141cc406Sopenharmony_ci
1840141cc406Sopenharmony_ci  current_request = word;
1841141cc406Sopenharmony_ci
1842141cc406Sopenharmony_ci  DBG (DBG_MSG, "process_request: got request %d\n", current_request);
1843141cc406Sopenharmony_ci
1844141cc406Sopenharmony_ci  switch (current_request)
1845141cc406Sopenharmony_ci    {
1846141cc406Sopenharmony_ci    case SANE_NET_GET_DEVICES:
1847141cc406Sopenharmony_ci      {
1848141cc406Sopenharmony_ci	SANE_Get_Devices_Reply reply;
1849141cc406Sopenharmony_ci
1850141cc406Sopenharmony_ci	reply.status =
1851141cc406Sopenharmony_ci	  sane_get_devices ((const SANE_Device ***) &reply.device_list,
1852141cc406Sopenharmony_ci			    SANE_TRUE);
1853141cc406Sopenharmony_ci	sanei_w_reply (w, (WireCodecFunc) sanei_w_get_devices_reply, &reply);
1854141cc406Sopenharmony_ci      }
1855141cc406Sopenharmony_ci      break;
1856141cc406Sopenharmony_ci
1857141cc406Sopenharmony_ci    case SANE_NET_OPEN:
1858141cc406Sopenharmony_ci      {
1859141cc406Sopenharmony_ci	SANE_Open_Reply reply;
1860141cc406Sopenharmony_ci	SANE_Handle be_handle;
1861141cc406Sopenharmony_ci	SANE_String name, resource;
1862141cc406Sopenharmony_ci
1863141cc406Sopenharmony_ci	sanei_w_string (w, &name);
1864141cc406Sopenharmony_ci	if (w->status)
1865141cc406Sopenharmony_ci	  {
1866141cc406Sopenharmony_ci	    DBG (DBG_ERR,
1867141cc406Sopenharmony_ci		 "process_request: (open) error while decoding args (%s)\n",
1868141cc406Sopenharmony_ci		 strerror (w->status));
1869141cc406Sopenharmony_ci	    return 1;
1870141cc406Sopenharmony_ci	  }
1871141cc406Sopenharmony_ci
1872141cc406Sopenharmony_ci	if (!name)
1873141cc406Sopenharmony_ci	  {
1874141cc406Sopenharmony_ci	    DBG (DBG_ERR, "process_request: (open) device_name == NULL\n");
1875141cc406Sopenharmony_ci	    reply.status = SANE_STATUS_INVAL;
1876141cc406Sopenharmony_ci	    sanei_w_reply (w, (WireCodecFunc) sanei_w_open_reply, &reply);
1877141cc406Sopenharmony_ci	    return 1;
1878141cc406Sopenharmony_ci	  }
1879141cc406Sopenharmony_ci
1880141cc406Sopenharmony_ci	can_authorize = 1;
1881141cc406Sopenharmony_ci
1882141cc406Sopenharmony_ci	resource = strdup (name);
1883141cc406Sopenharmony_ci
1884141cc406Sopenharmony_ci	if (strlen(resource) == 0) {
1885141cc406Sopenharmony_ci
1886141cc406Sopenharmony_ci	  const SANE_Device **device_list;
1887141cc406Sopenharmony_ci
1888141cc406Sopenharmony_ci	  DBG(DBG_DBG, "process_request: (open) strlen(resource) == 0\n");
1889141cc406Sopenharmony_ci	  free (resource);
1890141cc406Sopenharmony_ci
1891141cc406Sopenharmony_ci	  if ((i = sane_get_devices (&device_list, SANE_TRUE)) !=
1892141cc406Sopenharmony_ci	      SANE_STATUS_GOOD)
1893141cc406Sopenharmony_ci	    {
1894141cc406Sopenharmony_ci	      DBG(DBG_ERR, "process_request: (open) sane_get_devices failed\n");
1895141cc406Sopenharmony_ci	      memset (&reply, 0, sizeof (reply));
1896141cc406Sopenharmony_ci	      reply.status = i;
1897141cc406Sopenharmony_ci	      sanei_w_reply (w, (WireCodecFunc) sanei_w_open_reply, &reply);
1898141cc406Sopenharmony_ci	      break;
1899141cc406Sopenharmony_ci	    }
1900141cc406Sopenharmony_ci
1901141cc406Sopenharmony_ci	  if ((device_list == NULL) || (device_list[0] == NULL))
1902141cc406Sopenharmony_ci	    {
1903141cc406Sopenharmony_ci	      DBG(DBG_ERR, "process_request: (open) device_list[0] == 0\n");
1904141cc406Sopenharmony_ci	      memset (&reply, 0, sizeof (reply));
1905141cc406Sopenharmony_ci	      reply.status = SANE_STATUS_INVAL;
1906141cc406Sopenharmony_ci	      sanei_w_reply (w, (WireCodecFunc) sanei_w_open_reply, &reply);
1907141cc406Sopenharmony_ci	      break;
1908141cc406Sopenharmony_ci	    }
1909141cc406Sopenharmony_ci
1910141cc406Sopenharmony_ci	  resource = strdup (device_list[0]->name);
1911141cc406Sopenharmony_ci	}
1912141cc406Sopenharmony_ci
1913141cc406Sopenharmony_ci	if (strchr (resource, ':'))
1914141cc406Sopenharmony_ci	  *(strchr (resource, ':')) = 0;
1915141cc406Sopenharmony_ci
1916141cc406Sopenharmony_ci	if (sanei_authorize (resource, "saned", auth_callback) !=
1917141cc406Sopenharmony_ci	    SANE_STATUS_GOOD)
1918141cc406Sopenharmony_ci	  {
1919141cc406Sopenharmony_ci	    DBG (DBG_ERR, "process_request: access to resource `%s' denied\n",
1920141cc406Sopenharmony_ci		 resource);
1921141cc406Sopenharmony_ci	    free (resource);
1922141cc406Sopenharmony_ci	    memset (&reply, 0, sizeof (reply));	/* avoid leaking bits */
1923141cc406Sopenharmony_ci	    reply.status = SANE_STATUS_ACCESS_DENIED;
1924141cc406Sopenharmony_ci	  }
1925141cc406Sopenharmony_ci	else
1926141cc406Sopenharmony_ci	  {
1927141cc406Sopenharmony_ci	    DBG (DBG_MSG, "process_request: access to resource `%s' granted\n",
1928141cc406Sopenharmony_ci		 resource);
1929141cc406Sopenharmony_ci	    free (resource);
1930141cc406Sopenharmony_ci	    memset (&reply, 0, sizeof (reply));	/* avoid leaking bits */
1931141cc406Sopenharmony_ci	    reply.status = sane_open (name, &be_handle);
1932141cc406Sopenharmony_ci	    DBG (DBG_MSG, "process_request: sane_open returned: %s\n",
1933141cc406Sopenharmony_ci		 sane_strstatus (reply.status));
1934141cc406Sopenharmony_ci	  }
1935141cc406Sopenharmony_ci
1936141cc406Sopenharmony_ci	if (reply.status == SANE_STATUS_GOOD)
1937141cc406Sopenharmony_ci	  {
1938141cc406Sopenharmony_ci	    h = get_free_handle ();
1939141cc406Sopenharmony_ci	    if (h < 0)
1940141cc406Sopenharmony_ci	      reply.status = SANE_STATUS_NO_MEM;
1941141cc406Sopenharmony_ci	    else
1942141cc406Sopenharmony_ci	      {
1943141cc406Sopenharmony_ci		handle[h].handle = be_handle;
1944141cc406Sopenharmony_ci		reply.handle = h;
1945141cc406Sopenharmony_ci	      }
1946141cc406Sopenharmony_ci	  }
1947141cc406Sopenharmony_ci
1948141cc406Sopenharmony_ci	can_authorize = 0;
1949141cc406Sopenharmony_ci
1950141cc406Sopenharmony_ci	sanei_w_reply (w, (WireCodecFunc) sanei_w_open_reply, &reply);
1951141cc406Sopenharmony_ci	sanei_w_free (w, (WireCodecFunc) sanei_w_string, &name);
1952141cc406Sopenharmony_ci      }
1953141cc406Sopenharmony_ci      break;
1954141cc406Sopenharmony_ci
1955141cc406Sopenharmony_ci    case SANE_NET_CLOSE:
1956141cc406Sopenharmony_ci      {
1957141cc406Sopenharmony_ci	SANE_Word ack = 0;
1958141cc406Sopenharmony_ci
1959141cc406Sopenharmony_ci	h = decode_handle (w, "close");
1960141cc406Sopenharmony_ci	close_handle (h);
1961141cc406Sopenharmony_ci	sanei_w_reply (w, (WireCodecFunc) sanei_w_word, &ack);
1962141cc406Sopenharmony_ci      }
1963141cc406Sopenharmony_ci      break;
1964141cc406Sopenharmony_ci
1965141cc406Sopenharmony_ci    case SANE_NET_GET_OPTION_DESCRIPTORS:
1966141cc406Sopenharmony_ci      {
1967141cc406Sopenharmony_ci	SANE_Option_Descriptor_Array opt;
1968141cc406Sopenharmony_ci
1969141cc406Sopenharmony_ci	h = decode_handle (w, "get_option_descriptors");
1970141cc406Sopenharmony_ci	if (h < 0)
1971141cc406Sopenharmony_ci	  return 1;
1972141cc406Sopenharmony_ci	be_handle = handle[h].handle;
1973141cc406Sopenharmony_ci	sane_control_option (be_handle, 0, SANE_ACTION_GET_VALUE,
1974141cc406Sopenharmony_ci			     &opt.num_options, 0);
1975141cc406Sopenharmony_ci
1976141cc406Sopenharmony_ci	opt.desc = malloc (opt.num_options * sizeof (opt.desc[0]));
1977141cc406Sopenharmony_ci	for (i = 0; i < opt.num_options; ++i)
1978141cc406Sopenharmony_ci	  opt.desc[i] = (SANE_Option_Descriptor *)
1979141cc406Sopenharmony_ci	    sane_get_option_descriptor (be_handle, i);
1980141cc406Sopenharmony_ci
1981141cc406Sopenharmony_ci	sanei_w_reply (w,(WireCodecFunc) sanei_w_option_descriptor_array,
1982141cc406Sopenharmony_ci		       &opt);
1983141cc406Sopenharmony_ci
1984141cc406Sopenharmony_ci	free (opt.desc);
1985141cc406Sopenharmony_ci      }
1986141cc406Sopenharmony_ci      break;
1987141cc406Sopenharmony_ci
1988141cc406Sopenharmony_ci    case SANE_NET_CONTROL_OPTION:
1989141cc406Sopenharmony_ci      {
1990141cc406Sopenharmony_ci	SANE_Control_Option_Req req;
1991141cc406Sopenharmony_ci	SANE_Control_Option_Reply reply;
1992141cc406Sopenharmony_ci
1993141cc406Sopenharmony_ci	sanei_w_control_option_req (w, &req);
1994141cc406Sopenharmony_ci	if (w->status || (unsigned) req.handle >= (unsigned) num_handles
1995141cc406Sopenharmony_ci	    || !handle[req.handle].inuse)
1996141cc406Sopenharmony_ci	  {
1997141cc406Sopenharmony_ci	    DBG (DBG_ERR,
1998141cc406Sopenharmony_ci		 "process_request: (control_option) "
1999141cc406Sopenharmony_ci		 "error while decoding args h=%d (%s)\n"
2000141cc406Sopenharmony_ci		 , req.handle, strerror (w->status));
2001141cc406Sopenharmony_ci	    return 1;
2002141cc406Sopenharmony_ci	  }
2003141cc406Sopenharmony_ci
2004141cc406Sopenharmony_ci        /* Addresses CVE-2017-6318 (#315576, Debian BTS #853804) */
2005141cc406Sopenharmony_ci        /* This is done here (rather than in sanei/sanei_wire.c where
2006141cc406Sopenharmony_ci         * it should be done) to minimize scope of impact and amount
2007141cc406Sopenharmony_ci         * of code change.
2008141cc406Sopenharmony_ci         */
2009141cc406Sopenharmony_ci        if (w->direction == WIRE_DECODE
2010141cc406Sopenharmony_ci            && req.value_type == SANE_TYPE_STRING
2011141cc406Sopenharmony_ci            && req.action     == SANE_ACTION_GET_VALUE)
2012141cc406Sopenharmony_ci          {
2013141cc406Sopenharmony_ci            if (req.value)
2014141cc406Sopenharmony_ci              {
2015141cc406Sopenharmony_ci                /* FIXME: If req.value contains embedded NUL
2016141cc406Sopenharmony_ci                 *        characters, this is wrong but we do not have
2017141cc406Sopenharmony_ci                 *        access to the amount of memory allocated in
2018141cc406Sopenharmony_ci                 *        sanei/sanei_wire.c at this point.
2019141cc406Sopenharmony_ci                 */
2020141cc406Sopenharmony_ci                w->allocated_memory -= (1 + strlen (req.value));
2021141cc406Sopenharmony_ci                free (req.value);
2022141cc406Sopenharmony_ci              }
2023141cc406Sopenharmony_ci            req.value = malloc (req.value_size);
2024141cc406Sopenharmony_ci            if (!req.value)
2025141cc406Sopenharmony_ci              {
2026141cc406Sopenharmony_ci                w->status = ENOMEM;
2027141cc406Sopenharmony_ci                DBG (DBG_ERR,
2028141cc406Sopenharmony_ci                     "process_request: (control_option) "
2029141cc406Sopenharmony_ci                     "h=%d (%s)\n", req.handle, strerror (w->status));
2030141cc406Sopenharmony_ci                return 1;
2031141cc406Sopenharmony_ci              }
2032141cc406Sopenharmony_ci            memset (req.value, 0, req.value_size);
2033141cc406Sopenharmony_ci            w->allocated_memory += req.value_size;
2034141cc406Sopenharmony_ci          }
2035141cc406Sopenharmony_ci
2036141cc406Sopenharmony_ci	can_authorize = 1;
2037141cc406Sopenharmony_ci
2038141cc406Sopenharmony_ci	memset (&reply, 0, sizeof (reply));	/* avoid leaking bits */
2039141cc406Sopenharmony_ci	be_handle = handle[req.handle].handle;
2040141cc406Sopenharmony_ci	reply.status = sane_control_option (be_handle, req.option,
2041141cc406Sopenharmony_ci					    req.action, req.value,
2042141cc406Sopenharmony_ci					    &reply.info);
2043141cc406Sopenharmony_ci	reply.value_type = req.value_type;
2044141cc406Sopenharmony_ci	reply.value_size = req.value_size;
2045141cc406Sopenharmony_ci	reply.value = req.value;
2046141cc406Sopenharmony_ci
2047141cc406Sopenharmony_ci	can_authorize = 0;
2048141cc406Sopenharmony_ci
2049141cc406Sopenharmony_ci	sanei_w_reply (w, (WireCodecFunc) sanei_w_control_option_reply,
2050141cc406Sopenharmony_ci		       &reply);
2051141cc406Sopenharmony_ci	sanei_w_free (w, (WireCodecFunc) sanei_w_control_option_req, &req);
2052141cc406Sopenharmony_ci      }
2053141cc406Sopenharmony_ci      break;
2054141cc406Sopenharmony_ci
2055141cc406Sopenharmony_ci    case SANE_NET_GET_PARAMETERS:
2056141cc406Sopenharmony_ci      {
2057141cc406Sopenharmony_ci	SANE_Get_Parameters_Reply reply;
2058141cc406Sopenharmony_ci
2059141cc406Sopenharmony_ci	h = decode_handle (w, "get_parameters");
2060141cc406Sopenharmony_ci	if (h < 0)
2061141cc406Sopenharmony_ci	  return 1;
2062141cc406Sopenharmony_ci	be_handle = handle[h].handle;
2063141cc406Sopenharmony_ci
2064141cc406Sopenharmony_ci	reply.status = sane_get_parameters (be_handle, &reply.params);
2065141cc406Sopenharmony_ci
2066141cc406Sopenharmony_ci	sanei_w_reply (w, (WireCodecFunc) sanei_w_get_parameters_reply,
2067141cc406Sopenharmony_ci		       &reply);
2068141cc406Sopenharmony_ci      }
2069141cc406Sopenharmony_ci      break;
2070141cc406Sopenharmony_ci
2071141cc406Sopenharmony_ci    case SANE_NET_START:
2072141cc406Sopenharmony_ci      {
2073141cc406Sopenharmony_ci	SANE_Start_Reply reply;
2074141cc406Sopenharmony_ci	int fd = -1, data_fd = -1;
2075141cc406Sopenharmony_ci
2076141cc406Sopenharmony_ci	h = decode_handle (w, "start");
2077141cc406Sopenharmony_ci	if (h < 0)
2078141cc406Sopenharmony_ci	  return 1;
2079141cc406Sopenharmony_ci
2080141cc406Sopenharmony_ci	memset (&reply, 0, sizeof (reply));	/* avoid leaking bits */
2081141cc406Sopenharmony_ci	reply.byte_order = SANE_NET_LITTLE_ENDIAN;
2082141cc406Sopenharmony_ci	if (byte_order.w != 1)
2083141cc406Sopenharmony_ci	  reply.byte_order = SANE_NET_BIG_ENDIAN;
2084141cc406Sopenharmony_ci
2085141cc406Sopenharmony_ci	if (handle[h].scanning)
2086141cc406Sopenharmony_ci	  reply.status = SANE_STATUS_DEVICE_BUSY;
2087141cc406Sopenharmony_ci	else
2088141cc406Sopenharmony_ci	  fd = start_scan (w, h, &reply);
2089141cc406Sopenharmony_ci
2090141cc406Sopenharmony_ci	sanei_w_reply (w, (WireCodecFunc) sanei_w_start_reply, &reply);
2091141cc406Sopenharmony_ci
2092141cc406Sopenharmony_ci#ifdef SANED_USES_AF_INDEP
2093141cc406Sopenharmony_ci	if (reply.status == SANE_STATUS_GOOD)
2094141cc406Sopenharmony_ci	  {
2095141cc406Sopenharmony_ci	    struct sockaddr_storage ss;
2096141cc406Sopenharmony_ci	    char text_addr[64];
2097141cc406Sopenharmony_ci	    int len;
2098141cc406Sopenharmony_ci	    int error;
2099141cc406Sopenharmony_ci	    struct pollfd fds[1];
2100141cc406Sopenharmony_ci	    int ret;
2101141cc406Sopenharmony_ci
2102141cc406Sopenharmony_ci	    fds->fd = fd;
2103141cc406Sopenharmony_ci	    fds->events = POLLIN;
2104141cc406Sopenharmony_ci
2105141cc406Sopenharmony_ci	    DBG (DBG_MSG, "process_request: waiting 4s for data connection\n");
2106141cc406Sopenharmony_ci	    if(data_connect_timeout)
2107141cc406Sopenharmony_ci	      {
2108141cc406Sopenharmony_ci	        while (1)
2109141cc406Sopenharmony_ci	          {
2110141cc406Sopenharmony_ci	            ret = poll (fds, 1, data_connect_timeout);
2111141cc406Sopenharmony_ci	            if (ret < 0)
2112141cc406Sopenharmony_ci	              {
2113141cc406Sopenharmony_ci	                if (errno == EINTR)
2114141cc406Sopenharmony_ci	                  continue;
2115141cc406Sopenharmony_ci	                else
2116141cc406Sopenharmony_ci	                  {
2117141cc406Sopenharmony_ci	                    DBG (DBG_ERR, "run_standalone: poll failed: %s\n",
2118141cc406Sopenharmony_ci	                         strerror (errno));
2119141cc406Sopenharmony_ci	                  }
2120141cc406Sopenharmony_ci	                break;
2121141cc406Sopenharmony_ci	              }
2122141cc406Sopenharmony_ci	            break;
2123141cc406Sopenharmony_ci	          }
2124141cc406Sopenharmony_ci	      }
2125141cc406Sopenharmony_ci	    else
2126141cc406Sopenharmony_ci	      ret = 0;
2127141cc406Sopenharmony_ci	    if(ret >= 0)
2128141cc406Sopenharmony_ci	      data_fd = accept (fd, 0, 0);
2129141cc406Sopenharmony_ci	    close (fd);
2130141cc406Sopenharmony_ci
2131141cc406Sopenharmony_ci	    /* Get address of remote host */
2132141cc406Sopenharmony_ci	    len = sizeof (ss);
2133141cc406Sopenharmony_ci	    if (getpeername (data_fd, (struct sockaddr *) &ss, (socklen_t *) &len) < 0)
2134141cc406Sopenharmony_ci	      {
2135141cc406Sopenharmony_ci		DBG (DBG_ERR, "process_request: getpeername failed: %s\n",
2136141cc406Sopenharmony_ci		     strerror (errno));
2137141cc406Sopenharmony_ci		return 1;
2138141cc406Sopenharmony_ci	      }
2139141cc406Sopenharmony_ci
2140141cc406Sopenharmony_ci	    error = getnameinfo ((struct sockaddr *) &ss, len, text_addr,
2141141cc406Sopenharmony_ci				 sizeof (text_addr), NULL, 0, NI_NUMERICHOST);
2142141cc406Sopenharmony_ci	    if (error)
2143141cc406Sopenharmony_ci	      {
2144141cc406Sopenharmony_ci		DBG (DBG_ERR, "process_request: getnameinfo failed: %s\n",
2145141cc406Sopenharmony_ci		     gai_strerror (error));
2146141cc406Sopenharmony_ci		return 1;
2147141cc406Sopenharmony_ci	      }
2148141cc406Sopenharmony_ci
2149141cc406Sopenharmony_ci	    DBG (DBG_MSG, "process_request: access to data port from %s\n",
2150141cc406Sopenharmony_ci		 text_addr);
2151141cc406Sopenharmony_ci
2152141cc406Sopenharmony_ci	    if (strcmp (text_addr, remote_ip) != 0)
2153141cc406Sopenharmony_ci	      {
2154141cc406Sopenharmony_ci		DBG (DBG_ERR, "process_request: however, only %s is authorized\n",
2155141cc406Sopenharmony_ci		     text_addr);
2156141cc406Sopenharmony_ci		DBG (DBG_ERR, "process_request: configuration problem or attack?\n");
2157141cc406Sopenharmony_ci		close (data_fd);
2158141cc406Sopenharmony_ci		data_fd = -1;
2159141cc406Sopenharmony_ci		return -1;
2160141cc406Sopenharmony_ci	      }
2161141cc406Sopenharmony_ci
2162141cc406Sopenharmony_ci#else /* !SANED_USES_AF_INDEP */
2163141cc406Sopenharmony_ci
2164141cc406Sopenharmony_ci	if (reply.status == SANE_STATUS_GOOD)
2165141cc406Sopenharmony_ci	  {
2166141cc406Sopenharmony_ci	    struct sockaddr_in sin;
2167141cc406Sopenharmony_ci	    int len;
2168141cc406Sopenharmony_ci	    int ret;
2169141cc406Sopenharmony_ci	    struct pollfd fds[1];
2170141cc406Sopenharmony_ci
2171141cc406Sopenharmony_ci	    fds->fd = fd;
2172141cc406Sopenharmony_ci	    fds->events = POLLIN;
2173141cc406Sopenharmony_ci
2174141cc406Sopenharmony_ci	    DBG (DBG_MSG, "process_request: waiting for data connection\n");
2175141cc406Sopenharmony_ci	    if(data_connect_timeout)
2176141cc406Sopenharmony_ci	      {
2177141cc406Sopenharmony_ci	        while (1)
2178141cc406Sopenharmony_ci	         {
2179141cc406Sopenharmony_ci	           ret = poll (fds, 1, data_connect_timeout);
2180141cc406Sopenharmony_ci	           if (ret < 0)
2181141cc406Sopenharmony_ci	             {
2182141cc406Sopenharmony_ci	               if (errno == EINTR)
2183141cc406Sopenharmony_ci	                 continue;
2184141cc406Sopenharmony_ci	               else
2185141cc406Sopenharmony_ci	                 {
2186141cc406Sopenharmony_ci	                   DBG (DBG_ERR, "run_standalone: poll failed: %s\n", strerror (errno));
2187141cc406Sopenharmony_ci	                 }
2188141cc406Sopenharmony_ci	               break;
2189141cc406Sopenharmony_ci	             }
2190141cc406Sopenharmony_ci	           break;
2191141cc406Sopenharmony_ci	         }
2192141cc406Sopenharmony_ci	      }
2193141cc406Sopenharmony_ci	    else
2194141cc406Sopenharmony_ci	      ret = 0;
2195141cc406Sopenharmony_ci	    if(ret >= 0)
2196141cc406Sopenharmony_ci	      data_fd = accept (fd, 0, 0);
2197141cc406Sopenharmony_ci
2198141cc406Sopenharmony_ci	    close (fd);
2199141cc406Sopenharmony_ci
2200141cc406Sopenharmony_ci	    /* Get address of remote host */
2201141cc406Sopenharmony_ci	    len = sizeof (sin);
2202141cc406Sopenharmony_ci	    if (getpeername (data_fd, (struct sockaddr *) &sin,
2203141cc406Sopenharmony_ci			     (socklen_t *) &len) < 0)
2204141cc406Sopenharmony_ci	      {
2205141cc406Sopenharmony_ci		DBG (DBG_ERR, "process_request: getpeername failed: %s\n",
2206141cc406Sopenharmony_ci		     strerror (errno));
2207141cc406Sopenharmony_ci		return 1;
2208141cc406Sopenharmony_ci	      }
2209141cc406Sopenharmony_ci
2210141cc406Sopenharmony_ci	    if (memcmp (&remote_address, &sin.sin_addr,
2211141cc406Sopenharmony_ci			sizeof (remote_address)) != 0)
2212141cc406Sopenharmony_ci	      {
2213141cc406Sopenharmony_ci		DBG (DBG_ERR,
2214141cc406Sopenharmony_ci		     "process_request: access to data port from %s\n",
2215141cc406Sopenharmony_ci		     inet_ntoa (sin.sin_addr));
2216141cc406Sopenharmony_ci		DBG (DBG_ERR,
2217141cc406Sopenharmony_ci		     "process_request: however, only %s is authorized\n",
2218141cc406Sopenharmony_ci		     inet_ntoa (remote_address));
2219141cc406Sopenharmony_ci		DBG (DBG_ERR,
2220141cc406Sopenharmony_ci		     "process_request: configuration problem or attack?\n");
2221141cc406Sopenharmony_ci		close (data_fd);
2222141cc406Sopenharmony_ci		data_fd = -1;
2223141cc406Sopenharmony_ci		return -1;
2224141cc406Sopenharmony_ci	      }
2225141cc406Sopenharmony_ci	    else
2226141cc406Sopenharmony_ci	      DBG (DBG_MSG, "process_request: access to data port from %s\n",
2227141cc406Sopenharmony_ci		   inet_ntoa (sin.sin_addr));
2228141cc406Sopenharmony_ci#endif /* SANED_USES_AF_INDEP */
2229141cc406Sopenharmony_ci
2230141cc406Sopenharmony_ci	    if (data_fd < 0)
2231141cc406Sopenharmony_ci	      {
2232141cc406Sopenharmony_ci		sane_cancel (handle[h].handle);
2233141cc406Sopenharmony_ci		handle[h].scanning = 0;
2234141cc406Sopenharmony_ci		handle[h].docancel = 0;
2235141cc406Sopenharmony_ci		DBG (DBG_ERR, "process_request: accept failed! (%s)\n",
2236141cc406Sopenharmony_ci		     strerror (errno));
2237141cc406Sopenharmony_ci		return 1;
2238141cc406Sopenharmony_ci	      }
2239141cc406Sopenharmony_ci	    fcntl (data_fd, F_SETFL, 1);      /* set non-blocking */
2240141cc406Sopenharmony_ci	    shutdown (data_fd, 0);
2241141cc406Sopenharmony_ci	    do_scan (w, h, data_fd);
2242141cc406Sopenharmony_ci	    close (data_fd);
2243141cc406Sopenharmony_ci	  }
2244141cc406Sopenharmony_ci      }
2245141cc406Sopenharmony_ci      break;
2246141cc406Sopenharmony_ci
2247141cc406Sopenharmony_ci    case SANE_NET_CANCEL:
2248141cc406Sopenharmony_ci      {
2249141cc406Sopenharmony_ci	SANE_Word ack = 0;
2250141cc406Sopenharmony_ci
2251141cc406Sopenharmony_ci	h = decode_handle (w, "cancel");
2252141cc406Sopenharmony_ci	if (h >= 0)
2253141cc406Sopenharmony_ci	  {
2254141cc406Sopenharmony_ci	    sane_cancel (handle[h].handle);
2255141cc406Sopenharmony_ci	    handle[h].docancel = 1;
2256141cc406Sopenharmony_ci	  }
2257141cc406Sopenharmony_ci	sanei_w_reply (w, (WireCodecFunc) sanei_w_word, &ack);
2258141cc406Sopenharmony_ci      }
2259141cc406Sopenharmony_ci      break;
2260141cc406Sopenharmony_ci
2261141cc406Sopenharmony_ci    case SANE_NET_EXIT:
2262141cc406Sopenharmony_ci      return -1;
2263141cc406Sopenharmony_ci      break;
2264141cc406Sopenharmony_ci
2265141cc406Sopenharmony_ci    case SANE_NET_INIT:
2266141cc406Sopenharmony_ci    case SANE_NET_AUTHORIZE:
2267141cc406Sopenharmony_ci    default:
2268141cc406Sopenharmony_ci      DBG (DBG_ERR,
2269141cc406Sopenharmony_ci	   "process_request: received unexpected procedure number %d\n",
2270141cc406Sopenharmony_ci	   current_request);
2271141cc406Sopenharmony_ci      return -1;
2272141cc406Sopenharmony_ci    }
2273141cc406Sopenharmony_ci
2274141cc406Sopenharmony_ci  return 0;
2275141cc406Sopenharmony_ci}
2276141cc406Sopenharmony_ci
2277141cc406Sopenharmony_ci
2278141cc406Sopenharmony_cistatic int
2279141cc406Sopenharmony_ciwait_child (pid_t pid, int *status, int options)
2280141cc406Sopenharmony_ci{
2281141cc406Sopenharmony_ci  struct saned_child *c;
2282141cc406Sopenharmony_ci  struct saned_child *p = NULL;
2283141cc406Sopenharmony_ci  int ret;
2284141cc406Sopenharmony_ci
2285141cc406Sopenharmony_ci  ret = waitpid(pid, status, options);
2286141cc406Sopenharmony_ci
2287141cc406Sopenharmony_ci  if (ret <= 0)
2288141cc406Sopenharmony_ci    return ret;
2289141cc406Sopenharmony_ci
2290141cc406Sopenharmony_ci#if WITH_AVAHI
2291141cc406Sopenharmony_ci  if ((avahi_pid > 0) && (ret == avahi_pid))
2292141cc406Sopenharmony_ci    {
2293141cc406Sopenharmony_ci      avahi_pid = -1;
2294141cc406Sopenharmony_ci      numchildren--;
2295141cc406Sopenharmony_ci      return ret;
2296141cc406Sopenharmony_ci    }
2297141cc406Sopenharmony_ci#endif /* WITH_AVAHI */
2298141cc406Sopenharmony_ci
2299141cc406Sopenharmony_ci  for (c = children; (c != NULL) && (c->next != NULL); p = c, c = c->next)
2300141cc406Sopenharmony_ci    {
2301141cc406Sopenharmony_ci      if (c->pid == ret)
2302141cc406Sopenharmony_ci	{
2303141cc406Sopenharmony_ci	  if (c == children)
2304141cc406Sopenharmony_ci	    children = c->next;
2305141cc406Sopenharmony_ci	  else if (p != NULL)
2306141cc406Sopenharmony_ci	    p->next = c->next;
2307141cc406Sopenharmony_ci
2308141cc406Sopenharmony_ci	  free(c);
2309141cc406Sopenharmony_ci
2310141cc406Sopenharmony_ci	  numchildren--;
2311141cc406Sopenharmony_ci
2312141cc406Sopenharmony_ci	  break;
2313141cc406Sopenharmony_ci	}
2314141cc406Sopenharmony_ci    }
2315141cc406Sopenharmony_ci
2316141cc406Sopenharmony_ci  return ret;
2317141cc406Sopenharmony_ci}
2318141cc406Sopenharmony_ci
2319141cc406Sopenharmony_cistatic int
2320141cc406Sopenharmony_ciadd_child (pid_t pid)
2321141cc406Sopenharmony_ci{
2322141cc406Sopenharmony_ci  struct saned_child *c;
2323141cc406Sopenharmony_ci
2324141cc406Sopenharmony_ci  c = (struct saned_child *) malloc (sizeof(struct saned_child));
2325141cc406Sopenharmony_ci
2326141cc406Sopenharmony_ci  if (c == NULL)
2327141cc406Sopenharmony_ci    {
2328141cc406Sopenharmony_ci      DBG (DBG_ERR, "add_child: out of memory\n");
2329141cc406Sopenharmony_ci      return -1;
2330141cc406Sopenharmony_ci    }
2331141cc406Sopenharmony_ci
2332141cc406Sopenharmony_ci  c->pid = pid;
2333141cc406Sopenharmony_ci  c->next = children;
2334141cc406Sopenharmony_ci
2335141cc406Sopenharmony_ci  children = c;
2336141cc406Sopenharmony_ci
2337141cc406Sopenharmony_ci  return 0;
2338141cc406Sopenharmony_ci}
2339141cc406Sopenharmony_ci
2340141cc406Sopenharmony_ci
2341141cc406Sopenharmony_cistatic void
2342141cc406Sopenharmony_cihandle_connection (int fd)
2343141cc406Sopenharmony_ci{
2344141cc406Sopenharmony_ci#ifdef TCP_NODELAY
2345141cc406Sopenharmony_ci  int on = 1;
2346141cc406Sopenharmony_ci  int level = -1;
2347141cc406Sopenharmony_ci#endif
2348141cc406Sopenharmony_ci
2349141cc406Sopenharmony_ci  DBG (DBG_DBG, "handle_connection: processing client connection\n");
2350141cc406Sopenharmony_ci
2351141cc406Sopenharmony_ci  wire.io.fd = fd;
2352141cc406Sopenharmony_ci
2353141cc406Sopenharmony_ci  signal (SIGALRM, quit);
2354141cc406Sopenharmony_ci  signal (SIGPIPE, quit);
2355141cc406Sopenharmony_ci
2356141cc406Sopenharmony_ci#ifdef TCP_NODELAY
2357141cc406Sopenharmony_ci# ifdef SOL_TCP
2358141cc406Sopenharmony_ci  level = SOL_TCP;
2359141cc406Sopenharmony_ci# else /* !SOL_TCP */
2360141cc406Sopenharmony_ci  /* Look up the protocol level in the protocols database. */
2361141cc406Sopenharmony_ci  {
2362141cc406Sopenharmony_ci    struct protoent *p;
2363141cc406Sopenharmony_ci    p = getprotobyname ("tcp");
2364141cc406Sopenharmony_ci    if (p == 0)
2365141cc406Sopenharmony_ci      {
2366141cc406Sopenharmony_ci	DBG (DBG_WARN, "handle_connection: cannot look up `tcp' protocol number");
2367141cc406Sopenharmony_ci      }
2368141cc406Sopenharmony_ci    else
2369141cc406Sopenharmony_ci      level = p->p_proto;
2370141cc406Sopenharmony_ci  }
2371141cc406Sopenharmony_ci# endif	/* SOL_TCP */
2372141cc406Sopenharmony_ci  if (level == -1
2373141cc406Sopenharmony_ci      || setsockopt (wire.io.fd, level, TCP_NODELAY, &on, sizeof (on)))
2374141cc406Sopenharmony_ci    DBG (DBG_WARN, "handle_connection: failed to put socket in TCP_NODELAY mode (%s)",
2375141cc406Sopenharmony_ci	 strerror (errno));
2376141cc406Sopenharmony_ci#endif /* !TCP_NODELAY */
2377141cc406Sopenharmony_ci
2378141cc406Sopenharmony_ci  if (init (&wire) < 0)
2379141cc406Sopenharmony_ci    return;
2380141cc406Sopenharmony_ci
2381141cc406Sopenharmony_ci  while (1)
2382141cc406Sopenharmony_ci    {
2383141cc406Sopenharmony_ci      reset_watchdog ();
2384141cc406Sopenharmony_ci      if (process_request (&wire) < 0)
2385141cc406Sopenharmony_ci	break;
2386141cc406Sopenharmony_ci    }
2387141cc406Sopenharmony_ci}
2388141cc406Sopenharmony_ci
2389141cc406Sopenharmony_cistatic void
2390141cc406Sopenharmony_cihandle_client (int fd)
2391141cc406Sopenharmony_ci{
2392141cc406Sopenharmony_ci  pid_t pid;
2393141cc406Sopenharmony_ci  int i;
2394141cc406Sopenharmony_ci
2395141cc406Sopenharmony_ci  DBG (DBG_DBG, "handle_client: spawning child process\n");
2396141cc406Sopenharmony_ci
2397141cc406Sopenharmony_ci  pid = fork ();
2398141cc406Sopenharmony_ci  if (pid == 0)
2399141cc406Sopenharmony_ci    {
2400141cc406Sopenharmony_ci      /* child */
2401141cc406Sopenharmony_ci      if (log_to_syslog)
2402141cc406Sopenharmony_ci	closelog();
2403141cc406Sopenharmony_ci
2404141cc406Sopenharmony_ci      for (i = 3; i < fd; i++)
2405141cc406Sopenharmony_ci	close(i);
2406141cc406Sopenharmony_ci
2407141cc406Sopenharmony_ci      if (log_to_syslog)
2408141cc406Sopenharmony_ci	openlog ("saned", LOG_PID | LOG_CONS, LOG_DAEMON);
2409141cc406Sopenharmony_ci
2410141cc406Sopenharmony_ci      handle_connection (fd);
2411141cc406Sopenharmony_ci      quit (0);
2412141cc406Sopenharmony_ci    }
2413141cc406Sopenharmony_ci  else if (pid > 0)
2414141cc406Sopenharmony_ci    {
2415141cc406Sopenharmony_ci      /* parent */
2416141cc406Sopenharmony_ci      add_child (pid);
2417141cc406Sopenharmony_ci      close(fd);
2418141cc406Sopenharmony_ci    }
2419141cc406Sopenharmony_ci  else
2420141cc406Sopenharmony_ci    {
2421141cc406Sopenharmony_ci      /* FAILED */
2422141cc406Sopenharmony_ci      DBG (DBG_ERR, "handle_client: fork() failed: %s\n", strerror (errno));
2423141cc406Sopenharmony_ci      close(fd);
2424141cc406Sopenharmony_ci    }
2425141cc406Sopenharmony_ci}
2426141cc406Sopenharmony_ci
2427141cc406Sopenharmony_cistatic void
2428141cc406Sopenharmony_cibail_out (int error)
2429141cc406Sopenharmony_ci{
2430141cc406Sopenharmony_ci  DBG (DBG_ERR, "%sbailing out, waiting for children...\n", (error) ? "FATAL ERROR; " : "");
2431141cc406Sopenharmony_ci
2432141cc406Sopenharmony_ci#if WITH_AVAHI
2433141cc406Sopenharmony_ci  if (avahi_pid > 0)
2434141cc406Sopenharmony_ci    kill (avahi_pid, SIGTERM);
2435141cc406Sopenharmony_ci#endif /* WITH_AVAHI */
2436141cc406Sopenharmony_ci
2437141cc406Sopenharmony_ci  while (numchildren > 0)
2438141cc406Sopenharmony_ci    wait_child (-1, NULL, 0);
2439141cc406Sopenharmony_ci
2440141cc406Sopenharmony_ci  DBG (DBG_ERR, "bail_out: all children exited\n");
2441141cc406Sopenharmony_ci
2442141cc406Sopenharmony_ci  exit ((error) ? 1 : 0);
2443141cc406Sopenharmony_ci}
2444141cc406Sopenharmony_ci
2445141cc406Sopenharmony_civoid
2446141cc406Sopenharmony_cisig_int_term_handler (int signum);
2447141cc406Sopenharmony_ci
2448141cc406Sopenharmony_civoid
2449141cc406Sopenharmony_cisig_int_term_handler (int signum)
2450141cc406Sopenharmony_ci{
2451141cc406Sopenharmony_ci  /* unused */
2452141cc406Sopenharmony_ci  (void) signum;
2453141cc406Sopenharmony_ci
2454141cc406Sopenharmony_ci  signal (SIGINT, NULL);
2455141cc406Sopenharmony_ci  signal (SIGTERM, NULL);
2456141cc406Sopenharmony_ci
2457141cc406Sopenharmony_ci  bail_out (0);
2458141cc406Sopenharmony_ci}
2459141cc406Sopenharmony_ci
2460141cc406Sopenharmony_ci
2461141cc406Sopenharmony_ci#if WITH_AVAHI
2462141cc406Sopenharmony_cistatic void
2463141cc406Sopenharmony_cisaned_avahi (struct pollfd *fds, int nfds);
2464141cc406Sopenharmony_ci
2465141cc406Sopenharmony_cistatic void
2466141cc406Sopenharmony_cisaned_create_avahi_services (AvahiClient *c);
2467141cc406Sopenharmony_ci
2468141cc406Sopenharmony_cistatic void
2469141cc406Sopenharmony_cisaned_avahi_callback (AvahiClient *c, AvahiClientState state, void *userdata);
2470141cc406Sopenharmony_ci
2471141cc406Sopenharmony_cistatic void
2472141cc406Sopenharmony_cisaned_avahi_group_callback (AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata);
2473141cc406Sopenharmony_ci
2474141cc406Sopenharmony_ci
2475141cc406Sopenharmony_cistatic void
2476141cc406Sopenharmony_cisaned_avahi (struct pollfd *fds, int nfds)
2477141cc406Sopenharmony_ci{
2478141cc406Sopenharmony_ci  struct pollfd *fdp = NULL;
2479141cc406Sopenharmony_ci  int error;
2480141cc406Sopenharmony_ci
2481141cc406Sopenharmony_ci  avahi_pid = fork ();
2482141cc406Sopenharmony_ci
2483141cc406Sopenharmony_ci  if (avahi_pid > 0)
2484141cc406Sopenharmony_ci    {
2485141cc406Sopenharmony_ci      numchildren++;
2486141cc406Sopenharmony_ci      return;
2487141cc406Sopenharmony_ci    }
2488141cc406Sopenharmony_ci  else if (avahi_pid < 0)
2489141cc406Sopenharmony_ci    {
2490141cc406Sopenharmony_ci      DBG (DBG_ERR, "saned_avahi: could not spawn Avahi process: %s\n", strerror (errno));
2491141cc406Sopenharmony_ci      return;
2492141cc406Sopenharmony_ci    }
2493141cc406Sopenharmony_ci
2494141cc406Sopenharmony_ci  signal (SIGINT, NULL);
2495141cc406Sopenharmony_ci  signal (SIGTERM, NULL);
2496141cc406Sopenharmony_ci
2497141cc406Sopenharmony_ci  /* Close network fds */
2498141cc406Sopenharmony_ci  for (fdp = fds; nfds > 0; nfds--, fdp++)
2499141cc406Sopenharmony_ci    close (fdp->fd);
2500141cc406Sopenharmony_ci
2501141cc406Sopenharmony_ci  free(fds);
2502141cc406Sopenharmony_ci
2503141cc406Sopenharmony_ci  avahi_svc_name = avahi_strdup(SANED_NAME);
2504141cc406Sopenharmony_ci
2505141cc406Sopenharmony_ci  avahi_poll = avahi_simple_poll_new ();
2506141cc406Sopenharmony_ci  if (avahi_poll == NULL)
2507141cc406Sopenharmony_ci    {
2508141cc406Sopenharmony_ci      DBG (DBG_ERR, "saned_avahi: failed to create simple poll object\n");
2509141cc406Sopenharmony_ci      goto fail;
2510141cc406Sopenharmony_ci    }
2511141cc406Sopenharmony_ci
2512141cc406Sopenharmony_ci  avahi_client = avahi_client_new (avahi_simple_poll_get (avahi_poll), AVAHI_CLIENT_NO_FAIL, saned_avahi_callback, NULL, &error);
2513141cc406Sopenharmony_ci  if (avahi_client == NULL)
2514141cc406Sopenharmony_ci    {
2515141cc406Sopenharmony_ci      DBG (DBG_ERR, "saned_avahi: failed to create client: %s\n", avahi_strerror (error));
2516141cc406Sopenharmony_ci      goto fail;
2517141cc406Sopenharmony_ci    }
2518141cc406Sopenharmony_ci
2519141cc406Sopenharmony_ci  avahi_simple_poll_loop (avahi_poll);
2520141cc406Sopenharmony_ci
2521141cc406Sopenharmony_ci  DBG (DBG_INFO, "saned_avahi: poll loop exited\n");
2522141cc406Sopenharmony_ci
2523141cc406Sopenharmony_ci  exit(EXIT_SUCCESS);
2524141cc406Sopenharmony_ci
2525141cc406Sopenharmony_ci  /* NOT REACHED */
2526141cc406Sopenharmony_ci  return;
2527141cc406Sopenharmony_ci
2528141cc406Sopenharmony_ci fail:
2529141cc406Sopenharmony_ci  if (avahi_client)
2530141cc406Sopenharmony_ci    avahi_client_free (avahi_client);
2531141cc406Sopenharmony_ci
2532141cc406Sopenharmony_ci  if (avahi_poll)
2533141cc406Sopenharmony_ci    avahi_simple_poll_free (avahi_poll);
2534141cc406Sopenharmony_ci
2535141cc406Sopenharmony_ci  avahi_free (avahi_svc_name);
2536141cc406Sopenharmony_ci
2537141cc406Sopenharmony_ci  exit(EXIT_FAILURE);
2538141cc406Sopenharmony_ci}
2539141cc406Sopenharmony_ci
2540141cc406Sopenharmony_cistatic void
2541141cc406Sopenharmony_cisaned_avahi_group_callback (AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata)
2542141cc406Sopenharmony_ci{
2543141cc406Sopenharmony_ci  char *n;
2544141cc406Sopenharmony_ci
2545141cc406Sopenharmony_ci  /* unused */
2546141cc406Sopenharmony_ci  (void) userdata;
2547141cc406Sopenharmony_ci
2548141cc406Sopenharmony_ci  if ((!g) || (g != avahi_group))
2549141cc406Sopenharmony_ci    return;
2550141cc406Sopenharmony_ci
2551141cc406Sopenharmony_ci  switch (state)
2552141cc406Sopenharmony_ci    {
2553141cc406Sopenharmony_ci      case AVAHI_ENTRY_GROUP_ESTABLISHED:
2554141cc406Sopenharmony_ci	/* The entry group has been established successfully */
2555141cc406Sopenharmony_ci	DBG (DBG_INFO, "saned_avahi_group_callback: service '%s' successfully established\n", avahi_svc_name);
2556141cc406Sopenharmony_ci	break;
2557141cc406Sopenharmony_ci
2558141cc406Sopenharmony_ci      case AVAHI_ENTRY_GROUP_COLLISION:
2559141cc406Sopenharmony_ci	/* A service name collision with a remote service
2560141cc406Sopenharmony_ci	 * happened. Let's pick a new name */
2561141cc406Sopenharmony_ci	n = avahi_alternative_service_name (avahi_svc_name);
2562141cc406Sopenharmony_ci	avahi_free (avahi_svc_name);
2563141cc406Sopenharmony_ci	avahi_svc_name = n;
2564141cc406Sopenharmony_ci
2565141cc406Sopenharmony_ci	DBG (DBG_WARN, "saned_avahi_group_callback: service name collision, renaming service to '%s'\n", avahi_svc_name);
2566141cc406Sopenharmony_ci
2567141cc406Sopenharmony_ci	/* And recreate the services */
2568141cc406Sopenharmony_ci	saned_create_avahi_services (avahi_entry_group_get_client (g));
2569141cc406Sopenharmony_ci	break;
2570141cc406Sopenharmony_ci
2571141cc406Sopenharmony_ci      case AVAHI_ENTRY_GROUP_FAILURE :
2572141cc406Sopenharmony_ci	DBG (DBG_ERR, "saned_avahi_group_callback: entry group failure: %s\n", avahi_strerror (avahi_client_errno (avahi_entry_group_get_client (g))));
2573141cc406Sopenharmony_ci
2574141cc406Sopenharmony_ci	/* Some kind of failure happened while we were registering our services */
2575141cc406Sopenharmony_ci	avahi_simple_poll_quit (avahi_poll);
2576141cc406Sopenharmony_ci	break;
2577141cc406Sopenharmony_ci
2578141cc406Sopenharmony_ci      case AVAHI_ENTRY_GROUP_UNCOMMITED:
2579141cc406Sopenharmony_ci      case AVAHI_ENTRY_GROUP_REGISTERING:
2580141cc406Sopenharmony_ci	break;
2581141cc406Sopenharmony_ci    }
2582141cc406Sopenharmony_ci}
2583141cc406Sopenharmony_ci
2584141cc406Sopenharmony_cistatic void
2585141cc406Sopenharmony_cisaned_create_avahi_services (AvahiClient *c)
2586141cc406Sopenharmony_ci{
2587141cc406Sopenharmony_ci  char *n;
2588141cc406Sopenharmony_ci  char txt[32];
2589141cc406Sopenharmony_ci  AvahiProtocol proto;
2590141cc406Sopenharmony_ci  int ret;
2591141cc406Sopenharmony_ci
2592141cc406Sopenharmony_ci  if (!c)
2593141cc406Sopenharmony_ci    return;
2594141cc406Sopenharmony_ci
2595141cc406Sopenharmony_ci  if (!avahi_group)
2596141cc406Sopenharmony_ci    {
2597141cc406Sopenharmony_ci      avahi_group = avahi_entry_group_new (c, saned_avahi_group_callback, NULL);
2598141cc406Sopenharmony_ci      if (avahi_group == NULL)
2599141cc406Sopenharmony_ci	{
2600141cc406Sopenharmony_ci	  DBG (DBG_ERR, "saned_create_avahi_services: avahi_entry_group_new() failed: %s\n", avahi_strerror (avahi_client_errno (c)));
2601141cc406Sopenharmony_ci	  goto fail;
2602141cc406Sopenharmony_ci	}
2603141cc406Sopenharmony_ci    }
2604141cc406Sopenharmony_ci
2605141cc406Sopenharmony_ci  if (avahi_entry_group_is_empty (avahi_group))
2606141cc406Sopenharmony_ci    {
2607141cc406Sopenharmony_ci      DBG (DBG_INFO, "saned_create_avahi_services: adding service '%s'\n", avahi_svc_name);
2608141cc406Sopenharmony_ci
2609141cc406Sopenharmony_ci      snprintf(txt, sizeof (txt), "protovers=%x", SANE_VERSION_CODE (V_MAJOR, V_MINOR, SANEI_NET_PROTOCOL_VERSION));
2610141cc406Sopenharmony_ci
2611141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
2612141cc406Sopenharmony_ci      proto = AVAHI_PROTO_UNSPEC;
2613141cc406Sopenharmony_ci#else
2614141cc406Sopenharmony_ci      proto = AVAHI_PROTO_INET;
2615141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
2616141cc406Sopenharmony_ci
2617141cc406Sopenharmony_ci      ret = avahi_entry_group_add_service (avahi_group, AVAHI_IF_UNSPEC, proto, 0, avahi_svc_name, SANED_SERVICE_DNS, NULL, NULL, SANED_SERVICE_PORT, txt, NULL);
2618141cc406Sopenharmony_ci      if (ret < 0)
2619141cc406Sopenharmony_ci	{
2620141cc406Sopenharmony_ci	  if (ret == AVAHI_ERR_COLLISION)
2621141cc406Sopenharmony_ci	    {
2622141cc406Sopenharmony_ci	      n = avahi_alternative_service_name (avahi_svc_name);
2623141cc406Sopenharmony_ci	      avahi_free (avahi_svc_name);
2624141cc406Sopenharmony_ci	      avahi_svc_name = n;
2625141cc406Sopenharmony_ci
2626141cc406Sopenharmony_ci	      DBG (DBG_WARN, "saned_create_avahi_services: service name collision, renaming service to '%s'\n", avahi_svc_name);
2627141cc406Sopenharmony_ci
2628141cc406Sopenharmony_ci	      avahi_entry_group_reset (avahi_group);
2629141cc406Sopenharmony_ci
2630141cc406Sopenharmony_ci	      saned_create_avahi_services (c);
2631141cc406Sopenharmony_ci
2632141cc406Sopenharmony_ci	      return;
2633141cc406Sopenharmony_ci	    }
2634141cc406Sopenharmony_ci
2635141cc406Sopenharmony_ci	  DBG (DBG_ERR, "saned_create_avahi_services: failed to add %s service: %s\n", SANED_SERVICE_DNS, avahi_strerror (ret));
2636141cc406Sopenharmony_ci	  goto fail;
2637141cc406Sopenharmony_ci	}
2638141cc406Sopenharmony_ci
2639141cc406Sopenharmony_ci      /* Tell the server to register the service */
2640141cc406Sopenharmony_ci      ret = avahi_entry_group_commit (avahi_group);
2641141cc406Sopenharmony_ci      if (ret < 0)
2642141cc406Sopenharmony_ci	{
2643141cc406Sopenharmony_ci	  DBG (DBG_ERR, "saned_create_avahi_services: failed to commit entry group: %s\n", avahi_strerror (ret));
2644141cc406Sopenharmony_ci	  goto fail;
2645141cc406Sopenharmony_ci	}
2646141cc406Sopenharmony_ci    }
2647141cc406Sopenharmony_ci
2648141cc406Sopenharmony_ci  return;
2649141cc406Sopenharmony_ci
2650141cc406Sopenharmony_ci fail:
2651141cc406Sopenharmony_ci  avahi_simple_poll_quit (avahi_poll);
2652141cc406Sopenharmony_ci}
2653141cc406Sopenharmony_ci
2654141cc406Sopenharmony_cistatic void
2655141cc406Sopenharmony_cisaned_avahi_callback (AvahiClient *c, AvahiClientState state, void *userdata)
2656141cc406Sopenharmony_ci{
2657141cc406Sopenharmony_ci  int error;
2658141cc406Sopenharmony_ci
2659141cc406Sopenharmony_ci  /* unused */
2660141cc406Sopenharmony_ci  (void) userdata;
2661141cc406Sopenharmony_ci
2662141cc406Sopenharmony_ci  if (!c)
2663141cc406Sopenharmony_ci    return;
2664141cc406Sopenharmony_ci
2665141cc406Sopenharmony_ci  switch (state)
2666141cc406Sopenharmony_ci    {
2667141cc406Sopenharmony_ci      case AVAHI_CLIENT_CONNECTING:
2668141cc406Sopenharmony_ci	DBG (DBG_INFO, "saned_avahi_callback: AVAHI_CLIENT_CONNECTING\n");
2669141cc406Sopenharmony_ci	break;
2670141cc406Sopenharmony_ci
2671141cc406Sopenharmony_ci      case AVAHI_CLIENT_S_RUNNING:
2672141cc406Sopenharmony_ci	DBG (DBG_INFO, "saned_avahi_callback: AVAHI_CLIENT_S_RUNNING\n");
2673141cc406Sopenharmony_ci	saned_create_avahi_services (c);
2674141cc406Sopenharmony_ci	break;
2675141cc406Sopenharmony_ci
2676141cc406Sopenharmony_ci      case AVAHI_CLIENT_S_COLLISION:
2677141cc406Sopenharmony_ci	DBG (DBG_INFO, "saned_avahi_callback: AVAHI_CLIENT_S_COLLISION\n");
2678141cc406Sopenharmony_ci	if (avahi_group)
2679141cc406Sopenharmony_ci	  avahi_entry_group_reset (avahi_group);
2680141cc406Sopenharmony_ci	break;
2681141cc406Sopenharmony_ci
2682141cc406Sopenharmony_ci      case AVAHI_CLIENT_S_REGISTERING:
2683141cc406Sopenharmony_ci	DBG (DBG_INFO, "saned_avahi_callback: AVAHI_CLIENT_S_REGISTERING\n");
2684141cc406Sopenharmony_ci	if (avahi_group)
2685141cc406Sopenharmony_ci	  avahi_entry_group_reset (avahi_group);
2686141cc406Sopenharmony_ci	break;
2687141cc406Sopenharmony_ci
2688141cc406Sopenharmony_ci      case AVAHI_CLIENT_FAILURE:
2689141cc406Sopenharmony_ci	DBG (DBG_INFO, "saned_avahi_callback: AVAHI_CLIENT_FAILURE\n");
2690141cc406Sopenharmony_ci
2691141cc406Sopenharmony_ci	error = avahi_client_errno (c);
2692141cc406Sopenharmony_ci	if (error == AVAHI_ERR_DISCONNECTED)
2693141cc406Sopenharmony_ci	  {
2694141cc406Sopenharmony_ci	    DBG (DBG_INFO, "saned_avahi_callback: AVAHI_ERR_DISCONNECTED\n");
2695141cc406Sopenharmony_ci
2696141cc406Sopenharmony_ci	    /* Server disappeared - try to reconnect */
2697141cc406Sopenharmony_ci            avahi_client_free (avahi_client);
2698141cc406Sopenharmony_ci            avahi_client = NULL;
2699141cc406Sopenharmony_ci	    avahi_group = NULL;
2700141cc406Sopenharmony_ci
2701141cc406Sopenharmony_ci	    avahi_client = avahi_client_new (avahi_simple_poll_get (avahi_poll), AVAHI_CLIENT_NO_FAIL, saned_avahi_callback, NULL, &error);
2702141cc406Sopenharmony_ci	    if (avahi_client == NULL)
2703141cc406Sopenharmony_ci	      {
2704141cc406Sopenharmony_ci		DBG (DBG_ERR, "saned_avahi_callback: failed to create client: %s\n", avahi_strerror (error));
2705141cc406Sopenharmony_ci		avahi_simple_poll_quit (avahi_poll);
2706141cc406Sopenharmony_ci	      }
2707141cc406Sopenharmony_ci	  }
2708141cc406Sopenharmony_ci	else
2709141cc406Sopenharmony_ci	  {
2710141cc406Sopenharmony_ci	    /* Another error happened - game over */
2711141cc406Sopenharmony_ci	    DBG (DBG_ERR, "saned_avahi_callback: client failure: %s\n", avahi_strerror (error));
2712141cc406Sopenharmony_ci	    avahi_simple_poll_quit (avahi_poll);
2713141cc406Sopenharmony_ci	  }
2714141cc406Sopenharmony_ci	break;
2715141cc406Sopenharmony_ci    }
2716141cc406Sopenharmony_ci}
2717141cc406Sopenharmony_ci#endif /* WITH_AVAHI */
2718141cc406Sopenharmony_ci
2719141cc406Sopenharmony_ci
2720141cc406Sopenharmony_cistatic void
2721141cc406Sopenharmony_ciread_config (void)
2722141cc406Sopenharmony_ci{
2723141cc406Sopenharmony_ci  char config_line[PATH_MAX];
2724141cc406Sopenharmony_ci  const char *optval;
2725141cc406Sopenharmony_ci  char *endval;
2726141cc406Sopenharmony_ci  long val;
2727141cc406Sopenharmony_ci  FILE *fp;
2728141cc406Sopenharmony_ci  int len;
2729141cc406Sopenharmony_ci
2730141cc406Sopenharmony_ci  DBG (DBG_INFO, "read_config: searching for config file\n");
2731141cc406Sopenharmony_ci  fp = sanei_config_open (SANED_CONFIG_FILE);
2732141cc406Sopenharmony_ci  if (fp)
2733141cc406Sopenharmony_ci    {
2734141cc406Sopenharmony_ci      while (sanei_config_read (config_line, sizeof (config_line), fp))
2735141cc406Sopenharmony_ci        {
2736141cc406Sopenharmony_ci          if (config_line[0] == '#')
2737141cc406Sopenharmony_ci            continue;           /* ignore line comments */
2738141cc406Sopenharmony_ci
2739141cc406Sopenharmony_ci	  optval = strchr (config_line, '=');
2740141cc406Sopenharmony_ci	  if (optval == NULL)
2741141cc406Sopenharmony_ci	    continue;           /* only interested in options, skip hosts */
2742141cc406Sopenharmony_ci
2743141cc406Sopenharmony_ci          len = strlen (config_line);
2744141cc406Sopenharmony_ci          if (!len)
2745141cc406Sopenharmony_ci            continue;           /* ignore empty lines */
2746141cc406Sopenharmony_ci
2747141cc406Sopenharmony_ci          /*
2748141cc406Sopenharmony_ci           * Check for saned options.
2749141cc406Sopenharmony_ci           * Anything that isn't an option is a client.
2750141cc406Sopenharmony_ci           */
2751141cc406Sopenharmony_ci          if (strstr(config_line, "data_portrange") != NULL)
2752141cc406Sopenharmony_ci            {
2753141cc406Sopenharmony_ci              optval = sanei_config_skip_whitespace (++optval);
2754141cc406Sopenharmony_ci              if ((optval != NULL) && (*optval != '\0'))
2755141cc406Sopenharmony_ci                {
2756141cc406Sopenharmony_ci		  val = strtol (optval, &endval, 10);
2757141cc406Sopenharmony_ci		  if (optval == endval)
2758141cc406Sopenharmony_ci		    {
2759141cc406Sopenharmony_ci		      DBG (DBG_ERR, "read_config: invalid value for data_portrange\n");
2760141cc406Sopenharmony_ci		      continue;
2761141cc406Sopenharmony_ci		    }
2762141cc406Sopenharmony_ci		  else if ((val < 0) || (val > 65535))
2763141cc406Sopenharmony_ci		    {
2764141cc406Sopenharmony_ci		      DBG (DBG_ERR, "read_config: data_portrange start port is invalid\n");
2765141cc406Sopenharmony_ci		      continue;
2766141cc406Sopenharmony_ci		    }
2767141cc406Sopenharmony_ci
2768141cc406Sopenharmony_ci		  optval = strchr (endval, '-');
2769141cc406Sopenharmony_ci		  if (optval == NULL)
2770141cc406Sopenharmony_ci		    {
2771141cc406Sopenharmony_ci		      DBG (DBG_ERR, "read_config: no end port value for data_portrange\n");
2772141cc406Sopenharmony_ci		      continue;
2773141cc406Sopenharmony_ci		    }
2774141cc406Sopenharmony_ci
2775141cc406Sopenharmony_ci		  optval = sanei_config_skip_whitespace (++optval);
2776141cc406Sopenharmony_ci
2777141cc406Sopenharmony_ci		  data_port_lo = val;
2778141cc406Sopenharmony_ci
2779141cc406Sopenharmony_ci		  val = strtol (optval, &endval, 10);
2780141cc406Sopenharmony_ci		  if (optval == endval)
2781141cc406Sopenharmony_ci		    {
2782141cc406Sopenharmony_ci		      DBG (DBG_ERR, "read_config: invalid value for data_portrange\n");
2783141cc406Sopenharmony_ci		      data_port_lo = 0;
2784141cc406Sopenharmony_ci		      continue;
2785141cc406Sopenharmony_ci		    }
2786141cc406Sopenharmony_ci		  else if ((val < 0) || (val > 65535))
2787141cc406Sopenharmony_ci		    {
2788141cc406Sopenharmony_ci		      DBG (DBG_ERR, "read_config: data_portrange end port is invalid\n");
2789141cc406Sopenharmony_ci		      data_port_lo = 0;
2790141cc406Sopenharmony_ci		      continue;
2791141cc406Sopenharmony_ci		    }
2792141cc406Sopenharmony_ci		  else if (val < data_port_lo)
2793141cc406Sopenharmony_ci		    {
2794141cc406Sopenharmony_ci		      DBG (DBG_ERR, "read_config: data_portrange end port is less than start port\n");
2795141cc406Sopenharmony_ci		      data_port_lo = 0;
2796141cc406Sopenharmony_ci		      continue;
2797141cc406Sopenharmony_ci		    }
2798141cc406Sopenharmony_ci
2799141cc406Sopenharmony_ci		  data_port_hi = val;
2800141cc406Sopenharmony_ci
2801141cc406Sopenharmony_ci                  DBG (DBG_INFO, "read_config: data port range: %d - %d\n", data_port_lo, data_port_hi);
2802141cc406Sopenharmony_ci                }
2803141cc406Sopenharmony_ci            }
2804141cc406Sopenharmony_ci            else if(strstr(config_line, "data_connect_timeout") != NULL)
2805141cc406Sopenharmony_ci            {
2806141cc406Sopenharmony_ci              optval = sanei_config_skip_whitespace (++optval);
2807141cc406Sopenharmony_ci              if ((optval != NULL) && (*optval != '\0'))
2808141cc406Sopenharmony_ci              {
2809141cc406Sopenharmony_ci                val = strtol (optval, &endval, 10);
2810141cc406Sopenharmony_ci                if (optval == endval)
2811141cc406Sopenharmony_ci                {
2812141cc406Sopenharmony_ci                  DBG (DBG_ERR, "read_config: invalid value for data_connect_timeout\n");
2813141cc406Sopenharmony_ci                  continue;
2814141cc406Sopenharmony_ci                }
2815141cc406Sopenharmony_ci                else if ((val < 0) || (val > 65535))
2816141cc406Sopenharmony_ci                {
2817141cc406Sopenharmony_ci                  DBG (DBG_ERR, "read_config: data_connect_timeout is invalid\n");
2818141cc406Sopenharmony_ci                  continue;
2819141cc406Sopenharmony_ci                }
2820141cc406Sopenharmony_ci                data_connect_timeout = val;
2821141cc406Sopenharmony_ci                DBG (DBG_INFO, "read_config: data connect timeout: %d\n", data_connect_timeout);
2822141cc406Sopenharmony_ci              }
2823141cc406Sopenharmony_ci            }
2824141cc406Sopenharmony_ci        }
2825141cc406Sopenharmony_ci      fclose (fp);
2826141cc406Sopenharmony_ci      DBG (DBG_INFO, "read_config: done reading config\n");
2827141cc406Sopenharmony_ci    }
2828141cc406Sopenharmony_ci  else
2829141cc406Sopenharmony_ci    DBG (DBG_ERR, "read_config: could not open config file (%s): %s\n",
2830141cc406Sopenharmony_ci	 SANED_CONFIG_FILE, strerror (errno));
2831141cc406Sopenharmony_ci}
2832141cc406Sopenharmony_ci
2833141cc406Sopenharmony_ci
2834141cc406Sopenharmony_ci#ifdef SANED_USES_AF_INDEP
2835141cc406Sopenharmony_cistatic void
2836141cc406Sopenharmony_cido_bindings_family (int family, int *nfds, struct pollfd **fds, struct addrinfo *res)
2837141cc406Sopenharmony_ci{
2838141cc406Sopenharmony_ci  struct addrinfo *resp;
2839141cc406Sopenharmony_ci  struct pollfd *fdp;
2840141cc406Sopenharmony_ci  short sane_port;
2841141cc406Sopenharmony_ci  int fd = -1;
2842141cc406Sopenharmony_ci  int on = 1;
2843141cc406Sopenharmony_ci  int i;
2844141cc406Sopenharmony_ci
2845141cc406Sopenharmony_ci  sane_port = bind_port;
2846141cc406Sopenharmony_ci  fdp = *fds;
2847141cc406Sopenharmony_ci
2848141cc406Sopenharmony_ci  for (resp = res, i = 0; resp != NULL; resp = resp->ai_next, i++)
2849141cc406Sopenharmony_ci    {
2850141cc406Sopenharmony_ci      /* We're not interested */
2851141cc406Sopenharmony_ci      if (resp->ai_family != family)
2852141cc406Sopenharmony_ci	continue;
2853141cc406Sopenharmony_ci
2854141cc406Sopenharmony_ci      if (resp->ai_family == AF_INET)
2855141cc406Sopenharmony_ci	{
2856141cc406Sopenharmony_ci          if (sane_port != -1)
2857141cc406Sopenharmony_ci	      ((struct sockaddr_in *) resp->ai_addr)->sin_port = htons(sane_port);
2858141cc406Sopenharmony_ci          else
2859141cc406Sopenharmony_ci	      sane_port = ntohs(((struct sockaddr_in *) resp->ai_addr)->sin_port);
2860141cc406Sopenharmony_ci	}
2861141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
2862141cc406Sopenharmony_ci      else if (resp->ai_family == AF_INET6)
2863141cc406Sopenharmony_ci	{
2864141cc406Sopenharmony_ci          if (sane_port != -1)
2865141cc406Sopenharmony_ci              ((struct sockaddr_in6 *) resp->ai_addr)->sin6_port = htons(sane_port);
2866141cc406Sopenharmony_ci          else
2867141cc406Sopenharmony_ci              sane_port = ntohs (((struct sockaddr_in6 *) resp->ai_addr)->sin6_port);
2868141cc406Sopenharmony_ci	}
2869141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
2870141cc406Sopenharmony_ci      else
2871141cc406Sopenharmony_ci	continue;
2872141cc406Sopenharmony_ci
2873141cc406Sopenharmony_ci      DBG (DBG_DBG, "do_bindings: [%d] socket () using IPv%d\n", i, (family == AF_INET) ? 4 : 6);
2874141cc406Sopenharmony_ci      if ((fd = socket (resp->ai_family, SOCK_STREAM, 0)) < 0)
2875141cc406Sopenharmony_ci	{
2876141cc406Sopenharmony_ci	  DBG (DBG_ERR, "do_bindings: [%d] socket failed: %s\n", i, strerror (errno));
2877141cc406Sopenharmony_ci
2878141cc406Sopenharmony_ci	  continue;
2879141cc406Sopenharmony_ci	}
2880141cc406Sopenharmony_ci
2881141cc406Sopenharmony_ci      DBG (DBG_DBG, "do_bindings: [%d] setsockopt ()\n", i);
2882141cc406Sopenharmony_ci      if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)))
2883141cc406Sopenharmony_ci	DBG (DBG_ERR, "do_bindings: [%d] failed to put socket in SO_REUSEADDR mode (%s)\n", i, strerror (errno));
2884141cc406Sopenharmony_ci
2885141cc406Sopenharmony_ci
2886141cc406Sopenharmony_ci      DBG (DBG_DBG, "do_bindings: [%d] bind () to port %d\n", i, sane_port);
2887141cc406Sopenharmony_ci      if (bind (fd, resp->ai_addr, resp->ai_addrlen) < 0)
2888141cc406Sopenharmony_ci	{
2889141cc406Sopenharmony_ci	  /*
2890141cc406Sopenharmony_ci	   * Binding a socket may fail with EADDRINUSE if we already bound
2891141cc406Sopenharmony_ci	   * to an IPv6 addr returned by getaddrinfo (usually the first ones)
2892141cc406Sopenharmony_ci	   * and we're trying to bind to an IPv4 addr now.
2893141cc406Sopenharmony_ci	   * It can also fail because we're trying to bind an IPv6 socket and IPv6
2894141cc406Sopenharmony_ci	   * is not functional on this machine.
2895141cc406Sopenharmony_ci	   * In any case, a bind() call returning an error is not necessarily fatal.
2896141cc406Sopenharmony_ci	   */
2897141cc406Sopenharmony_ci	  DBG (DBG_WARN, "do_bindings: [%d] bind failed: %s\n", i, strerror (errno));
2898141cc406Sopenharmony_ci
2899141cc406Sopenharmony_ci	  close (fd);
2900141cc406Sopenharmony_ci
2901141cc406Sopenharmony_ci	  continue;
2902141cc406Sopenharmony_ci	}
2903141cc406Sopenharmony_ci
2904141cc406Sopenharmony_ci      DBG (DBG_DBG, "do_bindings: [%d] listen ()\n", i);
2905141cc406Sopenharmony_ci      if (listen (fd, 1) < 0)
2906141cc406Sopenharmony_ci	{
2907141cc406Sopenharmony_ci	  DBG (DBG_ERR, "do_bindings: [%d] listen failed: %s\n", i, strerror (errno));
2908141cc406Sopenharmony_ci
2909141cc406Sopenharmony_ci	  close (fd);
2910141cc406Sopenharmony_ci
2911141cc406Sopenharmony_ci	  continue;
2912141cc406Sopenharmony_ci	}
2913141cc406Sopenharmony_ci
2914141cc406Sopenharmony_ci      if (sane_port == 0)
2915141cc406Sopenharmony_ci	{
2916141cc406Sopenharmony_ci	  /* sane was asked to bind to an ephemeral port, log it */
2917141cc406Sopenharmony_ci	  socklen_t len = sizeof (*resp->ai_addr);
2918141cc406Sopenharmony_ci	  if (getsockname(fd, resp->ai_addr, &len) != -1)
2919141cc406Sopenharmony_ci	    {
2920141cc406Sopenharmony_ci	      if (resp->ai_family == AF_INET)
2921141cc406Sopenharmony_ci		{
2922141cc406Sopenharmony_ci		  DBG (DBG_INFO, "do_bindings: [%d] selected ephemeral port: %d\n", i, ntohs(((struct sockaddr_in *) resp->ai_addr)->sin_port));
2923141cc406Sopenharmony_ci		}
2924141cc406Sopenharmony_ci
2925141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
2926141cc406Sopenharmony_ci	      if (resp->ai_family == AF_INET6)
2927141cc406Sopenharmony_ci		{
2928141cc406Sopenharmony_ci		  DBG (DBG_INFO, "do_bindings: [%d] selected ephemeral port: %d\n", i, ntohs(((struct sockaddr_in6 *) resp->ai_addr)->sin6_port));
2929141cc406Sopenharmony_ci		}
2930141cc406Sopenharmony_ci
2931141cc406Sopenharmony_ci#endif /* ENABLE_IPV6 */
2932141cc406Sopenharmony_ci
2933141cc406Sopenharmony_ci	    }
2934141cc406Sopenharmony_ci	}
2935141cc406Sopenharmony_ci
2936141cc406Sopenharmony_ci      fdp->fd = fd;
2937141cc406Sopenharmony_ci      fdp->events = POLLIN;
2938141cc406Sopenharmony_ci
2939141cc406Sopenharmony_ci      (*nfds)++;
2940141cc406Sopenharmony_ci      fdp++;
2941141cc406Sopenharmony_ci    }
2942141cc406Sopenharmony_ci
2943141cc406Sopenharmony_ci  *fds = fdp;
2944141cc406Sopenharmony_ci}
2945141cc406Sopenharmony_ci
2946141cc406Sopenharmony_cistatic void
2947141cc406Sopenharmony_cido_bindings (int *nfds, struct pollfd **fds)
2948141cc406Sopenharmony_ci{
2949141cc406Sopenharmony_ci  struct addrinfo *res;
2950141cc406Sopenharmony_ci  struct addrinfo *resp;
2951141cc406Sopenharmony_ci  struct addrinfo hints;
2952141cc406Sopenharmony_ci  struct pollfd *fdp;
2953141cc406Sopenharmony_ci  int err;
2954141cc406Sopenharmony_ci
2955141cc406Sopenharmony_ci  DBG (DBG_DBG, "do_bindings: trying to get port for service \"%s\" (getaddrinfo)\n", SANED_SERVICE_NAME);
2956141cc406Sopenharmony_ci
2957141cc406Sopenharmony_ci  memset (&hints, 0, sizeof (struct addrinfo));
2958141cc406Sopenharmony_ci
2959141cc406Sopenharmony_ci  hints.ai_family = PF_UNSPEC;
2960141cc406Sopenharmony_ci  hints.ai_flags = AI_PASSIVE;
2961141cc406Sopenharmony_ci  hints.ai_socktype = SOCK_STREAM;
2962141cc406Sopenharmony_ci
2963141cc406Sopenharmony_ci  err = getaddrinfo (bind_addr, SANED_SERVICE_NAME, &hints, &res);
2964141cc406Sopenharmony_ci  if (err)
2965141cc406Sopenharmony_ci    {
2966141cc406Sopenharmony_ci      DBG (DBG_WARN, "do_bindings: \" %s \" service unknown on your host; you should add\n", SANED_SERVICE_NAME);
2967141cc406Sopenharmony_ci      DBG (DBG_WARN, "do_bindings:      %s %d/tcp saned # SANE network scanner daemon\n", SANED_SERVICE_NAME, SANED_SERVICE_PORT);
2968141cc406Sopenharmony_ci      DBG (DBG_WARN, "do_bindings: to your /etc/services file (or equivalent). Proceeding anyway.\n");
2969141cc406Sopenharmony_ci      err = getaddrinfo (bind_addr, SANED_SERVICE_PORT_S, &hints, &res);
2970141cc406Sopenharmony_ci      if (err)
2971141cc406Sopenharmony_ci	{
2972141cc406Sopenharmony_ci	  DBG (DBG_ERR, "do_bindings: getaddrinfo() failed even with numeric port: %s\n", gai_strerror (err));
2973141cc406Sopenharmony_ci	  bail_out (1);
2974141cc406Sopenharmony_ci	}
2975141cc406Sopenharmony_ci    }
2976141cc406Sopenharmony_ci
2977141cc406Sopenharmony_ci  for (resp = res, *nfds = 0; resp != NULL; resp = resp->ai_next, (*nfds)++)
2978141cc406Sopenharmony_ci    ;
2979141cc406Sopenharmony_ci
2980141cc406Sopenharmony_ci  *fds = malloc (*nfds * sizeof (struct pollfd));
2981141cc406Sopenharmony_ci
2982141cc406Sopenharmony_ci  if (fds == NULL)
2983141cc406Sopenharmony_ci    {
2984141cc406Sopenharmony_ci      DBG (DBG_ERR, "do_bindings: not enough memory for fds\n");
2985141cc406Sopenharmony_ci      freeaddrinfo (res);
2986141cc406Sopenharmony_ci      bail_out (1);
2987141cc406Sopenharmony_ci    }
2988141cc406Sopenharmony_ci
2989141cc406Sopenharmony_ci  fdp = *fds;
2990141cc406Sopenharmony_ci  *nfds = 0;
2991141cc406Sopenharmony_ci
2992141cc406Sopenharmony_ci  /* bind IPv6 first, IPv4 second */
2993141cc406Sopenharmony_ci#ifdef ENABLE_IPV6
2994141cc406Sopenharmony_ci  do_bindings_family (AF_INET6, nfds, &fdp, res);
2995141cc406Sopenharmony_ci#endif
2996141cc406Sopenharmony_ci  do_bindings_family (AF_INET, nfds, &fdp, res);
2997141cc406Sopenharmony_ci
2998141cc406Sopenharmony_ci  resp = NULL;
2999141cc406Sopenharmony_ci  freeaddrinfo (res);
3000141cc406Sopenharmony_ci
3001141cc406Sopenharmony_ci  if (*nfds <= 0)
3002141cc406Sopenharmony_ci    {
3003141cc406Sopenharmony_ci      DBG (DBG_ERR, "do_bindings: couldn't bind an address. Exiting.\n");
3004141cc406Sopenharmony_ci      bail_out (1);
3005141cc406Sopenharmony_ci    }
3006141cc406Sopenharmony_ci}
3007141cc406Sopenharmony_ci
3008141cc406Sopenharmony_ci#else /* !SANED_USES_AF_INDEP */
3009141cc406Sopenharmony_ci
3010141cc406Sopenharmony_cistatic void
3011141cc406Sopenharmony_cido_bindings (int *nfds, struct pollfd **fds)
3012141cc406Sopenharmony_ci{
3013141cc406Sopenharmony_ci  struct sockaddr_in sin;
3014141cc406Sopenharmony_ci  struct servent *serv;
3015141cc406Sopenharmony_ci  short port;
3016141cc406Sopenharmony_ci  int fd = -1;
3017141cc406Sopenharmony_ci  int on = 1;
3018141cc406Sopenharmony_ci
3019141cc406Sopenharmony_ci  DBG (DBG_DBG, "do_bindings: trying to get port for service \"%s\" (getservbyname)\n", SANED_SERVICE_NAME);
3020141cc406Sopenharmony_ci  serv = getservbyname (SANED_SERVICE_NAME, "tcp");
3021141cc406Sopenharmony_ci
3022141cc406Sopenharmony_ci  if (serv)
3023141cc406Sopenharmony_ci    {
3024141cc406Sopenharmony_ci      port = serv->s_port;
3025141cc406Sopenharmony_ci      DBG (DBG_MSG, "do_bindings: port is %d\n", ntohs (port));
3026141cc406Sopenharmony_ci    }
3027141cc406Sopenharmony_ci  else
3028141cc406Sopenharmony_ci    {
3029141cc406Sopenharmony_ci      port = htons (SANED_SERVICE_PORT);
3030141cc406Sopenharmony_ci      DBG (DBG_WARN, "do_bindings: \"%s\" service unknown on your host; you should add\n", SANED_SERVICE_NAME);
3031141cc406Sopenharmony_ci      DBG (DBG_WARN, "do_bindings:      %s %d/tcp saned # SANE network scanner daemon\n", SANED_SERVICE_NAME, SANED_SERVICE_PORT);
3032141cc406Sopenharmony_ci      DBG (DBG_WARN, "do_bindings: to your /etc/services file (or equivalent). Proceeding anyway.\n");
3033141cc406Sopenharmony_ci    }
3034141cc406Sopenharmony_ci
3035141cc406Sopenharmony_ci  *nfds = 1;
3036141cc406Sopenharmony_ci  *fds = malloc (*nfds * sizeof (struct pollfd));
3037141cc406Sopenharmony_ci
3038141cc406Sopenharmony_ci  if (fds == NULL)
3039141cc406Sopenharmony_ci    {
3040141cc406Sopenharmony_ci      DBG (DBG_ERR, "do_bindings: not enough memory for fds\n");
3041141cc406Sopenharmony_ci      bail_out (1);
3042141cc406Sopenharmony_ci    }
3043141cc406Sopenharmony_ci
3044141cc406Sopenharmony_ci  memset (&sin, 0, sizeof (sin));
3045141cc406Sopenharmony_ci
3046141cc406Sopenharmony_ci  sin.sin_family = AF_INET;
3047141cc406Sopenharmony_ci  if(bind_addr)
3048141cc406Sopenharmony_ci    sin.sin_addr.s_addr = inet_addr(bind_addr);
3049141cc406Sopenharmony_ci  else
3050141cc406Sopenharmony_ci    sin.sin_addr.s_addr = INADDR_ANY;
3051141cc406Sopenharmony_ci  sin.sin_port = port;
3052141cc406Sopenharmony_ci
3053141cc406Sopenharmony_ci  DBG (DBG_DBG, "do_bindings: socket ()\n");
3054141cc406Sopenharmony_ci  fd = socket (AF_INET, SOCK_STREAM, 0);
3055141cc406Sopenharmony_ci
3056141cc406Sopenharmony_ci  DBG (DBG_DBG, "do_bindings: setsockopt ()\n");
3057141cc406Sopenharmony_ci  if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)))
3058141cc406Sopenharmony_ci    DBG (DBG_ERR, "do_bindings: failed to put socket in SO_REUSEADDR mode (%s)", strerror (errno));
3059141cc406Sopenharmony_ci
3060141cc406Sopenharmony_ci  DBG (DBG_DBG, "do_bindings: bind ()\n");
3061141cc406Sopenharmony_ci  if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
3062141cc406Sopenharmony_ci    {
3063141cc406Sopenharmony_ci      DBG (DBG_ERR, "do_bindings: bind failed: %s", strerror (errno));
3064141cc406Sopenharmony_ci      bail_out (1);
3065141cc406Sopenharmony_ci    }
3066141cc406Sopenharmony_ci
3067141cc406Sopenharmony_ci  DBG (DBG_DBG, "do_bindings: listen ()\n");
3068141cc406Sopenharmony_ci  if (listen (fd, 1) < 0)
3069141cc406Sopenharmony_ci    {
3070141cc406Sopenharmony_ci      DBG (DBG_ERR, "do_bindings: listen failed: %s", strerror (errno));
3071141cc406Sopenharmony_ci      bail_out (1);
3072141cc406Sopenharmony_ci    }
3073141cc406Sopenharmony_ci
3074141cc406Sopenharmony_ci  (*fds)->fd = fd;
3075141cc406Sopenharmony_ci  (*fds)->events = POLLIN;
3076141cc406Sopenharmony_ci}
3077141cc406Sopenharmony_ci
3078141cc406Sopenharmony_ci#endif /* SANED_USES_AF_INDEP */
3079141cc406Sopenharmony_ci
3080141cc406Sopenharmony_ci
3081141cc406Sopenharmony_cistatic void
3082141cc406Sopenharmony_cirunas_user (char *user)
3083141cc406Sopenharmony_ci{
3084141cc406Sopenharmony_ci  uid_t runas_uid = 0;
3085141cc406Sopenharmony_ci  gid_t runas_gid = 0;
3086141cc406Sopenharmony_ci  struct passwd *pwent;
3087141cc406Sopenharmony_ci  gid_t *grplist = NULL;
3088141cc406Sopenharmony_ci  struct group *grp;
3089141cc406Sopenharmony_ci  int ngroups = 0;
3090141cc406Sopenharmony_ci  int ret;
3091141cc406Sopenharmony_ci
3092141cc406Sopenharmony_ci  pwent = getpwnam(user);
3093141cc406Sopenharmony_ci
3094141cc406Sopenharmony_ci  if (pwent == NULL)
3095141cc406Sopenharmony_ci    {
3096141cc406Sopenharmony_ci      DBG (DBG_ERR, "FATAL ERROR: user %s not found on system\n", user);
3097141cc406Sopenharmony_ci      bail_out (1);
3098141cc406Sopenharmony_ci    }
3099141cc406Sopenharmony_ci
3100141cc406Sopenharmony_ci  runas_uid = pwent->pw_uid;
3101141cc406Sopenharmony_ci  runas_gid = pwent->pw_gid;
3102141cc406Sopenharmony_ci
3103141cc406Sopenharmony_ci  /* Get group list for runas_uid */
3104141cc406Sopenharmony_ci  grplist = (gid_t *)malloc(sizeof(gid_t));
3105141cc406Sopenharmony_ci
3106141cc406Sopenharmony_ci  if (grplist == NULL)
3107141cc406Sopenharmony_ci    {
3108141cc406Sopenharmony_ci      DBG (DBG_ERR, "FATAL ERROR: cannot allocate memory for group list\n");
3109141cc406Sopenharmony_ci
3110141cc406Sopenharmony_ci      exit (1);
3111141cc406Sopenharmony_ci    }
3112141cc406Sopenharmony_ci
3113141cc406Sopenharmony_ci  ngroups = 1;
3114141cc406Sopenharmony_ci  grplist[0] = runas_gid;
3115141cc406Sopenharmony_ci
3116141cc406Sopenharmony_ci  setgrent();
3117141cc406Sopenharmony_ci  while ((grp = getgrent()) != NULL)
3118141cc406Sopenharmony_ci    {
3119141cc406Sopenharmony_ci      int i = 0;
3120141cc406Sopenharmony_ci
3121141cc406Sopenharmony_ci      /* Already added current group */
3122141cc406Sopenharmony_ci      if (grp->gr_gid == runas_gid)
3123141cc406Sopenharmony_ci	continue;
3124141cc406Sopenharmony_ci
3125141cc406Sopenharmony_ci      while (grp->gr_mem[i])
3126141cc406Sopenharmony_ci	{
3127141cc406Sopenharmony_ci	  if (strcmp(grp->gr_mem[i], user) == 0)
3128141cc406Sopenharmony_ci	    {
3129141cc406Sopenharmony_ci	      int need_to_add = 1, j;
3130141cc406Sopenharmony_ci
3131141cc406Sopenharmony_ci	      /* Make sure its not already in list */
3132141cc406Sopenharmony_ci	      for (j = 0; j < ngroups; j++)
3133141cc406Sopenharmony_ci		{
3134141cc406Sopenharmony_ci		  if (grp->gr_gid == grplist[i])
3135141cc406Sopenharmony_ci		    need_to_add = 0;
3136141cc406Sopenharmony_ci		}
3137141cc406Sopenharmony_ci	      if (need_to_add)
3138141cc406Sopenharmony_ci		{
3139141cc406Sopenharmony_ci		  grplist = (gid_t *)realloc(grplist,
3140141cc406Sopenharmony_ci					     sizeof(gid_t)*ngroups+1);
3141141cc406Sopenharmony_ci		  if (grplist == NULL)
3142141cc406Sopenharmony_ci		    {
3143141cc406Sopenharmony_ci		      DBG (DBG_ERR, "FATAL ERROR: cannot reallocate memory for group list\n");
3144141cc406Sopenharmony_ci
3145141cc406Sopenharmony_ci		      exit (1);
3146141cc406Sopenharmony_ci		    }
3147141cc406Sopenharmony_ci		  grplist[ngroups++] = grp->gr_gid;
3148141cc406Sopenharmony_ci		}
3149141cc406Sopenharmony_ci	    }
3150141cc406Sopenharmony_ci	  i++;
3151141cc406Sopenharmony_ci	}
3152141cc406Sopenharmony_ci    }
3153141cc406Sopenharmony_ci  endgrent();
3154141cc406Sopenharmony_ci
3155141cc406Sopenharmony_ci  /* Drop privileges if requested */
3156141cc406Sopenharmony_ci  if (runas_uid > 0)
3157141cc406Sopenharmony_ci    {
3158141cc406Sopenharmony_ci      ret = setgroups(ngroups, grplist);
3159141cc406Sopenharmony_ci      if (ret < 0)
3160141cc406Sopenharmony_ci	{
3161141cc406Sopenharmony_ci	  DBG (DBG_ERR, "FATAL ERROR: could not set group list: %s\n", strerror(errno));
3162141cc406Sopenharmony_ci
3163141cc406Sopenharmony_ci	  exit (1);
3164141cc406Sopenharmony_ci	}
3165141cc406Sopenharmony_ci
3166141cc406Sopenharmony_ci      free(grplist);
3167141cc406Sopenharmony_ci
3168141cc406Sopenharmony_ci      ret = setegid (runas_gid);
3169141cc406Sopenharmony_ci      if (ret < 0)
3170141cc406Sopenharmony_ci	{
3171141cc406Sopenharmony_ci	  DBG (DBG_ERR, "FATAL ERROR: setegid to gid %d failed: %s\n", runas_gid, strerror (errno));
3172141cc406Sopenharmony_ci
3173141cc406Sopenharmony_ci	  exit (1);
3174141cc406Sopenharmony_ci	}
3175141cc406Sopenharmony_ci
3176141cc406Sopenharmony_ci      ret = seteuid (runas_uid);
3177141cc406Sopenharmony_ci      if (ret < 0)
3178141cc406Sopenharmony_ci	{
3179141cc406Sopenharmony_ci	  DBG (DBG_ERR, "FATAL ERROR: seteuid to uid %d failed: %s\n", runas_uid, strerror (errno));
3180141cc406Sopenharmony_ci
3181141cc406Sopenharmony_ci	  exit (1);
3182141cc406Sopenharmony_ci	}
3183141cc406Sopenharmony_ci
3184141cc406Sopenharmony_ci      DBG (DBG_WARN, "Dropped privileges to uid %d gid %d\n", runas_uid, runas_gid);
3185141cc406Sopenharmony_ci    }
3186141cc406Sopenharmony_ci}
3187141cc406Sopenharmony_ci
3188141cc406Sopenharmony_ci
3189141cc406Sopenharmony_cistatic void
3190141cc406Sopenharmony_cirun_standalone (char *user)
3191141cc406Sopenharmony_ci{
3192141cc406Sopenharmony_ci  struct pollfd *fds = NULL;
3193141cc406Sopenharmony_ci  struct pollfd *fdp = NULL;
3194141cc406Sopenharmony_ci  int nfds;
3195141cc406Sopenharmony_ci  int fd = -1;
3196141cc406Sopenharmony_ci  int i;
3197141cc406Sopenharmony_ci  int ret;
3198141cc406Sopenharmony_ci
3199141cc406Sopenharmony_ci  FILE *pidfile;
3200141cc406Sopenharmony_ci
3201141cc406Sopenharmony_ci  do_bindings (&nfds, &fds);
3202141cc406Sopenharmony_ci
3203141cc406Sopenharmony_ci  if (run_foreground == SANE_FALSE)
3204141cc406Sopenharmony_ci    {
3205141cc406Sopenharmony_ci      DBG (DBG_MSG, "run_standalone: daemonizing now\n");
3206141cc406Sopenharmony_ci
3207141cc406Sopenharmony_ci      fd = open ("/dev/null", O_RDWR);
3208141cc406Sopenharmony_ci      if (fd < 0)
3209141cc406Sopenharmony_ci	{
3210141cc406Sopenharmony_ci	  DBG (DBG_ERR, "FATAL ERROR: cannot open /dev/null: %s\n", strerror (errno));
3211141cc406Sopenharmony_ci	  exit (1);
3212141cc406Sopenharmony_ci	}
3213141cc406Sopenharmony_ci
3214141cc406Sopenharmony_ci      ret = fork ();
3215141cc406Sopenharmony_ci      if (ret > 0)
3216141cc406Sopenharmony_ci	{
3217141cc406Sopenharmony_ci	  _exit (0);
3218141cc406Sopenharmony_ci	}
3219141cc406Sopenharmony_ci      else if (ret < 0)
3220141cc406Sopenharmony_ci	{
3221141cc406Sopenharmony_ci	  DBG (DBG_ERR, "FATAL ERROR: fork failed: %s\n", strerror (errno));
3222141cc406Sopenharmony_ci	  exit (1);
3223141cc406Sopenharmony_ci	}
3224141cc406Sopenharmony_ci
3225141cc406Sopenharmony_ci      DBG (DBG_WARN, "Now daemonized\n");
3226141cc406Sopenharmony_ci
3227141cc406Sopenharmony_ci      /* Write out PID file */
3228141cc406Sopenharmony_ci      pidfile = fopen (SANED_PID_FILE, "w");
3229141cc406Sopenharmony_ci      if (pidfile)
3230141cc406Sopenharmony_ci	{
3231141cc406Sopenharmony_ci	  fprintf (pidfile, "%d", getpid());
3232141cc406Sopenharmony_ci	  fclose (pidfile);
3233141cc406Sopenharmony_ci	}
3234141cc406Sopenharmony_ci      else
3235141cc406Sopenharmony_ci	DBG (DBG_ERR, "Could not write PID file: %s\n", strerror (errno));
3236141cc406Sopenharmony_ci
3237141cc406Sopenharmony_ci      chdir ("/");
3238141cc406Sopenharmony_ci
3239141cc406Sopenharmony_ci      dup2 (fd, STDIN_FILENO);
3240141cc406Sopenharmony_ci      dup2 (fd, STDOUT_FILENO);
3241141cc406Sopenharmony_ci      dup2 (fd, STDERR_FILENO);
3242141cc406Sopenharmony_ci
3243141cc406Sopenharmony_ci      close (fd);
3244141cc406Sopenharmony_ci
3245141cc406Sopenharmony_ci      setsid ();
3246141cc406Sopenharmony_ci
3247141cc406Sopenharmony_ci      signal(SIGINT, sig_int_term_handler);
3248141cc406Sopenharmony_ci      signal(SIGTERM, sig_int_term_handler);
3249141cc406Sopenharmony_ci    }
3250141cc406Sopenharmony_ci
3251141cc406Sopenharmony_ci  if (user)
3252141cc406Sopenharmony_ci    runas_user(user);
3253141cc406Sopenharmony_ci
3254141cc406Sopenharmony_ci#if WITH_AVAHI
3255141cc406Sopenharmony_ci  DBG (DBG_INFO, "run_standalone: spawning Avahi process\n");
3256141cc406Sopenharmony_ci  saned_avahi (fds, nfds);
3257141cc406Sopenharmony_ci
3258141cc406Sopenharmony_ci  /* NOT REACHED (Avahi process) */
3259141cc406Sopenharmony_ci#endif /* WITH_AVAHI */
3260141cc406Sopenharmony_ci
3261141cc406Sopenharmony_ci  DBG (DBG_MSG, "run_standalone: waiting for control connection\n");
3262141cc406Sopenharmony_ci
3263141cc406Sopenharmony_ci  while (1)
3264141cc406Sopenharmony_ci    {
3265141cc406Sopenharmony_ci      ret = poll (fds, nfds, 500);
3266141cc406Sopenharmony_ci      if (ret < 0)
3267141cc406Sopenharmony_ci	{
3268141cc406Sopenharmony_ci	  if (errno == EINTR)
3269141cc406Sopenharmony_ci	    continue;
3270141cc406Sopenharmony_ci	  else
3271141cc406Sopenharmony_ci	    {
3272141cc406Sopenharmony_ci	      DBG (DBG_ERR, "run_standalone: poll failed: %s\n", strerror (errno));
3273141cc406Sopenharmony_ci	      free (fds);
3274141cc406Sopenharmony_ci	      bail_out (1);
3275141cc406Sopenharmony_ci	    }
3276141cc406Sopenharmony_ci	}
3277141cc406Sopenharmony_ci
3278141cc406Sopenharmony_ci      /* Wait for children */
3279141cc406Sopenharmony_ci      while (wait_child (-1, NULL, WNOHANG) > 0)
3280141cc406Sopenharmony_ci	;
3281141cc406Sopenharmony_ci
3282141cc406Sopenharmony_ci      if (ret == 0)
3283141cc406Sopenharmony_ci	continue;
3284141cc406Sopenharmony_ci
3285141cc406Sopenharmony_ci      for (i = 0, fdp = fds; i < nfds; i++, fdp++)
3286141cc406Sopenharmony_ci	{
3287141cc406Sopenharmony_ci	  /* Error on an fd */
3288141cc406Sopenharmony_ci	  if (fdp->revents & (POLLERR | POLLHUP | POLLNVAL))
3289141cc406Sopenharmony_ci	    {
3290141cc406Sopenharmony_ci	      for (i = 0, fdp = fds; i < nfds; i++, fdp++)
3291141cc406Sopenharmony_ci		close (fdp->fd);
3292141cc406Sopenharmony_ci
3293141cc406Sopenharmony_ci	      free (fds);
3294141cc406Sopenharmony_ci
3295141cc406Sopenharmony_ci	      DBG (DBG_WARN, "run_standalone: invalid fd in set, attempting to re-bind\n");
3296141cc406Sopenharmony_ci
3297141cc406Sopenharmony_ci	      /* Reopen sockets */
3298141cc406Sopenharmony_ci	      do_bindings (&nfds, &fds);
3299141cc406Sopenharmony_ci
3300141cc406Sopenharmony_ci	      break;
3301141cc406Sopenharmony_ci	    }
3302141cc406Sopenharmony_ci	  else if (! (fdp->revents & POLLIN))
3303141cc406Sopenharmony_ci	    continue;
3304141cc406Sopenharmony_ci
3305141cc406Sopenharmony_ci	  fd = accept (fdp->fd, 0, 0);
3306141cc406Sopenharmony_ci	  if (fd < 0)
3307141cc406Sopenharmony_ci	    {
3308141cc406Sopenharmony_ci	      DBG (DBG_ERR, "run_standalone: accept failed: %s", strerror (errno));
3309141cc406Sopenharmony_ci	      continue;
3310141cc406Sopenharmony_ci	    }
3311141cc406Sopenharmony_ci
3312141cc406Sopenharmony_ci	  handle_client (fd);
3313141cc406Sopenharmony_ci
3314141cc406Sopenharmony_ci	  if (run_once == SANE_TRUE)
3315141cc406Sopenharmony_ci	    break; /* We have handled the only connection we're going to handle */
3316141cc406Sopenharmony_ci	}
3317141cc406Sopenharmony_ci
3318141cc406Sopenharmony_ci      if (run_once == SANE_TRUE)
3319141cc406Sopenharmony_ci	break;
3320141cc406Sopenharmony_ci    }
3321141cc406Sopenharmony_ci
3322141cc406Sopenharmony_ci  for (i = 0, fdp = fds; i < nfds; i++, fdp++)
3323141cc406Sopenharmony_ci    close (fdp->fd);
3324141cc406Sopenharmony_ci
3325141cc406Sopenharmony_ci  free (fds);
3326141cc406Sopenharmony_ci}
3327141cc406Sopenharmony_ci
3328141cc406Sopenharmony_ci
3329141cc406Sopenharmony_cistatic void
3330141cc406Sopenharmony_cirun_inetd (char __sane_unused__ *sock)
3331141cc406Sopenharmony_ci{
3332141cc406Sopenharmony_ci
3333141cc406Sopenharmony_ci  int fd = -1;
3334141cc406Sopenharmony_ci
3335141cc406Sopenharmony_ci#ifdef HAVE_SYSTEMD
3336141cc406Sopenharmony_ci  int n;
3337141cc406Sopenharmony_ci
3338141cc406Sopenharmony_ci  n = sd_listen_fds(0);
3339141cc406Sopenharmony_ci
3340141cc406Sopenharmony_ci  if (n > 1)
3341141cc406Sopenharmony_ci    {
3342141cc406Sopenharmony_ci      DBG (DBG_ERR, "run_inetd: Too many file descriptors (sockets) received from systemd!\n");
3343141cc406Sopenharmony_ci      return;
3344141cc406Sopenharmony_ci    }
3345141cc406Sopenharmony_ci
3346141cc406Sopenharmony_ci  if (n == 1)
3347141cc406Sopenharmony_ci    {
3348141cc406Sopenharmony_ci    fd = SD_LISTEN_FDS_START + 0;
3349141cc406Sopenharmony_ci    DBG (DBG_INFO, "run_inetd: Using systemd socket %d!\n", fd);
3350141cc406Sopenharmony_ci    }
3351141cc406Sopenharmony_ci#endif
3352141cc406Sopenharmony_ci
3353141cc406Sopenharmony_ci  if (fd == -1)
3354141cc406Sopenharmony_ci    {
3355141cc406Sopenharmony_ci      int dave_null;
3356141cc406Sopenharmony_ci
3357141cc406Sopenharmony_ci      /* Some backends really can't keep their dirty fingers off
3358141cc406Sopenharmony_ci       * stdin/stdout/stderr; we work around them here so they don't
3359141cc406Sopenharmony_ci       * mess up the network dialog and crash the remote net backend
3360141cc406Sopenharmony_ci       * by messing with the inetd socket.
3361141cc406Sopenharmony_ci       * For systemd this not an issue as systemd uses fd >= 3 for the
3362141cc406Sopenharmony_ci       * socket and can even redirect stdout and stderr to syslog.
3363141cc406Sopenharmony_ci       * We can even use this to get the debug logging
3364141cc406Sopenharmony_ci       */
3365141cc406Sopenharmony_ci      do
3366141cc406Sopenharmony_ci        {
3367141cc406Sopenharmony_ci          /* get new fd for the inetd socket */
3368141cc406Sopenharmony_ci          fd = dup (1);
3369141cc406Sopenharmony_ci
3370141cc406Sopenharmony_ci          if (fd == -1)
3371141cc406Sopenharmony_ci      	    {
3372141cc406Sopenharmony_ci              DBG (DBG_ERR, "run_inetd: duplicating fd failed: %s", strerror (errno));
3373141cc406Sopenharmony_ci              return;
3374141cc406Sopenharmony_ci            }
3375141cc406Sopenharmony_ci        }
3376141cc406Sopenharmony_ci      while (fd < 3);
3377141cc406Sopenharmony_ci
3378141cc406Sopenharmony_ci      /* Our good'ole friend Dave Null to the rescue */
3379141cc406Sopenharmony_ci      dave_null = open ("/dev/null", O_RDWR);
3380141cc406Sopenharmony_ci      if (dave_null < 0)
3381141cc406Sopenharmony_ci        {
3382141cc406Sopenharmony_ci          DBG (DBG_ERR, "run_inetd: could not open /dev/null: %s", strerror (errno));
3383141cc406Sopenharmony_ci          return;
3384141cc406Sopenharmony_ci        }
3385141cc406Sopenharmony_ci
3386141cc406Sopenharmony_ci      close (STDIN_FILENO);
3387141cc406Sopenharmony_ci      close (STDOUT_FILENO);
3388141cc406Sopenharmony_ci      close (STDERR_FILENO);
3389141cc406Sopenharmony_ci
3390141cc406Sopenharmony_ci      dup2 (dave_null, STDIN_FILENO);
3391141cc406Sopenharmony_ci      dup2 (dave_null, STDOUT_FILENO);
3392141cc406Sopenharmony_ci      dup2 (dave_null, STDERR_FILENO);
3393141cc406Sopenharmony_ci
3394141cc406Sopenharmony_ci      close (dave_null);
3395141cc406Sopenharmony_ci    }
3396141cc406Sopenharmony_ci#ifdef HAVE_OS2_H
3397141cc406Sopenharmony_ci  /* under OS/2, the socket handle is passed as argument on the command
3398141cc406Sopenharmony_ci     line; the socket handle is relative to IBM TCP/IP, so a call
3399141cc406Sopenharmony_ci     to impsockethandle() is required to add it to the EMX runtime */
3400141cc406Sopenharmony_ci  if (sock)
3401141cc406Sopenharmony_ci    {
3402141cc406Sopenharmony_ci      fd = _impsockhandle (atoi (sock), 0);
3403141cc406Sopenharmony_ci      if (fd == -1)
3404141cc406Sopenharmony_ci	perror ("impsockhandle");
3405141cc406Sopenharmony_ci    }
3406141cc406Sopenharmony_ci#endif /* HAVE_OS2_H */
3407141cc406Sopenharmony_ci
3408141cc406Sopenharmony_ci  handle_connection(fd);
3409141cc406Sopenharmony_ci}
3410141cc406Sopenharmony_ci
3411141cc406Sopenharmony_cistatic void usage(char *me, int err)
3412141cc406Sopenharmony_ci{
3413141cc406Sopenharmony_ci  fprintf (stderr,
3414141cc406Sopenharmony_ci       "Usage: %s [OPTIONS]\n\n"
3415141cc406Sopenharmony_ci       " Options:\n\n"
3416141cc406Sopenharmony_ci       "  -a, --alone[=user]	equal to `-l -D -u user'\n"
3417141cc406Sopenharmony_ci       "  -l, --listen		run in standalone mode (listen for connection)\n"
3418141cc406Sopenharmony_ci       "  -u, --user=user	run as `user'\n"
3419141cc406Sopenharmony_ci       "  -D, --daemonize	run in background\n"
3420141cc406Sopenharmony_ci       "  -o, --once		exit after first client disconnects\n"
3421141cc406Sopenharmony_ci       "  -d, --debug=level	set debug level `level' (default is 2)\n"
3422141cc406Sopenharmony_ci       "  -e, --stderr		output to stderr\n"
3423141cc406Sopenharmony_ci       "  -b, --bind=addr	bind address `addr' (default all interfaces)\n"
3424141cc406Sopenharmony_ci       "  -p, --port=port	bind port `port` (default sane-port or 6566)\n"
3425141cc406Sopenharmony_ci       "  -h, --help		show this help message and exit\n", me);
3426141cc406Sopenharmony_ci
3427141cc406Sopenharmony_ci  exit(err);
3428141cc406Sopenharmony_ci}
3429141cc406Sopenharmony_ci
3430141cc406Sopenharmony_cistatic int debug;
3431141cc406Sopenharmony_ci
3432141cc406Sopenharmony_cistatic struct option long_options[] =
3433141cc406Sopenharmony_ci{
3434141cc406Sopenharmony_ci/* These options set a flag. */
3435141cc406Sopenharmony_ci  {"help",	no_argument,		0, 'h'},
3436141cc406Sopenharmony_ci  {"alone",	optional_argument,	0, 'a'},
3437141cc406Sopenharmony_ci  {"listen",	no_argument,		0, 'l'},
3438141cc406Sopenharmony_ci  {"user",	required_argument,	0, 'u'},
3439141cc406Sopenharmony_ci  {"daemonize", no_argument,		0, 'D'},
3440141cc406Sopenharmony_ci  {"once",	no_argument,		0, 'o'},
3441141cc406Sopenharmony_ci  {"debug",	required_argument,	0, 'd'},
3442141cc406Sopenharmony_ci  {"stderr",	no_argument,		0, 'e'},
3443141cc406Sopenharmony_ci  {"bind",	required_argument,	0, 'b'},
3444141cc406Sopenharmony_ci  {"port",	required_argument,	0, 'p'},
3445141cc406Sopenharmony_ci  {0,		0,			0,  0 }
3446141cc406Sopenharmony_ci};
3447141cc406Sopenharmony_ci
3448141cc406Sopenharmony_ciint
3449141cc406Sopenharmony_cimain (int argc, char *argv[])
3450141cc406Sopenharmony_ci{
3451141cc406Sopenharmony_ci  char options[64] = "";
3452141cc406Sopenharmony_ci  char *user = NULL;
3453141cc406Sopenharmony_ci  char *sock = NULL;
3454141cc406Sopenharmony_ci  int c;
3455141cc406Sopenharmony_ci  int long_index = 0;
3456141cc406Sopenharmony_ci
3457141cc406Sopenharmony_ci  debug = DBG_WARN;
3458141cc406Sopenharmony_ci
3459141cc406Sopenharmony_ci  prog_name = strrchr (argv[0], '/');
3460141cc406Sopenharmony_ci  if (prog_name)
3461141cc406Sopenharmony_ci    ++prog_name;
3462141cc406Sopenharmony_ci  else
3463141cc406Sopenharmony_ci    prog_name = argv[0];
3464141cc406Sopenharmony_ci
3465141cc406Sopenharmony_ci  numchildren = 0;
3466141cc406Sopenharmony_ci  run_mode = SANED_RUN_INETD;
3467141cc406Sopenharmony_ci  run_foreground = SANE_TRUE;
3468141cc406Sopenharmony_ci  run_once = SANE_FALSE;
3469141cc406Sopenharmony_ci
3470141cc406Sopenharmony_ci  while((c = getopt_long(argc, argv,"ha::lu:Dod:eb:p:", long_options, &long_index )) != -1)
3471141cc406Sopenharmony_ci    {
3472141cc406Sopenharmony_ci      switch(c) {
3473141cc406Sopenharmony_ci      case 'a':
3474141cc406Sopenharmony_ci	run_mode = SANED_RUN_ALONE;
3475141cc406Sopenharmony_ci	run_foreground = SANE_FALSE;
3476141cc406Sopenharmony_ci	if (optarg)
3477141cc406Sopenharmony_ci	  user = optarg;
3478141cc406Sopenharmony_ci	break;
3479141cc406Sopenharmony_ci      case 'l':
3480141cc406Sopenharmony_ci	run_mode = SANED_RUN_ALONE;
3481141cc406Sopenharmony_ci	break;
3482141cc406Sopenharmony_ci      case 'u':
3483141cc406Sopenharmony_ci	user = optarg;
3484141cc406Sopenharmony_ci	break;
3485141cc406Sopenharmony_ci      case 'D':
3486141cc406Sopenharmony_ci	run_foreground = SANE_FALSE;
3487141cc406Sopenharmony_ci	break;
3488141cc406Sopenharmony_ci      case 'o':
3489141cc406Sopenharmony_ci	run_once = SANE_TRUE;
3490141cc406Sopenharmony_ci	break;
3491141cc406Sopenharmony_ci      case 'd':
3492141cc406Sopenharmony_ci	debug = atoi(optarg);
3493141cc406Sopenharmony_ci	break;
3494141cc406Sopenharmony_ci      case 'e':
3495141cc406Sopenharmony_ci	log_to_syslog = SANE_FALSE;
3496141cc406Sopenharmony_ci	break;
3497141cc406Sopenharmony_ci      case 'b':
3498141cc406Sopenharmony_ci	bind_addr = optarg;
3499141cc406Sopenharmony_ci	break;
3500141cc406Sopenharmony_ci      case 'p':
3501141cc406Sopenharmony_ci	bind_port = atoi(optarg);
3502141cc406Sopenharmony_ci	break;
3503141cc406Sopenharmony_ci      case 'h':
3504141cc406Sopenharmony_ci	usage(argv[0], EXIT_SUCCESS);
3505141cc406Sopenharmony_ci	break;
3506141cc406Sopenharmony_ci      default:
3507141cc406Sopenharmony_ci	usage(argv[0], EXIT_FAILURE);
3508141cc406Sopenharmony_ci	break;
3509141cc406Sopenharmony_ci      }
3510141cc406Sopenharmony_ci    }
3511141cc406Sopenharmony_ci
3512141cc406Sopenharmony_ci  if (log_to_syslog)
3513141cc406Sopenharmony_ci    openlog ("saned", LOG_PID | LOG_CONS, LOG_DAEMON);
3514141cc406Sopenharmony_ci
3515141cc406Sopenharmony_ci  read_config ();
3516141cc406Sopenharmony_ci
3517141cc406Sopenharmony_ci  byte_order.w = 0;
3518141cc406Sopenharmony_ci  byte_order.ch = 1;
3519141cc406Sopenharmony_ci
3520141cc406Sopenharmony_ci  sanei_w_init (&wire, sanei_codec_bin_init);
3521141cc406Sopenharmony_ci  wire.io.read = read;
3522141cc406Sopenharmony_ci  wire.io.write = write;
3523141cc406Sopenharmony_ci
3524141cc406Sopenharmony_ci#ifdef SANED_USES_AF_INDEP
3525141cc406Sopenharmony_ci  strcat(options, "AF-indep");
3526141cc406Sopenharmony_ci# ifdef ENABLE_IPV6
3527141cc406Sopenharmony_ci  strcat(options, "+IPv6");
3528141cc406Sopenharmony_ci#endif
3529141cc406Sopenharmony_ci#else
3530141cc406Sopenharmony_ci  strcat(options, "IPv4 only");
3531141cc406Sopenharmony_ci#endif
3532141cc406Sopenharmony_ci#ifdef HAVE_SYSTEMD
3533141cc406Sopenharmony_ci  if (sd_listen_fds(0) > 0)
3534141cc406Sopenharmony_ci    {
3535141cc406Sopenharmony_ci      strcat(options, "+systemd");
3536141cc406Sopenharmony_ci    }
3537141cc406Sopenharmony_ci#endif
3538141cc406Sopenharmony_ci
3539141cc406Sopenharmony_ci  if (strlen(options) > 0)
3540141cc406Sopenharmony_ci    {
3541141cc406Sopenharmony_ci      DBG (DBG_WARN, "saned (%s) from %s starting up\n", options, PACKAGE_STRING);
3542141cc406Sopenharmony_ci    }
3543141cc406Sopenharmony_ci  else
3544141cc406Sopenharmony_ci    {
3545141cc406Sopenharmony_ci      DBG (DBG_WARN, "saned from %s ready\n", PACKAGE_STRING);
3546141cc406Sopenharmony_ci    }
3547141cc406Sopenharmony_ci
3548141cc406Sopenharmony_ci  if (run_mode == SANED_RUN_ALONE)
3549141cc406Sopenharmony_ci    {
3550141cc406Sopenharmony_ci      run_standalone(user);
3551141cc406Sopenharmony_ci    }
3552141cc406Sopenharmony_ci  else
3553141cc406Sopenharmony_ci    {
3554141cc406Sopenharmony_ci#ifdef HAVE_OS2_H
3555141cc406Sopenharmony_ci      if (argc == 2)
3556141cc406Sopenharmony_ci	sock = argv[1];
3557141cc406Sopenharmony_ci#endif
3558141cc406Sopenharmony_ci      run_inetd(sock);
3559141cc406Sopenharmony_ci    }
3560141cc406Sopenharmony_ci
3561141cc406Sopenharmony_ci  DBG (DBG_WARN, "saned exiting\n");
3562141cc406Sopenharmony_ci
3563141cc406Sopenharmony_ci  return 0;
3564141cc406Sopenharmony_ci}
3565