• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 13741295555

08 Mar 2025 07:54PM UTC coverage: 92.319% (+0.08%) from 92.236%
13741295555

push

github

nickg
Pass mir_context_t around explicitly

33 of 38 new or added lines in 3 files covered. (86.84%)

542 existing lines in 5 files now uncovered.

68074 of 73738 relevant lines covered (92.32%)

433184.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.39
/src/server.c
1
//
2
//  Copyright (C) 2023  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "hash.h"
20
#include "ident.h"
21
#include "jit/jit.h"
22
#include "option.h"
23
#include "phase.h"
24
#include "rt/shell.h"
25
#include "server.h"
26
#include "sha1.h"
27
#include "thread.h"
28

29
#include <assert.h>
30
#include <errno.h>
31
#include <string.h>
32
#include <stdlib.h>
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
#include <sys/fcntl.h>
36
#include <unistd.h>
37
#include <fcntl.h>
38
#include <time.h>
39
#include <jansson.h>
40

41
#ifdef __MINGW32__
42
#define WIN32_LEAN_AND_MEAN
43
#include <winsock2.h>
44
#else
45
#include <sys/select.h>
46
#include <sys/socket.h>
47
#include <netinet/in.h>
48
#endif
49

50
#define WS_UPGRADE_VALUE     "websocket"
51
#define WS_WEBSOCKET_VERSION "13"
52
#define WS_GUID              "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
53
#define WS_GUID_LEN          36
54
#define WS_KEY_LEN           24
55
#define WS_KEY_GUID_LEN      (WS_KEY_LEN + WS_GUID_LEN)
56

57
#define HTTP_SWITCHING_PROTOCOLS   101
58
#define HTTP_OK                    200
59
#define HTTP_BAD_REQUEST           400
60
#define HTTP_NOT_FOUND             404
61
#define HTTP_METHOD_NOT_ALLOWED    405
62
#define HTTP_UPGRADE_REQUIRED      426
63
#define HTTP_INTERNAL_SERVER_ERROR 500
64

65
#define WS_OPCODE_TEXT_FRAME   0x1
66
#define WS_OPCODE_BINARY_FRAME 0x2
67
#define WS_OPCODE_CLOSE_FRAME  0x8
68
#define WS_OPCODE_PING_FRAME   0x9
69
#define WS_OPCODE_PONG_FRAME   0xa
70

71
#define MAX_HTTP_REQUEST 1024
72

73
#ifndef __MINGW32__
74
#define closesocket close
75
#endif
76

77
typedef struct _debug_server debug_server_t;
78

79
typedef struct _web_socket {
80
   int           sock;
81
   bool          mask;
82
   bool          closing;
83
   ws_handler_t  handler;
84
   size_t        tx_size;
85
   size_t        tx_wptr;
86
   size_t        tx_rptr;
87
   uint8_t      *tx_buf;
88
   size_t        rx_size;
89
   size_t        rx_wptr;
90
   size_t        rx_rptr;
91
   uint8_t      *rx_buf;
92
} web_socket_t;
93

94
typedef struct _packet_buf {
95
   char  *buf;
96
   size_t alloc;
97
   size_t wptr;
98
   size_t rptr;
99
} packet_buf_t;
100

101
typedef struct {
102
   debug_server_t *(*new_server)(void);
103
   void (*free_server)(debug_server_t *);
104
   void (*new_connection)(debug_server_t *, int);
105
   int (*fill_fd_set)(debug_server_t *, fd_set *, fd_set *);
106
   void (*poll_sockets)(debug_server_t *, fd_set *, fd_set *);
107
   void (*shutdown)(debug_server_t *);
108
} server_proto_t;
109

110
typedef struct _debug_server {
111
   const server_proto_t *proto;
112
   tcl_shell_t          *shell;
113
   bool                  shutdown;
114
   bool                  banner;
115
   int                   sock;
116
   tree_t                top;
117
   packet_buf_t         *packetbuf;
118
   const char           *init_cmd;
119
} debug_server_t;
120

121
typedef struct {
122
   debug_server_t  server;
123
   web_socket_t   *websocket;
124
} http_server_t;
125

126
typedef struct {
127
   debug_server_t server;
128
   int            sock;
129
   size_t         rx_size;
130
   size_t         rx_wptr;
131
   size_t         rx_rptr;
132
   char          *rx_buf;
133
   size_t         tx_size;
134
   size_t         tx_wptr;
135
   size_t         tx_rptr;
136
   uint8_t       *tx_buf;
137
} cxxrtl_server_t;
138

139
////////////////////////////////////////////////////////////////////////////////
140
// WebSocket wrapper
141

142
web_socket_t *ws_new(int sock, const ws_handler_t *handler, bool mask)
14✔
143
{
144
   web_socket_t *ws = xcalloc(sizeof(web_socket_t));
14✔
145
   ws->sock    = sock;
14✔
146
   ws->mask    = mask;
14✔
147
   ws->handler = *handler;
14✔
148

149
   return ws;
14✔
150
}
151

152
void ws_free(web_socket_t *ws)
14✔
153
{
154
   free(ws->tx_buf);
14✔
155
   free(ws->rx_buf);
14✔
156
   free(ws);
14✔
157
}
14✔
158

159
static void ws_queue_buf(web_socket_t *ws, const void *data, size_t size)
649✔
160
{
161
   if (ws->tx_wptr + size > ws->tx_size) {
649✔
162
      ws->tx_size = MAX(ws->tx_size + size, 1024);
555✔
163
      ws->tx_buf = xrealloc(ws->tx_buf, ws->tx_size);
555✔
164
   }
165

166
   memcpy(ws->tx_buf + ws->tx_wptr, data, size);
649✔
167
   ws->tx_wptr += size;
649✔
168
}
649✔
169

170
static void ws_send(web_socket_t *ws, int opcode, const void *data, size_t size)
37✔
171
{
172
   const uint8_t size0 = (size < 126 ? size : (size <= UINT16_MAX ? 126 : 127));
37✔
173

174
   const uint8_t header[2] = {
37✔
175
      0x80 | opcode,
37✔
176
      (ws->mask ? 0x80 : 0x00) | (size0 & 0x7f),
37✔
177
   };
178
   ws_queue_buf(ws, header, sizeof(header));
37✔
179

180
   if (size0 == 126) {
37✔
181
      const uint8_t extlength[2] = { PACK_BE16(size) };
2✔
182
      ws_queue_buf(ws, extlength, sizeof(extlength));
2✔
183
   }
184
   else if (size0 == 127) {
35✔
185
      const uint8_t extlength[8] = { PACK_BE64(size) };
2✔
186
      ws_queue_buf(ws, extlength, sizeof(extlength));
2✔
187
   }
188

189
   if (ws->mask) {
37✔
190
      const int key = rand();
16✔
191
      const uint8_t masks[4] = { PACK_BE32(key) };
16✔
192
      ws_queue_buf(ws, masks, sizeof(masks));
16✔
193

194
      char xord[128];
16✔
195
      for (size_t i = 0; i < size; i += sizeof(xord)) {
593✔
196
         const size_t chunksz = MIN(sizeof(xord), size - i);
577✔
197
         for (int j = 0; j < chunksz; j++)
72,663✔
198
            xord[j] = ((const uint8_t *)data)[i + j] ^ masks[(i + j) % 4];
72,086✔
199

200
         ws_queue_buf(ws, xord, chunksz);
577✔
201
      }
202
   }
203
   else if (size > 0)
21✔
204
      ws_queue_buf(ws, data, size);
15✔
205
}
37✔
206

207
void ws_send_binary(web_socket_t *ws, const void *data, size_t size)
12✔
208
{
209
   ws_send(ws, WS_OPCODE_BINARY_FRAME, data, size);
12✔
210
}
12✔
211

212
void ws_send_packet(web_socket_t *ws, packet_buf_t *pb)
7✔
213
{
214
   ws_send_binary(ws, pb->buf, pb->wptr);
7✔
215
}
7✔
216

217
void ws_send_text(web_socket_t *ws, const char *text)
13✔
218
{
219
   ws_send(ws, WS_OPCODE_TEXT_FRAME, text, strlen(text));
13✔
220
}
13✔
221

222
void ws_send_close(web_socket_t *ws)
6✔
223
{
224
   ws_send(ws, WS_OPCODE_CLOSE_FRAME, NULL, 0);
6✔
225
}
6✔
226

227
void ws_send_ping(web_socket_t *ws, const void *data, size_t size)
3✔
228
{
229
   ws_send(ws, WS_OPCODE_PING_FRAME, data, size);
3✔
230
}
3✔
231

232
void ws_flush(web_socket_t *ws)
33✔
233
{
234
   while (ws->tx_wptr != ws->tx_rptr) {
66✔
235
      const size_t chunksz = ws->tx_wptr - ws->tx_rptr;
33✔
236
      const ssize_t nbytes =
33✔
237
         send(ws->sock, (char *)ws->tx_buf + ws->tx_rptr, chunksz, 0);
33✔
238

239
      if (nbytes == 0)
33✔
240
         break;
241
      else if (nbytes < 0) {
33✔
242
         ws->closing = true;
×
243
         break;
×
244
      }
245

246
      ws->tx_rptr += nbytes;
33✔
247
   }
248

249
   if (ws->tx_wptr == ws->tx_rptr)
33✔
250
      ws->tx_rptr = ws->tx_wptr = 0;
33✔
251
}
33✔
252

253
void ws_poll(web_socket_t *ws)
39✔
254
{
255
 read_more:
256
   if (ws->rx_size - ws->rx_wptr < 1024)
175✔
257
      ws->rx_buf = xrealloc(ws->rx_buf, (ws->rx_size += 1024));
150✔
258

259
   const ssize_t nbytes = recv(ws->sock, (char *)ws->rx_buf + ws->rx_wptr,
350✔
260
                               ws->rx_size - ws->rx_wptr - 1, 0);
175✔
261
   if (nbytes == -1 && errno == EAGAIN)
175✔
262
      return;
263
   else if (nbytes <= 0) {
175✔
264
      ws->closing = true;
6✔
265
      return;
6✔
266
   }
267

268
   ws->rx_wptr += nbytes;
169✔
269
   assert(ws->rx_wptr <= ws->rx_size);
169✔
270
   assert(ws->rx_rptr < ws->rx_wptr);
169✔
271

272
   do {
173✔
273
      const size_t rbytes = ws->rx_wptr - ws->rx_rptr;
173✔
274

275
      if (rbytes < 2)
173✔
276
         goto read_more;   // Not enough for WebSocket header
×
277

278
      uint8_t *frame = ws->rx_buf + ws->rx_rptr;
173✔
279

280
      // Frame format
281
      //
282
      //   0    1     2     3     4 5 6 7  8     9 A B C D E F
283
      //   FIN  RSV1  RSV2  RSV3  Opcode   Mask  Payload length
284
      //   Extended payload length (optional)
285
      //   Masking key (optional)
286
      //   Payload data
287

288
      const bool fin = frame[0] & 0x80;
173✔
289
      const int opcode = frame[0] & 0xf;
173✔
290
      const bool mask = frame[1] & 0x80;
173✔
291
      const int size0 = frame[1] & 0x7f;
173✔
292

293
      size_t headersz = 2 + (mask ? 4 : 0);
173✔
294
      if (size0 == 126)
173✔
295
         headersz += 2;
4✔
296
      else if (size0 == 127)
169✔
297
         headersz += 8;
136✔
298

299
      if (rbytes < headersz)
173✔
300
         goto read_more;   // Not enough for extended header
×
301

302
      int flength = size0;
173✔
303
      if (size0 == 127)
173✔
304
         flength = UNPACK_BE64(frame + 2);
136✔
305
      else if (size0 == 126)
37✔
306
         flength = UNPACK_BE16(frame + 2);
4✔
307

308
      if (rbytes < flength + headersz)
173✔
309
         goto read_more;   // Not enough for full frame
136✔
310

311
      assert(fin);
37✔
312
      (void)fin;
37✔
313

314
      if (mask) {
37✔
315
         for (int i = 0; i < flength; i++)
72,102✔
316
            frame[headersz + i] ^= frame[headersz - 4 + (i % 4)];
72,086✔
317
      }
318

319
      void *payload = frame + headersz;
37✔
320

321
      switch (opcode) {
37✔
322
      case WS_OPCODE_TEXT_FRAME:
13✔
323
         {
324
            char *text = payload;
13✔
325
            assert(text + flength < (char *)ws->rx_buf + ws->rx_size);
13✔
326
            text[flength] = '\0';
13✔
327

328
            if (ws->handler.text_frame != NULL)
13✔
329
               (*ws->handler.text_frame)(ws, text, ws->handler.context);
13✔
330
         }
331
         break;
332

333
      case WS_OPCODE_BINARY_FRAME:
12✔
334
         if (ws->handler.binary_frame != NULL)
12✔
335
            (*ws->handler.binary_frame)(ws, payload, flength,
12✔
336
                                        ws->handler.context);
337
         break;
338

339
      case WS_OPCODE_CLOSE_FRAME:
6✔
340
         ws->closing = true;
6✔
341
         break;
6✔
342

343
      case WS_OPCODE_PING_FRAME:
3✔
344
         ws_send(ws, WS_OPCODE_PONG_FRAME, payload, flength);
3✔
345
         break;
3✔
346

347
      case WS_OPCODE_PONG_FRAME:
3✔
348
         if (ws->handler.pong_frame != NULL)
3✔
349
            (*ws->handler.pong_frame)(ws, payload, flength,
3✔
350
                                      ws->handler.context);
351
         break;
352

353
      default:
×
354
         DEBUG_ONLY(fatal_trace("unhandled WebSocket opcode %02x", opcode));
355
         break;
37✔
356
      }
357

358
      ws->rx_rptr += flength + headersz;
37✔
359
   } while (ws->rx_rptr < ws->rx_wptr);
37✔
360

361
   ws->rx_rptr = ws->rx_wptr = 0;
33✔
362
}
363

364
bool ws_closing(web_socket_t *ws)
6✔
365
{
366
   return ws->closing;
6✔
367
}
368

369
////////////////////////////////////////////////////////////////////////////////
370
// Packet buffers
371

372
static packet_buf_t *pb_new(void)
7✔
373
{
374
   packet_buf_t *pb = xcalloc(sizeof(packet_buf_t));
7✔
375
   pb->alloc = 128;
7✔
376
   pb->buf = xmalloc(pb->alloc);
7✔
377

378
   return pb;
7✔
379
}
380

381
static void pb_free(packet_buf_t *pb)
7✔
382
{
383
   free(pb->buf);
7✔
384
   free(pb);
7✔
385
}
7✔
386

387
static void pb_grow(packet_buf_t *pb, size_t need)
21✔
388
{
389
   if (pb->wptr + need > pb->alloc) {
21✔
390
      pb->alloc = MAX(pb->wptr + need, pb->alloc * 2);
×
391
      pb->buf = xrealloc(pb->buf, pb->alloc);
×
392
   }
393
}
21✔
394

395
static void pb_pack_u8(packet_buf_t *pb, uint8_t value)
7✔
396
{
397
   pb_grow(pb, 1);
7✔
398
   pb->buf[pb->wptr++] = value;
7✔
399
}
7✔
400

401
static void pb_pack_u16(packet_buf_t *pb, uint16_t value)
6✔
402
{
403
   pb_grow(pb, 2);
6✔
404
   pb->buf[pb->wptr++] = value >> 8;
6✔
405
   pb->buf[pb->wptr++] = value & 0xff;
6✔
406
}
6✔
407

408
static void pb_pack_u32(packet_buf_t *pb, uint32_t value)
×
409
{
410
   pb_grow(pb, 4);
×
411
   pb->buf[pb->wptr++] = (value >> 24) & 0xff;
×
412
   pb->buf[pb->wptr++] = (value >> 16) & 0xff;
×
413
   pb->buf[pb->wptr++] = (value >> 8) & 0xff;
×
414
   pb->buf[pb->wptr++] = value & 0xff;
×
415
}
×
416

417
static void pb_pack_u64(packet_buf_t *pb, uint64_t value)
2✔
418
{
419
   pb_grow(pb, 8);
2✔
420
   pb->buf[pb->wptr++] = (value >> 56) & 0xff;
2✔
421
   pb->buf[pb->wptr++] = (value >> 48) & 0xff;
2✔
422
   pb->buf[pb->wptr++] = (value >> 40) & 0xff;
2✔
423
   pb->buf[pb->wptr++] = (value >> 32) & 0xff;
2✔
424
   pb->buf[pb->wptr++] = (value >> 24) & 0xff;
2✔
425
   pb->buf[pb->wptr++] = (value >> 16) & 0xff;
2✔
426
   pb->buf[pb->wptr++] = (value >> 8) & 0xff;
2✔
427
   pb->buf[pb->wptr++] = value & 0xff;
2✔
428
}
2✔
429

430
static void pb_pack_bytes(packet_buf_t *pb, const void *data, size_t len)
6✔
431
{
432
   pb_grow(pb, len);
6✔
433
   memcpy(pb->buf + pb->wptr, data, len);
6✔
434
   pb->wptr += len;
6✔
435
}
6✔
436

437
static void pb_pack_str(packet_buf_t *pb, const char *str)
3✔
438
{
439
   const size_t len = strlen(str);
3✔
440
   assert(len < UINT16_MAX);
3✔
441

442
   pb_pack_u16(pb, len);
3✔
443
   pb_pack_bytes(pb, str, len);
3✔
444
}
3✔
445

446
static void pb_pack_ident(packet_buf_t *pb, ident_t ident)
3✔
447
{
448
   const size_t len = ident_len(ident);
3✔
449
   assert(len < UINT16_MAX);
3✔
450

451
   pb_pack_u16(pb, len);
3✔
452
   pb_pack_bytes(pb, istr(ident), len);
3✔
453
}
3✔
454

455
////////////////////////////////////////////////////////////////////////////////
456
// Generic networking utilities
457

458
typedef enum {
459
   LOG_DEBUG,
460
   LOG_INFO,
461
   LOG_WARN,
462
   LOG_ERROR
463
} log_level_t;
464

465
__attribute__((format(printf, 2, 3)))
466
static void server_log(log_level_t level, const char *fmt, ...)
31✔
467
{
468
   if (opt_get_int(OPT_UNIT_TEST))
31✔
469
      return;
31✔
470
   else if (DEBUG_ONLY(false &&) level < LOG_INFO)
×
471
      return;
472

473
   va_list ap;
×
474
   va_start(ap, fmt);
×
475

476
   switch (level) {
×
477
   case LOG_DEBUG: color_printf("$#8$D: "); break;
×
478
   case LOG_INFO: printf("I: "); break;
×
479
   case LOG_WARN: color_printf("$yellow$W: "); break;
×
480
   case LOG_ERROR: color_printf("$red$E: "); break;
×
481
   }
482

483
   vprintf(fmt, ap);
×
484
   color_printf("$$\n");
×
485
   fflush(stdout);
×
486

487
   va_end(ap);
×
488
}
489

490
static void send_fully(int fd, const void *data, size_t len)
7✔
491
{
492
   while (len > 0) {
14✔
493
      ssize_t nbytes = send(fd, data, len, 0);
7✔
494
      if (nbytes <= 0) {
7✔
495
         server_log(LOG_ERROR, "send: %s", strerror(errno));
×
496
         return;
×
497
      }
498

499
      data += nbytes;
7✔
500
      len -= nbytes;
7✔
501
   }
502
}
503

504
static void base64_encode(const void *in, size_t len, text_buf_t *tb)
7✔
505
{
506
   static const char map[] =
7✔
507
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
508

509
   const unsigned char *data = in;
7✔
510

511
   for (size_t i = 0; i < len; i++) {
56✔
512
      int c0 = (data[i] >> 2) & 0x3F;
49✔
513
      tb_append(tb, map[c0]);
49✔
514
      c0 = (data[i] << 4) & 0x3F;
49✔
515
      if (++i < len)
49✔
516
         c0 |= (data[i] >> 4) & 0x0F;
49✔
517
      tb_append(tb, map[c0]);
49✔
518

519
      if (i < len) {
49✔
520
         int c1 = (data[i] << 2) & 0x3F;
49✔
521
         if (++i < len)
49✔
522
            c1 |= (data[i] >> 6) & 0x03;
42✔
523
         tb_append(tb, map[c1]);
49✔
524
      }
525
      else {
526
         ++i;
×
527
         tb_append(tb, '=');
×
528
      }
529

530
      if (i < len) {
49✔
531
         int c2 = data[i] & 0x3F;
42✔
532
         tb_append(tb, map[c2]);
42✔
533
      }
534
      else
535
         tb_append(tb, '=');
7✔
536
   }
537
}
7✔
538

539
static packet_buf_t *fresh_packet_buffer(debug_server_t *server)
7✔
540
{
541
   server->packetbuf->wptr = 0;
7✔
542
   return server->packetbuf;
7✔
543
}
544

545
////////////////////////////////////////////////////////////////////////////////
546
// HTTP and WebSocket server
547

548
static void send_http_headers(int fd, int status, const char *type, size_t len,
7✔
549
                              const char *headers)
550
{
551
   LOCAL_TEXT_BUF date = tb_new();
14✔
552
   tb_strftime(date, "G%a, %d %b %Y %H:%M:%S %Z", time(NULL));
7✔
553

554
   char buf[512];
7✔
555
   const int nbytes = checked_sprintf(buf, sizeof(buf),
7✔
556
                                      "HTTP/1.1 %d\r\n"
557
                                      "Date: %s\r\n"
558
                                      "Content-Type: %s; charset=UTF-8\r\n"
559
                                      "Content-Length: %zu\r\n"
560
                                      "%s\r\n",
561
                                      status, tb_get(date), type, len, headers);
562

563
   send_fully(fd, buf, nbytes);
7✔
564
}
7✔
565

566
static void send_page(int fd, int status, const char *page)
×
567
{
568
   const size_t len = strlen(page);
×
569
   send_http_headers(fd, status, "text/html", len, "");
×
570
   send_fully(fd, page, len);
×
571
}
×
572

573
#ifdef ENABLE_GUI
574
static void send_file(int fd, const char *file, const char *mime)
×
575
{
576
   FILE *f = fopen(file, "rb");
×
577
   if (f == NULL) {
×
578
      send_page(fd, HTTP_NOT_FOUND, "File not found");
×
579
      return;
×
580
   }
581

582
   file_info_t info;
×
583
   if (!get_handle_info(fileno(f), &info)) {
×
584
      send_page(fd, HTTP_INTERNAL_SERVER_ERROR, "Cannot stat file");
×
585
      goto out_close;
×
586
   }
587

588
   send_http_headers(fd, HTTP_OK, mime, info.size, "");
×
589

590
   char buf[1024];
×
591
   for (ssize_t remain = info.size, nbytes; remain > 0; remain -= nbytes) {
×
592
      memset(buf, '\0', sizeof(buf));
×
593

594
      if ((nbytes = fread(buf, 1, MIN(remain, sizeof(buf)), f)) == 0) {
×
595
         server_log(LOG_ERROR, "fread: %s: %s", file, strerror(errno));
×
596
         goto out_close;
×
597
      }
598

599
      send_fully(fd, buf, nbytes);
×
600
   }
601

602
 out_close:
×
603
   fclose(f);
×
604
}
605
#endif
606

607
static void handle_text_frame(web_socket_t *ws, const char *text, void *context)
8✔
608
{
609
   debug_server_t *server = context;
8✔
610

611
   const char *result = NULL;
8✔
612
   if (shell_eval(server->shell, text, &result) && *result != '\0')
8✔
613
      ws_send_text(ws, result);
5✔
614
}
8✔
615

616
static void handle_binary_frame(web_socket_t *ws, const void *data,
5✔
617
                                size_t length, void *context)
618
{
619
   debug_server_t *server = context;
5✔
620

621
   if (length == 0) {
5✔
622
      server_log(LOG_WARN, "ignoring zero-length binary frame");
×
623
      return;
×
624
   }
625

626
   const c2s_opcode_t op = *(const uint8_t *)data;
5✔
627
   switch (op) {
5✔
628
   case C2S_SHUTDOWN:
5✔
629
      server->shutdown = true;
5✔
630
      break;
5✔
631
   default:
×
632
      server_log(LOG_ERROR, "unhandled client to server opcode %02x", op);
×
633
      break;
×
634
   }
635
}
636

637
static void kill_http_connection(http_server_t *http)
7✔
638
{
639
   diag_set_consumer(NULL, NULL);
7✔
640

641
   closesocket(http->websocket->sock);
7✔
642

643
   ws_free(http->websocket);
7✔
644
   http->websocket = NULL;
7✔
645
}
7✔
646

647
static void tunnel_diag(diag_t *d, void *context)
×
648
{
649
   http_server_t *http = container_of(context, http_server_t, server);
×
650

651
   if (http->websocket != NULL) {
×
652
      ws_send_text(http->websocket, diag_get_text(d));
×
653
   }
654
   else
655
      server_log(LOG_INFO, "%s", diag_get_text(d));
×
656
}
×
657

658
static void tunnel_output(const char *buf, size_t nchars, void *user)
×
659
{
660
   http_server_t *http = container_of(user, http_server_t, server);
×
661
   ws_send(http->websocket, WS_OPCODE_TEXT_FRAME, buf, nchars);
×
662
}
×
663

664
static void tunnel_backchannel(const char *buf, size_t nchars, void *user)
×
665
{
666
   http_server_t *http = container_of(user, http_server_t, server);
×
667

668
   packet_buf_t *pb = fresh_packet_buffer(&(http->server));
×
669
   pb_pack_u8(pb, S2C_BACKCHANNEL);
×
670
   pb_pack_u32(pb, nchars);
×
671
   pb_pack_bytes(pb, buf, nchars);
×
672
   ws_send_packet(http->websocket, pb);
×
673
}
×
674

675
static void add_wave_handler(ident_t path, const char *enc, void *user)
1✔
676
{
677
   http_server_t *http = container_of(user, http_server_t, server);
1✔
678

679
   packet_buf_t *pb = fresh_packet_buffer(&(http->server));
1✔
680
   pb_pack_u8(pb, S2C_ADD_WAVE);
1✔
681
   pb_pack_ident(pb, path);
1✔
682
   pb_pack_str(pb, enc);
1✔
683
   ws_send_packet(http->websocket, pb);
1✔
684
}
1✔
685

686
static void signal_update_handler(ident_t path, uint64_t now, rt_signal_t *s,
1✔
687
                                  const char *enc, void *user)
688
{
689
   http_server_t *http = container_of(user, http_server_t, server);
1✔
690

691
   packet_buf_t *pb = fresh_packet_buffer(&(http->server));
1✔
692
   pb_pack_u8(pb, S2C_SIGNAL_UPDATE);
1✔
693
   pb_pack_ident(pb, path);
1✔
694
   pb_pack_str(pb, enc);
1✔
695
   ws_send_packet(http->websocket, pb);
1✔
696
}
1✔
697

698
static void start_sim_handler(ident_t top, void *user)
1✔
699
{
700
   http_server_t *http = container_of(user, http_server_t, server);
1✔
701

702
   packet_buf_t *pb = fresh_packet_buffer(&(http->server));
1✔
703
   pb_pack_u8(pb, S2C_START_SIM);
1✔
704
   pb_pack_ident(pb, top);
1✔
705
   ws_send_packet(http->websocket, pb);
1✔
706
}
1✔
707

708
static void restart_sim_handler(void *user)
1✔
709
{
710
   http_server_t *http = container_of(user, http_server_t, server);
1✔
711

712
   packet_buf_t *pb = fresh_packet_buffer(&(http->server));
1✔
713
   pb_pack_u8(pb, S2C_RESTART_SIM);
1✔
714
   ws_send_packet(http->websocket, pb);
1✔
715
}
1✔
716

717
static void next_time_step_handler(uint64_t now, void *user)
2✔
718
{
719
   http_server_t *http = container_of(user, http_server_t, server);
2✔
720

721
   packet_buf_t *pb = fresh_packet_buffer(&(http->server));
2✔
722
   pb_pack_u8(pb, S2C_NEXT_TIME_STEP);
2✔
723
   pb_pack_u64(pb, now);
2✔
724
   ws_send_packet(http->websocket, pb);
2✔
725
}
2✔
726

727
static void open_websocket(http_server_t *http, int fd)
7✔
728
{
729
   if (http->websocket != NULL) {
7✔
730
      ws_send_close(http->websocket);
1✔
731
      ws_flush(http->websocket);
1✔
732
      kill_http_connection(http);
1✔
733
   }
734

735
   const ws_handler_t handler = {
7✔
736
      .text_frame   = handle_text_frame,
737
      .binary_frame = handle_binary_frame,
738
      .context      = &(http->server)
7✔
739
   };
740

741
   http->websocket = ws_new(fd, &handler, false);
7✔
742

743
   diag_set_consumer(tunnel_diag, &(http->server));
7✔
744

745
   if (http->server.banner)
7✔
UNCOV
746
      shell_print_banner(http->server.shell);
×
747

748
   if (http->server.top != NULL)
7✔
749
      shell_reset(http->server.shell, http->server.top);
1✔
750

751
   if (http->server.init_cmd != NULL) {
7✔
752
      packet_buf_t *pb = fresh_packet_buffer(&(http->server));
1✔
753
      pb_pack_u8(pb, S2C_INIT_CMD);
1✔
754
      pb_pack_str(pb, http->server.init_cmd);
1✔
755
      ws_send_packet(http->websocket, pb);
1✔
756
   }
757
}
7✔
758

759
static bool get_websocket_accept_value(const char *key, text_buf_t *tb)
7✔
760
{
761
   if (key == NULL || strlen(key) != WS_KEY_LEN)
7✔
762
      return false;
763

764
   char *str LOCAL = xmalloc(WS_KEY_LEN + WS_GUID_LEN + 1);
7✔
765
   strncpy(str, key, (WS_KEY_LEN + 1));
7✔
766
   strncpy(str + WS_KEY_LEN, WS_GUID, WS_GUID_LEN + 1);
7✔
767

768
   SHA1_CTX ctx;
7✔
769
   SHA1Init(&ctx);
7✔
770
   SHA1Update(&ctx, (unsigned char *)str, WS_KEY_GUID_LEN);
7✔
771

772
   unsigned char hash[SHA1_LEN];
7✔
773
   SHA1Final(hash, &ctx);
7✔
774

775
   base64_encode(hash, SHA1_LEN, tb);
7✔
776
   return true;
7✔
777
}
778

779
static void websocket_upgrade(http_server_t *http, int fd, const char *method,
7✔
780
                              const char *version, shash_t *headers)
781
{
782
   LOCAL_TEXT_BUF tb = tb_new();
7✔
783

784
   if (strcmp(method, "GET") != 0 || strcmp(version, "HTTP/1.1") != 0) {
7✔
UNCOV
785
      send_page(fd, HTTP_BAD_REQUEST, "Bad request");
×
UNCOV
786
      goto out_close;
×
787
   }
788

789
   const char *ws_version_header = shash_get(headers, "sec-websocket-version");
7✔
790

791
   if (ws_version_header == NULL
7✔
792
       || strcasecmp(ws_version_header, WS_WEBSOCKET_VERSION) != 0) {
7✔
793

794
      static const char page[] = "Upgrade required";
×
795
      static const char header[] =
×
796
         "Sec-WebSocket-Version:" WS_WEBSOCKET_VERSION;
797

UNCOV
798
      send_http_headers(fd, HTTP_UPGRADE_REQUIRED, "text/html",
×
799
                        sizeof(page), header);
UNCOV
800
      send_fully(fd, page, sizeof(page));
×
801

UNCOV
802
      goto out_close;
×
803
   }
804

805
   const char *ws_key_header = shash_get(headers, "sec-websocket-key");
7✔
806

807
   if (ws_key_header == NULL || strlen(ws_key_header) != WS_KEY_LEN) {
7✔
UNCOV
808
      send_page(fd, HTTP_BAD_REQUEST, "Bad request");
×
809
      goto out_close;
×
810
   }
811

812
   tb_cat(tb, "Connection: upgrade\r\n"
7✔
813
          "Upgrade: websocket\r\n"
814
          "Sec-WebSocket-Accept: ");
815

816
   if (!get_websocket_accept_value(ws_key_header, tb))
7✔
817
      goto out_close;
×
818

819
   tb_cat(tb, "\r\n");
7✔
820

821
   send_http_headers(fd, HTTP_SWITCHING_PROTOCOLS, "text/html", 0, tb_get(tb));
7✔
822

823
   open_websocket(http, fd);
7✔
824

825
   return;   // Socket left open
7✔
826

UNCOV
827
 out_close:
×
UNCOV
828
   closesocket(fd);
×
829
}
830

831
static bool is_websocket_request(shash_t *headers)
7✔
832
{
833
   const char *upg_header = shash_get(headers, "upgrade");
7✔
834
   const char *con_header = shash_get(headers, "connection");
7✔
835

836
   return (upg_header != NULL && con_header != NULL)
7✔
837
          && (strcasecmp(upg_header, WS_UPGRADE_VALUE) == 0)
7✔
838
          && (strcasestr(con_header, "Upgrade") != NULL);
14✔
839
}
840

841
#ifdef ENABLE_GUI
UNCOV
842
static void serve_gui_static_files(int fd, const char *url)
×
843
{
UNCOV
844
   LOCAL_TEXT_BUF tb = tb_new();
×
UNCOV
845
   get_data_dir(tb);
×
UNCOV
846
   tb_cat(tb, "/gui");
×
847

UNCOV
848
   if (strcmp(url, "/") == 0) {
×
UNCOV
849
      tb_cat(tb, "/index.html");
×
UNCOV
850
      send_file(fd, tb_get(tb), "text/html");
×
851
      return;
×
852
   }
853

854
   const char *mime = "application/octet-stream";
×
855
   const char *dot = strrchr(url, '.');
×
UNCOV
856
   if (dot != NULL) {
×
857
      static const char *mime_map[][2] = {
858
         { ".js",  "text/javascript" },
859
         { ".css", "text/css" },
860
         { ".map", "application/json" },
861
      };
862

863
      for (int i = 0; i < ARRAY_LEN(mime_map); i++) {
×
864
         if (strcmp(dot, mime_map[i][0]) == 0) {
×
865
            mime = mime_map[i][1];
×
UNCOV
866
            break;
×
867
         }
868
      }
869
   }
870

UNCOV
871
   tb_cat(tb, url);
×
872
   send_file(fd, tb_get(tb), mime);
×
873
}
874
#endif
875

876
static void handle_http_request(http_server_t *http, int fd,
7✔
877
                                const char *method, const char *url,
878
                                const char *version, shash_t *headers)
879
{
880
   server_log(LOG_DEBUG, "%s %s", method, url);
7✔
881

882
   if (is_websocket_request(headers)) {
7✔
883
      websocket_upgrade(http, fd, method, version, headers);
7✔
884
      return;    // Socket left open
7✔
885
   }
UNCOV
886
   else if (strcmp(method, "GET") != 0) {
×
UNCOV
887
      send_page(fd, HTTP_METHOD_NOT_ALLOWED, "Method not allowed");
×
UNCOV
888
      goto out_close;
×
889
   }
890

891
#ifdef ENABLE_GUI
UNCOV
892
   serve_gui_static_files(fd, url);
×
893
#else
894
   send_page(fd, HTTP_NOT_FOUND, "Not found");
895
#endif
896

897
 out_close:
×
UNCOV
898
   closesocket(fd);
×
899
}
900

901
static void http_new_connection(debug_server_t *server, int fd)
7✔
902
{
903
   char buf[MAX_HTTP_REQUEST + 1];
7✔
904
   size_t reqlen = 0;
7✔
905
   do {
7✔
906
      ssize_t n = recv(fd, buf + reqlen, MAX_HTTP_REQUEST - reqlen, 0);
7✔
907

908
#ifdef __MINGW32__
909
      const bool would_block =
910
         (n == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK);
911
#else
912
      const bool would_block = (n == -1 && errno == EWOULDBLOCK);
7✔
913
#endif
914

915
      if (would_block) {
7✔
UNCOV
916
         fd_set rfd;
×
UNCOV
917
         FD_ZERO(&rfd);
×
UNCOV
918
         FD_SET(fd, &rfd);
×
919

UNCOV
920
         struct timeval tv = {
×
921
            .tv_sec = 1,
922
            .tv_usec = 0
923
         };
924

925
         if (select(fd + 1, &rfd, NULL, NULL, &tv) == -1) {
×
926
            server_log(LOG_ERROR, "select: %s", last_os_error());
×
927
            goto out_close;
×
928
         }
929

UNCOV
930
         if (FD_ISSET(fd, &rfd))
×
UNCOV
931
            continue;
×
932

UNCOV
933
         server_log(LOG_ERROR, "timeout waiting for HTTP request");
×
934
         goto out_close;
×
935
      }
936
      else if (n <= 0) {
7✔
UNCOV
937
         server_log(LOG_ERROR, "recv: %s", last_os_error());
×
UNCOV
938
         goto out_close;
×
939
      }
940

941
      reqlen += n;
7✔
942
      assert(reqlen <= MAX_HTTP_REQUEST);
7✔
943

944
      if (reqlen == MAX_HTTP_REQUEST) {
7✔
UNCOV
945
         server_log(LOG_ERROR, "HTTP request too big");
×
946
         goto out_close;
×
947
      }
948

949
      buf[reqlen] = '\0';
7✔
950
   } while (strstr(buf, "\r\n\r\n") == NULL);
7✔
951

952
   const char *method = "GET";
7✔
953
   const char *url = "/";
7✔
954
   const char *version = "";
7✔
955

956
   char *saveptr, *saveptr2;
7✔
957
   char *line = strtok_r(buf, "\r\n", &saveptr);
7✔
958
   if (line == NULL) {
7✔
UNCOV
959
      server_log(LOG_ERROR, "malformed HTTP request");
×
UNCOV
960
      goto out_close;
×
961
   }
962

963
   method = strtok_r(line, " ", &saveptr2);
7✔
964
   if (method == NULL)
7✔
UNCOV
965
      goto malformed;
×
966

967
   url = strtok_r(NULL, " ", &saveptr2);
7✔
968
   if (url == NULL)
7✔
969
      goto malformed;
×
970

971
   version = strtok_r(NULL, " ", &saveptr2);
7✔
972
   if (version == NULL)
7✔
UNCOV
973
      goto malformed;
×
974

975
   shash_t *headers = shash_new(64);
7✔
976

977
   while ((line = strtok_r(NULL, "\r\n", &saveptr)) && line[0] != '\0') {
70✔
978
      char *colon = strchr(line, ':');
56✔
979
      if (colon != NULL) {
56✔
980
         *colon = '\0';
56✔
981

982
         char *value = colon + 1;
56✔
983
         while (*value == ' ')
112✔
984
            value++;
56✔
985

986
         for (char *p = line; *p; p++)
623✔
987
            *p = tolower_iso88591(*p);
567✔
988

989
         shash_put(headers, line, value);
56✔
990
      }
991
   }
992

993
   http_server_t *http = container_of(server, http_server_t, server);
7✔
994
   handle_http_request(http, fd, method, url, version, headers);
7✔
995
   shash_free(headers);
7✔
996
   return;
7✔
997

UNCOV
998
 malformed:
×
UNCOV
999
   server_log(LOG_ERROR, "malformed HTTP request");
×
1000

UNCOV
1001
 out_close:
×
UNCOV
1002
   closesocket(fd);
×
1003
}
1004

1005
static int http_fill_fd_set(debug_server_t *server, fd_set *rfd, fd_set *wfd)
49✔
1006
{
1007
   http_server_t *http = container_of(server, http_server_t, server);
49✔
1008

1009
   if (http->websocket == NULL)
49✔
1010
      return -1;
1011

1012
   FD_SET(http->websocket->sock, rfd);
38✔
1013

1014
   if (http->websocket->tx_wptr != http->websocket->tx_rptr)
38✔
1015
      FD_SET(http->websocket->sock, wfd);
16✔
1016

1017
   return http->websocket->sock;
38✔
1018
}
1019

1020
static void http_poll_sockets(debug_server_t *server, fd_set *rfd, fd_set *wfd)
44✔
1021
{
1022
   http_server_t *http = container_of(server, http_server_t, server);
44✔
1023

1024
   if (http->websocket == NULL)
44✔
1025
      return;
1026

1027
   if (FD_ISSET(http->websocket->sock, rfd))
44✔
1028
      ws_poll(http->websocket);
22✔
1029

1030
   if (FD_ISSET(http->websocket->sock, wfd))
44✔
1031
      ws_flush(http->websocket);
16✔
1032

1033
   if (http->websocket->closing)
44✔
1034
      kill_http_connection(http);
6✔
1035
}
1036

1037
static void http_shutdown(debug_server_t *server)
5✔
1038
{
1039
   http_server_t *http = container_of(server, http_server_t, server);
5✔
1040

1041
   if (http->websocket != NULL)
5✔
1042
      ws_send_close(http->websocket);
5✔
1043
}
5✔
1044

1045
static debug_server_t *http_server_new(void)
5✔
1046
{
1047
   http_server_t *http = xcalloc(sizeof(http_server_t));
5✔
1048
   return &(http->server);
5✔
1049
}
1050

1051
static void http_server_free(debug_server_t *server)
5✔
1052
{
1053
   http_server_t *http = container_of(server, http_server_t, server);
5✔
1054
   assert(http->websocket == NULL);
5✔
1055
   free(http);
5✔
1056
}
5✔
1057

1058
static const server_proto_t http_protocol = {
1059
   .new_server = http_server_new,
1060
   .free_server = http_server_free,
1061
   .new_connection = http_new_connection,
1062
   .fill_fd_set = http_fill_fd_set,
1063
   .poll_sockets = http_poll_sockets,
1064
   .shutdown = http_shutdown,
1065
};
1066

1067
////////////////////////////////////////////////////////////////////////////////
1068
// CXXRTL debug protocol over TCP
1069
//
1070
// https://gist.github.com/whitequark/59520e2de0947da8747061bc2ea91639
1071

UNCOV
1072
static void kill_cxxrtl_connection(cxxrtl_server_t *cxxrtl)
×
1073
{
UNCOV
1074
   diag_set_consumer(NULL, NULL);
×
1075

UNCOV
1076
   closesocket(cxxrtl->sock);
×
UNCOV
1077
   cxxrtl->sock = -1;
×
1078

UNCOV
1079
   cxxrtl->rx_rptr = cxxrtl->rx_wptr = 0;
×
UNCOV
1080
}
×
1081

1082
static void cxxrtl_send(cxxrtl_server_t *cxxrtl, json_t *json)
5✔
1083
{
1084
   // TODO: use json_dumpb or json_dump_callback
1085
   char *str LOCAL = json_dumps(json, JSON_COMPACT);
5✔
1086
   server_log(LOG_DEBUG, "S->C: %s", str);
5✔
1087

1088
   const size_t size = strlen(str) + 1;
5✔
1089

1090
   if (cxxrtl->tx_wptr + size > cxxrtl->tx_size) {
5✔
1091
      cxxrtl->tx_size = MAX(cxxrtl->tx_size + size, 1024);
2✔
1092
      cxxrtl->tx_buf = xrealloc(cxxrtl->tx_buf, cxxrtl->tx_size);
2✔
1093
   }
1094

1095
   memcpy(cxxrtl->tx_buf + cxxrtl->tx_wptr, str, size);
5✔
1096
   cxxrtl->tx_wptr += size;
5✔
1097
}
5✔
1098

1099
static void cxxrtl_error(cxxrtl_server_t *cxxrtl, json_t *json,
1✔
1100
                         const char *error, const char *message)
1101
{
1102
   json_object_set_new(json, "type", json_string("error"));
1✔
1103
   json_object_set_new(json, "error", json_string(error));
1✔
1104
   json_object_set_new(json, "message", json_string(message));
1✔
1105

1106
   cxxrtl_send(cxxrtl, json);
1✔
1107
}
1✔
1108

1109
static void handle_greeting(cxxrtl_server_t *cxxrtl, json_t *json)
2✔
1110
{
1111
   json_t *version = json_object_get(json, "version");
2✔
1112
   if (version == NULL)
2✔
UNCOV
1113
      return cxxrtl_error(cxxrtl, json, "parse_error", "Missing version");
×
1114
   else if (json_integer_value(version) != 0)
2✔
UNCOV
1115
      return cxxrtl_error(cxxrtl, json, "version_error", "Epected version 0");
×
1116

1117
   json_t *commands = json_array();
2✔
1118
   json_object_set_new(json, "commands", commands);
2✔
1119

1120
   static const char *supported_commands[] = {
2✔
1121
      "list_scopes",
1122
      "list_items",
1123
      "reference_items",
1124
      "query_interval",
1125
      "get_simulation_status",
1126
      "run_simulation",
1127
      "pause_simulation",
1128
      "nvc.quit_simulation",
1129
   };
1130

1131
   for (size_t i = 0; i < ARRAY_LEN(supported_commands); i++)
18✔
1132
      json_array_append_new(commands, json_string(supported_commands[i]));
16✔
1133

1134
   json_t *events = json_array();
2✔
1135
   json_object_set_new(json, "events", events);
2✔
1136

1137
   static const char *supported_events[] = {
2✔
1138
      "simulation_paused",
1139
      "simulation_finished"
1140
   };
1141

1142
   for (size_t i = 0; i < ARRAY_LEN(supported_events); i++)
6✔
1143
      json_array_append_new(events, json_string(supported_events[i]));
4✔
1144

1145
   json_t *features = json_object();
2✔
1146
   json_object_set_new(json, "features", features);
2✔
1147

1148
   json_t *encoding = json_array();
2✔
1149
   json_object_set_new(json, "item_values_encoding", encoding);
2✔
1150

1151
   json_array_append_new(encoding, json_string("base64(u32)"));
2✔
1152

1153
   json_object_set_new(features, "encoding", encoding);
2✔
1154

1155
   cxxrtl_send(cxxrtl, json);
2✔
1156
}
1157

UNCOV
1158
static void handle_get_simulation_status(cxxrtl_server_t *cxxrtl, json_t *json)
×
1159
{
UNCOV
1160
   json_object_set_new(json, "type", json_string("response"));
×
UNCOV
1161
   json_object_set_new(json, "status", json_string("paused"));
×
UNCOV
1162
   json_object_set_new(json, "latest_time", json_string("0.0"));
×
UNCOV
1163
   json_object_set_new(json, "next_sample_time", json_string("0.0"));
×
1164

UNCOV
1165
   cxxrtl_send(cxxrtl, json);
×
UNCOV
1166
}
×
1167

1168
static void handle_quit_simulation(cxxrtl_server_t *cxxrtl, json_t *json)
2✔
1169
{
1170
   cxxrtl->server.shutdown = true;
2✔
1171

1172
   json_object_set_new(json, "type", json_string("response"));
2✔
1173
   cxxrtl_send(cxxrtl, json);
2✔
1174
}
2✔
1175

1176
static void handle_command(cxxrtl_server_t *cxxrtl, json_t *json)
3✔
1177
{
1178
   json_t *command = json_object_get(json, "command");
3✔
1179
   if (command == NULL)
3✔
UNCOV
1180
      return cxxrtl_error(cxxrtl, json, "parse_error", "Missing command");
×
1181

1182
   const char *str = json_string_value(command);
3✔
1183
   if (strcmp(str, "get_simulation_status") == 0)
3✔
UNCOV
1184
      handle_get_simulation_status(cxxrtl, json);
×
1185
   else if (strcmp(str, "nvc.quit_simulation") == 0)
3✔
1186
      handle_quit_simulation(cxxrtl, json);
2✔
1187
   else
1188
      cxxrtl_error(cxxrtl, json, "bad_command", "Invalid command");
1✔
1189
}
1190

1191
static void cxxrtl_new_connection(debug_server_t *server, int fd)
2✔
1192
{
1193
   cxxrtl_server_t *cxxrtl = container_of(server, cxxrtl_server_t, server);
2✔
1194

1195
   if (cxxrtl->sock != -1) {
2✔
UNCOV
1196
      server_log(LOG_INFO, "closing old connection");
×
UNCOV
1197
      closesocket(cxxrtl->sock);
×
1198
   }
1199

1200
   cxxrtl->sock = fd;
2✔
1201
}
2✔
1202

1203
static int cxxrtl_fill_fd_set(debug_server_t *server, fd_set *rfd, fd_set *wfd)
14✔
1204
{
1205
   cxxrtl_server_t *cxxrtl = container_of(server, cxxrtl_server_t, server);
14✔
1206

1207
   if (cxxrtl->sock != -1)
14✔
1208
      FD_SET(cxxrtl->sock, rfd);
10✔
1209

1210
   if (cxxrtl->tx_wptr != cxxrtl->tx_rptr)
14✔
1211
      FD_SET(cxxrtl->sock, wfd);
5✔
1212

1213
   return cxxrtl->sock;
14✔
1214
}
1215

1216
static void cxxrtl_read_message(cxxrtl_server_t *cxxrtl)
5✔
1217
{
1218
   if (cxxrtl->rx_size - cxxrtl->rx_wptr < 1024)
5✔
1219
      cxxrtl->rx_buf = xrealloc(cxxrtl->rx_buf, (cxxrtl->rx_size += 1024));
2✔
1220

1221
   const ssize_t nbytes = recv(cxxrtl->sock, cxxrtl->rx_buf + cxxrtl->rx_wptr,
10✔
1222
                               cxxrtl->rx_size - cxxrtl->rx_wptr, 0);
5✔
1223
   if (nbytes == -1 && errno == EAGAIN)
5✔
1224
      return;
1225
   else if (nbytes == 0) {
5✔
UNCOV
1226
      kill_cxxrtl_connection(cxxrtl);
×
UNCOV
1227
      return;
×
1228
   }
1229
   else if (nbytes < 0) {
5✔
UNCOV
1230
      server_log(LOG_ERROR, "connection closed: %s", last_os_error());
×
UNCOV
1231
      kill_cxxrtl_connection(cxxrtl);
×
UNCOV
1232
      return;
×
1233
   }
1234

1235
   cxxrtl->rx_wptr += nbytes;
5✔
1236
   assert(cxxrtl->rx_wptr <= cxxrtl->rx_size);
5✔
1237
   assert(cxxrtl->rx_rptr < cxxrtl->rx_wptr);
5✔
1238

1239
   do {
5✔
1240
      char *endp = memchr(cxxrtl->rx_buf + cxxrtl->rx_rptr, '\0',
5✔
1241
                          cxxrtl->rx_wptr - cxxrtl->rx_rptr);
5✔
1242
      if (endp == NULL)
5✔
UNCOV
1243
         return;
×
1244

1245
      server_log(LOG_DEBUG, "C->S: %s", cxxrtl->rx_buf + cxxrtl->rx_rptr);
5✔
1246

1247
      json_error_t error;
5✔
1248
      json_t *root = json_loads(cxxrtl->rx_buf + cxxrtl->rx_rptr, 0, &error);
5✔
1249

1250
      if (endp == cxxrtl->rx_buf + cxxrtl->rx_wptr - 1)
5✔
1251
         cxxrtl->rx_wptr = cxxrtl->rx_rptr = 0;
5✔
1252
      else
UNCOV
1253
         cxxrtl->rx_rptr = endp - cxxrtl->rx_buf + 1;
×
1254

1255
      if (!json_is_object(root)) {
5✔
UNCOV
1256
         cxxrtl_error(cxxrtl, root, "bad_json", "Nnot a JSON object");
×
UNCOV
1257
         json_decref(root);
×
UNCOV
1258
         continue;
×
1259
      }
1260

1261
      json_t *type = json_object_get(root, "type");
5✔
1262
      if (!json_is_string(type)) {
5✔
UNCOV
1263
         cxxrtl_error(cxxrtl, root, "parse_error", "Missing type field");
×
UNCOV
1264
         json_decref(root);
×
1265
         continue;
×
1266
      }
1267

1268
      const char *typestr = json_string_value(type);
5✔
1269
      if (strcmp(typestr, "greeting") == 0)
5✔
1270
         handle_greeting(cxxrtl, root);
2✔
1271
      else if (strcmp(typestr, "command") == 0)
3✔
1272
         handle_command(cxxrtl, root);
3✔
1273
      else
1274
         server_log(LOG_ERROR, "unhandled message type '%s'", typestr);
×
1275

1276
      json_decref(root);
5✔
1277
   } while (cxxrtl->rx_rptr != cxxrtl->rx_wptr);
5✔
1278
}
1279

1280
static void cxxrtl_flush(cxxrtl_server_t *cxxrtl)
5✔
1281
{
1282
   while (cxxrtl->tx_wptr != cxxrtl->tx_rptr) {
10✔
1283
      const size_t chunksz = cxxrtl->tx_wptr - cxxrtl->tx_rptr;
5✔
1284
      const ssize_t nbytes =
5✔
1285
         send(cxxrtl->sock, cxxrtl->tx_buf + cxxrtl->tx_rptr, chunksz, 0);
5✔
1286

1287
      if (nbytes == 0)
5✔
1288
         break;
1289
      else if (nbytes < 0) {
5✔
UNCOV
1290
         kill_cxxrtl_connection(cxxrtl);
×
UNCOV
1291
         break;
×
1292
      }
1293

1294
      cxxrtl->tx_rptr += nbytes;
5✔
1295
   }
1296

1297
   if (cxxrtl->tx_wptr == cxxrtl->tx_rptr)
5✔
1298
      cxxrtl->tx_rptr = cxxrtl->tx_wptr = 0;
5✔
1299
}
5✔
1300

1301
static void cxxrtl_poll_sockets(debug_server_t *server, fd_set *rfd,
12✔
1302
                                fd_set *wfd)
1303
{
1304
   cxxrtl_server_t *cxxrtl = container_of(server, cxxrtl_server_t, server);
12✔
1305

1306
   if (cxxrtl->sock == -1)
12✔
1307
      return;
1308

1309
   if (FD_ISSET(cxxrtl->sock, rfd))
12✔
1310
      cxxrtl_read_message(cxxrtl);
5✔
1311

1312
   if (FD_ISSET(cxxrtl->sock, wfd))
12✔
1313
      cxxrtl_flush(cxxrtl);
5✔
1314

1315
   if (server->shutdown && cxxrtl->tx_wptr == cxxrtl->tx_rptr) {
12✔
1316
      closesocket(cxxrtl->sock);
2✔
1317
      cxxrtl->sock = -1;
2✔
1318
   }
1319
}
1320

1321
static void cxxrtl_shutdown(debug_server_t *server)
2✔
1322
{
1323
   // TODO: send an event to the client?
1324
}
2✔
1325

1326
static debug_server_t *cxxrtl_server_new(void)
2✔
1327
{
1328
   cxxrtl_server_t *cxxrtl = xcalloc(sizeof(cxxrtl_server_t));
2✔
1329
   cxxrtl->sock = -1;
2✔
1330

1331
   return &(cxxrtl->server);
2✔
1332
}
1333

1334
static void cxxrtl_server_free(debug_server_t *server)
2✔
1335
{
1336
   cxxrtl_server_t *cxxrtl = container_of(server, cxxrtl_server_t, server);
2✔
1337
   assert(cxxrtl->sock == -1);
2✔
1338
   free(cxxrtl->rx_buf);
2✔
1339
   free(cxxrtl->tx_buf);
2✔
1340
   free(cxxrtl);
2✔
1341
}
2✔
1342

1343
static const server_proto_t cxxrtl_protocol = {
1344
   .new_server = cxxrtl_server_new,
1345
   .free_server = cxxrtl_server_free,
1346
   .new_connection = cxxrtl_new_connection,
1347
   .fill_fd_set = cxxrtl_fill_fd_set,
1348
   .poll_sockets = cxxrtl_poll_sockets,
1349
   .shutdown = cxxrtl_shutdown,
1350
};
1351

1352
////////////////////////////////////////////////////////////////////////////////
1353
// Server event loop
1354

1355
static void handle_new_connection(debug_server_t *server)
9✔
1356
{
1357
   int fd = accept(server->sock, NULL, NULL);
9✔
1358
   if (fd < 0) {
9✔
UNCOV
1359
      server_log(LOG_ERROR, "accept: %s", last_os_error());
×
UNCOV
1360
      return;
×
1361
   }
1362

1363
#ifdef __MINGW32__
1364
   if (ioctlsocket(fd, FIONBIO, &(unsigned long){1})) {
1365
      server_log(LOG_ERROR, "ioctlsocket: %s", last_os_error());
1366
      closesocket(fd);
1367
      return;
1368
   }
1369
#else
1370
   const int flags = fcntl(fd, F_GETFL, 0);
9✔
1371
   if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
9✔
UNCOV
1372
      server_log(LOG_ERROR, "fcntl: %s", last_os_error());
×
UNCOV
1373
      close(fd);
×
UNCOV
1374
      return;
×
1375
   }
1376
#endif
1377

1378
   if (server->proto->new_connection != NULL)
9✔
1379
      (*server->proto->new_connection)(server, fd);
9✔
1380
}
1381

1382
static int open_server_socket(void)
7✔
1383
{
1384
#ifdef __MINGW32__
1385
   WSADATA wsaData;
1386
   if (WSAStartup(MAKEWORD(2, 2), &wsaData))
1387
      fatal_errno("WSAStartup failed");
1388
#endif
1389

1390
   int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
7✔
1391
   if (sock < 0)
7✔
UNCOV
1392
      fatal_errno("socket");
×
1393

1394
   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
7✔
1395
                  (char *)&(int){1}, sizeof(int)) < 0)
7✔
UNCOV
1396
      fatal_errno("setsockopt");
×
1397

1398
   const uint16_t port = opt_get_int(OPT_SERVER_PORT);
7✔
1399

1400
   struct sockaddr_in addr;
7✔
1401
   addr.sin_family = AF_INET;
7✔
1402
   addr.sin_port = htons(port);
7✔
1403
   addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
7✔
1404

1405
   if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
7✔
UNCOV
1406
      fatal_errno("bind");
×
1407

1408
   if (listen(sock, SOMAXCONN) < 0)
7✔
UNCOV
1409
      fatal_errno("listen");
×
1410

1411
   server_log(LOG_INFO, "listening on 127.0.0.1:%u", port);
7✔
1412

1413
   return sock;
7✔
1414
}
1415

1416
void start_server(server_kind_t kind, jit_t *jit, tree_t top,
7✔
1417
                  server_ready_fn_t cb, void *arg, const char *init_cmd)
1418
{
1419
   static const server_proto_t *map[] = {
7✔
1420
      [SERVER_HTTP] = &http_protocol,
1421
      [SERVER_CXXRTL] = &cxxrtl_protocol
1422
   };
1423

1424
   debug_server_t *server = (*map[kind]->new_server)();
7✔
1425
   server->shell     = shell_new(jit);
7✔
1426
   server->top       = top;
7✔
1427
   server->packetbuf = pb_new();
7✔
1428
   server->init_cmd  = init_cmd;
7✔
1429
   server->banner    = !opt_get_int(OPT_UNIT_TEST);
7✔
1430
   server->proto     = map[kind];
7✔
1431

1432
   shell_handler_t handler = {
7✔
1433
      .add_wave = add_wave_handler,
1434
      .signal_update = signal_update_handler,
1435
      .stderr_write = tunnel_output,
1436
      .stdout_write = tunnel_output,
1437
      .backchannel_write = tunnel_backchannel,
1438
      .start_sim = start_sim_handler,
1439
      .restart_sim = restart_sim_handler,
1440
      .next_time_step = next_time_step_handler,
1441
      .context = server
1442
   };
1443
   shell_set_handler(server->shell, &handler);
7✔
1444

1445
   server->sock = open_server_socket();
7✔
1446

1447
   if (cb != NULL)
7✔
1448
      (*cb)(arg);
7✔
1449

1450
   for (;;) {
56✔
1451
      fd_set rfd, wfd, efd;
63✔
1452
      FD_ZERO(&rfd);
1,134✔
1453
      FD_ZERO(&wfd);
1,071✔
1454
      FD_ZERO(&efd);
1,071✔
1455

1456
      int max_fd = -1;
63✔
1457

1458
      if (server->sock != -1) {
63✔
1459
         FD_SET(server->sock, &rfd);
44✔
1460
         max_fd = MAX(max_fd, server->sock);
44✔
1461
      }
1462

1463
      const int proto_max = (*server->proto->fill_fd_set)(server, &rfd, &wfd);
63✔
1464
      max_fd = MAX(max_fd, proto_max);
63✔
1465

1466
      if (max_fd == -1)
63✔
1467
         break;
1468

1469
      struct timeval tv = {
56✔
1470
         .tv_sec = 1,
1471
         .tv_usec = 0
1472
      };
1473

1474
      if (select(max_fd + 1, &rfd, &wfd, &efd, &tv) == -1)
56✔
UNCOV
1475
         fatal_errno("select");
×
1476

1477
      if (server->sock != -1 && FD_ISSET(server->sock, &rfd))
56✔
1478
         handle_new_connection(server);
9✔
1479

1480
      (*server->proto->poll_sockets)(server, &rfd, &wfd);
56✔
1481

1482
      if (server->shutdown && server->sock != -1) {
56✔
1483
         server_log(LOG_INFO, "stopping server");
7✔
1484

1485
         closesocket(server->sock);
7✔
1486
         server->sock = -1;
7✔
1487

1488
         (*server->proto->shutdown)(server);
7✔
1489
      }
1490
   }
1491

1492
   assert(server->sock == -1);
7✔
1493

1494
   pb_free(server->packetbuf);
7✔
1495
   shell_free(server->shell);
7✔
1496
   (*server->proto->free_server)(server);
7✔
1497
}
7✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc