666 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			666 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | #pragma once
 | ||
|  | 
 | ||
|  | #include <kinc/global.h>
 | ||
|  | 
 | ||
|  | /*! \file socket.h
 | ||
|  |     \brief Provides low-level network-communication via UDP or TCP-sockets. | ||
|  | */ | ||
|  | 
 | ||
|  | #ifdef __cplusplus
 | ||
|  | extern "C" { | ||
|  | #endif
 | ||
|  | 
 | ||
|  | typedef enum kinc_socket_protocol { KINC_SOCKET_PROTOCOL_UDP, KINC_SOCKET_PROTOCOL_TCP } kinc_socket_protocol_t; | ||
|  | 
 | ||
|  | typedef enum kinc_socket_family { KINC_SOCKET_FAMILY_IP4, KINC_SOCKET_FAMILY_IP6 } kinc_socket_family_t; | ||
|  | 
 | ||
|  | #ifdef KINC_MICROSOFT
 | ||
|  | #if defined(_WIN64)
 | ||
|  | typedef unsigned __int64 UINT_PTR, *PUINT_PTR; | ||
|  | #else
 | ||
|  | #if !defined _W64
 | ||
|  | #define _W64
 | ||
|  | #endif
 | ||
|  | typedef _W64 unsigned int UINT_PTR, *PUINT_PTR; | ||
|  | #endif
 | ||
|  | typedef UINT_PTR SOCKET; | ||
|  | #endif
 | ||
|  | 
 | ||
|  | typedef struct kinc_socket { | ||
|  | #ifdef KINC_MICROSOFT
 | ||
|  | 	SOCKET handle; | ||
|  | #else
 | ||
|  | 	int handle; | ||
|  | #endif
 | ||
|  | 	uint32_t host; | ||
|  | 	uint32_t port; | ||
|  | 	kinc_socket_protocol_t protocol; | ||
|  | 	kinc_socket_family_t family; | ||
|  | 	bool connected; | ||
|  | } kinc_socket_t; | ||
|  | 
 | ||
|  | typedef struct kinc_socket_options { | ||
|  | 	bool non_blocking; | ||
|  | 	bool broadcast; | ||
|  | 	bool tcp_no_delay; | ||
|  | } kinc_socket_options_t; | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Initializes a socket-options-object to the default options
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="options">The new default options</param>
 | ||
|  | KINC_FUNC void kinc_socket_options_set_defaults(kinc_socket_options_t *options); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Initializes a socket-object. To set the host and port use kinc_socket_set.
 | ||
|  | /// Host will be localhost
 | ||
|  | /// Port will be 8080
 | ||
|  | /// Family will be IPv4
 | ||
|  | /// Protocol will be TCP
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="socket">The socket to initialize</param>
 | ||
|  | KINC_FUNC void kinc_socket_init(kinc_socket_t *socket); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Sets the sockets properties.
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="socket">The socket-object to use</param>
 | ||
|  | /// <param name="host">The host to use as IP or URL</param>
 | ||
|  | /// <param name="port">The port to use</param>
 | ||
|  | /// <param name="family">The IP-family to use</param>
 | ||
|  | /// <param name="protocol">The protocol to use</param>
 | ||
|  | /// <returns>Whether the socket was set correctly</returns>
 | ||
|  | KINC_FUNC bool kinc_socket_set(kinc_socket_t *socket, const char *host, int port, kinc_socket_family_t family, kinc_socket_protocol_t protocol); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Destroys a socket-object.
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="socket">The socket to destroy</param>
 | ||
|  | KINC_FUNC void kinc_socket_destroy(kinc_socket_t *socket); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Opens a socket-connection.
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="socket">The socket-object to use</param>
 | ||
|  | /// <param name="protocol">The protocol to use</param>
 | ||
|  | /// <param name="port">The port to use</param>
 | ||
|  | /// <param name="options">The options to use</param>
 | ||
|  | /// <returns>Whether the socket-connection could be opened</returns>
 | ||
|  | KINC_FUNC bool kinc_socket_open(kinc_socket_t *socket, kinc_socket_options_t *options); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// For use with non-blocking sockets to try to see if we are connected.
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="socket">The socket-object to use</param>
 | ||
|  | /// <param name="waittime">The amount of time in seconds the select function will timeout.</param>
 | ||
|  | /// <param name="read">Check if the socket is ready to be read from.</param>
 | ||
|  | /// <param name="write">Check if the socket is ready to be written to.</param>
 | ||
|  | /// <returns>Whether the socket-connection can read or write or checks both.</returns>
 | ||
|  | KINC_FUNC bool kinc_socket_select(kinc_socket_t *socket, uint32_t waittime, bool read, bool write); | ||
|  | 
 | ||
|  | /*Typically these are server actions.*/ | ||
|  | KINC_FUNC bool kinc_socket_bind(kinc_socket_t *socket); | ||
|  | KINC_FUNC bool kinc_socket_listen(kinc_socket_t *socket, int backlog); | ||
|  | KINC_FUNC bool kinc_socket_accept(kinc_socket_t *socket, kinc_socket_t *new_socket, unsigned *remote_address, unsigned *remote_port); | ||
|  | 
 | ||
|  | /*Typically this is a client action.*/ | ||
|  | KINC_FUNC bool kinc_socket_connect(kinc_socket_t *socket); | ||
|  | 
 | ||
|  | KINC_FUNC int kinc_socket_send(kinc_socket_t *socket, const uint8_t *data, int size); | ||
|  | KINC_FUNC int kinc_socket_send_address(kinc_socket_t *socket, unsigned address, int port, const uint8_t *data, int size); | ||
|  | KINC_FUNC int kinc_socket_send_url(kinc_socket_t *socket, const char *url, int port, const uint8_t *data, int size); | ||
|  | KINC_FUNC int kinc_socket_receive(kinc_socket_t *socket, uint8_t *data, int maxSize, unsigned *from_address, unsigned *from_port); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Resolves a DNS-entry to an IP and returns its integer representation.
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="url"></param>
 | ||
|  | /// <param name="port"></param>
 | ||
|  | /// <returns></returns>
 | ||
|  | KINC_FUNC unsigned kinc_url_to_int(const char *url, int port); | ||
|  | 
 | ||
|  | #ifdef KINC_IMPLEMENTATION_NETWORK
 | ||
|  | #define KINC_IMPLEMENTATION
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifdef KINC_IMPLEMENTATION
 | ||
|  | 
 | ||
|  | #undef KINC_IMPLEMENTATION
 | ||
|  | #include <kinc/libs/stb_sprintf.h>
 | ||
|  | #include <kinc/log.h>
 | ||
|  | #define KINC_IMPLEMENTATION
 | ||
|  | 
 | ||
|  | #include <errno.h>
 | ||
|  | #include <stdbool.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 
 | ||
|  | // Windows 7
 | ||
|  | #define WINVER 0x0601
 | ||
|  | #define _WIN32_WINNT 0x0601
 | ||
|  | 
 | ||
|  | #define NOATOM
 | ||
|  | #define NOCLIPBOARD
 | ||
|  | #define NOCOLOR
 | ||
|  | #define NOCOMM
 | ||
|  | #define NOCTLMGR
 | ||
|  | #define NODEFERWINDOWPOS
 | ||
|  | #define NODRAWTEXT
 | ||
|  | #define NOGDI
 | ||
|  | #define NOGDICAPMASKS
 | ||
|  | #define NOHELP
 | ||
|  | #define NOICONS
 | ||
|  | #define NOKANJI
 | ||
|  | #define NOKEYSTATES
 | ||
|  | #define NOMB
 | ||
|  | #define NOMCX
 | ||
|  | #define NOMEMMGR
 | ||
|  | #define NOMENUS
 | ||
|  | #define NOMETAFILE
 | ||
|  | #define NOMINMAX
 | ||
|  | #define NOMSG
 | ||
|  | #define NONLS
 | ||
|  | #define NOOPENFILE
 | ||
|  | #define NOPROFILER
 | ||
|  | #define NORASTEROPS
 | ||
|  | #define NOSCROLL
 | ||
|  | #define NOSERVICE
 | ||
|  | #define NOSHOWWINDOW
 | ||
|  | #define NOSOUND
 | ||
|  | #define NOSYSCOMMANDS
 | ||
|  | #define NOSYSMETRICS
 | ||
|  | #define NOTEXTMETRIC
 | ||
|  | #define NOUSER
 | ||
|  | #define NOVIRTUALKEYCODES
 | ||
|  | #define NOWH
 | ||
|  | #define NOWINMESSAGES
 | ||
|  | #define NOWINOFFSETS
 | ||
|  | #define NOWINSTYLES
 | ||
|  | #define WIN32_LEAN_AND_MEAN
 | ||
|  | 
 | ||
|  | #include <Ws2tcpip.h>
 | ||
|  | #include <winsock2.h>
 | ||
|  | #elif defined(KINC_POSIX) || defined(KINC_EMSCRIPTEN)
 | ||
|  | #include <arpa/inet.h> // for inet_addr()
 | ||
|  | #include <ctype.h>
 | ||
|  | #include <fcntl.h>
 | ||
|  | #include <netdb.h>
 | ||
|  | #include <netinet/in.h>
 | ||
|  | #include <netinet/tcp.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <sys/socket.h>
 | ||
|  | #include <unistd.h>
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #if defined(KINC_EMSCRIPTEN)
 | ||
|  | #include <emscripten.h>
 | ||
|  | #include <emscripten/posix_socket.h>
 | ||
|  | #include <emscripten/threading.h>
 | ||
|  | #include <emscripten/websocket.h>
 | ||
|  | 
 | ||
|  | static EMSCRIPTEN_WEBSOCKET_T bridgeSocket = 0; | ||
|  | #elif defined(KINC_POSIX)
 | ||
|  | #include <sys/select.h>
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | static int counter = 0; | ||
|  | 
 | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | // Important: Must be cleaned with freeaddrinfo(address) later if the result is 0 in order to prevent memory leaks
 | ||
|  | static int resolveAddress(const char *url, int port, struct addrinfo **result) { | ||
|  | 	struct addrinfo hints = {0}; | ||
|  | 	hints.ai_family = AF_INET; | ||
|  | 	hints.ai_socktype = SOCK_DGRAM; | ||
|  | 	hints.ai_protocol = IPPROTO_UDP; | ||
|  | 
 | ||
|  | 	char serv[6]; | ||
|  | 	sprintf(serv, "%u", port); | ||
|  | 
 | ||
|  | 	return getaddrinfo(url, serv, &hints, result); | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | KINC_FUNC bool kinc_socket_bind(kinc_socket_t *sock) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	struct sockaddr_in address; | ||
|  | 	address.sin_family = sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6; | ||
|  | 	address.sin_addr.s_addr = sock->host; | ||
|  | 	address.sin_port = sock->port; | ||
|  | 	if (bind(sock->handle, (const struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Could not bind socket: %s", strerror(errno)); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 	return true; | ||
|  | #else
 | ||
|  | 	return false; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | KINC_FUNC void kinc_socket_options_set_defaults(kinc_socket_options_t *options) { | ||
|  | 	options->non_blocking = true; | ||
|  | 	options->broadcast = false; | ||
|  | 	options->tcp_no_delay = false; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_socket_init(kinc_socket_t *sock) { | ||
|  | 	sock->handle = 0; | ||
|  | 
 | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	sock->host = INADDR_ANY; | ||
|  | 	sock->port = htons((unsigned short)8080); | ||
|  | 	sock->protocol = KINC_SOCKET_PROTOCOL_TCP; | ||
|  | 	sock->family = KINC_SOCKET_FAMILY_IP4; | ||
|  | #endif
 | ||
|  | 	sock->connected = false; | ||
|  | 
 | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 	if (counter == 0) { | ||
|  | 		WSADATA WsaData; | ||
|  | 		WSAStartup(MAKEWORD(2, 2), &WsaData); | ||
|  | 	} | ||
|  | #if defined(KINC_EMSCRIPTEN)
 | ||
|  | 	if (!bridgeSocket) { | ||
|  | 		bridgeSocket = emscripten_init_websocket_to_posix_socket_bridge("ws://localhost:8080"); | ||
|  | 		// Synchronously wait until connection has been established.
 | ||
|  | 		uint16_t readyState = 0; | ||
|  | 		do { | ||
|  | 			emscripten_websocket_get_ready_state(bridgeSocket, &readyState); | ||
|  | 			emscripten_thread_sleep(100); | ||
|  | 		} while (readyState == 0); | ||
|  | 	} | ||
|  | #endif
 | ||
|  | #endif
 | ||
|  | 	++counter; | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_socket_open(kinc_socket_t *sock, struct kinc_socket_options *options) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	switch (sock->protocol) { | ||
|  | 	case KINC_SOCKET_PROTOCOL_UDP: | ||
|  | 		sock->handle = socket(sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6, SOCK_DGRAM, IPPROTO_UDP); | ||
|  | 		break; | ||
|  | 	case KINC_SOCKET_PROTOCOL_TCP: | ||
|  | 		sock->handle = socket(sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6, SOCK_STREAM, IPPROTO_TCP); | ||
|  | 		break; | ||
|  | 	default: | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Unsupported socket protocol."); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (sock->handle <= 0) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Could not create socket."); | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 		int errorCode = WSAGetLastError(); | ||
|  | 		switch (errorCode) { | ||
|  | 		case (WSANOTINITIALISED): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "A successful WSAStartup call must occur before using this function."); | ||
|  | 			break; | ||
|  | 		case (WSAENETDOWN): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "The network subsystem or the associated service provider has failed."); | ||
|  | 			break; | ||
|  | 		case (WSAEAFNOSUPPORT): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, | ||
|  | 			         "The specified address family is not supported.For example, an application tried to create a socket for the AF_IRDA address " | ||
|  | 			         "family but an infrared adapter and device driver is not installed on the local computer."); | ||
|  | 			break; | ||
|  | 		case (WSAEINPROGRESS): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, | ||
|  | 			         "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function."); | ||
|  | 			break; | ||
|  | 		case (WSAEMFILE): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "No more socket descriptors are available."); | ||
|  | 			break; | ||
|  | 		case (WSAEINVAL): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, | ||
|  | 			         "An invalid argument was supplied.This error is returned if the af parameter is set to AF_UNSPEC and the type and protocol " | ||
|  | 			         "parameter are unspecified."); | ||
|  | 			break; | ||
|  | 		case (WSAENOBUFS): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "No buffer space is available.The socket cannot be created."); | ||
|  | 			break; | ||
|  | 		case (WSAEPROTONOSUPPORT): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "The specified protocol is not supported."); | ||
|  | 			break; | ||
|  | 		case (WSAEPROTOTYPE): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "The specified protocol is the wrong type for this socket."); | ||
|  | 			break; | ||
|  | 		case (WSAEPROVIDERFAILEDINIT): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, | ||
|  | 			         "The service provider failed to initialize.This error is returned if a layered service provider(LSP) or namespace provider was " | ||
|  | 			         "improperly installed or the provider fails to operate correctly."); | ||
|  | 			break; | ||
|  | 		case (WSAESOCKTNOSUPPORT): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "The specified socket type is not supported in this address family."); | ||
|  | 			break; | ||
|  | 		case (WSAEINVALIDPROVIDER): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "The service provider returned a version other than 2.2."); | ||
|  | 			break; | ||
|  | 		case (WSAEINVALIDPROCTABLE): | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "The service provider returned an invalid or incomplete procedure table to the WSPStartup."); | ||
|  | 			break; | ||
|  | 		default: | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown error."); | ||
|  | 		} | ||
|  | #elif defined(KINC_POSIX) && !defined(KINC_EMSCRIPTEN)
 | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "%s", strerror(errno)); | ||
|  | #endif
 | ||
|  | 		return false; | ||
|  | 	} | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	if (options) { | ||
|  | 		if (options->non_blocking) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 			DWORD value = 1; | ||
|  | 			if (ioctlsocket(sock->handle, FIONBIO, &value) != 0) { | ||
|  | 				kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set non-blocking mode."); | ||
|  | 				return false; | ||
|  | 			} | ||
|  | #elif defined(KINC_POSIX)
 | ||
|  | 			int value = 1; | ||
|  | 			if (fcntl(sock->handle, F_SETFL, O_NONBLOCK, value) == -1) { | ||
|  | 				kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set non-blocking mode."); | ||
|  | 				return false; | ||
|  | 			} | ||
|  | #endif
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (options->broadcast) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 			int value = 1; | ||
|  | 			if (setsockopt(sock->handle, SOL_SOCKET, SO_BROADCAST, (const char *)&value, sizeof(value)) < 0) { | ||
|  | 				kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set broadcast mode."); | ||
|  | 				return false; | ||
|  | 			} | ||
|  | #endif
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (options->tcp_no_delay) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 			int value = 1; | ||
|  | 			if (setsockopt(sock->handle, IPPROTO_TCP, TCP_NODELAY, (const char *)&value, sizeof(value)) != 0) { | ||
|  | 				kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set no-delay mode."); | ||
|  | 				return false; | ||
|  | 			} | ||
|  | #endif
 | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_socket_destroy(kinc_socket_t *sock) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 	closesocket(sock->handle); | ||
|  | #elif defined(KINC_POSIX)
 | ||
|  | 	close(sock->handle); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	memset(sock, 0, sizeof(kinc_socket_t)); | ||
|  | 
 | ||
|  | 	--counter; | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 	if (counter == 0) { | ||
|  | 		WSACleanup(); | ||
|  | 	} | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_socket_select(kinc_socket_t *sock, uint32_t waittime, bool read, bool write) { | ||
|  | #if !defined(KINC_EMSCRIPTEN) && (defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX))
 | ||
|  | 	fd_set r_fds, w_fds; | ||
|  | 	struct timeval timeout; | ||
|  | 
 | ||
|  | 	FD_ZERO(&r_fds); | ||
|  | 	FD_ZERO(&w_fds); | ||
|  | 
 | ||
|  | 	FD_SET(sock->handle, &r_fds); | ||
|  | 	FD_SET(sock->handle, &w_fds); | ||
|  | 
 | ||
|  | 	timeout.tv_sec = waittime; | ||
|  | 	timeout.tv_usec = 0; | ||
|  | 
 | ||
|  | 	if (select(0, &r_fds, &w_fds, NULL, &timeout) < 0) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "kinc_socket_select didn't work: %s", strerror(errno)); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (read && write) { | ||
|  | 		return FD_ISSET(sock->handle, &w_fds) && FD_ISSET(sock->handle, &r_fds); | ||
|  | 	} | ||
|  | 	else if (read) { | ||
|  | 		return FD_ISSET(sock->handle, &r_fds); | ||
|  | 	} | ||
|  | 	else if (write) { | ||
|  | 		return FD_ISSET(sock->handle, &w_fds); | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Calling kinc_socket_select with both read and write set to false is useless."); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | #else
 | ||
|  | 	return false; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_socket_set(kinc_socket_t *sock, const char *host, int port, kinc_socket_family_t family, kinc_socket_protocol_t protocol) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 
 | ||
|  | 	sock->family = family; | ||
|  | 	sock->protocol = protocol; | ||
|  | 	sock->port = htons((unsigned short)port); | ||
|  | 
 | ||
|  | 	if (host == NULL) | ||
|  | 		return true; | ||
|  | 
 | ||
|  | 	if (isdigit(host[0]) || (family == KINC_SOCKET_FAMILY_IP6 && host[4] == ':')) { // Is IPv4 or IPv6 string
 | ||
|  | 		struct in_addr addr; | ||
|  | 
 | ||
|  | 		if (inet_pton(sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6, host, &addr) == 0) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid %s address: %s\n", sock->family == KINC_SOCKET_FAMILY_IP4 ? "IPv4" : "IPv6", host); | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 		sock->host = addr.s_addr; | ||
|  | 		return true; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		struct addrinfo *address = NULL; | ||
|  | 		int res = resolveAddress(host, port, &address); | ||
|  | 		if (res != 0) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Could not resolve address."); | ||
|  | 			return false; | ||
|  | 		} | ||
|  | #if defined(KINC_POSIX)
 | ||
|  | 		sock->host = ((struct sockaddr_in *)address->ai_addr)->sin_addr.s_addr; | ||
|  | #else
 | ||
|  | 		sock->host = ((struct sockaddr_in *)address->ai_addr)->sin_addr.S_un.S_addr; | ||
|  | #endif
 | ||
|  | 		freeaddrinfo(address); | ||
|  | 
 | ||
|  | 		return true; | ||
|  | 	} | ||
|  | #else
 | ||
|  | 	return false; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_socket_listen(kinc_socket_t *socket, int backlog) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	int res = listen(socket->handle, backlog); | ||
|  | 	return (res == 0); | ||
|  | #else
 | ||
|  | 	return false; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_socket_accept(kinc_socket_t *sock, kinc_socket_t *newSocket, unsigned *remoteAddress, unsigned *remotePort) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 	typedef int socklen_t; | ||
|  | #endif
 | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	struct sockaddr_in addr; | ||
|  | 	socklen_t addrLength = sizeof(addr); | ||
|  | 	newSocket->handle = accept(sock->handle, (struct sockaddr *)&addr, &addrLength); | ||
|  | 	if (newSocket->handle <= 0) { | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	newSocket->connected = sock->connected = true; | ||
|  | 	newSocket->host = addr.sin_addr.s_addr; | ||
|  | 	newSocket->port = addr.sin_port; | ||
|  | 	newSocket->family = sock->family; | ||
|  | 	newSocket->protocol = sock->protocol; | ||
|  | 	*remoteAddress = ntohl(addr.sin_addr.s_addr); | ||
|  | 	*remotePort = ntohs(addr.sin_port); | ||
|  | 	return true; | ||
|  | #else
 | ||
|  | 	return false; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_socket_connect(kinc_socket_t *sock) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	struct sockaddr_in addr; | ||
|  | 	addr.sin_family = sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6; | ||
|  | 	addr.sin_addr.s_addr = sock->host; | ||
|  | 	addr.sin_port = sock->port; | ||
|  | 
 | ||
|  | 	int res = connect(sock->handle, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); | ||
|  | 	return sock->connected = (res == 0); | ||
|  | #else
 | ||
|  | 	return false; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | int kinc_socket_send(kinc_socket_t *sock, const uint8_t *data, int size) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 	typedef int socklen_t; | ||
|  | #endif
 | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	if (sock->protocol == KINC_SOCKET_PROTOCOL_UDP) { | ||
|  | 		struct sockaddr_in addr; | ||
|  | 		addr.sin_family = sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6; | ||
|  | 		addr.sin_addr.s_addr = sock->host; | ||
|  | 		addr.sin_port = sock->port; | ||
|  | 
 | ||
|  | 		size_t sent = sendto(sock->handle, (const char *)data, size, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); | ||
|  | 		if (sent != size) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); | ||
|  | 			return -1; | ||
|  | 		} | ||
|  | 		return (int)sent; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		if (!sock->connected) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Call kinc_sockect_connect/bind before send/recv can be called for TCP sockets."); | ||
|  | 			return -1; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		size_t sent = send(sock->handle, (const char *)data, size, 0); | ||
|  | 		if (sent != size) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); | ||
|  | 		} | ||
|  | 		return (int)sent; | ||
|  | 	} | ||
|  | #else
 | ||
|  | 	return 0; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | int kinc_socket_send_address(kinc_socket_t *sock, unsigned address, int port, const uint8_t *data, int size) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	struct sockaddr_in addr; | ||
|  | 	addr.sin_family = AF_INET; | ||
|  | 	addr.sin_addr.s_addr = htonl(address); | ||
|  | 	addr.sin_port = htons(port); | ||
|  | 
 | ||
|  | 	size_t sent = sendto(sock->handle, (const char *)data, size, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); | ||
|  | 	if (sent != size) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); | ||
|  | 	} | ||
|  | 	return (int)sent; | ||
|  | #else
 | ||
|  | 	return 0; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | int kinc_socket_send_url(kinc_socket_t *sock, const char *url, int port, const uint8_t *data, int size) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 	struct addrinfo *address = NULL; | ||
|  | 	int res = resolveAddress(url, port, &address); | ||
|  | 	if (res != 0) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Could not resolve address."); | ||
|  | 		return 0; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	size_t sent = sendto(sock->handle, (const char *)data, size, 0, address->ai_addr, sizeof(struct sockaddr_in)); | ||
|  | 	if (sent != size) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); | ||
|  | 	} | ||
|  | 	freeaddrinfo(address); | ||
|  | 	return (int)sent; | ||
|  | #else
 | ||
|  | 	return 0; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | int kinc_socket_receive(kinc_socket_t *sock, uint8_t *data, int maxSize, unsigned *fromAddress, unsigned *fromPort) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 	typedef int socklen_t; | ||
|  | 	typedef int ssize_t; | ||
|  | #endif
 | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP) || defined(KINC_POSIX)
 | ||
|  | 
 | ||
|  | 	if (sock->protocol == KINC_SOCKET_PROTOCOL_UDP) { | ||
|  | 		struct sockaddr_in from; | ||
|  | 		socklen_t fromLength = sizeof(from); | ||
|  | 		ssize_t bytes = recvfrom(sock->handle, (char *)data, maxSize, 0, (struct sockaddr *)&from, &fromLength); | ||
|  | 		if (bytes <= 0) { | ||
|  | 			return (int)bytes; | ||
|  | 		} | ||
|  | 		*fromAddress = ntohl(from.sin_addr.s_addr); | ||
|  | 		*fromPort = ntohs(from.sin_port); | ||
|  | 		return (int)bytes; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 
 | ||
|  | 		if (!sock->connected) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Call kinc_sockect_connect/bind before send/recv can be called for TCP sockets."); | ||
|  | 			return -1; | ||
|  | 		} | ||
|  | 		ssize_t bytes = recv(sock->handle, (char *)data, maxSize, 0); | ||
|  | 		*fromAddress = ntohl(sock->host); | ||
|  | 		*fromPort = ntohs(sock->port); | ||
|  | 		return (int)bytes; | ||
|  | 	} | ||
|  | #else
 | ||
|  | 	return 0; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | unsigned kinc_url_to_int(const char *url, int port) { | ||
|  | #if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
 | ||
|  | 	struct addrinfo *address = NULL; | ||
|  | 	int res = resolveAddress(url, port, &address); | ||
|  | 	if (res != 0) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Could not resolve address."); | ||
|  | 		return -1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	unsigned fromAddress = ntohl(((struct sockaddr_in *)address->ai_addr)->sin_addr.S_un.S_addr); | ||
|  | 	freeaddrinfo(address); | ||
|  | 
 | ||
|  | 	return fromAddress; | ||
|  | #else
 | ||
|  | 	return 0; | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifdef __cplusplus
 | ||
|  | } | ||
|  | #endif
 |