From b3d8586dc867da9a21877395d506eb1ab312527f Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 12:15:31 +0900 Subject: [PATCH 01/12] Add extern "C" to header section for mixed C/C++ project --- sts_net.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sts_net.h b/sts_net.h index 4229245..d2636bb 100644 --- a/sts_net.h +++ b/sts_net.h @@ -49,6 +49,9 @@ #endif // STS_NET_PACKET_SIZE #endif // STS_NET_NO_PACKETS +#ifdef __cplusplus +extern "C" { +#endif //////////////////////////////////////////////////////////////////////////////// // @@ -179,6 +182,11 @@ int sts_net_receive_packet(sts_net_socket_t* socket); // drops the packet after you used it void sts_net_drop_packet(sts_net_socket_t* socket); #endif // STS_NET_NO_PACKETS + +#ifdef __cplusplus +} +#endif + #endif // __INCLUDED__STS_NET_H__ From ae3ed60f9ef740093b608dcc23536d0dee211df4 Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 12:17:26 +0900 Subject: [PATCH 02/12] Add optional define STS_NET_NO_ERRORSTRINGS to remove error strings from build --- sts_net.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sts_net.h b/sts_net.h index d2636bb..3f5de3d 100644 --- a/sts_net.h +++ b/sts_net.h @@ -82,8 +82,11 @@ typedef struct { // // General API // + +#ifndef STS_NET_NO_ERRORSTRINGS // Get the last error from sts_net (can be called even before sts_net_init) const char* sts_net_get_last_error(); +#endif // Initialized the sts_net library. You have to call this before any other function (except sts_net_get_last_error) int sts_net_init(); @@ -233,6 +236,7 @@ typedef int socklen_t; #endif // sts__memset +#ifndef STS_NET_NO_ERRORSTRINGS static const char* sts_net__error_message = ""; @@ -242,6 +246,14 @@ static int sts_net__set_error(const char* message) { } +const char *sts_net_get_last_error() { + return sts_net__error_message; +} +#else +#define sts_net__set_error(m) -1 +#endif + + void sts_net_reset_socket(sts_net_socket_t* socket) { socket->fd = INVALID_SOCKET; socket->ready = 0; @@ -258,11 +270,6 @@ int sts_net_is_socket_valid(sts_net_socket_t* socket) { } -const char *sts_net_get_last_error() { - return sts_net__error_message; -} - - int sts_net_init() { #ifdef _WIN32 WSADATA wsa_data; From 83f27e9400496d34b7cd1ac6e918d4183d08214e Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 12:20:25 +0900 Subject: [PATCH 03/12] Define _WINSOCK_DEPRECATED_NO_WARNINGS to remove build warnings --- sts_net.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sts_net.h b/sts_net.h index 3f5de3d..20d7183 100644 --- a/sts_net.h +++ b/sts_net.h @@ -210,6 +210,7 @@ void sts_net_drop_packet(sts_net_socket_t* socket); #include // NULL and possibly memcpy, memset #ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS #include #include typedef int socklen_t; From 3821f1d7d702296387e5adec0c73a7d24077fb53 Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 12:44:30 +0900 Subject: [PATCH 04/12] Added sts_net_gethostname function to query the host name (or ip address) of a connected client (or the listen interface of a server) --- sts_net.h | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/sts_net.h b/sts_net.h index 20d7183..367923b 100644 --- a/sts_net.h +++ b/sts_net.h @@ -143,6 +143,12 @@ int sts_net_remove_socket_from_set(sts_net_socket_t* socket, sts_net_set_t* set) // >0 amount of sockets with activity int sts_net_check_socket_set(sts_net_set_t* set, const float timeout); +// Read the connected hostname of a socket into out buffer +// Supply 1 to want_only_ip if instead of the resolved host name only the ip address is needed +// out_port can be supplied as NULL if you're not interested in the connected port number +// Returns the length of what was written into out_host or -1 on error (sets out_host empty string) +int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port); + //////////////////////////////////////////////////////////////////////////////// // @@ -478,6 +484,42 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { } +int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port) { + struct sockaddr_in sin; + struct in_addr in; + struct hostent* hostEntry; + char* host; + socklen_t sinLength; + int addrLen; + + if (out_size) out_host[0] = '\0'; + + if (socket->fd == INVALID_SOCKET) { + return sts_net__set_error("Cannot get host name of closed socket"); + } + + sinLength = sizeof(struct sockaddr_in); + if (getsockname(socket->fd, (struct sockaddr *)&sin, &sinLength) == -1) { + return sts_net__set_error("Error while getting host name of socket"); + } + + if (out_port) *out_port = sin.sin_port; + + in.s_addr = sin.sin_addr.s_addr; + hostEntry = (want_only_ip ? NULL : gethostbyaddr((char*)&in, sizeof(in), AF_INET)); + host = (hostEntry ? hostEntry->h_name : inet_ntoa(in)); + if (host == NULL) { + return sts_net__set_error("Error while getting host name of socket"); + } + addrLen = strlen(host); + if (addrLen >= out_size) { + return sts_net__set_error("Provided buffer is too small for host name"); + } + memcpy(out_host, host, addrLen + 1); + return addrLen; +} + + #ifndef STS_NET_NO_PACKETS int sts_net_refill_packet_data(sts_net_socket_t* socket) { if (socket->ready) return 0; @@ -562,7 +604,8 @@ int main(int argc, char *argv[]) { if (clients[i].fd == INVALID_SOCKET) { if (sts_net_accept_socket(&server, &clients[i]) < 0) panic(sts_net_get_last_error()); if (sts_net_add_socket_to_set(&clients[i], &set) < 0) panic(sts_net_get_last_error()); - puts("Client connected!"); + sts_net_gethostname(&clients[i], buffer, sizeof(buffer), 1, NULL); + printf("Client connected '%s'!\n", buffer); break; } } From e9ce5df69fee577499e685d910d61d47a99157e1 Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 12:51:35 +0900 Subject: [PATCH 05/12] Fix build error when compiling against C89 standard --- sts_net.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sts_net.h b/sts_net.h index 367923b..2bbf034 100644 --- a/sts_net.h +++ b/sts_net.h @@ -522,8 +522,9 @@ int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, #ifndef STS_NET_NO_PACKETS int sts_net_refill_packet_data(sts_net_socket_t* socket) { + int received; if (socket->ready) return 0; - int received = sts_net_recv(socket, &socket->data[socket->received], STS_NET_PACKET_SIZE - socket->received); + received = sts_net_recv(socket, &socket->data[socket->received], STS_NET_PACKET_SIZE - socket->received); if (received < 0) return -1; socket->received += received; return 1; From 96ff41664c424f0075f8a2f5ed25d997b94be625 Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 14:10:32 +0900 Subject: [PATCH 06/12] Fix memcpy to use sts__ macro in sts_net_gethostname --- sts_net.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sts_net.h b/sts_net.h index 2bbf034..1388718 100644 --- a/sts_net.h +++ b/sts_net.h @@ -515,7 +515,7 @@ int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, if (addrLen >= out_size) { return sts_net__set_error("Provided buffer is too small for host name"); } - memcpy(out_host, host, addrLen + 1); + sts__memcpy(out_host, host, addrLen + 1); return addrLen; } From a9392b7433faeb5e98076d58d981ffaf136c7957 Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 14:13:06 +0900 Subject: [PATCH 07/12] Added sts_net_enumerate_interfaces to get a list of interface names and ip-addresses of the host machine --- sts_net.h | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/sts_net.h b/sts_net.h index 1388718..379d4d3 100644 --- a/sts_net.h +++ b/sts_net.h @@ -149,6 +149,18 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout); // Returns the length of what was written into out_host or -1 on error (sets out_host empty string) int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port); +#ifndef STS_NET_NO_ENUMERATEINTERFACES +typedef struct { + char interface_name[48], address[47], IsIPV6; +} sts_net_interfaceinfo_t; + +// Get a list of interface names and ip-addresses of the host machine +// The supplied table needs to be allocated memory of size tablesize * sizeof(sts_net_interfaceinfo_t) +// Boolean parametrs want_ipv4 and want_ipv6 can be set to 0 or 1 +// The function returns the max number of table entries (can be more than tablesize) +// You can supply NULL as table and 0 as tablesize to query for the count of entries. +int sts_net_enumerate_interfaces(sts_net_interfaceinfo_t* table, int tablesize, int want_ipv4, int want_ipv6); +#endif // STS_NET_NO_ENUMERATEINTERFACES //////////////////////////////////////////////////////////////////////////////// // @@ -221,6 +233,10 @@ void sts_net_drop_packet(sts_net_socket_t* socket); #include typedef int socklen_t; #pragma comment(lib, "Ws2_32.lib") +#ifndef STS_NET_NO_ENUMERATEINTERFACES +#include +#pragma comment(lib, "iphlpapi.lib") +#endif #else #include #include @@ -232,6 +248,9 @@ typedef int socklen_t; #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket(fd) close(fd) +#ifndef STS_NET_NO_ENUMERATEINTERFACES +#include +#endif #endif @@ -519,6 +538,62 @@ int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, return addrLen; } +#ifndef STS_NET_NO_ENUMERATEINTERFACES +int sts_net_enumerate_interfaces(sts_net_interfaceinfo_t* table, int tablesize, int want_ipv4, int want_ipv6) { + void* sinaddr; + struct sockaddr* addr; + int family, ifnamelen, totalcount = 0; + +#if _WIN32 + DWORD size; + PIP_ADAPTER_ADDRESSES adapter_addresses, aa; + PIP_ADAPTER_UNICAST_ADDRESS ua; + + if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &size) != ERROR_BUFFER_OVERFLOW || !size) return 0; + adapter_addresses = (PIP_ADAPTER_ADDRESSES)HeapAlloc(GetProcessHeap(), 0, size); + if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adapter_addresses, &size) != ERROR_SUCCESS) { free(adapter_addresses); return 0; } + + for (aa = adapter_addresses; aa; aa = aa->Next) { + if (aa->OperStatus != IfOperStatusUp) continue; + for (ua = aa->FirstUnicastAddress; ua; ua = ua->Next) { + addr = ua->Address.lpSockaddr; +#else + struct ifaddrs *ifAddrStruct, *ifa; + getifaddrs(&ifAddrStruct); + for (ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr) { + addr = ifa->ifa_addr; +#endif + family = addr->sa_family; + if (family == AF_INET) { if (!want_ipv4) continue; } + else if (family == AF_INET6) { if (!want_ipv6) continue; } + else continue; + totalcount++; + if (!tablesize) continue; + if (family == AF_INET) { sinaddr = &((struct sockaddr_in*)addr)->sin_addr; table->IsIPV6 = 0; } + else { sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr; table->IsIPV6 = 1; } +#if _WIN32 + ifnamelen = WideCharToMultiByte(CP_UTF8, 0, aa->FriendlyName, -1, table->interface_name, sizeof(table->interface_name) - 1, 0, 0); + if (!ifnamelen) ifnamelen = sizeof(table->interface_name) - 1; +#else + ifnamelen = strlen(ifa->ifa_name); + if (ifnamelen >= sizeof(table->interface_name)) ifnamelen = sizeof(table->interface_name) - 1; + sts__memcpy(table->interface_name, ifa->ifa_name, ifnamelen); +#endif + table->interface_name[ifnamelen] = '\0'; + inet_ntop(family, sinaddr, table->address, sizeof(table->address)); + tablesize--; + table++; + } + } +#if _WIN32 + HeapFree(GetProcessHeap(), 0, adapter_addresses); +#else + if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); +#endif + return totalcount; +} +#endif // STS_NET_NO_ENUMERATEINTERFACES #ifndef STS_NET_NO_PACKETS int sts_net_refill_packet_data(sts_net_socket_t* socket) { From 47a53d4b23152915c691ec11ee9a8ae4efc6a4df Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 14:30:35 +0900 Subject: [PATCH 08/12] Improved socket opening and socket sets - Split sts_net_open_socket into sts_net_listen (server) and sts_net_connect (client) - Changed socket set to contain sockets not user managed pointer to sockets - Added sts_net_get_available_socket_from_set which returns a socket for accepting - Added sts_net_drop_socket which can be used to drop a new connection if no socket is available - Removed sts_net_add_socket_to_set and sts_net_remove_socket_from_set as it is now automatic! - Updated example --- sts_net.h | 206 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 116 insertions(+), 90 deletions(-) diff --git a/sts_net.h b/sts_net.h index 379d4d3..aeb9eca 100644 --- a/sts_net.h +++ b/sts_net.h @@ -70,7 +70,7 @@ typedef struct { typedef struct { - sts_net_socket_t* sockets[STS_NET_SET_SOCKETS]; + sts_net_socket_t sockets[STS_NET_SET_SOCKETS]; } sts_net_set_t; @@ -106,9 +106,12 @@ void sts_net_reset_socket(sts_net_socket_t* socket); // Check if the socket structure contains a "valid" socket. int sts_net_is_socket_valid(sts_net_socket_t* socket); -// Open a (TCP) socket. If you provide "host" sts_net will try to connect to a remove host. -// Pass NULL for host and you'll have a server socket. -int sts_net_open_socket(sts_net_socket_t* socket, const char* host, const char* service); +// Open a (TCP) client socket +int sts_net_connect(sts_net_socket_t* socket, const char* host, int port); + +// Open a (TCP) server socket +// Supply NULL as bind_host to listen on any address +int sts_net_listen(sts_net_socket_t* socket, int port, const char* bind_address); // Closes the socket. void sts_net_close_socket(sts_net_socket_t* socket); @@ -116,6 +119,9 @@ void sts_net_close_socket(sts_net_socket_t* socket); // Try to accept a connection from the given server socket. int sts_net_accept_socket(sts_net_socket_t* listen_socket, sts_net_socket_t* remote_socket); +// Instead of accept, drop an incoming connection from the given server socket +int sts_net_drop_socket(sts_net_socket_t* listen_socket); + // Send data to the socket. int sts_net_send(sts_net_socket_t* socket, const void* data, int length); @@ -126,12 +132,8 @@ int sts_net_recv(sts_net_socket_t* socket, void* data, int length); // Initialized a socket set. void sts_net_init_socket_set(sts_net_set_t* set); -// Add a socket to the socket set. -int sts_net_add_socket_to_set(sts_net_socket_t* socket, sts_net_set_t* set); - -// Remove a socket from the socket set. You have to remove the socket from a set manually. -// sts_net_close_socket WILL NOT DO THAT! -int sts_net_remove_socket_from_set(sts_net_socket_t* socket, sts_net_set_t* set); +// Get an available socket from the set (returns NULL if none available) +sts_net_socket_t* sts_net_get_available_socket_from_set(sts_net_set_t* set); // Checks for activity on all sockets in the given socket set. If you want to peek for events // pass 0.0f to the timeout. @@ -316,56 +318,76 @@ void sts_net_shutdown() { } -int sts_net_open_socket(sts_net_socket_t* sock, const char* host, const char* service) { - struct addrinfo hints; - struct addrinfo *res = NULL, *r = NULL; +static int sts_net__resolvehost(struct sockaddr_in* sin, const char* host, int port) { + sts__memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + sin->sin_addr.s_addr = (host ? inet_addr(host) : INADDR_ANY); + if (sin->sin_addr.s_addr == INADDR_NONE) { + struct hostent *hostent = gethostbyname(host); + if (hostent) + sts__memcpy(&sin->sin_addr.s_addr, hostent->h_addr, hostent->h_length); + else + return -1; + } + return 0; +} + + +int sts_net_connect(sts_net_socket_t* sock, const char* host, int port) { int fd = INVALID_SOCKET; + struct sockaddr_in sin; sts_net_reset_socket(sock); - sts__memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if (host != NULL) { + if (sts_net__resolvehost(&sin, host, port)) { + return sts_net__set_error("Cannot resolve hostname"); + } + // try to connect to remote host - if (getaddrinfo(host, service, &hints, &res) != 0) return sts_net__set_error("Cannot resolve hostname"); - for (r = res; r; r = r->ai_next) { - fd = (int)socket(r->ai_family, r->ai_socktype, r->ai_protocol); - if (fd == INVALID_SOCKET) continue; - if (connect(fd, r->ai_addr, (int)r->ai_addrlen) == 0) break; - closesocket(fd); + fd = (int)socket(PF_INET, SOCK_STREAM, 0); + if (connect(fd, (const struct sockaddr *)&sin, sizeof(sin))) { + return sts_net__set_error("Could not create socket"); } - freeaddrinfo(res); - if (!r) return sts_net__set_error("Cannot connect to host"); + sock->fd = fd; - } else { + return 0; +} + + +int sts_net_listen(sts_net_socket_t* sock, int port, const char* bind_address) { + int fd = INVALID_SOCKET; + struct sockaddr_in sin; + + sts_net_reset_socket(sock); + // listen for connection (start server) - hints.ai_flags = AI_PASSIVE; - if (getaddrinfo(NULL, service, &hints, &res) != 0) return sts_net__set_error("Cannot resolve hostname"); - fd = (int)socket(res->ai_family, res->ai_socktype, res->ai_protocol); + fd = (int)socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd == INVALID_SOCKET) { - freeaddrinfo(res); return sts_net__set_error("Could not create socket"); } + #ifndef _WIN32 { int yes = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); } #endif // _WIN32 - if (bind(fd, res->ai_addr, (int)res->ai_addrlen) == SOCKET_ERROR) { - freeaddrinfo(res); + + if (sts_net__resolvehost(&sin, bind_address, port)) { + return sts_net__set_error("Cannot resolve bind address"); + } + + if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) { closesocket(fd); return sts_net__set_error("Could not bind to port"); } - freeaddrinfo(res); if (listen(fd, STS_NET_BACKLOG) == SOCKET_ERROR) { closesocket(fd); return sts_net__set_error("Could not listen to socket"); } sock->server = 1; sock->fd = fd; - } return 0; } @@ -399,6 +421,29 @@ int sts_net_accept_socket(sts_net_socket_t* listen_socket, sts_net_socket_t* rem } +int sts_net_drop_socket(sts_net_socket_t* listen_socket) { + int fd; + struct sockaddr_in sock_addr; + socklen_t sock_alen; + + if (!listen_socket->server) { + return sts_net__set_error("Cannot drop incoming connection on client socket"); + } + if (listen_socket->fd == INVALID_SOCKET) { + return sts_net__set_error("Cannot drop incoming connection on closed socket"); + } + + sock_alen = sizeof(sock_addr); + listen_socket->ready = 0; + fd = (int)accept(listen_socket->fd, (struct sockaddr*)&sock_addr, &sock_alen); + if (fd == INVALID_SOCKET) { + return sts_net__set_error("Accept failed"); + } + closesocket(fd); + return 0; +} + + int sts_net_send(sts_net_socket_t* socket, const void* data, int length) { if (socket->server) { return sts_net__set_error("Cannot send on server socket"); @@ -433,38 +478,19 @@ int sts_net_recv(sts_net_socket_t* socket, void* data, int length) { void sts_net_init_socket_set(sts_net_set_t* set) { int i; for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - set->sockets[i] = NULL; - } -} - - -int sts_net_add_socket_to_set(sts_net_socket_t *socket, sts_net_set_t *set) { - int i; - if (socket->fd == INVALID_SOCKET) { - return sts_net__set_error("Cannot add closed socket to set"); - } - for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (!set->sockets[i]) { - set->sockets[i] = socket; - return 0; - } + set->sockets[i].ready = 0; + set->sockets[i].fd = INVALID_SOCKET; } - return sts_net__set_error("Socket set is full"); } -int sts_net_remove_socket_from_set(sts_net_socket_t *socket, sts_net_set_t *set) { +sts_net_socket_t* sts_net_get_available_socket_from_set(sts_net_set_t* set) { int i; - if (socket->fd == INVALID_SOCKET) { - return sts_net__set_error("Cannot remove closed socket from set"); - } for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (set->sockets[i] == socket) { - set->sockets[i] = NULL; - return 0; - } + if (set->sockets[i].fd == INVALID_SOCKET) + return &set->sockets[i]; } - return sts_net__set_error("Socket not found in set"); + return NULL; } @@ -473,13 +499,19 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { struct timeval tv; int i, max_fd, result; + //static assertion to make sure STS_NET_SET_SOCKETS fits into FD_SETSIZE + typedef char static_assert_set_size_too_large[(STS_NET_SET_SOCKETS <= FD_SETSIZE)?1:-1]; FD_ZERO(&fds); for (i = 0, max_fd = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (set->sockets[i]) { - FD_SET(set->sockets[i]->fd, &fds); - if (set->sockets[i]->fd > max_fd) { - max_fd = set->sockets[i]->fd; + if (set->sockets[i].fd != INVALID_SOCKET) { + #ifdef _WIN32 + FD_SET((SOCKET)set->sockets[i].fd, &fds); + #else + FD_SET(set->sockets[i].fd, &fds); + #endif + if (set->sockets[i].fd > max_fd) { + max_fd = set->sockets[i].fd; } } } @@ -490,15 +522,15 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { result = select(max_fd + 1, &fds, NULL, NULL, &tv); if (result > 0) { for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (set->sockets[i]) { - if (FD_ISSET(set->sockets[i]->fd, &fds)) { - set->sockets[i]->ready = 1; + if (set->sockets[i].fd != INVALID_SOCKET) { + if (FD_ISSET(set->sockets[i].fd, &fds)) { + set->sockets[i].ready = 1; } } - } + } } else if (result == SOCKET_ERROR) { sts_net__set_error("Error on select()"); - } + } return result; } @@ -654,52 +686,46 @@ void panic(const char* msg) { int main(int argc, char *argv[]) { int i, j, bytes; sts_net_set_t set; - sts_net_socket_t server; - sts_net_socket_t clients[STS_NET_SET_SOCKETS]; + sts_net_socket_t *server, *client; char buffer[256]; (void)(argc); (void)(argv); - for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - clients[i].ready = 0; - clients[i].fd = INVALID_SOCKET; - } - sts_net_init(); - if (sts_net_open_socket(&server, NULL, "4040") < 0) panic(sts_net_get_last_error()); sts_net_init_socket_set(&set); - if (sts_net_add_socket_to_set(&server, &set) < 0) panic(sts_net_get_last_error()); + server = &set.sockets[0]; + if (sts_net_listen(server, 4040, NULL) < 0) panic(sts_net_get_last_error()); while(1) { puts("Waiting..."); if (sts_net_check_socket_set(&set, 0.5) < 0) panic(sts_net_get_last_error()); // check server - if (server.ready) { - for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (clients[i].fd == INVALID_SOCKET) { - if (sts_net_accept_socket(&server, &clients[i]) < 0) panic(sts_net_get_last_error()); - if (sts_net_add_socket_to_set(&clients[i], &set) < 0) panic(sts_net_get_last_error()); - sts_net_gethostname(&clients[i], buffer, sizeof(buffer), 1, NULL); + if (server->ready) { + client = sts_net_get_available_socket_from_set(&set); + if (client) { + if (sts_net_accept_socket(server, client) < 0) panic(sts_net_get_last_error()); + sts_net_gethostname(client, buffer, sizeof(buffer), 1, NULL); printf("Client connected '%s'!\n", buffer); - break; } + else { + sts_net_drop_socket(server); + puts("Connection set full, client dropped"); } } // check clients for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (clients[i].ready) { + if (set.sockets[i].ready) { memset(buffer, 0, sizeof(buffer)); - bytes = sts_net_recv(&clients[i], buffer, sizeof(buffer) - 1); + bytes = sts_net_recv(&set.sockets[i], buffer, sizeof(buffer) - 1); if (bytes <= 0) { - if (sts_net_remove_socket_from_set(&clients[i], &set) < 0) panic(sts_net_get_last_error()); - sts_net_close_socket(&clients[i]); + sts_net_close_socket(&set.sockets[i]); puts("Client disconnected"); } else { // broadcast for (j = 0; j < STS_NET_SET_SOCKETS; ++j) { - if (clients[j].fd != INVALID_SOCKET) { - if (sts_net_send(&clients[j], buffer, bytes) < 0) panic(sts_net_get_last_error()); + if (set.sockets[j].fd != INVALID_SOCKET && set.sockets[j].server == 0) { + if (sts_net_send(&set.sockets[j], buffer, bytes) < 0) panic(sts_net_get_last_error()); } } printf("Broadcast: %s\n", buffer); From 287d17535da1ab6c1bb99952bc8f79758c619de1 Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Fri, 7 Apr 2017 14:31:57 +0900 Subject: [PATCH 09/12] Added sts_net_check_socket to check just a single socket --- sts_net.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/sts_net.h b/sts_net.h index aeb9eca..4b43712 100644 --- a/sts_net.h +++ b/sts_net.h @@ -145,6 +145,9 @@ sts_net_socket_t* sts_net_get_available_socket_from_set(sts_net_set_t* set); // >0 amount of sockets with activity int sts_net_check_socket_set(sts_net_set_t* set, const float timeout); +// Check for activity on a single socket. Parameters and return value see above. +int sts_net_check_socket(sts_net_socket_t* socket, const float timeout); + // Read the connected hostname of a socket into out buffer // Supply 1 to want_only_ip if instead of the resolved host name only the ip address is needed // out_port can be supplied as NULL if you're not interested in the connected port number @@ -535,6 +538,34 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { } +int sts_net_check_socket(sts_net_socket_t* socket, const float timeout) { + fd_set fds; + struct timeval tv; + int result; + + if (socket->fd == INVALID_SOCKET) { + return sts_net__set_error("Cannot check a closed socket"); + } + + FD_ZERO(&fds); + #ifdef _WIN32 + FD_SET((SOCKET)socket->fd, &fds); + #else + FD_SET(socket->fd, &fds); + #endif + + tv.tv_sec = (int)timeout; + tv.tv_usec = (int)((timeout - (float)tv.tv_sec) * 1000000.0f); + result = select(socket->fd + 1, &fds, NULL, NULL, &tv); + if (result > 0) { + socket->ready = 1; + } else if (result == SOCKET_ERROR) { + sts_net__set_error("Error on select()"); + } + return result; +} + + int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port) { struct sockaddr_in sin; struct in_addr in; From ad4c1e825b84edae338e68d4c5697f2b2e96cb6f Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Sun, 9 Apr 2017 21:37:30 +0900 Subject: [PATCH 10/12] Fixed some more compile warnings on stricter warning levels --- sts_net.h | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/sts_net.h b/sts_net.h index 4b43712..8a35107 100644 --- a/sts_net.h +++ b/sts_net.h @@ -4,6 +4,12 @@ written 2017 by Sebastian Steinhauer VERSION HISTORY + 0.07s (2017-04-07) build warning fixes + split sts_net_open_socket into connect/listen + added sts_net_gethostname + added sts_net_enumerate_interfaces + added sts_net_drop_socket + changed socket set usage (see new example code) 0.07 (2017-02-24) added checks for a valid socket in every function return 0 for an empty socket set 0.06 (2017-01-14) fixed warnings when compiling on Windows 64-bit @@ -324,7 +330,7 @@ void sts_net_shutdown() { static int sts_net__resolvehost(struct sockaddr_in* sin, const char* host, int port) { sts__memset(sin, 0, sizeof(struct sockaddr_in)); sin->sin_family = AF_INET; - sin->sin_port = htons(port); + sin->sin_port = htons((short)port); sin->sin_addr.s_addr = (host ? inet_addr(host) : INADDR_ANY); if (sin->sin_addr.s_addr == INADDR_NONE) { struct hostent *hostent = gethostbyname(host); @@ -503,7 +509,11 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { int i, max_fd, result; //static assertion to make sure STS_NET_SET_SOCKETS fits into FD_SETSIZE - typedef char static_assert_set_size_too_large[(STS_NET_SET_SOCKETS <= FD_SETSIZE)?1:-1]; + typedef char static_assert_set_size_too_large[(STS_NET_SET_SOCKETS <= FD_SETSIZE)?1:-1] + #ifndef _MSC_VER + __attribute__((unused)) + #endif + ; FD_ZERO(&fds); for (i = 0, max_fd = 0; i < STS_NET_SET_SOCKETS; ++i) { @@ -532,8 +542,8 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { } } } else if (result == SOCKET_ERROR) { - sts_net__set_error("Error on select()"); - } + return sts_net__set_error("Error on select()"); + } return result; } @@ -560,7 +570,7 @@ int sts_net_check_socket(sts_net_socket_t* socket, const float timeout) { if (result > 0) { socket->ready = 1; } else if (result == SOCKET_ERROR) { - sts_net__set_error("Error on select()"); + return sts_net__set_error("Error on select()"); } return result; } @@ -593,7 +603,7 @@ int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, if (host == NULL) { return sts_net__set_error("Error while getting host name of socket"); } - addrLen = strlen(host); + addrLen = (int)strlen(host); if (addrLen >= out_size) { return sts_net__set_error("Provided buffer is too small for host name"); } @@ -640,7 +650,7 @@ int sts_net_enumerate_interfaces(sts_net_interfaceinfo_t* table, int tablesize, if (!ifnamelen) ifnamelen = sizeof(table->interface_name) - 1; #else ifnamelen = strlen(ifa->ifa_name); - if (ifnamelen >= sizeof(table->interface_name)) ifnamelen = sizeof(table->interface_name) - 1; + if (ifnamelen >= (int)sizeof(table->interface_name)) ifnamelen = sizeof(table->interface_name) - 1; sts__memcpy(table->interface_name, ifa->ifa_name, ifnamelen); #endif table->interface_name[ifnamelen] = '\0'; From 6fada02fb4c5e3f12e5aafff1fbb395e8a54184a Mon Sep 17 00:00:00 2001 From: Bernhard Schelling Date: Mon, 19 Jun 2017 02:00:25 +0900 Subject: [PATCH 11/12] fix possible missing ptrdiff_t on non-windows platforms fix source formatting --- sts_net.h | 93 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/sts_net.h b/sts_net.h index 8a35107..1ea662e 100644 --- a/sts_net.h +++ b/sts_net.h @@ -248,6 +248,7 @@ typedef int socklen_t; #include #pragma comment(lib, "iphlpapi.lib") #endif +#define STS_INVALID_SOCKET (int)(ptrdiff_t)INVALID_SOCKET #else #include #include @@ -256,9 +257,9 @@ typedef int socklen_t; #include #include #include -#define INVALID_SOCKET -1 -#define SOCKET_ERROR -1 -#define closesocket(fd) close(fd) +#define STS_INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket(fd) close(fd) #ifndef STS_NET_NO_ENUMERATEINTERFACES #include #endif @@ -292,7 +293,7 @@ const char *sts_net_get_last_error() { void sts_net_reset_socket(sts_net_socket_t* socket) { - socket->fd = INVALID_SOCKET; + socket->fd = STS_INVALID_SOCKET; socket->ready = 0; socket->server = 0; #ifndef STS_NET_NO_PACKETS @@ -303,7 +304,7 @@ void sts_net_reset_socket(sts_net_socket_t* socket) { int sts_net_is_socket_valid(sts_net_socket_t* socket) { - return socket->fd != INVALID_SOCKET; + return socket->fd != STS_INVALID_SOCKET; } @@ -344,7 +345,7 @@ static int sts_net__resolvehost(struct sockaddr_in* sin, const char* host, int p int sts_net_connect(sts_net_socket_t* sock, const char* host, int port) { - int fd = INVALID_SOCKET; + int fd = STS_INVALID_SOCKET; struct sockaddr_in sin; sts_net_reset_socket(sock); @@ -353,34 +354,34 @@ int sts_net_connect(sts_net_socket_t* sock, const char* host, int port) { return sts_net__set_error("Cannot resolve hostname"); } - // try to connect to remote host + // try to connect to remote host fd = (int)socket(PF_INET, SOCK_STREAM, 0); if (connect(fd, (const struct sockaddr *)&sin, sizeof(sin))) { return sts_net__set_error("Could not create socket"); - } + } - sock->fd = fd; + sock->fd = fd; return 0; } int sts_net_listen(sts_net_socket_t* sock, int port, const char* bind_address) { - int fd = INVALID_SOCKET; + int fd = STS_INVALID_SOCKET; struct sockaddr_in sin; sts_net_reset_socket(sock); - // listen for connection (start server) + // listen for connection (start server) fd = (int)socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (fd == INVALID_SOCKET) { - return sts_net__set_error("Could not create socket"); - } + if (fd == STS_INVALID_SOCKET) { + return sts_net__set_error("Could not create socket"); + } #ifndef _WIN32 - { - int yes = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); - } + { + int yes = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); + } #endif // _WIN32 if (sts_net__resolvehost(&sin, bind_address, port)) { @@ -388,21 +389,21 @@ int sts_net_listen(sts_net_socket_t* sock, int port, const char* bind_address) { } if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) { - closesocket(fd); - return sts_net__set_error("Could not bind to port"); - } - if (listen(fd, STS_NET_BACKLOG) == SOCKET_ERROR) { - closesocket(fd); - return sts_net__set_error("Could not listen to socket"); - } - sock->server = 1; - sock->fd = fd; + closesocket(fd); + return sts_net__set_error("Could not bind to port"); + } + if (listen(fd, STS_NET_BACKLOG) == SOCKET_ERROR) { + closesocket(fd); + return sts_net__set_error("Could not listen to socket"); + } + sock->server = 1; + sock->fd = fd; return 0; } void sts_net_close_socket(sts_net_socket_t* socket) { - if (socket->fd != INVALID_SOCKET) closesocket(socket->fd); + if (socket->fd != STS_INVALID_SOCKET) closesocket(socket->fd); sts_net_reset_socket(socket); } @@ -414,7 +415,7 @@ int sts_net_accept_socket(sts_net_socket_t* listen_socket, sts_net_socket_t* rem if (!listen_socket->server) { return sts_net__set_error("Cannot accept on client socket"); } - if (listen_socket->fd == INVALID_SOCKET) { + if (listen_socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Cannot accept on closed socket"); } @@ -423,7 +424,7 @@ int sts_net_accept_socket(sts_net_socket_t* listen_socket, sts_net_socket_t* rem remote_socket->ready = 0; remote_socket->server = 0; remote_socket->fd = (int)accept(listen_socket->fd, (struct sockaddr*)&sock_addr, &sock_alen); - if (remote_socket->fd == INVALID_SOCKET) { + if (remote_socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Accept failed"); } return 0; @@ -438,14 +439,14 @@ int sts_net_drop_socket(sts_net_socket_t* listen_socket) { if (!listen_socket->server) { return sts_net__set_error("Cannot drop incoming connection on client socket"); } - if (listen_socket->fd == INVALID_SOCKET) { + if (listen_socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Cannot drop incoming connection on closed socket"); } sock_alen = sizeof(sock_addr); listen_socket->ready = 0; fd = (int)accept(listen_socket->fd, (struct sockaddr*)&sock_addr, &sock_alen); - if (fd == INVALID_SOCKET) { + if (fd == STS_INVALID_SOCKET) { return sts_net__set_error("Accept failed"); } closesocket(fd); @@ -457,7 +458,7 @@ int sts_net_send(sts_net_socket_t* socket, const void* data, int length) { if (socket->server) { return sts_net__set_error("Cannot send on server socket"); } - if (socket->fd == INVALID_SOCKET) { + if (socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Cannot send on closed socket"); } if (send(socket->fd, (const char*)data, length, 0) != length) { @@ -472,7 +473,7 @@ int sts_net_recv(sts_net_socket_t* socket, void* data, int length) { if (socket->server) { return sts_net__set_error("Cannot receive on server socket"); } - if (socket->fd == INVALID_SOCKET) { + if (socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Cannot receive on closed socket"); } socket->ready = 0; @@ -488,7 +489,7 @@ void sts_net_init_socket_set(sts_net_set_t* set) { int i; for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { set->sockets[i].ready = 0; - set->sockets[i].fd = INVALID_SOCKET; + set->sockets[i].fd = STS_INVALID_SOCKET; } } @@ -496,7 +497,7 @@ void sts_net_init_socket_set(sts_net_set_t* set) { sts_net_socket_t* sts_net_get_available_socket_from_set(sts_net_set_t* set) { int i; for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (set->sockets[i].fd == INVALID_SOCKET) + if (set->sockets[i].fd == STS_INVALID_SOCKET) return &set->sockets[i]; } return NULL; @@ -517,7 +518,7 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { FD_ZERO(&fds); for (i = 0, max_fd = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (set->sockets[i].fd != INVALID_SOCKET) { + if (set->sockets[i].fd != STS_INVALID_SOCKET) { #ifdef _WIN32 FD_SET((SOCKET)set->sockets[i].fd, &fds); #else @@ -535,12 +536,12 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { result = select(max_fd + 1, &fds, NULL, NULL, &tv); if (result > 0) { for (i = 0; i < STS_NET_SET_SOCKETS; ++i) { - if (set->sockets[i].fd != INVALID_SOCKET) { + if (set->sockets[i].fd != STS_INVALID_SOCKET) { if (FD_ISSET(set->sockets[i].fd, &fds)) { set->sockets[i].ready = 1; } } - } + } } else if (result == SOCKET_ERROR) { return sts_net__set_error("Error on select()"); } @@ -553,7 +554,7 @@ int sts_net_check_socket(sts_net_socket_t* socket, const float timeout) { struct timeval tv; int result; - if (socket->fd == INVALID_SOCKET) { + if (socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Cannot check a closed socket"); } @@ -586,7 +587,7 @@ int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, if (out_size) out_host[0] = '\0'; - if (socket->fd == INVALID_SOCKET) { + if (socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Cannot get host name of closed socket"); } @@ -725,10 +726,10 @@ void panic(const char* msg) { int main(int argc, char *argv[]) { - int i, j, bytes; - sts_net_set_t set; + int i, j, bytes; + sts_net_set_t set; sts_net_socket_t *server, *client; - char buffer[256]; + char buffer[256]; (void)(argc); (void)(argv); @@ -748,7 +749,7 @@ int main(int argc, char *argv[]) { if (sts_net_accept_socket(server, client) < 0) panic(sts_net_get_last_error()); sts_net_gethostname(client, buffer, sizeof(buffer), 1, NULL); printf("Client connected '%s'!\n", buffer); - } + } else { sts_net_drop_socket(server); puts("Connection set full, client dropped"); @@ -765,7 +766,7 @@ int main(int argc, char *argv[]) { } else { // broadcast for (j = 0; j < STS_NET_SET_SOCKETS; ++j) { - if (set.sockets[j].fd != INVALID_SOCKET && set.sockets[j].server == 0) { + if (set.sockets[j].fd != STS_INVALID_SOCKET && set.sockets[j].server == 0) { if (sts_net_send(&set.sockets[j], buffer, bytes) < 0) panic(sts_net_get_last_error()); } } From 43f73d2da67cc7bc753db9266e00f3d29a6cbc1f Mon Sep 17 00:00:00 2001 From: Bernhard Schelling <14200249+schellingb@users.noreply.github.com> Date: Sat, 23 May 2020 04:49:31 +0900 Subject: [PATCH 12/12] added basic udp functionality (sts_net_udp_*) added want_local_name to sts_net_gethostname added share_port option to sts_net_listen --- sts_net.h | 252 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 215 insertions(+), 37 deletions(-) diff --git a/sts_net.h b/sts_net.h index 1ea662e..03c6d41 100644 --- a/sts_net.h +++ b/sts_net.h @@ -4,12 +4,15 @@ written 2017 by Sebastian Steinhauer VERSION HISTORY - 0.07s (2017-04-07) build warning fixes - split sts_net_open_socket into connect/listen - added sts_net_gethostname - added sts_net_enumerate_interfaces - added sts_net_drop_socket - changed socket set usage (see new example code) + 0.07s2 (2020-05-23) added basic udp functionality (sts_net_udp_*) + added want_local_name to sts_net_gethostname + added share_port option to sts_net_listen + 0.07s1 (2017-04-07) build warning fixes + split sts_net_open_socket into connect/listen + added sts_net_gethostname + added sts_net_enumerate_interfaces + added sts_net_drop_socket + changed socket set usage (see new example code) 0.07 (2017-02-24) added checks for a valid socket in every function return 0 for an empty socket set 0.06 (2017-01-14) fixed warnings when compiling on Windows 64-bit @@ -57,7 +60,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif // __cplusplus //////////////////////////////////////////////////////////////////////////////// // @@ -92,7 +95,7 @@ typedef struct { #ifndef STS_NET_NO_ERRORSTRINGS // Get the last error from sts_net (can be called even before sts_net_init) const char* sts_net_get_last_error(); -#endif +#endif // STS_NET_NO_ERRORSTRINGS // Initialized the sts_net library. You have to call this before any other function (except sts_net_get_last_error) int sts_net_init(); @@ -116,8 +119,9 @@ int sts_net_is_socket_valid(sts_net_socket_t* socket); int sts_net_connect(sts_net_socket_t* socket, const char* host, int port); // Open a (TCP) server socket -// Supply NULL as bind_host to listen on any address -int sts_net_listen(sts_net_socket_t* socket, int port, const char* bind_address); +// Supply NULL as bind_address to listen on any address +// Set share_port to 0 to block multiple sockets listening on the same port +int sts_net_listen(sts_net_socket_t* socket, int port, const char* bind_address, int share_port); // Closes the socket. void sts_net_close_socket(sts_net_socket_t* socket); @@ -158,7 +162,30 @@ int sts_net_check_socket(sts_net_socket_t* socket, const float timeout); // Supply 1 to want_only_ip if instead of the resolved host name only the ip address is needed // out_port can be supplied as NULL if you're not interested in the connected port number // Returns the length of what was written into out_host or -1 on error (sets out_host empty string) -int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port); +// Supply 1 to want_local_name if you want the name of the local side not the remote peer name +int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port, int want_local_name); + +// Open a UDP client socket +int sts_net_udp_open(sts_net_socket_t* socket); + +// Set UDP multicast TTL (time to live, after how many hops on the network the packet will not be re-sent/broadcast) +int sts_net_udp_set_multicast_ttl(sts_net_socket_t* socket, int ttl); + +// Join UDP socket to a multi cast address +int sts_net_udp_join_multicast(sts_net_socket_t* socket, const char* multicast_host); + +// Bind UDP socket to a port +// Supply NULL as bind_address to listen on any address +// Set share_port to 0 to block multiple sockets listening on the same port +int sts_net_udp_bind(sts_net_socket_t* socket, int port, const char* bind_address, int share_port); + +// Receive data from a bound UDP socket +// ip (if not NULL) will be filled with 4 ip parts of sender (must be unsigned char[4]) +// port (if not NULL) will be filled with the senders port number +int sts_net_udp_recv_from(sts_net_socket_t* socket, void* data, int length, unsigned char* ip, int* port); + +// Send data through a UDP socket +int sts_net_udp_send(sts_net_socket_t* socket, const char* host, int port, const void* data, int length); #ifndef STS_NET_NO_ENUMERATEINTERFACES typedef struct { @@ -217,7 +244,7 @@ void sts_net_drop_packet(sts_net_socket_t* socket); #ifdef __cplusplus } -#endif +#endif // __cplusplus #endif // __INCLUDED__STS_NET_H__ @@ -247,7 +274,8 @@ typedef int socklen_t; #ifndef STS_NET_NO_ENUMERATEINTERFACES #include #pragma comment(lib, "iphlpapi.lib") -#endif +#endif // STS_NET_NO_ENUMERATEINTERFACES +#include // ptrdiff_t #define STS_INVALID_SOCKET (int)(ptrdiff_t)INVALID_SOCKET #else #include @@ -262,9 +290,14 @@ typedef int socklen_t; #define closesocket(fd) close(fd) #ifndef STS_NET_NO_ENUMERATEINTERFACES #include -#endif -#endif +#endif // STS_NET_NO_ENUMERATEINTERFACES +#endif // _WIN32 +#ifdef MSG_NOSIGNAL +#define STS_SEND_FLAG MSG_NOSIGNAL +#else +#define STS_SEND_FLAG 0 +#endif // MSG_NOSIGNAL #ifndef sts__memcpy #define sts__memcpy memcpy @@ -289,9 +322,19 @@ const char *sts_net_get_last_error() { } #else #define sts_net__set_error(m) -1 -#endif +#endif // STS_NET_NO_ERRORSTRINGS +int sts_net__create_socket(int type) { + int fd = (int)socket(AF_INET, type, 0), yes = 1; + if (fd != STS_INVALID_SOCKET) { + #ifdef SO_NOSIGPIPE + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (char*)&yes, sizeof(yes)); + #endif // SO_NOSIGPIPE + } + return fd; +} + void sts_net_reset_socket(sts_net_socket_t* socket) { socket->fd = STS_INVALID_SOCKET; socket->ready = 0; @@ -345,7 +388,7 @@ static int sts_net__resolvehost(struct sockaddr_in* sin, const char* host, int p int sts_net_connect(sts_net_socket_t* sock, const char* host, int port) { - int fd = STS_INVALID_SOCKET; + int fd; struct sockaddr_in sin; sts_net_reset_socket(sock); @@ -355,8 +398,8 @@ int sts_net_connect(sts_net_socket_t* sock, const char* host, int port) { } // try to connect to remote host - fd = (int)socket(PF_INET, SOCK_STREAM, 0); - if (connect(fd, (const struct sockaddr *)&sin, sizeof(sin))) { + fd = sts_net__create_socket(SOCK_STREAM); + if (fd == STS_INVALID_SOCKET || connect(fd, (const struct sockaddr *)&sin, sizeof(sin))) { return sts_net__set_error("Could not create socket"); } @@ -365,25 +408,23 @@ int sts_net_connect(sts_net_socket_t* sock, const char* host, int port) { } -int sts_net_listen(sts_net_socket_t* sock, int port, const char* bind_address) { - int fd = STS_INVALID_SOCKET; +int sts_net_listen(sts_net_socket_t* sock, int port, const char* bind_address, int share_port) { + int fd; struct sockaddr_in sin; sts_net_reset_socket(sock); // listen for connection (start server) - fd = (int)socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + fd = sts_net__create_socket(SOCK_STREAM); if (fd == STS_INVALID_SOCKET) { return sts_net__set_error("Could not create socket"); } -#ifndef _WIN32 - { + if (share_port) { int yes = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); } -#endif // _WIN32 - + if (sts_net__resolvehost(&sin, bind_address, port)) { return sts_net__set_error("Cannot resolve bind address"); } @@ -461,7 +502,7 @@ int sts_net_send(sts_net_socket_t* socket, const void* data, int length) { if (socket->fd == STS_INVALID_SOCKET) { return sts_net__set_error("Cannot send on closed socket"); } - if (send(socket->fd, (const char*)data, length, 0) != length) { + if (send(socket->fd, (const char*)data, length, STS_SEND_FLAG) != length) { return sts_net__set_error("Cannot send data"); } return 0; @@ -513,7 +554,7 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { typedef char static_assert_set_size_too_large[(STS_NET_SET_SOCKETS <= FD_SETSIZE)?1:-1] #ifndef _MSC_VER __attribute__((unused)) - #endif + #endif // _MSC_VER ; FD_ZERO(&fds); @@ -523,7 +564,7 @@ int sts_net_check_socket_set(sts_net_set_t* set, const float timeout) { FD_SET((SOCKET)set->sockets[i].fd, &fds); #else FD_SET(set->sockets[i].fd, &fds); - #endif + #endif // _WIN32 if (set->sockets[i].fd > max_fd) { max_fd = set->sockets[i].fd; } @@ -563,7 +604,7 @@ int sts_net_check_socket(sts_net_socket_t* socket, const float timeout) { FD_SET((SOCKET)socket->fd, &fds); #else FD_SET(socket->fd, &fds); - #endif + #endif // _WIN32 tv.tv_sec = (int)timeout; tv.tv_usec = (int)((timeout - (float)tv.tv_sec) * 1000000.0f); @@ -577,7 +618,7 @@ int sts_net_check_socket(sts_net_socket_t* socket, const float timeout) { } -int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port) { +int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, int want_only_ip, int* out_port, int want_local_name) { struct sockaddr_in sin; struct in_addr in; struct hostent* hostEntry; @@ -592,7 +633,7 @@ int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, } sinLength = sizeof(struct sockaddr_in); - if (getsockname(socket->fd, (struct sockaddr *)&sin, &sinLength) == -1) { + if ((want_local_name ? getsockname : getpeername)(socket->fd, (struct sockaddr *)&sin, &sinLength) == -1) { return sts_net__set_error("Error while getting host name of socket"); } @@ -612,6 +653,143 @@ int sts_net_gethostname(sts_net_socket_t* socket, char* out_host, int out_size, return addrLen; } + +int sts_net_udp_open(sts_net_socket_t* sock) { + int fd; + + sts_net_reset_socket(sock); + + fd = sts_net__create_socket(SOCK_DGRAM); + if (fd == STS_INVALID_SOCKET) { + return sts_net__set_error("Could not create socket"); + } + + sock->fd = fd; + return 0; +} + + +int sts_net_udp_set_multicast_ttl(sts_net_socket_t* socket, int ttl) { + if (socket->fd == STS_INVALID_SOCKET) { + return sts_net__set_error("Cannot set ttl on closed socket"); + } + if (socket->server) { + return sts_net__set_error("Cannot set ttl on server ocket"); + } + if (setsockopt(socket->fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(int)) < 0) { + return sts_net__set_error("Set TTL option failed"); + } + return 0; +} + + +int sts_net_udp_join_multicast(sts_net_socket_t* socket, const char* multicast_host) { + struct sockaddr_in sin; + struct ip_mreq mreq; + + if (sts_net__resolvehost(&sin, multicast_host, 0)) { + return sts_net__set_error("Cannot resolve hostname"); + } + + mreq.imr_interface.s_addr = INADDR_ANY; + mreq.imr_multiaddr = sin.sin_addr; + if (setsockopt(socket->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { + return sts_net__set_error("Multicast add membership failed"); + } + return 0; +} + + +int sts_net_udp_bind(sts_net_socket_t* socket, int port, const char* bind_address, int share_port) { + struct sockaddr_in sin; + + if (socket->fd == STS_INVALID_SOCKET) { + return sts_net__set_error("Cannot bind on closed socket"); + } + if (socket->server) { + return sts_net__set_error("Cannot bind on server socket"); + } + + if (share_port) { + int yes = 1; + setsockopt(socket->fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); + } + + if (sts_net__resolvehost(&sin, bind_address, port)) { + return sts_net__set_error("Cannot resolve bind address"); + } + + if (bind(socket->fd, (struct sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) { + return sts_net__set_error("Could not bind to port"); + } + + return 0; +} + + +int sts_net_udp_recv_from(sts_net_socket_t* socket, void* data, int length, unsigned char* ip, int* port) { + struct sockaddr_in sin; + socklen_t fromlen = sizeof(sin); + int result; + + if (socket->server) { + return sts_net__set_error("Cannot receive on server socket"); + } + if (socket->fd == STS_INVALID_SOCKET) { + return sts_net__set_error("Cannot receive on closed socket"); + } + socket->ready = 0; + result = recvfrom(socket->fd, (char*)data, length, 0, (struct sockaddr*)&sin, &fromlen); + if (result < 0) { + return sts_net__set_error("Cannot receive data"); + } + + if (ip) { + sts__memcpy(ip, &sin.sin_addr, 4); + } + if (port) { + *port = ntohs(sin.sin_port); + } + + return result; +} + +int sts_net_udp_send(sts_net_socket_t* socket, const char* host, int port, const void* data, int length) +{ + struct sockaddr_in sin; + #ifdef _WIN32 + WSABUF bufs[] = { { (ULONG)length, (CHAR*)data } }; + DWORD sent; + #else + struct msghdr mhdr; + struct iovec iovs[] = { { (void*)data, (size_t)length } }; + #endif // _WIN32 + + if (socket->fd == STS_INVALID_SOCKET) { + return sts_net__set_error("Cannot send on closed socket"); + } + if (socket->server) { + return sts_net__set_error("Cannot send on server socket"); + } + if (sts_net__resolvehost(&sin, host, port)) { + return sts_net__set_error("Cannot resolve hostname"); + } + + #ifdef _WIN32 + if (WSASendTo((SOCKET)socket->fd, bufs, (DWORD)1, &sent, 0, (struct sockaddr*)&sin, sizeof(sin), NULL, NULL) == SOCKET_ERROR) { + #else + mhdr.msg_name = & sin; + mhdr.msg_namelen = sizeof (struct sockaddr_in); + mhdr.msg_iov = iovs; + mhdr.msg_iovlen = (int)1; + if ((int)sendmsg(socket->fd, &mhdr, STS_SEND_FLAG) == -1) { + #endif // _WIN32 + return sts_net__set_error("Cannot send data"); + } + + return 0; +} + #ifndef STS_NET_NO_ENUMERATEINTERFACES int sts_net_enumerate_interfaces(sts_net_interfaceinfo_t* table, int tablesize, int want_ipv4, int want_ipv6) { void* sinaddr; @@ -637,7 +815,7 @@ int sts_net_enumerate_interfaces(sts_net_interfaceinfo_t* table, int tablesize, for (ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr) { addr = ifa->ifa_addr; -#endif +#endif // _WIN32 family = addr->sa_family; if (family == AF_INET) { if (!want_ipv4) continue; } else if (family == AF_INET6) { if (!want_ipv6) continue; } @@ -653,7 +831,7 @@ int sts_net_enumerate_interfaces(sts_net_interfaceinfo_t* table, int tablesize, ifnamelen = strlen(ifa->ifa_name); if (ifnamelen >= (int)sizeof(table->interface_name)) ifnamelen = sizeof(table->interface_name) - 1; sts__memcpy(table->interface_name, ifa->ifa_name, ifnamelen); -#endif +#endif // _WIN32 table->interface_name[ifnamelen] = '\0'; inet_ntop(family, sinaddr, table->address, sizeof(table->address)); tablesize--; @@ -664,7 +842,7 @@ int sts_net_enumerate_interfaces(sts_net_interfaceinfo_t* table, int tablesize, HeapFree(GetProcessHeap(), 0, adapter_addresses); #else if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); -#endif +#endif // _WIN32 return totalcount; } #endif // STS_NET_NO_ENUMERATEINTERFACES @@ -737,7 +915,7 @@ int main(int argc, char *argv[]) { sts_net_init(); sts_net_init_socket_set(&set); server = &set.sockets[0]; - if (sts_net_listen(server, 4040, NULL) < 0) panic(sts_net_get_last_error()); + if (sts_net_listen(server, 4040, NULL, 0) < 0) panic(sts_net_get_last_error()); while(1) { puts("Waiting..."); @@ -747,7 +925,7 @@ int main(int argc, char *argv[]) { client = sts_net_get_available_socket_from_set(&set); if (client) { if (sts_net_accept_socket(server, client) < 0) panic(sts_net_get_last_error()); - sts_net_gethostname(client, buffer, sizeof(buffer), 1, NULL); + sts_net_gethostname(client, buffer, sizeof(buffer), 1, NULL, 0); printf("Client connected '%s'!\n", buffer); } else {