Browse code

WIP: Don't wait for JOIN reply in client

Robert Cranston authored on 11/06/2023 16:19:57
Showing 1 changed files
... ...
@@ -574,8 +574,8 @@ void run_client(int sockfd) {
574 574
         inputbuf[inputbuflen++] = '\0';
575 575
         if (-1 == send(sockfd, inputbuf, inputbuflen, 0))
576 576
             report_fatal(errno, "Failed to send %s", action.name);
577
-        if (-1 == recv(sockfd, sockbuf, BUFLEN_MAX, MSG_PEEK))
578
-            report_fatal(errno, "Failed to receive %s", action.name);
577
+        // if (-1 == recv(sockfd, sockbuf, BUFLEN_MAX, MSG_PEEK))
578
+        //     report_fatal(errno, "Failed to receive %s", action.name);
579 579
     }
580 580
 
581 581
     // Setup terminal.
Browse code

WIP: Add implementation

Robert Cranston authored on 06/02/2021 04:14:21
Showing 1 changed files
... ...
@@ -1,7 +1,24 @@
1
-// TODO: Add `listen` and `accept` for if the socket has a `STREAM` `socktype`.
1
+// TODO: Break out `run_*` into `*.{c,h}`?
2
+// TODO: Harmonize all reporting strings and comments.
3
+// TODO: Harmonize all conditional and loop statements.
4
+// TODO: Add `listen` and `accept` if the socket has a `STREAM` `socktype`.
2 5
 // TODO: Compare to the example in `getaddrinfo(3)`.
3 6
 // TODO: Mention `getaddrinfo` and the RFC 3484 sorting order in readme.
4 7
 // TODO: Change client store from queue to binary tree?
8
+// TODO: Create graph of parameter flow.
9
+// TODO: Use Kaj and Börje in readme example.
10
+// TODO: Document idioms:
11
+// - Trim: `buflen -= buflen && buf[buflen - 1] == char;`
12
+// - Format: `buflen += snprintf(&buf[buflen], BUFLEN_MAX - buflen, format, args...);`
13
+// TODO: Document "size" for raw bytes and "len" for "logical" data (e.g.
14
+// excluding string terminating null).
15
+// TODO: Give each client an `id`.
16
+// TODO: Also make the server send a timestamp base on join?
17
+// TODO: `#include <stdbool.h>` and change any relevant `int`s to `bool`s.
18
+// TODO: Change to `run_*(int * sockfd, int sockfdlen, bool nonblock)`.
19
+// TODO: Do different things on `initsockfd` depending on `family`.
20
+// TODO: We can combine IDs and actions: an ID of 0 implies a JOIN action,
21
+// otherwise a MESSAGE action.
5 22
 
6 23
 /// Headers
7 24
 
... ...
@@ -25,17 +42,15 @@
25 42
 #include <termios.h>
26 43
 
27 44
 // Other
28
-#include "args.h"
45
+#include "arg.h"
29 46
 #include "report.h"
30 47
 
31 48
 
32
-/// Parameters
49
+/// Constants
50
+
51
+//// Program
33 52
 
34
-//// Application
35 53
 #define PROGNAME "sockchat"
36
-#define VERSION_STR "1.0"
37
-#define DESCRIPTION \
38
-    "Chat with unlimited number of peers through a variety of sockets"
39 54
 
40 55
 //// Arguments
41 56
 // TODO
... ...
@@ -49,12 +64,14 @@
49 64
 
50 65
 //// Command line interface
51 66
 #define VERSION \
52
-    PROGNAME " " VERSION_STR " - " DESCRIPTION "\n"
67
+    PROGNAME " 1.0\n"
53 68
 #define USAGE \
54 69
     "Usage:\n" \
55 70
     "  " PROGNAME " (server|client) [options]\n" \
56 71
     "  " PROGNAME " -h|--help\n" \
57 72
     "  " PROGNAME " --version\n"
73
+#define DESCRIPTION \
74
+    "Chat with unlimited number of peers through a variety of sockets.\n"
58 75
 #define OPTIONS \
59 76
     "Options:\n" \
60 77
     "  -f <family>    [default: " DEFAULT_FAMILY   "]\n" \
... ...
@@ -64,15 +81,15 @@
64 81
     "  -s <service>   [default: " DEFAULT_SERVICE  "]\n"
65 82
 
66 83
 //// Implementation
67
-#define BUF_SIZE _POSIX_MAX_CANON
84
+#define BUFLEN_MAX _POSIX_MAX_CANON
68 85
 
69 86
 
70 87
 /// Forward declarations
71 88
 
72 89
 //// Implementation
73
-int getsockfd(
74
-    char const * action_name,
75
-    int (*action)(int sockfd, struct sockaddr const * addr, socklen_t addrlen),
90
+int initsockfd(
91
+    char const * init_name,
92
+    int (*init)(int sockfd, struct sockaddr const * addr, socklen_t addrlen),
76 93
     int flags,
77 94
     int family,
78 95
     int socktype,
... ...
@@ -87,105 +104,103 @@ void run_client(int sockfd);
87 104
 /// Data
88 105
 
89 106
 //// Arguments
107
+#define ROLE(ROLE, INIT, FLAGS) { #ROLE, #INIT, INIT, FLAGS, run_##ROLE }
90 108
 struct role {
91 109
     char const * name;
92
-    char const * action_name;
93
-    int (*action)(int sockfd, struct sockaddr const * addr, socklen_t addrlen);
110
+    char const * init_name;
111
+    int (*init)(int sockfd, struct sockaddr const * addr, socklen_t addrlen);
94 112
     int flags;
95 113
     void (*run)(int sockfd);
96 114
 };
97
-struct option {
98
-    char const * name;
99
-    int value;
100
-};
101
-#define ROLE(NAME, ACTION, FLAGS) { #NAME, #ACTION, ACTION, FLAGS, run_##NAME }
102 115
 static struct role roles[] = {
103 116
     ROLE(server, bind, AI_PASSIVE),
104 117
     ROLE(client, connect, 0),
105 118
 };
106
-#define OPTION_FAMILY(FAMILY) { #FAMILY, AF_##FAMILY }
107
-static struct option families[] = {
108
-    OPTION_FAMILY(UNSPEC),
109
-    OPTION_FAMILY(UNIX),
110
-    OPTION_FAMILY(LOCAL),
111
-    OPTION_FAMILY(INET),
112
-    OPTION_FAMILY(INET6),
113
-    OPTION_FAMILY(PACKET),
119
+
120
+#define CONST_FAMILY(FAMILY) { #FAMILY, AF_##FAMILY }
121
+static struct arg_const families[] = {
122
+    CONST_FAMILY(UNSPEC),
123
+    CONST_FAMILY(INET),
124
+    CONST_FAMILY(INET6),
125
+    CONST_FAMILY(UNIX),
114 126
 };
115
-#define OPTION_SOCKTYPE(SOCKTYPE) { #SOCKTYPE, SOCK_##SOCKTYPE }
116
-static struct option socktypes[] = {
117
-    { "0", 0 },
118
-    OPTION_SOCKTYPE(STREAM),
119
-    OPTION_SOCKTYPE(DGRAM),
120
-    OPTION_SOCKTYPE(SEQPACKET),
121
-    OPTION_SOCKTYPE(RAW),
122
-    OPTION_SOCKTYPE(RDM),
123
-    OPTION_SOCKTYPE(PACKET),
127
+
128
+#define CONST_SOCKTYPE(SOCKTYPE) { #SOCKTYPE, SOCK_##SOCKTYPE }
129
+static struct arg_const socktypes[] = {
130
+    // { "0", 0 },
131
+    CONST_SOCKTYPE(DGRAM),
132
+    // CONST_SOCKTYPE(SEQPACKET),
133
+    // CONST_SOCKTYPE(STREAM),
134
+    // CONST_SOCKTYPE(RAW),
124 135
 };
125
-#define OPTION_PROTOCOL(PROTOCOL) { #PROTOCOL, IPPROTO_##PROTOCOL }
126
-static struct option protocols[] = {
136
+
137
+#define CONST_PROTOCOL(PROTOCOL) { #PROTOCOL, IPPROTO_##PROTOCOL }
138
+static struct arg_const protocols[] = {
127 139
     { "0", 0 },
128
-    OPTION_PROTOCOL(IP),
129
-    OPTION_PROTOCOL(TCP),
130
-    OPTION_PROTOCOL(UDP),
131
-    OPTION_PROTOCOL(UDPLITE),
132
-    OPTION_PROTOCOL(SCTP),
133
-    OPTION_PROTOCOL(ICMP),
140
+    // CONST_PROTOCOL(IP),
141
+    // CONST_PROTOCOL(IPV6),
142
+    // CONST_PROTOCOL(ICMP),
143
+    // CONST_PROTOCOL(TCP),
144
+    // CONST_PROTOCOL(UDP),
145
+    // CONST_PROTOCOL(RAW),
134 146
 };
135 147
 
136 148
 
137 149
 /// Command line interface
138
-void version(FILE * stream) { fprintf(stream, "%s", VERSION); }
139
-void usage  (FILE * stream) { fprintf(stream, "%s", USAGE);   }
140
-void options(FILE * stream) { fprintf(stream, "%s", OPTIONS); }
141
-void args   (FILE * stream) {
142
-    ARGS_PRINT(stream, family,   families ); fprintf(stream, "\n");
143
-    ARGS_PRINT(stream, socktype, socktypes); fprintf(stream, "\n");
144
-    ARGS_PRINT(stream, protocol, protocols);
150
+void version    (FILE * stream) { fprintf(stream, "%s", VERSION);     }
151
+void usage      (FILE * stream) { fprintf(stream, "%s", USAGE);       }
152
+void description(FILE * stream) { fprintf(stream, "%s", DESCRIPTION); }
153
+void options    (FILE * stream) { fprintf(stream, "%s", OPTIONS);     }
154
+void args       (FILE * stream) {
155
+    ARG_PRINT(stream, family,   families ); fprintf(stream, "\n");
156
+    ARG_PRINT(stream, socktype, socktypes); fprintf(stream, "\n");
157
+    ARG_PRINT(stream, protocol, protocols);
145 158
 }
146 159
 void help(FILE * stream) {
147
-    version(stream); fprintf(stream, "\n");
148
-    usage  (stream); fprintf(stream, "\n");
149
-    options(stream); fprintf(stream, "\n");
150
-    args   (stream);
160
+    version    (stream); fprintf(stream, "\n");
161
+    usage      (stream); fprintf(stream, "\n");
162
+    description(stream); fprintf(stream, "\n");
163
+    options    (stream); fprintf(stream, "\n");
164
+    args       (stream);
151 165
 }
152 166
 
153 167
 
154
-/// main
168
+/// Main
169
+
155 170
 int main(int argc, char * argv[]) {
156 171
     // Command line interface
157
-    ARGS_SPECIALS()
158
-    ARGS_DECLARE(role,     NULL)
159
-    ARGS_DECLARE(family,   DEFAULT_FAMILY)
160
-    ARGS_DECLARE(socktype, DEFAULT_SOCKTYPE)
161
-    ARGS_DECLARE(protocol, DEFAULT_PROTOCOL)
162
-    ARGS_DECLARE(node,     DEFAULT_NODE)
163
-    ARGS_DECLARE(service,  DEFAULT_SERVICE)
164
-    ARGS_POSITIONAL(role)
165
-    ARGS_OPTIONS_BEGIN("f:t:p:n:s:")
166
-    ARGS_OPTION_WITH_ARGUMENT('f', family)
167
-    ARGS_OPTION_WITH_ARGUMENT('t', socktype)
168
-    ARGS_OPTION_WITH_ARGUMENT('p', protocol)
169
-    ARGS_OPTION_WITH_ARGUMENT('n', node)
170
-    ARGS_OPTION_WITH_ARGUMENT('s', service)
171
-    ARGS_OPTIONS_END()
172
-    ARGS_CONVERT_FIND(role,   role,     roles)
173
-    ARGS_CONVERT_FIND(option, family,   families)
174
-    ARGS_CONVERT_FIND(option, socktype, socktypes)
175
-    ARGS_CONVERT_FIND(option, protocol, protocols)
176
-    ARGS_CONVERT_NULL(node)
177
-    ARGS_CONVERT_NULL(service)
178
-    ARGS_CATCH()
172
+    ARG_SPECIALS()
173
+    ARG_DECLARE(role,     NULL)
174
+    ARG_DECLARE(family,   DEFAULT_FAMILY)
175
+    ARG_DECLARE(socktype, DEFAULT_SOCKTYPE)
176
+    ARG_DECLARE(protocol, DEFAULT_PROTOCOL)
177
+    ARG_DECLARE(node,     DEFAULT_NODE)
178
+    ARG_DECLARE(service,  DEFAULT_SERVICE)
179
+    ARG_POSITIONAL(role)
180
+    ARG_GETOPT_BEGIN("f:t:p:n:s:")
181
+    ARG_GETOPT_WITH_ARGUMENT('f', family)
182
+    ARG_GETOPT_WITH_ARGUMENT('t', socktype)
183
+    ARG_GETOPT_WITH_ARGUMENT('p', protocol)
184
+    ARG_GETOPT_WITH_ARGUMENT('n', node)
185
+    ARG_GETOPT_WITH_ARGUMENT('s', service)
186
+    ARG_GETOPT_END()
187
+    ARG_CONVERT_FIND(role, role, roles)
188
+    ARG_CONVERT_FIND_CONST(family,   families)
189
+    ARG_CONVERT_FIND_CONST(socktype, socktypes)
190
+    ARG_CONVERT_FIND_CONST(protocol, protocols)
191
+    ARG_CONVERT_NULL(node)
192
+    ARG_CONVERT_NULL(service)
193
+    ARG_CATCH()
179 194
 
180 195
     // Implementation
181
-    report_info(0, "Using buffer size %d", BUF_SIZE);
182
-    role->run(getsockfd(
183
-        role->action_name,
184
-        role->action,
196
+    report_info(0, "Using buffer length %d", BUFLEN_MAX);
197
+    role->run(initsockfd(
198
+        role->init_name,
199
+        role->init,
185 200
         role->flags,
186
-        family->value,
187
-        socktype->value,
188
-        protocol->value,
201
+        family,
202
+        socktype,
203
+        protocol,
189 204
         node,
190 205
         service
191 206
     ));
... ...
@@ -194,10 +209,33 @@ int main(int argc, char * argv[]) {
194 209
 
195 210
 /// Implementation
196 211
 
197
-//// getsockfd
198
-int getsockfd(
199
-    char const * action_name,
200
-    int (*action)(int sockfd, struct sockaddr const * addr, socklen_t addrlen),
212
+//// Common
213
+
214
+// `NI_MAXHOST` and `NI_MAXSERV` are non-standard.
215
+static char gai_host[1025];
216
+static char gai_serv[32];
217
+static int gai_errno;
218
+
219
+#define ACTION_NONE    0
220
+#define ACTION_LEAVE   1
221
+#define ACTION_JOIN    2
222
+#define ACTION_MESSAGE 3
223
+struct action {
224
+    char const * name;
225
+    int value;
226
+};
227
+#define ACTION(ACTION) { #ACTION, ACTION_##ACTION }
228
+static struct action actions[] = {
229
+    ACTION(NONE),
230
+    ACTION(LEAVE),
231
+    ACTION(JOIN),
232
+    ACTION(MESSAGE),
233
+};
234
+
235
+// TODO: Call and print results of `getsockname` and `getpeername`?
236
+int initsockfd(
237
+    char const * init_name,
238
+    int (*init)(int sockfd, struct sockaddr const * addr, socklen_t addrlen),
201 239
     int flags,
202 240
     int family,
203 241
     int socktype,
... ...
@@ -205,63 +243,87 @@ int getsockfd(
205 243
     char const * node,
206 244
     char const * service
207 245
 ) {
208
-    int gai_errno;
209 246
     int sockfd;
247
+    // Initialize socket.
210 248
     {
211 249
         struct addrinfo * addrinfos;
212 250
         // Get addresses.
213 251
         {
214
-            // Populate hints.
215
-            // The extra flags are assumed by the GNU C library if no hints are
216
-            // given (in contradiction to POSIX), and are a good idea.
217
-            struct addrinfo hints;
218
-            memset(&hints, 0, sizeof(hints));
219
-            hints.ai_flags = flags | AI_V4MAPPED | AI_ADDRCONFIG;
220
-            hints.ai_family = family;
221
-            hints.ai_socktype = socktype;
222
-            hints.ai_protocol = protocol;
223
-            // Query.
252
+            // The assumption of these flags are non-standard.
253
+            flags |= AI_V4MAPPED | AI_ADDRCONFIG;
254
+            struct addrinfo hints = {
255
+                flags, family, socktype, protocol, 0, 0, 0, 0
256
+            };
224 257
             if (0 != (gai_errno = getaddrinfo(
225 258
                 node, service, &hints, &addrinfos
226 259
             )))
227
-                report_fatal(gai_errno == EAI_SYSTEM ? errno : 0,
228
-                    "Failed to get addresses for '%s:%s': %s",
229
-                    node, service, gai_strerror(gai_errno)
260
+                report_fatal(
261
+                    gai_errno == EAI_SYSTEM ? errno : 0,
262
+                    "Failed to get %s addresses for '%s:%s': %s",
263
+                    init_name,
264
+                    node    ? node    : "",
265
+                    service ? service : "",
266
+                    gai_strerror(gai_errno)
230 267
                 );
231 268
         }
232
-        // Try action on addresses until one works.
233
-        report_info(0, "Trying to %s...", action_name);
269
+        // Try init on addresses until one works.
234 270
         struct addrinfo * addrinfo;
235 271
         for (
236 272
             addrinfo = addrinfos;
237 273
             addrinfo != NULL;
238 274
             addrinfo = addrinfo->ai_next
239 275
         ) {
276
+            // Get address name.
277
+            if (0 != (gai_errno = getnameinfo(
278
+                addrinfo->ai_addr,
279
+                addrinfo->ai_addrlen,
280
+                gai_host,
281
+                sizeof(gai_host),
282
+                gai_serv,
283
+                sizeof(gai_serv),
284
+                NI_NUMERICHOST | NI_NUMERICSERV
285
+            ))) {
286
+                report_error(
287
+                    gai_errno == EAI_SYSTEM ? errno : 0,
288
+                    "Failed to get %s address name: %s",
289
+                    init_name,
290
+                    gai_strerror(gai_errno)
291
+                );
292
+                continue;
293
+            }
294
+            report_info(0, "Trying to %s to '%s:%s'...",
295
+                init_name, gai_host, gai_serv
296
+            );
240 297
             // Create socket.
241 298
             if (-1 == (sockfd = socket(
242 299
                 addrinfo->ai_family,
243 300
                 addrinfo->ai_socktype,
244 301
                 addrinfo->ai_protocol
245 302
             ))) {
246
-                report_info(errno, "> Failed to create socket");
303
+                report_error(errno, "Failed to create %s socket", init_name);
247 304
                 continue;
248 305
             }
249
-            // Perform action.
250
-            if (-1 == action(
306
+            // Perform init.
307
+            if (-1 == init(
251 308
                 sockfd,
252 309
                 addrinfo->ai_addr,
253 310
                 addrinfo->ai_addrlen
254 311
             )) {
312
+                report_error(errno, "Failed to %s to '%s:%s'",
313
+                    init_name, gai_host, gai_serv
314
+                );
255 315
                 close(sockfd);
256
-                report_info(errno, "> Failed to %s", action_name);
257 316
                 continue;
258 317
             }
259 318
             // Succeeded.
260
-            report_info(0, "Succeeded to %s", action_name);
319
+            report_info(0, "Succeeded to %s to '%s:%s'",
320
+                init_name, gai_host, gai_serv
321
+            );
261 322
             break;
262 323
         }
324
+        // Fail.
263 325
         if (NULL == addrinfo)
264
-            report_fatal(0, "Failed to get socket");
326
+            report_fatal(0, "Failed to %s", init_name);
265 327
         // Clean up.
266 328
         freeaddrinfo(addrinfos);
267 329
     }
... ...
@@ -269,13 +331,13 @@ int getsockfd(
269 331
 }
270 332
 
271 333
 
272
-//// run_server
273
-void run_server(int sockfd) {
274
-    int gai_errno;
334
+//// Server
275 335
 
336
+// TODO: Also store `addlen` in `client` (and use in `memcmp`)?
337
+void run_server(int sockfd) {
276 338
     // Client data.
277 339
     struct client_data {
278
-        char * name;
340
+        char * user_name;
279 341
     };
280 342
 
281 343
     // Client accounting.
... ...
@@ -283,198 +345,242 @@ void run_server(int sockfd) {
283 345
         struct client * next;
284 346
         struct client * prev;
285 347
         struct sockaddr_storage addr;
286
-        struct client_data data;
348
+        void * data;
287 349
     };
288 350
     struct client * clients = NULL;
289 351
 
290
-    // Allocate buffer.
291
-    int buflen;
292
-    char * buf = malloc(BUF_SIZE);
293
-    if (!buf)
294
-        report_fatal(errno, "Failed to allocate buffer of size %d", BUF_SIZE);
352
+    // Allocate socket buffer.
353
+    ssize_t sockbuflen;
354
+    char * sockbuf = malloc(BUFLEN_MAX + 1);
355
+    if (!sockbuf)
356
+        report_fatal(errno, "Failed to allocate %s of size %d",
357
+            "input buffer", BUFLEN_MAX + 1
358
+        );
295 359
 
296 360
     // Talk to clients.
297 361
     while (1) {
298
-        // Peek receive length and address.
299
-        int recvlen;
362
+        // Get client and action.
363
+        // `NI_MAXHOST` and `NI_MAXSERV` are non-standard.
300 364
         struct sockaddr_storage addr;
301
-        socklen_t addrlen = sizeof(addr);
302
-        if (-1 == (recvlen = recvfrom(
303
-            sockfd,
304
-            buf,
305
-            BUF_SIZE - 1,
306
-            MSG_PEEK,
307
-            (struct sockaddr *)&addr,
308
-            &addrlen
309
-        )))
310
-            report_fatal(errno, "Failed to peek receive");
311
-        // Check for too large addresses (guaranteed not to happen).
312
-        if (addrlen > sizeof(addr))
313
-            report_fatal(0,
314
-                "Failed to store address of size %d, can only hold %d",
315
-                addrlen, sizeof(addr)
316
-            );
317
-        // Look up client.
318 365
         struct client * client;
319
-        for (
320
-            client = clients;
321
-            client != NULL;
322
-            client = client->next
323
-        )
324
-            if (0 == memcmp(&client->addr, &addr, sizeof(addr)))
325
-                break;
326
-        // Client left.
327
-        if (!recvlen) {
328
-            if (-1 == recv(sockfd, NULL, 0, 0))
329
-                report_fatal(errno, "Failed to receive leave");
330
-            if (client) {
331
-                // Data.
332
-                report_info(0, "Client '%s' left", client->data.name);
333
-                free(client->data.name);
334
-                // Accounting.
335
-                remque(client);
336
-                free(client);
337
-            }
338
-            continue;
339
-        }
340
-        // Client joined.
341
-        if (!client) {
342
-            // Accounting.
343
-            if (NULL == (client = calloc(1, sizeof(*client))))
344
-                report_fatal(errno, "Failed allocate client of size %d",
345
-                    sizeof(*client)
346
-                );
347
-            insque(client, &clients);
348
-            client->addr = addr;
349
-            // Data.
350
-            buflen = 0;
351
-            // Add open delimiter.
352
-            buflen += snprintf(&buf[buflen], BUF_SIZE - buflen - 1, "[");
353
-            // Get client address node name.
354
-            if (0 != (gai_errno = getnameinfo(
366
+        struct action action;
367
+        {
368
+            // Peek receive.
369
+            ssize_t recvlen;
370
+            socklen_t addrlen = sizeof(addr);
371
+            if (-1 == (recvlen = recvfrom(
372
+                sockfd,
373
+                // TODO
374
+                sockbuf,
375
+                BUFLEN_MAX,
376
+                // NULL,
377
+                // 0,
378
+                MSG_PEEK, // TODO: Also specify `MSG_TRUNC` so that we don't have to give the buffer?
355 379
                 (struct sockaddr *)&addr,
356
-                addrlen,
357
-                &buf[buflen],
358
-                BUF_SIZE - buflen - 1,
359
-                NULL,
360
-                0,
361
-                0
380
+                &addrlen
362 381
             )))
363
-                report_error(gai_errno == EAI_SYSTEM ? errno : 0,
364
-                    "Failed to get client address node name: %s",
365
-                    gai_strerror(gai_errno)
382
+                report_fatal(errno, "Failed to peek receive");
383
+            // Check for too large addresses (guaranteed not to happen).
384
+            if (addrlen > sizeof(addr))
385
+                report_fatal(0,
386
+                    "Failed to store address of size %d, max size is %d",
387
+                    addrlen, sizeof(addr)
366 388
                 );
367
-            buflen += strlen(&buf[buflen]);
368
-            // Add separator.
369
-            buflen += snprintf(&buf[buflen], BUF_SIZE - buflen - 1, ":");
370
-            // Get client address service name.
389
+            // Get client address name.
371 390
             if (0 != (gai_errno = getnameinfo(
372 391
                 (struct sockaddr *)&addr,
373 392
                 addrlen,
374
-                NULL,
375
-                0,
376
-                &buf[buflen],
377
-                BUF_SIZE - buflen - 1,
378
-                0
393
+                gai_host,
394
+                sizeof(gai_host),
395
+                gai_serv,
396
+                sizeof(gai_serv),
397
+                NI_NUMERICHOST | NI_NUMERICSERV
379 398
             )))
380
-                report_error(gai_errno == EAI_SYSTEM ? errno : 0,
381
-                    "Failed to get client address service name: %s",
399
+                report_fatal(
400
+                    gai_errno == EAI_SYSTEM ? errno : 0,
401
+                    "Failed to get client address name: %s",
382 402
                     gai_strerror(gai_errno)
383 403
                 );
384
-            buflen += strlen(&buf[buflen]);
385
-            // Add close delimiter.
386
-            buflen += snprintf(&buf[buflen], BUF_SIZE - buflen - 1, "] ");
387
-            // Get client user name.
388
-            if (-1 == (recvlen = recv(
389
-                sockfd, &buf[buflen], BUF_SIZE - buflen - 1, 0
390
-            )))
391
-                report_fatal(errno, "Failed to receive client user name");
392
-            buflen += recvlen;
393
-            buflen -= buflen && buf[buflen - 1] == '\0';
394
-            // Store.
395
-            buf[buflen++] = '\0';
396
-            client->data.name = strdup(buf);
397
-            report_info(0, "Client '%s' joined", client->data.name);
404
+            // Look up client.
405
+            for (
406
+                client = clients;
407
+                client != NULL;
408
+                client = client->next
409
+            )
410
+                if (0 == memcmp(&client->addr, &addr, sizeof(addr)))
411
+                    break;
412
+            // Look up action.
413
+            action = actions[!!recvlen<<1 | !!client<<0];
414
+            // Report.
415
+            report_info(0, "Received %d byte%s from '%s:%s' %s",
416
+                recvlen,
417
+                recvlen == 1 ? "" : "s",
418
+                gai_host,
419
+                gai_serv,
420
+                action.name
421
+            );
422
+        }
423
+        // No action.
424
+        if (action.value == ACTION_NONE) {
425
+            // Receive none.
426
+            if (-1 == recv(sockfd, NULL, 0, 0))
427
+                report_fatal(errno, "Failed to receive %s", action.name);
428
+            continue;
429
+        }
430
+        // Client left.
431
+        else if (action.value == ACTION_LEAVE) {
432
+            // Receive leave.
433
+            if (-1 == recv(sockfd, NULL, 0, 0))
434
+                report_fatal(errno, "Failed to receive %s", action.name);
435
+            // Client data.
436
+            struct client_data * client_data = client->data;
437
+            free(client_data->user_name);
438
+            free(client_data);
439
+            // Client accounting.
440
+            remque(client);
441
+            free(client);
442
+            // Done.
443
+            continue;
444
+        }
445
+        // Client joined.
446
+        else if (action.value == ACTION_JOIN) {
447
+            sockbuflen = 0;
448
+            // Receive.
449
+            {
450
+                ssize_t recvlen;
451
+                if (-1 == (recvlen = recv(
452
+                    sockfd, sockbuf, BUFLEN_MAX - sockbuflen, 0
453
+                )))
454
+                    report_fatal(errno, "Failed to receive %s", action.name);
455
+                sockbuflen += recvlen;
456
+            }
457
+            // Validate.
458
+            if (!sockbuflen || sockbuf[sockbuflen - 1] != '\0') {
459
+                report_error(0, "Malformed %s", action.name);
460
+            }
461
+            // Client data.
462
+            struct client_data * client_data;
463
+            if (NULL == (client_data = malloc(sizeof(*client_data))))
464
+                report_fatal(errno, "Failed to allocate %s of size %d",
465
+                    "client data", sizeof(*client_data)
466
+                );
467
+            if (NULL == (client_data->user_name = strdup(sockbuf)))
468
+                report_fatal(errno, "Failed to allocate %s",
469
+                    "client user name"
470
+                );
471
+            // Client accounting.
472
+            if (NULL == (client = calloc(1, sizeof(*client))))
473
+                report_fatal(errno, "Failed allocate %s of size %d",
474
+                    "client", sizeof(*client)
475
+                );
476
+            insque(client, &clients);
477
+            client->addr = addr;
478
+            client->data = client_data;
479
+            // Done.
398 480
             continue;
399 481
         }
400 482
         // Client sent message.
401
-        report_info(0, "Client '%s' sent %d byte%s",
402
-            client->data.name, recvlen, recvlen == 1 ? "" : "s"
403
-        );
404
-        buflen = 0;
405
-        // Add client user name and separator.
406
-        buflen += snprintf(
407
-            &buf[buflen], BUF_SIZE - buflen - 1, "%s: ", client->data.name
408
-        );
409
-        // Add message.
410
-        if (-1 == (recvlen = recv(
411
-            sockfd, &buf[buflen], BUF_SIZE - buflen - 1, 0
412
-        )))
413
-            report_fatal(errno, "Failed to receive");
414
-        buflen += recvlen;
415
-        buflen -= buflen && buf[buflen - 1] == '\0';
416
-        // Send message to all clients.
417
-        buf[buflen++] = '\0';
418
-        for (
419
-            client = clients;
420
-            client != NULL;
421
-            client = client->next
422
-        ) {
423
-            if (-1 == sendto(
424
-                sockfd,
425
-                buf,
426
-                buflen,
427
-                0,
428
-                (struct sockaddr *)&client->addr,
429
-                sizeof(client->addr)
430
-            ))
431
-                report_fatal(errno, "Failed to send");
483
+        else if (action.value == ACTION_MESSAGE) {
484
+            sockbuflen = 0;
485
+            // Client data.
486
+            struct client_data * client_data = client->data;
487
+            // TODO: `snprintf` returns how many bytes *would* have been
488
+            // written! Use `strlen`?
489
+            sockbuflen += snprintf(
490
+                &sockbuf[sockbuflen], BUFLEN_MAX - sockbuflen, "[%s:%s] %s: ",
491
+                gai_host, gai_serv, client_data->user_name
492
+            );
493
+            // Receive message.
494
+            {
495
+                ssize_t recvlen;
496
+                if (-1 == (recvlen = recv(
497
+                    sockfd, &sockbuf[sockbuflen], BUFLEN_MAX - sockbuflen, 0
498
+                )))
499
+                    report_fatal(errno, "Failed to receive %s", action.name);
500
+                sockbuflen += recvlen;
501
+            }
502
+            sockbuflen -= sockbuflen && sockbuf[sockbuflen - 1] == '\0';
503
+            // Finalize buffer.
504
+            sockbuf[sockbuflen++] = '\0';
505
+            // Send message.
506
+            for (
507
+                client = clients;
508
+                client != NULL;
509
+                client = client->next
510
+            ) {
511
+                if (-1 == sendto(
512
+                    sockfd,
513
+                    sockbuf,
514
+                    sockbuflen,
515
+                    0,
516
+                    (struct sockaddr *)&client->addr,
517
+                    sizeof(client->addr)
518
+                ))
519
+                    report_fatal(errno, "Failed to send %s", action.name);
520
+            }
521
+        } else {
522
+            report_fatal(0, "Unexpected action %d", action.value);
432 523
         }
433 524
     }
434 525
 }
435 526
 
436 527
 
437
-//// run_client
528
+//// Client
529
+
530
+static struct termios termios_stdin;
531
+static void termios_restore() {
532
+    if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &termios_stdin))
533
+        report_fatal(errno, "Failed to restore terminal settings");
534
+}
535
+static void termios_setup() {
536
+    if (-1 == tcgetattr(STDIN_FILENO, &termios_stdin))
537
+        report_fatal(errno, "Failed to get terminal settings");
538
+    {
539
+        struct termios termios = termios_stdin;
540
+        termios.c_lflag &= ~(ICANON | ECHO);
541
+        if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &termios))
542
+            report_fatal(errno, "Failed to set terminal settings");
543
+    }
544
+    atexit(termios_restore);
545
+}
546
+
438 547
 void run_client(int sockfd) {
439 548
     // Allocate socket buffer.
440
-    int sockbuflen;
441
-    char * sockbuf = malloc(BUF_SIZE);
549
+    ssize_t sockbuflen;
550
+    char * sockbuf = malloc(BUFLEN_MAX + 1);
442 551
     if (!sockbuf)
443
-        report_fatal(errno, "Failed to allocate socket buffer of size %d",
444
-            BUF_SIZE
552
+        report_fatal(errno, "Failed to allocate %s of size %d",
553
+            "input buffer", BUFLEN_MAX + 1
445 554
         );
446 555
 
447 556
     // Allocate input buffer.
448
-    int inputbuflen;
449
-    char * inputbuf = malloc(BUF_SIZE);
557
+    ssize_t inputbuflen;
558
+    char * inputbuf = malloc(BUFLEN_MAX + 1);
450 559
     if (!inputbuf)
451
-        report_fatal(errno, "Failed to allocate input buffer of size %d",
452
-            BUF_SIZE
560
+        report_fatal(errno, "Failed to allocate %s of size %d",
561
+            "socket buffer", BUFLEN_MAX + 1
453 562
         );
454 563
 
455 564
     // Join.
456
-    // Get user name.
457
-    printf("User name: ");
458
-    fflush(stdout);
459
-    if (-1 == (inputbuflen = read(STDIN_FILENO, inputbuf, BUF_SIZE - 1)))
460
-        report_fatal(errno, "Failed to read user name");
461
-    inputbuflen -= inputbuflen && inputbuf[inputbuflen - 1] == '\n';
462
-    // Send join.
463
-    inputbuf[inputbuflen++] = '\0';
464
-    if (-1 == send(sockfd, inputbuf, inputbuflen, 0))
465
-        report_fatal(errno, "Failed to send join");
466
-
467
-    // Setup terminal.
468
-    struct termios termios_stdin;
469
-    if (-1 == tcgetattr(STDIN_FILENO, &termios_stdin))
470
-        report_fatal(errno, "Failed to get terminal settings");
471 565
     {
472
-        struct termios termios = termios_stdin;
473
-        termios.c_lflag &= ~(ICANON | ECHO);
474
-        if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &termios))
475
-            report_fatal(errno, "Failed to set terminal settings");
566
+        struct action action = actions[ACTION_JOIN];
567
+        // Get user name.
568
+        printf("User name: ");
569
+        fflush(stdout);
570
+        if (-1 == (inputbuflen = read(STDIN_FILENO, inputbuf, BUFLEN_MAX)))
571
+            report_fatal(errno, "Failed to read user name");
572
+        inputbuflen -= inputbuflen && inputbuf[inputbuflen - 1] == '\n';
573
+        // Send join.
574
+        inputbuf[inputbuflen++] = '\0';
575
+        if (-1 == send(sockfd, inputbuf, inputbuflen, 0))
576
+            report_fatal(errno, "Failed to send %s", action.name);
577
+        if (-1 == recv(sockfd, sockbuf, BUFLEN_MAX, MSG_PEEK))
578
+            report_fatal(errno, "Failed to receive %s", action.name);
476 579
     }
477 580
 
581
+    // Setup terminal.
582
+    termios_setup();
583
+
478 584
     // Talk to user and server.
479 585
     inputbuflen = 0;
480 586
     while (1) {
... ...
@@ -488,11 +594,11 @@ void run_client(int sockfd) {
488 594
             report_fatal(errno, "Failed to wait for data");
489 595
         }
490 596
         // Clear input line.
491
-        printf("\r%*s\r", inputbuflen, "");
597
+        printf("\r%*s\r", (int)inputbuflen, "");
492 598
         // Socket data available.
493 599
         if (FD_ISSET(sockfd, &rfds)) {
494 600
             // Receive.
495
-            if (-1 == (sockbuflen = recv(sockfd, sockbuf, BUF_SIZE - 1, 0)))
601
+            if (-1 == (sockbuflen = recv(sockfd, sockbuf, BUFLEN_MAX, 0)))
496 602
                 report_fatal(errno, "Failed to receive");
497 603
             sockbuf[sockbuflen++] = '\0';
498 604
             // Print socket line.
... ...
@@ -503,9 +609,9 @@ void run_client(int sockfd) {
503 609
             // Read.
504 610
             char c;
505 611
             if (-1 == read(STDIN_FILENO, &c, 1))
506
-                report_fatal(errno, "Failed to read input");
612
+                report_fatal(errno, "Failed to read");
507 613
             // Printable with non-full buffer.
508
-            if (isprint(c) && inputbuflen < BUF_SIZE - 1) {
614
+            if (isprint(c) && inputbuflen < BUFLEN_MAX) {
509 615
                 // Add.
510 616
                 inputbuf[inputbuflen++] = c;
511 617
             // Backspace.
... ...
@@ -516,8 +622,7 @@ void run_client(int sockfd) {
516 622
             // Enter.
517 623
             } else if (c == '\n' || c == '\r') {
518 624
                 // Send.
519
-                if (inputbuflen)
520
-                    inputbuf[inputbuflen++] = '\0';
625
+                inputbuf[inputbuflen] = '\0';
521 626
                 if (-1 == send(sockfd, inputbuf, inputbuflen, 0))
522 627
                     report_fatal(errno, "Failed to send");
523 628
                 // Quit if input line empty.
... ...
@@ -528,11 +633,8 @@ void run_client(int sockfd) {
528 633
             }
529 634
         }
530 635
         // Print input line.
531
-        printf("%.*s", inputbuflen, inputbuf);
636
+        // TODO: Have we not "finalized" the buffer here?
637
+        printf("%.*s", (int)inputbuflen, inputbuf);
532 638
         fflush(stdout);
533 639
     }
534
-
535
-    // Restore terminal.
536
-    if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &termios_stdin))
537
-        report_fatal(errno, "Failed to restore terminal settings");
538 640
 }
Browse code

WIP: Add implementation

Robert Cranston authored on 28/11/2020 18:28:43
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,538 @@
1
+// TODO: Add `listen` and `accept` for if the socket has a `STREAM` `socktype`.
2
+// TODO: Compare to the example in `getaddrinfo(3)`.
3
+// TODO: Mention `getaddrinfo` and the RFC 3484 sorting order in readme.
4
+// TODO: Change client store from queue to binary tree?
5
+
6
+/// Headers
7
+
8
+//// POSIX 2004
9
+#define _XOPEN_SOURCE 600
10
+
11
+//// C standard library
12
+#include <stdlib.h>
13
+#include <stdio.h>
14
+#include <string.h>
15
+#include <ctype.h>
16
+#include <limits.h>
17
+
18
+//// POSIX
19
+#include <unistd.h>
20
+#include <sys/types.h>
21
+#include <sys/socket.h>
22
+#include <sys/select.h>
23
+#include <netdb.h>
24
+#include <search.h>
25
+#include <termios.h>
26
+
27
+// Other
28
+#include "args.h"
29
+#include "report.h"
30
+
31
+
32
+/// Parameters
33
+
34
+//// Application
35
+#define PROGNAME "sockchat"
36
+#define VERSION_STR "1.0"
37
+#define DESCRIPTION \
38
+    "Chat with unlimited number of peers through a variety of sockets"
39
+
40
+//// Arguments
41
+// TODO
42
+// #define DEFAULT_FAMILY   "UNSPEC"
43
+// #define DEFAULT_SOCKTYPE "0"
44
+#define DEFAULT_FAMILY   "INET"
45
+#define DEFAULT_SOCKTYPE "DGRAM"
46
+#define DEFAULT_PROTOCOL "0"
47
+#define DEFAULT_NODE     ""
48
+#define DEFAULT_SERVICE  "3200"
49
+
50
+//// Command line interface
51
+#define VERSION \
52
+    PROGNAME " " VERSION_STR " - " DESCRIPTION "\n"
53
+#define USAGE \
54
+    "Usage:\n" \
55
+    "  " PROGNAME " (server|client) [options]\n" \
56
+    "  " PROGNAME " -h|--help\n" \
57
+    "  " PROGNAME " --version\n"
58
+#define OPTIONS \
59
+    "Options:\n" \
60
+    "  -f <family>    [default: " DEFAULT_FAMILY   "]\n" \
61
+    "  -t <socktype>  [default: " DEFAULT_SOCKTYPE "]\n" \
62
+    "  -p <protocol>  [default: " DEFAULT_PROTOCOL "]\n" \
63
+    "  -n <node>      [default: " DEFAULT_NODE     "]\n" \
64
+    "  -s <service>   [default: " DEFAULT_SERVICE  "]\n"
65
+
66
+//// Implementation
67
+#define BUF_SIZE _POSIX_MAX_CANON
68
+
69
+
70
+/// Forward declarations
71
+
72
+//// Implementation
73
+int getsockfd(
74
+    char const * action_name,
75
+    int (*action)(int sockfd, struct sockaddr const * addr, socklen_t addrlen),
76
+    int flags,
77
+    int family,
78
+    int socktype,
79
+    int protocol,
80
+    char const * node,
81
+    char const * service
82
+);
83
+void run_server(int sockfd);
84
+void run_client(int sockfd);
85
+
86
+
87
+/// Data
88
+
89
+//// Arguments
90
+struct role {
91
+    char const * name;
92
+    char const * action_name;
93
+    int (*action)(int sockfd, struct sockaddr const * addr, socklen_t addrlen);
94
+    int flags;
95
+    void (*run)(int sockfd);
96
+};
97
+struct option {
98
+    char const * name;
99
+    int value;
100
+};
101
+#define ROLE(NAME, ACTION, FLAGS) { #NAME, #ACTION, ACTION, FLAGS, run_##NAME }
102
+static struct role roles[] = {
103
+    ROLE(server, bind, AI_PASSIVE),
104
+    ROLE(client, connect, 0),
105
+};
106
+#define OPTION_FAMILY(FAMILY) { #FAMILY, AF_##FAMILY }
107
+static struct option families[] = {
108
+    OPTION_FAMILY(UNSPEC),
109
+    OPTION_FAMILY(UNIX),
110
+    OPTION_FAMILY(LOCAL),
111
+    OPTION_FAMILY(INET),
112
+    OPTION_FAMILY(INET6),
113
+    OPTION_FAMILY(PACKET),
114
+};
115
+#define OPTION_SOCKTYPE(SOCKTYPE) { #SOCKTYPE, SOCK_##SOCKTYPE }
116
+static struct option socktypes[] = {
117
+    { "0", 0 },
118
+    OPTION_SOCKTYPE(STREAM),
119
+    OPTION_SOCKTYPE(DGRAM),
120
+    OPTION_SOCKTYPE(SEQPACKET),
121
+    OPTION_SOCKTYPE(RAW),
122
+    OPTION_SOCKTYPE(RDM),
123
+    OPTION_SOCKTYPE(PACKET),
124
+};
125
+#define OPTION_PROTOCOL(PROTOCOL) { #PROTOCOL, IPPROTO_##PROTOCOL }
126
+static struct option protocols[] = {
127
+    { "0", 0 },
128
+    OPTION_PROTOCOL(IP),
129
+    OPTION_PROTOCOL(TCP),
130
+    OPTION_PROTOCOL(UDP),
131
+    OPTION_PROTOCOL(UDPLITE),
132
+    OPTION_PROTOCOL(SCTP),
133
+    OPTION_PROTOCOL(ICMP),
134
+};
135
+
136
+
137
+/// Command line interface
138
+void version(FILE * stream) { fprintf(stream, "%s", VERSION); }
139
+void usage  (FILE * stream) { fprintf(stream, "%s", USAGE);   }
140
+void options(FILE * stream) { fprintf(stream, "%s", OPTIONS); }
141
+void args   (FILE * stream) {
142
+    ARGS_PRINT(stream, family,   families ); fprintf(stream, "\n");
143
+    ARGS_PRINT(stream, socktype, socktypes); fprintf(stream, "\n");
144
+    ARGS_PRINT(stream, protocol, protocols);
145
+}
146
+void help(FILE * stream) {
147
+    version(stream); fprintf(stream, "\n");
148
+    usage  (stream); fprintf(stream, "\n");
149
+    options(stream); fprintf(stream, "\n");
150
+    args   (stream);
151
+}
152
+
153
+
154
+/// main
155
+int main(int argc, char * argv[]) {
156
+    // Command line interface
157
+    ARGS_SPECIALS()
158
+    ARGS_DECLARE(role,     NULL)
159
+    ARGS_DECLARE(family,   DEFAULT_FAMILY)
160
+    ARGS_DECLARE(socktype, DEFAULT_SOCKTYPE)
161
+    ARGS_DECLARE(protocol, DEFAULT_PROTOCOL)
162
+    ARGS_DECLARE(node,     DEFAULT_NODE)
163
+    ARGS_DECLARE(service,  DEFAULT_SERVICE)
164
+    ARGS_POSITIONAL(role)
165
+    ARGS_OPTIONS_BEGIN("f:t:p:n:s:")
166
+    ARGS_OPTION_WITH_ARGUMENT('f', family)
167
+    ARGS_OPTION_WITH_ARGUMENT('t', socktype)
168
+    ARGS_OPTION_WITH_ARGUMENT('p', protocol)
169
+    ARGS_OPTION_WITH_ARGUMENT('n', node)
170
+    ARGS_OPTION_WITH_ARGUMENT('s', service)
171
+    ARGS_OPTIONS_END()
172
+    ARGS_CONVERT_FIND(role,   role,     roles)
173
+    ARGS_CONVERT_FIND(option, family,   families)
174
+    ARGS_CONVERT_FIND(option, socktype, socktypes)
175
+    ARGS_CONVERT_FIND(option, protocol, protocols)
176
+    ARGS_CONVERT_NULL(node)
177
+    ARGS_CONVERT_NULL(service)
178
+    ARGS_CATCH()
179
+
180
+    // Implementation
181
+    report_info(0, "Using buffer size %d", BUF_SIZE);
182
+    role->run(getsockfd(
183
+        role->action_name,
184
+        role->action,
185
+        role->flags,
186
+        family->value,
187
+        socktype->value,
188
+        protocol->value,
189
+        node,
190
+        service
191
+    ));
192
+}
193
+
194
+
195
+/// Implementation
196
+
197
+//// getsockfd
198
+int getsockfd(
199
+    char const * action_name,
200
+    int (*action)(int sockfd, struct sockaddr const * addr, socklen_t addrlen),
201
+    int flags,
202
+    int family,
203
+    int socktype,
204
+    int protocol,
205
+    char const * node,
206
+    char const * service
207
+) {
208
+    int gai_errno;
209
+    int sockfd;
210
+    {
211
+        struct addrinfo * addrinfos;
212
+        // Get addresses.
213
+        {
214
+            // Populate hints.
215
+            // The extra flags are assumed by the GNU C library if no hints are
216
+            // given (in contradiction to POSIX), and are a good idea.
217
+            struct addrinfo hints;
218
+            memset(&hints, 0, sizeof(hints));
219
+            hints.ai_flags = flags | AI_V4MAPPED | AI_ADDRCONFIG;
220
+            hints.ai_family = family;
221
+            hints.ai_socktype = socktype;
222
+            hints.ai_protocol = protocol;
223
+            // Query.
224
+            if (0 != (gai_errno = getaddrinfo(
225
+                node, service, &hints, &addrinfos
226
+            )))
227
+                report_fatal(gai_errno == EAI_SYSTEM ? errno : 0,
228
+                    "Failed to get addresses for '%s:%s': %s",
229
+                    node, service, gai_strerror(gai_errno)
230
+                );
231
+        }
232
+        // Try action on addresses until one works.
233
+        report_info(0, "Trying to %s...", action_name);
234
+        struct addrinfo * addrinfo;
235
+        for (
236
+            addrinfo = addrinfos;
237
+            addrinfo != NULL;
238
+            addrinfo = addrinfo->ai_next
239
+        ) {
240
+            // Create socket.
241
+            if (-1 == (sockfd = socket(
242
+                addrinfo->ai_family,
243
+                addrinfo->ai_socktype,
244
+                addrinfo->ai_protocol
245
+            ))) {
246
+                report_info(errno, "> Failed to create socket");
247
+                continue;
248
+            }
249
+            // Perform action.
250
+            if (-1 == action(
251
+                sockfd,
252
+                addrinfo->ai_addr,
253
+                addrinfo->ai_addrlen
254
+            )) {
255
+                close(sockfd);
256
+                report_info(errno, "> Failed to %s", action_name);
257
+                continue;
258
+            }
259
+            // Succeeded.
260
+            report_info(0, "Succeeded to %s", action_name);
261
+            break;
262
+        }
263
+        if (NULL == addrinfo)
264
+            report_fatal(0, "Failed to get socket");
265
+        // Clean up.
266
+        freeaddrinfo(addrinfos);
267
+    }
268
+    return sockfd;
269
+}
270
+
271
+
272
+//// run_server
273
+void run_server(int sockfd) {
274
+    int gai_errno;
275
+
276
+    // Client data.
277
+    struct client_data {
278
+        char * name;
279
+    };
280
+
281
+    // Client accounting.
282
+    struct client {
283
+        struct client * next;
284
+        struct client * prev;
285
+        struct sockaddr_storage addr;
286
+        struct client_data data;
287
+    };
288
+    struct client * clients = NULL;
289
+
290
+    // Allocate buffer.
291
+    int buflen;
292
+    char * buf = malloc(BUF_SIZE);
293
+    if (!buf)
294
+        report_fatal(errno, "Failed to allocate buffer of size %d", BUF_SIZE);
295
+
296
+    // Talk to clients.
297
+    while (1) {
298
+        // Peek receive length and address.
299
+        int recvlen;
300
+        struct sockaddr_storage addr;
301
+        socklen_t addrlen = sizeof(addr);
302
+        if (-1 == (recvlen = recvfrom(
303
+            sockfd,
304
+            buf,
305
+            BUF_SIZE - 1,
306
+            MSG_PEEK,
307
+            (struct sockaddr *)&addr,
308
+            &addrlen
309
+        )))
310
+            report_fatal(errno, "Failed to peek receive");
311
+        // Check for too large addresses (guaranteed not to happen).
312
+        if (addrlen > sizeof(addr))
313
+            report_fatal(0,
314
+                "Failed to store address of size %d, can only hold %d",
315
+                addrlen, sizeof(addr)
316
+            );
317
+        // Look up client.
318
+        struct client * client;
319
+        for (
320
+            client = clients;
321
+            client != NULL;
322
+            client = client->next
323
+        )
324
+            if (0 == memcmp(&client->addr, &addr, sizeof(addr)))
325
+                break;
326
+        // Client left.
327
+        if (!recvlen) {
328
+            if (-1 == recv(sockfd, NULL, 0, 0))
329
+                report_fatal(errno, "Failed to receive leave");
330
+            if (client) {
331
+                // Data.
332
+                report_info(0, "Client '%s' left", client->data.name);
333
+                free(client->data.name);
334
+                // Accounting.
335
+                remque(client);
336
+                free(client);
337
+            }
338
+            continue;
339
+        }
340
+        // Client joined.
341
+        if (!client) {
342
+            // Accounting.
343
+            if (NULL == (client = calloc(1, sizeof(*client))))
344
+                report_fatal(errno, "Failed allocate client of size %d",
345
+                    sizeof(*client)
346
+                );
347
+            insque(client, &clients);
348
+            client->addr = addr;
349
+            // Data.
350
+            buflen = 0;
351
+            // Add open delimiter.
352
+            buflen += snprintf(&buf[buflen], BUF_SIZE - buflen - 1, "[");
353
+            // Get client address node name.
354
+            if (0 != (gai_errno = getnameinfo(
355
+                (struct sockaddr *)&addr,
356
+                addrlen,
357
+                &buf[buflen],
358
+                BUF_SIZE - buflen - 1,
359
+                NULL,
360
+                0,
361
+                0
362
+            )))
363
+                report_error(gai_errno == EAI_SYSTEM ? errno : 0,
364
+                    "Failed to get client address node name: %s",
365
+                    gai_strerror(gai_errno)
366
+                );
367
+            buflen += strlen(&buf[buflen]);
368
+            // Add separator.
369
+            buflen += snprintf(&buf[buflen], BUF_SIZE - buflen - 1, ":");
370
+            // Get client address service name.
371
+            if (0 != (gai_errno = getnameinfo(
372
+                (struct sockaddr *)&addr,
373
+                addrlen,
374
+                NULL,
375
+                0,
376
+                &buf[buflen],
377
+                BUF_SIZE - buflen - 1,
378
+                0
379
+            )))
380
+                report_error(gai_errno == EAI_SYSTEM ? errno : 0,
381
+                    "Failed to get client address service name: %s",
382
+                    gai_strerror(gai_errno)
383
+                );
384
+            buflen += strlen(&buf[buflen]);
385
+            // Add close delimiter.
386
+            buflen += snprintf(&buf[buflen], BUF_SIZE - buflen - 1, "] ");
387
+            // Get client user name.
388
+            if (-1 == (recvlen = recv(
389
+                sockfd, &buf[buflen], BUF_SIZE - buflen - 1, 0
390
+            )))
391
+                report_fatal(errno, "Failed to receive client user name");
392
+            buflen += recvlen;
393
+            buflen -= buflen && buf[buflen - 1] == '\0';
394
+            // Store.
395
+            buf[buflen++] = '\0';
396
+            client->data.name = strdup(buf);
397
+            report_info(0, "Client '%s' joined", client->data.name);
398
+            continue;
399
+        }
400
+        // Client sent message.
401
+        report_info(0, "Client '%s' sent %d byte%s",
402
+            client->data.name, recvlen, recvlen == 1 ? "" : "s"
403
+        );
404
+        buflen = 0;
405
+        // Add client user name and separator.
406
+        buflen += snprintf(
407
+            &buf[buflen], BUF_SIZE - buflen - 1, "%s: ", client->data.name
408
+        );
409
+        // Add message.
410
+        if (-1 == (recvlen = recv(
411
+            sockfd, &buf[buflen], BUF_SIZE - buflen - 1, 0
412
+        )))
413
+            report_fatal(errno, "Failed to receive");
414
+        buflen += recvlen;
415
+        buflen -= buflen && buf[buflen - 1] == '\0';
416
+        // Send message to all clients.
417
+        buf[buflen++] = '\0';
418
+        for (
419
+            client = clients;
420
+            client != NULL;
421
+            client = client->next
422
+        ) {
423
+            if (-1 == sendto(
424
+                sockfd,
425
+                buf,
426
+                buflen,
427
+                0,
428
+                (struct sockaddr *)&client->addr,
429
+                sizeof(client->addr)
430
+            ))
431
+                report_fatal(errno, "Failed to send");
432
+        }
433
+    }
434
+}
435
+
436
+
437
+//// run_client
438
+void run_client(int sockfd) {
439
+    // Allocate socket buffer.
440
+    int sockbuflen;
441
+    char * sockbuf = malloc(BUF_SIZE);
442
+    if (!sockbuf)
443
+        report_fatal(errno, "Failed to allocate socket buffer of size %d",
444
+            BUF_SIZE
445
+        );
446
+
447
+    // Allocate input buffer.
448
+    int inputbuflen;
449
+    char * inputbuf = malloc(BUF_SIZE);
450
+    if (!inputbuf)
451
+        report_fatal(errno, "Failed to allocate input buffer of size %d",
452
+            BUF_SIZE
453
+        );
454
+
455
+    // Join.
456
+    // Get user name.
457
+    printf("User name: ");
458
+    fflush(stdout);
459
+    if (-1 == (inputbuflen = read(STDIN_FILENO, inputbuf, BUF_SIZE - 1)))
460
+        report_fatal(errno, "Failed to read user name");
461
+    inputbuflen -= inputbuflen && inputbuf[inputbuflen - 1] == '\n';
462
+    // Send join.
463
+    inputbuf[inputbuflen++] = '\0';
464
+    if (-1 == send(sockfd, inputbuf, inputbuflen, 0))
465
+        report_fatal(errno, "Failed to send join");
466
+
467
+    // Setup terminal.
468
+    struct termios termios_stdin;
469
+    if (-1 == tcgetattr(STDIN_FILENO, &termios_stdin))
470
+        report_fatal(errno, "Failed to get terminal settings");
471
+    {
472
+        struct termios termios = termios_stdin;
473
+        termios.c_lflag &= ~(ICANON | ECHO);
474
+        if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &termios))
475
+            report_fatal(errno, "Failed to set terminal settings");
476
+    }
477
+
478
+    // Talk to user and server.
479
+    inputbuflen = 0;
480
+    while (1) {
481
+        // Wait for data to be available from input or socket.
482
+        fd_set rfds;
483
+        FD_ZERO(&rfds);
484
+        FD_SET(STDIN_FILENO, &rfds);
485
+        FD_SET(sockfd, &rfds);
486
+        if (-1 == select(sockfd+1, &rfds, NULL, NULL, NULL)) {
487
+            printf("\n");
488
+            report_fatal(errno, "Failed to wait for data");
489
+        }
490
+        // Clear input line.
491
+        printf("\r%*s\r", inputbuflen, "");
492
+        // Socket data available.
493
+        if (FD_ISSET(sockfd, &rfds)) {
494
+            // Receive.
495
+            if (-1 == (sockbuflen = recv(sockfd, sockbuf, BUF_SIZE - 1, 0)))
496
+                report_fatal(errno, "Failed to receive");
497
+            sockbuf[sockbuflen++] = '\0';
498
+            // Print socket line.
499
+            printf("%s\n", sockbuf);
500
+        }
501
+        // Input data available.
502
+        if (FD_ISSET(STDIN_FILENO, &rfds)) {
503
+            // Read.
504
+            char c;
505
+            if (-1 == read(STDIN_FILENO, &c, 1))
506
+                report_fatal(errno, "Failed to read input");
507
+            // Printable with non-full buffer.
508
+            if (isprint(c) && inputbuflen < BUF_SIZE - 1) {
509
+                // Add.
510
+                inputbuf[inputbuflen++] = c;
511
+            // Backspace.
512
+            } else if (c == '\b' || c == 127) {
513
+                // Remove last character from buffer.
514
+                if (inputbuflen)
515
+                    --inputbuflen;
516
+            // Enter.
517
+            } else if (c == '\n' || c == '\r') {
518
+                // Send.
519
+                if (inputbuflen)
520
+                    inputbuf[inputbuflen++] = '\0';
521
+                if (-1 == send(sockfd, inputbuf, inputbuflen, 0))
522
+                    report_fatal(errno, "Failed to send");
523
+                // Quit if input line empty.
524
+                if (!inputbuflen)
525
+                    break;
526
+                // Reset input line.
527
+                inputbuflen = 0;
528
+            }
529
+        }
530
+        // Print input line.
531
+        printf("%.*s", inputbuflen, inputbuf);
532
+        fflush(stdout);
533
+    }
534
+
535
+    // Restore terminal.
536
+    if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &termios_stdin))
537
+        report_fatal(errno, "Failed to restore terminal settings");
538
+}