| /* |
| * Copyright 2012 The Emscripten Authors. All rights reserved. |
| * Emscripten is available under two separate licenses, the MIT license and the |
| * University of Illinois/NCSA Open Source License. Both these licenses can be |
| * found in the LICENSE file. |
| */ |
| |
| /** |
| @file unix.c |
| @brief ENet Unix system specific functions |
| */ |
| #ifndef WIN32 |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/ioctl.h> |
| #include <sys/time.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <time.h> |
| |
| #define ENET_BUILDING_LIB 1 |
| #include "enet/enet.h" |
| |
| #ifdef HAS_FCNTL |
| #include <fcntl.h> |
| #endif |
| |
| #ifdef __APPLE__ |
| #undef HAS_POLL |
| #endif |
| |
| #ifdef HAS_POLL |
| #include <sys/poll.h> |
| #endif |
| |
| #ifndef HAS_SOCKLEN_T |
| typedef int socklen_t; |
| #endif |
| |
| #ifndef MSG_NOSIGNAL |
| #define MSG_NOSIGNAL 0 |
| #endif |
| |
| static enet_uint32 timeBase = 0; |
| |
| int |
| enet_initialize (void) |
| { |
| return 0; |
| } |
| |
| void |
| enet_deinitialize (void) |
| { |
| } |
| |
| enet_uint32 |
| enet_time_get (void) |
| { |
| struct timeval timeVal; |
| |
| gettimeofday (& timeVal, NULL); |
| |
| return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase; |
| } |
| |
| void |
| enet_time_set (enet_uint32 newTimeBase) |
| { |
| struct timeval timeVal; |
| |
| gettimeofday (& timeVal, NULL); |
| |
| timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase; |
| } |
| |
| int |
| enet_address_set_host (ENetAddress * address, const char * name) |
| { |
| struct hostent * hostEntry = NULL; |
| #ifdef HAS_GETHOSTBYNAME_R |
| struct hostent hostData; |
| char buffer [2048]; |
| int errnum; |
| |
| #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__EMSCRIPTEN__) |
| gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); |
| #else |
| hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum); |
| #endif |
| #else |
| hostEntry = gethostbyname (name); |
| #endif |
| |
| if (hostEntry == NULL || |
| hostEntry -> h_addrtype != AF_INET) |
| { |
| #ifdef HAS_INET_PTON |
| if (! inet_pton (AF_INET, name, & address -> host)) |
| #else |
| if (! inet_aton (name, (struct in_addr *) & address -> host)) |
| #endif |
| return -1; |
| return 0; |
| } |
| |
| address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; |
| |
| return 0; |
| } |
| |
| int |
| enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) |
| { |
| #ifdef HAS_INET_NTOP |
| if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL) |
| #else |
| char * addr = inet_ntoa (* (struct in_addr *) & address -> host); |
| if (addr != NULL) |
| strncpy (name, addr, nameLength); |
| else |
| #endif |
| return -1; |
| return 0; |
| } |
| |
| int |
| enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) |
| { |
| struct in_addr in; |
| struct hostent * hostEntry = NULL; |
| #ifdef HAS_GETHOSTBYADDR_R |
| struct hostent hostData; |
| char buffer [2048]; |
| int errnum; |
| |
| in.s_addr = address -> host; |
| |
| #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__EMSCRIPTEN__) |
| gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); |
| #else |
| hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum); |
| #endif |
| #else |
| in.s_addr = address -> host; |
| |
| hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); |
| #endif |
| |
| if (hostEntry == NULL) |
| return enet_address_get_host_ip (address, name, nameLength); |
| |
| strncpy (name, hostEntry -> h_name, nameLength); |
| |
| return 0; |
| } |
| |
| int |
| enet_socket_bind (ENetSocket socket, const ENetAddress * address) |
| { |
| struct sockaddr_in sin; |
| |
| memset (& sin, 0, sizeof (struct sockaddr_in)); |
| |
| sin.sin_family = AF_INET; |
| |
| if (address != NULL) |
| { |
| sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); |
| sin.sin_addr.s_addr = address -> host; |
| } |
| else |
| { |
| sin.sin_port = 0; |
| sin.sin_addr.s_addr = INADDR_ANY; |
| } |
| |
| return bind (socket, |
| (struct sockaddr *) & sin, |
| sizeof (struct sockaddr_in)); |
| } |
| |
| int |
| enet_socket_listen (ENetSocket socket, int backlog) |
| { |
| return listen (socket, backlog < 0 ? SOMAXCONN : backlog); |
| } |
| |
| ENetSocket |
| enet_socket_create (ENetSocketType type) |
| { |
| return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); |
| } |
| |
| int |
| enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) |
| { |
| int result = -1; |
| switch (option) |
| { |
| case ENET_SOCKOPT_NONBLOCK: |
| #ifdef HAS_FCNTL |
| result = fcntl (socket, F_SETFL, O_NONBLOCK | fcntl (socket, F_GETFL)); |
| #else |
| result = ioctl (socket, FIONBIO, & value); |
| #endif |
| break; |
| |
| case ENET_SOCKOPT_BROADCAST: |
| result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); |
| break; |
| |
| case ENET_SOCKOPT_REUSEADDR: |
| result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); |
| break; |
| |
| case ENET_SOCKOPT_RCVBUF: |
| result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); |
| break; |
| |
| case ENET_SOCKOPT_SNDBUF: |
| result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); |
| break; |
| |
| case ENET_SOCKOPT_RCVTIMEO: |
| result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int)); |
| break; |
| |
| case ENET_SOCKOPT_SNDTIMEO: |
| result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int)); |
| break; |
| |
| default: |
| break; |
| } |
| return result == -1 ? -1 : 0; |
| } |
| |
| int |
| enet_socket_connect (ENetSocket socket, const ENetAddress * address) |
| { |
| struct sockaddr_in sin; |
| |
| memset (& sin, 0, sizeof (struct sockaddr_in)); |
| |
| sin.sin_family = AF_INET; |
| sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); |
| sin.sin_addr.s_addr = address -> host; |
| |
| return connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); |
| } |
| |
| ENetSocket |
| enet_socket_accept (ENetSocket socket, ENetAddress * address) |
| { |
| int result; |
| struct sockaddr_in sin; |
| socklen_t sinLength = sizeof (struct sockaddr_in); |
| |
| result = accept (socket, |
| address != NULL ? (struct sockaddr *) & sin : NULL, |
| address != NULL ? & sinLength : NULL); |
| |
| if (result == -1) |
| return ENET_SOCKET_NULL; |
| |
| if (address != NULL) |
| { |
| address -> host = (enet_uint32) sin.sin_addr.s_addr; |
| address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); |
| } |
| |
| return result; |
| } |
| |
| void |
| enet_socket_destroy (ENetSocket socket) |
| { |
| close (socket); |
| } |
| |
| int |
| enet_socket_send (ENetSocket socket, |
| const ENetAddress * address, |
| const ENetBuffer * buffers, |
| size_t bufferCount) |
| { |
| struct msghdr msgHdr; |
| struct sockaddr_in sin; |
| int sentLength; |
| |
| memset (& msgHdr, 0, sizeof (struct msghdr)); |
| |
| if (address != NULL) |
| { |
| memset (& sin, 0, sizeof (struct sockaddr_in)); |
| |
| sin.sin_family = AF_INET; |
| sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); |
| sin.sin_addr.s_addr = address -> host; |
| |
| msgHdr.msg_name = & sin; |
| msgHdr.msg_namelen = sizeof (struct sockaddr_in); |
| } |
| |
| msgHdr.msg_iov = (struct iovec *) buffers; |
| msgHdr.msg_iovlen = bufferCount; |
| |
| sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL); |
| |
| if (sentLength == -1) |
| { |
| if (errno == EWOULDBLOCK) |
| return 0; |
| |
| return -1; |
| } |
| |
| return sentLength; |
| } |
| |
| int |
| enet_socket_receive (ENetSocket socket, |
| ENetAddress * address, |
| ENetBuffer * buffers, |
| size_t bufferCount) |
| { |
| struct msghdr msgHdr; |
| struct sockaddr_in sin; |
| int recvLength; |
| |
| memset (& msgHdr, 0, sizeof (struct msghdr)); |
| |
| if (address != NULL) |
| { |
| msgHdr.msg_name = & sin; |
| msgHdr.msg_namelen = sizeof (struct sockaddr_in); |
| } |
| |
| msgHdr.msg_iov = (struct iovec *) buffers; |
| msgHdr.msg_iovlen = bufferCount; |
| |
| recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL); |
| |
| if (recvLength == -1) |
| { |
| if (errno == EWOULDBLOCK) |
| return 0; |
| |
| return -1; |
| } |
| |
| #ifdef HAS_MSGHDR_FLAGS |
| if (msgHdr.msg_flags & MSG_TRUNC) |
| return -1; |
| #endif |
| |
| if (address != NULL) |
| { |
| address -> host = (enet_uint32) sin.sin_addr.s_addr; |
| address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); |
| } |
| |
| return recvLength; |
| } |
| |
| int |
| enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) |
| { |
| struct timeval timeVal; |
| |
| timeVal.tv_sec = timeout / 1000; |
| timeVal.tv_usec = (timeout % 1000) * 1000; |
| |
| return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); |
| } |
| |
| int |
| enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) |
| { |
| #ifdef HAS_POLL |
| struct pollfd pollSocket; |
| int pollCount; |
| |
| pollSocket.fd = socket; |
| pollSocket.events = 0; |
| |
| if (* condition & ENET_SOCKET_WAIT_SEND) |
| pollSocket.events |= POLLOUT; |
| |
| if (* condition & ENET_SOCKET_WAIT_RECEIVE) |
| pollSocket.events |= POLLIN; |
| |
| pollCount = poll (& pollSocket, 1, timeout); |
| |
| if (pollCount < 0) |
| return -1; |
| |
| * condition = ENET_SOCKET_WAIT_NONE; |
| |
| if (pollCount == 0) |
| return 0; |
| |
| if (pollSocket.revents & POLLOUT) |
| * condition |= ENET_SOCKET_WAIT_SEND; |
| |
| if (pollSocket.revents & POLLIN) |
| * condition |= ENET_SOCKET_WAIT_RECEIVE; |
| |
| return 0; |
| #else |
| fd_set readSet, writeSet; |
| struct timeval timeVal; |
| int selectCount; |
| |
| timeVal.tv_sec = timeout / 1000; |
| timeVal.tv_usec = (timeout % 1000) * 1000; |
| |
| FD_ZERO (& readSet); |
| FD_ZERO (& writeSet); |
| |
| if (* condition & ENET_SOCKET_WAIT_SEND) |
| FD_SET (socket, & writeSet); |
| |
| if (* condition & ENET_SOCKET_WAIT_RECEIVE) |
| FD_SET (socket, & readSet); |
| |
| selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); |
| |
| if (selectCount < 0) |
| return -1; |
| |
| * condition = ENET_SOCKET_WAIT_NONE; |
| |
| if (selectCount == 0) |
| return 0; |
| |
| if (FD_ISSET (socket, & writeSet)) |
| * condition |= ENET_SOCKET_WAIT_SEND; |
| |
| if (FD_ISSET (socket, & readSet)) |
| * condition |= ENET_SOCKET_WAIT_RECEIVE; |
| |
| return 0; |
| #endif |
| } |
| |
| #endif |
| |