/* * file x11_socket.c - true bsd sockets for xblast * * $Id: x11_socket.c,v 1.3 2004/05/14 10:00:36 alfie Exp $ * * Program XBLAST * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2; or (at your option) * any later version * * This program is distributed in the hope that it will be entertaining, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "x11_socket.h" #include "socket.h" #include "x11_common.h" #include "x11_joystick.h" #include "x11_sound.h" #include "str_util.h" #include "com.h" /* * local constants */ #define CONNECT_TIMEOUT 60 #define ACCEPT_TIMEOUT 30 #define LISTENQ 5 #ifndef SHUT_WR #define SHUT_WR 1 #endif #ifndef MSG_DONTWAIT #define MSG_DONTWAIT 0 #endif /* * type defintions */ typedef struct _xb_socket_address { //ssize_t len; socklen_t len; struct sockaddr *addr; } XBSocketAddress; #ifdef sparc typedef uint32_t u_int32_t; #endif struct _xb_socket { int fd; XBSocketAddress sock; XBSocketAddress peer; XBBool shutDown; }; /* sockets */ static int socketMax = 0; static int socketX11 = -1; static int socketSnd = -1; static fd_set socketReadSet; static fd_set socketWriteSet; static fd_set fdJoystickSet; /* interface list */ static XBSocketInterface *inter = NULL; static size_t numInter = 0; /* * local prototypes */ static void DeleteInterfaces (void); #ifdef DEBUG_SOCKET static void DebugFdSet (const char *header, fd_set *set) { int i; Dbg_Out ("%s:", header); for (i = 0; i <= socketMax; i ++) { if (FD_ISSET (i, set) ) { Dbg_Out (" %d", i); } } Dbg_Out ("\n"); } #endif /* * handler for SIGCHLD */ static void HandleSigChld (int sig_num) { int stat; pid_t child; while (0 < (child = waitpid (-1, &stat, WNOHANG) ) ) { Dbg_Out ("child %d terminated\n", child); } } /* HandleSigChld */ /* * initialisation routine */ XBBool Socket_Init (void) { static XBBool initDone = XBFalse; if (! initDone) { signal (SIGCHLD, HandleSigChld); signal (SIGPIPE, SIG_IGN); signal (SIGALRM, SIG_IGN); initDone = XBTrue; } /* clear fd_Set for sockets */ FD_ZERO (&socketReadSet); FD_ZERO (&socketWriteSet); FD_ZERO (&fdJoystickSet); return XBTrue; } /* Net_Init */ /* * cleaning up */ void Socket_Finish (void) { } /* Socket_Finish */ /* * adress family fo socket */ int Socket_Fd (const XBSocket *pSocket) { assert (NULL != pSocket); return pSocket->fd; } /* Socket_Fd */ /* * adress family fo socket */ int Socket_Family (const XBSocket *pSocket) { assert (NULL != pSocket); return pSocket->sock.addr->sa_family; } /* Socket_Family */ /* * create socket structure */ XBSocket * Socket_Alloc (int family) { int len; XBSocket *pSocket; switch (family) { case AF_INET: len = sizeof (struct sockaddr_in); break; default: return NULL; } /* alloc socket data structure */ pSocket = calloc (1, sizeof (XBSocket) ); assert (NULL != pSocket); pSocket->fd = -1; /* out address */ pSocket->sock.len = len; pSocket->sock.addr = calloc (1, len); assert (NULL != pSocket->sock.addr); pSocket->sock.addr->sa_family = family; /* other addresse */ pSocket->peer.len = len; pSocket->peer.addr = calloc (1, len); assert (NULL != pSocket->peer.addr); pSocket->peer.addr->sa_family = family; /* set shutdown flags to false */ pSocket->shutDown = XBFalse; /* that's all */ return pSocket; } /* AllocSocketInet */ /* * free socket structure memory */ void Socket_Free (XBSocket *pSocket) { assert (NULL != pSocket); if (NULL != pSocket->sock.addr) { free (pSocket->sock.addr); } if (NULL != pSocket->peer.addr) { free (pSocket->peer.addr); } free (pSocket); } /* Socket_Free */ /* * get inet address */ static u_int32_t GetAddressInet (const char *hostName) { int32_t addr; struct hostent *serverEnt; assert (hostName != NULL); /* try to convert ip-adress string to address */ if (-1L != (addr = inet_addr (hostName) ) ) { return ntohl (addr); } /* lookup hostname */ if (NULL != (serverEnt = gethostbyname (hostName) ) ) { return ntohl (*(int32_t *) serverEnt->h_addr_list[0]); } return 0L; } /* GetAddressInet */ /* * set socket adress */ XBBool Socket_SetAddressInet (XBSocket *pSocket, XBBool peer, const char *hostName, unsigned short port) { XBSocketAddress *sa; u_int32_t addr; struct sockaddr_in *serverAddr; assert (NULL != pSocket); sa = peer ? &pSocket->peer : &pSocket->sock; /* get host name */ if (NULL != hostName) { if (0 == (addr = GetAddressInet (hostName) ) ) { return XBFalse; } } else { addr = INADDR_ANY; } assert (NULL != sa); memset (sa->addr, 0, sa->len); serverAddr = (struct sockaddr_in *) sa->addr; serverAddr->sin_family = AF_INET; /* IP */ serverAddr->sin_addr.s_addr = htonl (addr); /* host address */ serverAddr->sin_port = htons (port); /* our well known port */ return XBTrue; } /* SetAddressInet */ /* * create socket */ XBBool Socket_Open (XBSocket *pSocket, int type) { assert (pSocket != NULL); /* now create a stream socket */ if (-1 == (pSocket->fd = socket (pSocket->sock.addr->sa_family, type, 0) ) ) { return XBFalse; } Dbg_Out ("open socket %d (type=%d)\n", pSocket->fd, type); return XBTrue; } /* Socket */ /* * close socket */ void Socket_Close (XBSocket *pSocket) { assert (NULL != pSocket); if (pSocket->fd < 0 || pSocket->shutDown) { return; } Dbg_Out ("close socket %d %s:%u %s:%u\n", pSocket->fd, Socket_HostName (pSocket, XBFalse), Socket_HostPort (pSocket, XBFalse), Socket_HostName (pSocket, XBTrue), Socket_HostPort (pSocket, XBTrue) ); if (0 != close (pSocket->fd) ) { Dbg_Out ("error while closing socket %d\n", pSocket->fd); } else { pSocket->shutDown = XBTrue; } } /* Socket_Close */ /* * close socket */ void Socket_ShutdownWrite (XBSocket *pSocket) { assert (NULL != pSocket); if (pSocket->fd < 0 || pSocket->shutDown) { return; } Dbg_Out ("shutdown write socket %d %s:%u %s:%u\n", pSocket->fd, Socket_HostName (pSocket, XBFalse), Socket_HostPort (pSocket, XBFalse), Socket_HostName (pSocket, XBTrue), Socket_HostPort (pSocket, XBTrue) ); if (0 != shutdown (pSocket->fd, SHUT_WR) ) { Dbg_Out ("error while shutting down socket %d: ", pSocket->fd); } else { pSocket->shutDown = XBTrue; } } /* Socket_Shutdown */ /* * connect to server (generic) */ XBBool Socket_Connect (XBSocket *pSocket) { assert (pSocket != NULL); /* connect to serverAdr */ alarm (CONNECT_TIMEOUT); if (-1 == connect (pSocket->fd, pSocket->peer.addr, pSocket->peer.len) ) { alarm (0); return XBFalse; } alarm (0); /* now get adress assigned by kernel. the cast to void* is needed since not all systems know socklen_t */ if (-1 == getsockname (pSocket->fd, pSocket->sock.addr, (void *) &pSocket->sock.len) ) { return XBFalse; } Dbg_Out ("connection established\n"); return XBTrue; } /* Connect */ /* * bind a datagramm socket */ XBBool Socket_Bind (XBSocket *pSocket) { /* bind to port */ if (-1 == bind (pSocket->fd, pSocket->sock.addr, pSocket->sock.len) ) { return XBFalse; } /* now get adress assigned by kernel. the cast to void* is needed since not all systems know socklen_t */ if (-1 == getsockname (pSocket->fd, pSocket->sock.addr, (void *) &pSocket->sock.len) ) { return XBFalse; } /* that's all */ return XBTrue; } /* Bind */ /* * */ XBBool Socket_Accept (XBSocket *pSocket, const XBSocket *pListen) { assert (pSocket != NULL); assert (pListen != NULL); /* set timeout */ alarm (CONNECT_TIMEOUT); if (-1 == (pSocket->fd = accept (pListen->fd, pSocket->peer.addr, (void *) &pSocket->peer.len) ) ) { alarm (0); return XBFalse; } alarm (0); Dbg_Out ("accept socket %d\n", pSocket->fd); /* now retrieve local adresse */ if (-1 == getsockname (pSocket->fd, pSocket->sock.addr, (void *) &pSocket->sock.len) ) { return XBFalse; } /* that's all */ return XBTrue; } /* Accept */ /* * listen to */ XBBool Socket_Listen (XBSocket *pSocket) { assert (pSocket != NULL); /* now listen for client to connect */ if (0 != listen (pSocket->fd, LISTENQ) ) { return XBFalse; } return XBTrue; } /* Listen */ /* * write n bytes to socket (for non blocking i/o) */ int Socket_Send (const XBSocket *pSocket, const void *buf, size_t len) { int result; assert (NULL != pSocket); assert (NULL != buf); result = send (pSocket->fd, buf, len, MSG_DONTWAIT); if (result < 0) { Dbg_Out ("Socket_Send: errno = %d\n", errno); return (EAGAIN == errno) ? XB_SOCKET_WOULD_BLOCK : XB_SOCKET_ERROR; } return result; } /* Net_Write */ /* * send n byte to given host */ int Socket_SendTo (XBSocket *pSocket, const void *buf, size_t len, const char *host, unsigned short port) { int result; assert (NULL != pSocket); assert (NULL != buf); assert (NULL != host); /* convert destionation adress */ if (! Socket_SetAddressInet (pSocket, XBTrue, host, port)) { return -1; } /* now write data */ result = sendto (pSocket->fd, buf, len, MSG_DONTWAIT, pSocket->peer.addr, pSocket->peer.len); if (result < 0) { Dbg_Out ("Socket_SendTo: errno = %d\n", errno); return (EAGAIN == errno) ? XB_SOCKET_WOULD_BLOCK : XB_SOCKET_ERROR; } return result; } /* Net_SendTo */ /* * read n bytes from socket */ int Socket_Receive (const XBSocket *pSocket, void *buf, size_t len) { int result; assert (NULL != pSocket); assert (NULL != buf); result = recv (pSocket->fd, buf, len, 0); if (result < 0) { Dbg_Out ("Socket_Receive: errno = %d\n", errno); return (EAGAIN == errno) ? XB_SOCKET_WOULD_BLOCK : XB_SOCKET_ERROR; } return result; } /* Net_Read */ /* * receive upto n bytes from socket */ int Socket_ReceiveFrom (XBSocket *pSocket, void *buf, size_t len, const char **host, unsigned short *port) { ssize_t numRead; assert (NULL != pSocket); assert (NULL != buf); assert (NULL != host); numRead = recvfrom (pSocket->fd, buf, len, 0, pSocket->peer.addr, (void *) &pSocket->peer.len); if (numRead > 0) { *host = Socket_HostName (pSocket, XBTrue); *port = Socket_HostPort (pSocket, XBTrue); } else { *host = NULL; *port = 0; if (numRead < 0) { Dbg_Out ("Socket_Receive: errno = %d\n", errno); return (EAGAIN == errno) ? XB_SOCKET_WOULD_BLOCK : XB_SOCKET_ERROR; } } return numRead; } /* Net_ReceiveFrom */ /* * get host name of client */ const char * Socket_HostName (const XBSocket *pSocket, XBBool peer) { const XBSocketAddress *sa; struct sockaddr_in *inetAddr; assert (NULL != pSocket); sa = peer ? &pSocket->peer : &pSocket->sock; assert (NULL != sa); assert (NULL != sa->addr); inetAddr = (struct sockaddr_in *) sa->addr; return inet_ntoa (inetAddr->sin_addr); } /* AddressName */ /* * get port of host */ unsigned Socket_HostPort (const XBSocket *pSocket, XBBool peer) { const XBSocketAddress *sa; struct sockaddr_in *inetAddr; assert (NULL != pSocket); sa = peer ? &pSocket->peer : &pSocket->sock; assert (NULL != sa); assert (NULL != sa->addr); inetAddr = (struct sockaddr_in *) sa->addr; return ntohs (inetAddr->sin_port); } /* HostPort */ /* * enable or disable broadcast */ XBBool Socket_SetBroadcast (XBSocket *pSocket, XBBool enable) { int flag = enable ? 1: 0; return (0 == setsockopt (pSocket->fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof (flag) ) ); } /* Socket_SetBroadcast */ /* * delete list with all interfaces */ static void DeleteInterfaces (void) { if (NULL != inter) { size_t i; for (i = 0; i < numInter; i ++) { if (NULL != inter[i].name) { free (inter[i].name); } if (NULL != inter[i].addrDevice) { free (inter[i].addrDevice); } if (NULL != inter[i].addrBroadcast) { free (inter[i].addrBroadcast); } } free (inter); } inter = NULL; numInter = 0; } /* DeleteInterfaces */ /* * get list of all interfaces */ static const struct ifconf * GetInterfaceConfig (int fd) { size_t len, lastLen; char *buf = NULL; /*---*/ static struct ifconf ifConf; /* get list of all interfaces */ len = 100 * sizeof (struct ifreq); lastLen = 0; for (;;) { /* alloc buffer to receive data */ buf = calloc (1, len ); assert (NULL != buf); /* prepare structure for query */ ifConf.ifc_len = len; ifConf.ifc_buf = buf; /* query list of interfaces */ if (-1 == ioctl (fd, SIOCGIFCONF, &ifConf)) { if (errno != EINVAL || lastLen != 0) { free (buf); return NULL; } } else if (ifConf.ifc_len == lastLen) { /* success */ return &ifConf; } else { /* net new length */ lastLen = ifConf.ifc_len; } /* next guess */ len += 10 * sizeof (struct ifreq); free (buf); } return XBFalse; } /* GetInterfaceConfig */ /* * */ static XBBool GetSingleInterface (int fd, XBSocketInterface *pInter, const struct ifreq *ifReq, size_t *len) { struct ifreq ifrFlags; struct ifreq ifrBroadcast; struct sockaddr_in *inetDevice; struct sockaddr_in *inetBroadcast; /* get length of current entry */ switch (ifReq->ifr_addr.sa_family) { #ifdef IPV6 case AF_INET6: *len = IFNAMSIZ + sizeof (struct sockaddr_in6); break; #endif case AF_INET: default: *len = IFNAMSIZ + sizeof (struct sockaddr); break; } Dbg_Out ("%u bytes ", *len); /* checks for protocol family */ if (ifReq->ifr_addr.sa_family != AF_INET) { return XBFalse; } /* get flags ... */ ifrFlags = *ifReq; if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifrFlags) ) { return XBFalse; } /* check if interface is down */ if (! (IFF_UP & ifrFlags.ifr_flags) ) { return XBFalse; } /* try to get broadcast adress */ inetBroadcast = NULL; ifrBroadcast = *ifReq; if (IFF_BROADCAST & ifrFlags.ifr_flags) { if (-1 != (ioctl (fd, SIOCGIFBRDADDR, &ifrBroadcast) ) ) { inetBroadcast = (struct sockaddr_in *) &ifrBroadcast.ifr_broadaddr; } } /* device address is simple */ inetDevice = (struct sockaddr_in *) &ifReq->ifr_addr; /* store data */ pInter->name = DupString (ifReq->ifr_name); pInter->addrDevice = DupString (inet_ntoa (inetDevice->sin_addr)); pInter->addrBroadcast = inetBroadcast ? DupString (inet_ntoa (inetBroadcast->sin_addr)) : NULL; Dbg_Out ("found interface %s %s %s\n", pInter->name, pInter->addrDevice, pInter->addrBroadcast ? pInter->addrBroadcast : ""); return XBTrue; } /* GetSingleInterface */ /* * list available interfaces */ const XBSocketInterface * Socket_GetInterfaces (size_t *pNum) { int fd; size_t maxInter; char *ptr; const struct ifconf *ifConf = NULL; size_t len; assert (pNum != NULL); /* clean up */ DeleteInterfaces (); /* open UDP socket */ fd = socket (AF_INET, SOCK_DGRAM, 0); if (-1 == fd) { goto Error; } /* get config */ if (NULL == (ifConf = GetInterfaceConfig (fd) ) ) { goto Error; } #ifdef DEBUG { size_t i; Dbg_Out ("Interface Config (%u bytes)", ifConf->ifc_len); for (i = 0; i < ifConf->ifc_len; i ++) { if (i % 8 == 0) { Dbg_Out ("\n"); } Dbg_Out ("%02x ", (unsigned) (unsigned char) ifConf->ifc_buf[i]); } Dbg_Out ("\n"); } #endif /* alloc result buffer */ numInter = 0; maxInter = ifConf->ifc_len / sizeof (struct ifreq); inter = calloc (maxInter, sizeof (XBSocketInterface)); assert (NULL != inter); Dbg_Out ("max inter = %u\n", maxInter); /* now walk thru buffer */ for (ptr = ifConf->ifc_buf; ptr < ifConf->ifc_buf + ifConf->ifc_len; ptr += len) { if (GetSingleInterface (fd, inter + numInter, (struct ifreq *) ptr, &len) ) { numInter ++; } } /* clean up */ free (ifConf->ifc_buf); close (fd); /* that's all */ *pNum = numInter; return inter; Error: if (NULL != ifConf && NULL != ifConf->ifc_buf) { free (ifConf->ifc_buf); } if (-1 != fd) { close (fd); } DeleteInterfaces (); *pNum = 0; return NULL; } /* Socket_GetInterfaces */ /* * */ void RegisterDisplay (int fd) { if (fd > socketMax) { socketMax = fd; } FD_SET (fd, &socketReadSet); socketX11 = fd; } /* RegisterDisplay */ /* * */ void RegisterJoystick (int fd) { if (fd > socketMax) { socketMax = fd; } FD_SET (fd, &socketReadSet); FD_SET (fd, &fdJoystickSet); } /* RegisterJoystick */ /* * */ void RegisterSound (int fd) { if (fd > socketMax) { socketMax = fd; } assert (-1 == socketSnd); FD_SET (fd, &socketReadSet); socketSnd = fd; } /* RegisterSound */ /* * */ void UnregisterSound (int fd) { assert (fd == socketSnd); FD_CLR (fd, &socketReadSet); socketSnd = -1; } /* RegisterSound */ /* * register read socket for event handling */ void Socket_RegisterRead (XBSocket *pSocket) { assert (NULL != pSocket); if (pSocket->fd > socketMax) { socketMax = pSocket->fd; } FD_SET (pSocket->fd, &socketReadSet); #ifdef DEBUG_SOCKET DebugFdSet ("read fd_set", &socketReadSet); #endif } /* RegisterSocket */ /* * register read socket for event handling */ void Socket_RegisterWrite (XBSocket *pSocket) { assert (NULL != pSocket); if (pSocket->fd > socketMax) { socketMax = pSocket->fd; } FD_SET (pSocket->fd, &socketWriteSet); #ifdef DEBUG_SOCKET DebugFdSet ("write fd_set", &socketWriteSet); #endif } /* RegisterSocket */ /* * unregister read socket for event handling */ void Socket_UnregisterRead (XBSocket *pSocket) { assert (NULL != pSocket); FD_CLR (pSocket->fd, &socketReadSet); #ifdef DEBUG_SOCKET DebugFdSet ("read fd_set", &socketReadSet); #endif } /* RegisterSocket */ /* * register read socket for event handling */ void Socket_UnregisterWrite (XBSocket *pSocket) { assert (NULL != pSocket); FD_CLR (pSocket->fd, &socketWriteSet); #ifdef DEBUG_SOCKET DebugFdSet ("write fd_set", &socketWriteSet); #endif } /* RegisterSocket */ /* * handle select */ XBBool SelectSockets (XBKeyboardMode kbMode, struct timeval *timeout) { XBBool xEvents = XBFalse; int fd; fd_set rdfs; fd_set wrfs; /* now poll X11 and other sockets */ memcpy (&rdfs, &socketReadSet, sizeof (fd_set)); memcpy (&wrfs, &socketWriteSet, sizeof (fd_set)); select (socketMax + 1, &rdfs, &wrfs, NULL, timeout); /* check each socket */ for (fd = 0; fd <= socketMax; fd ++) { /* socket is readable */ if (FD_ISSET (fd, &rdfs)) { if (fd == socketX11) { xEvents = XBTrue; } else if (fd == socketSnd) { HandleSound (fd); } else if (FD_ISSET (fd, &fdJoystickSet)) { switch (kbMode) { case KB_XBLAST: HandleXBlastJoystick (fd); break; case KB_MENU: HandleMenuJoystick (fd); break; default: break; } } else { CommReadable (fd); } } if (FD_ISSET (fd, &wrfs)) { CommWriteable (fd); } } return xEvents; } /* * end of file x11_socket.c */