├── .gitattributes ├── examples ├── xmake.lua ├── simple-server.c ├── compression-server.c ├── simple-client.c ├── compression-client.c ├── encryption-server.c └── encryption-client.c ├── libenet6.pc.in ├── include └── enet6 │ ├── utility.h │ ├── types.h │ ├── time.h │ ├── callbacks.h │ ├── list.h │ ├── unix.h │ ├── win32.h │ └── protocol.h ├── .github └── workflows │ ├── cmake.yml │ └── xmake.yml ├── docs ├── FAQ.dox ├── license.dox ├── mainpage.dox ├── install.dox ├── design.dox └── tutorial.dox ├── LICENSE ├── src ├── callbacks.c ├── list.c ├── packet.c ├── win32.c ├── unix.c ├── host.c └── address.c ├── xmake.lua ├── CMakeLists.txt ├── DoxygenLayout.xml ├── .gitignore ├── README.md └── ChangeLog /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /examples/xmake.lua: -------------------------------------------------------------------------------- 1 | for _, file in ipairs(os.files("*.c")) do 2 | target(path.basename(file), function () 3 | set_group("examples") 4 | add_files(file) 5 | add_deps("enet6") 6 | end) 7 | end 8 | -------------------------------------------------------------------------------- /libenet6.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: @PACKAGE_NAME@ 7 | Description: Low-latency UDP networking library supporting optional reliability (with IPv6 support) 8 | Version: @PACKAGE_VERSION@ 9 | Cflags: -I${includedir} 10 | Libs: -L${libdir} -lenet6 11 | -------------------------------------------------------------------------------- /include/enet6/utility.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file utility.h 3 | @brief ENet utility header 4 | */ 5 | #ifndef __ENET_UTILITY_H__ 6 | #define __ENET_UTILITY_H__ 7 | 8 | #define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) 9 | #define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) 10 | #define ENET_DIFFERENCE(x, y) ((x) < (y) ? (y) - (x) : (x) - (y)) 11 | 12 | #endif /* __ENET_UTILITY_H__ */ 13 | 14 | -------------------------------------------------------------------------------- /include/enet6/types.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file types.h 3 | @brief type definitions for ENet 4 | */ 5 | #ifndef __ENET_TYPES_H__ 6 | #define __ENET_TYPES_H__ 7 | 8 | typedef unsigned char enet_uint8; /**< unsigned 8-bit type */ 9 | typedef unsigned short enet_uint16; /**< unsigned 16-bit type */ 10 | typedef unsigned int enet_uint32; /**< unsigned 32-bit type */ 11 | 12 | #endif /* __ENET_TYPES_H__ */ 13 | 14 | -------------------------------------------------------------------------------- /include/enet6/time.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file time.h 3 | @brief ENet time constants and macros 4 | */ 5 | #ifndef __ENET_TIME_H__ 6 | #define __ENET_TIME_H__ 7 | 8 | #define ENET_TIME_OVERFLOW 86400000 9 | 10 | #define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) 11 | #define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) 12 | #define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) 13 | #define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) 14 | 15 | #define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) 16 | 17 | #endif /* __ENET_TIME_H__ */ 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: CMake 4 | 5 | jobs: 6 | cmake-build: 7 | name: CMake ${{ matrix.os }} ${{ matrix.build_type }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: ["ubuntu-latest", "windows-latest", "macos-latest"] 13 | build_type: ["Debug", "Release"] 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Configure CMake 18 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} 19 | 20 | - name: Build 21 | run: cmake --build ${{github.workspace}}/build --config ${{ matrix.build_type }} 22 | -------------------------------------------------------------------------------- /include/enet6/callbacks.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file callbacks.h 3 | @brief ENet callbacks 4 | */ 5 | #ifndef __ENET_CALLBACKS_H__ 6 | #define __ENET_CALLBACKS_H__ 7 | 8 | #include 9 | 10 | typedef struct _ENetCallbacks 11 | { 12 | void * (ENET_CALLBACK * malloc) (size_t size); 13 | void (ENET_CALLBACK * free) (void * memory); 14 | void (ENET_CALLBACK * no_memory) (void); 15 | } ENetCallbacks; 16 | 17 | #ifdef __cplusplus 18 | extern "C" 19 | { 20 | #endif 21 | 22 | /** @defgroup callbacks ENet internal callbacks 23 | @{ 24 | @ingroup private 25 | */ 26 | 27 | extern void * enet_malloc (size_t); 28 | extern void enet_free (void *); 29 | 30 | /** @} */ 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif /* __ENET_CALLBACKS_H__ */ 37 | 38 | -------------------------------------------------------------------------------- /docs/FAQ.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @page FAQ Frequently Answered Questions 3 | 4 | @section Q1 Is ENet thread-safe? 5 | 6 | ENet does not use any significant global variables, the vast majority 7 | of state is encapsulated in the ENetHost structure. As such, as long 8 | as the application guards access to this structure, then ENet should 9 | operate fine in a multi-threaded environment. 10 | 11 | @section Q2 Isn't ENet just re-inventing TCP?! What's the point? 12 | 13 | In a perfect world, that would be true. But as many have found, using 14 | TCP either in lieu of or in conjunction with UDP can lead to all kinds 15 | of nightmares. TCP is a good, solid protocol, however it simply isn't 16 | up to the task of real-time games. Too much of TCP's implementation 17 | dictates a policy that isn't practical for games. If you want to use 18 | TCP, then do so -- this library is for people that either don't want 19 | to use TCP or have tried and ended up being discouraged with the 20 | performance. 21 | 22 | */ 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2002-2024 Lee Salzman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /docs/license.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @page License License 3 | 4 | Copyright (c) 2002-2024 Lee Salzman 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | -------------------------------------------------------------------------------- /src/callbacks.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file callbacks.c 3 | @brief ENet callback functions 4 | */ 5 | #define ENET_BUILDING_LIB 1 6 | #include "enet6/enet.h" 7 | 8 | static ENetCallbacks callbacks = { malloc, free, abort }; 9 | 10 | int 11 | enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits) 12 | { 13 | if (version < ENET_VERSION_CREATE (1, 3, 0)) 14 | return -1; 15 | 16 | if (inits -> malloc != NULL || inits -> free != NULL) 17 | { 18 | if (inits -> malloc == NULL || inits -> free == NULL) 19 | return -1; 20 | 21 | callbacks.malloc = inits -> malloc; 22 | callbacks.free = inits -> free; 23 | } 24 | 25 | if (inits -> no_memory != NULL) 26 | callbacks.no_memory = inits -> no_memory; 27 | 28 | return enet_initialize (); 29 | } 30 | 31 | ENetVersion 32 | enet_linked_version (void) 33 | { 34 | return ENET_VERSION; 35 | } 36 | 37 | void * 38 | enet_malloc (size_t size) 39 | { 40 | void * memory = callbacks.malloc (size); 41 | 42 | if (memory == NULL) 43 | callbacks.no_memory (); 44 | 45 | return memory; 46 | } 47 | 48 | void 49 | enet_free (void * memory) 50 | { 51 | callbacks.free (memory); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /include/enet6/list.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file list.h 3 | @brief ENet list management 4 | */ 5 | #ifndef __ENET_LIST_H__ 6 | #define __ENET_LIST_H__ 7 | 8 | #include 9 | 10 | typedef struct _ENetListNode 11 | { 12 | struct _ENetListNode * next; 13 | struct _ENetListNode * previous; 14 | } ENetListNode; 15 | 16 | typedef ENetListNode * ENetListIterator; 17 | 18 | typedef struct _ENetList 19 | { 20 | ENetListNode sentinel; 21 | } ENetList; 22 | 23 | #ifdef __cplusplus 24 | extern "C" 25 | { 26 | #endif 27 | 28 | extern void enet_list_clear (ENetList *); 29 | 30 | extern ENetListIterator enet_list_insert (ENetListIterator, void *); 31 | extern void * enet_list_remove (ENetListIterator); 32 | extern ENetListIterator enet_list_move (ENetListIterator, void *, void *); 33 | 34 | extern size_t enet_list_size (ENetList *); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #define enet_list_begin(list) ((list) -> sentinel.next) 41 | #define enet_list_end(list) (& (list) -> sentinel) 42 | 43 | #define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list)) 44 | 45 | #define enet_list_next(iterator) ((iterator) -> next) 46 | #define enet_list_previous(iterator) ((iterator) -> previous) 47 | 48 | #define enet_list_front(list) ((void *) (list) -> sentinel.next) 49 | #define enet_list_back(list) ((void *) (list) -> sentinel.previous) 50 | 51 | #endif /* __ENET_LIST_H__ */ 52 | 53 | -------------------------------------------------------------------------------- /include/enet6/unix.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file unix.h 3 | @brief ENet Unix header 4 | */ 5 | #ifndef __ENET_UNIX_H__ 6 | #define __ENET_UNIX_H__ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef MSG_MAXIOVLEN 17 | #define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN 18 | #endif 19 | 20 | typedef int ENetSocket; 21 | 22 | #define ENET_SOCKET_NULL -1 23 | 24 | #define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */ 25 | #define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */ 26 | 27 | #define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */ 28 | #define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */ 29 | 30 | typedef struct 31 | { 32 | void * data; 33 | size_t dataLength; 34 | } ENetBuffer; 35 | 36 | #define ENET_CALLBACK 37 | 38 | #define ENET_API extern 39 | 40 | typedef fd_set ENetSocketSet; 41 | 42 | #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) 43 | #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) 44 | #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) 45 | #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) 46 | 47 | #endif /* __ENET_UNIX_H__ */ 48 | 49 | -------------------------------------------------------------------------------- /include/enet6/win32.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file win32.h 3 | @brief ENet Win32 header 4 | */ 5 | #ifndef __ENET_WIN32_H__ 6 | #define __ENET_WIN32_H__ 7 | 8 | #ifdef _MSC_VER 9 | #ifdef ENET_BUILDING_LIB 10 | #pragma warning (disable: 4267) /* size_t to int conversion */ 11 | #pragma warning (disable: 4244) /* 64bit to 32bit int */ 12 | #pragma warning (disable: 4018) /* signed/unsigned mismatch */ 13 | #pragma warning (disable: 4146) /* unary minus operator applied to unsigned type */ 14 | #ifndef _CRT_SECURE_NO_DEPRECATE 15 | #define _CRT_SECURE_NO_DEPRECATE 16 | #endif 17 | #ifndef _CRT_SECURE_NO_WARNINGS 18 | #define _CRT_SECURE_NO_WARNINGS 19 | #endif 20 | #endif 21 | #endif 22 | 23 | #include 24 | #include 25 | 26 | typedef SOCKET ENetSocket; 27 | 28 | #define ENET_SOCKET_NULL INVALID_SOCKET 29 | 30 | #define ENET_HOST_TO_NET_16(value) (htons (value)) 31 | #define ENET_HOST_TO_NET_32(value) (htonl (value)) 32 | 33 | #define ENET_NET_TO_HOST_16(value) (ntohs (value)) 34 | #define ENET_NET_TO_HOST_32(value) (ntohl (value)) 35 | 36 | typedef struct 37 | { 38 | size_t dataLength; 39 | void * data; 40 | } ENetBuffer; 41 | 42 | #define ENET_CALLBACK __cdecl 43 | 44 | #ifdef ENET_DLL 45 | #ifdef ENET_BUILDING_LIB 46 | #define ENET_API __declspec( dllexport ) 47 | #else 48 | #define ENET_API __declspec( dllimport ) 49 | #endif /* ENET_BUILDING_LIB */ 50 | #else /* !ENET_DLL */ 51 | #define ENET_API extern 52 | #endif /* ENET_DLL */ 53 | 54 | typedef fd_set ENetSocketSet; 55 | 56 | #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) 57 | #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) 58 | #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) 59 | #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) 60 | 61 | #endif /* __ENET_WIN32_H__ */ 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file list.c 3 | @brief ENet linked list functions 4 | */ 5 | #define ENET_BUILDING_LIB 1 6 | #include "enet6/enet.h" 7 | 8 | /** 9 | @defgroup list ENet linked list utility functions 10 | @ingroup private 11 | @{ 12 | */ 13 | void 14 | enet_list_clear (ENetList * list) 15 | { 16 | list -> sentinel.next = & list -> sentinel; 17 | list -> sentinel.previous = & list -> sentinel; 18 | } 19 | 20 | ENetListIterator 21 | enet_list_insert (ENetListIterator position, void * data) 22 | { 23 | ENetListIterator result = (ENetListIterator) data; 24 | 25 | result -> previous = position -> previous; 26 | result -> next = position; 27 | 28 | result -> previous -> next = result; 29 | position -> previous = result; 30 | 31 | return result; 32 | } 33 | 34 | void * 35 | enet_list_remove (ENetListIterator position) 36 | { 37 | position -> previous -> next = position -> next; 38 | position -> next -> previous = position -> previous; 39 | 40 | return position; 41 | } 42 | 43 | ENetListIterator 44 | enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast) 45 | { 46 | ENetListIterator first = (ENetListIterator) dataFirst, 47 | last = (ENetListIterator) dataLast; 48 | 49 | first -> previous -> next = last -> next; 50 | last -> next -> previous = first -> previous; 51 | 52 | first -> previous = position -> previous; 53 | last -> next = position; 54 | 55 | first -> previous -> next = first; 56 | position -> previous = last; 57 | 58 | return first; 59 | } 60 | 61 | size_t 62 | enet_list_size (ENetList * list) 63 | { 64 | size_t size = 0; 65 | ENetListIterator position; 66 | 67 | for (position = enet_list_begin (list); 68 | position != enet_list_end (list); 69 | position = enet_list_next (position)) 70 | ++ size; 71 | 72 | return size; 73 | } 74 | 75 | /** @} */ 76 | -------------------------------------------------------------------------------- /docs/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** @mainpage ENet 2 | 3 | ENet's purpose is to provide a relatively thin, simple and robust 4 | network communication layer on top of UDP (User Datagram Protocol). 5 | The primary feature it provides is optional reliable, in-order 6 | delivery of packets. 7 | 8 | ENet omits certain higher level networking features such as authentication, 9 | lobbying, server discovery, encryption, or other similar tasks that are 10 | particularly application specific so that the library remains flexible, 11 | portable, and easily embeddable. 12 | 13 | @ref Features 14 | 15 | @ref Downloads 16 | 17 | @ref Installation 18 | 19 | @ref Tutorial 20 | 21 | @ref MailingList 22 | 23 | @ref IRCChannel 24 | 25 | @ref FAQ 26 | 27 | @ref License 28 | 29 | Documentation 30 | 31 | */ 32 | 33 | /** 34 | @page Downloads Downloads 35 | 36 | You can retrieve the source to ENet by downloading it in either .tar.gz form 37 | or accessing the github distribution directly. 38 | 39 | The most recent stable release (1.3.18) can be downloaded here. 40 | The last release that is protocol compatible with the 1.2 series or earlier (1.2.5) can be downloaded here. 41 | 42 | You can find the most recent ENet source at the github repository. 43 | 44 | */ 45 | 46 | /** 47 | @page MailingList Mailing List 48 | 49 | The enet-discuss list is for discussion of ENet, including bug reports or feature requests. 50 | 51 | */ 52 | 53 | /** 54 | @page IRCChannel IRC Channel 55 | 56 | Join the \#enet channel on the Libera Chat IRC network (irc.libera.chat) for real-time discussion about the ENet library. 57 | 58 | */ 59 | 60 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.debug", "mode.release") 2 | 3 | set_project("enet-ipv6") 4 | set_version("6.1.2") 5 | 6 | if not is_plat("windows", "mingw") then 7 | -- detect features on Unix platforms 8 | option("fcntl", { cincludes = {"fcntl.h", "unistd.h"}, cfuncs = "fcntl", defines = "HAS_FCNTL=1"}) 9 | option("poll", { cincludes = {"poll.h"}, cfuncs = "poll", defines = "HAS_POLL=1"}) 10 | option("getaddrinfo", { cincludes = {"sys/types.h", "sys/socket.h", "netdb.h"}, cfuncs = "getaddrinfo", defines = "HAS_GETADDRINFO=1"}) 11 | option("getnameinfo", { cincludes = {"sys/types.h", "sys/socket.h", "netdb.h"}, cfuncs = "getnameinfo", defines = "HAS_GETNAMEINFO=1"}) 12 | option("gethostbyaddr_r", { cincludes = {"netdb.h"}, cfuncs = "gethostbyaddr_r", defines = "HAS_GETHOSTBYADDR_R=1"}) 13 | option("gethostbyname_r", { cincludes = {"netdb.h"}, cfuncs = "gethostbyname_r", defines = "HAS_GETHOSTBYNAME_R=1"}) 14 | option("inet_pton", { cincludes = {"arpa/inet.h"}, cfuncs = "inet_pton", defines = "HAS_INET_PTON=1"}) 15 | option("inet_ntop", { cincludes = {"arpa/inet.h"}, cfuncs = "inet_ntop", defines = "HAS_INET_NTOP=1"}) 16 | option("socklen_t", { cincludes = {"sys/types.h", "sys/socket.h"}, ctypes = "socklen_t", defines = "HAS_SOCKLEN_T=1"}) 17 | 18 | option("msghdr_flags", function () 19 | add_csnippets("msghdr_flags", [[ 20 | #include 21 | int get_flags(struct msghdr* m) 22 | { 23 | return m->msg_flags; 24 | }]]) 25 | end) 26 | end 27 | 28 | option("examples", { default = true }) 29 | 30 | add_includedirs("include") 31 | 32 | target("enet6", function () 33 | set_kind("$(kind)") 34 | set_group("lib") 35 | 36 | add_headerfiles("include/(enet6/*.h)") 37 | add_files("src/*.c") 38 | 39 | if is_kind("shared") then 40 | add_defines("ENET_DLL", { public = true }) 41 | end 42 | 43 | if is_plat("windows", "mingw") then 44 | add_syslinks("winmm", "ws2_32", { public = true }) 45 | else 46 | add_options( 47 | "fcntl", 48 | "poll", 49 | "getaddrinfo", 50 | "getnameinfo", 51 | "gethostbyaddr_r", 52 | "gethostbyname_r", 53 | "inet_pton", 54 | "inet_ntop", 55 | "msghdr_flags", 56 | "socklen_t") 57 | end 58 | end) 59 | 60 | if has_config("examples") then 61 | includes("examples/xmake.lua") 62 | end 63 | -------------------------------------------------------------------------------- /docs/install.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @page Installation Installation 3 | 4 | ENet should be trivially simple to integrate with most applications. 5 | First, make sure you download the latest source distribution at @ref Downloads. 6 | 7 | @section Unix Unix-like Operating Systems 8 | 9 | If you are using an ENet release, then you should simply be able to build it 10 | by doing the following: 11 | 12 | ./configure && make && make install 13 | 14 | If you obtained the package from github, you must have automake and autoconf 15 | available to generate the build system first by doing the following command 16 | before using the above mentioned build procedure: 17 | 18 | autoreconf -vfi 19 | 20 | 21 | @subsection SolarisBSD Solaris and BSD 22 | 23 | When building ENet under Solaris, you must specify the -lsocket and 24 | -lnsl parameters to your compiler to ensure that the sockets library 25 | is linked in. 26 | 27 | @section Windows Microsoft Windows 28 | 29 | You may simply use the included "enet.lib" or "enet64.lib" static libraries. 30 | However, if you wish to build the library yourself, then the following 31 | instructions apply: 32 | 33 | There is an included MSVC 6 project (enet.dsp) which you may use to 34 | build a suitable library file. Alternatively, you may simply drag all 35 | the ENet source files into your main project. 36 | 37 | You will have to link to the Winsock2 libraries, so make sure to add 38 | ws2_32.lib and winmm.lib to your library list (Project Settings | Link | 39 | Object/library modules). 40 | 41 | @subsection enet.dsp Building with the included enet.dsp 42 | 43 | Load the included enet.dsp. MSVC may ask you to convert it if you 44 | are on a newer version of MSVC - just allow the conversion and save 45 | the resulting project as "enet" or similar. After you build this 46 | project, it will output an "enet.lib" file to either the "Debug/" 47 | or "Release/" directory, depending on which configuration you have 48 | selected to build. By default, it should produce "Debug/enet.lib". 49 | 50 | You may then copy the resulting "enet.lib" file and the header files 51 | found in the "include/" directory to your other projects and add it to 52 | their library lists. Make sure to also link against "ws2_32.lib" and 53 | "winmm.lib" as described above. 54 | 55 | @subsection DLL DLL 56 | 57 | If you wish to build ENet as a DLL you must first define ENET_DLL 58 | within the project (Project Settings | C/C++ | Preprocessor | 59 | Preprocessor definitions) or, more invasively, simply define ENET_DLL 60 | at the top of enet.h. 61 | 62 | */ 63 | 64 | -------------------------------------------------------------------------------- /examples/simple-server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) 5 | { 6 | ENetAddress address; 7 | ENetHost* serverHost; 8 | ENetEvent event; 9 | int eventStatus; 10 | char addressBuffer[ENET_ADDRESS_MAX_LENGTH]; 11 | 12 | /* Initialize enet6 */ 13 | if (enet_initialize() != 0) 14 | { 15 | fprintf(stderr, "An error occured while initializing ENet6.\n"); 16 | return EXIT_FAILURE; 17 | } 18 | 19 | atexit(enet_deinitialize); 20 | 21 | /* Build the listen address (any + port) */ 22 | enet_address_build_any(&address, ENET_ADDRESS_TYPE_IPV6); 23 | address.port = 1234; 24 | 25 | /* Create a host using enet_host_create, address type has to match the address, */ 26 | /* except for the combination IPv6 + Any which enables dual stack (IPv6 socket allowing IPv4 connection) */ 27 | serverHost = enet_host_create(ENET_ADDRESS_TYPE_ANY, &address, 32, 2, 0, 0); 28 | if (serverHost == NULL) 29 | { 30 | fprintf(stderr, "An error occured while trying to create an ENet6 server host\n"); 31 | exit(EXIT_FAILURE); 32 | } 33 | 34 | /* Connect and user service */ 35 | eventStatus = 1; 36 | 37 | while (1) 38 | { 39 | eventStatus = enet_host_service(serverHost, &event, 100); 40 | 41 | /* If we had some event that interested us */ 42 | if (eventStatus > 0) 43 | { 44 | switch (event.type) 45 | { 46 | case ENET_EVENT_TYPE_CONNECT: 47 | { 48 | enet_address_get_host_ip(&event.peer->address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 49 | printf("(Server) We got a new connection from %s\n", addressBuffer); 50 | break; 51 | } 52 | 53 | case ENET_EVENT_TYPE_RECEIVE: 54 | printf("(Server) Message from client : %s\n", event.packet->data); 55 | /* Re-send the message to all clients */ 56 | enet_host_broadcast(serverHost, 0, event.packet); 57 | break; 58 | 59 | case ENET_EVENT_TYPE_DISCONNECT: 60 | case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: 61 | { 62 | enet_address_get_host_ip(&event.peer->address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 63 | printf("Client %s disconnected%s.\n", addressBuffer, (event.type == ENET_EVENT_TYPE_DISCONNECT_TIMEOUT) ? " (timeout)" : ""); 64 | break; 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/compression-server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) 5 | { 6 | ENetAddress address; 7 | ENetHost* serverHost; 8 | ENetEvent event; 9 | int eventStatus; 10 | char addressBuffer[ENET_ADDRESS_MAX_LENGTH]; 11 | 12 | /* Initialize enet6 */ 13 | if (enet_initialize() != 0) 14 | { 15 | fprintf(stderr, "An error occured while initializing ENet6.\n"); 16 | return EXIT_FAILURE; 17 | } 18 | 19 | atexit(enet_deinitialize); 20 | 21 | /* Build the listen address (any + port) */ 22 | enet_address_build_any(&address, ENET_ADDRESS_TYPE_IPV6); 23 | address.port = 1234; 24 | 25 | /* Create a host using enet_host_create, address type has to match the address, */ 26 | /* except for the combination IPv6 + Any which enables dual stack (IPv6 socket allowing IPv4 connection) */ 27 | serverHost = enet_host_create(ENET_ADDRESS_TYPE_ANY, &address, 32, 2, 0, 0); 28 | if (serverHost == NULL) 29 | { 30 | fprintf(stderr, "An error occured while trying to create an ENet6 server host\n"); 31 | exit(EXIT_FAILURE); 32 | } 33 | 34 | /* Enable ENet builtin compressor */ 35 | enet_host_compress_with_range_coder(serverHost); 36 | 37 | /* Also enable builtin CRC32 checksum */ 38 | enet_host_set_checksum_callback(serverHost, enet_crc32); 39 | 40 | /* Connect and user service */ 41 | eventStatus = 1; 42 | 43 | while (1) 44 | { 45 | eventStatus = enet_host_service(serverHost, &event, 100); 46 | 47 | /* If we had some event that interested us */ 48 | if (eventStatus > 0) 49 | { 50 | switch (event.type) 51 | { 52 | case ENET_EVENT_TYPE_CONNECT: 53 | { 54 | enet_address_get_host_ip(&event.peer->address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 55 | printf("(Server) We got a new connection from %s\n", addressBuffer); 56 | break; 57 | } 58 | 59 | case ENET_EVENT_TYPE_RECEIVE: 60 | printf("(Server) Message from client : %s\n", event.packet->data); 61 | /* Re-send the message to all clients */ 62 | enet_host_broadcast(serverHost, 0, event.packet); 63 | break; 64 | 65 | case ENET_EVENT_TYPE_DISCONNECT: 66 | case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: 67 | { 68 | enet_address_get_host_ip(&event.peer->address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 69 | printf("Client %s disconnected%s.\n", addressBuffer, (event.type == ENET_EVENT_TYPE_DISCONNECT_TIMEOUT) ? " (timeout)" : ""); 70 | break; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12...3.20) 2 | 3 | project(enet) 4 | 5 | # The "configure" step. 6 | include(CheckFunctionExists) 7 | include(CheckStructHasMember) 8 | include(CheckTypeSize) 9 | check_function_exists("fcntl" HAS_FCNTL) 10 | check_function_exists("poll" HAS_POLL) 11 | check_function_exists("getaddrinfo" HAS_GETADDRINFO) 12 | check_function_exists("getnameinfo" HAS_GETNAMEINFO) 13 | check_function_exists("gethostbyname_r" HAS_GETHOSTBYNAME_R) 14 | check_function_exists("gethostbyaddr_r" HAS_GETHOSTBYADDR_R) 15 | check_function_exists("inet_pton" HAS_INET_PTON) 16 | check_function_exists("inet_ntop" HAS_INET_NTOP) 17 | check_struct_has_member("struct msghdr" "msg_flags" "sys/types.h;sys/socket.h" HAS_MSGHDR_FLAGS) 18 | set(CMAKE_EXTRA_INCLUDE_FILES "sys/types.h" "sys/socket.h") 19 | check_type_size("socklen_t" HAS_SOCKLEN_T BUILTIN_TYPES_ONLY) 20 | unset(CMAKE_EXTRA_INCLUDE_FILES) 21 | if(MSVC) 22 | add_definitions(-W3) 23 | else() 24 | add_definitions(-Wno-error) 25 | endif() 26 | 27 | if(HAS_FCNTL) 28 | add_definitions(-DHAS_FCNTL=1) 29 | endif() 30 | if(HAS_POLL) 31 | add_definitions(-DHAS_POLL=1) 32 | endif() 33 | if(HAS_GETNAMEINFO) 34 | add_definitions(-DHAS_GETNAMEINFO=1) 35 | endif() 36 | if(HAS_GETADDRINFO) 37 | add_definitions(-DHAS_GETADDRINFO=1) 38 | endif() 39 | if(HAS_GETHOSTBYNAME_R) 40 | add_definitions(-DHAS_GETHOSTBYNAME_R=1) 41 | endif() 42 | if(HAS_GETHOSTBYADDR_R) 43 | add_definitions(-DHAS_GETHOSTBYADDR_R=1) 44 | endif() 45 | if(HAS_INET_PTON) 46 | add_definitions(-DHAS_INET_PTON=1) 47 | endif() 48 | if(HAS_INET_NTOP) 49 | add_definitions(-DHAS_INET_NTOP=1) 50 | endif() 51 | if(HAS_MSGHDR_FLAGS) 52 | add_definitions(-DHAS_MSGHDR_FLAGS=1) 53 | endif() 54 | if(HAS_SOCKLEN_T) 55 | add_definitions(-DHAS_SOCKLEN_T=1) 56 | endif() 57 | 58 | include_directories(${PROJECT_SOURCE_DIR}/include) 59 | 60 | set(INCLUDE_FILES_PREFIX include/enet6) 61 | set(INCLUDE_FILES 62 | ${INCLUDE_FILES_PREFIX}/callbacks.h 63 | ${INCLUDE_FILES_PREFIX}/enet.h 64 | ${INCLUDE_FILES_PREFIX}/list.h 65 | ${INCLUDE_FILES_PREFIX}/protocol.h 66 | ${INCLUDE_FILES_PREFIX}/time.h 67 | ${INCLUDE_FILES_PREFIX}/types.h 68 | ${INCLUDE_FILES_PREFIX}/unix.h 69 | ${INCLUDE_FILES_PREFIX}/utility.h 70 | ${INCLUDE_FILES_PREFIX}/win32.h 71 | ) 72 | 73 | set(SOURCE_FILES 74 | src/address.c 75 | src/callbacks.c 76 | src/compress.c 77 | src/host.c 78 | src/list.c 79 | src/packet.c 80 | src/peer.c 81 | src/protocol.c 82 | src/unix.c 83 | src/win32.c) 84 | 85 | source_group(include FILES ${INCLUDE_FILES}) 86 | source_group(source FILES ${SOURCE_FILES}) 87 | 88 | if(WIN32 AND BUILD_SHARED_LIBS AND (MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")) 89 | add_definitions(-DENET_DLL=1) 90 | add_definitions(-DENET_BUILDING_LIB) 91 | endif() 92 | 93 | add_library(enet 94 | ${INCLUDE_FILES} 95 | ${SOURCE_FILES} 96 | ) 97 | 98 | if (WIN32) 99 | target_link_libraries(enet winmm ws2_32) 100 | endif() 101 | 102 | include(GNUInstallDirs) 103 | install(TARGETS enet 104 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 105 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 106 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 107 | ) 108 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/enet 109 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 110 | ) 111 | -------------------------------------------------------------------------------- /examples/simple-client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) 6 | { 7 | ENetAddress address; 8 | ENetHost* clientHost; 9 | ENetPeer* serverPeer; 10 | ENetEvent event; 11 | int eventStatus; 12 | char message[1024]; 13 | char addressBuffer[ENET_ADDRESS_MAX_LENGTH]; 14 | int running = 1; 15 | 16 | /* Initialize enet6 */ 17 | if (enet_initialize() != 0) 18 | { 19 | fprintf(stderr, "An error occured while initializing ENet6.\n"); 20 | return EXIT_FAILURE; 21 | } 22 | 23 | atexit(enet_deinitialize); 24 | 25 | /* Build an IPv6 or IPv4 address, depending on what the domain resolves to */ 26 | enet_address_set_host(&address, ENET_ADDRESS_TYPE_ANY, "localhost"); 27 | address.port = 1234; 28 | 29 | enet_address_get_host_ip(&address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 30 | printf("Connecting to %s...\n", addressBuffer); 31 | 32 | /* Create a non-listening host using enet_host_create */ 33 | /* Note we create a host using the same address family we resolved earlier */ 34 | clientHost = enet_host_create(address.type, NULL, 1, 2, 0, 0); 35 | if (clientHost == NULL) 36 | { 37 | fprintf(stderr, "An error occured while trying to create an ENet6 server host\n"); 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | /* Connect and user service */ 42 | serverPeer = enet_host_connect(clientHost, &address, 2, 0); 43 | if (serverPeer == NULL) 44 | { 45 | fprintf(stderr, "No available peers for initializing an ENet connection"); 46 | exit(EXIT_FAILURE); 47 | } 48 | 49 | while (running) 50 | { 51 | eventStatus = enet_host_service(clientHost, &event, 100); 52 | 53 | /* Inspect events */ 54 | if (eventStatus > 0) 55 | { 56 | switch(event.type) 57 | { 58 | case ENET_EVENT_TYPE_CONNECT: 59 | printf("(Client) Connected to server %s\n", addressBuffer); 60 | break; 61 | 62 | case ENET_EVENT_TYPE_RECEIVE: 63 | printf("(Client) Message from server: %s\n", event.packet->data); 64 | enet_packet_destroy(event.packet); 65 | break; 66 | 67 | case ENET_EVENT_TYPE_DISCONNECT: 68 | printf("(Client) Disconnected from server.\n"); 69 | return EXIT_SUCCESS; 70 | 71 | case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: 72 | printf("(Client) Disconnected from server (timed out).\n"); 73 | return EXIT_FAILURE; 74 | } 75 | } 76 | else if (serverPeer->state == ENET_PEER_STATE_CONNECTED) 77 | { 78 | /* Prompt some message to send to the server, be quick to prevent timeout (TODO: Read asynchronously) */ 79 | printf("> "); 80 | fgets(message, sizeof(message), stdin); 81 | 82 | if (strlen(message) > 0 && strcmp(message, "\n") != 0) 83 | { 84 | /* Build a packet passing our bytes, length and flags (reliable means this packet will be resent if lost) */ 85 | ENetPacket* packet = enet_packet_create(message, strlen(message) + 1, ENET_PACKET_FLAG_RELIABLE); 86 | /* Send the packet to the server on channel 0 */ 87 | enet_peer_send(serverPeer, 0, packet); 88 | } 89 | else 90 | { 91 | running = 0; 92 | enet_peer_disconnect_now(serverPeer, 0); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/compression-client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) 6 | { 7 | ENetAddress address; 8 | ENetHost* clientHost; 9 | ENetPeer* serverPeer; 10 | ENetEvent event; 11 | int eventStatus; 12 | char message[1024]; 13 | char addressBuffer[ENET_ADDRESS_MAX_LENGTH]; 14 | int running = 1; 15 | 16 | /* Initialize enet6 */ 17 | if (enet_initialize() != 0) 18 | { 19 | fprintf(stderr, "An error occured while initializing ENet6.\n"); 20 | return EXIT_FAILURE; 21 | } 22 | 23 | atexit(enet_deinitialize); 24 | 25 | /* Build an IPv6 or IPv4 address, depending on what the domain resolves to */ 26 | enet_address_set_host(&address, ENET_ADDRESS_TYPE_ANY, "localhost"); 27 | address.port = 1234; 28 | 29 | enet_address_get_host_ip(&address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 30 | printf("Connecting to %s...\n", addressBuffer); 31 | 32 | /* Create a non-listening host using enet_host_create */ 33 | /* Note we create a host using the same address family we resolved earlier */ 34 | clientHost = enet_host_create(address.type, NULL, 1, 2, 0, 0); 35 | if (clientHost == NULL) 36 | { 37 | fprintf(stderr, "An error occured while trying to create an ENet6 server host\n"); 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | /* Enable ENet builtin compressor */ 42 | enet_host_compress_with_range_coder(clientHost); 43 | 44 | /* Also enable builtin CRC32 checksum */ 45 | enet_host_set_checksum_callback(clientHost, enet_crc32); 46 | 47 | /* Connect and user service */ 48 | serverPeer = enet_host_connect(clientHost, &address, 2, 0); 49 | if (serverPeer == NULL) 50 | { 51 | fprintf(stderr, "No available peers for initializing an ENet connection"); 52 | exit(EXIT_FAILURE); 53 | } 54 | 55 | while (running) 56 | { 57 | eventStatus = enet_host_service(clientHost, &event, 100); 58 | 59 | /* Inspect events */ 60 | if (eventStatus > 0) 61 | { 62 | switch(event.type) 63 | { 64 | case ENET_EVENT_TYPE_CONNECT: 65 | printf("(Client) Connected to server %s\n", addressBuffer); 66 | break; 67 | 68 | case ENET_EVENT_TYPE_RECEIVE: 69 | printf("(Client) Message from server: %s\n", event.packet->data); 70 | enet_packet_destroy(event.packet); 71 | break; 72 | 73 | case ENET_EVENT_TYPE_DISCONNECT: 74 | printf("(Client) Disconnected from server.\n"); 75 | return EXIT_SUCCESS; 76 | } 77 | } 78 | else if (serverPeer->state == ENET_PEER_STATE_CONNECTED) 79 | { 80 | /* Prompt some message to send to the server, be quick to prevent timeout (TODO: Read asynchronously) */ 81 | printf("> "); 82 | fgets(message, sizeof(message), stdin); 83 | 84 | if (strlen(message) > 0 && strcmp(message, "\n") != 0) 85 | { 86 | /* Build a packet passing our bytes, length and flags (reliable means this packet will be resent if lost) */ 87 | ENetPacket* packet = enet_packet_create(message, strlen(message) + 1, ENET_PACKET_FLAG_RELIABLE); 88 | /* Send the packet to the server on channel 0 */ 89 | enet_peer_send(serverPeer, 0, packet); 90 | } 91 | else 92 | { 93 | running = 0; 94 | enet_peer_disconnect_now(serverPeer, 0); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /.github/workflows/xmake.yml: -------------------------------------------------------------------------------- 1 | name: XMake build 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | pull_request: 8 | push: 9 | paths-ignore: 10 | - '.gitignore' 11 | - 'LICENSE' 12 | - 'README.md' 13 | 14 | jobs: 15 | xmake-build: 16 | name: ${{ matrix.config.plat }} ${{ matrix.config.arch }} ${{ matrix.mode }} ${{ matrix.kind }} 17 | runs-on: ${{ matrix.config.runner }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | config: 22 | - { runner: windows-latest, plat: windows, arch: x86 } 23 | - { runner: windows-latest, plat: windows, arch: x64 } 24 | - { runner: windows-latest, plat: windows, arch: arm64 } 25 | - { runner: windows-latest, plat: mingw, arch: i386, msys_arch: i686, msys_msystem: mingw32 } 26 | - { runner: windows-latest, plat: mingw, arch: x86_64, msys_arch: x86_64, msys_msystem: mingw64 } 27 | - { runner: ubuntu-latest, plat: linux, arch: x86_64 } 28 | - { runner: ubuntu-latest, plat: android, arch: x86 } 29 | - { runner: ubuntu-latest, plat: android, arch: x86_64 } 30 | - { runner: ubuntu-latest, plat: android, arch: armeabi-v7a } 31 | - { runner: ubuntu-latest, plat: android, arch: arm64-v8a } 32 | - { runner: macos-14, plat: macosx, arch: x86_64 } 33 | - { runner: macos-14, plat: macosx, arch: arm64 } 34 | - { runner: macos-14, plat: iphoneos, arch: x86_64 } 35 | - { runner: macos-14, plat: iphoneos, arch: arm64 } 36 | mode: [debug, release] 37 | kind: [shared, static] 38 | defaults: 39 | run: 40 | shell: ${{ matrix.config.plat == 'mingw' && 'msys2 {0}' || 'bash' }} 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - name: "Set OUTPUT_FILE variable" 46 | run: echo "OUTPUT_FILE=enet6-${{ matrix.config.plat }}_${{ matrix.config.arch }}_${{ matrix.mode }}_${{ matrix.kind }}${{ (matrix.config.plat == 'windows' || matrix.config.plat == 'mingw') && '.zip' || '.tar.gz' }}" >> $GITHUB_ENV 47 | shell: bash 48 | 49 | - name: Setup xmake (!MinGW) 50 | # This action also works with MinGW but is slower (compiles from source) 51 | if: ${{ matrix.config.plat != 'mingw' }} 52 | uses: xmake-io/github-action-setup-xmake@v1 53 | 54 | - name: Setup MSys2 (MinGW) 55 | if: ${{ matrix.config.plat == 'mingw' }} 56 | uses: msys2/setup-msys2@v2 57 | with: 58 | msystem: ${{ matrix.config.msys_msystem }} 59 | install: base-devel git unzip p7zip mingw-w64-${{ matrix.config.msys_arch }}-toolchain mingw-w64-${{ matrix.config.msys_arch }}-xmake 60 | update: true 61 | 62 | - name: Download NDK (Android) 63 | if: ${{ matrix.config.plat == 'android' }} 64 | run: | 65 | wget -q https://dl.google.com/android/repository/android-ndk-r25c-linux.zip 66 | unzip -q -o ./android-ndk-r25c-linux.zip 67 | 68 | - name: Configure (!Android) 69 | if: ${{ matrix.config.plat != 'android' && matrix.config.plat != 'mingw' }} 70 | run: xmake config --plat=${{ matrix.config.plat }} --arch=${{ matrix.config.arch }} --mode=${{ matrix.mode }} --kind=${{ matrix.kind }} --yes 71 | 72 | - name: Configure (Android) 73 | if: ${{ matrix.config.plat == 'android' }} 74 | run: xmake config --ndk=`pwd`/android-ndk-r25c --ndk_sdkver=28 --plat=${{ matrix.config.plat }} --arch=${{ matrix.config.arch }} --mode=${{ matrix.mode }} --kind=${{ matrix.kind }} --yes 75 | 76 | - name: Build 77 | run: xmake 78 | 79 | - name: Install 80 | run: xmake install -v -o enet6/ 81 | 82 | # For some reason macOS-14 doesn't seem to have Python 83 | - uses: actions/setup-python@v5 84 | if: ${{ matrix.config.runner == 'macos-14' }} 85 | with: 86 | python-version: "3.11" 87 | 88 | - name: Archive result 89 | uses: ihiroky/archive-action@v1 90 | with: 91 | root_dir: enet6/ 92 | file_path: ${{ env.OUTPUT_FILE }} 93 | verbose: true 94 | 95 | # Release tags (for tags) 96 | - name: Upload binaries to release (Tag) 97 | uses: svenstaro/upload-release-action@v2 98 | if: ${{ startsWith(github.event.ref, 'refs/tags/') && matrix.mode == 'release' }} 99 | with: 100 | repo_token: ${{ secrets.GITHUB_TOKEN }} 101 | file: ${{ env.OUTPUT_FILE }} 102 | asset_name: ${{ env.OUTPUT_FILE }} 103 | tag: ${{ github.ref }} 104 | -------------------------------------------------------------------------------- /examples/encryption-server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct 7 | { 8 | enet_uint32 key; 9 | } encryption_data; 10 | 11 | /* Very simple byte rotation algorithm, of course for encryption you should consider a serious algorithm like xchacha20 */ 12 | size_t simple_encrypt(void* context, ENetPeer* peer, const ENetBuffer* inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8* outData, size_t outLimit) 13 | { 14 | size_t i, j; 15 | encryption_data* peerData; 16 | 17 | /* get per-peer key */ 18 | peerData = (encryption_data*) peer->data; 19 | 20 | /* if encryption has not yet been enabled, stop here */ 21 | if (!peerData) 22 | return 0; 23 | 24 | for (i = 0; i < inBufferCount; ++i) 25 | { 26 | const enet_uint8* src = (const enet_uint8*) inBuffers[i].data; 27 | for (j = 0; j < inBuffers[i].dataLength; ++j) 28 | *outData++ = (enet_uint8) (src[j] + peerData->key); 29 | } 30 | 31 | return inLimit; 32 | } 33 | 34 | size_t simple_decrypt(void* context, ENetPeer* peer, const enet_uint8* inData, size_t inLimit, enet_uint8* outData, size_t outLimit) 35 | { 36 | size_t i; 37 | encryption_data* peerData; 38 | 39 | /* a NULL peer can happen on connection requests */ 40 | if (!peer) 41 | return 0; 42 | 43 | peerData = (encryption_data*) peer->data; 44 | if (!peerData) 45 | return 0; 46 | 47 | for (i = 0; i < inLimit; ++i) 48 | *outData++ = (enet_uint8) (inData[i] - peerData->key); 49 | 50 | return inLimit; 51 | } 52 | 53 | static ENetEncryptor encryptor = { 54 | &encryptor, /* can't be null but irrelevant here */ 55 | simple_encrypt, 56 | simple_decrypt, 57 | NULL 58 | }; 59 | 60 | int main(int argc, char **argv) 61 | { 62 | ENetAddress address; 63 | ENetHost* serverHost; 64 | ENetEvent event; 65 | int eventStatus; 66 | char addressBuffer[ENET_ADDRESS_MAX_LENGTH]; 67 | 68 | /* Initialize enet6 */ 69 | if (enet_initialize() != 0) 70 | { 71 | fprintf(stderr, "An error occured while initializing ENet6.\n"); 72 | return EXIT_FAILURE; 73 | } 74 | 75 | atexit(enet_deinitialize); 76 | 77 | /* Build the listen address (any + port) */ 78 | enet_address_build_any(&address, ENET_ADDRESS_TYPE_IPV6); 79 | address.port = 1234; 80 | 81 | /* Create a host using enet_host_create, address type has to match the address, */ 82 | /* except for the combination IPv6 + Any which enables dual stack (IPv6 socket allowing IPv4 connection) */ 83 | serverHost = enet_host_create(ENET_ADDRESS_TYPE_ANY, &address, 32, 2, 0, 0); 84 | if (serverHost == NULL) 85 | { 86 | fprintf(stderr, "An error occured while trying to create an ENet6 server host\n"); 87 | exit(EXIT_FAILURE); 88 | } 89 | 90 | /* Enable ENet builtin compressor */ 91 | enet_host_compress_with_range_coder(serverHost); 92 | 93 | /* Also enable builtin CRC32 checksum */ 94 | enet_host_set_checksum_callback(serverHost, enet_crc32); 95 | 96 | /* Enables encryption */ 97 | enet_host_encrypt(serverHost, &encryptor); 98 | 99 | /* Connect and user service */ 100 | eventStatus = 1; 101 | 102 | while (1) 103 | { 104 | eventStatus = enet_host_service(serverHost, &event, 100); 105 | 106 | /* If we had some event that interested us */ 107 | if (eventStatus > 0) 108 | { 109 | switch (event.type) 110 | { 111 | case ENET_EVENT_TYPE_CONNECT: 112 | { 113 | encryption_data* peerData; 114 | 115 | enet_address_get_host_ip(&event.peer->address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 116 | printf("(Server) We got a new connection from %s using %u as encryption key\n", addressBuffer, event.data); 117 | 118 | peerData = (encryption_data*) malloc(sizeof(encryption_data)); 119 | peerData->key = event.data; 120 | 121 | event.peer->data = peerData; 122 | break; 123 | } 124 | 125 | case ENET_EVENT_TYPE_RECEIVE: 126 | printf("(Server) Message from client : %s\n", event.packet->data); 127 | /* Re-send the message to all clients */ 128 | enet_host_broadcast(serverHost, 0, event.packet); 129 | break; 130 | 131 | case ENET_EVENT_TYPE_DISCONNECT: 132 | case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: 133 | { 134 | enet_address_get_host_ip(&event.peer->address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 135 | printf("Client %s disconnected%s.\n", addressBuffer, (event.type == ENET_EVENT_TYPE_DISCONNECT_TIMEOUT) ? " (timeout)" : ""); 136 | free(event.peer->data); 137 | break; 138 | } 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /examples/encryption-client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct 8 | { 9 | enet_uint32 key; 10 | int enabled; 11 | } encryption_context; 12 | 13 | /* Very simple byte rotation algorithm, of course for encryption you should consider a serious algorithm like xchacha20 */ 14 | size_t simple_encrypt(void* context, ENetPeer* peer, const ENetBuffer* inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8* outData, size_t outLimit) 15 | { 16 | size_t i, j; 17 | encryption_context* encryptionContext = (encryption_context*) context; 18 | 19 | /* Wait until server sends us an encrypted message before enabling encryption */ 20 | if (!encryptionContext->enabled) 21 | return 0; 22 | 23 | for (i = 0; i < inBufferCount; ++i) 24 | { 25 | const enet_uint8* src = (const enet_uint8*)inBuffers[i].data; 26 | for (j = 0; j < inBuffers[i].dataLength; ++j) 27 | *outData++ = (enet_uint8) (src[j] + encryptionContext->key); 28 | } 29 | 30 | return inLimit; 31 | } 32 | 33 | size_t simple_decrypt(void* context, ENetPeer* peer, const enet_uint8* inData, size_t inLimit, enet_uint8* outData, size_t outLimit) 34 | { 35 | size_t i, j; 36 | encryption_context* encryptionContext = (encryption_context*)context; 37 | encryptionContext->enabled = 1; 38 | 39 | for (i = 0; i < inLimit; ++i) 40 | *outData++ = (enet_uint8)(inData[i] - encryptionContext->key); 41 | 42 | return inLimit; 43 | } 44 | 45 | int main(int argc, char **argv) 46 | { 47 | ENetAddress address; 48 | ENetHost* clientHost; 49 | ENetPeer* serverPeer; 50 | ENetEvent event; 51 | int eventStatus; 52 | char message[1024]; 53 | char addressBuffer[ENET_ADDRESS_MAX_LENGTH]; 54 | int running = 1; 55 | encryption_context encryptionContext = { 0, 0 }; 56 | ENetEncryptor encryptor = { 57 | &encryptionContext, 58 | simple_encrypt, 59 | simple_decrypt, 60 | NULL 61 | }; 62 | 63 | srand(time(NULL)); 64 | encryptionContext.key = rand(); 65 | 66 | /* Initialize enet6 */ 67 | if (enet_initialize() != 0) 68 | { 69 | fprintf(stderr, "An error occured while initializing ENet6.\n"); 70 | return EXIT_FAILURE; 71 | } 72 | 73 | atexit(enet_deinitialize); 74 | 75 | /* Build an IPv6 or IPv4 address, depending on what the domain resolves to */ 76 | enet_address_set_host(&address, ENET_ADDRESS_TYPE_ANY, "localhost"); 77 | address.port = 1234; 78 | 79 | enet_address_get_host_ip(&address, addressBuffer, ENET_ADDRESS_MAX_LENGTH); 80 | printf("Connecting to %s...\n", addressBuffer); 81 | 82 | /* Create a non-listening host using enet_host_create */ 83 | /* Note we create a host using the same address family we resolved earlier */ 84 | clientHost = enet_host_create(address.type, NULL, 1, 2, 0, 0); 85 | if (clientHost == NULL) 86 | { 87 | fprintf(stderr, "An error occured while trying to create an ENet6 server host\n"); 88 | exit(EXIT_FAILURE); 89 | } 90 | 91 | /* Enable ENet builtin compressor */ 92 | enet_host_compress_with_range_coder(clientHost); 93 | 94 | /* Also enable builtin CRC32 checksum */ 95 | enet_host_set_checksum_callback(clientHost, enet_crc32); 96 | 97 | /* Enables encryption after connection succeeded */ 98 | enet_host_encrypt(clientHost, &encryptor); 99 | 100 | /* Connect and send our key */ 101 | serverPeer = enet_host_connect(clientHost, &address, 2, encryptionContext.key); 102 | if (serverPeer == NULL) 103 | { 104 | fprintf(stderr, "No available peers for initializing an ENet connection"); 105 | exit(EXIT_FAILURE); 106 | } 107 | 108 | while (running) 109 | { 110 | eventStatus = enet_host_service(clientHost, &event, 100); 111 | 112 | /* Inspect events */ 113 | if (eventStatus > 0) 114 | { 115 | switch(event.type) 116 | { 117 | case ENET_EVENT_TYPE_CONNECT: 118 | printf("(Client) Connected to server %s\n", addressBuffer); 119 | break; 120 | 121 | case ENET_EVENT_TYPE_RECEIVE: 122 | printf("(Client) Message from server: %s\n", event.packet->data); 123 | enet_packet_destroy(event.packet); 124 | break; 125 | 126 | case ENET_EVENT_TYPE_DISCONNECT: 127 | printf("(Client) Disconnected from server.\n"); 128 | return EXIT_SUCCESS; 129 | } 130 | } 131 | else if (serverPeer->state == ENET_PEER_STATE_CONNECTED) 132 | { 133 | /* Prompt some message to send to the server, be quick to prevent timeout (TODO: Read asynchronously) */ 134 | printf("> "); 135 | fgets(message, sizeof(message), stdin); 136 | 137 | if (strlen(message) > 0 && strcmp(message, "\n") != 0) 138 | { 139 | /* Build a packet passing our bytes, length and flags (reliable means this packet will be resent if lost) */ 140 | ENetPacket* packet = enet_packet_create(message, strlen(message) + 1, ENET_PACKET_FLAG_RELIABLE); 141 | /* Send the packet to the server on channel 0 */ 142 | enet_peer_send(serverPeer, 0, packet); 143 | } 144 | else 145 | { 146 | running = 0; 147 | enet_peer_disconnect_now(serverPeer, 0); 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /docs/design.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @page Features Features and Architecture 3 | 4 | ENet evolved specifically as a UDP networking layer for the 5 | multiplayer first person shooter Cube. Cube necessitated low latency 6 | communication with data sent out very frequently, so TCP was an 7 | unsuitable choice due to its high latency and stream orientation. UDP, 8 | however, lacks many sometimes necessary features from TCP such as 9 | reliability, sequencing, unrestricted packet sizes, and connection 10 | management. So UDP by itself was not suitable as a network protocol 11 | either. No suitable freely available networking libraries existed at 12 | the time of ENet's creation to fill this niche. 13 | 14 | UDP and TCP could have been used together in Cube to benefit somewhat 15 | from both of their features, however, the resulting combinations of 16 | protocols still leaves much to be desired. TCP lacks multiple streams 17 | of communication without resorting to opening many sockets and 18 | complicates delineation of packets due to its buffering behavior. UDP 19 | lacks sequencing, connection management, management of bandwidth 20 | resources, and imposes limitations on the size of packets. A 21 | significant investment is required to integrate these two protocols, 22 | and the end result is worse off in features and performance than the 23 | uniform protocol presented by ENet. 24 | 25 | ENet thus attempts to address these issues and provide a single, 26 | uniform protocol layered over UDP to the developer with the best 27 | features of UDP and TCP as well as some useful features neither 28 | provide, with a much cleaner integration than any resulting from a 29 | mixture of UDP and TCP. 30 | 31 | @section CM Connection Management 32 | 33 | ENet provides a simple connection interface over which to communicate 34 | with a foreign host. The liveness of the connection is actively 35 | monitored by pinging the foreign host at frequent intervals, and also 36 | monitors the network conditions from the local host to the foreign 37 | host such as the mean round trip time and packet loss in this fashion. 38 | 39 | @section Sequencing Sequencing 40 | 41 | Rather than a single byte stream that complicates the delineation of 42 | packets, ENet presents connections as multiple, properly sequenced 43 | packet streams that simplify the transfer of various types of data. 44 | 45 | ENet provides sequencing for all packets by assigning to each sent 46 | packet a sequence number that is incremented as packets are sent. ENet 47 | guarantees that no packet with a higher sequence number will be 48 | delivered before a packet with a lower sequence number, thus ensuring 49 | packets are delivered exactly in the order they are sent. 50 | 51 | For unreliable packets, ENet will simply discard the lower sequence 52 | number packet if a packet with a higher sequence number has already 53 | been delivered. This allows the packets to be dispatched immediately 54 | as they arrive, and reduce latency of unreliable packets to an 55 | absolute minimum. For reliable packets, if a higher sequence number 56 | packet arrives, but the preceding packets in the sequence have not yet 57 | arrived, ENet will stall delivery of the higher sequence number 58 | packets until its predecessors have arrived. 59 | 60 | @section Channels Channels 61 | 62 | Since ENet will stall delivery of reliable packets to ensure proper 63 | sequencing, and consequently any packets of higher sequence number 64 | whether reliable or unreliable, in the event the reliable packet's 65 | predecessors have not yet arrived, this can introduce latency into the 66 | delivery of other packets which may not need to be as strictly ordered 67 | with respect to the packet that stalled their delivery. 68 | 69 | To combat this latency and reduce the ordering restrictions on 70 | packets, ENet provides multiple channels of communication over a given 71 | connection. Each channel is independently sequenced, and so the 72 | delivery status of a packet in one channel will not stall the delivery 73 | of other packets in another channel. 74 | 75 | @section Reliability Reliability 76 | 77 | ENet provides optional reliability of packet delivery by ensuring the 78 | foreign host acknowledges receipt of all reliable packets. ENet will 79 | attempt to resend the packet up to a reasonable amount of times, if no 80 | acknowledgement of the packet's receipt happens within a specified 81 | timeout. Retry timeouts are progressive and become more lenient with 82 | every failed attempt to allow for temporary turbulence in network 83 | conditions. 84 | 85 | @section FaR Fragmentation and Reassembly 86 | 87 | ENet will send and deliver packets regardless of size. Large packets 88 | are fragmented into many smaller packets of suitable size, and 89 | reassembled on the foreign host to recover the original packet for 90 | delivery. The process is entirely transparent to the developer. 91 | 92 | @section Aggregation Aggregation 93 | 94 | ENet aggregates all protocol commands, including acknowledgements and 95 | packet transfer, into larger protocol packets to ensure the proper 96 | utilization of the connection and to limit the opportunities for 97 | packet loss that might otherwise result in further delivery latency. 98 | 99 | @section Adaptability Adaptability 100 | 101 | ENet provides an in-flight data window for reliable packets to ensure 102 | connections are not overwhelmed by volumes of packets. It also 103 | provides a static bandwidth allocation mechanism to ensure the total 104 | volume of packets sent and received to a host don't exceed the host's 105 | capabilities. Further, ENet also provides a dynamic throttle that 106 | responds to deviations from normal network connections to rectify 107 | various types of network congestion by further limiting the volume of 108 | packets sent. 109 | 110 | @section Portability Portability 111 | 112 | ENet works on Windows and any other Unix or Unix-like platform 113 | providing a BSD sockets interface. The library has a small and stable 114 | code base that can easily be extended to support other platforms and 115 | integrates easily. ENet makes no assumptions about the underlying 116 | platform's endianess or word size. 117 | 118 | @section Freedom Freedom 119 | 120 | ENet demands no royalties and doesn't carry a viral license that would 121 | restrict you in how you might use it in your programs. ENet is 122 | licensed under a short-and-sweet MIT-style license, which gives you 123 | the freedom to do anything you want with it (well, almost anything). 124 | 125 | */ 126 | 127 | -------------------------------------------------------------------------------- /include/enet6/protocol.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file protocol.h 3 | @brief ENet protocol 4 | */ 5 | #ifndef __ENET_PROTOCOL_H__ 6 | #define __ENET_PROTOCOL_H__ 7 | 8 | #include "enet6/types.h" 9 | 10 | enum 11 | { 12 | ENET_PROTOCOL_MINIMUM_MTU = 576, 13 | ENET_PROTOCOL_MAXIMUM_MTU = 4096, 14 | ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, 15 | ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, 16 | ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536, 17 | ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, 18 | ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, 19 | ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, 20 | ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 21 | }; 22 | 23 | typedef enum _ENetProtocolCommand 24 | { 25 | ENET_PROTOCOL_COMMAND_NONE = 0, 26 | ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, 27 | ENET_PROTOCOL_COMMAND_CONNECT = 2, 28 | ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, 29 | ENET_PROTOCOL_COMMAND_DISCONNECT = 4, 30 | ENET_PROTOCOL_COMMAND_PING = 5, 31 | ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, 32 | ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, 33 | ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, 34 | ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, 35 | ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, 36 | ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, 37 | ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, 38 | ENET_PROTOCOL_COMMAND_COUNT = 13, 39 | 40 | ENET_PROTOCOL_COMMAND_MASK = 0x0F 41 | } ENetProtocolCommand; 42 | 43 | typedef enum _ENetProtocolFlag 44 | { 45 | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), 46 | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), 47 | 48 | ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), 49 | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), 50 | ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, 51 | 52 | ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), 53 | ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 54 | } ENetProtocolFlag; 55 | 56 | typedef enum _ENetProtocolExtendedFlag 57 | { 58 | /* bits [0-3] are reserved for future peer id extension */ 59 | 60 | ENET_PROTOCOL_HEADER_EXTENDED_FLAG_ENCRYPTED = (1 << 4), 61 | ENET_PROTOCOL_HEADER_EXTENDED_FLAG_MASK = ENET_PROTOCOL_HEADER_EXTENDED_FLAG_ENCRYPTED, 62 | } ENetProtocolExtendedFlag; 63 | 64 | #ifdef _MSC_VER 65 | #pragma pack(push, 1) 66 | #define ENET_PACKED 67 | #elif defined(__GNUC__) || defined(__clang__) 68 | #define ENET_PACKED __attribute__ ((packed)) 69 | #else 70 | #define ENET_PACKED 71 | #endif 72 | 73 | typedef struct _ENetProtocolHeader 74 | { 75 | enet_uint16 peerID; 76 | enet_uint16 sentTime; 77 | } ENET_PACKED ENetProtocolHeader; 78 | 79 | typedef struct _ENetProtocolCommandHeader 80 | { 81 | enet_uint8 command; 82 | enet_uint8 channelID; 83 | enet_uint16 reliableSequenceNumber; 84 | } ENET_PACKED ENetProtocolCommandHeader; 85 | 86 | typedef struct _ENetProtocolAcknowledge 87 | { 88 | ENetProtocolCommandHeader header; 89 | enet_uint16 receivedReliableSequenceNumber; 90 | enet_uint16 receivedSentTime; 91 | } ENET_PACKED ENetProtocolAcknowledge; 92 | 93 | typedef struct _ENetProtocolConnect 94 | { 95 | ENetProtocolCommandHeader header; 96 | enet_uint16 outgoingPeerID; 97 | enet_uint8 incomingSessionID; 98 | enet_uint8 outgoingSessionID; 99 | enet_uint32 mtu; 100 | enet_uint32 windowSize; 101 | enet_uint32 channelCount; 102 | enet_uint32 incomingBandwidth; 103 | enet_uint32 outgoingBandwidth; 104 | enet_uint32 packetThrottleInterval; 105 | enet_uint32 packetThrottleAcceleration; 106 | enet_uint32 packetThrottleDeceleration; 107 | enet_uint32 connectID; 108 | enet_uint32 data; 109 | } ENET_PACKED ENetProtocolConnect; 110 | 111 | typedef struct _ENetProtocolVerifyConnect 112 | { 113 | ENetProtocolCommandHeader header; 114 | enet_uint16 outgoingPeerID; 115 | enet_uint8 incomingSessionID; 116 | enet_uint8 outgoingSessionID; 117 | enet_uint32 mtu; 118 | enet_uint32 windowSize; 119 | enet_uint32 channelCount; 120 | enet_uint32 incomingBandwidth; 121 | enet_uint32 outgoingBandwidth; 122 | enet_uint32 packetThrottleInterval; 123 | enet_uint32 packetThrottleAcceleration; 124 | enet_uint32 packetThrottleDeceleration; 125 | enet_uint32 connectID; 126 | } ENET_PACKED ENetProtocolVerifyConnect; 127 | 128 | typedef struct _ENetProtocolBandwidthLimit 129 | { 130 | ENetProtocolCommandHeader header; 131 | enet_uint32 incomingBandwidth; 132 | enet_uint32 outgoingBandwidth; 133 | } ENET_PACKED ENetProtocolBandwidthLimit; 134 | 135 | typedef struct _ENetProtocolThrottleConfigure 136 | { 137 | ENetProtocolCommandHeader header; 138 | enet_uint32 packetThrottleInterval; 139 | enet_uint32 packetThrottleAcceleration; 140 | enet_uint32 packetThrottleDeceleration; 141 | } ENET_PACKED ENetProtocolThrottleConfigure; 142 | 143 | typedef struct _ENetProtocolDisconnect 144 | { 145 | ENetProtocolCommandHeader header; 146 | enet_uint32 data; 147 | } ENET_PACKED ENetProtocolDisconnect; 148 | 149 | typedef struct _ENetProtocolPing 150 | { 151 | ENetProtocolCommandHeader header; 152 | } ENET_PACKED ENetProtocolPing; 153 | 154 | typedef struct _ENetProtocolSendReliable 155 | { 156 | ENetProtocolCommandHeader header; 157 | enet_uint16 dataLength; 158 | } ENET_PACKED ENetProtocolSendReliable; 159 | 160 | typedef struct _ENetProtocolSendUnreliable 161 | { 162 | ENetProtocolCommandHeader header; 163 | enet_uint16 unreliableSequenceNumber; 164 | enet_uint16 dataLength; 165 | } ENET_PACKED ENetProtocolSendUnreliable; 166 | 167 | typedef struct _ENetProtocolSendUnsequenced 168 | { 169 | ENetProtocolCommandHeader header; 170 | enet_uint16 unsequencedGroup; 171 | enet_uint16 dataLength; 172 | } ENET_PACKED ENetProtocolSendUnsequenced; 173 | 174 | typedef struct _ENetProtocolSendFragment 175 | { 176 | ENetProtocolCommandHeader header; 177 | enet_uint16 startSequenceNumber; 178 | enet_uint16 dataLength; 179 | enet_uint32 fragmentCount; 180 | enet_uint32 fragmentNumber; 181 | enet_uint32 totalLength; 182 | enet_uint32 fragmentOffset; 183 | } ENET_PACKED ENetProtocolSendFragment; 184 | 185 | typedef union _ENetProtocol 186 | { 187 | ENetProtocolCommandHeader header; 188 | ENetProtocolAcknowledge acknowledge; 189 | ENetProtocolConnect connect; 190 | ENetProtocolVerifyConnect verifyConnect; 191 | ENetProtocolDisconnect disconnect; 192 | ENetProtocolPing ping; 193 | ENetProtocolSendReliable sendReliable; 194 | ENetProtocolSendUnreliable sendUnreliable; 195 | ENetProtocolSendUnsequenced sendUnsequenced; 196 | ENetProtocolSendFragment sendFragment; 197 | ENetProtocolBandwidthLimit bandwidthLimit; 198 | ENetProtocolThrottleConfigure throttleConfigure; 199 | } ENET_PACKED ENetProtocol; 200 | 201 | #ifdef _MSC_VER 202 | #pragma pack(pop) 203 | #endif 204 | 205 | #endif /* __ENET_PROTOCOL_H__ */ 206 | 207 | -------------------------------------------------------------------------------- /DoxygenLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /src/packet.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file packet.c 3 | @brief ENet packet management functions 4 | */ 5 | #include 6 | #define ENET_BUILDING_LIB 1 7 | #include "enet6/enet.h" 8 | 9 | /** @defgroup Packet ENet packet functions 10 | @{ 11 | */ 12 | 13 | /** Creates a packet that may be sent to a peer. 14 | @param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL. 15 | @param dataLength size of the data allocated for this packet 16 | @param flags flags for this packet as described for the ENetPacket structure. 17 | @returns the packet on success, NULL on failure 18 | */ 19 | ENetPacket * 20 | enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags) 21 | { 22 | ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket)); 23 | if (packet == NULL) 24 | return NULL; 25 | 26 | if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) 27 | packet -> data = (enet_uint8 *) data; 28 | else 29 | if (dataLength <= 0) 30 | packet -> data = NULL; 31 | else 32 | { 33 | packet -> data = (enet_uint8 *) enet_malloc (dataLength); 34 | if (packet -> data == NULL) 35 | { 36 | enet_free (packet); 37 | return NULL; 38 | } 39 | 40 | if (data != NULL) 41 | memcpy (packet -> data, data, dataLength); 42 | } 43 | 44 | packet -> referenceCount = 0; 45 | packet -> flags = flags; 46 | packet -> dataLength = dataLength; 47 | packet -> freeCallback = NULL; 48 | packet -> userData = NULL; 49 | packet -> acknowledgeCallback = NULL; 50 | packet -> remainingFragments = 0; 51 | 52 | return packet; 53 | } 54 | 55 | /** Destroys the packet and deallocates its data. 56 | @param packet packet to be destroyed 57 | */ 58 | void 59 | enet_packet_destroy (ENetPacket * packet) 60 | { 61 | if (packet == NULL) 62 | return; 63 | 64 | if (packet -> freeCallback != NULL) 65 | (* packet -> freeCallback) (packet); 66 | if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) && 67 | packet -> data != NULL) 68 | enet_free (packet -> data); 69 | enet_free (packet); 70 | } 71 | 72 | /** Attempts to resize the data in the packet to length specified in the 73 | dataLength parameter 74 | @param packet packet to resize 75 | @param dataLength new size for the packet data 76 | @returns 0 on success, < 0 on failure 77 | */ 78 | int 79 | enet_packet_resize (ENetPacket * packet, size_t dataLength) 80 | { 81 | enet_uint8 * newData; 82 | 83 | if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE)) 84 | { 85 | packet -> dataLength = dataLength; 86 | 87 | return 0; 88 | } 89 | 90 | newData = (enet_uint8 *) enet_malloc (dataLength); 91 | if (newData == NULL) 92 | return -1; 93 | 94 | if (packet -> data != NULL) 95 | { 96 | if (packet -> dataLength > 0) 97 | memcpy (newData, packet -> data, packet -> dataLength); 98 | 99 | enet_free (packet -> data); 100 | } 101 | 102 | packet -> data = newData; 103 | packet -> dataLength = dataLength; 104 | 105 | return 0; 106 | } 107 | 108 | static const enet_uint32 crcTable [256] = 109 | { 110 | 0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 111 | 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 112 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 113 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 114 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 115 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 116 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 117 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 118 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 119 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 120 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 121 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 122 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 123 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 124 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 125 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 126 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 127 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 128 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 129 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 130 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 131 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 132 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 133 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 134 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x5005713, 135 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21, 136 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 137 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 138 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 139 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 140 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 141 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 142 | }; 143 | 144 | enet_uint32 145 | enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) 146 | { 147 | enet_uint32 crc = 0xFFFFFFFF; 148 | 149 | while (bufferCount -- > 0) 150 | { 151 | const enet_uint8 * data = (const enet_uint8 *) buffers -> data, 152 | * dataEnd = & data [buffers -> dataLength]; 153 | 154 | while (data < dataEnd) 155 | { 156 | crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; 157 | } 158 | 159 | ++ buffers; 160 | } 161 | 162 | return ENET_HOST_TO_NET_32 (~ crc); 163 | } 164 | 165 | void* enet_packet_get_data(const ENetPacket* packet) { 166 | return (void*)packet->data; 167 | } 168 | 169 | void* enet_packet_get_user_data(const ENetPacket* packet) { 170 | return packet->userData; 171 | } 172 | 173 | void enet_packet_set_user_data(ENetPacket* packet, void* userData) { 174 | packet->userData = userData; 175 | } 176 | 177 | int enet_packet_get_length(const ENetPacket* packet) { 178 | return packet->dataLength; 179 | } 180 | 181 | void enet_packet_set_acknowledge_callback(ENetPacket* packet, ENetPacketAcknowledgedCallback callback) { 182 | packet->acknowledgeCallback = callback; 183 | } 184 | 185 | void enet_packet_set_free_callback(ENetPacket* packet, ENetPacketFreeCallback callback) { 186 | packet->freeCallback = callback; 187 | } 188 | 189 | int enet_packet_check_references(const ENetPacket* packet) { 190 | return (int)packet->referenceCount; 191 | } 192 | 193 | void enet_packet_dispose(ENetPacket* packet) { 194 | if (packet->referenceCount == 0) 195 | enet_packet_destroy(packet); 196 | } 197 | 198 | /** @} */ 199 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # XMake 2 | .xmake 3 | bin/* 4 | build/ 5 | vs*/ 6 | 7 | # Prerequisites 8 | *.d 9 | 10 | # Compiled Object files 11 | *.slo 12 | *.lo 13 | *.o 14 | *.obj 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Compiled Dynamic libraries 21 | *.so 22 | *.dylib 23 | *.dll 24 | 25 | # Fortran module files 26 | *.mod 27 | *.smod 28 | 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | *.lib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | 40 | 41 | # User-specific files 42 | *.rsuser 43 | *.suo 44 | *.user 45 | *.userosscache 46 | *.sln.docstates 47 | 48 | # User-specific files (MonoDevelop/Xamarin Studio) 49 | *.userprefs 50 | 51 | # Mono auto generated files 52 | mono_crash.* 53 | 54 | # Build results 55 | [Dd]ebug/ 56 | [Dd]ebugPublic/ 57 | [Rr]elease/ 58 | [Rr]eleases/ 59 | x64/ 60 | x86/ 61 | [Ww][Ii][Nn]32/ 62 | [Aa][Rr][Mm]/ 63 | [Aa][Rr][Mm]64/ 64 | bld/ 65 | [Bb]in/ 66 | [Oo]bj/ 67 | [Ll]og/ 68 | [Ll]ogs/ 69 | 70 | # Visual Studio 2015/2017 cache/options directory 71 | .vs/ 72 | # Uncomment if you have tasks that create the project's static files in wwwroot 73 | #wwwroot/ 74 | 75 | # Visual Studio 2017 auto generated files 76 | Generated\ Files/ 77 | 78 | # MSTest test Results 79 | [Tt]est[Rr]esult*/ 80 | [Bb]uild[Ll]og.* 81 | 82 | # NUnit 83 | *.VisualState.xml 84 | TestResult.xml 85 | nunit-*.xml 86 | 87 | # Build Results of an ATL Project 88 | [Dd]ebugPS/ 89 | [Rr]eleasePS/ 90 | dlldata.c 91 | 92 | # Benchmark Results 93 | BenchmarkDotNet.Artifacts/ 94 | 95 | # .NET Core 96 | project.lock.json 97 | project.fragment.lock.json 98 | artifacts/ 99 | 100 | # ASP.NET Scaffolding 101 | ScaffoldingReadMe.txt 102 | 103 | # StyleCop 104 | StyleCopReport.xml 105 | 106 | # Files built by Visual Studio 107 | *_i.c 108 | *_p.c 109 | *_h.h 110 | *.ilk 111 | *.meta 112 | *.obj 113 | *.iobj 114 | *.pch 115 | *.pdb 116 | *.ipdb 117 | *.pgc 118 | *.pgd 119 | *.rsp 120 | *.sbr 121 | *.tlb 122 | *.tli 123 | *.tlh 124 | *.tmp 125 | *.tmp_proj 126 | *_wpftmp.csproj 127 | *.log 128 | *.tlog 129 | *.vspscc 130 | *.vssscc 131 | .builds 132 | *.pidb 133 | *.svclog 134 | *.scc 135 | 136 | # Chutzpah Test files 137 | _Chutzpah* 138 | 139 | # Visual C++ cache files 140 | ipch/ 141 | *.aps 142 | *.ncb 143 | *.opendb 144 | *.opensdf 145 | *.sdf 146 | *.cachefile 147 | *.VC.db 148 | *.VC.VC.opendb 149 | 150 | # Visual Studio profiler 151 | *.psess 152 | *.vsp 153 | *.vspx 154 | *.sap 155 | 156 | # Visual Studio Trace Files 157 | *.e2e 158 | 159 | # TFS 2012 Local Workspace 160 | $tf/ 161 | 162 | # Guidance Automation Toolkit 163 | *.gpState 164 | 165 | # ReSharper is a .NET coding add-in 166 | _ReSharper*/ 167 | *.[Rr]e[Ss]harper 168 | *.DotSettings.user 169 | 170 | # TeamCity is a build add-in 171 | _TeamCity* 172 | 173 | # DotCover is a Code Coverage Tool 174 | *.dotCover 175 | 176 | # AxoCover is a Code Coverage Tool 177 | .axoCover/* 178 | !.axoCover/settings.json 179 | 180 | # Coverlet is a free, cross platform Code Coverage Tool 181 | coverage*.json 182 | coverage*.xml 183 | coverage*.info 184 | 185 | # Visual Studio code coverage results 186 | *.coverage 187 | *.coveragexml 188 | 189 | # NCrunch 190 | _NCrunch_* 191 | .*crunch*.local.xml 192 | nCrunchTemp_* 193 | 194 | # MightyMoose 195 | *.mm.* 196 | AutoTest.Net/ 197 | 198 | # Web workbench (sass) 199 | .sass-cache/ 200 | 201 | # Installshield output folder 202 | [Ee]xpress/ 203 | 204 | # DocProject is a documentation generator add-in 205 | DocProject/buildhelp/ 206 | DocProject/Help/*.HxT 207 | DocProject/Help/*.HxC 208 | DocProject/Help/*.hhc 209 | DocProject/Help/*.hhk 210 | DocProject/Help/*.hhp 211 | DocProject/Help/Html2 212 | DocProject/Help/html 213 | 214 | # Click-Once directory 215 | publish/ 216 | 217 | # Publish Web Output 218 | *.[Pp]ublish.xml 219 | *.azurePubxml 220 | # Note: Comment the next line if you want to checkin your web deploy settings, 221 | # but database connection strings (with potential passwords) will be unencrypted 222 | *.pubxml 223 | *.publishproj 224 | 225 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 226 | # checkin your Azure Web App publish settings, but sensitive information contained 227 | # in these scripts will be unencrypted 228 | PublishScripts/ 229 | 230 | # NuGet Packages 231 | *.nupkg 232 | # NuGet Symbol Packages 233 | *.snupkg 234 | # The packages folder can be ignored because of Package Restore 235 | **/[Pp]ackages/* 236 | # except build/, which is used as an MSBuild target. 237 | !**/[Pp]ackages/build/ 238 | # Uncomment if necessary however generally it will be regenerated when needed 239 | #!**/[Pp]ackages/repositories.config 240 | # NuGet v3's project.json files produces more ignorable files 241 | *.nuget.props 242 | *.nuget.targets 243 | 244 | # Microsoft Azure Build Output 245 | csx/ 246 | *.build.csdef 247 | 248 | # Microsoft Azure Emulator 249 | ecf/ 250 | rcf/ 251 | 252 | # Windows Store app package directories and files 253 | AppPackages/ 254 | BundleArtifacts/ 255 | Package.StoreAssociation.xml 256 | _pkginfo.txt 257 | *.appx 258 | *.appxbundle 259 | *.appxupload 260 | 261 | # Visual Studio cache files 262 | # files ending in .cache can be ignored 263 | *.[Cc]ache 264 | # but keep track of directories ending in .cache 265 | !?*.[Cc]ache/ 266 | 267 | # Others 268 | ClientBin/ 269 | ~$* 270 | *~ 271 | *.dbmdl 272 | *.dbproj.schemaview 273 | *.jfm 274 | *.pfx 275 | *.publishsettings 276 | orleans.codegen.cs 277 | 278 | # Including strong name files can present a security risk 279 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 280 | #*.snk 281 | 282 | # Since there are multiple workflows, uncomment next line to ignore bower_components 283 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 284 | #bower_components/ 285 | 286 | # RIA/Silverlight projects 287 | Generated_Code/ 288 | 289 | # Backup & report files from converting an old project file 290 | # to a newer Visual Studio version. Backup files are not needed, 291 | # because we have git ;-) 292 | _UpgradeReport_Files/ 293 | Backup*/ 294 | UpgradeLog*.XML 295 | UpgradeLog*.htm 296 | ServiceFabricBackup/ 297 | *.rptproj.bak 298 | 299 | # SQL Server files 300 | *.mdf 301 | *.ldf 302 | *.ndf 303 | 304 | # Business Intelligence projects 305 | *.rdl.data 306 | *.bim.layout 307 | *.bim_*.settings 308 | *.rptproj.rsuser 309 | *- [Bb]ackup.rdl 310 | *- [Bb]ackup ([0-9]).rdl 311 | *- [Bb]ackup ([0-9][0-9]).rdl 312 | 313 | # Microsoft Fakes 314 | FakesAssemblies/ 315 | 316 | # GhostDoc plugin setting file 317 | *.GhostDoc.xml 318 | 319 | # Node.js Tools for Visual Studio 320 | .ntvs_analysis.dat 321 | node_modules/ 322 | 323 | # Visual Studio 6 build log 324 | *.plg 325 | 326 | # Visual Studio 6 workspace options file 327 | *.opt 328 | 329 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 330 | *.vbw 331 | 332 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 333 | *.vbp 334 | 335 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 336 | *.dsw 337 | *.dsp 338 | 339 | # Visual Studio 6 technical files 340 | *.ncb 341 | *.aps 342 | 343 | # Visual Studio LightSwitch build output 344 | **/*.HTMLClient/GeneratedArtifacts 345 | **/*.DesktopClient/GeneratedArtifacts 346 | **/*.DesktopClient/ModelManifest.xml 347 | **/*.Server/GeneratedArtifacts 348 | **/*.Server/ModelManifest.xml 349 | _Pvt_Extensions 350 | 351 | # Paket dependency manager 352 | .paket/paket.exe 353 | paket-files/ 354 | 355 | # FAKE - F# Make 356 | .fake/ 357 | 358 | # CodeRush personal settings 359 | .cr/personal 360 | 361 | # Python Tools for Visual Studio (PTVS) 362 | __pycache__/ 363 | *.pyc 364 | 365 | # Cake - Uncomment if you are using it 366 | # tools/** 367 | # !tools/packages.config 368 | 369 | # Tabs Studio 370 | *.tss 371 | 372 | # Telerik's JustMock configuration file 373 | *.jmconfig 374 | 375 | # BizTalk build output 376 | *.btp.cs 377 | *.btm.cs 378 | *.odx.cs 379 | *.xsd.cs 380 | 381 | # OpenCover UI analysis results 382 | OpenCover/ 383 | 384 | # Azure Stream Analytics local run output 385 | ASALocalRun/ 386 | 387 | # MSBuild Binary and Structured Log 388 | *.binlog 389 | 390 | # NVidia Nsight GPU debugger configuration file 391 | *.nvuser 392 | 393 | # MFractors (Xamarin productivity tool) working folder 394 | .mfractor/ 395 | 396 | # Local History for Visual Studio 397 | .localhistory/ 398 | 399 | # Visual Studio History (VSHistory) files 400 | .vshistory/ 401 | 402 | # BeatPulse healthcheck temp database 403 | healthchecksdb 404 | 405 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 406 | MigrationBackup/ 407 | 408 | # Ionide (cross platform F# VS Code tools) working folder 409 | .ionide/ 410 | 411 | # Fody - auto-generated XML schema 412 | FodyWeavers.xsd 413 | 414 | # VS Code files for those working on multiple tools 415 | .vscode/* 416 | !.vscode/settings.json 417 | !.vscode/tasks.json 418 | !.vscode/launch.json 419 | !.vscode/extensions.json 420 | *.code-workspace 421 | 422 | # Local History for Visual Studio Code 423 | .history/ 424 | 425 | # Windows Installer files from build outputs 426 | *.cab 427 | *.msi 428 | *.msix 429 | *.msm 430 | *.msp 431 | 432 | # JetBrains Rider 433 | *.sln.iml 434 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ENet6 2 | 3 | This is a fork of [ENet](https://github.com/lsalzman/enet), a reliable UDP networking library which still doesn't support IPv6 in 2024 (and [ENet's author seems to dislike contributions](https://github.com/lsalzman/enet/issues/78)). 4 | 5 | So here's ENet with full support for dual-stack IPv4 / IPv6. 6 | 7 | It supports additional features ENet lacks while staying true to its lightweight and simple spirit. It's also compatible with ENet protocol so its possible for ENet6 and ENet clients to connect and exchange packets. 8 | 9 | Additional features include: 10 | - Dual-stack IPv4/IPv6 support 11 | - Callbacks for encryption 12 | - A separate timed out disconnect event 13 | - Packets acknowledge callbacks 14 | - Added a C# binding (derived from the amazing https://github.com/nxrighthere/ENet-CSharp) 15 | 16 | ## Build 17 | 18 | You can build this library using either [XMake](https://xmake.io) or [CMake](https://cmake.org/download/). 19 | 20 | XMake has full support for all desktop and mobile platforms (plus a simpler syntax) so I would suggest to prefer it over CMake. 21 | 22 | Build using xmake: 23 | 24 | ``` 25 | xmake 26 | ``` 27 | 28 | You can then install it using `sudo xmake install` (system-wide) or `xmake install -o dest_folder` 29 | 30 | ## How to use this library 31 | 32 | You can find precompiled binaries in the [releases](https://github.com/SirLynix/enet6/releases). 33 | 34 | If you're using [XMake](https://xmake.io), you can also simply use the `enet6` packages from xmake-repo, for example: 35 | ```lua 36 | add_rules("mode.debug", "mode.release") 37 | add_requires("enet6") 38 | 39 | target("my_project") 40 | add_files("src/main.c") 41 | add_packages("enet6") 42 | ``` 43 | 44 | This will make XMake download and compile enet6 on your computer and configure the compiler to have the right include dirs/link dirs/links so you can use it out of the box. 45 | 46 | You can then generate Visual Studio projects, CMakeLists, or directly compile using xmake. 47 | 48 | ## Frequently asked questions: 49 | 50 | ### Is there a tutorial? / How to use it? 51 | 52 | You can check the docs folder or read the official [ENet tutorial](http://enet.bespin.org/Tutorial.html). 53 | 54 | Here are the key differences between the official library and this fork: 55 | 56 | 1. Header files are in the enet6 folder instead of the enet folder (`#include `) 57 | 2. `enet_host_create` now takes a `ENetAddressType` as its first parameter. I suggest to use `ENET_ADDRESS_TYPE_ANY` on a server host (which will make it a dual-stack socket, able to handle IPv4 and IPv6). On a client host use either `ENET_ADDRESS_TYPE_IPV4` or `ENET_ADDRESS_TYPE_IPV6` depending on the ENetAddress you wish to connect to. 58 | 3. `ENetAddress` now has a `type` field, and its `host` field shouldn't be manipulated directly. 59 | 4. Peers disconnected by timing out now emit a `ENET_EVENT_TYPE_DISCONNECT_TIMEOUT` instead of a `ENET_EVENT_TYPE_DISCONNECT` 60 | 61 | Examples: 62 | 63 | #### Creating an ENet6 server** 64 | 65 | ```c 66 | ENetAddress address; 67 | ENetHost* server; 68 | /* Bind the server to the default localhost. */ 69 | /* A specific host address can be specified by */ 70 | /* enet_address_set_host (& address, type, "x.x.x.x"); */ 71 | 72 | /* use enet_address_build_any to configure a ENetAddress */ 73 | enet_address_build_any(ENET_ADDRESS_TYPE_IPV6, &address); 74 | /* Default port set by enet_address_build_any is ENET_PORT_ANY which means a random port, this is not convenient for a server */ 75 | /* Bind the server to port 1234. */ 76 | address.port = 1234; 77 | server = enet_host_create (ENET_ADDRESS_TYPE_ANY, /* either has to match address->type or be ENET_ADDRESS_TYPE_ANY to dual stack the socket */ 78 | & address /* the address to bind the server host to */, 79 | 32 /* allow up to 32 clients and/or outgoing connections */, 80 | 2 /* allow up to 2 channels to be used, 0 and 1 */, 81 | 0 /* assume any amount of incoming bandwidth */, 82 | 0 /* assume any amount of outgoing bandwidth */); 83 | if (server == NULL) 84 | { 85 | fprintf (stderr, 86 | "An error occurred while trying to create an ENet server host.\n"); 87 | exit (EXIT_FAILURE); 88 | } 89 | ... 90 | ... 91 | ... 92 | enet_host_destroy(server); 93 | ``` 94 | 95 | #### Connecting to a ENet6 client (using IPv4 or IPv6 depending on what's supported) 96 | 97 | ```c 98 | ENetAddress address; 99 | ENetEvent event; 100 | ENetPeer* peer; 101 | ENetHost* client; 102 | /* Connect to some.server.net:1234. */ 103 | /* enet_address_set_host will resolve the target address using the target family */ 104 | /* if ENET_ADDRESS_TYPE_ANY is specified it will try to resolve using IPv6 if possible */ 105 | /* but may fallback on IPv4 */ 106 | enet_address_set_host (& address, ENET_ADDRESS_TYPE_ANY, "some.server.net"); 107 | address.port = 1234; 108 | /* Initiate the connection, allocating the two channels 0 and 1. */ 109 | 110 | /* Create the enet host after resolving the address (allowing to create a IPv6 or IPv4 host) */ 111 | /* Using ENET_ADDRESS_TYPE_ANY type could also work but may fail on computers not supporting IPv6 at all */ 112 | client = enet_host_create (address.type, 113 | NULL /* create a client host */, 114 | 1 /* only allow 1 outgoing connection */, 115 | 2 /* allow up 2 channels to be used, 0 and 1 */, 116 | 0 /* assume any amount of incoming bandwidth */, 117 | 0 /* assume any amount of outgoing bandwidth */); 118 | if (client == NULL) 119 | { 120 | fprintf (stderr, 121 | "An error occurred while trying to create an ENet client host.\n"); 122 | exit (EXIT_FAILURE); 123 | } 124 | 125 | peer = enet_host_connect (client, & address, 2, 0); 126 | if (peer == NULL) 127 | { 128 | fprintf (stderr, 129 | "No available peers for initiating an ENet connection.\n"); 130 | exit (EXIT_FAILURE); 131 | } 132 | /* Wait up to 5 seconds for the connection attempt to succeed. */ 133 | if (enet_host_service (client, & event, 5000) > 0 && 134 | event.type == ENET_EVENT_TYPE_CONNECT) 135 | { 136 | puts ("Connection to some.server.net:1234 succeeded."); 137 | ... 138 | ... 139 | ... 140 | } 141 | else 142 | { 143 | /* Either the 5 seconds are up or a disconnect event was */ 144 | /* received. Reset the peer in the event the 5 seconds */ 145 | /* had run out without any significant event. */ 146 | enet_peer_reset (peer); 147 | puts ("Connection to some.server.net:1234 failed."); 148 | } 149 | ... 150 | ... 151 | ... 152 | enet_host_destroy(client); 153 | ``` 154 | 155 | ### Is it compatible with enet? 156 | 157 | Yes! You can connect to a regular enet server using a enet host created with the ENET_ADDRESS_TYPE_IPV4 address type (or ENET_ADDRESS_TYPE_ANY by using a IPv4-mapped IPv6). Regular enet clients can also connect to a enet6 host, if it was created using a ENET_ADDRESS_TYPE_ANY address type. 158 | 159 | However, enet6 is not compatible with regular enet once encryption is enabled, for obvious reasons. 160 | 161 | ### But why? 162 | 163 | ENet received **four** pull requests to add IPv6 support between 2013 and 2019, and no one has ever been accepted or dismissed by ENet author which seems to just ignore the problem and keep ENet as a growing old library with no support for IPv6. 164 | 165 | As a network programming teacher, I'm constantly running into issues like ENet only supporting IPv4 and other forks (like [ENet-CSharp](https://github.com/nxrighthere/ENet-CSharp)) solely relying on IPv6 which may not work on some network/computers. 166 | 167 | This implementation aims to be an updated version of ENet, with IPv6 support and some other stuff I can use to teach network programming. 168 | 169 | Feel free to use it. 170 | 171 | ### Is there a closer version to libenet with only IPv6 support? 172 | 173 | Yes! Check the ipv6 branch which was used for my IPv6 pull request to ENet 174 | 175 | ### Why bump from ENet 1.x.y to ENet6 6.x.y? 176 | 177 | ENet has been in version 1.x.y for 15+ years, I'm catching up (jk). 178 | 179 | ### How does encryption works? 180 | 181 | If you wish to use encryption, you need to set up enet_host_encrypt and pass an ENetEncryptor *on both ends*. 182 | 183 | Since original ENet protocol can't be extended to support encryption, setting up an encryptor changes the protocol and breaks compatibility with regular ENet clients (this shouldn't be an issue as ENet does not support encryption at all anyway). 184 | 185 | ENet6 will then call the encrypt callback before sending the packet, this callbacks works in a similar way to the compress callback (from compressor) except it receives the peer (allowing you to set a per-peer encryption key) and the callback can output more data than it receives. 186 | When receiving an encrypted packet, the decrypt callback will be called as well. 187 | 188 | Note that the peer pointer will be NULL when connecting in the encrypt callback. 189 | 190 | A common pattern is to enable encryption only connection (by sending a special packet), to do this: 191 | 1. Set up encryption callbacks on both ends (this is necessary because it changes the protocol) but make the encrypt callback return 0 for now (using a boolean in the peer data). 192 | 2. (Client) Connect to the server as usual. 193 | 3. (Client) Send a special packet to enable encryption (for better security, use an encrypted token which only the remote host can decrypt). 194 | 4. (Server) Enables encryption in the peer data, making packets encrypted from now. 195 | 5. (Client) When receiving an encrypted packet (in decrypt callback), enable encryption as well. 196 | 6. Encryption is now enabled on both server and client. 197 | 198 | Check encryption example. 199 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | ENet6 6.1.1 (unreleased): 2 | 3 | * Updated ENet base code up until https://github.com/lsalzman/enet/commit/4ce1625b8ae22a4e0030f07d80360e25ae558a1e 4 | 5 | ENet6 6.1.0 (April 6, 2024): 6 | 7 | * Added new architectures: 8 | - Windows 32bits 9 | - Windows ARM64 10 | - MinGW i386 11 | - Android x86 12 | - Android x86_64 13 | - iOS x86_64 14 | * Added ENetPacket acnowledge callback, allowing the application to know when a packet has been acknowledged by the peer 15 | * Added encryption support. If enabled it allows you to encrypt in a similar way to the compressor (except it receives the peer as a parameter and can output a bigger buffer) 16 | * Added examples (simple, compressed, encrypted) 17 | * Fixed ENET_EVENT_TYPE_DISCONNECT_TIMEOUT value 18 | * Updated ENet base code up until https://github.com/lsalzman/enet/commit/c44b7d0f7ff21edb702745e4c019d0537928c373 19 | 20 | ENet6 6.0.2 (October 6, 2023): 21 | 22 | * Added macOS arm64 binaries 23 | * Added a C# binding (derived from the amazing https://github.com/nxrighthere/ENet-CSharp) so enet6 can be used in C# projects or Unity (next step would be to make CI output Unity-ready packages). 24 | * Added setter/getters functions for packets/host/peers (from https://github.com/nxrighthere/ENet-CSharp) to make it easier to bind ENet6 to other languages 25 | * Fixed host socket dual-stack on Windows (host created with ENET_ADDRESS_TYPE_ANY could only receive connections from IPv6 peers, now they also can receive connections from IPv4 peers) 26 | * Fixed ENET_VERSION_PATCH value 27 | 28 | ENet6 6.0.1 (October 3, 2023): 29 | 30 | * enet_address_get_host_ip: fixed \0 character not being copied to the buffer 31 | * Added ENET_ADDRESS_MAX_LENGTH define 32 | * Changed behavior of enet_address_set_host when used with ENET_ADDRESS_TYPE_ANY 33 | In v6.0.0 this function could only output IPv4 addresses when using ENET_ADDRESS_TYPE_IPV4. If ENET_ADDRESS_TYPE_ANY were to be used IPv4 were converted to IPv6. 34 | This behavior was changed/fixed so that : 35 | - ENET_ADDRESS_TYPE_IPV4: Returns IPv4 36 | - ENET_ADDRESS_TYPE_IPV6: Returns an IPv6 or a IPv4-mapped IPv6 if no IPv6 is known 37 | - ENET_ADDRESS_TYPE_ANY: Returns IPv4 or IPv6 (priorize IPv6s) 38 | 39 | ENet6 6.0.0 (October 3, 2023): 40 | 41 | * Fork ENet to add IPv6 support 42 | * ENet peers timing out now generates ENET_EVENT_TYPE_DISCONNECT_TIMEOUT event 43 | 44 | ENet 1.3.17 (November 15, 2020): 45 | 46 | * fixes for sender getting too far ahead of receiver that can cause instability with reliable packets 47 | 48 | ENet 1.3.16 (September 8, 2020): 49 | 50 | * fix bug in unreliable fragment queuing 51 | * use single output queue for reliable and unreliable packets for saner ordering 52 | * revert experimental throttle changes that were less stable than prior algorithm 53 | 54 | ENet 1.3.15 (April 20, 2020): 55 | 56 | * quicker RTT initialization 57 | * use fractional precision for RTT calculations 58 | * fixes for packet throttle with low RTT variance 59 | * miscellaneous socket bug fixes 60 | 61 | ENet 1.3.14 (January 27, 2019): 62 | 63 | * bug fix for enet_peer_disconnect_later() 64 | * use getaddrinfo and getnameinfo where available 65 | * miscellaneous cleanups 66 | 67 | ENet 1.3.13 (April 30, 2015): 68 | 69 | * miscellaneous bug fixes 70 | * added premake and cmake support 71 | * miscellaneous documentation cleanups 72 | 73 | ENet 1.3.12 (April 24, 2014): 74 | 75 | * added maximumPacketSize and maximumWaitingData fields to ENetHost to limit the amount of 76 | data waiting to be delivered on a peer (beware that the default maximumPacketSize is 77 | 32MB and should be set higher if desired as should maximumWaitingData) 78 | 79 | ENet 1.3.11 (December 26, 2013): 80 | 81 | * allow an ENetHost to connect to itself 82 | * fixed possible bug with disconnect notifications during connect attempts 83 | * fixed some preprocessor definition bugs 84 | 85 | ENet 1.3.10 (October 23, 2013); 86 | 87 | * doubled maximum reliable window size 88 | * fixed RCVTIMEO/SNDTIMEO socket options and also added NODELAY 89 | 90 | ENet 1.3.9 (August 19, 2013): 91 | 92 | * added duplicatePeers option to ENetHost which can limit the number of peers from duplicate IPs 93 | * added enet_socket_get_option() and ENET_SOCKOPT_ERROR 94 | * added enet_host_random_seed() platform stub 95 | 96 | ENet 1.3.8 (June 2, 2013): 97 | 98 | * added enet_linked_version() for checking the linked version 99 | * added enet_socket_get_address() for querying the local address of a socket 100 | * silenced some debugging prints unless ENET_DEBUG is defined during compilation 101 | * handle EINTR in enet_socket_wait() so that enet_host_service() doesn't propagate errors from signals 102 | * optimized enet_host_bandwidth_throttle() to be less expensive for large numbers of peers 103 | 104 | ENet 1.3.7 (March 6, 2013): 105 | 106 | * added ENET_PACKET_FLAG_SENT to indicate that a packet is being freed because it has been sent 107 | * added userData field to ENetPacket 108 | * changed how random seed is generated on Windows to avoid import warnings 109 | * fixed case where disconnects could be generated with no preceding connect event 110 | 111 | ENet 1.3.6 (December 11, 2012): 112 | 113 | * added support for intercept callback in ENetHost that can be used to process raw packets before ENet 114 | * added enet_socket_shutdown() for issuing shutdown on a socket 115 | * fixed enet_socket_connect() to not error on non-blocking connects 116 | * fixed bug in MTU negotiation during connections 117 | 118 | ENet 1.3.5 (July 31, 2012): 119 | 120 | * fixed bug in unreliable packet fragment queuing 121 | 122 | ENet 1.3.4 (May 29, 2012): 123 | 124 | * added enet_peer_ping_interval() for configuring per-peer ping intervals 125 | * added enet_peer_timeout() for configuring per-peer timeouts 126 | * added protocol packet size limits 127 | 128 | ENet 1.3.3 (June 28, 2011): 129 | 130 | * fixed bug with simultaneous disconnects not dispatching events 131 | 132 | ENet 1.3.2 (May 31, 2011): 133 | 134 | * added support for unreliable packet fragmenting via the packet flag 135 | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT 136 | * fixed regression in unreliable packet queuing 137 | * added check against received port to limit some forms of IP-spoofing 138 | 139 | ENet 1.3.1 (February 10, 2011): 140 | 141 | * fixed bug in tracking of reliable data in transit 142 | * reliable data window size now scales with the throttle 143 | * fixed bug in fragment length calculation when checksums are used 144 | 145 | ENet 1.3.0 (June 5, 2010): 146 | 147 | * enet_host_create() now requires the channel limit to be specified as 148 | a parameter 149 | * enet_host_connect() now accepts a data parameter which is supplied 150 | to the receiving receiving host in the event data field for a connect event 151 | * added an adaptive order-2 PPM range coder as a built-in compressor option 152 | which can be set with enet_host_compress_with_range_coder() 153 | * added support for packet compression configurable with a callback 154 | * improved session number handling to not rely on the packet checksum 155 | field, saving 4 bytes per packet unless the checksum option is used 156 | * removed the dependence on the rand callback for session number handling 157 | 158 | Caveats: This version is not protocol compatible with the 1.2 series or 159 | earlier. The enet_host_connect and enet_host_create API functions require 160 | supplying additional parameters. 161 | 162 | ENet 1.2.5 (June 28, 2011): 163 | 164 | * fixed bug with simultaneous disconnects not dispatching events 165 | 166 | ENet 1.2.4 (May 31, 2011): 167 | 168 | * fixed regression in unreliable packet queuing 169 | * added check against received port to limit some forms of IP-spoofing 170 | 171 | ENet 1.2.3 (February 10, 2011): 172 | 173 | * fixed bug in tracking reliable data in transit 174 | 175 | ENet 1.2.2 (June 5, 2010): 176 | 177 | * checksum functionality is now enabled by setting a checksum callback 178 | inside ENetHost instead of being a configure script option 179 | * added totalSentData, totalSentPackets, totalReceivedData, and 180 | totalReceivedPackets counters inside ENetHost for getting usage 181 | statistics 182 | * added enet_host_channel_limit() for limiting the maximum number of 183 | channels allowed by connected peers 184 | * now uses dispatch queues for event dispatch rather than potentially 185 | unscalable array walking 186 | * added no_memory callback that is called when a malloc attempt fails, 187 | such that if no_memory returns rather than aborts (the default behavior), 188 | then the error is propagated to the return value of the API calls 189 | * now uses packed attribute for protocol structures on platforms with 190 | strange alignment rules 191 | * improved autoconf build system contributed by Nathan Brink allowing 192 | for easier building as a shared library 193 | 194 | Caveats: If you were using the compile-time option that enabled checksums, 195 | make sure to set the checksum callback inside ENetHost to enet_crc32 to 196 | regain the old behavior. The ENetCallbacks structure has added new fields, 197 | so make sure to clear the structure to zero before use if 198 | using enet_initialize_with_callbacks(). 199 | 200 | ENet 1.2.1 (November 12, 2009): 201 | 202 | * fixed bug that could cause disconnect events to be dropped 203 | * added thin wrapper around select() for portable usage 204 | * added ENET_SOCKOPT_REUSEADDR socket option 205 | * factored enet_socket_bind()/enet_socket_listen() out of enet_socket_create() 206 | * added contributed Code::Blocks build file 207 | 208 | ENet 1.2 (February 12, 2008): 209 | 210 | * fixed bug in VERIFY_CONNECT acknowledgement that could cause connect 211 | attempts to occasionally timeout 212 | * fixed acknowledgements to check both the outgoing and sent queues 213 | when removing acknowledged packets 214 | * fixed accidental bit rot in the MSVC project file 215 | * revised sequence number overflow handling to address some possible 216 | disconnect bugs 217 | * added enet_host_check_events() for getting only local queued events 218 | * factored out socket option setting into enet_socket_set_option() so 219 | that socket options are now set separately from enet_socket_create() 220 | 221 | Caveats: While this release is superficially protocol compatible with 1.1, 222 | differences in the sequence number overflow handling can potentially cause 223 | random disconnects. 224 | 225 | ENet 1.1 (June 6, 2007): 226 | 227 | * optional CRC32 just in case someone needs a stronger checksum than UDP 228 | provides (--enable-crc32 configure option) 229 | * the size of packet headers are half the size they used to be (so less 230 | overhead when sending small packets) 231 | * enet_peer_disconnect_later() that waits till all queued outgoing 232 | packets get sent before issuing an actual disconnect 233 | * freeCallback field in individual packets for notification of when a 234 | packet is about to be freed 235 | * ENET_PACKET_FLAG_NO_ALLOCATE for supplying pre-allocated data to a 236 | packet (can be used in concert with freeCallback to support some custom 237 | allocation schemes that the normal memory allocation callbacks would 238 | normally not allow) 239 | * enet_address_get_host_ip() for printing address numbers 240 | * promoted the enet_socket_*() functions to be part of the API now 241 | * a few stability/crash fixes 242 | 243 | 244 | -------------------------------------------------------------------------------- /docs/tutorial.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @page Tutorial Tutorial 3 | 4 | @ref Initialization 5 | 6 | @ref CreateServer 7 | 8 | @ref CreateClient 9 | 10 | @ref ManageHost 11 | 12 | @ref SendingPacket 13 | 14 | @ref Disconnecting 15 | 16 | @ref Connecting 17 | 18 | @section Initialization Initialization 19 | 20 | You should include the file when using ENet. Do not 21 | include without the directory prefix, as this may cause 22 | file name conflicts on some systems. 23 | 24 | Before using ENet, you must call enet_initialize() to initialize the 25 | library. Upon program exit, you should call enet_deinitialize() so 26 | that the library may clean up any used resources. 27 | 28 | @code 29 | #include 30 | 31 | int 32 | main (int argc, char ** argv) 33 | { 34 | if (enet_initialize () != 0) 35 | { 36 | fprintf (stderr, "An error occurred while initializing ENet.\n"); 37 | return EXIT_FAILURE; 38 | } 39 | atexit (enet_deinitialize); 40 | ... 41 | ... 42 | ... 43 | } 44 | @endcode 45 | 46 | @section CreateServer Creating an ENet server 47 | 48 | Servers in ENet are constructed with enet_host_create(). You must 49 | specify an address on which to receive data and new connections, as 50 | well as the maximum allowable numbers of connected peers. You may 51 | optionally specify the incoming and outgoing bandwidth of the server 52 | in bytes per second so that ENet may try to statically manage 53 | bandwidth resources among connected peers in addition to its dynamic 54 | throttling algorithm; specifying 0 for these two options will cause 55 | ENet to rely entirely upon its dynamic throttling algorithm to manage 56 | bandwidth. 57 | 58 | When done with a host, the host may be destroyed with 59 | enet_host_destroy(). All connected peers to the host will be reset, 60 | and the resources used by the host will be freed. 61 | 62 | @code 63 | ENetAddress address; 64 | ENetHost * server; 65 | 66 | /* Bind the server to the default localhost. */ 67 | /* A specific host address can be specified by */ 68 | /* enet_address_set_host (& address, "x.x.x.x"); */ 69 | 70 | enet_address_build_any(&address, ENET_ADDRESS_TYPE_IPV6); 71 | /* Bind the server to port 1234. */ 72 | address.port = 1234; 73 | 74 | server = enet_host_create (ENET_ADDRESS_TYPE_ANY /* has to match address.type, except if ENET_ADDRESS_TYPE_ANY which means dual-stack*/, 75 | & address /* the address to bind the server host to */, 76 | 32 /* allow up to 32 clients and/or outgoing connections */, 77 | 2 /* allow up to 2 channels to be used, 0 and 1 */, 78 | 0 /* assume any amount of incoming bandwidth */, 79 | 0 /* assume any amount of outgoing bandwidth */); 80 | if (server == NULL) 81 | { 82 | fprintf (stderr, 83 | "An error occurred while trying to create an ENet server host.\n"); 84 | exit (EXIT_FAILURE); 85 | } 86 | ... 87 | ... 88 | ... 89 | enet_host_destroy(server); 90 | @endcode 91 | 92 | @section CreateClient Creating an ENet client 93 | 94 | Clients in ENet are similarly constructed with enet_host_create() when 95 | no address is specified to bind the host to. Bandwidth may be 96 | specified for the client host as in the above example. The peer count 97 | controls the maximum number of connections to other server hosts that 98 | may be simultaneously open. 99 | 100 | @code 101 | ENetHost * client; 102 | 103 | client = enet_host_create (ENET_ADDRESS_TYPE_IPV6 /* create an IPv6 host*/, 104 | NULL /* create a client host */, 105 | 1 /* only allow 1 outgoing connection */, 106 | 2 /* allow up 2 channels to be used, 0 and 1 */, 107 | 0 /* assume any amount of incoming bandwidth */, 108 | 0 /* assume any amount of outgoing bandwidth */); 109 | 110 | if (client == NULL) 111 | { 112 | fprintf (stderr, 113 | "An error occurred while trying to create an ENet client host.\n"); 114 | exit (EXIT_FAILURE); 115 | } 116 | ... 117 | ... 118 | ... 119 | enet_host_destroy(client); 120 | @endcode 121 | 122 | @section ManageHost Managing an ENet host 123 | 124 | ENet uses a polled event model to notify the programmer of significant 125 | events. ENet hosts are polled for events with enet_host_service(), 126 | where an optional timeout value in milliseconds may be specified to 127 | control how long ENet will poll; if a timeout of 0 is specified, 128 | enet_host_service() will return immediately if there are no events to 129 | dispatch. enet_host_service() will return 1 if an event was dispatched 130 | within the specified timeout. 131 | 132 | Beware that most processing of the network with the ENet stack is done 133 | inside enet_host_service(). Both hosts that make up the sides of a connection 134 | must regularly call this function to ensure packets are actually sent and 135 | received. A common symptom of not actively calling enet_host_service() 136 | on both ends is that one side receives events while the other does not. 137 | The best way to schedule this activity to ensure adequate service is, for 138 | example, to call enet_host_service() with a 0 timeout (meaning non-blocking) 139 | at the beginning of every frame in a game loop. 140 | 141 | Currently there are only four types of significant events in ENet: 142 | 143 | An event of type ENET_EVENT_TYPE_NONE is returned if no event occurred 144 | within the specified time limit. enet_host_service() will return 0 145 | with this event. 146 | 147 | An event of type ENET_EVENT_TYPE_CONNECT is returned when either a new client 148 | host has connected to the server host or when an attempt to establish a 149 | connection with a foreign host has succeeded. Only the "peer" field of the 150 | event structure is valid for this event and contains the newly connected peer. 151 | 152 | An event of type ENET_EVENT_TYPE_RECEIVE is returned when a packet is received 153 | from a connected peer. The "peer" field contains the peer the packet was 154 | received from, "channelID" is the channel on which the packet was sent, and 155 | "packet" is the packet that was sent. The packet contained in the "packet" 156 | field must be destroyed with enet_packet_destroy() when you are done 157 | inspecting its contents. 158 | 159 | An event of type ENET_EVENT_TYPE_DISCONNECT is returned when a connected peer 160 | has explicitly disconnected. Only the "peer" field of the 161 | event structure is valid for this event and contains the peer that 162 | disconnected. Only the "data" field of the peer is still valid on a 163 | disconnect event and must be explicitly reset. 164 | 165 | An event of type ENET_EVENT_TYPE_DISCONNECT_TIMEOUT is returned when a connected peer 166 | has timed out. This event is identicaly to ENET_EVENT_TYPE_DISCONNECT except for the 167 | disconnection reason. 168 | 169 | @code 170 | ENetEvent event; 171 | 172 | /* Wait up to 1000 milliseconds for an event. */ 173 | while (enet_host_service (client, & event, 1000) > 0) 174 | { 175 | switch (event.type) 176 | { 177 | case ENET_EVENT_TYPE_CONNECT: 178 | printf ("A new client connected from %x:%u.\n", 179 | event.peer -> address.host, 180 | event.peer -> address.port); 181 | 182 | /* Store any relevant client information here. */ 183 | event.peer -> data = "Client information"; 184 | 185 | break; 186 | 187 | case ENET_EVENT_TYPE_RECEIVE: 188 | printf ("A packet of length %u containing %s was received from %s on channel %u.\n", 189 | event.packet -> dataLength, 190 | event.packet -> data, 191 | event.peer -> data, 192 | event.channelID); 193 | 194 | /* Clean up the packet now that we're done using it. */ 195 | enet_packet_destroy (event.packet); 196 | 197 | break; 198 | 199 | case ENET_EVENT_TYPE_DISCONNECT: 200 | case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: 201 | printf ("%s disconnected.\n", event.peer -> data); 202 | 203 | /* Reset the peer's client information. */ 204 | 205 | event.peer -> data = NULL; 206 | } 207 | } 208 | ... 209 | ... 210 | ... 211 | @endcode 212 | 213 | @section SendingPacket Sending a packet to an ENet peer 214 | 215 | Packets in ENet are created with enet_packet_create(), where the size 216 | of the packet must be specified. Optionally, initial data may be 217 | specified to copy into the packet. 218 | 219 | Certain flags may also be supplied to enet_packet_create() to control 220 | various packet features: 221 | 222 | ENET_PACKET_FLAG_RELIABLE specifies that the packet must use reliable 223 | delivery. A reliable packet is guaranteed to be delivered, and a 224 | number of retry attempts will be made until an acknowledgement is 225 | received from the foreign host the packet is sent to. If a certain 226 | number of retry attempts is reached without any acknowledgement, ENet 227 | will assume the peer has disconnected and forcefully reset the 228 | connection. If this flag is not specified, the packet is assumed an 229 | unreliable packet, and no retry attempts will be made nor 230 | acknowledgements generated. 231 | 232 | A packet may be resized (extended or truncated) with 233 | enet_packet_resize(). 234 | 235 | A packet is sent to a foreign host with 236 | enet_peer_send(). enet_peer_send() accepts a channel id over which to 237 | send the packet to a given peer. Once the packet is handed over to 238 | ENet with enet_peer_send(), ENet will handle its deallocation and 239 | enet_packet_destroy() should not be used upon it. 240 | 241 | One may also use enet_host_broadcast() to send a packet to all 242 | connected peers on a given host over a specified channel id, as with 243 | enet_peer_send(). 244 | 245 | Queued packets will be sent on a call to enet_host_service(). 246 | Alternatively, enet_host_flush() will send out queued packets without 247 | dispatching any events. 248 | 249 | @code 250 | /* Create a reliable packet of size 7 containing "packet\0" */ 251 | ENetPacket * packet = enet_packet_create ("packet", 252 | strlen ("packet") + 1, 253 | ENET_PACKET_FLAG_RELIABLE); 254 | 255 | /* Extend the packet so and append the string "foo", so it now */ 256 | /* contains "packetfoo\0" */ 257 | enet_packet_resize (packet, strlen ("packetfoo") + 1); 258 | strcpy (& packet -> data [strlen ("packet")], "foo"); 259 | 260 | /* Send the packet to the peer over channel id 0. */ 261 | /* One could also broadcast the packet by */ 262 | /* enet_host_broadcast (host, 0, packet); */ 263 | enet_peer_send (peer, 0, packet); 264 | ... 265 | ... 266 | ... 267 | /* One could just use enet_host_service() instead. */ 268 | enet_host_flush (host); 269 | @endcode 270 | 271 | @section Disconnecting Disconnecting an ENet peer 272 | 273 | Peers may be gently disconnected with enet_peer_disconnect(). A 274 | disconnect request will be sent to the foreign host, and ENet will 275 | wait for an acknowledgement from the foreign host before finally 276 | disconnecting. An event of type ENET_EVENT_TYPE_DISCONNECT will be 277 | generated once the disconnection succeeds. Normally timeouts apply to 278 | the disconnect acknowledgement, and so if no acknowledgement is 279 | received after a length of time the peer will be forcefully 280 | disconnected. 281 | 282 | enet_peer_reset() will forcefully disconnect a peer. The foreign host 283 | will get no notification of a disconnect and will time out on the 284 | foreign host. No event is generated. 285 | 286 | @code 287 | ENetEvent event; 288 | 289 | enet_peer_disconnect (peer, 0); 290 | 291 | /* Allow up to 3 seconds for the disconnect to succeed 292 | * and drop any packets received packets. 293 | */ 294 | while (enet_host_service (client, & event, 3000) > 0) 295 | { 296 | switch (event.type) 297 | { 298 | case ENET_EVENT_TYPE_RECEIVE: 299 | enet_packet_destroy (event.packet); 300 | break; 301 | 302 | case ENET_EVENT_TYPE_DISCONNECT: 303 | puts ("Disconnection succeeded."); 304 | return; 305 | ... 306 | ... 307 | ... 308 | } 309 | } 310 | 311 | /* We've arrived here, so the disconnect attempt didn't */ 312 | /* succeed yet. Force the connection down. */ 313 | enet_peer_reset (peer); 314 | ... 315 | ... 316 | ... 317 | @endcode 318 | 319 | @section Connecting Connecting to an ENet host 320 | 321 | A connection to a foreign host is initiated with enet_host_connect(). 322 | It accepts the address of a foreign host to connect to, and the number 323 | of channels that should be allocated for communication. If N channels 324 | are allocated for use, their channel ids will be numbered 0 through 325 | N-1. A peer representing the connection attempt is returned, or NULL 326 | if there were no available peers over which to initiate the 327 | connection. When the connection attempt succeeds, an event of type 328 | ENET_EVENT_TYPE_CONNECT will be generated. If the connection attempt 329 | times out or otherwise fails, an event of type 330 | ENET_EVENT_TYPE_DISCONNECT will be generated. 331 | 332 | @code 333 | ENetAddress address; 334 | ENetEvent event; 335 | ENetPeer *peer; 336 | 337 | /* Connect to some.server.net:1234. */ 338 | enet_address_set_host (& address, "some.server.net"); 339 | address.port = 1234; 340 | 341 | /* Initiate the connection, allocating the two channels 0 and 1. */ 342 | peer = enet_host_connect (client, & address, 2, 0); 343 | 344 | if (peer == NULL) 345 | { 346 | fprintf (stderr, 347 | "No available peers for initiating an ENet connection.\n"); 348 | exit (EXIT_FAILURE); 349 | } 350 | 351 | /* Wait up to 5 seconds for the connection attempt to succeed. */ 352 | if (enet_host_service (client, & event, 5000) > 0 && 353 | event.type == ENET_EVENT_TYPE_CONNECT) 354 | { 355 | puts ("Connection to some.server.net:1234 succeeded."); 356 | ... 357 | ... 358 | ... 359 | } 360 | else 361 | { 362 | /* Either the 5 seconds are up or a disconnect event was */ 363 | /* received. Reset the peer in the event the 5 seconds */ 364 | /* had run out without any significant event. */ 365 | enet_peer_reset (peer); 366 | 367 | puts ("Connection to some.server.net:1234 failed."); 368 | } 369 | ... 370 | ... 371 | ... 372 | @endcode 373 | */ 374 | -------------------------------------------------------------------------------- /src/win32.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file win32.c 3 | @brief ENet Win32 system specific functions 4 | */ 5 | #ifdef _WIN32 6 | 7 | #define ENET_BUILDING_LIB 1 8 | #include "enet6/enet.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | static enet_uint32 timeBase = 0; 18 | 19 | static int addressFamily[] = { 20 | AF_UNSPEC, /* ENET_ADDRESS_TYPE_ANY */ 21 | AF_INET, /* ENET_ADDRESS_TYPE_IPV4 */ 22 | AF_INET6 /* ENET_ADDRESS_TYPE_IPV6 */ 23 | }; 24 | 25 | static int 26 | enet_address_from_sock_addr4(ENetAddress * address, const struct sockaddr_in* sockAddr) 27 | { 28 | address->type = ENET_ADDRESS_TYPE_IPV4; 29 | address->port = ENET_NET_TO_HOST_16(sockAddr->sin_port); 30 | 31 | address->host.v4[0] = sockAddr->sin_addr.S_un.S_un_b.s_b1; 32 | address->host.v4[1] = sockAddr->sin_addr.S_un.S_un_b.s_b2; 33 | address->host.v4[2] = sockAddr->sin_addr.S_un.S_un_b.s_b3; 34 | address->host.v4[3] = sockAddr->sin_addr.S_un.S_un_b.s_b4; 35 | 36 | return 0; 37 | } 38 | 39 | static int 40 | enet_address_from_sock_addr6(ENetAddress * address, const struct sockaddr_in6* sockAddr) 41 | { 42 | int i; 43 | 44 | address->type = ENET_ADDRESS_TYPE_IPV6; 45 | address->port = ENET_NET_TO_HOST_16(sockAddr->sin6_port); 46 | 47 | for (i = 0; i < 8; ++i) 48 | address->host.v6[i] = ((enet_uint16) sockAddr->sin6_addr.s6_addr[i * 2]) << 8 | sockAddr->sin6_addr.s6_addr[i * 2 + 1]; 49 | 50 | return 0; 51 | } 52 | 53 | static int 54 | enet_address_from_addr_info(ENetAddress * address, const struct addrinfo * info) 55 | { 56 | switch (info->ai_family) 57 | { 58 | case AF_INET: 59 | return enet_address_from_sock_addr4(address, (struct sockaddr_in*) info->ai_addr); 60 | 61 | case AF_INET6: 62 | return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) info->ai_addr); 63 | 64 | default: 65 | return -1; 66 | } 67 | } 68 | 69 | static int 70 | enet_address_from_sock_addr(ENetAddress * address, const struct sockaddr * sockAddr) 71 | { 72 | switch (sockAddr->sa_family) 73 | { 74 | case AF_INET: 75 | return enet_address_from_sock_addr4(address, (struct sockaddr_in*) sockAddr); 76 | 77 | case AF_INET6: 78 | return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) sockAddr); 79 | 80 | default: 81 | return -1; 82 | } 83 | } 84 | 85 | static int 86 | enet_address_to_sock_addr(const ENetAddress * address, void * sockAddr) 87 | { 88 | switch (address->type) 89 | { 90 | case ENET_ADDRESS_TYPE_IPV4: 91 | { 92 | struct sockaddr_in* socketAddress = (struct sockaddr_in*) sockAddr; 93 | int addr; 94 | 95 | memset(socketAddress, 0, sizeof(struct sockaddr_in)); 96 | socketAddress->sin_family = AF_INET; 97 | socketAddress->sin_port = ENET_HOST_TO_NET_16(address->port); 98 | 99 | addr = ((unsigned int) address->host.v4[0]) << 24 100 | | ((unsigned int) address->host.v4[1]) << 16 101 | | ((unsigned int) address->host.v4[2]) << 8 102 | | ((unsigned int) address->host.v4[3]) << 0; 103 | 104 | socketAddress->sin_addr.s_addr = htonl(addr); 105 | 106 | return sizeof(struct sockaddr_in); 107 | } 108 | 109 | case ENET_ADDRESS_TYPE_IPV6: 110 | { 111 | struct sockaddr_in6* socketAddress = (struct sockaddr_in6*) sockAddr; 112 | int i; 113 | 114 | memset(socketAddress, 0, sizeof(struct sockaddr_in6)); 115 | socketAddress->sin6_family = AF_INET6; 116 | socketAddress->sin6_port = ENET_HOST_TO_NET_16(address->port); 117 | 118 | for (i = 0; i < 8; ++i) 119 | { 120 | u_short addressPart = ENET_HOST_TO_NET_16(address->host.v6[i]); 121 | socketAddress->sin6_addr.s6_addr[i * 2 + 0] = addressPart >> 0; 122 | socketAddress->sin6_addr.s6_addr[i * 2 + 1] = addressPart >> 8; 123 | } 124 | 125 | return sizeof(struct sockaddr_in6); 126 | } 127 | 128 | default: 129 | return 0; 130 | } 131 | } 132 | 133 | int 134 | enet_initialize (void) 135 | { 136 | WORD versionRequested = MAKEWORD (1, 1); 137 | WSADATA wsaData; 138 | 139 | if (WSAStartup (versionRequested, & wsaData)) 140 | return -1; 141 | 142 | if (LOBYTE (wsaData.wVersion) != 1|| 143 | HIBYTE (wsaData.wVersion) != 1) 144 | { 145 | WSACleanup (); 146 | 147 | return -1; 148 | } 149 | 150 | timeBeginPeriod (1); 151 | 152 | return 0; 153 | } 154 | 155 | void 156 | enet_deinitialize (void) 157 | { 158 | timeEndPeriod (1); 159 | 160 | WSACleanup (); 161 | } 162 | 163 | enet_uint32 164 | enet_host_random_seed (void) 165 | { 166 | return (enet_uint32) timeGetTime (); 167 | } 168 | 169 | enet_uint32 170 | enet_time_get (void) 171 | { 172 | return (enet_uint32) timeGetTime () - timeBase; 173 | } 174 | 175 | void 176 | enet_time_set (enet_uint32 newTimeBase) 177 | { 178 | timeBase = (enet_uint32) timeGetTime () - newTimeBase; 179 | } 180 | 181 | int 182 | enet_address_set_host(ENetAddress * address, ENetAddressType type, const char * name) 183 | { 184 | struct addrinfo hints; 185 | struct addrinfo* result; 186 | struct addrinfo* resultList = NULL; 187 | enet_uint16 port; 188 | ENetAddress tempAddress; 189 | int bestScore = -1; 190 | 191 | memset(&hints, 0, sizeof(hints)); 192 | hints.ai_family = AF_UNSPEC; 193 | 194 | if (getaddrinfo(name, NULL, &hints, &resultList) != 0) 195 | return -1; 196 | 197 | port = address->port; /* preserve port */ 198 | 199 | for (result = resultList; result != NULL; result = result->ai_next) 200 | { 201 | if (result->ai_addr != NULL) 202 | { 203 | if (enet_address_from_addr_info (&tempAddress, result) == 0) 204 | { 205 | tempAddress.port = port; /* preserve port */ 206 | 207 | int addressScore = 0; 208 | if (tempAddress.type == type || (tempAddress.type == ENET_ADDRESS_TYPE_IPV6 && type == ENET_ADDRESS_TYPE_ANY)) 209 | addressScore += 10; 210 | else if (tempAddress.type == ENET_ADDRESS_TYPE_IPV4) 211 | { 212 | if (type == ENET_ADDRESS_TYPE_ANY) 213 | addressScore += 5; /* lower score than IPv6 addresses */ 214 | else if (type == ENET_ADDRESS_TYPE_IPV6) 215 | { 216 | /* Convert that IPv4 to an IPv6 */ 217 | enet_address_convert_ipv6(&tempAddress); 218 | addressScore += 3; /* lower score than a real IPv6 */ 219 | } 220 | } 221 | 222 | if (addressScore > bestScore) 223 | { 224 | memcpy(address, &tempAddress, sizeof(ENetAddress)); 225 | bestScore = addressScore; 226 | } 227 | } 228 | } 229 | } 230 | 231 | if (resultList != NULL) 232 | freeaddrinfo(resultList); 233 | 234 | if (bestScore >= 0) 235 | return 0; 236 | else 237 | { 238 | if (enet_address_set_host_ip(address, name) == 0) 239 | { 240 | if (type == ENET_ADDRESS_TYPE_ANY) 241 | enet_address_convert_ipv6(address); 242 | 243 | return 0; 244 | } 245 | else 246 | return -1; 247 | } 248 | } 249 | 250 | int 251 | enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) 252 | { 253 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 254 | int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); 255 | 256 | int result = getnameinfo((struct sockaddr*) sockAddrBuf, socketAddressLen, name, nameLength, NULL, 0, NI_NAMEREQD); 257 | if (result != 0) 258 | return enet_address_get_host_ip (address, name, nameLength); 259 | else 260 | return 0; 261 | } 262 | 263 | int 264 | enet_socket_bind (ENetSocket socket, const ENetAddress * address) 265 | { 266 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 267 | int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); 268 | 269 | return bind (socket, (struct sockaddr *) sockAddrBuf, socketAddressLen) == SOCKET_ERROR ? -1 : 0; 270 | } 271 | 272 | int 273 | enet_socket_get_address (ENetSocket socket, ENetAddress * address) 274 | { 275 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; 276 | int bufferLength; 277 | 278 | if (getsockname (socket, (struct sockaddr *) sockAddrBuf, &bufferLength) == -1) 279 | return -1; 280 | 281 | return enet_address_from_sock_addr(address, (struct sockaddr *) sockAddrBuf); 282 | } 283 | 284 | int 285 | enet_socket_listen (ENetSocket socket, int backlog) 286 | { 287 | return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0; 288 | } 289 | 290 | ENetSocket 291 | enet_socket_create (ENetAddressType addressType, ENetSocketType socketType) 292 | { 293 | return socket (addressType == ENET_ADDRESS_TYPE_IPV4 ? PF_INET : PF_INET6, socketType == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); 294 | } 295 | 296 | int 297 | enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) 298 | { 299 | int result = SOCKET_ERROR; 300 | switch (option) 301 | { 302 | case ENET_SOCKOPT_NONBLOCK: 303 | { 304 | u_long nonBlocking = (u_long) value; 305 | result = ioctlsocket (socket, FIONBIO, & nonBlocking); 306 | break; 307 | } 308 | 309 | case ENET_SOCKOPT_BROADCAST: 310 | result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); 311 | break; 312 | 313 | case ENET_SOCKOPT_REUSEADDR: 314 | result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); 315 | break; 316 | 317 | case ENET_SOCKOPT_RCVBUF: 318 | result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); 319 | break; 320 | 321 | case ENET_SOCKOPT_SNDBUF: 322 | result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); 323 | break; 324 | 325 | case ENET_SOCKOPT_RCVTIMEO: 326 | result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int)); 327 | break; 328 | 329 | case ENET_SOCKOPT_SNDTIMEO: 330 | result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int)); 331 | break; 332 | 333 | case ENET_SOCKOPT_NODELAY: 334 | result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); 335 | break; 336 | 337 | case ENET_SOCKOPT_TTL: 338 | result = setsockopt (socket, IPPROTO_IP, IP_TTL, (char *) & value, sizeof (int)); 339 | break; 340 | 341 | case ENET_SOCKOPT_IPV6ONLY: 342 | { 343 | DWORD option = value; 344 | result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) & option, sizeof(option)); 345 | break; 346 | } 347 | 348 | default: 349 | break; 350 | } 351 | return result == SOCKET_ERROR ? -1 : 0; 352 | } 353 | 354 | int 355 | enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) 356 | { 357 | int result = SOCKET_ERROR, len; 358 | switch (option) 359 | { 360 | case ENET_SOCKOPT_ERROR: 361 | len = sizeof(int); 362 | result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len); 363 | break; 364 | 365 | case ENET_SOCKOPT_TTL: 366 | len = sizeof(int); 367 | result = getsockopt (socket, IPPROTO_IP, IP_TTL, (char *) value, & len); 368 | break; 369 | 370 | default: 371 | break; 372 | } 373 | return result == SOCKET_ERROR ? -1 : 0; 374 | } 375 | 376 | int 377 | enet_socket_connect (ENetSocket socket, const ENetAddress * address) 378 | { 379 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 380 | int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); 381 | int result; 382 | 383 | result = connect (socket, (struct sockaddr*) sockAddrBuf, socketAddressLen); 384 | if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK) 385 | return -1; 386 | 387 | return 0; 388 | } 389 | 390 | ENetSocket 391 | enet_socket_accept (ENetSocket socket, ENetAddress * address) 392 | { 393 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; 394 | int socketAddressLen = sizeof(sockAddrBuf); 395 | SOCKET result; 396 | 397 | result = accept (socket, 398 | address != NULL ? (struct sockaddr*) sockAddrBuf : NULL, 399 | address != NULL ? & socketAddressLen : NULL); 400 | 401 | if (result == INVALID_SOCKET) 402 | return ENET_SOCKET_NULL; 403 | 404 | if (address != NULL) 405 | { 406 | if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) 407 | return ENET_SOCKET_NULL; 408 | } 409 | 410 | return result; 411 | } 412 | 413 | int 414 | enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) 415 | { 416 | return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0; 417 | } 418 | 419 | void 420 | enet_socket_destroy (ENetSocket socket) 421 | { 422 | if (socket != INVALID_SOCKET) 423 | closesocket (socket); 424 | } 425 | 426 | int 427 | enet_socket_send (ENetSocket socket, 428 | const ENetAddress * address, 429 | const ENetBuffer * buffers, 430 | size_t bufferCount) 431 | { 432 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 433 | int socketAddressLen; 434 | 435 | DWORD sentLength = 0; 436 | 437 | if (address != NULL) 438 | { 439 | socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); 440 | if (socketAddressLen == 0) 441 | return -1; 442 | } 443 | 444 | if (WSASendTo (socket, 445 | (LPWSABUF) buffers, 446 | (DWORD) bufferCount, 447 | & sentLength, 448 | 0, 449 | address != NULL ? (struct sockaddr *) sockAddrBuf : NULL, 450 | address != NULL ? socketAddressLen : 0, 451 | NULL, 452 | NULL) == SOCKET_ERROR) 453 | { 454 | if (WSAGetLastError() == WSAEWOULDBLOCK) 455 | return 0; 456 | 457 | return -1; 458 | } 459 | 460 | return (int) sentLength; 461 | } 462 | 463 | int 464 | enet_socket_receive (ENetSocket socket, 465 | ENetAddress * address, 466 | ENetBuffer * buffers, 467 | size_t bufferCount) 468 | { 469 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; 470 | int socketAddressLen = sizeof(sockAddrBuf); 471 | DWORD flags = 0, 472 | recvLength = 0; 473 | struct sockaddr_in sin; 474 | 475 | if (WSARecvFrom (socket, 476 | (LPWSABUF) buffers, 477 | (DWORD) bufferCount, 478 | & recvLength, 479 | & flags, 480 | address != NULL ? (struct sockaddr *) & sockAddrBuf : NULL, 481 | address != NULL ? & socketAddressLen : NULL, 482 | NULL, 483 | NULL) == SOCKET_ERROR) 484 | { 485 | switch (WSAGetLastError()) 486 | { 487 | case WSAEWOULDBLOCK: 488 | case WSAECONNRESET: 489 | return 0; 490 | case WSAEINTR: 491 | case WSAEMSGSIZE: 492 | return -2; 493 | default: 494 | return -1; 495 | } 496 | } 497 | 498 | if (flags & MSG_PARTIAL) 499 | return -2; 500 | 501 | if (address != NULL) 502 | { 503 | if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) 504 | return -1; 505 | } 506 | 507 | return (int) recvLength; 508 | } 509 | 510 | int 511 | enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) 512 | { 513 | struct timeval timeVal; 514 | 515 | timeVal.tv_sec = timeout / 1000; 516 | timeVal.tv_usec = (timeout % 1000) * 1000; 517 | 518 | return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); 519 | } 520 | 521 | int 522 | enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) 523 | { 524 | fd_set readSet, writeSet; 525 | struct timeval timeVal; 526 | int selectCount; 527 | 528 | timeVal.tv_sec = timeout / 1000; 529 | timeVal.tv_usec = (timeout % 1000) * 1000; 530 | 531 | FD_ZERO (& readSet); 532 | FD_ZERO (& writeSet); 533 | 534 | if (* condition & ENET_SOCKET_WAIT_SEND) 535 | FD_SET (socket, & writeSet); 536 | 537 | if (* condition & ENET_SOCKET_WAIT_RECEIVE) 538 | FD_SET (socket, & readSet); 539 | 540 | selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); 541 | 542 | if (selectCount < 0) 543 | return -1; 544 | 545 | * condition = ENET_SOCKET_WAIT_NONE; 546 | 547 | if (selectCount == 0) 548 | return 0; 549 | 550 | if (FD_ISSET (socket, & writeSet)) 551 | * condition |= ENET_SOCKET_WAIT_SEND; 552 | 553 | if (FD_ISSET (socket, & readSet)) 554 | * condition |= ENET_SOCKET_WAIT_RECEIVE; 555 | 556 | return 0; 557 | } 558 | 559 | #endif 560 | 561 | -------------------------------------------------------------------------------- /src/unix.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file unix.c 3 | @brief ENet Unix system specific functions 4 | */ 5 | #ifndef _WIN32 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define ENET_BUILDING_LIB 1 19 | #include "enet6/enet.h" 20 | 21 | #ifdef __APPLE__ 22 | #ifdef HAS_POLL 23 | #undef HAS_POLL 24 | #endif 25 | #ifndef HAS_FCNTL 26 | #define HAS_FCNTL 1 27 | #endif 28 | #ifndef HAS_INET_PTON 29 | #define HAS_INET_PTON 1 30 | #endif 31 | #ifndef HAS_INET_NTOP 32 | #define HAS_INET_NTOP 1 33 | #endif 34 | #ifndef HAS_MSGHDR_FLAGS 35 | #define HAS_MSGHDR_FLAGS 1 36 | #endif 37 | #ifndef HAS_SOCKLEN_T 38 | #define HAS_SOCKLEN_T 1 39 | #endif 40 | #ifndef HAS_GETADDRINFO 41 | #define HAS_GETADDRINFO 1 42 | #endif 43 | #ifndef HAS_GETNAMEINFO 44 | #define HAS_GETNAMEINFO 1 45 | #endif 46 | #endif 47 | 48 | #ifdef HAS_FCNTL 49 | #include 50 | #endif 51 | 52 | #ifdef HAS_POLL 53 | #include 54 | #endif 55 | 56 | #if !defined(HAS_SOCKLEN_T) && !defined(__socklen_t_defined) 57 | typedef int socklen_t; 58 | #endif 59 | 60 | #ifndef MSG_NOSIGNAL 61 | #define MSG_NOSIGNAL 0 62 | #endif 63 | 64 | static enet_uint32 timeBase = 0; 65 | 66 | static int addressFamily[] = { 67 | AF_UNSPEC, /* ENET_ADDRESS_TYPE_ANY */ 68 | AF_INET, /* ENET_ADDRESS_TYPE_IPV4 */ 69 | AF_INET6 /* ENET_ADDRESS_TYPE_IPV6 */ 70 | }; 71 | 72 | static int 73 | enet_address_from_sock_addr4(ENetAddress * address, const struct sockaddr_in* sockAddr) 74 | { 75 | address->type = ENET_ADDRESS_TYPE_IPV4; 76 | address->port = ENET_NET_TO_HOST_16(sockAddr->sin_port); 77 | 78 | memcpy(&address->host.v4[0], &sockAddr->sin_addr.s_addr, 4 * sizeof(enet_uint8)); 79 | 80 | return 0; 81 | } 82 | 83 | static int 84 | enet_address_from_sock_addr6(ENetAddress * address, const struct sockaddr_in6* sockAddr) 85 | { 86 | int i; 87 | 88 | address->type = ENET_ADDRESS_TYPE_IPV6; 89 | address->port = ENET_NET_TO_HOST_16(sockAddr->sin6_port); 90 | 91 | for (i = 0; i < 8; ++i) 92 | address->host.v6[i] = ((enet_uint16) sockAddr->sin6_addr.s6_addr[i * 2]) << 8 | sockAddr->sin6_addr.s6_addr[i * 2 + 1]; 93 | 94 | return 0; 95 | } 96 | 97 | static int 98 | enet_address_from_addr_info(ENetAddress * address, const struct addrinfo * info) 99 | { 100 | switch (info->ai_family) 101 | { 102 | case AF_INET: 103 | return enet_address_from_sock_addr4(address, (struct sockaddr_in*) info->ai_addr); 104 | 105 | case AF_INET6: 106 | return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) info->ai_addr); 107 | 108 | default: 109 | return -1; 110 | } 111 | } 112 | 113 | static int 114 | enet_address_from_sock_addr(ENetAddress * address, const struct sockaddr * sockAddr) 115 | { 116 | switch (sockAddr->sa_family) 117 | { 118 | case AF_INET: 119 | return enet_address_from_sock_addr4(address, (struct sockaddr_in*) sockAddr); 120 | 121 | case AF_INET6: 122 | return enet_address_from_sock_addr6(address, (struct sockaddr_in6*) sockAddr); 123 | 124 | default: 125 | return -1; 126 | } 127 | } 128 | 129 | static int 130 | enet_address_to_sock_addr(const ENetAddress * address, void * sockAddr) 131 | { 132 | switch (address->type) 133 | { 134 | case ENET_ADDRESS_TYPE_IPV4: 135 | { 136 | struct sockaddr_in* socketAddress = (struct sockaddr_in*) sockAddr; 137 | int addr; 138 | 139 | memset(socketAddress, 0, sizeof(struct sockaddr_in)); 140 | socketAddress->sin_family = AF_INET; 141 | socketAddress->sin_port = ENET_HOST_TO_NET_16(address->port); 142 | 143 | addr = ((unsigned int) address->host.v4[0]) << 24 144 | | ((unsigned int) address->host.v4[1]) << 16 145 | | ((unsigned int) address->host.v4[2]) << 8 146 | | ((unsigned int) address->host.v4[3]) << 0; 147 | 148 | socketAddress->sin_addr.s_addr = htonl(addr); 149 | 150 | return sizeof(struct sockaddr_in); 151 | } 152 | 153 | case ENET_ADDRESS_TYPE_IPV6: 154 | { 155 | struct sockaddr_in6* socketAddress = (struct sockaddr_in6*) sockAddr; 156 | int i; 157 | 158 | memset(socketAddress, 0, sizeof(struct sockaddr_in6)); 159 | socketAddress->sin6_family = AF_INET6; 160 | socketAddress->sin6_port = ENET_HOST_TO_NET_16(address->port); 161 | 162 | for (i = 0; i < 8; ++i) 163 | { 164 | u_short addressPart = ENET_HOST_TO_NET_16(address->host.v6[i]); 165 | socketAddress->sin6_addr.s6_addr[i * 2 + 0] = addressPart >> 0; 166 | socketAddress->sin6_addr.s6_addr[i * 2 + 1] = addressPart >> 8; 167 | } 168 | 169 | return sizeof(struct sockaddr_in6); 170 | } 171 | 172 | default: 173 | return 0; 174 | } 175 | } 176 | 177 | int 178 | enet_initialize (void) 179 | { 180 | return 0; 181 | } 182 | 183 | void 184 | enet_deinitialize (void) 185 | { 186 | } 187 | 188 | enet_uint32 189 | enet_host_random_seed (void) 190 | { 191 | return (enet_uint32) time (NULL); 192 | } 193 | 194 | enet_uint32 195 | enet_time_get (void) 196 | { 197 | struct timeval timeVal; 198 | 199 | gettimeofday (& timeVal, NULL); 200 | 201 | return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase; 202 | } 203 | 204 | void 205 | enet_time_set (enet_uint32 newTimeBase) 206 | { 207 | struct timeval timeVal; 208 | 209 | gettimeofday (& timeVal, NULL); 210 | 211 | timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase; 212 | } 213 | 214 | int 215 | enet_address_set_host (ENetAddress * address, ENetAddressType type, const char * name) 216 | { 217 | #ifdef HAS_GETADDRINFO 218 | struct addrinfo hints; 219 | struct addrinfo* result; 220 | struct addrinfo* resultList = NULL; 221 | enet_uint16 port; 222 | ENetAddress tempAddress; 223 | int bestScore = -1; 224 | 225 | memset(&hints, 0, sizeof(hints)); 226 | hints.ai_family = AF_UNSPEC; 227 | 228 | if (getaddrinfo(name, NULL, &hints, &resultList) != 0) 229 | return -1; 230 | 231 | port = address->port; /* preserve port */ 232 | 233 | for (result = resultList; result != NULL; result = result->ai_next) 234 | { 235 | if (result->ai_addr != NULL) 236 | { 237 | if (enet_address_from_addr_info(&tempAddress, result) == 0) 238 | { 239 | tempAddress.port = port; /* preserve port */ 240 | 241 | int addressScore = 0; 242 | if (tempAddress.type == type || (tempAddress.type == ENET_ADDRESS_TYPE_IPV6 && type == ENET_ADDRESS_TYPE_ANY)) 243 | addressScore += 10; 244 | else if (tempAddress.type == ENET_ADDRESS_TYPE_IPV4) 245 | { 246 | if (type == ENET_ADDRESS_TYPE_ANY) 247 | addressScore += 5; /* lower score than IPv6 addresses */ 248 | else if (type == ENET_ADDRESS_TYPE_IPV6) 249 | { 250 | /* Convert that IPv4 to an IPv6 */ 251 | enet_address_convert_ipv6(&tempAddress); 252 | addressScore += 3; /* lower score than a real IPv6 */ 253 | } 254 | } 255 | 256 | if (addressScore > bestScore) 257 | { 258 | memcpy(address, &tempAddress, sizeof(ENetAddress)); 259 | bestScore = addressScore; 260 | } 261 | } 262 | } 263 | } 264 | 265 | if (resultList != NULL) 266 | freeaddrinfo(resultList); 267 | 268 | if (bestScore >= 0) 269 | return 0; 270 | #else 271 | struct hostent * hostEntry = NULL; 272 | #ifdef HAS_GETHOSTBYNAME_R 273 | struct hostent hostData; 274 | char buffer [2048]; 275 | int errnum; 276 | 277 | #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__GNU__) 278 | gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); 279 | #else 280 | hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum); 281 | #endif 282 | #else 283 | hostEntry = gethostbyname (name); 284 | #endif 285 | 286 | /* TODO */ 287 | /*if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET) 288 | { 289 | address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; 290 | 291 | return 0; 292 | }*/ 293 | #endif 294 | 295 | if (enet_address_set_host_ip(address, name) == 0) 296 | { 297 | if (type == ENET_ADDRESS_TYPE_ANY) 298 | enet_address_convert_ipv6(address); 299 | 300 | return 0; 301 | } 302 | else 303 | return -1; 304 | } 305 | 306 | int 307 | enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) 308 | { 309 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 310 | int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); 311 | #ifdef HAS_GETNAMEINFO 312 | int err; 313 | 314 | err = getnameinfo ((struct sockaddr *) sockAddrBuf, socketAddressLen, name, nameLength, NULL, 0, NI_NAMEREQD); 315 | if (! err) 316 | { 317 | if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength)) 318 | return -1; 319 | 320 | return 0; 321 | } 322 | if (err != EAI_NONAME) 323 | return -1; 324 | #else 325 | struct in_addr in; 326 | struct hostent * hostEntry = NULL; 327 | #ifdef HAS_GETHOSTBYADDR_R 328 | struct hostent hostData; 329 | char buffer [2048]; 330 | int errnum; 331 | 332 | #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__GNU__) 333 | gethostbyaddr_r ((char *) sockAddrBuf, socketAddressLen, addressFamily[address->type], & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); 334 | #else 335 | hostEntry = gethostbyaddr_r ((char *) sockAddrBuf, socketAddressLen, addressFamily[address->type], & hostData, buffer, sizeof (buffer), & errnum); 336 | #endif 337 | #else 338 | hostEntry = gethostbyaddr ((char *) sockAddrBuf, socketAddressLen, addressFamily[address->type]); 339 | #endif 340 | 341 | if (hostEntry != NULL) 342 | { 343 | size_t hostLen = strlen (hostEntry -> h_name); 344 | if (hostLen >= nameLength) 345 | return -1; 346 | memcpy (name, hostEntry -> h_name, hostLen + 1); 347 | return 0; 348 | } 349 | #endif 350 | 351 | return enet_address_get_host_ip (address, name, nameLength); 352 | } 353 | 354 | int 355 | enet_socket_bind (ENetSocket socket, const ENetAddress * address) 356 | { 357 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 358 | int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); 359 | 360 | return bind(socket, (struct sockaddr *) sockAddrBuf, socketAddressLen); 361 | } 362 | 363 | int 364 | enet_socket_get_address (ENetSocket socket, ENetAddress * address) 365 | { 366 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; 367 | int bufferLength; 368 | 369 | if (getsockname(socket, (struct sockaddr *) sockAddrBuf, &bufferLength) == -1) 370 | return -1; 371 | 372 | return enet_address_from_sock_addr(address, (struct sockaddr *) sockAddrBuf); 373 | } 374 | 375 | int 376 | enet_socket_listen (ENetSocket socket, int backlog) 377 | { 378 | return listen (socket, backlog < 0 ? SOMAXCONN : backlog); 379 | } 380 | 381 | ENetSocket 382 | enet_socket_create (ENetAddressType addressType, ENetSocketType socketType) 383 | { 384 | return socket(addressType == ENET_ADDRESS_TYPE_IPV4 ? PF_INET : PF_INET6, socketType == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); 385 | } 386 | 387 | int 388 | enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) 389 | { 390 | int result = -1; 391 | switch (option) 392 | { 393 | case ENET_SOCKOPT_NONBLOCK: 394 | #ifdef HAS_FCNTL 395 | result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK)); 396 | #else 397 | result = ioctl (socket, FIONBIO, & value); 398 | #endif 399 | break; 400 | 401 | case ENET_SOCKOPT_BROADCAST: 402 | result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); 403 | break; 404 | 405 | case ENET_SOCKOPT_REUSEADDR: 406 | result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); 407 | break; 408 | 409 | case ENET_SOCKOPT_RCVBUF: 410 | result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); 411 | break; 412 | 413 | case ENET_SOCKOPT_SNDBUF: 414 | result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); 415 | break; 416 | 417 | case ENET_SOCKOPT_RCVTIMEO: 418 | { 419 | struct timeval timeVal; 420 | timeVal.tv_sec = value / 1000; 421 | timeVal.tv_usec = (value % 1000) * 1000; 422 | result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval)); 423 | break; 424 | } 425 | 426 | case ENET_SOCKOPT_SNDTIMEO: 427 | { 428 | struct timeval timeVal; 429 | timeVal.tv_sec = value / 1000; 430 | timeVal.tv_usec = (value % 1000) * 1000; 431 | result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval)); 432 | break; 433 | } 434 | 435 | case ENET_SOCKOPT_NODELAY: 436 | result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); 437 | break; 438 | 439 | case ENET_SOCKOPT_TTL: 440 | result = setsockopt (socket, IPPROTO_IP, IP_TTL, (char *) & value, sizeof (int)); 441 | break; 442 | 443 | case ENET_SOCKOPT_IPV6ONLY: 444 | result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) & value, sizeof(int)); 445 | break; 446 | 447 | default: 448 | break; 449 | } 450 | return result == -1 ? -1 : 0; 451 | } 452 | 453 | int 454 | enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) 455 | { 456 | int result = -1; 457 | socklen_t len; 458 | switch (option) 459 | { 460 | case ENET_SOCKOPT_ERROR: 461 | len = sizeof (int); 462 | result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len); 463 | break; 464 | 465 | case ENET_SOCKOPT_TTL: 466 | len = sizeof (int); 467 | result = getsockopt (socket, IPPROTO_IP, IP_TTL, (char *) value, & len); 468 | break; 469 | 470 | default: 471 | break; 472 | } 473 | return result == -1 ? -1 : 0; 474 | } 475 | 476 | int 477 | enet_socket_connect (ENetSocket socket, const ENetAddress * address) 478 | { 479 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 480 | int socketAddressLen = enet_address_to_sock_addr(address, sockAddrBuf); 481 | int result; 482 | 483 | result = connect(socket, (struct sockaddr*) sockAddrBuf, socketAddressLen); 484 | if (result == -1 && errno == EINPROGRESS) 485 | return 0; 486 | 487 | return result; 488 | } 489 | 490 | ENetSocket 491 | enet_socket_accept (ENetSocket socket, ENetAddress * address) 492 | { 493 | int result; 494 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; 495 | int socketAddressLen = sizeof(sockAddrBuf); 496 | 497 | result = accept (socket, 498 | address != NULL ? (struct sockaddr*) sockAddrBuf : NULL, 499 | address != NULL ? & socketAddressLen : NULL); 500 | 501 | if (result == -1) 502 | return ENET_SOCKET_NULL; 503 | 504 | if (address != NULL) 505 | { 506 | if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) 507 | return ENET_SOCKET_NULL; 508 | } 509 | 510 | return result; 511 | } 512 | 513 | int 514 | enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) 515 | { 516 | return shutdown (socket, (int) how); 517 | } 518 | 519 | void 520 | enet_socket_destroy (ENetSocket socket) 521 | { 522 | if (socket != -1) 523 | close (socket); 524 | } 525 | 526 | int 527 | enet_socket_send (ENetSocket socket, 528 | const ENetAddress * address, 529 | const ENetBuffer * buffers, 530 | size_t bufferCount) 531 | { 532 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)]; 533 | struct msghdr msgHdr; 534 | int sentLength; 535 | 536 | memset (& msgHdr, 0, sizeof (struct msghdr)); 537 | 538 | if (address != NULL) 539 | { 540 | msgHdr.msg_namelen = enet_address_to_sock_addr(address, sockAddrBuf); 541 | if (msgHdr.msg_namelen == 0) 542 | return -1; 543 | 544 | msgHdr.msg_name = (struct sockaddr *) sockAddrBuf; 545 | } 546 | 547 | msgHdr.msg_iov = (struct iovec *) buffers; 548 | msgHdr.msg_iovlen = bufferCount; 549 | 550 | sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL); 551 | 552 | if (sentLength == -1) 553 | { 554 | if (errno == EWOULDBLOCK) 555 | return 0; 556 | 557 | return -1; 558 | } 559 | 560 | return sentLength; 561 | } 562 | 563 | int 564 | enet_socket_receive (ENetSocket socket, 565 | ENetAddress * address, 566 | ENetBuffer * buffers, 567 | size_t bufferCount) 568 | { 569 | unsigned char sockAddrBuf[sizeof(struct sockaddr_in6)] = { 0 }; 570 | int socketAddressLen = sizeof(sockAddrBuf); 571 | struct msghdr msgHdr; 572 | struct sockaddr_in sin; 573 | int recvLength; 574 | 575 | memset (& msgHdr, 0, sizeof (struct msghdr)); 576 | 577 | if (address != NULL) 578 | { 579 | msgHdr.msg_name = (struct sockaddr*) &sockAddrBuf; 580 | msgHdr.msg_namelen = socketAddressLen; 581 | } 582 | 583 | msgHdr.msg_iov = (struct iovec *) buffers; 584 | msgHdr.msg_iovlen = bufferCount; 585 | 586 | recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL); 587 | 588 | if (recvLength == -1) 589 | { 590 | switch (errno) 591 | { 592 | case EWOULDBLOCK: 593 | return 0; 594 | case EINTR: 595 | case EMSGSIZE: 596 | return -2; 597 | default: 598 | return -1; 599 | } 600 | } 601 | 602 | #ifdef HAS_MSGHDR_FLAGS 603 | if (msgHdr.msg_flags & MSG_TRUNC) 604 | return -2; 605 | #endif 606 | 607 | if (address != NULL) 608 | { 609 | if (enet_address_from_sock_addr(address, (struct sockaddr*) sockAddrBuf) != 0) 610 | return -1; 611 | } 612 | 613 | return recvLength; 614 | } 615 | 616 | int 617 | enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) 618 | { 619 | struct timeval timeVal; 620 | 621 | timeVal.tv_sec = timeout / 1000; 622 | timeVal.tv_usec = (timeout % 1000) * 1000; 623 | 624 | return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); 625 | } 626 | 627 | int 628 | enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) 629 | { 630 | #ifdef HAS_POLL 631 | struct pollfd pollSocket; 632 | int pollCount; 633 | 634 | pollSocket.fd = socket; 635 | pollSocket.events = 0; 636 | 637 | if (* condition & ENET_SOCKET_WAIT_SEND) 638 | pollSocket.events |= POLLOUT; 639 | 640 | if (* condition & ENET_SOCKET_WAIT_RECEIVE) 641 | pollSocket.events |= POLLIN; 642 | 643 | pollCount = poll (& pollSocket, 1, timeout); 644 | 645 | if (pollCount < 0) 646 | { 647 | if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) 648 | { 649 | * condition = ENET_SOCKET_WAIT_INTERRUPT; 650 | 651 | return 0; 652 | } 653 | 654 | return -1; 655 | } 656 | 657 | * condition = ENET_SOCKET_WAIT_NONE; 658 | 659 | if (pollCount == 0) 660 | return 0; 661 | 662 | if (pollSocket.revents & POLLOUT) 663 | * condition |= ENET_SOCKET_WAIT_SEND; 664 | 665 | if (pollSocket.revents & POLLIN) 666 | * condition |= ENET_SOCKET_WAIT_RECEIVE; 667 | 668 | return 0; 669 | #else 670 | fd_set readSet, writeSet; 671 | struct timeval timeVal; 672 | int selectCount; 673 | 674 | timeVal.tv_sec = timeout / 1000; 675 | timeVal.tv_usec = (timeout % 1000) * 1000; 676 | 677 | FD_ZERO (& readSet); 678 | FD_ZERO (& writeSet); 679 | 680 | if (* condition & ENET_SOCKET_WAIT_SEND) 681 | FD_SET (socket, & writeSet); 682 | 683 | if (* condition & ENET_SOCKET_WAIT_RECEIVE) 684 | FD_SET (socket, & readSet); 685 | 686 | selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); 687 | 688 | if (selectCount < 0) 689 | { 690 | if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) 691 | { 692 | * condition = ENET_SOCKET_WAIT_INTERRUPT; 693 | 694 | return 0; 695 | } 696 | 697 | return -1; 698 | } 699 | 700 | * condition = ENET_SOCKET_WAIT_NONE; 701 | 702 | if (selectCount == 0) 703 | return 0; 704 | 705 | if (FD_ISSET (socket, & writeSet)) 706 | * condition |= ENET_SOCKET_WAIT_SEND; 707 | 708 | if (FD_ISSET (socket, & readSet)) 709 | * condition |= ENET_SOCKET_WAIT_RECEIVE; 710 | 711 | return 0; 712 | #endif 713 | } 714 | 715 | #endif 716 | 717 | -------------------------------------------------------------------------------- /src/host.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file host.c 3 | @brief ENet host management functions 4 | */ 5 | #define ENET_BUILDING_LIB 1 6 | #include 7 | #include "enet6/enet.h" 8 | 9 | /** @defgroup host ENet host functions 10 | @{ 11 | */ 12 | 13 | /** Creates a host for communicating to peers. 14 | 15 | @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. 16 | @param peerCount the maximum number of peers that should be allocated for the host. 17 | @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT 18 | @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. 19 | @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. 20 | 21 | @returns the host on success and NULL on failure 22 | 23 | @remarks ENet will strategically drop packets on specific sides of a connection between hosts 24 | to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine 25 | the window size of a connection which limits the amount of reliable packets that may be in transit 26 | at any given time. 27 | */ 28 | ENetHost * 29 | enet_host_create (ENetAddressType type, const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) 30 | { 31 | ENetHost * host; 32 | ENetPeer * currentPeer; 33 | 34 | if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) 35 | return NULL; 36 | 37 | if (address && address->type != type && type != ENET_ADDRESS_TYPE_ANY) 38 | return NULL; 39 | 40 | host = (ENetHost *) enet_malloc (sizeof (ENetHost)); 41 | if (host == NULL) 42 | return NULL; 43 | memset (host, 0, sizeof (ENetHost)); 44 | 45 | host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer)); 46 | if (host -> peers == NULL) 47 | { 48 | enet_free (host); 49 | 50 | return NULL; 51 | } 52 | memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); 53 | 54 | host -> socket = enet_socket_create (type, ENET_SOCKET_TYPE_DATAGRAM); 55 | 56 | if (host -> socket != ENET_SOCKET_NULL && type == ENET_ADDRESS_TYPE_ANY) 57 | enet_socket_set_option (host -> socket, ENET_SOCKOPT_IPV6ONLY, 0); 58 | 59 | if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0)) 60 | { 61 | if (host -> socket != ENET_SOCKET_NULL) 62 | enet_socket_destroy (host -> socket); 63 | 64 | enet_free (host -> peers); 65 | enet_free (host); 66 | 67 | return NULL; 68 | } 69 | 70 | enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1); 71 | enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1); 72 | enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE); 73 | enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE); 74 | 75 | if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0) 76 | host -> address = * address; 77 | 78 | if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) 79 | channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; 80 | else 81 | if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) 82 | channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; 83 | 84 | host -> randomSeed = (enet_uint32) (size_t) host; 85 | host -> randomSeed += enet_host_random_seed (); 86 | host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16); 87 | host -> channelLimit = channelLimit; 88 | host -> incomingBandwidth = incomingBandwidth; 89 | host -> outgoingBandwidth = outgoingBandwidth; 90 | host -> bandwidthThrottleEpoch = 0; 91 | host -> recalculateBandwidthLimits = 0; 92 | host -> mtu = ENET_HOST_DEFAULT_MTU; 93 | host -> peerCount = peerCount; 94 | host -> commandCount = 0; 95 | host -> bufferCount = 0; 96 | host -> checksum = NULL; 97 | host -> receivedAddress.type = ENET_ADDRESS_TYPE_ANY; 98 | host -> receivedAddress.port = 0; 99 | host -> receivedData = NULL; 100 | host -> receivedDataLength = 0; 101 | 102 | host -> totalSentData = 0; 103 | host -> totalSentPackets = 0; 104 | host -> totalReceivedData = 0; 105 | host -> totalReceivedPackets = 0; 106 | host -> totalQueued = 0; 107 | 108 | host -> connectedPeers = 0; 109 | host -> bandwidthLimitedPeers = 0; 110 | host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID; 111 | host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE; 112 | host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA; 113 | 114 | host -> compressor.context = NULL; 115 | host -> compressor.compress = NULL; 116 | host -> compressor.decompress = NULL; 117 | host -> compressor.destroy = NULL; 118 | 119 | host -> encryptor.context = NULL; 120 | host -> encryptor.encrypt = NULL; 121 | host -> encryptor.decrypt = NULL; 122 | host -> encryptor.destroy = NULL; 123 | 124 | host -> intercept = NULL; 125 | 126 | enet_list_clear (& host -> dispatchQueue); 127 | 128 | for (currentPeer = host -> peers; 129 | currentPeer < & host -> peers [host -> peerCount]; 130 | ++ currentPeer) 131 | { 132 | currentPeer -> host = host; 133 | currentPeer -> incomingPeerID = currentPeer - host -> peers; 134 | currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF; 135 | currentPeer -> data = NULL; 136 | 137 | enet_list_clear (& currentPeer -> acknowledgements); 138 | enet_list_clear (& currentPeer -> sentReliableCommands); 139 | enet_list_clear (& currentPeer -> outgoingCommands); 140 | enet_list_clear (& currentPeer -> outgoingSendReliableCommands); 141 | enet_list_clear (& currentPeer -> dispatchedCommands); 142 | 143 | enet_peer_reset (currentPeer); 144 | } 145 | 146 | return host; 147 | } 148 | 149 | /** Destroys the host and all resources associated with it. 150 | @param host pointer to the host to destroy 151 | */ 152 | void 153 | enet_host_destroy (ENetHost * host) 154 | { 155 | ENetPeer * currentPeer; 156 | 157 | if (host == NULL) 158 | return; 159 | 160 | enet_socket_destroy (host -> socket); 161 | 162 | for (currentPeer = host -> peers; 163 | currentPeer < & host -> peers [host -> peerCount]; 164 | ++ currentPeer) 165 | { 166 | enet_peer_reset (currentPeer); 167 | } 168 | 169 | if (host -> compressor.context != NULL && host -> compressor.destroy) 170 | (* host -> compressor.destroy) (host -> compressor.context); 171 | 172 | if (host -> encryptor.context != NULL && host ->encryptor.destroy) 173 | (* host ->encryptor.destroy) (host ->encryptor.context); 174 | 175 | enet_free (host -> peers); 176 | enet_free (host); 177 | } 178 | 179 | enet_uint32 180 | enet_host_random (ENetHost * host) 181 | { 182 | /* Mulberry32 by Tommy Ettinger */ 183 | enet_uint32 n = (host -> randomSeed += 0x6D2B79F5U); 184 | n = (n ^ (n >> 15)) * (n | 1U); 185 | n ^= n + (n ^ (n >> 7)) * (n | 61U); 186 | return n ^ (n >> 14); 187 | } 188 | 189 | /** Sets the packet encryptor the host should use to encrypt and decrypt packets. 190 | @param host host to enable or disable encryption for 191 | @param compressor callbacks for for the packet encryptor; if NULL, then encryption is disabled 192 | 193 | @remarks enabling encryption enables the enet6 extended protocol and breaks compatibility 194 | with the regular enet protocol. 195 | */ 196 | void 197 | enet_host_encrypt(ENetHost* host, const ENetEncryptor* encryptor) 198 | { 199 | if (host->encryptor.context != NULL && host->encryptor.destroy) 200 | (*host->encryptor.destroy) (host->encryptor.context); 201 | 202 | if (encryptor) 203 | host->encryptor = *encryptor; 204 | else 205 | host->encryptor.context = NULL; 206 | } 207 | 208 | /** Initiates a connection to a foreign host. 209 | @param host host seeking the connection 210 | @param address destination for the connection 211 | @param channelCount number of channels to allocate 212 | @param data user data supplied to the receiving host 213 | @returns a peer representing the foreign host on success, NULL on failure 214 | @remarks The peer returned will have not completed the connection until enet_host_service() 215 | notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. 216 | */ 217 | ENetPeer * 218 | enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data) 219 | { 220 | ENetPeer * currentPeer; 221 | ENetChannel * channel; 222 | ENetProtocol command; 223 | 224 | if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) 225 | channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; 226 | else 227 | if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) 228 | channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; 229 | 230 | for (currentPeer = host -> peers; 231 | currentPeer < & host -> peers [host -> peerCount]; 232 | ++ currentPeer) 233 | { 234 | if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) 235 | break; 236 | } 237 | 238 | if (currentPeer >= & host -> peers [host -> peerCount]) 239 | return NULL; 240 | 241 | currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); 242 | if (currentPeer -> channels == NULL) 243 | return NULL; 244 | currentPeer -> channelCount = channelCount; 245 | currentPeer -> state = ENET_PEER_STATE_CONNECTING; 246 | currentPeer -> address = * address; 247 | currentPeer -> connectID = enet_host_random (host); 248 | currentPeer -> mtu = host -> mtu; 249 | 250 | if (host -> outgoingBandwidth == 0) 251 | currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; 252 | else 253 | currentPeer -> windowSize = (host -> outgoingBandwidth / 254 | ENET_PEER_WINDOW_SIZE_SCALE) * 255 | ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; 256 | 257 | if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) 258 | currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; 259 | else 260 | if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) 261 | currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; 262 | 263 | for (channel = currentPeer -> channels; 264 | channel < & currentPeer -> channels [channelCount]; 265 | ++ channel) 266 | { 267 | channel -> outgoingReliableSequenceNumber = 0; 268 | channel -> outgoingUnreliableSequenceNumber = 0; 269 | channel -> incomingReliableSequenceNumber = 0; 270 | channel -> incomingUnreliableSequenceNumber = 0; 271 | 272 | enet_list_clear (& channel -> incomingReliableCommands); 273 | enet_list_clear (& channel -> incomingUnreliableCommands); 274 | 275 | channel -> usedReliableWindows = 0; 276 | memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); 277 | } 278 | 279 | command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; 280 | command.header.channelID = 0xFF; 281 | command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); 282 | command.connect.incomingSessionID = currentPeer -> incomingSessionID; 283 | command.connect.outgoingSessionID = currentPeer -> outgoingSessionID; 284 | command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu); 285 | command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize); 286 | command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount); 287 | command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); 288 | command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); 289 | command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); 290 | command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); 291 | command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); 292 | command.connect.connectID = currentPeer -> connectID; 293 | command.connect.data = ENET_HOST_TO_NET_32 (data); 294 | 295 | enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0); 296 | 297 | return currentPeer; 298 | } 299 | 300 | /** Queues a packet to be sent to all peers associated with the host. 301 | @param host host on which to broadcast the packet 302 | @param channelID channel on which to broadcast 303 | @param packet packet to broadcast 304 | */ 305 | void 306 | enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet) 307 | { 308 | ENetPeer * currentPeer; 309 | 310 | for (currentPeer = host -> peers; 311 | currentPeer < & host -> peers [host -> peerCount]; 312 | ++ currentPeer) 313 | { 314 | if (currentPeer -> state != ENET_PEER_STATE_CONNECTED) 315 | continue; 316 | 317 | enet_peer_send (currentPeer, channelID, packet); 318 | } 319 | 320 | if (packet -> referenceCount == 0) 321 | enet_packet_destroy (packet); 322 | } 323 | 324 | /** Sets the packet compressor the host should use to compress and decompress packets. 325 | @param host host to enable or disable compression for 326 | @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled 327 | */ 328 | void 329 | enet_host_compress (ENetHost * host, const ENetCompressor * compressor) 330 | { 331 | if (host -> compressor.context != NULL && host -> compressor.destroy) 332 | (* host -> compressor.destroy) (host -> compressor.context); 333 | 334 | if (compressor) 335 | host -> compressor = * compressor; 336 | else 337 | host -> compressor.context = NULL; 338 | } 339 | 340 | /** Limits the maximum allowed channels of future incoming connections. 341 | @param host host to limit 342 | @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT 343 | */ 344 | void 345 | enet_host_channel_limit (ENetHost * host, size_t channelLimit) 346 | { 347 | if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) 348 | channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; 349 | else 350 | if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) 351 | channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; 352 | 353 | host -> channelLimit = channelLimit; 354 | } 355 | 356 | 357 | /** Adjusts the bandwidth limits of a host. 358 | @param host host to adjust 359 | @param incomingBandwidth new incoming bandwidth 360 | @param outgoingBandwidth new outgoing bandwidth 361 | @remarks the incoming and outgoing bandwidth parameters are identical in function to those 362 | specified in enet_host_create(). 363 | */ 364 | void 365 | enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) 366 | { 367 | host -> incomingBandwidth = incomingBandwidth; 368 | host -> outgoingBandwidth = outgoingBandwidth; 369 | host -> recalculateBandwidthLimits = 1; 370 | } 371 | 372 | void 373 | enet_host_bandwidth_throttle (ENetHost * host) 374 | { 375 | enet_uint32 timeCurrent = enet_time_get (), 376 | elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch, 377 | peersRemaining = (enet_uint32) host -> connectedPeers, 378 | dataTotal = ~0, 379 | bandwidth = ~0, 380 | throttle = 0, 381 | bandwidthLimit = 0; 382 | int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0; 383 | ENetPeer * peer; 384 | ENetProtocol command; 385 | 386 | if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) 387 | return; 388 | 389 | host -> bandwidthThrottleEpoch = timeCurrent; 390 | 391 | if (peersRemaining == 0) 392 | return; 393 | 394 | if (host -> outgoingBandwidth != 0) 395 | { 396 | dataTotal = 0; 397 | bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000; 398 | 399 | for (peer = host -> peers; 400 | peer < & host -> peers [host -> peerCount]; 401 | ++ peer) 402 | { 403 | if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) 404 | continue; 405 | 406 | dataTotal += peer -> outgoingDataTotal; 407 | } 408 | } 409 | 410 | while (peersRemaining > 0 && needsAdjustment != 0) 411 | { 412 | needsAdjustment = 0; 413 | 414 | if (dataTotal <= bandwidth) 415 | throttle = ENET_PEER_PACKET_THROTTLE_SCALE; 416 | else 417 | throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; 418 | 419 | for (peer = host -> peers; 420 | peer < & host -> peers [host -> peerCount]; 421 | ++ peer) 422 | { 423 | enet_uint32 peerBandwidth; 424 | 425 | if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || 426 | peer -> incomingBandwidth == 0 || 427 | peer -> outgoingBandwidthThrottleEpoch == timeCurrent) 428 | continue; 429 | 430 | peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000; 431 | if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) 432 | continue; 433 | 434 | peer -> packetThrottleLimit = (peerBandwidth * 435 | ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal; 436 | 437 | if (peer -> packetThrottleLimit == 0) 438 | peer -> packetThrottleLimit = 1; 439 | 440 | if (peer -> packetThrottle > peer -> packetThrottleLimit) 441 | peer -> packetThrottle = peer -> packetThrottleLimit; 442 | 443 | peer -> outgoingBandwidthThrottleEpoch = timeCurrent; 444 | 445 | peer -> incomingDataTotal = 0; 446 | peer -> outgoingDataTotal = 0; 447 | 448 | needsAdjustment = 1; 449 | -- peersRemaining; 450 | bandwidth -= peerBandwidth; 451 | dataTotal -= peerBandwidth; 452 | } 453 | } 454 | 455 | if (peersRemaining > 0) 456 | { 457 | if (dataTotal <= bandwidth) 458 | throttle = ENET_PEER_PACKET_THROTTLE_SCALE; 459 | else 460 | throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; 461 | 462 | for (peer = host -> peers; 463 | peer < & host -> peers [host -> peerCount]; 464 | ++ peer) 465 | { 466 | if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || 467 | peer -> outgoingBandwidthThrottleEpoch == timeCurrent) 468 | continue; 469 | 470 | peer -> packetThrottleLimit = throttle; 471 | 472 | if (peer -> packetThrottle > peer -> packetThrottleLimit) 473 | peer -> packetThrottle = peer -> packetThrottleLimit; 474 | 475 | peer -> incomingDataTotal = 0; 476 | peer -> outgoingDataTotal = 0; 477 | } 478 | } 479 | 480 | if (host -> recalculateBandwidthLimits) 481 | { 482 | host -> recalculateBandwidthLimits = 0; 483 | 484 | peersRemaining = (enet_uint32) host -> connectedPeers; 485 | bandwidth = host -> incomingBandwidth; 486 | needsAdjustment = 1; 487 | 488 | if (bandwidth == 0) 489 | bandwidthLimit = 0; 490 | else 491 | while (peersRemaining > 0 && needsAdjustment != 0) 492 | { 493 | needsAdjustment = 0; 494 | bandwidthLimit = bandwidth / peersRemaining; 495 | 496 | for (peer = host -> peers; 497 | peer < & host -> peers [host -> peerCount]; 498 | ++ peer) 499 | { 500 | if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || 501 | peer -> incomingBandwidthThrottleEpoch == timeCurrent) 502 | continue; 503 | 504 | if (peer -> outgoingBandwidth > 0 && 505 | peer -> outgoingBandwidth >= bandwidthLimit) 506 | continue; 507 | 508 | peer -> incomingBandwidthThrottleEpoch = timeCurrent; 509 | 510 | needsAdjustment = 1; 511 | -- peersRemaining; 512 | bandwidth -= peer -> outgoingBandwidth; 513 | } 514 | } 515 | 516 | for (peer = host -> peers; 517 | peer < & host -> peers [host -> peerCount]; 518 | ++ peer) 519 | { 520 | if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) 521 | continue; 522 | 523 | command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; 524 | command.header.channelID = 0xFF; 525 | command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); 526 | 527 | if (peer -> incomingBandwidthThrottleEpoch == timeCurrent) 528 | command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth); 529 | else 530 | command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit); 531 | 532 | enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); 533 | } 534 | } 535 | } 536 | 537 | enet_uint32 enet_host_get_peers_count(const ENetHost* host) { 538 | return host->connectedPeers; 539 | } 540 | 541 | enet_uint32 enet_host_get_packets_sent(const ENetHost* host) { 542 | return host->totalSentPackets; 543 | } 544 | 545 | enet_uint32 enet_host_get_packets_received(const ENetHost* host) { 546 | return host->totalReceivedPackets; 547 | } 548 | 549 | enet_uint32 enet_host_get_bytes_sent(const ENetHost* host) { 550 | return host->totalSentData; 551 | } 552 | 553 | enet_uint32 enet_host_get_bytes_received(const ENetHost* host) { 554 | return host->totalReceivedData; 555 | } 556 | 557 | void enet_host_set_max_duplicate_peers(ENetHost* host, enet_uint16 number) { 558 | if (number < 1) 559 | number = 1; 560 | 561 | if (number > ENET_PROTOCOL_MAXIMUM_PEER_ID) 562 | number = ENET_PROTOCOL_MAXIMUM_PEER_ID; 563 | 564 | host->duplicatePeers = number; 565 | } 566 | 567 | void enet_host_set_intercept_callback(ENetHost* host, ENetInterceptCallback callback) { 568 | host->intercept = callback; 569 | } 570 | 571 | void enet_host_set_checksum_callback(ENetHost* host, ENetChecksumCallback callback) { 572 | host->checksum = callback; 573 | } 574 | 575 | /** @} */ 576 | -------------------------------------------------------------------------------- /src/address.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file list.c 3 | @brief ENet linked list functions 4 | */ 5 | #define ENET_BUILDING_LIB 1 6 | #include "enet6/enet.h" 7 | 8 | #include 9 | #include 10 | 11 | /* http: rosettacode.org/wiki/Parse_an_IP_Address#C */ 12 | 13 | static unsigned int _parseDecimal ( const char** pchCursor ) 14 | { 15 | unsigned int nVal = 0; 16 | char chNow; 17 | while ( chNow = **pchCursor, chNow >= '0' && chNow <= '9' ) 18 | { 19 | /* shift digit in */ 20 | nVal *= 10; 21 | nVal += chNow - '0'; 22 | 23 | ++*pchCursor; 24 | } 25 | return nVal; 26 | } 27 | 28 | 29 | 30 | static unsigned int _parseHex ( const char** pchCursor ) 31 | { 32 | unsigned int nVal = 0; 33 | char chNow; 34 | while ( chNow = **pchCursor & 0x5f, /* (collapses case, but mutilates digits) */ 35 | (chNow >= ('0'&0x5f) && chNow <= ('9'&0x5f)) || 36 | (chNow >= 'A' && chNow <= 'F') 37 | ) 38 | { 39 | unsigned char nybbleValue; 40 | chNow -= 0x10; /* scootch digital values down; hex now offset by x31 */ 41 | nybbleValue = ( chNow > 9 ? chNow - (0x31-0x0a) : chNow ); 42 | /* shift nybble in */ 43 | nVal <<= 4; 44 | nVal += nybbleValue; 45 | 46 | ++*pchCursor; 47 | } 48 | return nVal; 49 | } 50 | 51 | 52 | 53 | /* Parse a textual IPv4 or IPv6 address, optionally with port, into a binary */ 54 | /* array (for the address, in network order), and an optionally provided port. */ 55 | /* Also, indicate which of those forms (4 or 6) was parsed. Return true on */ 56 | /* success. ppszText must be a nul-terminated ASCII string. It will be */ 57 | /* updated to point to the character which terminated parsing (so you can carry */ 58 | /* on with other things. abyAddr must be 16 bytes. You can provide NULL for */ 59 | /* abyAddr, nPort, bIsIPv6, if you are not interested in any of those */ 60 | /* informations. If we request port, but there is no port part, then nPort will */ 61 | /* be set to 0. There may be no whitespace leading or internal (though this may */ 62 | /* be used to terminate a successful parse. */ 63 | /* Note: the binary address and integer port are in network order. */ 64 | static int ParseIPv4OrIPv6 ( const char** ppszText, enet_uint8* abyAddr, enet_uint16* pnPort, int* pbIsIPv6 ) 65 | { 66 | unsigned char* abyAddrLocal; 67 | unsigned char abyDummyAddr[16]; 68 | 69 | /* find first colon, dot, and open bracket */ 70 | const char* pchColon = strchr ( *ppszText, ':' ); 71 | const char* pchDot = strchr ( *ppszText, '.' ); 72 | const char* pchOpenBracket = strchr ( *ppszText, '[' ); 73 | const char* pchCloseBracket = NULL; 74 | 75 | 76 | /* we'll consider this to (probably) be IPv6 if we find an open */ 77 | /* bracket, or an absence of dots, or if there is a colon, and it */ 78 | /* precedes any dots that may or may not be there */ 79 | int bIsIPv6local = NULL != pchOpenBracket || NULL == pchDot || 80 | ( NULL != pchColon && ( NULL == pchDot || pchColon < pchDot ) ); 81 | /* OK, now do a little further sanity check our initial guess... */ 82 | if ( bIsIPv6local ) 83 | { 84 | /* if open bracket, then must have close bracket that follows somewhere */ 85 | pchCloseBracket = strchr ( *ppszText, ']' ); 86 | if ( NULL != pchOpenBracket && ( NULL == pchCloseBracket || 87 | pchCloseBracket < pchOpenBracket ) ) 88 | return 0; 89 | } 90 | else /* probably ipv4 */ 91 | { 92 | /* dots must exist, and precede any colons */ 93 | if ( NULL == pchDot || ( NULL != pchColon && pchColon < pchDot ) ) 94 | return 0; 95 | } 96 | 97 | /* we figured out this much so far.... */ 98 | if ( NULL != pbIsIPv6 ) 99 | *pbIsIPv6 = bIsIPv6local; 100 | 101 | /* especially for IPv6 (where we will be decompressing and validating) */ 102 | /* we really need to have a working buffer even if the caller didn't */ 103 | /* care about the results. */ 104 | abyAddrLocal = abyAddr; /* prefer to use the caller's */ 105 | if ( NULL == abyAddrLocal ) /* but use a dummy if we must */ 106 | abyAddrLocal = abyDummyAddr; 107 | 108 | /* OK, there should be no correctly formed strings which are miscategorized, */ 109 | /* and now any format errors will be found out as we continue parsing */ 110 | /* according to plan. */ 111 | if ( ! bIsIPv6local ) /* try to parse as IPv4 */ 112 | { 113 | /* 4 dotted quad decimal; optional port if there is a colon */ 114 | /* since there are just 4, and because the last one can be terminated */ 115 | /* differently, I'm just going to unroll any potential loop. */ 116 | unsigned char* pbyAddrCursor = abyAddrLocal; 117 | unsigned int nVal; 118 | const char* pszTextBefore = *ppszText; 119 | nVal =_parseDecimal ( ppszText ); /* get first val */ 120 | if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText ) /* must be in range and followed by dot and nonempty */ 121 | return 0; 122 | *(pbyAddrCursor++) = (unsigned char) nVal; /* stick it in addr */ 123 | ++(*ppszText); /* past the dot */ 124 | 125 | pszTextBefore = *ppszText; 126 | nVal =_parseDecimal ( ppszText ); /* get second val */ 127 | if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText ) 128 | return 0; 129 | *(pbyAddrCursor++) = (unsigned char) nVal; 130 | ++(*ppszText); /* past the dot */ 131 | 132 | pszTextBefore = *ppszText; 133 | nVal =_parseDecimal ( ppszText ); /* get third val */ 134 | if ( '.' != **ppszText || nVal > 255 || pszTextBefore == *ppszText ) 135 | return 0; 136 | *(pbyAddrCursor++) = (unsigned char) nVal; 137 | ++(*ppszText); /* past the dot */ 138 | 139 | pszTextBefore = *ppszText; 140 | nVal =_parseDecimal ( ppszText ); /* get fourth val */ 141 | if ( nVal > 255 || pszTextBefore == *ppszText ) /* (we can terminate this one in several ways) */ 142 | return 0; 143 | *(pbyAddrCursor++) = (unsigned char) nVal; 144 | 145 | if ( ':' == **ppszText && NULL != pnPort ) /* have port part, and we want it */ 146 | { 147 | ++(*ppszText); /* past the colon */ 148 | pszTextBefore = *ppszText; 149 | nVal =_parseDecimal ( ppszText ); 150 | if ( nVal > 65535 || pszTextBefore == *ppszText ) 151 | return 0; 152 | 153 | *pnPort = (enet_uint16) nVal; 154 | return 1; 155 | } 156 | else /* finished just with ip address */ 157 | { 158 | if ( NULL != pnPort ) 159 | *pnPort = 0; /* indicate we have no port part */ 160 | return 1; 161 | } 162 | } 163 | else /* try to parse as IPv6 */ 164 | { 165 | unsigned char* pbyAddrCursor; 166 | unsigned char* pbyZerosLoc; 167 | int bIPv4Detected; 168 | int nIdx; 169 | /* up to 8 16-bit hex quantities, separated by colons, with at most one */ 170 | /* empty quantity, acting as a stretchy run of zeroes. optional port */ 171 | /* if there are brackets followed by colon and decimal port number. */ 172 | /* A further form allows an ipv4 dotted quad instead of the last two */ 173 | /* 16-bit quantities, but only if in the ipv4 space ::ffff:x:x . */ 174 | if ( NULL != pchOpenBracket ) /* start past the open bracket, if it exists */ 175 | *ppszText = pchOpenBracket + 1; 176 | pbyAddrCursor = abyAddrLocal; 177 | pbyZerosLoc = NULL; /* if we find a 'zero compression' location */ 178 | bIPv4Detected = 0; 179 | for ( nIdx = 0; nIdx < 8; ++nIdx ) /* we've got up to 8 of these, so we will use a loop */ 180 | { 181 | const char* pszTextBefore = *ppszText; 182 | unsigned nVal =_parseHex ( ppszText ); /* get value; these are hex */ 183 | if ( pszTextBefore == *ppszText ) /* if empty, we are zero compressing; note the loc */ 184 | { 185 | if ( NULL != pbyZerosLoc ) /* there can be only one! */ 186 | { 187 | /* unless it's a terminal empty field, then this is OK, it just means we're done with the host part */ 188 | if ( pbyZerosLoc == pbyAddrCursor ) 189 | { 190 | --nIdx; 191 | break; 192 | } 193 | return 0; /* otherwise, it's a format error */ 194 | } 195 | if ( ':' != **ppszText ) /* empty field can only be via : */ 196 | return 0; 197 | if ( 0 == nIdx ) /* leading zero compression requires an extra peek, and adjustment */ 198 | { 199 | ++(*ppszText); 200 | if ( ':' != **ppszText ) 201 | return 0; 202 | } 203 | 204 | pbyZerosLoc = pbyAddrCursor; 205 | ++(*ppszText); 206 | } 207 | else 208 | { 209 | if ( '.' == **ppszText ) /* special case of ipv4 convenience notation */ 210 | { 211 | /* who knows how to parse ipv4? we do! */ 212 | const char* pszTextlocal = pszTextBefore; /* back it up */ 213 | unsigned char abyAddrlocal[16]; 214 | int bIsIPv6local; 215 | int bParseResultlocal = ParseIPv4OrIPv6 ( &pszTextlocal, abyAddrlocal, NULL, &bIsIPv6local ); 216 | *ppszText = pszTextlocal; /* success or fail, remember the terminating char */ 217 | if ( ! bParseResultlocal || bIsIPv6local ) /* must parse and must be ipv4 */ 218 | return 0; 219 | /* transfer addrlocal into the present location */ 220 | *(pbyAddrCursor++) = abyAddrlocal[0]; 221 | *(pbyAddrCursor++) = abyAddrlocal[1]; 222 | *(pbyAddrCursor++) = abyAddrlocal[2]; 223 | *(pbyAddrCursor++) = abyAddrlocal[3]; 224 | ++nIdx; /* pretend like we took another short, since the ipv4 effectively is two shorts */ 225 | bIPv4Detected = 1; /* remember how we got here for further validation later */ 226 | break; /* totally done with address */ 227 | } 228 | 229 | if ( nVal > 65535 ) /* must be 16 bit quantity */ 230 | return 0; 231 | *(pbyAddrCursor++) = nVal >> 8; /* transfer in network order */ 232 | *(pbyAddrCursor++) = nVal & 0xff; 233 | if ( ':' == **ppszText ) /* typical case inside; carry on */ 234 | { 235 | ++(*ppszText); 236 | } 237 | else /* some other terminating character; done with this parsing parts */ 238 | { 239 | break; 240 | } 241 | } 242 | } 243 | 244 | /* handle any zero compression we found */ 245 | if ( NULL != pbyZerosLoc ) 246 | { 247 | int nHead = (int)( pbyZerosLoc - abyAddrLocal ); /* how much before zero compression */ 248 | int nTail = nIdx * 2 - (int)( pbyZerosLoc - abyAddrLocal ); /* how much after zero compression */ 249 | int nZeros = 16 - nTail - nHead; /* how much zeros */ 250 | memmove ( &abyAddrLocal[16-nTail], pbyZerosLoc, nTail ); /* scootch stuff down */ 251 | memset ( pbyZerosLoc, 0, nZeros ); /* clear the compressed zeros */ 252 | } 253 | 254 | /* validation of ipv4 subspace ::ffff:x.x */ 255 | if ( bIPv4Detected ) 256 | { 257 | static const unsigned char abyPfx[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; 258 | if ( 0 != memcmp ( abyAddrLocal, abyPfx, sizeof(abyPfx) ) ) 259 | return 0; 260 | } 261 | 262 | /* close bracket */ 263 | if ( NULL != pchOpenBracket ) 264 | { 265 | if ( ']' != **ppszText ) 266 | return 0; 267 | ++(*ppszText); 268 | } 269 | 270 | if ( ':' == **ppszText && NULL != pnPort ) /* have port part, and we want it */ 271 | { 272 | const char* pszTextBefore; 273 | unsigned int nVal; 274 | ++(*ppszText); /* past the colon */ 275 | pszTextBefore = *ppszText; 276 | pszTextBefore = *ppszText; 277 | nVal =_parseDecimal ( ppszText ); 278 | if ( nVal > 65535 || pszTextBefore == *ppszText ) 279 | return 0; 280 | 281 | *pnPort = (enet_uint16) nVal; 282 | return 1; 283 | } 284 | else /* finished just with ip address */ 285 | { 286 | if ( NULL != pnPort ) 287 | *pnPort = 0; /* indicate we have no port part */ 288 | return 1; 289 | } 290 | } 291 | 292 | } 293 | 294 | int enet_address_equal_host(const ENetAddress * firstAddress, const ENetAddress * secondAddress) 295 | { 296 | if (firstAddress->type != secondAddress->type) 297 | return 0; 298 | 299 | if (firstAddress->port != secondAddress->port) 300 | return 0; 301 | 302 | if (firstAddress->type == ENET_ADDRESS_TYPE_IPV4) 303 | { 304 | if (memcmp(&firstAddress->host.v4[0], &secondAddress->host.v4[0], 4*sizeof(enet_uint8)) != 0) 305 | return 0; 306 | } 307 | else if (firstAddress->type == ENET_ADDRESS_TYPE_IPV6) 308 | { 309 | if (memcmp(&firstAddress->host.v6[0], &secondAddress->host.v6[0], 8 * sizeof(enet_uint16)) != 0) 310 | return 0; 311 | } 312 | 313 | return 1; 314 | } 315 | 316 | int enet_address_equal(const ENetAddress * firstAddress, const ENetAddress * secondAddress) 317 | { 318 | if (!enet_address_equal_host(firstAddress, secondAddress)) 319 | return 0; 320 | 321 | if (firstAddress->port != secondAddress->port) 322 | return 0; 323 | 324 | return 1; 325 | } 326 | 327 | int 328 | enet_address_is_any(const ENetAddress * address) 329 | { 330 | switch (address->type) 331 | { 332 | case ENET_ADDRESS_TYPE_IPV4: 333 | { 334 | enet_uint8 zero[4] = { 0 }; 335 | return memcmp(&address->host.v4[0], zero, 4 * sizeof(enet_uint8)) != 0; 336 | } 337 | 338 | case ENET_ADDRESS_TYPE_IPV6: 339 | { 340 | enet_uint16 zero[8] = { 0 }; 341 | return memcmp(&address->host.v6[0], zero, 8 * sizeof(enet_uint16)) != 0; 342 | } 343 | 344 | default: 345 | break; 346 | } 347 | 348 | return 0; 349 | } 350 | 351 | int 352 | enet_address_is_broadcast(const ENetAddress * address) 353 | { 354 | /* there's no broadcast address in ipv6 */ 355 | if (address->type == ENET_ADDRESS_TYPE_IPV4 && 356 | address->host.v4[0] == 0xFF && 357 | address->host.v4[1] == 0xFF && 358 | address->host.v4[2] == 0xFF && 359 | address->host.v4[3] == 0xFF) 360 | { 361 | return 1; 362 | } 363 | 364 | return 0; 365 | } 366 | 367 | int 368 | enet_address_is_loopback(const ENetAddress * address) 369 | { 370 | switch (address->type) 371 | { 372 | case ENET_ADDRESS_TYPE_IPV4: 373 | return address->host.v4[0] == 127; 374 | 375 | case ENET_ADDRESS_TYPE_IPV6: 376 | { 377 | enet_uint16 loopback[8] = { 0, 0, 0, 0, 0, 0, 0, 1 }; 378 | return memcmp(&address->host.v6[0], loopback, 8 * sizeof(enet_uint16)) != 0; 379 | } 380 | 381 | default: 382 | break; 383 | } 384 | 385 | return 0; 386 | } 387 | 388 | int 389 | enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) 390 | { 391 | enum MaxSize 392 | { 393 | MS_PortPart = 1 + 4, /* semicolon + 4 digits */ 394 | 395 | MS_IPv4 = 4 * 3 + 3 + 1, /* 4 * 3 digits plus three dots plus end of string character */ 396 | MS_IPv4MappedIPv6 = 7 + 4 * 3 + 3 + 1, /* prefix + 4 * 3 digits plus three dots plus end of string character */ 397 | MS_IPv6 = 8 * 4 + 7 + 1, /* 4 * 3 digits plus three dots plus end of string character */ 398 | 399 | MS_IPv4WithPort = MS_IPv4 + MS_PortPart, 400 | MS_IPv4MappedIPv6WithPort = MS_IPv4MappedIPv6 + MS_PortPart + 2, /* plus two brackets */ 401 | MS_IPv6WithPort = MS_IPv6 + MS_PortPart + 2, /* plus two brackets */ 402 | }; 403 | 404 | if (address -> type == ENET_ADDRESS_TYPE_IPV4) 405 | { 406 | if (address->port != 0) 407 | { 408 | if (nameLength < MS_IPv4WithPort) 409 | return -1; 410 | 411 | sprintf(name, "%u.%u.%u.%u:%u", address->host.v4[0], address->host.v4[1], address->host.v4[2], address->host.v4[3], address->port); 412 | } 413 | else 414 | { 415 | if (nameLength < MS_IPv4) 416 | return -1; 417 | 418 | sprintf(name, "%u.%u.%u.%u", address->host.v4[0], address->host.v4[1], address->host.v4[2], address->host.v4[3]); 419 | } 420 | } 421 | else if (address->type == ENET_ADDRESS_TYPE_IPV6) 422 | { 423 | /* check if ipv4-mapped address */ 424 | if (address -> host.v6[0] == 0 && address -> host.v6[1] == 0 && 425 | address -> host.v6[2] == 0 && address -> host.v6[3] == 0 && 426 | address -> host.v6[4] == 0 && address -> host.v6[5] == 0xFFFF) 427 | { 428 | if (address->port != 0) 429 | { 430 | if (nameLength < MS_IPv4MappedIPv6WithPort) 431 | return -1; 432 | 433 | sprintf(name, "[::ffff:%u.%u.%u.%u]:%u", 434 | (address->host.v6[6] >> 8) & 0xFF, (address->host.v6[6] >> 0) & 0xFF, 435 | (address->host.v6[7] >> 8) & 0xFF, (address->host.v6[7] >> 0) & 0xFF, 436 | address->port); 437 | } 438 | else 439 | { 440 | if (nameLength < MS_IPv4MappedIPv6) 441 | return -1; 442 | 443 | sprintf(name, "::ffff:%u.%u.%u.%u", 444 | (address->host.v6[6] >> 8) & 0xFF, (address->host.v6[6] >> 0) & 0xFF, 445 | (address->host.v6[7] >> 8) & 0xFF, (address->host.v6[7] >> 0) & 0xFF); 446 | } 447 | } 448 | else 449 | { 450 | /* standard IPv6 451 | * canonical representation of an IPv6 452 | * https: tools.ietf.org/html/rfc5952 453 | */ 454 | char buffer[MS_IPv6WithPort]; 455 | 456 | /* find the longest zero sequence */ 457 | int f0 = -1; 458 | int l0 = -1; 459 | int i, j; 460 | char* p = buffer; 461 | int advance; 462 | 463 | for (i = 0; i < 8; ++i) 464 | { 465 | if (address -> host.v6[i] == 0) 466 | { 467 | for (j = i + 1; j < 8; ++j) 468 | { 469 | if (address -> host.v6[j] != 0) 470 | break; 471 | } 472 | 473 | if (f0 == -1 || j - i > l0 - f0) 474 | { 475 | f0 = i; 476 | l0 = j; 477 | } 478 | } 479 | } 480 | 481 | /* We need brackets around our IPv6 address if we have a port */ 482 | if (address->port != 0) 483 | *p++ = '['; 484 | 485 | for (i = 0; i < 8; ++i) 486 | { 487 | if (i == f0) 488 | { 489 | *p++ = ':'; 490 | *p++ = ':'; 491 | 492 | i = l0; 493 | if (i >= 8) 494 | break; 495 | } 496 | else if (i != 0) 497 | *p++ = ':'; 498 | 499 | advance = sprintf(p, "%x", address -> host.v6[i]); 500 | p += advance; 501 | } 502 | 503 | if (address->port != 0) 504 | { 505 | *p++ = ']'; 506 | *p++ = ':'; 507 | 508 | advance = sprintf(p, "%u", address->port); 509 | p += advance; 510 | } 511 | 512 | *p++ = '\0'; 513 | 514 | size_t addrLen = p - buffer; 515 | if (nameLength < addrLen) 516 | return -1; 517 | 518 | memcpy(name, buffer, addrLen); 519 | } 520 | } 521 | 522 | return 0; 523 | } 524 | 525 | 526 | int 527 | enet_address_set_host_ip (ENetAddress * address, const char * name) 528 | { 529 | enet_uint8 result[16]; 530 | int isIPv6; 531 | int i; 532 | enet_uint16 port; 533 | 534 | if (!ParseIPv4OrIPv6(&name, result, &port, &isIPv6)) 535 | return -1; 536 | 537 | if (isIPv6) 538 | { 539 | address->type = ENET_ADDRESS_TYPE_IPV6; 540 | 541 | for (i = 0; i < 8; ++i) 542 | address->host.v6[i] = (result[i * 2] << 8) | result[i * 2 + 1]; 543 | } 544 | else 545 | { 546 | address->type = ENET_ADDRESS_TYPE_IPV4; 547 | 548 | for (i = 0; i < 4; ++i) 549 | address->host.v4[i] = result[i]; 550 | } 551 | 552 | if (port) 553 | address->port = port; 554 | 555 | return 0; 556 | } 557 | 558 | 559 | void enet_address_build_any(ENetAddress * address, ENetAddressType type) 560 | { 561 | address->type = type; 562 | 563 | switch (type) 564 | { 565 | case ENET_ADDRESS_TYPE_IPV4: 566 | { 567 | memset(&address->host.v4, 0, sizeof(address->host.v4)); 568 | address->port = ENET_PORT_ANY; 569 | break; 570 | } 571 | 572 | case ENET_ADDRESS_TYPE_IPV6: 573 | { 574 | memset(&address->host.v6, 0, sizeof(address->host.v6)); 575 | address->port = ENET_PORT_ANY; 576 | break; 577 | } 578 | 579 | default: 580 | break; 581 | } 582 | } 583 | 584 | void enet_address_build_loopback(ENetAddress * address, ENetAddressType type) 585 | { 586 | address->type = type; 587 | 588 | switch (type) 589 | { 590 | case ENET_ADDRESS_TYPE_IPV4: 591 | { 592 | enet_uint8 loopbackIpV4[4] = { 127, 0, 0, 1 }; 593 | 594 | memcpy(&address->host.v4, loopbackIpV4, sizeof(address->host.v4)); 595 | address->port = ENET_PORT_ANY; 596 | break; 597 | } 598 | 599 | case ENET_ADDRESS_TYPE_IPV6: 600 | { 601 | enet_uint16 loopbackIpV6[8] = { 0, 0, 0, 0, 0, 0, 0, 1 }; 602 | 603 | memcpy(&address->host.v6, loopbackIpV6, sizeof(address->host.v6)); 604 | address->port = ENET_PORT_ANY; 605 | break; 606 | } 607 | 608 | default: 609 | break; 610 | } 611 | } 612 | 613 | void enet_address_convert_ipv6(ENetAddress * address) 614 | { 615 | enet_uint8 ipv4[4]; 616 | 617 | if (address->type == ENET_ADDRESS_TYPE_IPV4) 618 | { 619 | /* Turn into IPv4-mapped IPv6 */ 620 | address->type = ENET_ADDRESS_TYPE_IPV6; 621 | memcpy(&ipv4[0], &address->host.v4[0], 4 * sizeof(enet_uint8)); /* save IPv4 before modifying the IPv6 because of the union */ 622 | 623 | address->host.v6[0] = 0; 624 | address->host.v6[1] = 0; 625 | address->host.v6[2] = 0; 626 | address->host.v6[3] = 0; 627 | address->host.v6[4] = 0; 628 | address->host.v6[5] = 0xFFFF; 629 | address->host.v6[6] = ((enet_uint16) ipv4[0]) << 8 | ipv4[1]; 630 | address->host.v6[7] = ((enet_uint16) ipv4[2]) << 8 | ipv4[3]; 631 | } 632 | } 633 | 634 | 635 | /** @} */ 636 | --------------------------------------------------------------------------------