Added support for IPv6. Updated manpages. Patch from Julien BLACHE

<jb@jblache.org>.
merge-requests/1/head
Henning Geinitz 2003-03-30 19:07:18 +00:00
rodzic e1b27e1c18
commit 088281c4e1
11 zmienionych plików z 1467 dodań i 37 usunięć

Wyświetl plik

@ -2,6 +2,10 @@
* doc/descriptions/unsupported.desc: Added Medion MD 6228, Microtek
ScanPort 3000, and PIE Primefilm 1800u.
* acinclude.m4 aclocal.m4 configure configure.in backend/net.c
backend/net.h doc/sane-net.man doc/saned.man frontend/saned.c
include/sane/config.h.in: Added support for IPv6. Updated
manpages. Patch from Julien BLACHE <jb@jblache.org>.
2003-03-28 Oliver Schirrmeister <oschirr@abm.de>

Wyświetl plik

@ -12,6 +12,7 @@ dnl JAPHAR_GREP_CFLAGS(flag, cmd_if_missing, cmd_if_present)
dnl SANE_LINKER_RPATH
dnl SANE_CHECK_U_TYPES
dnl SANE_CHECK_GPHOTO2
dnl SANE_CHECK_IPV6
dnl SANE_PROTOTYPES
dnl AC_PROG_LIBTOOL
dnl
@ -344,6 +345,45 @@ AC_DEFUN([SANE_CHECK_GPHOTO2],
]) fi
])
#
# Check for AF_INET6, determines whether or not to enable IPv6 support
AC_DEFUN([SANE_CHECK_IPV6],
[
AC_MSG_CHECKING([whether to enable IPv6])
AC_ARG_ENABLE(ipv6,
[ --enable-ipv6 enable IPv6 (with IPv4) support
--disable-ipv6 disable IPv6 support],
[ case "$enableval" in
no)
AC_MSG_RESULT(no)
ipv6=no
;;
*) AC_MSG_RESULT(yes)
AC_DEFINE([ENABLE_IPV6], 1, [Define to 1 if the system supports IPv6])
ipv6=yes
;;
esac ],
AC_TRY_COMPILE([
#define INET6
#include <sys/types.h>
#include <sys/socket.h> ], [
/* AF_INET6 available check */
if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
exit(1);
else
exit(0);
],
AC_MSG_RESULT(yes)
AC_DEFINE([ENABLE_IPV6], 1, [Define to 1 if the system supports IPv6])
ipv6=yes,
AC_MSG_RESULT(no)
ipv6=no,
AC_MSG_RESULT(no)
ipv6=no
))
])
#
# Generate prototypes for functions not available on the system
AC_DEFUN([SANE_PROTOTYPES],

40
aclocal.m4 vendored
Wyświetl plik

@ -24,6 +24,7 @@ dnl JAPHAR_GREP_CFLAGS(flag, cmd_if_missing, cmd_if_present)
dnl SANE_LINKER_RPATH
dnl SANE_CHECK_U_TYPES
dnl SANE_CHECK_GPHOTO2
dnl SANE_CHECK_IPV6
dnl SANE_PROTOTYPES
dnl AC_PROG_LIBTOOL
dnl
@ -356,6 +357,45 @@ AC_DEFUN([SANE_CHECK_GPHOTO2],
]) fi
])
#
# Check for AF_INET6, determines whether or not to enable IPv6 support
AC_DEFUN([SANE_CHECK_IPV6],
[
AC_MSG_CHECKING([whether to enable IPv6])
AC_ARG_ENABLE(ipv6,
[ --enable-ipv6 enable IPv6 (with IPv4) support
--disable-ipv6 disable IPv6 support],
[ case "$enableval" in
no)
AC_MSG_RESULT(no)
ipv6=no
;;
*) AC_MSG_RESULT(yes)
AC_DEFINE([ENABLE_IPV6], 1, [Define to 1 if the system supports IPv6])
ipv6=yes
;;
esac ],
AC_TRY_COMPILE([
#define INET6
#include <sys/types.h>
#include <sys/socket.h> ], [
/* AF_INET6 available check */
if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
exit(1);
else
exit(0);
],
AC_MSG_RESULT(yes)
AC_DEFINE([ENABLE_IPV6], 1, [Define to 1 if the system supports IPv6])
ipv6=yes,
AC_MSG_RESULT(no)
ipv6=no,
AC_MSG_RESULT(no)
ipv6=no
))
])
#
# Generate prototypes for functions not available on the system
AC_DEFUN([SANE_PROTOTYPES],

Wyświetl plik

@ -1,5 +1,8 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 1997 David Mosberger-Tang
Copyright (C) 2003 Julien BLACHE <jb@jblache.org>
AF-independent code + IPv6
This file is part of the SANE package.
This program is free software; you can redistribute it and/or
@ -40,10 +43,6 @@
This file implements a SANE network-based meta backend. */
/* Please increase version number with every change
(don't forget to update net.desc) */
#define NET_VERSION "1.0.10"
#ifdef _AIX
# include "../include/lalloca.h" /* MUST come first for AIX! */
#endif
@ -83,15 +82,34 @@
#include "../include/sane/sanei_config.h"
#define NET_CONFIG_FILE "net.conf"
/* Please increase version number with every change
(don't forget to update net.desc) */
/* define the version string depending on which network code is used */
#if defined (HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO)
# define NET_USES_AF_INDEP
# ifdef ENABLE_IPV6
# define NET_VERSION "1.0.11 (AF-indep+IPv6)"
# else
# define NET_VERSION "1.0.11 (AF-indep)"
# endif /* ENABLE_IPV6 */
#else
# undef ENABLE_IPV6
# define NET_VERSION "1.0.11"
#endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */
static SANE_Auth_Callback auth_callback;
static Net_Device *first_device;
static Net_Scanner *first_handle;
static const SANE_Device **devlist;
static int saned_port;
static int client_big_endian; /* 1 == big endian; 0 == little endian */
static int server_big_endian; /* 1 == big endian; 0 == little endian */
static int depth; /* bits per pixel */
#ifndef NET_USES_AF_INDEP
static int saned_port;
#endif /* !NET_USES_AF_INDEP */
/* This variable is only needed, if the depth is 16bit/channel and
client/server have different endianness. A value of -1 means, that there's
no hang over; otherwise the value has to be casted to SANE_Byte. hang_over
@ -110,6 +128,99 @@ static int hang_over;
*/
static int left_over;
#ifdef NET_USES_AF_INDEP
static SANE_Status
add_device (const char *name, Net_Device ** ndp)
{
struct addrinfo hints;
struct addrinfo *res;
struct addrinfo *resp;
struct sockaddr_in *sin;
#ifdef ENABLE_IPV6
struct sockaddr_in6 *sin6;
#endif /* ENABLE_IPV6 */
Net_Device *nd = NULL;
int error;
short sane_port = htons (6566);
DBG (1, "add_device: adding backend %s\n", name);
memset (&hints, 0, sizeof(hints));
# ifdef ENABLE_IPV6
hints.ai_family = PF_UNSPEC;
# else
hints.ai_family = PF_INET;
# endif /* ENABLE_IPV6 */
error = getaddrinfo (name, "sane", &hints, &res);
if (error)
{
error = getaddrinfo (name, NULL, &hints, &res);
if (error)
{
DBG (1, "add_device: error while getting address of host %s: %s\n",
name, gai_strerror (error));
return SANE_STATUS_IO_ERROR;
}
else
{
for (resp = res; resp != NULL; resp = resp->ai_next)
{
switch (resp->ai_family)
{
case AF_INET:
sin = (struct sockaddr_in *) resp->ai_addr;
sin->sin_port = sane_port;
break;
#ifdef ENABLE_IPV6
case AF_INET6:
sin6 = (struct sockaddr_in6 *) resp->ai_addr;
sin6->sin6_port = sane_port;
break;
#endif /* ENABLE_IPV6 */
}
}
}
}
nd = malloc (sizeof (Net_Device));
if (!nd)
{
DBG (1, "add_device: not enough memory for Net_Device struct\n");
freeaddrinfo (res);
return SANE_STATUS_NO_MEM;
}
memset (nd, 0, sizeof (Net_Device));
nd->name = strdup (name);
if (!nd->name)
{
DBG (1, "add_device: not enough memory to duplicate name\n");
free(nd);
return SANE_STATUS_NO_MEM;
}
nd->addr = res;
nd->ctl = -1;
nd->next = first_device;
first_device = nd;
if (ndp)
*ndp = nd;
DBG (2, "add_device: backend %s added\n", name);
return SANE_STATUS_GOOD;
}
#else /* !NET_USES_AF_INDEP */
static SANE_Status
add_device (const char *name, Net_Device ** ndp)
{
@ -161,6 +272,69 @@ add_device (const char *name, Net_Device ** ndp)
DBG (2, "add_device: backend %s added\n", name);
return SANE_STATUS_GOOD;
}
#endif /* NET_USES_AF_INDEP */
#ifdef NET_USES_AF_INDEP
static SANE_Status
connect_dev (Net_Device * dev)
{
struct addrinfo *addrp;
SANE_Word version_code;
SANE_Init_Reply reply;
SANE_Status status = SANE_STATUS_IO_ERROR;
SANE_Init_Req req;
SANE_Bool connected = SANE_FALSE;
#ifdef TCP_NODELAY
int on = 1;
int level = -1;
#endif
int i;
DBG (2, "connect_dev: trying to connect to %s\n", dev->name);
for (addrp = dev->addr, i = 0; (addrp != NULL) && (connected == SANE_FALSE); addrp = addrp->ai_next, i++)
{
# ifdef ENABLE_IPV6
if ((addrp->ai_family != AF_INET) && (addrp->ai_family != AF_INET6))
# else /* !ENABLE_IPV6 */
if (addrp->ai_family != AF_INET)
# endif /* ENABLE_IPV6 */
{
DBG (1, "connect_dev: [%d] don't know how to deal with addr family %d\n",
i, addrp->ai_family);
break;
}
dev->ctl = socket (addrp->ai_family, SOCK_STREAM, 0);
if (dev->ctl < 0)
{
DBG (1, "connect_dev: [%d] failed to obtain socket (%s)\n",
i, strerror (errno));
dev->ctl = -1;
break;
}
if (connect (dev->ctl, addrp->ai_addr, addrp->ai_addrlen) < 0)
{
DBG (1, "connect_dev: [%d] failed to connect (%s)\n", i, strerror (errno));
dev->ctl = -1;
break;
}
DBG (3, "connect_dev: [%d] connection succeeded (%s)\n", i, (addrp->ai_family == AF_INET6) ? "IPv6" : "IPv4");
dev->addr_used = addrp;
connected = SANE_TRUE;
}
if (connected != SANE_TRUE)
{
DBG (1, "connect_dev: couldn't connect to host (see messages above)\n");
return SANE_STATUS_IO_ERROR;
}
#else /* !NET_USES_AF_INDEP */
static SANE_Status
connect_dev (Net_Device * dev)
@ -202,6 +376,7 @@ connect_dev (Net_Device * dev)
return SANE_STATUS_IO_ERROR;
}
DBG (3, "connect_dev: connection succeeded\n");
#endif /* NET_USES_AF_INDEP */
#ifdef TCP_NODELAY
# ifdef SOL_TCP
@ -288,6 +463,7 @@ fail:
return status;
}
static SANE_Status
fetch_options (Net_Scanner * s)
{
@ -444,13 +620,16 @@ SANE_Status
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
{
char device_name[PATH_MAX];
struct servent *serv;
const char *env;
size_t len;
FILE *fp;
short ns = 0x1234;
unsigned char *p = (unsigned char *)(&ns);
#ifndef NET_USES_AF_INDEP
struct servent *serv;
#endif /* !NET_USES_AF_INDEP */
DBG_INIT ();
DBG (2, "sane_init: authorize = %p, version_code = %p\n", authorize,
@ -484,6 +663,7 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
DBG (3, "sane_init: Client has little endian byte order\n");
}
#ifndef NET_USES_AF_INDEP
DBG (2, "sane_init: determining sane service port\n");
serv = getservbyname ("sane", "tcp");
@ -498,6 +678,7 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
DBG (1, "sane_init: could not find `sane' service (%s); using default "
"port %d\n", strerror (errno), ntohs (saned_port));
}
#endif /* !NET_USES_AF_INDEP */
DBG (2, "sane_init: searching for config file\n");
fp = sanei_config_open (NET_CONFIG_FILE);
@ -533,6 +714,30 @@ sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
next = copy;
while ((host = strsep (&next, ":")))
{
#ifdef ENABLE_IPV6
if (host[0] == '[')
{
/* skip '[' (host[0]) */
host++;
/* get the rest of the IPv6 addr (we're screwed if ] is missing)
* Is it worth checking for the matching ] ? Not for now. */
strsep (&next, "]");
/* add back the ":" that got removed by the strsep() */
host[strlen (host)] = ':';
/* host now holds the IPv6 address */
/* skip the ':' that could be after ] (avoids a call to strsep() */
if (next[0] == ':')
next++;
}
/*
* if the IPv6 is last in the list, the strsep() call in the while()
* will return a string with the first char being '\0'. Skip it.
*/
if (host[0] == '\0')
continue;
#endif /* ENABLE_IPV6 */
DBG (2, "sane_init: trying to add %s\n", host);
add_device (host, 0);
}
@ -580,6 +785,12 @@ sane_exit (void)
}
if (dev->name)
free ((void *) dev->name);
#ifdef NET_USES_AF_INDEP
if (dev->addr)
freeaddrinfo(dev->addr);
#endif /* NET_USES_AF_INDEP */
free (dev);
}
if (devlist)
@ -693,11 +904,23 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
SANE_Device *rdev;
char *mem;
#ifdef ENABLE_IPV6
SANE_Bool IPv6 = SANE_FALSE;
#endif /* ENABLE_IPV6 */
/* create a new device entry with a device name that is the
sum of the backend name a colon and the backend's device
name: */
len = strlen (dev->name) + 1 + strlen (reply.device_list[i]->name);
#ifdef ENABLE_IPV6
if (strchr (dev->name, ':') != NULL)
{
len += 2;
IPv6 = SANE_TRUE;
}
#endif /* ENABLE_IPV6 */
mem = malloc (sizeof (*dev) + len + 1);
if (!mem)
{
@ -707,8 +930,22 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
&reply);
return SANE_STATUS_NO_MEM;
}
memset (mem, 0, sizeof (*dev) + len);
full_name = mem + sizeof (*dev);
strcpy (full_name, dev->name);
#ifdef ENABLE_IPV6
if (IPv6 == SANE_TRUE)
strcat (full_name, "[");
#endif /* ENABLE_IPV6 */
strcat (full_name, dev->name);
#ifdef ENABLE_IPV6
if (IPv6 == SANE_TRUE)
strcat (full_name, "]");
#endif /* ENABLE_IPV6 */
strcat (full_name, ":");
strcat (full_name, reply.device_list[i]->name);
DBG (3, "sane_get_devices: got %s\n", full_name);
@ -723,11 +960,11 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
DBG (1, "sane_get_devices: not enough free memory\n");
if (rdev->vendor)
free (rdev->vendor);
free ((void *) rdev->vendor);
if (rdev->model)
free (rdev->model);
free ((void *) rdev->model);
if (rdev->type)
free (rdev->type);
free ((void *) rdev->type);
free (rdev);
sanei_w_free (&dev->wire,
(WireCodecFunc) sanei_w_get_devices_reply,
@ -756,6 +993,10 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
{
SANE_Open_Reply reply;
const char *dev_name;
#ifdef ENABLE_IPV6
const char *tmp_name;
SANE_Bool v6addr = SANE_FALSE;
#endif /* ENABLE_IPV6 */
SANE_String nd_name;
SANE_Status status;
SANE_Word handle;
@ -764,12 +1005,46 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
int need_auth;
DBG (3, "sane_open(\"%s\")\n", full_name);
#ifdef ENABLE_IPV6
/*
* Check whether a numerical IPv6 host was specified
* [2001:42:42::12] <== check for '[' as full_name[0]
* ex: [2001:42:42::12]:test:0 (syntax taken from Apache 2)
*/
if (full_name[0] == '[')
{
v6addr = SANE_TRUE;
tmp_name = strchr (full_name, ']');
if (!tmp_name)
{
DBG (1, "sane_open: incorrect host address: missing matching ']'\n");
return SANE_STATUS_INVAL;
}
}
else
tmp_name = full_name;
dev_name = strchr (tmp_name, ':');
#else /* !ENABLE_IPV6 */
dev_name = strchr (full_name, ':');
#endif /* ENABLE_IPV6 */
dev_name = strchr (full_name, ':');
if (dev_name)
{
#ifdef strndupa
# ifdef ENABLE_IPV6
if (v6addr == SANE_TRUE)
nd_name = strndupa (full_name + 1, dev_name - full_name - 2);
else
nd_name = strndupa (full_name, dev_name - full_name);
# else /* !ENABLE_IPV6 */
nd_name = strndupa (full_name, dev_name - full_name);
# endif /* ENABLE_IPV6 */
if (!nd_name)
{
DBG (1, "sane_open: not enough free memory\n");
@ -778,14 +1053,41 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
#else
char *tmp;
# ifdef ENABLE_IPV6
if (v6addr == SANE_TRUE)
tmp = alloca (dev_name - full_name - 2 + 1);
else
tmp = alloca (dev_name - full_name + 1);
# else /* !ENABLE_IPV6 */
tmp = alloca (dev_name - full_name + 1);
# endif /* ENABLE_IPV6 */
if (!tmp)
{
DBG (1, "sane_open: not enough free memory\n");
return SANE_STATUS_NO_MEM;
}
# ifdef ENABLE_IPV6
if (v6addr == SANE_TRUE)
{
memcpy (tmp, full_name + 1, dev_name - full_name - 2);
tmp[dev_name - full_name - 2] = '\0';
}
else
{
memcpy (tmp, full_name, dev_name - full_name);
tmp[dev_name - full_name] = '\0';
}
# else /* !ENABLE_IPV6 */
memcpy (tmp, full_name, dev_name - full_name);
tmp[dev_name - full_name] = '\0';
# endif /* ENABLE_IPV6 */
nd_name = tmp;
#endif
++dev_name; /* skip colon */
@ -795,7 +1097,26 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
/* if no colon interpret full_name as the host name; an empty
device name will cause us to open the first device of that
host. */
#ifdef ENABLE_IPV6
if (v6addr == SANE_TRUE)
{
nd_name = alloca (strlen (full_name) - 2 + 1);
if (!nd_name)
{
DBG (1, "sane_open: not enough free memory\n");
return SANE_STATUS_NO_MEM;
}
memcpy (nd_name, full_name + 1, strlen (full_name) - 2);
nd_name[strlen (full_name) - 2] = '\0';
}
else
nd_name = (char *) full_name;
#else /* !ENABLE_IPV6 */
nd_name = (char *) full_name;
#endif /* ENABLE_IPV6 */
dev_name = "";
}
DBG (2, "sane_open: host = %s, device = %s\n", nd_name, dev_name);
@ -1136,6 +1457,146 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
return status;
}
#ifdef NET_USES_AF_INDEP
SANE_Status
sane_start (SANE_Handle handle)
{
Net_Scanner *s = handle;
SANE_Start_Reply reply;
struct sockaddr_in sin;
struct sockaddr *sa;
#ifdef ENABLE_IPV6
struct sockaddr_in6 sin6;
#endif /* ENABLE_IPV6 */
SANE_Status status;
int fd, need_auth;
socklen_t len;
u_int16_t port; /* Internet-specific */
DBG (3, "sane_start\n");
hang_over = -1;
left_over = -1;
if (s->data >= 0)
{
DBG (2, "sane_start: data pipe already exists\n");
return SANE_STATUS_INVAL;
}
/* Do this ahead of time so in case anything fails, we can
recover gracefully (without hanging our server). */
switch (s->hw->addr_used->ai_family)
{
case AF_INET:
len = sizeof (sin);
sa = (struct sockaddr *) &sin;
break;
#ifdef ENABLE_IPV6
case AF_INET6:
len = sizeof (sin6);
sa = (struct sockaddr *) &sin6;
break;
#endif /* ENABLE_IPV6 */
default:
DBG (1, "sane_start: unknown address family : %d\n",
s->hw->addr_used->ai_family);
return SANE_STATUS_INVAL;
}
if (getpeername (s->hw->ctl, sa, &len) < 0)
{
DBG (1, "sane_start: getpeername() failed (%s)\n", strerror (errno));
return SANE_STATUS_IO_ERROR;
}
fd = socket (s->hw->addr_used->ai_family, SOCK_STREAM, 0);
if (fd < 0)
{
DBG (1, "sane_start: socket() failed (%s)\n", strerror (errno));
return SANE_STATUS_IO_ERROR;
}
DBG (3, "sane_start: remote start\n");
sanei_w_call (&s->hw->wire, SANE_NET_START,
(WireCodecFunc) sanei_w_word, &s->handle,
(WireCodecFunc) sanei_w_start_reply, &reply);
do
{
status = reply.status;
port = reply.port;
if (reply.byte_order == 0x1234)
{
server_big_endian = 0;
DBG (1, "sane_start: server has little endian byte order\n");
}
else
{
server_big_endian = 1;
DBG (1, "sane_start: server has big endian byte order\n");
}
need_auth = (reply.resource_to_authorize != 0);
if (need_auth)
{
DBG (3, "sane_start: auth required\n");
do_authorization (s->hw, reply.resource_to_authorize);
sanei_w_free (&s->hw->wire,
(WireCodecFunc) sanei_w_start_reply, &reply);
sanei_w_set_dir (&s->hw->wire, WIRE_DECODE);
sanei_w_start_reply (&s->hw->wire, &reply);
continue;
}
sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_start_reply,
&reply);
if (need_auth && !s->hw->auth_active)
return SANE_STATUS_CANCELLED;
if (status != SANE_STATUS_GOOD)
{
DBG (1, "sane_start: remote start failed (%s)\n",
sane_strstatus (status));
close (fd);
return status;
}
}
while (need_auth);
DBG (3, "sane_start: remote start finished, data at port %hu\n", port);
switch (s->hw->addr_used->ai_family)
{
case AF_INET:
sin.sin_port = htons (port);
break;
#ifdef ENABLE_IPV6
case AF_INET6:
sin6.sin6_port = htons (port);
break;
#endif /* ENABLE_IPV6 */
}
if (connect (fd, sa, len) < 0)
{
DBG (1, "sane_start: connect() failed (%s)\n", strerror (errno));
close (fd);
return SANE_STATUS_IO_ERROR;
}
shutdown (fd, 1);
s->data = fd;
s->reclen_buf_offset = 0;
s->bytes_remaining = 0;
DBG (3, "sane_start: done (%s)\n", sane_strstatus (status));
return status;
}
#else /* !NET_USES_AF_INDEP */
SANE_Status
sane_start (SANE_Handle handle)
{
@ -1240,6 +1701,8 @@ sane_start (SANE_Handle handle)
DBG (3, "sane_start: done (%s)\n", sane_strstatus (status));
return status;
}
#endif /* NET_USES_AF_INDEP */
SANE_Status
sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length,

Wyświetl plik

@ -1,5 +1,8 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 1997 David Mosberger-Tang
Copyright (C) 2003 Julien BLACHE <jb@jblache.org>
AF-independent code + IPv6
This file is part of the SANE package.
This program is free software; you can redistribute it and/or
@ -44,12 +47,18 @@
#include <sys/socket.h>
#include "../include/sane/sanei_wire.h"
#include "../include/sane/config.h"
typedef struct Net_Device
{
struct Net_Device *next;
const char *name;
#if defined (HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO)
struct addrinfo *addr;
struct addrinfo *addr_used;
#else
struct sockaddr addr;
#endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */
int ctl; /* socket descriptor (or -1) */
Wire wire;
int auth_active;

101
configure vendored
Wyświetl plik

@ -1011,6 +1011,8 @@ Optional Features:
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-warnings turn on tons of compiler warnings (GCC only)
--enable-ipv6 enable IPv6 (with IPv4) support
--disable-ipv6 disable IPv6 support
--enable-static=PKGS build static libraries default=no
--enable-shared=PKGS build shared libraries default=yes
--enable-fast-install=PKGS optimize for fast installation default=yes
@ -8013,9 +8015,12 @@ rm -f conftest.mmap
for ac_func in atexit inet_addr inet_aton inet_ntoa ioperm mkdir \
scsireq_enter strftime strstr strtod \
cfmakeraw tcsendbreak strcasecmp strncasecmp _portaccess
cfmakeraw tcsendbreak strcasecmp strncasecmp _portaccess \
getaddrinfo getnameinfo
do
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
echo "$as_me:$LINENO: checking for $ac_func" >&5
@ -8298,6 +8303,90 @@ _ACEOF
fi
if test "$ac_cv_func_getnameinfo" = "yes" && test "$ac_cv_func_getaddrinfo" = "yes" ; then
echo "$as_me:$LINENO: checking whether to enable IPv6" >&5
echo $ECHO_N "checking whether to enable IPv6... $ECHO_C" >&6
# Check whether --enable-ipv6 or --disable-ipv6 was given.
if test "${enable_ipv6+set}" = set; then
enableval="$enable_ipv6"
case "$enableval" in
no)
echo "$as_me:$LINENO: result: no" >&5
echo "${ECHO_T}no" >&6
ipv6=no
;;
*) echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6
cat >>confdefs.h <<\_ACEOF
#define ENABLE_IPV6 1
_ACEOF
ipv6=yes
;;
esac
else
cat >conftest.$ac_ext <<_ACEOF
#line $LINENO "configure"
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#define INET6
#include <sys/types.h>
#include <sys/socket.h>
int
main ()
{
/* AF_INET6 available check */
if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
exit(1);
else
exit(0);
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } &&
{ ac_try='test -s conftest.$ac_objext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6
cat >>confdefs.h <<\_ACEOF
#define ENABLE_IPV6 1
_ACEOF
ipv6=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
echo "$as_me:$LINENO: result: no" >&5
echo "${ECHO_T}no" >&6
ipv6=no
fi
rm -f conftest.$ac_objext conftest.$ac_ext
fi;
else
ipv6="no"
fi
# Check whether --enable-static or --disable-static was given.
if test "${enable_static+set}" = set; then
@ -9475,7 +9564,7 @@ test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
case $host in
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 9478 "configure"' > conftest.$ac_ext
echo '#line 9567 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@ -10004,7 +10093,7 @@ chmod -w .
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -o out/conftest2.$ac_objext"
compiler_c_o=no
if { (eval echo configure:10007: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
if { (eval echo configure:10096: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s out/conftest.err; then
@ -11836,7 +11925,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
#line 11839 "configure"
#line 11928 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -11934,7 +12023,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
#line 11937 "configure"
#line 12026 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -14026,6 +14115,8 @@ echo "Configuration: `eval eval echo ${sysconfdir}`"
echo "Libraries: `eval eval echo ${libdir}`"
echo "Binaries: `eval eval echo ${bindir}` and `eval eval echo ${sbindir}`"
echo "Manpages: `eval eval echo ${mandir}`"
echo "Network parameters:"
echo "IPv6 support: `eval eval echo ${ipv6}`"
if test "$SANE_CONFIG_PATH" != "no" ; then
SANE_INSTALLED_VERSION=`$SANE_CONFIG_PATH --version`

Wyświetl plik

@ -180,7 +180,8 @@ AC_FUNC_ALLOCA
AC_FUNC_MMAP
AC_CHECK_FUNCS(atexit inet_addr inet_aton inet_ntoa ioperm mkdir \
scsireq_enter strftime strstr strtod \
cfmakeraw tcsendbreak strcasecmp strncasecmp _portaccess)
cfmakeraw tcsendbreak strcasecmp strncasecmp _portaccess \
getaddrinfo getnameinfo)
AC_REPLACE_FUNCS(getenv inet_ntop inet_pton isfdtype sigprocmask snprintf \
strdup strndup strsep usleep vsyslog)
SANE_PROTOTYPES
@ -208,6 +209,13 @@ if sparc64 -q > /dev/null 2>&1 ; then
AC_DEFINE(DISABLE_LINUX_SG_IO, 1, [Should we disable SCSI generic v3?])
fi
dnl check for IPv6 (can be overriden by --enable-ipv6)
if test "$ac_cv_func_getnameinfo" = "yes" && test "$ac_cv_func_getaddrinfo" = "yes" ; then
SANE_CHECK_IPV6
else
ipv6="no"
fi
dnl ***********************************************************************
dnl initialize libtool
dnl ***********************************************************************
@ -433,6 +441,8 @@ echo "Configuration: `eval eval echo ${sysconfdir}`"
echo "Libraries: `eval eval echo ${libdir}`"
echo "Binaries: `eval eval echo ${bindir}` and `eval eval echo ${sbindir}`"
echo "Manpages: `eval eval echo ${mandir}`"
echo "Network parameters:"
echo "IPv6 support: `eval eval echo ${ipv6}`"
if test "$SANE_CONFIG_PATH" != "no" ; then
SANE_INSTALLED_VERSION=`$SANE_CONFIG_PATH --version`

Wyświetl plik

@ -20,7 +20,7 @@ This backend expects device names of the form:
.PP
Where
.I host
is the name of the (remote-) host and
is the name (or IP address) of the (remote-) host and
.I device
is the name of the device on this host that should be addressed.
If the device name does not contain a colon (:), then the entire string
@ -28,35 +28,50 @@ is treated as the
.I device
string for the default host. The default host is the host listed last
in the configuration file (see below).
.PP
An IPv6 address can be specified enclosed in square brackets:
.PP
.RS
.IR [::1] : device
.RE
.SH CONFIGURATION
The contents of the
.IR net.conf .
file is a list of host names that should be contacted for
.IR net.conf
file is a list of host names (or IP addresses) that should be contacted for
scan requests. Empty lines and lines starting with a hash mark (#) are
ignored. A sample configuration file is shown below:
ignored. Note that IPv6 addresses in this file do not need to be enclosed
in square brackets. A sample configuration file is shown below:
.PP
.RS
scan-server.somedomain.firm
.br
192.168.0.1
.br
# this is a comment
.br
localhost
.br
::1
.RE
.PP
The above list of host names can be extended at run-time using environment
The above list of hosts can be extended at run-time using environment
variable
.BR SANE_NET_HOSTS .
This environment variable is a colon-separated list of hostnames that
should be contacted in addition to the hosts mentioned in the
configuration file. For example, a user could set the environment
This environment variable is a colon-separated list of hostnames or IP
addresses that should be contacted in addition to the hosts mentioned in
the configuration file. For example, a user could set the environment
variable to the string:
.PP
.RS
new.scanner.com:scanner.univ.edu
new.scanner.com:[::1]:192.168.0.2:scanner.univ.edu
.RE
.PP
To request that hosts
.I new.scanner.com
,
.I [::1]
,
.I 192.168.0.2
and
.I scanner.univ.edu
are contacted in addition to the hosts listed above.
@ -65,7 +80,7 @@ For this backend to function properly, it is also necessary to define the
.B sane
service in
.IR /etc/services .
At present, the
The
.B sane
service should be defined using a line of the following form:
.PP
@ -103,7 +118,8 @@ to "/tmp/config:" would result in directories "tmp/config", ".", and
"@CONFIGDIR@" being searched (in this order).
.TP
.B SANE_NET_HOSTS
A colon-separated list of host names to be contacted by this backend.
A colon-separated list of host names or IP addresses to be contacted by this
backend.
.TP
.B SANE_DEBUG_NET
If the library was compiled with debug support enabled, this

Wyświetl plik

@ -68,15 +68,22 @@ scan-client.somedomain.firm
# this is a comment
.br
192.168.0.1
.br
::1
.RE
.PP
The case of the host names does not matter, so AHost.COM is considered
identical to ahost.com.
identical to ahost.com. IPv6 addresses should always be specified in their
compressed form.
For
.B saned
to work properly, it is also necessary to add a configuration line to
.IR /etc/inetd.conf .
Note that your inetd must support IPv6 if you
want to connect to saned over IPv6 ; xinetd and openbsd-inetd are known to
support IPv6, check the documentation for your inetd daemon.
.PP
The configuration line normally looks like this:
.PP
.RS

Wyświetl plik

@ -1,6 +1,9 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 1997 Andreas Beck
Copyright (C) 2001, 2002 Henning Meier-Geinitz
Copyright (C) 2003 Julien BLACHE <jb@jblache.org>
AF-independent + IPv6 code
This file is part of the SANE package.
SANE is free software; you can redistribute it and/or modify it under
@ -54,6 +57,7 @@
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
@ -74,12 +78,26 @@
# define IN_LOOPBACK(addr) (addr == 0x7f000001L)
#endif
#ifdef ENABLE_IPV6
# define SANE_IN6_IS_ADDR_LOOPBACK(a) \
(((const uint32_t *) (a))[0] == 0 \
&& ((const uint32_t *) (a))[1] == 0 \
&& ((const uint32_t *) (a))[2] == 0 \
&& ((const uint32_t *) (a))[3] == htonl (1))
#endif /* ENABLE_IPV6 */
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 120
#endif
#define SANED_CONFIG_FILE "saned.conf"
#if defined(HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO)
# define SANED_USES_AF_INDEP
#else
# undef ENABLE_IPV6
#endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */
typedef struct
{
u_int inuse:1; /* is this handle in use? */
@ -107,8 +125,14 @@ byte_order;
it does is save a remote user some work by reducing the amount of
text s/he has to type when authentication is requested. */
static const char *default_username = "saned-user";
static char *remote_hostname;
static char *remote_ip;
#ifdef SANED_USES_AF_INDEP
static struct sockaddr_storage remote_address;
static int remote_address_len;
#else
static struct in_addr remote_address;
#endif /* SANED_USES_AF_INDEP */
#ifndef _PATH_HEQUIV
# define _PATH_HEQUIV "/etc/hosts.equiv"
@ -371,6 +395,300 @@ decode_handle (Wire * w, const char *op)
}
/* Access control */
#ifdef SANED_USES_AF_INDEP
static SANE_Status
check_host (int fd)
{
struct sockaddr_in *sin = NULL;
#ifdef ENABLE_IPV6
struct sockaddr_in6 *sin6;
#endif /* ENABLE_IPV6 */
struct addrinfo hints;
struct addrinfo *res;
struct addrinfo *resp;
int j, access_ok = 0;
int err;
char text_addr[64];
#ifdef ENABLE_IPV6
char *remote_ipv4 = NULL; /* in case we have an IPv4-mapped address (eg ::ffff:127.0.0.1) */
struct addrinfo *remote_ipv4_addr = NULL;
#endif /* ENABLE_IPV6 */
char config_line[1024];
char hostname[MAXHOSTNAMELEN];
#ifdef ENABLE_IPV6
SANE_Bool IPv4map = SANE_FALSE;
#endif /* ENABLE_IPV6 */
int len;
FILE *fp;
/* Get address of remote host */
remote_address_len = sizeof (remote_address);
if (getpeername (fd, (struct sockaddr *) &remote_address, (socklen_t *) &remote_address_len) < 0)
{
DBG (DBG_ERR, "check_host: getpeername failed: %s\n", strerror (errno));
remote_ip = strdup ("[error]");
return SANE_STATUS_INVAL;
}
err = getnameinfo ((struct sockaddr *) &remote_address, remote_address_len,
hostname, sizeof (hostname), NULL, 0, NI_NUMERICHOST);
if (err)
{
DBG (DBG_DBG, "check_host: getnameinfo failed: %s\n", gai_strerror(err));
remote_ip = strdup ("[error]");
return SANE_STATUS_INVAL;
}
else
remote_ip = strdup (hostname);
#ifdef ENABLE_IPV6
if (strncmp (remote_ip, "::ffff:", 7) == 0)
{
DBG (DBG_DBG, "check_host: detected an IPv4-mapped address\n");
remote_ipv4 = remote_ip + 7;
IPv4map = SANE_TRUE;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_INET;
err = getaddrinfo (remote_ipv4, NULL, &hints, &res);
if (err)
{
DBG (DBG_DBG, "check_host: getaddrinfo() failed: %s\n", gai_strerror (err));
IPv4map = SANE_FALSE; /* we failed, remote_ipv4_addr points to nothing */
}
else
{
remote_ipv4_addr = res;
sin = (struct sockaddr_in *)res->ai_addr;
}
}
#endif /* ENABLE_IPV6 */
DBG (DBG_WARN, "check_host: access by remote host: %s\n", remote_ip);
/* Always allow access from local host. Do it here to avoid DNS lookups
and reading saned.conf. */
#ifdef ENABLE_IPV6
if (IPv4map == SANE_TRUE)
{
if (IN_LOOPBACK (ntohl (sin->sin_addr.s_addr)))
{
DBG (DBG_MSG,
"check_host: remote host is IN_LOOPBACK: access granted\n");
freeaddrinfo (remote_ipv4_addr);
return SANE_STATUS_GOOD;
}
freeaddrinfo (remote_ipv4_addr);
}
#endif /* ENABLE_IPV6 */
sin = (struct sockaddr_in *) &remote_address;
#ifdef ENABLE_IPV6
sin6 = (struct sockaddr_in6 *) &remote_address;
#endif /* ENABLE_IPV6 */
switch (remote_address.ss_family)
{
case AF_INET:
if (IN_LOOPBACK (ntohl (sin->sin_addr.s_addr)))
{
DBG (DBG_MSG,
"check_host: remote host is IN_LOOPBACK: access granted\n");
return SANE_STATUS_GOOD;
}
break;
#ifdef ENABLE_IPV6
case AF_INET6:
if (SANE_IN6_IS_ADDR_LOOPBACK (sin6->sin6_addr.s6_addr))
{
DBG (DBG_MSG,
"check_host: remote host is IN6_LOOPBACK: access granted\n");
return SANE_STATUS_GOOD;
}
break;
#endif /* ENABLE_IPV6 */
default:
break;
}
DBG (DBG_DBG, "check_host: remote host is not IN_LOOPBACK"
#ifdef ENABLE_IPV6
" nor IN6_LOOPBACK"
#endif /* ENABLE_IPV6 */
"\n");
/* Get name of local host */
if (gethostname (hostname, sizeof (hostname)) < 0)
{
DBG (DBG_ERR, "check_host: gethostname failed: %s\n", strerror (errno));
return SANE_STATUS_INVAL;
}
DBG (DBG_DBG, "check_host: local hostname: %s\n", hostname);
/* Get local addresses */
memset (&hints, 0, sizeof (hints));
hints.ai_flags = AI_CANONNAME;
#ifdef ENABLE_IPV6
hints.ai_family = PF_UNSPEC;
#else
hints.ai_family = PF_INET;
#endif /* ENABLE_IPV6 */
err = getaddrinfo (hostname, NULL, &hints, &res);
if (err)
{
DBG (DBG_ERR, "check_host: getaddrinfo failed: %s\n",
gai_strerror (err));
return SANE_STATUS_INVAL;
}
else
{
for (resp = res; resp != NULL; resp = resp->ai_next)
{
DBG (DBG_DBG, "check_host: local hostname(s) (from DNS): %s\n",
resp->ai_canonname);
err = getnameinfo (resp->ai_addr, resp->ai_addrlen, text_addr,
sizeof (text_addr), NULL, 0, NI_NUMERICHOST);
if (err)
strncpy (text_addr, "[error]", 8);
#ifdef ENABLE_IPV6
if ((strcmp (text_addr, remote_ip) == 0) ||
((IPv4map == SANE_TRUE) && (strcmp (text_addr, remote_ipv4) == 0)))
#else
if (strcmp (text_addr, remote_ip) == 0)
#endif /* ENABLE_IPV6 */
{
DBG (DBG_MSG, "check_host: remote host has same addr as local: access granted\n");
freeaddrinfo (res);
return SANE_STATUS_GOOD;
}
}
freeaddrinfo (res);
DBG (DBG_DBG,
"check_host: remote host doesn't have same addr as local\n");
}
/* must be a remote host: check contents of PATH_NET_CONFIG or
/etc/hosts.equiv if former doesn't exist: */
for (j = 0; j < NELEMS (config_file_names); ++j)
{
DBG (DBG_DBG, "check_host: opening config file: %s\n",
config_file_names[j]);
if (config_file_names[j][0] == '/')
fp = fopen (config_file_names[j], "r");
else
fp = sanei_config_open (config_file_names[j]);
if (!fp)
{
DBG (DBG_MSG,
"check_host: can't open config file: %s (%s)\n",
config_file_names[j], strerror (errno));
continue;
}
while (!access_ok && sanei_config_read (config_line,
sizeof (config_line), fp))
{
DBG (DBG_DBG, "check_host: config file line: `%s'\n", config_line);
if (config_line[0] == '#') /* ignore line comments */
continue;
len = strlen (config_line);
if (!len)
continue; /* ignore empty lines */
if (strcmp (config_line, "+") == 0)
{
access_ok = 1;
DBG (DBG_DBG,
"check_host: access granted from any host (`+')\n");
}
/* compare remote_ip (remote IP address) to the config_line */
else if (strcmp (config_line, remote_ip) == 0)
{
access_ok = 1;
DBG (DBG_DBG,
"check_host: access granted from IP address %s\n", remote_ip);
}
#ifdef ENABLE_IPV6
else if ((IPv4map == SANE_TRUE) && (strcmp (config_line, remote_ipv4) == 0))
{
access_ok = 1;
DBG (DBG_DBG,
"check_host: access granted from IP address %s (IPv4-mapped)\n", remote_ip);
}
#endif /* ENABLE_IPV6 */
else
{
memset (&hints, 0, sizeof (hints));
hints.ai_flags = AI_CANONNAME;
#ifdef ENABLE_IPV6
hints.ai_family = PF_UNSPEC;
#else
hints.ai_family = PF_INET;
#endif /* ENABLE_IPV6 */
err = getaddrinfo (config_line, NULL, &hints, &res);
if (err)
{
DBG (DBG_DBG,
"check_host: getaddrinfo for `%s' failed: %s\n",
config_line, gai_strerror (err));
DBG (DBG_MSG, "check_host: entry isn't an IP address "
"and can't be found in DNS\n");
continue;
}
else
{
for (resp = res; resp != NULL; resp = resp->ai_next)
{
err = getnameinfo (resp->ai_addr, resp->ai_addrlen, text_addr,
sizeof (text_addr), NULL, 0, NI_NUMERICHOST);
if (err)
strncpy (text_addr, "[error]", 8);
DBG (DBG_MSG,
"check_host: DNS lookup returns IP address: %s\n",
text_addr);
#ifdef ENABLE_IPV6
if ((strcmp (text_addr, remote_ip) == 0) ||
((IPv4map == SANE_TRUE) && (strcmp (text_addr, remote_ipv4) == 0)))
#else
if (strcmp (text_addr, remote_ip) == 0)
#endif /* ENABLE_IPV6 */
access_ok = 1;
if (access_ok)
break;
}
freeaddrinfo (res);
}
}
}
}
fclose (fp);
if (access_ok)
return SANE_STATUS_GOOD;
return SANE_STATUS_ACCESS_DENIED;
}
#else /* !SANED_USES_AF_INDEP */
static SANE_Status
check_host (int fd)
{
@ -391,12 +709,13 @@ check_host (int fd)
if (getpeername (fd, (struct sockaddr *) &sin, (socklen_t *) &len) < 0)
{
DBG (DBG_ERR, "check_host: getpeername failed: %s\n", strerror (errno));
remote_ip = strdup ("[error]");
return SANE_STATUS_INVAL;
}
r_hostname = inet_ntoa (sin.sin_addr);
remote_hostname = strdup (r_hostname);
remote_ip = strdup (r_hostname);
DBG (DBG_WARN, "check_host: access by remote host: %s\n",
remote_hostname);
remote_ip);
/* Save remote address for check of control and data connections */
memcpy (&remote_address, &sin.sin_addr, sizeof (remote_address));
@ -530,6 +849,8 @@ check_host (int fd)
return SANE_STATUS_ACCESS_DENIED;
}
#endif /* SANED_USES_AF_INDEP */
static int
init (Wire * w)
{
@ -543,9 +864,11 @@ init (Wire * w)
status = check_host (w->io.fd);
if (status != SANE_STATUS_GOOD)
{
DBG (DBG_WARN, "init: access by host %s denied\n", remote_hostname);
DBG (DBG_WARN, "init: access by host %s denied\n", remote_ip);
return -1;
}
else
DBG (DBG_MSG, "init: access granted\n");
sanei_w_set_dir (w, WIRE_DECODE);
if (w->status)
@ -583,8 +906,8 @@ init (Wire * w)
reply.version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR,
SANEI_NET_PROTOCOL_VERSION);
DBG (DBG_WARN, "init: access by %s@%s accepted\n",
default_username, remote_hostname);
DBG (DBG_WARN, "init: access granted to %s@%s\n",
default_username, remote_ip);
if (status == SANE_STATUS_GOOD)
{
@ -612,6 +935,108 @@ init (Wire * w)
return 0;
}
#ifdef SANED_USES_AF_INDEP
static int
start_scan (Wire * w, int h, SANE_Start_Reply * reply)
{
struct sockaddr_storage ss;
struct sockaddr_in *sin;
#ifdef ENABLE_IPV6
struct sockaddr_in6 *sin6;
#endif /* ENABLE_IPV6 */
SANE_Handle be_handle;
int fd, len;
be_handle = handle[h].handle;
len = sizeof (ss);
if (getsockname (w->io.fd, (struct sockaddr *) &ss, (socklen_t *) &len) < 0)
{
DBG (DBG_ERR, "start_scan: failed to obtain socket address (%s)\n",
strerror (errno));
reply->status = SANE_STATUS_IO_ERROR;
return -1;
}
fd = socket (ss.ss_family, SOCK_STREAM, 0);
if (fd < 0)
{
DBG (DBG_ERR, "start_scan: failed to obtain data socket (%s)\n",
strerror (errno));
reply->status = SANE_STATUS_IO_ERROR;
return -1;
}
switch (ss.ss_family)
{
case AF_INET:
sin = (struct sockaddr_in *) &ss;
sin->sin_port = 0;
break;
#ifdef ENABLE_IPV6
case AF_INET6:
sin6 = (struct sockaddr_in6 *) &ss;
sin6->sin6_port = 0;
break;
#endif /* ENABLE_IPV6 */
default:
break;
}
if (bind (fd, (struct sockaddr *) &ss, len) < 0)
{
DBG (DBG_ERR, "start_scan: failed to bind address (%s)\n",
strerror (errno));
reply->status = SANE_STATUS_IO_ERROR;
return -1;
}
if (listen (fd, 1) < 0)
{
DBG (DBG_ERR, "start_scan: failed to make socket listen (%s)\n",
strerror (errno));
reply->status = SANE_STATUS_IO_ERROR;
return -1;
}
if (getsockname (fd, (struct sockaddr *) &ss, (socklen_t *) &len) < 0)
{
DBG (DBG_ERR, "start_scan: failed to obtain socket address (%s)\n",
strerror (errno));
reply->status = SANE_STATUS_IO_ERROR;
return -1;
}
switch (ss.ss_family)
{
case AF_INET:
sin = (struct sockaddr_in *) &ss;
reply->port = ntohs (sin->sin_port);
break;
#ifdef ENABLE_IPV6
case AF_INET6:
sin6 = (struct sockaddr_in6 *) &ss;
reply->port = ntohs (sin6->sin6_port);
break;
#endif /* ENABLE_IPV6 */
default:
break;
}
DBG (DBG_MSG, "start_scan: using port %d for data\n", reply->port);
reply->status = sane_start (be_handle);
if (reply->status == SANE_STATUS_GOOD)
{
handle[h].scanning = 1;
handle[h].docancel = 0;
}
return fd;
}
#else /* !SANED_USES_AF_INDEP */
static int
start_scan (Wire * w, int h, SANE_Start_Reply * reply)
{
@ -677,6 +1102,7 @@ start_scan (Wire * w, int h, SANE_Start_Reply * reply)
return fd;
}
#endif /* SANED_USES_AF_INDEP */
static int
store_reclen (SANE_Byte * buf, size_t buf_size, int i, size_t reclen)
@ -958,7 +1384,7 @@ process_request (Wire * w)
}
else
{
DBG (DBG_MSG, "process_request: access to resource `%s' accepted\n",
DBG (DBG_MSG, "process_request: access to resource `%s' granted\n",
resource);
free (resource);
memset (&reply, 0, sizeof (reply)); /* avoid leaking bits */
@ -1089,6 +1515,51 @@ process_request (Wire * w)
sanei_w_reply (w, (WireCodecFunc) sanei_w_start_reply, &reply);
#ifdef SANED_USES_AF_INDEP
if (reply.status == SANE_STATUS_GOOD)
{
struct sockaddr_storage ss;
char text_addr[64];
int len;
int error;
DBG (DBG_MSG, "process_request: waiting for data connection\n");
data_fd = accept (fd, 0, 0);
close (fd);
/* Get address of remote host */
len = sizeof (ss);
if (getpeername (data_fd, (struct sockaddr *) &ss, (socklen_t *) &len) < 0)
{
DBG (DBG_ERR, "process_request: getpeername failed: %s\n",
strerror (errno));
return;
}
error = getnameinfo ((struct sockaddr *) &ss, len, text_addr,
sizeof (text_addr), NULL, 0, NI_NUMERICHOST);
if (error)
{
DBG (DBG_ERR, "process_request: getnameinfo failed: %s\n",
gai_strerror (error));
return;
}
DBG (DBG_MSG, "process_request: access to data port from %s\n",
text_addr);
if (strcmp (text_addr, remote_ip) != 0)
{
DBG (DBG_ERR, "process_request: however, only %s is authorized\n",
text_addr);
DBG (DBG_ERR, "process_request: configuration problem or attack?\n");
close (data_fd);
data_fd = -1;
quit (0);
}
#else /* !SANED_USES_AF_INDEP */
if (reply.status == SANE_STATUS_GOOD)
{
struct sockaddr_in sin;
@ -1126,6 +1597,7 @@ process_request (Wire * w)
else
DBG (DBG_MSG, "process_request: access to data port from %s\n",
inet_ntoa (sin.sin_addr));
#endif /* SANED_USES_AF_INDEP */
if (data_fd < 0)
{
@ -1169,6 +1641,274 @@ process_request (Wire * w)
}
}
#ifdef SANED_USES_AF_INDEP
int
main (int argc, char *argv[])
{
int fd = -1;
int on = 1;
#ifdef TCP_NODELAY
int level = -1;
#endif
debug = DBG_WARN;
openlog ("saned", LOG_PID | LOG_CONS, LOG_DAEMON);
prog_name = strrchr (argv[0], '/');
if (prog_name)
++prog_name;
else
prog_name = argv[0];
byte_order.w = 0;
byte_order.ch = 1;
sanei_w_init (&wire, sanei_codec_bin_init);
wire.io.read = read;
wire.io.write = write;
if (argc == 2 &&
(strncmp (argv[1], "-d", 2) == 0 || strncmp (argv[1], "-s", 2) == 0))
{
/* don't operate in daemon mode: wait for connection request: */
struct addrinfo *res;
struct addrinfo *resp;
struct addrinfo hints;
struct sockaddr_in *sin;
#ifdef ENABLE_IPV6
struct sockaddr_in6 *sin6;
#endif /* ENABLE_IPV6 */
struct pollfd *fds = NULL;
struct pollfd *fdp = NULL;
int nfds;
int err;
int i;
short sane_port = htons (6566);
if (argv[1][2])
debug = atoi (argv[1] + 2);
if (strncmp (argv[1], "-d", 2) == 0)
log_to_syslog = SANE_FALSE;
DBG (DBG_WARN, "main: starting debug mode (level %d)\n", debug);
DBG (DBG_DBG,
"main: trying to get port for service `sane' (getaddrinfo)\n");
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = PF_UNSPEC;
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo (NULL, "sane", &hints, &res);
if (err)
{
/*
* You cannot pass (NULL, NULL, &hints, &res) to getaddrinfo,
* so request a good-old well known service, and change the port
* afterwards as a workaround
*/
err = getaddrinfo (NULL, "telnet", &hints, &res);
if (err)
{
DBG (DBG_ERR, "main: getaddrinfo() failed: %s\n", gai_strerror (err));
exit (1);
}
else
{
DBG (DBG_WARN, "main: \"sane\" service unknown on your host; you should add\n");
DBG (DBG_WARN, "main: sane 6566/tcp saned # SANE network scanner daemon\n");
DBG (DBG_WARN, "main: to your /etc/services file (or equivalent). Proceeding anyway.\n");
for (resp = res; resp != NULL; resp = resp->ai_next)
{
switch (resp->ai_family)
{
case AF_INET:
sin = (struct sockaddr_in *) resp->ai_addr;
sin->sin_port = sane_port;
break;
#ifdef ENABLE_IPV6
case AF_INET6:
sin6 = (struct sockaddr_in6 *) resp->ai_addr;
sin6->sin6_port = sane_port;
break;
#endif /* ENABLE_IPV6 */
}
}
}
}
for (resp = res, nfds = 0; resp != NULL; resp = resp->ai_next, nfds++)
;
fds = malloc (nfds * sizeof (struct pollfd));
if (fds == NULL)
{
DBG (DBG_ERR, "main: not enough memory for fds\n");
freeaddrinfo (res);
exit (1);
}
for (resp = res, i = 0, fdp = fds; resp != NULL; resp = resp->ai_next, i++, fdp++)
{
#ifdef ENABLE_IPV6
if ((resp->ai_family != AF_INET) && (resp->ai_family != AF_INET6))
#else
if (resp->ai_family != AF_INET)
#endif /* ENABLE_IPV6 */
{
fdp--;
nfds--;
continue;
}
DBG (DBG_DBG, "main: [%d] socket ()\n", i);
fd = socket (resp->ai_family, SOCK_STREAM, 0);
DBG (DBG_DBG, "main: [%d] setsockopt ()\n", i);
if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)))
DBG (DBG_ERR, "main: [%d] failed to put socket in SO_REUSEADDR mode (%s)\n",
i, strerror (errno));
DBG (DBG_DBG, "main: [%d] bind ()\n", i);
if (bind (fd, resp->ai_addr, resp->ai_addrlen) < 0)
{
/*
* binding a socket may fail, eg if we already
* to the IPv6 addr returned by getaddrinfo (usually the first one),
* or if IPv6 isn't supported, but saned was built with IPv6 support
*/
DBG (DBG_ERR, "main: [%d] bind failed: %s\n", i, strerror (errno));
close (fd);
nfds--;
fdp--;
continue;
}
DBG (DBG_DBG, "main: [%d] listen ()\n", i);
if (listen (fd, 1) < 0)
{
DBG (DBG_ERR, "main: [%d] listen failed: %s\n", i, strerror (errno));
exit (1);
}
fdp->fd = fd;
fdp->events = POLLIN;
}
resp = NULL;
freeaddrinfo (res);
if (nfds <= 0)
{
DBG (DBG_ERR, "main: couldn't bind an address. Exiting.\n");
exit (1);
}
DBG (DBG_MSG, "main: waiting for control connection\n");
while (1)
{
if (poll (fds, nfds, -1) < 0)
{
if (errno == EINTR)
continue;
else
{
DBG (DBG_ERR, "main: poll failed: %s\n", strerror (errno));
free (fds);
exit (1);
}
}
for (i = 0, fdp = fds; i < nfds; i++, fdp++)
{
if (! (fdp->revents & POLLIN))
continue;
wire.io.fd = accept (fdp->fd, 0, 0);
if (wire.io.fd < 0)
{
DBG (DBG_ERR, "main: accept failed: %s", strerror (errno));
free (fds);
exit (1);
}
for (i = 0, fdp = fds; i < nfds; i++, fdp++)
close (fdp->fd);
free (fds);
break;
}
break;
}
}
else
/* use filedescriptor opened by inetd: */
#ifdef HAVE_OS2_H
/* under OS/2, the socket handle is passed as argument on the command
line; the socket handle is relative to IBM TCP/IP, so a call
to impsockethandle() is required to add it to the EMX runtime */
if (argc == 2)
{
wire.io.fd = _impsockhandle (atoi (argv[1]), 0);
if (wire.io.fd == -1)
perror ("impsockhandle");
}
else
#endif /* HAVE_OS2_H */
wire.io.fd = 1;
signal (SIGALRM, quit);
signal (SIGPIPE, quit);
#ifdef TCP_NODELAY
# ifdef SOL_TCP
level = SOL_TCP;
# else /* !SOL_TCP */
/* Look up the protocol level in the protocols database. */
{
struct protoent *p;
p = getprotobyname ("tcp");
if (p == 0)
{
DBG (DBG_WARN, "main: cannot look up `tcp' protocol number");
}
else
level = p->p_proto;
}
# endif /* SOL_TCP */
if (level == -1
|| setsockopt (wire.io.fd, level, TCP_NODELAY, &on, sizeof (on)))
DBG (DBG_WARN, "main: failed to put socket in TCP_NODELAY mode (%s)",
strerror (errno));
#endif /* !TCP_NODELAY */
/* define the version string depending on which network code is used */
#ifdef ENABLE_IPV6
DBG (DBG_WARN, "saned (AF-indep+IPv6) from %s ready\n", PACKAGE_STRING);
#else
DBG (DBG_WARN, "saned (AF-indep) from %s ready\n", PACKAGE_STRING);
#endif /* ENABLE_IPV6 */
if (init (&wire) < 0)
quit (0);
while (1)
{
reset_watchdog ();
process_request (&wire);
}
}
#else /* !SANED_USES_AF_INDEP */
int
main (int argc, char *argv[])
{
@ -1287,7 +2027,7 @@ main (int argc, char *argv[])
p = getprotobyname ("tcp");
if (p == 0)
{
DBG (DBG_WARN, "main:: cannot look up `tcp' protocol number");
DBG (DBG_WARN, "main: cannot look up `tcp' protocol number");
}
else
level = p->p_proto;
@ -1310,3 +2050,4 @@ main (int argc, char *argv[])
process_request (&wire);
}
}
#endif /* SANED_USES_AF_INDEP */

Wyświetl plik

@ -11,6 +11,9 @@
/* Should we disable SCSI generic v3? */
#undef DISABLE_LINUX_SG_IO
/* Define to 1 if the system supports IPv6 */
#undef ENABLE_IPV6
/* Define to 1 if you have `alloca', as a function or macro. */
#undef HAVE_ALLOCA
@ -51,9 +54,15 @@
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the `getaddrinfo' function. */
#undef HAVE_GETADDRINFO
/* Define to 1 if you have the `getenv' function. */
#undef HAVE_GETENV
/* Define to 1 if you have the `getnameinfo' function. */
#undef HAVE_GETNAMEINFO
/* Define to 1 if you have the `getpagesize' function. */
#undef HAVE_GETPAGESIZE