pixma_bjnp: added inintial ipv6 support

merge-requests/1/head
Louis Lagendijk 2012-08-16 23:42:08 +02:00
rodzic f9a70e14be
commit d6040014df
3 zmienionych plików z 189 dodań i 94 usunięć

Wyświetl plik

@ -1,6 +1,6 @@
/* SANE - Scanner Access Now Easy. /* SANE - Scanner Access Now Easy.
Copyright (C) 2008 by Louis Lagendijk Copyright (C) 2008 2012 by Louis Lagendijk
This file is part of the SANE package. This file is part of the SANE package.
@ -98,7 +98,7 @@
/* static data */ /* static data */
static device_t device[BJNP_NO_DEVICES]; static device_t device[BJNP_NO_DEVICES];
static int first_free_device = 0; static int bjnp_no_devices = 0;
/* /*
* Private functions * Private functions
@ -205,9 +205,13 @@ static int
bjnp_open_tcp (int devno) bjnp_open_tcp (int devno)
{ {
int sock; int sock;
int socklen;
int val; int val;
int domain;
if ((sock = socket (PF_INET, SOCK_STREAM, 0)) < 0) domain = device[devno].addr -> sa_family;
if ((sock = socket (domain, SOCK_STREAM, 0)) < 0)
{ {
PDBG (pixma_dbg (LOG_CRIT, "bjnp_open_tcp: Can not create socket: %s\n", PDBG (pixma_dbg (LOG_CRIT, "bjnp_open_tcp: Can not create socket: %s\n",
strerror (errno))); strerror (errno)));
@ -238,9 +242,9 @@ bjnp_open_tcp (int devno)
fcntl (sock, F_SETFD, FD_CLOEXEC); fcntl (sock, F_SETFD, FD_CLOEXEC);
socklen = (device[devno].addr -> sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof( struct sockaddr_in6);
if (connect if (connect
(sock, (struct sockaddr *) &device[devno].addr, (sock, device[devno].addr, socklen) != 0)
sizeof (device[devno].addr)) != 0)
{ {
PDBG (pixma_dbg PDBG (pixma_dbg
(LOG_CRIT, "bjnp_open_tcp: Can not connect to scanner: %s\n", (LOG_CRIT, "bjnp_open_tcp: Can not connect to scanner: %s\n",
@ -252,7 +256,7 @@ bjnp_open_tcp (int devno)
} }
static int static int
split_uri (const char *devname, char *method, char *hostname, int *port, split_uri (const char *devname, char *method, char *host, char *port,
char *args) char *args)
{ {
char copy[1024]; char copy[1024];
@ -273,7 +277,7 @@ split_uri (const char *devname, char *method, char *hostname, int *port,
i++; i++;
} }
if (((strncmp (start + i, "://", 3) != 0)) || (i > 255)) if (((strncmp (start + i, "://", 3) != 0)) || (i > BJNP_METHOD_MAX -1 ))
{ {
PDBG (pixma_dbg (LOG_NOTICE, "Can not find method in %s (offset %d)\n", PDBG (pixma_dbg (LOG_NOTICE, "Can not find method in %s (offset %d)\n",
devname, i)); devname, i));
@ -285,51 +289,70 @@ split_uri (const char *devname, char *method, char *hostname, int *port,
start = start + i + 3; start = start + i + 3;
/* /*
* retrieve hostname * retrieve host
*/ */
i = 0; if (start[0] == '[')
while ((start[i] != '\0') && (start[i] != '/') && (start[i] != ':'))
{ {
i++; /* literal IPv6 address */
char *end_of_address = strchr(start, ']');
if ( ( end_of_address == NULL) ||
( (end_of_address[1] != ':') && (end_of_address[1] != '/' ) && (end_of_address[1] != '\0' )) ||
( (end_of_address - start) >= BJNP_HOST_MAX ) )
{
PDBG (pixma_dbg (LOG_NOTICE, "Can not find hostname or address in %s\n", devname));
return -1;
}
next = end_of_address[1];
*end_of_address = '\0';
strcpy(host, start + 1);
start = end_of_address +1;
}
else
{
i = 0;
while ((start[i] != '\0') && (start[i] != '/') && (start[i] != ':'))
{
i++;
}
next = start[i];
start[i] = '\0';
if ((i == 0) || (i >= BJNP_HOST_MAX ) )
{
PDBG (pixma_dbg (LOG_NOTICE, "Can not find hostname or address in %s\n", devname));
return -1;
}
strcpy (host, start);
start = start + i +1;
} }
if ((strlen (start) == 0) || (i > 255))
{
PDBG (pixma_dbg (LOG_NOTICE, "Can not find hostname in %s\n", devname));
return -1;
}
next = start[i];
strncpy (hostname, start, i);
hostname[i] = '\0';
start = start + i + 1;
/* /*
* retrieve port number * retrieve port number
*/ */
if (next != ':') if (next != ':')
*port = 0; strcpy(port, "");
else else
{ {
i = 0; char *end_of_port = strchr(start, '/');
while ((start[i] != '\0') && (start[i] != '/')) if (end_of_port == NULL)
{ {
if ((start[i] < '0') || (start[i] > '9') || (i > 5)) next = '\0';
{ }
PDBG (pixma_dbg (LOG_NOTICE, "Can not find port number in %s\n", else
devname)); {
return -1; next = *end_of_port;
} *end_of_port = '\0';
}
i++; if ((strlen(start) == 0) || (strlen(start) >= BJNP_PORT_MAX ) )
} {
next = start[i]; PDBG (pixma_dbg (LOG_NOTICE, "Can not find port in %s (have \"%s\")\n", devname, start));
start[i] = '\0'; return -1;
sscanf (start, "%d", port); }
start = start + i + 1; strcpy(port, start);
} }
/* /*
@ -337,7 +360,14 @@ split_uri (const char *devname, char *method, char *hostname, int *port,
*/ */
if (next == '/') if (next == '/')
{
i = strlen(start);
if ( i >= BJNP_ARGS_MAX)
{
PDBG (pixma_dbg (LOG_NOTICE, "Argument string too long in %s\n", devname));
}
strcpy (args, start); strcpy (args, start);
}
else else
strcpy (args, ""); strcpy (args, "");
return 0; return 0;
@ -358,7 +388,7 @@ set_cmd (int devno, struct BJNP_command *cmd, char cmd_code, int payload_len)
cmd->unknown1 = htons (0); cmd->unknown1 = htons (0);
if (devno == -1) if (devno == -1)
{ {
/* device not opened, use 0 for serial and session) */ /* device not yet opened, use 0 for serial and session) */
cmd->seq_no = htons (0); cmd->seq_no = htons (0);
cmd->session_id = htons (0); cmd->session_id = htons (0);
} }
@ -366,9 +396,9 @@ set_cmd (int devno, struct BJNP_command *cmd, char cmd_code, int payload_len)
{ {
cmd->seq_no = htons (++(device[devno].serial)); cmd->seq_no = htons (++(device[devno].serial));
cmd->session_id = (cmd_code == CMD_UDP_POLL ) ? 0 : htons (device[devno].session_id); cmd->session_id = (cmd_code == CMD_UDP_POLL ) ? 0 : htons (device[devno].session_id);
device[devno].last_cmd = cmd_code;
} }
cmd->payload_len = htonl (payload_len); cmd->payload_len = htonl (payload_len);
device[devno].last_cmd = cmd_code;
} }
static int static int
@ -380,12 +410,33 @@ bjnp_setup_udp_socket ( const int dev_no )
*/ */
int sockfd; int sockfd;
int socklen;
char addr_string[256];
int port;
PDBG (pixma_dbg (LOG_DEBUG, "setup_udp_socket: Setting up the UDP socket to: %s:%d\n", struct sockaddr * addr = device[dev_no].addr;
inet_ntoa (device[dev_no].addr.sin_addr), if ( addr->sa_family == AF_INET)
ntohs (device[dev_no].addr.sin_port))); {
struct sockaddr_in *addr_ipv4 = (struct sockaddr_in *) addr;
inet_ntop( AF_INET, &(addr_ipv4 -> sin_addr.s_addr), addr_string, 256);
port = ntohs (addr_ipv4->sin_port);
}
else if (addr->sa_family == AF_INET6)
{
struct sockaddr_in6 *addr_ipv6 = (struct sockaddr_in6 *) addr;
inet_ntop( AF_INET6, &(addr_ipv6->sin6_addr), addr_string, 256);
port = ntohs (addr_ipv6->sin6_port);
}
else
{
/* unknown address family, should not occur */
return -1;
}
if ((sockfd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) PDBG (pixma_dbg (LOG_DEBUG, "setup_udp_socket: Setting up the UDP socket to: %s port %d\n",
addr_string, port ) );
if ((sockfd = socket (addr->sa_family, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{ {
PDBG (pixma_dbg PDBG (pixma_dbg
(LOG_CRIT, "setup_udp_socket: can not open socket - %s\n", (LOG_CRIT, "setup_udp_socket: can not open socket - %s\n",
@ -393,9 +444,9 @@ bjnp_setup_udp_socket ( const int dev_no )
return -1; return -1;
} }
socklen = (device[dev_no].addr -> sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
if (connect if (connect
(sockfd, (struct sockaddr *) &device[dev_no].addr, (sockfd, device[dev_no].addr, socklen )!= 0)
(socklen_t) sizeof (struct sockaddr_in)) != 0)
{ {
PDBG (pixma_dbg PDBG (pixma_dbg
(LOG_CRIT, "setup_udp_socket: connect failed- %s\n", (LOG_CRIT, "setup_udp_socket: connect failed- %s\n",
@ -570,6 +621,7 @@ parse_scanner_address (char *resp_buf, char *address, char *serial)
/* do reverse name lookup, if hostname can not be found return ip-address */ /* do reverse name lookup, if hostname can not be found return ip-address */
/* TODO: use ipv6 compatible function or start using recvfrom?*/
inet_aton (ip_address, &ip_addr); inet_aton (ip_address, &ip_addr);
myhost = gethostbyaddr ((void *) &ip_addr, sizeof (ip_addr), AF_INET); myhost = gethostbyaddr ((void *) &ip_addr, sizeof (ip_addr), AF_INET);
@ -580,6 +632,7 @@ parse_scanner_address (char *resp_buf, char *address, char *serial)
/* some buggy routers return rubbish if reverse lookup fails, so /* some buggy routers return rubbish if reverse lookup fails, so
* we do a forward lookup on the received name to see if it matches */ * we do a forward lookup on the received name to see if it matches */
/* TODO: get rid of hints or use protocol family? */
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_socktype = 0; hints.ai_socktype = 0;
hints.ai_protocol = 0; hints.ai_protocol = 0;
@ -709,6 +762,7 @@ bjnp_send_broadcast (int sockfd, struct in_addr broadcast_addr,
sendaddr.sin_addr = broadcast_addr; sendaddr.sin_addr = broadcast_addr;
memset (sendaddr.sin_zero, '\0', sizeof sendaddr.sin_zero); memset (sendaddr.sin_zero, '\0', sizeof sendaddr.sin_zero);
/* TODO: IPv6 compat */
if ((num_bytes = sendto (sockfd, &cmd, size, 0, if ((num_bytes = sendto (sockfd, &cmd, size, 0,
(struct sockaddr *) &sendaddr, (struct sockaddr *) &sendaddr,
sizeof (sendaddr))) != size) sizeof (sendaddr))) != size)
@ -1175,19 +1229,19 @@ bjnp_recv_data (int devno, SANE_Byte * buffer, size_t * len)
static BJNP_Status static BJNP_Status
bjnp_allocate_device (SANE_String_Const devname, SANE_Int * dn, bjnp_allocate_device (SANE_String_Const devname, SANE_Int * dn,
char *res_hostname) char *res_host)
{ {
char method[256]; char method[BJNP_METHOD_MAX];
char hostname[256]; char host[BJNP_HOST_MAX];
int port; char port[BJNP_PORT_MAX] = "";
char args[256]; char args[BJNP_ARGS_MAX];
struct hostent *result; struct addrinfo *res, *cur;
struct in_addr *addr_list; int result;
int i; int i;
PDBG (pixma_dbg (LOG_DEBUG, "bjnp_allocate_device(%s)\n", devname)); PDBG (pixma_dbg (LOG_DEBUG, "bjnp_allocate_device(%s) %d\n", devname, bjnp_no_devices));
if (split_uri (devname, method, hostname, &port, args) != 0) if (split_uri (devname, method, host, port, args) != 0)
{ {
return BJNP_STATUS_INVAL; return BJNP_STATUS_INVAL;
} }
@ -1209,51 +1263,80 @@ bjnp_allocate_device (SANE_String_Const devname, SANE_Int * dn,
return BJNP_STATUS_INVAL; return BJNP_STATUS_INVAL;
} }
result = gethostbyname (hostname); if (strlen(port) == 0)
if ((result == NULL) || result->h_addrtype != AF_INET)
{ {
PDBG (pixma_dbg (LOG_CRIT, "Cannot resolve hostname: %s\n", hostname)); sprintf( port, "%d", BJNP_PORT_SCAN );
}
result = getaddrinfo (host, port, NULL, &res );
if (result != 0 )
{
PDBG (pixma_dbg (LOG_CRIT, "Cannot resolve host: %s\n", host));
return SANE_STATUS_INVAL; return SANE_STATUS_INVAL;
} }
if (port == 0) PDBG(pixma_dbg( LOG_INFO, "Have now %s - %s - %d\n", host, port, bjnp_no_devices ) );
/* Check if a device number is already allocated to any of the scanner's addresses */
for (i = 0; i < bjnp_no_devices; i++)
{ {
port = BJNP_PORT_SCAN; cur = res;
} while( cur != NULL)
{
addr_list = (struct in_addr *) *result->h_addr_list; struct sockaddr * dev_addr = device[i].addr;
if (dev_addr->sa_family == cur -> ai_family)
/* Check if a device number is already allocated */ {
if( cur -> ai_family == AF_INET)
for (i = 0; i < first_free_device; i++) {
{ struct sockaddr_in *dev_addr4 = (struct sockaddr_in *) dev_addr;
/* check address, AF_INET is assumed */ struct sockaddr_in *res_addr4 = (struct sockaddr_in *) cur->ai_addr;
if ( (dev_addr4->sin_port == res_addr4->sin_port) &&
if ((device[i].addr.sin_port == htons (port)) && (dev_addr4->sin_addr.s_addr == res_addr4->sin_addr.s_addr))
(device[i].addr.sin_addr.s_addr == addr_list[0].s_addr)) {
{ *dn = i;
*dn = i; freeaddrinfo(res);
return BJNP_STATUS_ALREADY_ALLOCATED; return BJNP_STATUS_ALREADY_ALLOCATED;
} }
} else if (cur -> ai_family == AF_INET6 )
{
struct sockaddr_in6 *dev_addr6 = ( struct sockaddr_in6 *) dev_addr;
struct sockaddr_in6 *res_addr6 = ( struct sockaddr_in6 *) cur->ai_addr;
if ( (dev_addr6->sin6_port == res_addr6->sin6_port) &&
(memcmp(&(dev_addr6->sin6_addr), &(res_addr6->sin6_addr), sizeof(struct sockaddr_in6)) == 0))
{
*dn = i;
freeaddrinfo(res);
return BJNP_STATUS_ALREADY_ALLOCATED;
}
}
}
cur = cur->ai_next;
}
} }
PDBG (pixma_dbg (LOG_INFO, "Scanner not found, adding it: %s:%s\n", host, port));
sleep(4);
/* return hostname if required */ /* return hostname if required */
if (res_hostname != NULL) if (res_host != NULL)
strcpy (res_hostname, hostname); strcpy (res_host, host);
/* /*
* No existing device structure found, fill new device structure * No existing device structure found, fill new device structure
*/ */
if (first_free_device == BJNP_NO_DEVICES) if (bjnp_no_devices == BJNP_NO_DEVICES)
{ {
PDBG (pixma_dbg PDBG (pixma_dbg
(LOG_CRIT, (LOG_CRIT,
"Too many devices, ran out of device structures, can not add %s\n", "Too many devices, ran out of device structures, can not add %s\n",
devname)); devname));
freeaddrinfo(res);
return BJNP_STATUS_INVAL; return BJNP_STATUS_INVAL;
} }
*dn = first_free_device++; *dn = bjnp_no_devices;
bjnp_no_devices++;
device[*dn].open = 1; device[*dn].open = 1;
device[*dn].active = 0; device[*dn].active = 0;
#ifdef PIXMA_BJNP_USE_STATUS #ifdef PIXMA_BJNP_USE_STATUS
@ -1262,9 +1345,12 @@ bjnp_allocate_device (SANE_String_Const devname, SANE_Int * dn,
device[*dn].status_key = 0; device[*dn].status_key = 0;
#endif #endif
device[*dn].tcp_socket = -1; device[*dn].tcp_socket = -1;
device[*dn].addr.sin_family = AF_INET;
device[*dn].addr.sin_port = htons (port); /* this should fit both a struct sockaddr_in and a sockaddr_in6 */
device[*dn].addr.sin_addr = addr_list[0]; device[*dn].addr = (struct sockaddr *) malloc(sizeof ( struct sockaddr_in6) );
memcpy(device[*dn].addr, res-> ai_addr, sizeof(struct sockaddr_in6) );
device[*dn].session_id = 0; device[*dn].session_id = 0;
device[*dn].serial = -1; device[*dn].serial = -1;
device[*dn].bjnp_timeout = 0; device[*dn].bjnp_timeout = 0;
@ -1276,10 +1362,12 @@ bjnp_allocate_device (SANE_String_Const devname, SANE_Int * dn,
device[*dn].blocksize = 1024; device[*dn].blocksize = 1024;
device[*dn].short_read = 0; device[*dn].short_read = 0;
freeaddrinfo(res);
if (bjnp_setup_udp_socket(*dn) == -1 ) if (bjnp_setup_udp_socket(*dn) == -1 )
{ {
first_free_device--; bjnp_no_devices--;
free(device[*dn].addr);
return BJNP_STATUS_INVAL; return BJNP_STATUS_INVAL;
} }
return BJNP_STATUS_GOOD; return BJNP_STATUS_GOOD;
@ -1296,7 +1384,7 @@ bjnp_allocate_device (SANE_String_Const devname, SANE_Int * dn,
extern void extern void
sanei_bjnp_init (void) sanei_bjnp_init (void)
{ {
first_free_device = 0; bjnp_no_devices = 0;
} }
/** /**
@ -1344,7 +1432,7 @@ sanei_bjnp_find_devices (const char **conf_devices,
#endif #endif
PDBG (pixma_dbg (LOG_INFO, "sanei_bjnp_find_devices:\n")); PDBG (pixma_dbg (LOG_INFO, "sanei_bjnp_find_devices:\n"));
first_free_device = 0; bjnp_no_devices = 0;
for (i=0; i < BJNP_SOCK_MAX; i++) for (i=0; i < BJNP_SOCK_MAX; i++)
{ {
@ -1352,10 +1440,13 @@ sanei_bjnp_find_devices (const char **conf_devices,
} }
/* First add devices from config file */ /* First add devices from config file */
if (conf_devices[0] == NULL)
PDBG (pixma_dbg( LOG_DEBUG, "No devices specified in configuration file.\n" ) );
for (i = 0; conf_devices[i] != NULL; i++) for (i = 0; conf_devices[i] != NULL; i++)
{ {
PDBG (pixma_dbg PDBG (pixma_dbg
(LOG_DEBUG, "Adding configured scanner: %s\n", conf_devices[i])); (LOG_DEBUG, "Adding scanner from pixma.conf: %s\n", conf_devices[i]));
/* Allocate device structure for scanner and read its model */ /* Allocate device structure for scanner and read its model */
switch (bjnp_allocate_device (conf_devices[i], &dev_no, hostname)) switch (bjnp_allocate_device (conf_devices[i], &dev_no, hostname))
@ -1563,7 +1654,7 @@ sanei_bjnp_find_devices (const char **conf_devices,
} }
active_fdset = fdset; active_fdset = fdset;
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 500 * USLEEP_MS; timeout.tv_usec = 2 * BJNP_BROADCAST_INTERVAL * USLEEP_MS;
} }
PDBG (pixma_dbg (LOG_DEBUG, "scanner discovery finished...\n")); PDBG (pixma_dbg (LOG_DEBUG, "scanner discovery finished...\n"));

Wyświetl plik

@ -66,9 +66,9 @@ extern void sanei_bjnp_init (void);
/** Find scanners responding to a BJNP broadcast. /** Find scanners responding to a BJNP broadcast.
* *
* The function attach is called for every device which has been found. * The function attach is called for every device which has been found.
* Serial is the address of the pinter in himan readable form of max * Serial is the address of the scanner in human readable form of max
* SHORT_HOSTNAME_MAX characters * SHORT_HOSTNAME_MAX characters
* @param conf_devices lsit of pre-configures device URI's to attach * @param conf_devices list of pre-configures device URI's to attach
* @param attach attach function * @param attach attach function
* @param pixma_devices device informatio needed by attach function * @param pixma_devices device informatio needed by attach function
* *

Wyświetl plik

@ -62,8 +62,12 @@
#define BJNP_MODEL_MAX 64 /* max allowed size for make&model */ #define BJNP_MODEL_MAX 64 /* max allowed size for make&model */
#define BJNP_STATUS_MAX 256 /* max size for status string */ #define BJNP_STATUS_MAX 256 /* max size for status string */
#define BJNP_IEEE1284_MAX 1024 /* max. allowed size of IEEE1284 id */ #define BJNP_IEEE1284_MAX 1024 /* max. allowed size of IEEE1284 id */
#define BJNP_METHOD_MAX 16 /* max length of method */
#define BJNP_HOST_MAX 128 /* max length of hostname or address */
#define BJNP_PORT_MAX 64 /* max length of port string */
#define BJNP_ARGS_MAX 128 /* max length of argument string */
#define BJNP_NO_DEVICES 16 /* max number of open devices */ #define BJNP_NO_DEVICES 16 /* max number of open devices */
#define BJNP_BROADCAST_INTERVAL 30 /* ms between broadcasts */ #define BJNP_BROADCAST_INTERVAL 10 /* ms between broadcasts */
#define SCAN_BUF_MAX 65536 /* size of scanner data intermediate buffer */ #define SCAN_BUF_MAX 65536 /* size of scanner data intermediate buffer */
#define MAX_SELECT_ATTEMPTS 5 /* max nr of retries on select (EINTR) */ #define MAX_SELECT_ATTEMPTS 5 /* max nr of retries on select (EINTR) */
#define USLEEP_MS 1000 /* sleep for 1 msec */ #define USLEEP_MS 1000 /* sleep for 1 msec */
@ -272,7 +276,7 @@ typedef struct device_s
int active; /* connection is active (has open tcp connection */ int active; /* connection is active (has open tcp connection */
int tcp_socket; /* open tcp socket for communcation to scannner */ int tcp_socket; /* open tcp socket for communcation to scannner */
int udp_socket; /* open udp socket for communication to scanner */ int udp_socket; /* open udp socket for communication to scanner */
struct sockaddr_in addr; struct sockaddr * addr; /* ip-address of the scanner */
int session_id; /* session id used in bjnp protocol for TCP packets */ int session_id; /* session id used in bjnp protocol for TCP packets */
int16_t serial; /* sequence number of command */ int16_t serial; /* sequence number of command */
int bjnp_timeout; /* timeout (msec) for next poll command */ int bjnp_timeout; /* timeout (msec) for next poll command */