fix missing byte-order conversion in ntoh_move() and change protocol byte order to little-endian

Sun, 23 Aug 2026 11:38:27 +0200

author
Mike Becker <universe@uap-core.de>
date
Sun, 23 Aug 2026 11:38:27 +0200
changeset 183
39aa77b7188b
parent 182
04c65336777f
child 184
93c81539b702

fix missing byte-order conversion in ntoh_move() and change protocol byte order to little-endian

relates to #976

PROTOCOL.md file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
src/network.c file | annotate | diff | comparison | revisions
src/network.h file | annotate | diff | comparison | revisions
--- a/PROTOCOL.md	Sat Aug 22 16:22:05 2026 +0200
+++ b/PROTOCOL.md	Sun Aug 23 11:38:27 2026 +0200
@@ -29,8 +29,8 @@
 The datatypes used in the message payloads are as follows:
 
 * BYTE — a single byte
-* WORD — two bytes integer (big-endian)
-* DWORD — four bytes integer (big-endian)
+* WORD — two bytes integer (little-endian)
+* DWORD — four bytes integer (little-endian)
 * MOVE — a data structure describing a move, consisting of
   * a DWORD for the elapsed move time in seconds
   * a DWORD for additional elapsed move time in microseconds
@@ -41,6 +41,10 @@
   * a BYTE index for the row the piece was moved to
   * a BYTE describing the selected piece for promotion (otherwise zero)
 
+Note carefully that the byte order used by this protocol is little-endian while
+the standard network byte order would be big-endian. But since the vast majority
+of processors use little-endian nowadays, this protocol uses little-endian, too.
+
 The following table shows the possible values for the BYTEs describing a piece.
 
 | Piece        | Value (Hex) |
--- a/src/main.c	Sat Aug 22 16:22:05 2026 +0200
+++ b/src/main.c	Sun Aug 23 11:38:27 2026 +0200
@@ -498,8 +498,8 @@
 
 static NetMove hton_move(const Move *move) {
     return (NetMove) {
-        .mtime_sec = htonl(move->movetime / 1000000ull),
-        .mtime_usec = htonl(move->movetime % 1000000ull),
+        .mtime_sec = net_htonl(move->movetime / 1000000ull),
+        .mtime_usec = net_htonl(move->movetime % 1000000ull),
         .piece = move->piece,
         .fromfile = move->fromfile, .fromrow = move->fromrow,
         .tofile = move->tofile, .torow = move->torow,
@@ -511,7 +511,8 @@
     return (Move) {
         .string = {0}, /* calculated before applying the move */
         .timestamp = {0}, /* planned to be removed */
-        .movetime = (uint64_t)move->mtime_sec * 1000000ull + move->mtime_usec,
+        .movetime = (uint64_t)net_ntohl(move->mtime_sec)
+                    * 1000000ull + net_ntohl(move->mtime_usec),
         .piece = move->piece,
         .fromfile = move->fromfile, .fromrow = move->fromrow,
         .tofile = move->tofile, .torow = move->torow,
@@ -646,7 +647,7 @@
                     if (code == NETCODE_DECLINE_MOVE) {
                         uint32_t reason;
                         net_recieve_data(opponent, &reason, sizeof(uint32_t));
-                        reason = ntohl(reason);
+                        reason = net_ntohl(reason);
                         eval_move_failed_msg(reason);
                     } else if (code == NETCODE_ACCEPT
                             || code == NETCODE_CHECK
@@ -831,7 +832,7 @@
                     return 0;
                 }
             } else {
-                uint32_t reason = htonl(code);
+                uint32_t reason = net_htonl(code);
                 net_send_data(opponent, NETCODE_DECLINE_MOVE,
                     &reason, sizeof(uint32_t));
             }
@@ -1081,7 +1082,7 @@
         char *pgndata = malloc(pgndata_size);
         memcpy(pgndata, &(gamestate->info), sizeof(GameInfo));
         unsigned offset = sizeof(GameInfo);
-        uint16_t mcn = htons(mc);
+        uint16_t mcn = net_htons(mc);
         memcpy(pgndata+offset, &mcn, 2);
         offset += 2;
         for (unsigned i = 0 ; i < mc ; i++) {
@@ -1183,7 +1184,7 @@
         net_recieve_data(server.fd, &(gamestate->info), sizeof(GameInfo));
         uint16_t mc;
         net_recieve_data(server.fd, &mc, sizeof(mc));
-        mc = ntohs(mc);
+        mc = net_ntohs(mc);
         char *movedata = malloc(mc * NETMOVE_LEN);
         net_recieve_data(server.fd, movedata, mc*NETMOVE_LEN);
         unsigned offset = 0;
--- a/src/network.c	Sat Aug 22 16:22:05 2026 +0200
+++ b/src/network.c	Sun Aug 23 11:38:27 2026 +0200
@@ -33,6 +33,7 @@
 #include <unistd.h>
 #include <sys/un.h>
 #include <sys/stat.h>
+#include <netdb.h>
 #include "network.h"
 
 int net_create_tcp(Server *server, short port) {
--- a/src/network.h	Sat Aug 22 16:22:05 2026 +0200
+++ b/src/network.h	Sun Aug 23 11:38:27 2026 +0200
@@ -31,7 +31,7 @@
 #define	TCHESS_NETWORK_H
 
 #include <sys/socket.h>
-#include <netdb.h>
+#include <stdint.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -99,6 +99,54 @@
 uint8_t net_recieve_code_async(int socket);
 void net_recieve_data(int socket, void *data, size_t len);
 
+
+/* Functions that convert WORD and DWORD values to terminal-chess
+ * network protocol byte order.
+ *
+ * Note: due to the vast majority of processors using little-endian,
+ * the network byte order of terminal-chess is little-endian, too.
+ * While the standard network byte order is big-endian for historic reasons.
+ */
+
+static inline uint16_t net_htons(uint16_t s) {
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    return s;
+#else
+#if !defined(__BYTE_ORDER__)
+    const uint16_t endian_test = 1;
+    if (*(const uint8_t *)&endian_test) {
+        return s;
+    }
+#endif
+    return (uint16_t)((s >> 8) | (s << 8));
+#endif
+}
+
+static inline uint16_t net_ntohs(uint16_t s) {
+    return net_htons(s);
+}
+
+static inline uint32_t net_ntohl(uint32_t l) {
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    return l;
+#else
+#if !defined(__BYTE_ORDER__)
+    const uint16_t endian_test = 1;
+    if (*(const uint8_t *)&endian_test) {
+        return l;
+    }
+#endif
+    return ((l & 0x000000FFu) << 24)
+         | ((l & 0x0000FF00u) << 8)
+         | ((l & 0x00FF0000u) >> 8)
+         | ((l & 0xFF000000u) >> 24);
+#endif
+}
+
+static inline uint32_t net_htonl(uint32_t l) {
+    return net_ntohl(l);
+}
+
 #ifdef	__cplusplus
 }
 #endif

mercurial