/* * $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 #endif #include #include #include #include #include #include #include #include #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: */