diff options
Diffstat (limited to 'debian/pilot-link/pilot-link-0.12.5-dfsg/libpisock/inet.c')
-rw-r--r-- | debian/pilot-link/pilot-link-0.12.5-dfsg/libpisock/inet.c | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/debian/pilot-link/pilot-link-0.12.5-dfsg/libpisock/inet.c b/debian/pilot-link/pilot-link-0.12.5-dfsg/libpisock/inet.c new file mode 100644 index 00000000..9906bdde --- /dev/null +++ b/debian/pilot-link/pilot-link-0.12.5-dfsg/libpisock/inet.c @@ -0,0 +1,595 @@ +/* + * $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: */ |