You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

596 lines
14 KiB

/*
* $Id: inet.c,v 1.61 2006/10/12 14:21:22 desrod Exp $
*
* inet.c: Interface layer to TCP/IP NetSync connections
*
* Copyright (c) 1997, Kenneth Albanowski
* Copyright (c) 1999, Tilo Christ
* Copyright (c) 1999, John Franks
* Copyright (c) 2004, 2005 Florent Pillet
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
* General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include "pi-debug.h"
#include "pi-source.h"
#include "pi-inet.h"
#include "pi-cmp.h"
#include "pi-net.h"
/* Declare prototypes */
static void pi_inet_device_free (pi_device_t *dev);
static pi_protocol_t* pi_inet_protocol (pi_device_t *dev);
static pi_protocol_t* pi_inet_protocol_dup (pi_protocol_t *prot);
static void pi_inet_protocol_free (pi_protocol_t *prot);
static int pi_inet_close(pi_socket_t *ps);
static int pi_inet_connect(pi_socket_t *ps, struct sockaddr *addr, size_t addrlen);
static int pi_inet_bind(pi_socket_t *ps, struct sockaddr *addr, size_t addrlen);
static int pi_inet_listen(pi_socket_t *ps, int backlog);
static int pi_inet_accept(pi_socket_t *ps, struct sockaddr *addr, size_t *addrlen);
static ssize_t pi_inet_read(pi_socket_t *ps, pi_buffer_t *msg, size_t len, int flags);
static ssize_t pi_inet_write(pi_socket_t *ps, const unsigned char *msg, size_t len, int flags);
static int pi_inet_getsockopt(pi_socket_t *ps, int level, int option_name, void *option_value, size_t *option_len);
static int pi_inet_setsockopt(pi_socket_t *ps, int level, int option_name, const void *option_value, size_t *option_len);
static int pi_inet_flush(pi_socket_t *ps, int flags);
extern int pi_socket_init(pi_socket_t *ps);
pi_device_t*
pi_inet_device (int type)
{
pi_device_t *dev = NULL;
pi_inet_data_t *data = NULL;
dev = (pi_device_t *)malloc (sizeof (pi_device_t));
if (dev != NULL) {
data = (pi_inet_data_t *)malloc (sizeof (pi_inet_data_t));
if (data == NULL) {
free(dev);
dev = NULL;
}
}
if (dev != NULL && data != NULL) {
dev->free = pi_inet_device_free;
dev->protocol = pi_inet_protocol;
dev->bind = pi_inet_bind;
dev->listen = pi_inet_listen;
dev->accept = pi_inet_accept;
dev->connect = pi_inet_connect;
dev->close = pi_inet_close;
data->timeout = 0;
data->rx_bytes = 0;
data->rx_errors = 0;
data->tx_bytes = 0;
data->tx_errors = 0;
dev->data = data;
}
return dev;
}
static void
pi_inet_device_free (pi_device_t *dev)
{
ASSERT (dev != NULL);
if (dev != NULL) {
if (dev->data != NULL)
free(dev->data);
free(dev);
}
}
static pi_protocol_t*
pi_inet_protocol (pi_device_t *dev)
{
pi_protocol_t *prot;
pi_inet_data_t *data;
ASSERT (dev != NULL);
data = dev->data;
prot = (pi_protocol_t *)malloc (sizeof (pi_protocol_t));
if (prot != NULL) {
prot->level = PI_LEVEL_DEV;
prot->dup = pi_inet_protocol_dup;
prot->free = pi_inet_protocol_free;
prot->read = pi_inet_read;
prot->write = pi_inet_write;
prot->flush = pi_inet_flush;
prot->getsockopt = pi_inet_getsockopt;
prot->setsockopt = pi_inet_setsockopt;
prot->data = NULL;
}
return prot;
}
static pi_protocol_t*
pi_inet_protocol_dup (pi_protocol_t *prot)
{
pi_protocol_t *new_prot;
ASSERT (prot != NULL);
new_prot = (pi_protocol_t *)malloc (sizeof (pi_protocol_t));
if (new_prot != NULL) {
new_prot->level = prot->level;
new_prot->dup = prot->dup;
new_prot->free = prot->free;
new_prot->read = prot->read;
new_prot->write = prot->write;
new_prot->flush = prot->flush;
new_prot->getsockopt = prot->getsockopt;
new_prot->setsockopt = prot->setsockopt;
new_prot->data = NULL;
}
return new_prot;
}
static void
pi_inet_protocol_free (pi_protocol_t *prot)
{
ASSERT (prot != NULL);
if (prot != NULL)
free(prot);
}
static int
pi_inet_bind(pi_socket_t *ps, struct sockaddr *addr, size_t addrlen)
{
int opt,
sd,
err;
size_t optlen;
struct pi_sockaddr *paddr = (struct pi_sockaddr *) addr;
struct sockaddr_in serv_addr;
char *device = paddr->pi_device,
*port = NULL;
/* Figure out the addresses to allow */
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
if (strlen(device) > 1 && strncmp(device, "any", 3)) {
serv_addr.sin_addr.s_addr = inet_addr(device);
if (serv_addr.sin_addr.s_addr == (in_addr_t)-1) {
struct hostent *hostent = gethostbyname(device);
if (!hostent)
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
memcpy((char *) &serv_addr.sin_addr.s_addr,
hostent->h_addr, (size_t)hostent->h_length);
}
} else {
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
if ((port = strchr(device, ':')) != NULL) {
serv_addr.sin_port = htons(atoi(++port));
} else {
serv_addr.sin_port = htons(14238);
}
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
LOG((PI_DBG_DEV, PI_DBG_LVL_ERR,
"DEV BIND Inet: Unable to create socket\n"));
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
}
if ((err = pi_socket_setsd (ps, sd)) < 0)
return err;
opt = 1;
optlen = sizeof(opt);
if (setsockopt(ps->sd, SOL_SOCKET, SO_REUSEADDR, (void *) &opt,
(int)optlen) < 0) {
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
}
if (bind(ps->sd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO,
"DEV BIND Inet Bound to %s\n", device));
ps->raddr = malloc(addrlen);
memcpy(ps->raddr, addr, addrlen);
ps->raddrlen = addrlen;
ps->laddr = malloc(addrlen);
memcpy(ps->laddr, addr, addrlen);
ps->laddrlen = addrlen;
return 0;
}
static int
pi_inet_connect(pi_socket_t *ps, struct sockaddr *addr, size_t addrlen)
{
int sd,
err;
struct pi_sockaddr *paddr = (struct pi_sockaddr *) addr;
struct sockaddr_in serv_addr;
char *device = paddr->pi_device;
/* Figure out the addresses to allow */
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
if (strlen(device) > 1) {
serv_addr.sin_addr.s_addr = inet_addr(device);
if (serv_addr.sin_addr.s_addr == (in_addr_t)-1) {
struct hostent *hostent = gethostbyname(device);
if (!hostent) {
LOG((PI_DBG_DEV, PI_DBG_LVL_ERR,
"DEV CONNECT Inet: Unable"
" to determine host\n"));
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
}
memcpy((char *) &serv_addr.sin_addr.s_addr,
hostent->h_addr, (size_t)hostent->h_length);
}
} else {
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
serv_addr.sin_port = htons(14238);
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
LOG((PI_DBG_DEV, PI_DBG_LVL_ERR,
"DEV CONNECT Inet: Unable to create socket\n"));
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
}
if ((err = pi_socket_setsd (ps, sd)) < 0)
return err;
if (connect (ps->sd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0) {
LOG((PI_DBG_DEV, PI_DBG_LVL_ERR,
"DEV CONNECT Inet: Unable to connect\n"));
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
}
ps->raddr = malloc(addrlen);
memcpy(ps->raddr, addr, addrlen);
ps->raddrlen = addrlen;
ps->laddr = malloc(addrlen);
memcpy(ps->laddr, addr, addrlen);
ps->laddrlen = addrlen;
switch (ps->cmd) {
case PI_CMD_CMP:
if ((err = cmp_tx_handshake(ps)) < 0)
goto fail;
break;
case PI_CMD_NET:
if ((err = net_tx_handshake(ps)) < 0)
goto fail;
break;
}
ps->state = PI_SOCK_CONN_INIT;
ps->command = 0;
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO, "DEV CONNECT Inet: Connected\n"));
return 0;
fail:
return err;
}
static int
pi_inet_listen(pi_socket_t *ps, int backlog)
{
int result;
result = listen(ps->sd, backlog);
if (result == 0)
ps->state = PI_SOCK_LISTEN;
return result;
}
static int
pi_inet_accept(pi_socket_t *ps, struct sockaddr *addr, size_t *addrlen)
{
int sd,
err,
split = 0,
chunksize = 0;
size_t len,
size;
pl_socklen_t l = 0;
unsigned char cmp_flags;
if (addrlen)
l = *addrlen;
sd = accept(ps->sd, addr, &l);
if (addrlen)
*addrlen = l;
if (sd < 0) {
pi_set_error(ps->sd, sd);
err = PI_ERR_GENERIC_SYSTEM;
goto fail;
}
pi_socket_setsd(ps, sd);
pi_socket_init(ps);
switch (ps->cmd) {
case PI_CMD_CMP:
if ((err = cmp_rx_handshake(ps, 57600, 0)) < 0)
goto fail;
/* propagate the long packet format flag to both command and non-command stacks */
size = sizeof(cmp_flags);
pi_getsockopt(ps->sd, PI_LEVEL_CMP, PI_CMP_FLAGS, &cmp_flags, &size);
if (cmp_flags & CMP_FL_LONG_PACKET_SUPPORT) {
int use_long_format = 1;
size = sizeof(int);
pi_setsockopt(ps->sd, PI_LEVEL_PADP, PI_PADP_USE_LONG_FORMAT,
&use_long_format, &size);
ps->command ^= 1;
pi_setsockopt(ps->sd, PI_LEVEL_PADP, PI_PADP_USE_LONG_FORMAT,
&use_long_format, &size);
ps->command ^= 1;
}
break;
case PI_CMD_NET:
/* network: make sure we don't split writes. set socket option
* on both the command and non-command instances of the protocol
*/
len = sizeof (split);
pi_setsockopt(ps->sd, PI_LEVEL_NET, PI_NET_SPLIT_WRITES,
&split, &len);
len = sizeof (chunksize);
pi_setsockopt(ps->sd, PI_LEVEL_NET, PI_NET_WRITE_CHUNKSIZE,
&chunksize, &len);
ps->command ^= 1;
len = sizeof (split);
pi_setsockopt(ps->sd, PI_LEVEL_NET, PI_NET_SPLIT_WRITES,
&split, &len);
len = sizeof (chunksize);
pi_setsockopt(ps->sd, PI_LEVEL_NET, PI_NET_WRITE_CHUNKSIZE,
&chunksize, &len);
ps->command ^= 1;
if ((err = net_rx_handshake(ps)) < 0)
goto fail;
break;
}
ps->state = PI_SOCK_CONN_ACCEPT;
ps->command = 0;
ps->dlprecord = 0;
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO, "DEV INET ACCEPT accepted\n"));
return ps->sd;
fail:
return err;
}
static int
pi_inet_close(pi_socket_t *ps)
{
if (ps->sd) {
close(ps->sd);
ps->sd = 0;
}
if (ps->laddr) {
free(ps->laddr);
ps->laddr = NULL;
}
if (ps->raddr) {
free(ps->raddr);
ps->raddr = NULL;
}
return 0;
}
static int
pi_inet_flush(pi_socket_t *ps, int flags)
{
char buf[256];
int fl;
if (flags & PI_FLUSH_INPUT) {
if ((fl = fcntl(ps->sd, F_GETFL, 0)) != -1) {
fcntl(ps->sd, F_SETFL, fl | O_NONBLOCK);
while (recv(ps->sd, buf, sizeof(buf), 0) > 0)
;
fcntl(ps->sd, F_SETFL, fl);
}
}
return 0;
}
static ssize_t
pi_inet_write(pi_socket_t *ps, const unsigned char *msg, size_t len, int flags)
{
int total,
nwrote;
pi_inet_data_t *data = (pi_inet_data_t *)ps->device->data;
struct timeval t;
fd_set ready;
FD_ZERO(&ready);
FD_SET(ps->sd, &ready);
total = len;
while (total > 0) {
if (data->timeout == 0) {
if (select(ps->sd + 1, 0, &ready, 0, 0) < 0
&& errno == EINTR)
continue;
} else {
t.tv_sec = data->timeout / 1000;
t.tv_usec = (data->timeout % 1000) * 1000;
if (select(ps->sd + 1, 0, &ready, 0, &t) == 0)
return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT);
}
if (!FD_ISSET(ps->sd, &ready)) {
ps->state = PI_SOCK_CONN_BREAK;
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
}
nwrote = write(ps->sd, msg, len);
if (nwrote < 0) {
/* test errno to properly set the socket error */
if (errno == EPIPE || errno == EBADF) {
ps->state = PI_SOCK_CONN_BREAK;
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
}
return pi_set_error(ps->sd, PI_ERR_SOCK_IO);
}
total -= nwrote;
}
data->tx_bytes += len;
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO, "DEV TX Inet Bytes: %d\n", len));
return len;
}
static ssize_t
pi_inet_read(pi_socket_t *ps, pi_buffer_t *msg, size_t len, int flags)
{
int r,
fl = 0;
pi_inet_data_t *data = (pi_inet_data_t *)ps->device->data;
fd_set ready;
struct timeval t;
if (pi_buffer_expect (msg, len) == NULL) {
errno = ENOMEM;
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
}
if (flags == PI_MSG_PEEK)
fl = MSG_PEEK;
FD_ZERO(&ready);
FD_SET(ps->sd, &ready);
/* If timeout == 0, wait forever for packet, otherwise wait till
timeout milliseconds */
if (data->timeout == 0)
select(ps->sd + 1, &ready, 0, 0, 0);
else {
t.tv_sec = data->timeout / 1000;
t.tv_usec = (data->timeout % 1000) * 1000;
if (select(ps->sd + 1, &ready, 0, 0, &t) == 0)
return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT);
}
/* If data is available in time, read it */
if (FD_ISSET(ps->sd, &ready)) {
r = recv(ps->sd, msg->data + msg->used, len, fl);
if (r < 0) {
if (errno == EPIPE || errno == EBADF) {
ps->state = PI_SOCK_CONN_BREAK;
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
}
return pi_set_error(ps->sd, PI_ERR_SOCK_IO);
}
data->rx_bytes += r;
msg->used += r;
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO, "DEV RX Inet Bytes: %d\n", r));
return r;
}
/* otherwise throw out any current packet and return */
LOG((PI_DBG_DEV, PI_DBG_LVL_WARN, "DEV RX Inet timeout\n"));
data->rx_errors++;
return 0;
}
static int
pi_inet_getsockopt(pi_socket_t *ps, int level, int option_name,
void *option_value, size_t *option_len)
{
pi_inet_data_t *data = (pi_inet_data_t *)ps->device->data;
switch (option_name) {
case PI_DEV_TIMEOUT:
if (*option_len != sizeof (data->timeout)) {
errno = EINVAL;
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
}
memcpy (option_value, &data->timeout,
sizeof (data->timeout));
*option_len = sizeof (data->timeout);
break;
}
return 0;
}
static int
pi_inet_setsockopt(pi_socket_t *ps, int level, int option_name,
const void *option_value, size_t *option_len)
{
pi_inet_data_t *data = (pi_inet_data_t *)ps->device->data;
switch (option_name) {
case PI_DEV_TIMEOUT:
if (*option_len != sizeof (data->timeout)) {
errno = EINVAL;
return pi_set_error(ps->sd, PI_ERR_GENERIC_ARGUMENT);
}
memcpy (&data->timeout, option_value,
sizeof (data->timeout));
break;
}
return 0;
}
/* vi: set ts=8 sw=4 sts=4 noexpandtab: cin */
/* ex: set tabstop=4 expandtab: */
/* Local Variables: */
/* indent-tabs-mode: t */
/* c-basic-offset: 8 */
/* End: */