/* * file w32_socket.c - true bsd sockets for xblast * * $Id: w32_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 */ #define __USE_W32_SOCKETS #include #include #include "socket.h" #include "w32_socket.h" #include "w32_event.h" #include "str_util.h" #include "com.h" #include "gui.h" /* * local constants */ #define LISTENQ 5 /* needed winsock version */ #define WINSOCK_VERSION (MAKEWORD (2, 0)) /* maxmimum number of sockets in map */ #define MAX_SOCKET 64 /* * type defintions */ typedef struct _xb_socket_address { int len; struct sockaddr *addr; } XBSocketAddress; struct _xb_socket { SOCKET fd; XBSocketAddress sock; XBSocketAddress peer; XBBool read; XBBool write; XBBool shutdown; }; typedef struct { size_t num; SOCKET fd[MAX_SOCKET]; const XBSocket *socket[MAX_SOCKET]; } XBSocketMap; /* struct in_addr6 { u_char s6_addr[16]; }; struct sockaddr_in6 { short sin6_family; u_short sin6_port; u_long sin6_flowinfo; struct in_addr6 sin6_addr; }; */ typedef union sockaddr_genk{ /* KOEN */ struct sockaddr address; struct sockaddr_in addressIn; struct sockaddr_in6 addressIn6; } sockaddr_genk; typedef struct _interface_info { u_long iiFlags; /* Interface flags */ sockaddr_genk iiAddress; /* Interface address */ sockaddr_genk iiBroadcastAddress; /* Broadcast address */ sockaddr_genk iiNetmask; /* Network mask */ } XB_INTERFACE_INFO; /* * local variables */ static XBSocketMap socketMap; static XBSocketInterface *inter = NULL; static size_t numInter = 0; /* * local prototypes */ static void DeleteInterfaces (void); static void AsyncSelect (const XBSocket *pSocket); /* * add socket to set */ static void SocketMapAdd (XBSocketMap *map, const XBSocket *pSocket) { assert (NULL != socket); assert (NULL != map); assert (map->num < MAX_SOCKET); Dbg_Out ("add socket %u at %u\n", pSocket->fd, map->num); map->fd[map->num] = pSocket->fd; map->socket[map->num] = pSocket; map->num ++; } /* SocketMapAdd */ /* * subtract socket from set */ static void SocketMapSubtract (XBSocketMap *map, const XBSocket *pSocket) { size_t i; assert (NULL != socket); assert (NULL != map); assert (map->num > 0); for (i = 0; i < map->num; i ++) { if (map->socket[i] == pSocket) { Dbg_Out ("sub socket %u from %u/%u\n", pSocket->fd, i, map->num); map->num --; if (i < map->num) { map->fd[i] = map->fd[map->num]; map->socket[i] = map->socket[map->num]; } } } } /* SocketMapSubtract */ /* * find socket to fd */ static const XBSocket * SocketMapFind (XBSocketMap *map, SOCKET fd) { size_t i; assert (NULL != socket); assert (NULL != map); for (i = 0; i < map->num; i ++) { if (map->fd[i] == fd) { return map->socket[i]; } } return NULL; } /* SocketMapFind */ /* * */ XBBool Socket_Init (void) { WSADATA wsaData; memset (&socketMap, 0, sizeof (socketMap)); if (0 != WSAStartup (WINSOCK_VERSION, &wsaData)) { GUI_ErrorMessage ("network init failed\nwinsock startup failed\n"); return XBFalse; } if (wsaData.wVersion != WINSOCK_VERSION) { GUI_ErrorMessage ("network init failed\nwinsock startup failed\n"); WSACleanup (); return XBFalse; } return XBTrue; } /* Net_Init */ /* * */ void Socket_Finish (void) { DeleteInterfaces (); WSACleanup (); } /* Net_Init */ /* * 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; pSocket->read = XBFalse; pSocket->write = XBFalse; pSocket->shutdown = XBFalse; /* 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; /* 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 unsigned long GetAddressInet (const char *hostName) { long 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 (*(long *) 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; unsigned long 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) ) ) { pSocket->shutdown = XBTrue; return XBFalse; } SocketMapAdd (&socketMap, pSocket); Dbg_Out ("open socket %d (type=%d)\n", pSocket->fd, type); return XBTrue; } /* Socket */ /* * close socket */ void Socket_Close (XBSocket *pSocket) { assert (NULL != pSocket); Dbg_Out ("close socket %d\n", pSocket->fd); if (pSocket->fd >= 0) { if (! pSocket->shutdown) { SocketMapSubtract (&socketMap, pSocket); pSocket->shutdown = XBTrue; } closesocket (pSocket->fd); } } /* Socket_Close */ /* * close socket */ void Socket_ShutdownWrite (XBSocket *pSocket) { assert (NULL != pSocket); Dbg_Out ("shutdown write socket %d\n", pSocket->fd); if (pSocket->fd >= 0) { if (! pSocket->shutdown) { SocketMapSubtract (&socketMap, pSocket); pSocket->shutdown = XBTrue; } shutdown (pSocket->fd, 1); } } /* Socket_Close */ /* * connect to server (generic) */ XBBool Socket_Connect (XBSocket *pSocket) { assert (pSocket != NULL); /* connect to serverAdr */ if (-1 == connect (pSocket->fd, pSocket->peer.addr, pSocket->peer.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; } 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; } #ifdef DEBUG Dbg_Out ("bind %d to %s:%u\n", pSocket->fd, Socket_HostName (pSocket, XBFalse), Socket_HostPort (pSocket, XBFalse)); #endif /* that's all */ return XBTrue; } /* Bind */ /* * */ XBBool Socket_Accept (XBSocket *pSocket, const XBSocket *pListen) { assert (pSocket != NULL); assert (pListen != NULL); /* set timeout */ if (-1 == (pSocket->fd = accept (pListen->fd, pSocket->peer.addr, (void *) &pSocket->peer.len) ) ) { return XBFalse; } /* now retrieve local adresse */ if (-1 == getsockname (pSocket->fd, pSocket->sock.addr, (void *) &pSocket->sock.len) ) { return XBFalse; } SocketMapAdd (&socketMap, pSocket); Dbg_Out ("accept socket %d\n", pSocket->fd); /* 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, 0); if (result < 0) { int err = WSAGetLastError (); Dbg_Out ("send error %d\n", err); if (err == WSAEWOULDBLOCK) { Dbg_Out ("socket send %d would block\n", pSocket->fd); return XB_SOCKET_WOULD_BLOCK; } else { return 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, 0, pSocket->peer.addr, pSocket->peer.len); if (result < 0) { int err = WSAGetLastError (); Dbg_Out ("sendto error %d\n", err); if (err == WSAEWOULDBLOCK) { Dbg_Out ("socket sendto %d would block\n", pSocket->fd); return XB_SOCKET_WOULD_BLOCK; } else { return 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) { int err = WSAGetLastError (); Dbg_Out ("receive error %d\n", err); if (err == WSAEWOULDBLOCK) { Dbg_Out ("socket receive %d would block\n", pSocket->fd); if (pSocket->read) { AsyncSelect (pSocket); } return XB_SOCKET_WOULD_BLOCK; } else { return 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) { long numRead; assert (NULL != pSocket); assert (NULL != buf); assert (NULL != host); numRead = recvfrom (pSocket->fd, buf, len, 0, pSocket->peer.addr, &pSocket->peer.len); if (numRead >= 0) { *host = Socket_HostName (pSocket, XBTrue); *port = Socket_HostPort (pSocket, XBTrue); } else { int err = WSAGetLastError (); Dbg_Out ("receive error %d\n", err); *host = NULL; *port = 0; if (err == WSAEWOULDBLOCK) { Dbg_Out ("socket receivefrom %d would block\n", pSocket->fd); return XB_SOCKET_WOULD_BLOCK; } else { return 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 */ /* * trigger async select */ static void AsyncSelect (const XBSocket *pSocket) { long event = 0; assert (NULL != pSocket); #ifdef DEBUG_SOCKET Dbg_Out ("async select %d %c%c\n", pSocket->fd, pSocket->read ? 'R' : '-', pSocket->write ? 'W' : '-'); #endif if (pSocket->read) { event |= (FD_READ|FD_ACCEPT); } if (pSocket->write) { event |= FD_WRITE; } WSAAsyncSelect (pSocket->fd, window, MSG_XBLAST_SELECT, event); } /* AsyncSelect */ /* * register read socket for event handling */ void Socket_RegisterRead (XBSocket *pSocket) { assert (NULL != pSocket); if (! pSocket->read) { pSocket->read = XBTrue; AsyncSelect (pSocket); } } /* RegisterSocket */ /* * register read socket for event handling */ void Socket_RegisterWrite (XBSocket *pSocket) { assert (NULL != pSocket); if (! pSocket->write) { pSocket->write = XBTrue; AsyncSelect (pSocket); } } /* RegisterSocket */ /* * register read socket for event handling */ void Socket_UnregisterRead (XBSocket *pSocket) { assert (NULL != pSocket); if (pSocket->read) { pSocket->read = XBFalse; AsyncSelect (pSocket); } } /* RegisterSocket */ /* * register read socket for event handling */ void Socket_UnregisterWrite (XBSocket *pSocket) { assert (NULL != pSocket); if (pSocket->write) { pSocket->write = XBFalse; AsyncSelect (pSocket); } } /* RegisterSocket */ /* * handle selections events */ void HandleSelect (UINT wParam, LONG lParam) { SOCKET fd = wParam; UINT event = WSAGETSELECTEVENT (lParam); const XBSocket *pSocket; switch (event) { case FD_READ: case FD_ACCEPT: #ifdef DEBUG_SOCKET Dbg_Out ("socket readable %u\n", fd); #endif CommReadable (fd); break; case FD_WRITE: #ifdef DEBUG_SOCKET Dbg_Out ("socket writeable %u\n", fd); #endif CommWriteable (fd); if (NULL != (pSocket = SocketMapFind (&socketMap, fd) ) && pSocket->write) { AsyncSelect (pSocket); } break; default: Dbg_Out ("select event %04x\n", event); break; } } /* HandleSelect */ /* * set broadcast for socket */ XBBool Socket_SetBroadcast (XBSocket *pSocket, XBBool enable) { BOOL flag = enable ? TRUE : FALSE; assert (NULL != pSocket); return (0 == setsockopt (pSocket->fd, SOL_SOCKET, SO_BROADCAST, (void *) &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 */ /* * list available network interface */ const XBSocketInterface * Socket_GetInterfaces (size_t *num) { SOCKET fd = SOCKET_ERROR; DWORD i, len; XB_INTERFACE_INFO *info = NULL; DWORD iLen = 10; /* clean up */ DeleteInterfaces (); /* open udp/ip socket */ if (SOCKET_ERROR == (fd = socket (AF_INET, SOCK_DGRAM, 0) ) ) { Dbg_Out ("socket open failed\n"); goto Error; } /* */ info = calloc (iLen, sizeof (*info)); assert (NULL != info); while (SOCKET_ERROR == WSAIoctl (fd, SIO_GET_INTERFACE_LIST, NULL, 0, info, iLen*sizeof (*info), &len, NULL, NULL) ) { if (WSAEFAULT != WSAGetLastError ()) { Dbg_Out ("socket ioctl failed\n"); goto Error; } free (info); iLen += 10; info = calloc (iLen, sizeof (*info)); assert (NULL != info); } len /= sizeof (*info); /* alloc output buffer */ inter = calloc (len, sizeof (XBSocketInterface)); assert (NULL != inter); /* create interface list */ numInter = 0; for (i = 0; i < len; i ++) { if (info[i].iiAddress.address.sa_family == AF_INET && info[i].iiAddress.addressIn.sin_addr.s_addr != INADDR_ANY && (info[i].iiFlags & IFF_UP) ) { inter[numInter].name = DupString ("net"); inter[numInter].addrDevice = DupString (inet_ntoa (info[i].iiAddress.addressIn.sin_addr)); if ( (info[i].iiFlags & IFF_BROADCAST) && ! (info[i].iiFlags & IFF_POINTTOPOINT) ) { /* we need to calculate the broadcast addresse by hand, as winsock2 returns the global broadcast address */ struct in_addr bcAddr = info[i].iiAddress.addressIn.sin_addr; bcAddr.s_addr |= ~info[i].iiNetmask.addressIn.sin_addr.s_addr; inter[numInter].addrBroadcast = DupString (inet_ntoa (bcAddr)); } Dbg_Out ("%s\t%s\t%s\n", inter[numInter].name, inter[numInter].addrDevice, inter[numInter].addrBroadcast); numInter ++; } } /* clean up */ free (info); closesocket (fd); /* that's all */ *num = numInter; return inter; Error: if (NULL != info) { free (info); } if (SOCKET_ERROR != fd) { closesocket (fd); } return NULL; } /* Socket_GetInterfaces */ /* * end of file w32_socket.c */