syndilights/open-lighting-architecture/libartnet-1.1.0/artnet/network.c

743 lines
18 KiB
C

/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* network.c
* Network code for libartnet
* Copyright (C) 2004-2007 Simon Newton
*
*/
#include <errno.h>
#ifndef WIN32
#include <sys/socket.h> // socket before net/if.h for mac
#include <net/if.h>
#include <sys/ioctl.h>
#else
typedef int socklen_t;
#include <winsock2.h>
#include <Lm.h>
#include <iphlpapi.h>
#endif
#include <unistd.h>
#include "private.h"
#ifdef HAVE_GETIFADDRS
#ifdef HAVE_LINUX_IF_PACKET_H
#define USE_GETIFADDRS
#endif
#endif
#ifdef USE_GETIFADDRS
#include <ifaddrs.h>
#include <linux/types.h> // required by if_packet
#include <linux/if_packet.h>
#endif
enum { INITIAL_IFACE_COUNT = 10 };
enum { IFACE_COUNT_INC = 5 };
enum { IFNAME_SIZE = 32 }; // 32 sounds a reasonable size
typedef struct iface_s {
struct sockaddr_in ip_addr;
struct sockaddr_in bcast_addr;
int8_t hw_addr[ARTNET_MAC_SIZE];
char if_name[IFNAME_SIZE];
struct iface_s *next;
} iface_t;
unsigned long LOOPBACK_IP = 0x7F000001;
/*
* Free memory used by the iface's list
* @param head a pointer to the head of the list
*/
static void free_ifaces(iface_t *head) {
iface_t *ift, *ift_next;
for (ift = head; ift != NULL; ift = ift_next) {
ift_next = ift->next;
free(ift);
}
}
/*
* Add a new interface to an interface list
* @param head pointer to the head of the list
* @param tail pointer to the end of the list
* @return a new iface_t or void
*/
static iface_t *new_iface(iface_t **head, iface_t **tail) {
iface_t *iface = (iface_t*) calloc(1, sizeof(iface_t));
if (!iface) {
artnet_error("%s: calloc error %s" , __FUNCTION__, strerror(errno));
return NULL;
}
memset(iface, 0, sizeof(iface_t));
if (!*head) {
*head = *tail = iface;
} else {
(*tail)->next = iface;
*tail = iface;
}
return iface;
}
#ifdef WIN32
/*
* Set if_head to point to a list of iface_t structures which represent the
* interfaces on this machine
* @param ift_head the address of the pointer to the head of the list
*/
static int get_ifaces(iface_t **if_head) {
iface_t *if_tail, *iface;
PIP_ADAPTER_INFO pAdapter = NULL;
PIP_ADAPTER_INFO pAdapterInfo;
IP_ADDR_STRING *ipAddress;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
unsigned long net, mask;
if_tail = NULL;
while (1) {
pAdapterInfo = (IP_ADAPTER_INFO*) malloc(ulOutBufLen);
if (!pAdapterInfo) {
artnet_error("Error allocating memory needed for GetAdaptersinfo");
return ARTNET_EMEM;
}
DWORD status = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (status == NO_ERROR)
break;
free(pAdapterInfo);
if (status != ERROR_BUFFER_OVERFLOW) {
printf("GetAdaptersInfo failed with error: %d\n", (int) status);
return ARTNET_ENET;
}
}
for (pAdapter = pAdapterInfo;
pAdapter && pAdapter < pAdapterInfo + ulOutBufLen;
pAdapter = pAdapter->Next) {
if(pAdapter->Type != MIB_IF_TYPE_ETHERNET)
continue;
for (ipAddress = &pAdapter->IpAddressList; ipAddress;
ipAddress = ipAddress->Next) {
net = inet_addr(ipAddress->IpAddress.String);
if (net) {
// Windows doesn't seem to have the notion of an interface being 'up'
// so we check if this interface has an address assigned.
iface = new_iface(if_head, &if_tail);
if (!iface)
continue;
mask = inet_addr(ipAddress->IpMask.String);
strncpy(iface->if_name, pAdapter->AdapterName, IFNAME_SIZE);
memcpy(iface->hw_addr, pAdapter->Address, ARTNET_MAC_SIZE);
iface->ip_addr.sin_addr.s_addr = net;
iface->bcast_addr.sin_addr.s_addr = (
(net & mask) | (0xFFFFFFFF ^ mask));
}
}
}
free(pAdapterInfo);
return (ARTNET_EOK);
}
# else // not WIN32
#ifdef USE_GETIFADDRS
/*
* Check if we are interested in this interface
* @param ifa a pointer to a ifaddr struct
*/
static void add_iface_if_needed(iface_t **head, iface_t **tail,
struct ifaddrs *ifa) {
// skip down, loopback and non inet interfaces
if (!ifa || !ifa->ifa_addr) return;
if (!(ifa->ifa_flags & IFF_UP)) return;
if (ifa->ifa_flags & IFF_LOOPBACK) return;
if (ifa->ifa_addr->sa_family != AF_INET) return;
iface_t *iface = new_iface(head, tail);
struct sockaddr_in *sin = (struct sockaddr_in*) ifa->ifa_addr;
iface->ip_addr.sin_addr = sin->sin_addr;
strncpy(iface->if_name, ifa->ifa_name, IFNAME_SIZE - 1);
if (ifa->ifa_flags & IFF_BROADCAST) {
sin = (struct sockaddr_in *) ifa->ifa_broadaddr;
iface->bcast_addr.sin_addr = sin->sin_addr;
}
}
/*
* Set if_head to point to a list of iface_t structures which represent the
* interfaces on this machine
* @param ift_head the address of the pointer to the head of the list
*/
static int get_ifaces(iface_t **if_head) {
struct ifaddrs *ifa_list, *ifa_iter;
iface_t *if_tail, *if_iter;
struct sockaddr_ll *sll;
char *if_name, *cptr;
*if_head = if_tail = NULL;
if (getifaddrs(&ifa_list) != 0) {
artnet_error("Error getting interfaces: %s", strerror(errno));
return ARTNET_ENET;
}
for (ifa_iter = ifa_list; ifa_iter; ifa_iter = ifa_iter->ifa_next)
add_iface_if_needed(if_head, &if_tail, ifa_iter);
// Match up the interfaces with the corrosponding AF_PACKET interface
// to fetch the hw addresses
//
// TODO: Will probably not work on OS X, it should
// return AF_LINK -type sockaddr
for (if_iter = *if_head; if_iter; if_iter = if_iter->next) {
if_name = strdup(if_iter->if_name);
// if this is an alias, get mac of real interface
if ((cptr = strchr(if_name, ':')))
*cptr = 0;
// Find corresponding iface_t structure
for (ifa_iter = ifa_list; ifa_iter; ifa_iter = ifa_iter->ifa_next) {
if ((!ifa_iter->ifa_addr) || ifa_iter->ifa_addr->sa_family != AF_PACKET)
continue;
if (strncmp(if_name, ifa_iter->ifa_name, IFNAME_SIZE) == 0) {
// Found matching hw-address
sll = (struct sockaddr_ll*) ifa_iter->ifa_addr;
memcpy(if_iter->hw_addr, sll->sll_addr, ARTNET_MAC_SIZE);
break;
}
}
free(if_name);
}
freeifaddrs(ifa_list);
return 0;
}
#else // no GETIFADDRS
/*
* Set if_head to point to a list of iface_t structures which represent the
* interfaces on this machine
* @param ift_head the address of the pointer to the head of the list
*/
static int get_ifaces(iface_t **if_head) {
struct ifconf ifc;
struct ifreq *ifr, ifrcopy;
struct sockaddr_in *sin;
int len, lastlen, flags;
char *buf, *ptr;
iface_t *if_tail, *iface;
int ret = ARTNET_EOK;
int sd;
*if_head = if_tail = NULL;
// create socket to get iface config
sd = socket(PF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
artnet_error("%s : Could not create socket %s", __FUNCTION__, strerror(errno));
ret = ARTNET_ENET;
goto e_return;
}
// first use ioctl to get a listing of interfaces
lastlen = 0;
len = INITIAL_IFACE_COUNT * sizeof(struct ifreq);
for (;;) {
buf = malloc(len);
if (buf == NULL) {
artnet_error_malloc();
ret = ARTNET_EMEM;
goto e_free;
}
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sd, SIOCGIFCONF, &ifc) < 0) {
if (errno != EINVAL || lastlen != 0) {
artnet_error("%s : ioctl error %s", __FUNCTION__, strerror(errno));
ret = ARTNET_ENET;
goto e_free;
}
} else {
if (ifc.ifc_len == lastlen)
break;
lastlen = ifc.ifc_len;
}
len += IFACE_COUNT_INC * sizeof(struct ifreq);
free(buf);
}
// loop through each iface
for (ptr = buf; ptr < buf + ifc.ifc_len;) {
ifr = (struct ifreq*) ptr;
// work out length here
#ifdef HAVE_SOCKADDR_SA_LEN
len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
#else
switch (ifr->ifr_addr.sa_family) {
#ifdef IPV6
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
#endif
case AF_INET:
default:
len = sizeof(SA);
break;
}
#endif
ptr += sizeof(ifr->ifr_name) + len;
// look for AF_INET interfaces
if (ifr->ifr_addr.sa_family == AF_INET) {
ifrcopy = *ifr;
if (ioctl(sd, SIOCGIFFLAGS, &ifrcopy) < 0) {
artnet_error("%s : ioctl error %s" , __FUNCTION__, strerror(errno));
ret = ARTNET_ENET;
goto e_free_list;
}
flags = ifrcopy.ifr_flags;
if ((flags & IFF_UP) == 0)
continue; //skip down interfaces
if ((flags & IFF_LOOPBACK))
continue; //skip lookback
iface = new_iface(if_head, &if_tail);
if (!iface)
goto e_free_list;
sin = (struct sockaddr_in *) &ifr->ifr_addr;
iface->ip_addr.sin_addr = sin->sin_addr;
// fetch bcast address
#ifdef SIOCGIFBRDADDR
if (flags & IFF_BROADCAST) {
if (ioctl(sd, SIOCGIFBRDADDR, &ifrcopy) < 0) {
artnet_error("%s : ioctl error %s" , __FUNCTION__, strerror(errno));
ret = ARTNET_ENET;
goto e_free_list;
}
sin = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
iface->bcast_addr.sin_addr = sin->sin_addr;
}
#endif
// fetch hardware address
#ifdef SIOCGIFHWADDR
if (flags & SIOCGIFHWADDR) {
if (ioctl(sd, SIOCGIFHWADDR, &ifrcopy) < 0) {
artnet_error("%s : ioctl error %s", __FUNCTION__, strerror(errno));
ret = ARTNET_ENET;
goto e_free_list;
}
memcpy(&iface->hw_addr, ifrcopy.ifr_hwaddr.sa_data, ARTNET_MAC_SIZE);
}
#endif
/* ok, if that all failed we should prob try and use sysctl to work out the bcast
* and hware addresses
* i'll leave that for another day
*/
}
}
free(buf);
return ARTNET_EOK;
e_free_list:
free_ifaces(*if_head);
e_free:
free(buf);
close(sd);
e_return:
return ret;
}
#endif // GETIFADDRS
#endif // not WIN32
/*
* Scan for interfaces, and work out which one the user wanted to use.
*/
int artnet_net_init(node n, const char *preferred_ip) {
iface_t *ift, *ift_head = NULL;
struct in_addr wanted_ip;
int found = FALSE;
int i;
int ret = ARTNET_EOK;
if ((ret = get_ifaces(&ift_head)))
goto e_return;
if (n->state.verbose) {
printf("#### INTERFACES FOUND ####\n");
for (ift = ift_head; ift != NULL; ift = ift->next) {
printf("IP: %s\n", inet_ntoa(ift->ip_addr.sin_addr));
printf(" bcast: %s\n" , inet_ntoa(ift->bcast_addr.sin_addr));
printf(" hwaddr: ");
for (i = 0; i < ARTNET_MAC_SIZE; i++) {
if (i)
printf(":");
printf("%02x", (uint8_t) ift->hw_addr[i]);
}
printf("\n");
}
printf("#########################\n");
}
if (preferred_ip) {
// search through list of interfaces for one with the correct address
ret = artnet_net_inet_aton(preferred_ip, &wanted_ip);
if (ret)
goto e_cleanup;
for (ift = ift_head; ift != NULL; ift = ift->next) {
if (ift->ip_addr.sin_addr.s_addr == wanted_ip.s_addr) {
found = TRUE;
n->state.ip_addr = ift->ip_addr.sin_addr;
n->state.bcast_addr = ift->bcast_addr.sin_addr;
memcpy(&n->state.hw_addr, &ift->hw_addr, ARTNET_MAC_SIZE);
break;
}
}
if (!found) {
artnet_error("Cannot find ip %s", preferred_ip);
ret = ARTNET_ENET;
goto e_cleanup;
}
} else {
if (ift_head) {
// pick first address
// copy ip address, bcast address and hardware address
n->state.ip_addr = ift_head->ip_addr.sin_addr;
n->state.bcast_addr = ift_head->bcast_addr.sin_addr;
memcpy(&n->state.hw_addr, &ift_head->hw_addr, ARTNET_MAC_SIZE);
} else {
artnet_error("No interfaces found!");
ret = ARTNET_ENET;
}
}
e_cleanup:
free_ifaces(ift_head);
e_return :
return ret;
}
/*
* Start listening on the socket
*/
int artnet_net_start(node n) {
int sock;
struct sockaddr_in servAddr;
int true_flag = TRUE;
node tmp;
// only attempt to bind if we are the group master
if (n->peering.master == TRUE) {
#ifdef WIN32
// check winsock version
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
if (WSAStartup(wVersionRequested, &wsaData) != 0)
return (-1);
if (wsaData.wVersion != wVersionRequested)
return (-2);
#endif
// create socket
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
artnet_error("Could not create socket %s", artnet_net_last_error());
return ARTNET_ENET;
}
memset(&servAddr, 0x00, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(ARTNET_PORT);
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (n->state.verbose)
printf("Binding to %s \n", inet_ntoa(servAddr.sin_addr));
// bind sockets
if (bind(sock, (SA *) &servAddr, sizeof(servAddr)) == -1) {
artnet_error("Failed to bind to socket %s", artnet_net_last_error());
artnet_net_close(sock);
return ARTNET_ENET;
}
// allow bcasting
if (setsockopt(sock,
SOL_SOCKET,
SO_BROADCAST,
(char*) &true_flag, // char* for win32
sizeof(int)) == -1) {
artnet_error("Failed to bind to socket %s", artnet_net_last_error());
artnet_net_close(sock);
return ARTNET_ENET;
}
#ifdef WIN32
// ### LH - 22.08.2008
// make it possible to reuse port, if SO_REUSEADDR
// exists on operating system
// NEVER USE SO_EXCLUSIVEADDRUSE, as that freezes the application
// on WinXP, if port is in use !
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &true_flag,
sizeof(true_flag)) < 0) {
artnet_error("Set reuse failed", artnet_net_last_error());
artnet_net_close(sock);
return ARTNET_ENET;
}
u_long true = 1;
if (SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &true)) {
artnet_error("ioctlsocket", artnet_net_last_error());
artnet_net_close(sock);
return ARTNET_ENET;
}
#endif
n->sd = sock;
// Propagate the socket to all our peers
for (tmp = n->peering.peer; tmp && tmp != n; tmp = tmp->peering.peer)
tmp->sd = sock;
}
return ARTNET_EOK;
}
/*
* Receive a packet.
*/
int artnet_net_recv(node n, artnet_packet p, int delay) {
ssize_t len;
struct sockaddr_in cliAddr;
socklen_t cliLen = sizeof(cliAddr);
fd_set rset;
struct timeval tv;
int maxfdp1 = n->sd + 1;
FD_ZERO(&rset);
FD_SET((unsigned int) n->sd, &rset);
tv.tv_usec = 0;
tv.tv_sec = delay;
p->length = 0;
switch (select(maxfdp1, &rset, NULL, NULL, &tv)) {
case 0:
// timeout
return RECV_NO_DATA;
break;
case -1:
if ( errno != EINTR) {
artnet_error("Select error %s", artnet_net_last_error());
return ARTNET_ENET;
}
return ARTNET_EOK;
break;
default:
break;
}
// need a check here for the amount of data read
// should prob allow an extra byte after data, and pass the size as sizeof(Data) +1
// then check the size read and if equal to size(data)+1 we have an error
len = recvfrom(n->sd,
(char*) &(p->data), // char* for win32
sizeof(p->data),
0,
(SA*) &cliAddr,
&cliLen);
if (len < 0) {
artnet_error("Recvfrom error %s", artnet_net_last_error());
return ARTNET_ENET;
}
if (cliAddr.sin_addr.s_addr == n->state.ip_addr.s_addr ||
ntohl(cliAddr.sin_addr.s_addr) == LOOPBACK_IP) {
p->length = 0;
return ARTNET_EOK;
}
p->length = len;
memcpy(&(p->from), &cliAddr.sin_addr, sizeof(struct in_addr));
// should set to in here if we need it
return ARTNET_EOK;
}
/*
* Send a packet.
*/
int artnet_net_send(node n, artnet_packet p) {
struct sockaddr_in addr;
int ret;
if (n->state.mode != ARTNET_ON)
return ARTNET_EACTION;
addr.sin_family = AF_INET;
addr.sin_port = htons(ARTNET_PORT);
addr.sin_addr = p->to;
p->from = n->state.ip_addr;
if (n->state.verbose)
printf("sending to %s\n" , inet_ntoa(addr.sin_addr));
ret = sendto(n->sd,
(char*) &p->data, // char* required for win32
p->length,
0,
(SA*) &addr,
sizeof(addr));
if (ret == -1) {
artnet_error("Sendto failed: %s", artnet_net_last_error());
n->state.report_code = ARTNET_RCUDPFAIL;
return ARTNET_ENET;
} else if (p->length != ret) {
artnet_error("failed to send full datagram");
n->state.report_code = ARTNET_RCSOCKETWR1;
return ARTNET_ENET;
}
if (n->callbacks.send.fh) {
get_type(p);
n->callbacks.send.fh(n, p, n->callbacks.send.data);
}
return ARTNET_EOK;
}
/*
int artnet_net_reprogram(node n) {
iface_t *ift_head, *ift;
int i;
ift_head = get_ifaces(n->sd[0]);
for (ift = ift_head;ift != NULL; ift = ift->next ) {
printf("IP: %s\n", inet_ntoa(ift->ip_addr.sin_addr) );
printf(" bcast: %s\n" , inet_ntoa(ift->bcast_addr.sin_addr) );
printf(" hwaddr: ");
for(i = 0; i < 6; i++ ) {
printf("%hhx:", ift->hw_addr[i] );
}
printf("\n");
}
free_ifaces(ift_head);
}*/
int artnet_net_set_fdset(node n, fd_set *fdset) {
FD_SET((unsigned int) n->sd, fdset);
return ARTNET_EOK;
}
/*
* Close a socket
*/
int artnet_net_close(int sock) {
#ifdef WIN32
shutdown(sock, SD_BOTH);
closesocket(sock);
//WSACancelBlockingCall();
WSACleanup();
#else
if (close(sock)) {
artnet_error(artnet_net_last_error());
return ARTNET_ENET;
}
#endif
return ARTNET_EOK;
}
/*
* Convert a string to an in_addr
*/
int artnet_net_inet_aton(const char *ip_address, struct in_addr *address) {
#ifdef HAVE_INET_ATON
if (!inet_aton(ip_address, address)) {
#else
in_addr_t *addr = (in_addr_t*) address;
if ((*addr = inet_addr(ip_address)) == INADDR_NONE) {
#endif
artnet_error("IP conversion from %s failed", ip_address);
return ARTNET_EARG;
}
return ARTNET_EOK;
}
/*
*
*/
const char *artnet_net_last_error() {
#ifdef WIN32
static char error_str[10];
int error = WSAGetLastError();
snprintf(error_str, sizeof(error_str), "%d", error);
return error_str;
#else
return strerror(errno);
#endif
}