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

kos-lang / kos / 6046924975

01 Sep 2023 07:59AM UTC coverage: 95.345% (-0.4%) from 95.715%
6046924975

push

github

Chris Dragan
net: add getsockopt()

97 of 97 new or added lines in 1 file covered. (100.0%)

23841 of 25005 relevant lines covered (95.34%)

1022498.44 hits per line

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

50.98
/modules/kos_mod_net.c
1
/* SPDX-License-Identifier: MIT
2
 * Copyright (c) 2014-2023 Chris Dragan
3
 */
4

5
#include "../inc/kos_array.h"
6
#include "../inc/kos_buffer.h"
7
#include "../inc/kos_constants.h"
8
#include "../inc/kos_error.h"
9
#include "../inc/kos_instance.h"
10
#include "../inc/kos_malloc.h"
11
#include "../inc/kos_memory.h"
12
#include "../inc/kos_module.h"
13
#include "../inc/kos_object.h"
14
#include "../inc/kos_string.h"
15
#include "../inc/kos_utils.h"
16
#include "../core/kos_object_internal.h"
17
#include "../core/kos_try.h"
18

19
#define __STDC_FORMAT_MACROS
20
#include <inttypes.h>
21
#include <math.h>
22
#include <stdlib.h>
23
#include <string.h>
24

25
#ifdef _WIN32
26
#   include <winsock2.h>
27
#   include <ws2tcpip.h>
28
#   define SHUT_RD   SD_RECEIVE
29
#   define SHUT_WR   SD_SEND
30
#   define SHUT_RDWR SD_BOTH
31
#else
32
#   include <arpa/inet.h>
33
#   include <errno.h>
34
#   include <fcntl.h>
35
#   include <netdb.h>
36
#   include <netinet/in.h>
37
#   include <sys/socket.h>
38
#   include <sys/select.h>
39
#   include <sys/time.h>
40
#   include <sys/types.h>
41
#   include <sys/un.h>
42
#   include <unistd.h>
43
#endif
44

45
#ifdef _WIN32
46
typedef SOCKET KOS_SOCKET;
47
typedef int    DATA_LEN;
48
typedef int    ADDR_LEN;
49
typedef long   TIME_FRAGMENT;
50

51
#define KOS_INVALID_SOCKET ((KOS_SOCKET)INVALID_SOCKET)
52

53
#define reset_last_error() ((void)0)
54

55
static int get_error(void)
56
{
57
    return WSAGetLastError();
58
}
59
#else
60
typedef int       KOS_SOCKET;
61
typedef size_t    DATA_LEN;
62
typedef socklen_t ADDR_LEN;
63
typedef unsigned  TIME_FRAGMENT;
64

65
#define KOS_INVALID_SOCKET ((KOS_SOCKET)-1)
66

67
#define closesocket close
68

69
static void reset_last_error(void)
44✔
70
{
71
    errno = 0;
44✔
72
}
44✔
73

74
static int get_error(void)
20✔
75
{
76
    return errno;
20✔
77
}
78
#endif
79

80
KOS_DECLARE_STATIC_CONST_STRING(str_address,               "address");
81
KOS_DECLARE_STATIC_CONST_STRING(str_blocking,              "blocking");
82
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_buffer,        "argument to socket.recv is not a buffer");
83
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_buffer_or_str, "argument to socket.send is neither a buffer nor a string");
84
KOS_DECLARE_STATIC_CONST_STRING(str_err_too_many_to_read,  "requested read size exceeds buffer size limit");
85
KOS_DECLARE_STATIC_CONST_STRING(str_err_socket_not_open,   "socket not open or not a socket object");
86
KOS_DECLARE_STATIC_CONST_STRING(str_port,                  "port");
87
KOS_DECLARE_STATIC_CONST_STRING(str_socket,                "socket");
88
KOS_DECLARE_STATIC_CONST_STRING(str_timeout_sec,           "timeout_sec");
89

90
typedef struct KOS_SOCKET_HOLDER_S {
91
    KOS_ATOMIC(uint32_t) socket_fd;
92
    KOS_ATOMIC(uint32_t) ref_count;
93
    int                  family;
94
#ifdef _WIN32
95
    int                  blocking;
96
#endif
97
} KOS_SOCKET_HOLDER;
98

99
static int acquire_socket(KOS_SOCKET_HOLDER *socket_holder)
29✔
100
{
101
    uint32_t ref_count;
102

103
    assert(socket_holder);
29✔
104

105
    do {
106
        ref_count = KOS_atomic_read_relaxed_u32(socket_holder->ref_count);
29✔
107
        if ((int32_t)ref_count <= 0)
29✔
108
            return (int)ref_count;
×
109
    } while ( ! KOS_atomic_cas_weak_u32(socket_holder->ref_count, ref_count, ref_count + 1));
29✔
110

111
    return (int)ref_count;
29✔
112
}
113

114
static void release_socket(KOS_SOCKET_HOLDER *socket_holder)
48✔
115
{
116
    if (socket_holder) {
48✔
117
        const int32_t ref_count = KOS_atomic_add_i32(*(KOS_ATOMIC(int32_t)*)&socket_holder->ref_count, -1);
48✔
118

119
        assert(ref_count >= 1);
48✔
120

121
        if (ref_count == 1) {
48✔
122
            const int32_t socket_fd = (int32_t)KOS_atomic_swap_u32(socket_holder->socket_fd, ~0U);
19✔
123

124
            if (socket_fd >= 0)
19✔
125
                closesocket(socket_fd);
16✔
126

127
            KOS_free(socket_holder);
19✔
128
        }
129
    }
130
}
48✔
131

132
static void socket_finalize(KOS_CONTEXT ctx, void *priv)
16✔
133
{
134
    if (priv)
16✔
135
        release_socket((KOS_SOCKET_HOLDER *)priv);
16✔
136
}
16✔
137

138
KOS_DECLARE_PRIVATE_CLASS(socket_priv_class);
139

140
static KOS_SOCKET_HOLDER *make_socket_holder(KOS_CONTEXT ctx,
19✔
141
                                             KOS_SOCKET  socket_fd,
142
                                             int         family)
143
{
144
    KOS_SOCKET_HOLDER *const socket_holder = (KOS_SOCKET_HOLDER *)KOS_malloc(sizeof(KOS_SOCKET_HOLDER));
19✔
145

146
    if (socket_holder) {
19✔
147
        socket_holder->socket_fd = (uint32_t)socket_fd;
19✔
148
        socket_holder->ref_count = 1;
19✔
149
        socket_holder->family    = family;
19✔
150
#ifdef _WIN32
151
        socket_holder->blocking  = 1;
152
#endif
153
    }
154
    else
155
        KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
×
156

157
    return socket_holder;
19✔
158
}
159

160
static int set_socket_object(KOS_CONTEXT ctx,
16✔
161
                             KOS_OBJ_ID  socket_obj,
162
                             KOS_SOCKET  socket_fd,
163
                             int         family)
164
{
165
    KOS_SOCKET_HOLDER *const socket_holder = make_socket_holder(ctx, socket_fd, family);
16✔
166

167
    if (socket_holder)
16✔
168
        KOS_object_set_private_ptr(socket_obj, socket_holder);
16✔
169

170
    return socket_holder ? KOS_SUCCESS : KOS_ERROR_EXCEPTION;
16✔
171
}
172

173
static KOS_SOCKET get_socket(KOS_SOCKET_HOLDER *socket_holder)
58✔
174
{
175
    return (KOS_SOCKET)KOS_atomic_read_relaxed_u32(socket_holder->socket_fd);
58✔
176
}
177

178
static int is_socket_valid(KOS_SOCKET s)
29✔
179
{
180
#ifdef _WIN32
181
    return s != KOS_INVALID_SOCKET;
182
#else
183
    return s >= 0;
29✔
184
#endif
185
}
186

187
static int acquire_socket_object(KOS_CONTEXT         ctx,
29✔
188
                                 KOS_OBJ_ID          socket_obj,
189
                                 KOS_SOCKET_HOLDER **socket_holder)
190
{
191
    *socket_holder = (KOS_SOCKET_HOLDER *)KOS_object_get_private(socket_obj, &socket_priv_class);
29✔
192

193
    if ( ! *socket_holder || (acquire_socket(*socket_holder) <= 0)) {
29✔
194
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_socket_not_open));
×
195
        return KOS_ERROR_EXCEPTION;
×
196
    }
197

198
    if ( ! is_socket_valid(get_socket(*socket_holder))) {
29✔
199
        release_socket(*socket_holder);
×
200
        *socket_holder = KOS_NULL;
×
201

202
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_socket_not_open));
×
203
        return KOS_ERROR_EXCEPTION;
×
204
    }
205

206
    return KOS_SUCCESS;
29✔
207
}
208

209
typedef union KOS_GENERIC_ADDR_U {
210
    struct sockaddr     addr;
211
    struct sockaddr_in  inet;
212
    struct sockaddr_in6 inet6;
213
#ifndef _WIN32
214
    struct sockaddr_un  local;
215
#endif
216
} KOS_GENERIC_ADDR;
217

218
static int get_ip_address(KOS_CONTEXT        ctx,
15✔
219
                          KOS_SOCKET_HOLDER *socket_holder,
220
                          const char        *addr_cstr,
221
                          uint16_t           port,
222
                          KOS_GENERIC_ADDR  *addr,
223
                          ADDR_LEN          *addr_len)
224
{
225
    int error = KOS_SUCCESS;
15✔
226

227
#if 1
228
    if (addr_cstr[0]) {
15✔
229
        struct addrinfo  hint;
230
        struct addrinfo *info = KOS_NULL;
9✔
231

232
        memset(&hint, 0, sizeof(hint));
9✔
233

234
        hint.ai_family = socket_holder->family;
9✔
235

236
        KOS_suspend_context(ctx);
9✔
237

238
        error = getaddrinfo(addr_cstr,
9✔
239
                            KOS_NULL,
240
                            &hint,
241
                            &info);
242

243
        KOS_resume_context(ctx);
9✔
244

245
        if (error) {
9✔
246
            KOS_raise_printf(ctx, "getaddrinfo: %s", gai_strerror(error));
×
247
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
248
        }
249
        else if (info->ai_addrlen > sizeof(*addr)) {
9✔
250
            KOS_DECLARE_STATIC_CONST_STRING(str_addrinfo, "getaddrinfo: address too long");
251

252
            KOS_raise_exception(ctx, KOS_CONST_ID(str_addrinfo));
×
253
            error = KOS_ERROR_EXCEPTION;
×
254
        }
255
        else {
256
            struct addrinfo *cur_info = info;
9✔
257

258
            for (; cur_info; cur_info = cur_info->ai_next)
9✔
259
                if (cur_info->ai_family == socket_holder->family)
9✔
260
                    break;
9✔
261

262
            if ( ! cur_info) {
9✔
263
                KOS_DECLARE_STATIC_CONST_STRING(str_addrinfo, "getaddrinfo: requested address family not available");
264

265
                freeaddrinfo(info);
×
266
                RAISE_EXCEPTION_STR(str_addrinfo);
×
267
            }
268

269
            if (socket_holder->family == AF_INET) {
9✔
270
                addr->inet          = *(struct sockaddr_in *)cur_info->ai_addr;
6✔
271
                addr->inet.sin_port = htons(port);
6✔
272
                *addr_len           = sizeof(addr->inet);
6✔
273
            }
274
            else {
275
                addr->inet6           = *(struct sockaddr_in6 *)cur_info->ai_addr;
3✔
276
                addr->inet6.sin6_port = htons(port);
3✔
277
                *addr_len             = sizeof(addr->inet6);
3✔
278
            }
279
        }
280

281
        freeaddrinfo(info);
9✔
282
    }
283
#else
284
    if (addr_cstr[0]) {
285
        struct hostent *ent;
286

287
        KOS_suspend_context(ctx);
288

289
        ent = gethostbyname(addr_cstr);
290

291
        error = ent ? KOS_SUCCESS : h_errno;
292

293
        KOS_resume_context(ctx);
294

295
        if (error) {
296
            KOS_raise_printf(ctx, "gethostbyname: %s", hstrerror(error));
297
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
298
        }
299

300
        addr->inet.sin_family = AF_INET;
301
        addr->inet.sin_addr   = *(struct in_addr *)ent->h_addr;
302
        addr->inet.sin_port   = htons(port);
303
        *addr_len             = sizeof(addr->inet);
304
    }
305
#endif
306
    else {
307
        if (socket_holder->family == AF_INET) {
6✔
308
            addr->inet.sin_family = AF_INET;
3✔
309
            addr->inet.sin_port   = htons(port);
3✔
310
            *addr_len             = sizeof(addr->inet);
3✔
311
        }
312
        else {
313
            addr->inet6.sin6_family = AF_INET6;
3✔
314
            addr->inet6.sin6_port   = htons(port);
3✔
315
            *addr_len               = sizeof(addr->inet6);
3✔
316
        }
317
    }
318

319
cleanup:
15✔
320
    return error;
15✔
321
}
322

323
static int get_address(KOS_CONTEXT        ctx,
15✔
324
                       KOS_SOCKET_HOLDER *socket_holder,
325
                       const char        *addr_cstr,
326
                       uint16_t           port,
327
                       KOS_GENERIC_ADDR  *addr,
328
                       ADDR_LEN          *addr_len)
329
{
330
    int error = KOS_SUCCESS;
15✔
331

332
    memset(addr, 0, sizeof(*addr));
15✔
333

334
    if (socket_holder->family == AF_INET ||
15✔
335
        socket_holder->family == AF_INET6) {
6✔
336

337
        error = get_ip_address(ctx,
15✔
338
                               socket_holder,
339
                               addr_cstr,
340
                               port,
341
                               addr,
342
                               addr_len);
343
    }
344
    else {
345
        /* TODO AF_LOCAL */
346
        assert(0);
×
347
    }
348

349
    return error;
15✔
350
}
351

352
static KOS_OBJ_ID get_blocking(KOS_CONTEXT ctx,
353
                               KOS_OBJ_ID  this_obj,
354
                               KOS_OBJ_ID  args_obj);
355

356
static KOS_OBJ_ID set_blocking(KOS_CONTEXT ctx,
357
                               KOS_OBJ_ID  this_obj,
358
                               KOS_OBJ_ID  args_obj);
359

360
KOS_DECLARE_STATIC_CONST_STRING(str_domain,   "domain");
361
KOS_DECLARE_STATIC_CONST_STRING(str_type,     "type");
362
KOS_DECLARE_STATIC_CONST_STRING(str_protocol, "protocol");
363

364
static const KOS_CONVERT socket_args[4] = {
365
    { KOS_CONST_ID(str_domain),   TO_SMALL_INT(PF_INET),     0, 0, KOS_NATIVE_INT32 },
366
    { KOS_CONST_ID(str_type),     TO_SMALL_INT(SOCK_STREAM), 0, 0, KOS_NATIVE_INT32 },
367
    { KOS_CONST_ID(str_protocol), TO_SMALL_INT(0),           0, 0, KOS_NATIVE_INT32 },
368
    KOS_DEFINE_TAIL_ARG()
369
};
370

371
/* @item net socket()
372
 *
373
 *     socket(domain = AF_INET, type = SOCK_STREAM, protocol = 0)
374
 *
375
 * Socket object class.
376
 *
377
 * Returns created socket object.
378
 *
379
 * `domain` is the communication domain, e.g. `AF_INET`, `AF_INET6` or `AF_LOCAL`.
380
 * `type` specifies the semantics of communication, e.g. `SOCK_STREAM`, `SOCK_DGRAM` or `SOCK_RAW`.
381
 * `protocol` specifies particular protocol, 0 typically indicates default protocol.
382
 *
383
 * On error throws an exception.
384
 */
385
static KOS_OBJ_ID kos_socket(KOS_CONTEXT ctx,
15✔
386
                             KOS_OBJ_ID  this_obj,
387
                             KOS_OBJ_ID  args_obj)
388
{
389
    KOS_LOCAL  this_;
390
    KOS_LOCAL  ret;
391
    KOS_SOCKET socket_fd    = KOS_INVALID_SOCKET;
15✔
392
    int32_t    arg_domain   = 0;
15✔
393
    int32_t    arg_type     = 0;
15✔
394
    int32_t    arg_protocol = 0;
15✔
395
    int        saved_errno  = 0;
15✔
396
    int        error;
397

398
    assert(KOS_get_array_size(args_obj) >= 3);
15✔
399

400
    KOS_init_local(     ctx, &ret);
15✔
401
    KOS_init_local_with(ctx, &this_, this_obj);
15✔
402

403
    TRY(KOS_extract_native_from_array(ctx, args_obj, "argument", socket_args, KOS_NULL,
15✔
404
                                      &arg_domain, &arg_type, &arg_protocol));
405

406
    KOS_suspend_context(ctx);
15✔
407

408
    reset_last_error();
15✔
409

410
    socket_fd = socket(arg_domain, arg_type, arg_protocol);
15✔
411

412
    if (socket_fd == -1)
15✔
413
        saved_errno = get_error();
×
414

415
    KOS_resume_context(ctx);
15✔
416

417
    if (socket_fd == KOS_INVALID_SOCKET) {
15✔
418
        KOS_raise_errno_value(ctx, "socket", saved_errno);
×
419
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
420
    }
421

422
    ret.o = KOS_new_object_with_private(ctx, this_.o, &socket_priv_class, socket_finalize);
15✔
423
    TRY_OBJID(ret.o);
15✔
424

425
    TRY(KOS_set_builtin_dynamic_property(ctx,
15✔
426
                                         ret.o,
427
                                         KOS_CONST_ID(str_blocking),
428
                                         KOS_get_module(ctx),
429
                                         get_blocking,
430
                                         set_blocking));
431

432
    TRY(set_socket_object(ctx, ret.o, socket_fd, arg_domain));
15✔
433

434
cleanup:
15✔
435
    ret.o = KOS_destroy_top_locals(ctx, &this_, &ret);
15✔
436

437
    return error ? KOS_BADPTR : ret.o;
15✔
438
}
439

440
/* @item net socket.prototype.accept()
441
 *
442
 *     socket.prototype.accept()
443
 *
444
 * Accepts pending connection on a listening socket.
445
 *
446
 * The `this` socket must be in a listening state, i.e.
447
 * `listen()` must have been called on it.
448
 *
449
 * Returns an object with two properties:
450
 * - `socket`: new socket with the accepted connection,
451
 * - `address`: address of the remote host from which the connetion has been made.
452
 *
453
 * On error throws an exception.
454
 */
455
static KOS_OBJ_ID kos_accept(KOS_CONTEXT ctx,
1✔
456
                             KOS_OBJ_ID  this_obj,
457
                             KOS_OBJ_ID  args_obj)
458
{
459
    KOS_LOCAL          this_;
460
    KOS_LOCAL          sock;
461
    KOS_LOCAL          ret;
462
    KOS_GENERIC_ADDR   addr;
463
    KOS_SOCKET_HOLDER *socket_holder = KOS_NULL;
1✔
464
    KOS_OBJ_ID         proto_obj;
465
    KOS_SOCKET         socket_fd     = KOS_INVALID_SOCKET;
1✔
466
    ADDR_LEN           addr_len      = (ADDR_LEN)sizeof(addr);
1✔
467
    int                saved_errno   = 0;
1✔
468
    int                error;
469

470
    KOS_init_local(     ctx, &ret);
1✔
471
    KOS_init_local(     ctx, &sock);
1✔
472
    KOS_init_local_with(ctx, &this_, this_obj);
1✔
473

474
    TRY(acquire_socket_object(ctx, this_obj, &socket_holder));
1✔
475

476
    KOS_suspend_context(ctx);
1✔
477

478
    reset_last_error();
1✔
479

480
    socket_fd = accept(get_socket(socket_holder), (struct sockaddr *)&addr, &addr_len);
1✔
481

482
    if (socket_fd == -1)
1✔
483
        saved_errno = get_error();
×
484

485
    KOS_resume_context(ctx);
1✔
486

487
    if (socket_fd == KOS_INVALID_SOCKET) {
1✔
488
        KOS_raise_errno_value(ctx, "accept", saved_errno);
×
489
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
490
    }
491

492
    proto_obj = KOS_get_prototype(ctx, this_.o);
1✔
493

494
    sock.o = KOS_new_object_with_private(ctx, proto_obj, &socket_priv_class, socket_finalize);
1✔
495
    TRY_OBJID(sock.o);
1✔
496

497
    TRY(set_socket_object(ctx, sock.o, socket_fd, socket_holder->family));
1✔
498

499
    ret.o = KOS_new_object(ctx);
1✔
500
    TRY_OBJID(ret.o);
1✔
501

502
    TRY(KOS_set_property(ctx, ret.o, KOS_CONST_ID(str_socket), sock.o));
1✔
503

504
cleanup:
1✔
505
    release_socket(socket_holder);
1✔
506

507
    ret.o = KOS_destroy_top_locals(ctx, &this_, &ret);
1✔
508

509
    return error ? KOS_BADPTR : ret.o;
1✔
510
}
511

512
KOS_DECLARE_STATIC_CONST_STRING(str_empty, "");
513

514
static const KOS_CONVERT bind_args[3] = {
515
    { KOS_CONST_ID(str_address), KOS_CONST_ID(str_empty), 0, 0, KOS_NATIVE_STRING_PTR },
516
    { KOS_CONST_ID(str_port),    TO_SMALL_INT(0),         0, 0, KOS_NATIVE_UINT16     },
517
    KOS_DEFINE_TAIL_ARG()
518
};
519

520
/* @item net socket.prototype.bind()
521
 *
522
 *     socket.prototype.bind(address = "", port = 0)
523
 *
524
 * Binds an address to a socket.
525
 *
526
 * `address` specifies the IP address to bind.  For IPv4 and IPv6 sockets this is
527
 * a hostname or a numeric IP address.  If not specified, the default address
528
 * 0.0.0.0 is bound.
529
 *
530
 * `port` specifies the port to bind.  It is an integer value from 0 to 65535.
531
 * If `port` is not specified, a random port number is chosen.  Ports below 1024
532
 * are typically reserved for system services and require administrator privileges.
533
 *
534
 * Returns the socket itself (`this`).
535
 *
536
 * On error throws an exception.
537
 */
538
static KOS_OBJ_ID kos_bind(KOS_CONTEXT ctx,
14✔
539
                           KOS_OBJ_ID  this_obj,
540
                           KOS_OBJ_ID  args_obj)
541
{
542
    struct KOS_MEMPOOL_S alloc;
543
    KOS_GENERIC_ADDR     addr;
544
    ADDR_LEN             addr_len;
545
    KOS_LOCAL            this_;
546
    char                *address_cstr  = KOS_NULL;
14✔
547
    KOS_SOCKET_HOLDER   *socket_holder = KOS_NULL;
14✔
548
    int                  saved_errno;
549
    int                  error;
550
    uint16_t             port          = 0;
14✔
551

552
    KOS_init_local_with(ctx, &this_, this_obj);
14✔
553

554
    KOS_mempool_init_small(&alloc, 512U);
14✔
555

556
    TRY(KOS_extract_native_from_array(ctx, args_obj, "argument", bind_args, &alloc, &address_cstr, &port));
14✔
557

558
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
14✔
559

560
    TRY(get_address(ctx, socket_holder, address_cstr, port, &addr, &addr_len));
14✔
561

562
    KOS_suspend_context(ctx);
14✔
563

564
    reset_last_error();
14✔
565

566
    error = bind(get_socket(socket_holder), &addr.addr, addr_len);
14✔
567

568
    saved_errno = get_error();
14✔
569

570
    KOS_resume_context(ctx);
14✔
571

572
    if (error) {
14✔
573
        KOS_raise_errno_value(ctx, "bind", saved_errno);
×
574
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
575
    }
576

577
cleanup:
14✔
578
    release_socket(socket_holder);
14✔
579

580
    KOS_mempool_destroy(&alloc);
14✔
581

582
    this_.o = KOS_destroy_top_local(ctx, &this_);
14✔
583

584
    return error ? KOS_BADPTR : this_.o;
14✔
585
}
586

587
/* @item net socket.prototype.close()
588
 *
589
 *     socket.prototype.close()
590
 *
591
 * Closes the socket object if it is still opened.
592
 *
593
 * Returns the socket itself (`this`).
594
 *
595
 * On error throws an exception.
596
 */
597
/* @item net socket.prototype.release()
598
 *
599
 *     socket.prototype.release()
600
 *
601
 * Closes the socket object if it is still opened.  This function is identical
602
 * with `socket.prototype.close()` and it is suitable for use with the `with` statement.
603
 *
604
 * Returns the socket itself (`this`).
605
 *
606
 * On error throws an exception.
607
 */
608
static KOS_OBJ_ID kos_close(KOS_CONTEXT ctx,
3✔
609
                            KOS_OBJ_ID  this_obj,
610
                            KOS_OBJ_ID  args_obj)
611
{
612
    KOS_SOCKET_HOLDER *closed_holder;
613
    KOS_SOCKET_HOLDER *socket_holder;
614

615
    if (GET_OBJ_TYPE(this_obj) != OBJ_OBJECT) {
3✔
616
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_socket_not_open));
×
617
        return KOS_BADPTR;
×
618
    }
619

620
    closed_holder = make_socket_holder(ctx, KOS_INVALID_SOCKET, -1);
3✔
621
    if ( ! closed_holder)
3✔
622
        return KOS_BADPTR;
×
623

624
    socket_holder = (KOS_SOCKET_HOLDER *)KOS_object_swap_private(this_obj, &socket_priv_class, closed_holder);
3✔
625

626
    release_socket(socket_holder);
3✔
627

628
    return this_obj;
3✔
629

630
}
631

632
static const KOS_CONVERT connect_args[3] = {
633
    { KOS_CONST_ID(str_address), KOS_BADPTR, 0, 0, KOS_NATIVE_STRING_PTR },
634
    { KOS_CONST_ID(str_port),    KOS_BADPTR, 0, 0, KOS_NATIVE_UINT16     },
635
    KOS_DEFINE_TAIL_ARG()
636
};
637

638
/* @item net socket.prototype.connect()
639
 *
640
 *     socket.prototype.connect(address, port)
641
 *
642
 * Connects the socket to a remote address.
643
 *
644
 * `address` specifies the IP address to connect to.  For IPv4 and IPv6 sockets this is
645
 * a hostname or a numeric IP address.
646
 *
647
 * `port` specifies the port to bind.  It is an integer value from 1 to 65535.
648
 *
649
 * Returns the socket itself (`this`).
650
 *
651
 * On error throws an exception.
652
 */
653
static KOS_OBJ_ID kos_connect(KOS_CONTEXT ctx,
1✔
654
                              KOS_OBJ_ID  this_obj,
655
                              KOS_OBJ_ID  args_obj)
656
{
657
    struct KOS_MEMPOOL_S alloc;
658
    KOS_GENERIC_ADDR     addr;
659
    ADDR_LEN             addr_len;
660
    KOS_LOCAL            this_;
661
    char                *address_cstr  = KOS_NULL;
1✔
662
    KOS_SOCKET_HOLDER   *socket_holder = KOS_NULL;
1✔
663
    int                  saved_errno;
664
    int                  error;
665
    uint16_t             port          = 0;
1✔
666

667
    KOS_init_local_with(ctx, &this_, this_obj);
1✔
668

669
    KOS_mempool_init_small(&alloc, 512U);
1✔
670

671
    TRY(KOS_extract_native_from_array(ctx, args_obj, "argument", connect_args, &alloc, &address_cstr, &port));
1✔
672

673
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
1✔
674

675
    TRY(get_address(ctx, socket_holder, address_cstr, port, &addr, &addr_len));
1✔
676

677
    KOS_suspend_context(ctx);
1✔
678

679
    reset_last_error();
1✔
680

681
    error = connect(get_socket(socket_holder), &addr.addr, addr_len);
1✔
682

683
    saved_errno = get_error();
1✔
684

685
    KOS_resume_context(ctx);
1✔
686

687
    if (error) {
1✔
688
        KOS_raise_errno_value(ctx, "connect", saved_errno);
×
689
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
690
    }
691

692
cleanup:
1✔
693
    release_socket(socket_holder);
1✔
694

695
    KOS_mempool_destroy(&alloc);
1✔
696

697
    this_.o = KOS_destroy_top_local(ctx, &this_);
1✔
698

699
    return error ? KOS_BADPTR : this_.o;
1✔
700
}
701

702
KOS_DECLARE_STATIC_CONST_STRING(str_backlog, "backlog");
703

704
static const KOS_CONVERT listen_args[2] = {
705
    { KOS_CONST_ID(str_backlog), TO_SMALL_INT(5), 0, 0, KOS_NATIVE_INT32 },
706
    KOS_DEFINE_TAIL_ARG()
707
};
708

709
/* @item net socket.prototype.listen()
710
 *
711
 *     socket.prototype.listen(backlog = 5)
712
 *
713
 * Prepares a socket for accepting connections.
714
 *
715
 * `backlog` specifies how many connections can be waiting.
716
 *
717
 * Returns the socket itself (`this`).
718
 *
719
 * On error throws an exception.
720
 */
721
static KOS_OBJ_ID kos_listen(KOS_CONTEXT ctx,
1✔
722
                             KOS_OBJ_ID  this_obj,
723
                             KOS_OBJ_ID  args_obj)
724
{
725
    KOS_LOCAL          this_;
726
    KOS_SOCKET_HOLDER *socket_holder = KOS_NULL;
1✔
727
    int                saved_errno;
728
    int                error;
729
    int32_t            backlog       = 0;
1✔
730

731
    KOS_init_local_with(ctx, &this_, this_obj);
1✔
732

733
    TRY(KOS_extract_native_from_array(ctx, args_obj, "argument", listen_args, KOS_NULL, &backlog));
1✔
734

735
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
1✔
736

737
    KOS_suspend_context(ctx);
1✔
738

739
    reset_last_error();
1✔
740

741
    error = listen(get_socket(socket_holder), backlog);
1✔
742

743
    saved_errno = get_error();
1✔
744

745
    KOS_resume_context(ctx);
1✔
746

747
    if (error) {
1✔
748
        KOS_raise_errno_value(ctx, "listen", saved_errno);
×
749
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
750
    }
751

752
cleanup:
1✔
753
    release_socket(socket_holder);
1✔
754

755
    this_.o = KOS_destroy_top_local(ctx, &this_);
1✔
756

757
    return error ? KOS_BADPTR : this_.o;
1✔
758
}
759

760
KOS_DECLARE_STATIC_CONST_STRING(str_buffer, "buffer");
761
KOS_DECLARE_STATIC_CONST_STRING(str_size,   "size");
762

763
/* @item net socket.prototype.read()
764
 *
765
 *     socket.prototype.read(size = 4096 [, buffer])
766
 *
767
 * This is the same function as `socket.prototype.recv()`.
768
 */
769
/* @item net socket.prototype.recv()
770
 *
771
 *     socket.prototype.recv(size = 4096 [, buffer])
772
 *
773
 * Receives a variable number of bytes from a connected socket object.
774
 *
775
 * Receives as many bytes as it can, up to the specified `size`.
776
 *
777
 * `size` is the maximum bytes to receive.  `size` defaults to 4096.  Fewer
778
 * bytes can be received if no more bytes are available.
779
 *
780
 * If `buffer` is specified, bytes are appended to it and that buffer is
781
 * returned instead of creating a new buffer.
782
 *
783
 * Returns a buffer containing the bytes read.
784
 *
785
 * On error throws an exception.
786
 */
787
static KOS_OBJ_ID kos_recv(KOS_CONTEXT ctx,
1✔
788
                           KOS_OBJ_ID  this_obj,
789
                           KOS_OBJ_ID  args_obj)
790
{
791
    KOS_LOCAL          args;
792
    KOS_LOCAL          buf;
793
    int64_t            num_read;
794
    int64_t            to_read;
795
    KOS_SOCKET_HOLDER *socket_holder = KOS_NULL;
1✔
796
    uint8_t           *data;
797
    KOS_OBJ_ID         arg;
798
    uint32_t           offset;
799
    int                error         = KOS_SUCCESS;
1✔
800
    int                saved_errno   = 0;
1✔
801

802
    assert(KOS_get_array_size(args_obj) >= 2);
1✔
803

804
    KOS_init_local(     ctx, &buf);
1✔
805
    KOS_init_local_with(ctx, &args, args_obj);
1✔
806

807
    TRY(acquire_socket_object(ctx, this_obj, &socket_holder));
1✔
808

809
    arg = KOS_array_read(ctx, args.o, 0);
1✔
810
    TRY_OBJID(arg);
1✔
811

812
    TRY(KOS_get_integer(ctx, arg, &to_read));
1✔
813

814
    if (to_read < 1)
1✔
815
        to_read = 1;
×
816

817
    buf.o = KOS_array_read(ctx, args.o, 1);
1✔
818
    TRY_OBJID(buf.o);
1✔
819

820
    if (buf.o == KOS_VOID)
1✔
821
        buf.o = KOS_new_buffer(ctx, 0);
1✔
822
    else if (GET_OBJ_TYPE(buf.o) != OBJ_BUFFER)
×
823
        RAISE_EXCEPTION_STR(str_err_not_buffer);
×
824

825
    offset = KOS_get_buffer_size(buf.o);
1✔
826

827
    if (to_read > (int64_t)(0xFFFFFFFFU - offset))
1✔
828
        RAISE_EXCEPTION_STR(str_err_too_many_to_read);
×
829

830
    TRY(KOS_buffer_resize(ctx, buf.o, (unsigned)(offset + to_read)));
1✔
831

832
    data = KOS_buffer_data(ctx, buf.o);
1✔
833

834
    if ( ! data)
1✔
835
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
836

837
    KOS_suspend_context(ctx);
1✔
838

839
    reset_last_error();
1✔
840

841
    num_read = recv(get_socket(socket_holder), (char *)(data + offset), (DATA_LEN)to_read, 0);
1✔
842

843
    if (num_read < -1)
1✔
844
        saved_errno = get_error();
×
845

846
    KOS_resume_context(ctx);
1✔
847

848
    assert(num_read <= to_read);
1✔
849

850
    TRY(KOS_buffer_resize(ctx, buf.o, (unsigned)(offset + num_read)));
1✔
851

852
    if (saved_errno) {
1✔
853
        KOS_raise_errno_value(ctx, "recv", saved_errno);
×
854
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
855
    }
856

857
cleanup:
1✔
858
    release_socket(socket_holder);
1✔
859

860
    buf.o = KOS_destroy_top_locals(ctx, &args, &buf);
1✔
861

862
    return error ? KOS_BADPTR : buf.o;
1✔
863
}
864

865
/* @item net socket.prototype.recvfrom()
866
 *
867
 *     socket.prototype.recvfrom()
868
 */
869
static KOS_OBJ_ID kos_recvfrom(KOS_CONTEXT ctx,
×
870
                               KOS_OBJ_ID  this_obj,
871
                               KOS_OBJ_ID  args_obj)
872
{
873
    /* TODO */
874
    return KOS_VOID;
×
875
}
876

877
/* @item net socket.prototype.wait()
878
 *
879
 *     socket.prototype.wait(timeout_sec = void)
880
 *
881
 * Waits for data to be available to read from the socket.
882
 *
883
 * On a connected or datagram socket, this function waits for data to be
884
 * received and be ready to read via the `recv()` or `recvfrom()` function.
885
 *
886
 * On a listening socket, this function waits for for a connection to be
887
 * established and the socket to be ready to accept a new connection.
888
 *
889
 * `timeout_sec` is the timeout value in seconds.  This can be a `float`, so
890
 * for example to wait for 500 ms, `0.5` can be passed.  If this is `void`
891
 * (which is the default) the function will wait indefinitely.
892
 *
893
 * Returns a boolean indicating whether the wait operation succeeded.
894
 * The return value `true` indicates that there is data available on the
895
 * socket to read.  The return value `false` indicates that the timeout
896
 * was reached.
897
 *
898
 * On error throws an exception.
899
 */
900
static KOS_OBJ_ID kos_wait(KOS_CONTEXT ctx,
×
901
                           KOS_OBJ_ID  this_obj,
902
                           KOS_OBJ_ID  args_obj)
903
{
904
#ifdef _WIN32
905
    TIMEVAL            time_value;
906
    TIMEVAL           *timeout_tv  = KOS_NULL;
907
#else
908
    struct timeval     time_value;
909
    struct timeval    *timeout_tv  = KOS_NULL;
×
910
#endif
911
    KOS_NUMERIC        timeout;
912
    fd_set             fds;
913
    KOS_SOCKET_HOLDER *socket_holder;
914
    KOS_LOCAL          args;
915
    KOS_LOCAL          this_;
916
    KOS_OBJ_ID         wait_obj;
917
    KOS_OBJ_ID         ret_obj     = KOS_FALSE;
×
918
    int                nfds        = 0;
×
919
    int                saved_errno = 0;
×
920
    int                error;
921

922
    memset(&timeout, 0, sizeof(timeout));
×
923

924
    KOS_init_local_with(ctx, &this_, this_obj);
×
925
    KOS_init_local_with(ctx, &args,  args_obj);
×
926

927
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
×
928

929
    assert(KOS_get_array_size(args.o) >= 1);
×
930

931
    wait_obj = KOS_array_read(ctx, args.o, 0);
×
932
    TRY_OBJID(wait_obj);
×
933

934
    if (wait_obj != KOS_VOID)
×
935
        TRY(KOS_get_numeric_arg(ctx, args.o, 0, &timeout));
×
936

937
    FD_ZERO(&fds);
×
938
    FD_SET(socket_holder->socket_fd, &fds);
×
939

940
#ifndef _WIN32
941
    nfds = socket_holder->socket_fd + 1;
×
942
#endif
943

944
    if (timeout.type != KOS_NON_NUMERIC) {
×
945
        uint64_t tv_usec;
946

947
        if (timeout.type == KOS_INTEGER_VALUE)
×
948
            tv_usec = (uint64_t)timeout.u.i * 1000000U;
×
949
        else
950
            tv_usec = (uint64_t)floor(timeout.u.d * 1000000.0);
×
951

952
        memset(&time_value, 0, sizeof(time_value));
×
953
        time_value.tv_sec  = (TIME_FRAGMENT)(tv_usec / 1000000U);
×
954
        time_value.tv_usec = (TIME_FRAGMENT)(tv_usec % 1000000U);
×
955

956
        timeout_tv = &time_value;
×
957
    }
958

959
    KOS_suspend_context(ctx);
×
960

961
    reset_last_error();
×
962

963
    nfds = select(nfds, &fds, KOS_NULL, KOS_NULL, timeout_tv);
×
964
    if (nfds < 0)
×
965
        saved_errno = get_error();
×
966

967
    KOS_resume_context(ctx);
×
968

969
    if (saved_errno) {
×
970
        KOS_raise_errno_value(ctx, "select", saved_errno);
×
971
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
972
    }
973

974
    ret_obj = KOS_BOOL(nfds);
×
975

976
cleanup:
×
977
    release_socket(socket_holder);
×
978

979
    KOS_destroy_top_locals(ctx, &args, &this_);
×
980

981
    return error ? KOS_BADPTR : ret_obj;
×
982
}
983

984
/* @item net socket.prototype.blocking
985
 *
986
 *     socket.prototype.blocking
987
 *
988
 * Blocking state of a socket.
989
 *
990
 * A newly created socket is in a blocking state.  It can be
991
 * changed to non-blocking by writing `false` to this property.
992
 * This property can also be read to determine whether a socket
993
 * is blocking or non-blocking.
994
 */
995
static KOS_OBJ_ID get_blocking(KOS_CONTEXT ctx,
×
996
                               KOS_OBJ_ID  this_obj,
997
                               KOS_OBJ_ID  args_obj)
998
{
999
    KOS_SOCKET_HOLDER *socket_holder = KOS_NULL;
×
1000
    int                blocking      = 1;
×
1001
    int                saved_errno   = 0;
×
1002
    int                error         = KOS_SUCCESS;
×
1003

1004
    TRY(acquire_socket_object(ctx, this_obj, &socket_holder));
×
1005

1006
    KOS_suspend_context(ctx);
×
1007

1008
    reset_last_error();
×
1009

1010
#ifdef _WIN32
1011
    blocking = socket_holder->blocking;
1012
#else
1013
    {
1014
        const int flags = fcntl(get_socket(socket_holder), F_GETFL);
×
1015

1016
        if (flags != -1)
×
1017
            blocking = ! (flags & O_NONBLOCK);
×
1018
        else
1019
            saved_errno = get_error();
×
1020
    }
1021
#endif
1022

1023
    KOS_resume_context(ctx);
×
1024

1025
    if (saved_errno) {
×
1026
        KOS_raise_errno_value(ctx, "fcntl", saved_errno);
×
1027
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1028
    }
1029

1030
cleanup:
×
1031
    release_socket(socket_holder);
×
1032

1033
    return error ? KOS_BADPTR : KOS_BOOL(blocking);
×
1034
}
1035

1036
static KOS_OBJ_ID set_blocking(KOS_CONTEXT ctx,
×
1037
                               KOS_OBJ_ID  this_obj,
1038
                               KOS_OBJ_ID  args_obj)
1039
{
1040
    KOS_LOCAL          this_;
1041
    KOS_SOCKET_HOLDER *socket_holder = KOS_NULL;
×
1042
    KOS_OBJ_ID         arg;
1043
    int                blocking;
1044
    int                saved_errno   = 0;
×
1045
    int                error         = KOS_SUCCESS;
×
1046

1047
    assert(KOS_get_array_size(args_obj) >= 1);
×
1048

1049
    KOS_init_local_with(ctx, &this_, this_obj);
×
1050

1051
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
×
1052

1053
    arg = KOS_array_read(ctx, args_obj, 0);
×
1054
    TRY_OBJID(arg);
×
1055

1056
    if (GET_OBJ_TYPE(arg) != OBJ_BOOLEAN) {
×
1057
        KOS_raise_printf(ctx, "blocking is a boolean, cannot set %s",
×
1058
                         KOS_get_type_name(GET_OBJ_TYPE(arg)));
×
1059
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1060
    }
1061

1062
    blocking = KOS_get_bool(arg);
×
1063

1064
    KOS_suspend_context(ctx);
×
1065

1066
    reset_last_error();
×
1067

1068
#ifdef _WIN32
1069
    {
1070
        unsigned long non_blocking = blocking ? 0 : 1;
1071

1072
        if (ioctlsocket(get_socket(socket_holder), FIONBIO, &non_blocking) == 0)
1073
            socket_holder->blocking = blocking;
1074
        else
1075
            saved_errno = get_error();
1076
    }
1077
#else
1078
    {
1079
        int flags = fcntl(get_socket(socket_holder), F_GETFL);
×
1080

1081
        if (flags == -1)
×
1082
            saved_errno = get_error();
×
1083
        else {
1084
            if (blocking)
×
1085
                flags &= ~O_NONBLOCK;
×
1086
            else
1087
                flags |= O_NONBLOCK;
×
1088

1089
            if (fcntl(get_socket(socket_holder), F_SETFL, flags) == -1)
×
1090
                saved_errno = get_error();
×
1091
        }
1092
    }
1093
#endif
1094

1095
    KOS_resume_context(ctx);
×
1096

1097
    if (saved_errno) {
×
1098
        KOS_raise_errno_value(ctx, "fcntl", saved_errno);
×
1099
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1100
    }
1101

1102
cleanup:
×
1103
    release_socket(socket_holder);
×
1104

1105
    this_.o = KOS_destroy_top_local(ctx, &this_);
×
1106

1107
    return error ? KOS_BADPTR : this_.o;
×
1108
}
1109

1110
/* @item net socket.prototype.write()
1111
 *
1112
 *     socket.prototype.write(values...)
1113
 *
1114
 * This is the same function as `socket.prototype.send()`.
1115
 */
1116
/* @item net socket.prototype.send()
1117
 *
1118
 *     socket.prototype.send(values...)
1119
 *
1120
 * Sends strings or buffers containing bytes through a connected socket.
1121
 *
1122
 * Each argument is either a buffer or a string object.  Empty buffers
1123
 * or strings are ignored and nothing is sent through the socket.
1124
 *
1125
 * If an argument is a string, it is converted to UTF-8 bytes representation
1126
 * before being sent.
1127
 *
1128
 * Invoking this function without any arguments doesn't send anything
1129
 * through the socket but ensures that the socket object is correct.
1130
 *
1131
 * Returns the socket itself (`this`).
1132
 *
1133
 * On error throws an exception.
1134
 */
1135
static KOS_OBJ_ID kos_send(KOS_CONTEXT ctx,
1✔
1136
                           KOS_OBJ_ID  this_obj,
1137
                           KOS_OBJ_ID  args_obj)
1138
{
1139
    KOS_VECTOR         cstr;
1140
    KOS_LOCAL          print_args;
1141
    KOS_LOCAL          arg;
1142
    KOS_LOCAL          args;
1143
    KOS_LOCAL          this_;
1144
    KOS_SOCKET_HOLDER *socket_holder = KOS_NULL;
1✔
1145
    const uint32_t     num_args      = KOS_get_array_size(args_obj);
1✔
1146
    uint32_t           i_arg;
1147
    int                error;
1148

1149
    KOS_vector_init(&cstr);
1✔
1150

1151
    KOS_init_locals(ctx, &print_args, &arg, &args, &this_, kos_end_locals);
1✔
1152

1153
    args.o  = args_obj;
1✔
1154
    this_.o = this_obj;
1✔
1155

1156
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
1✔
1157

1158
    for (i_arg = 0; i_arg < num_args; i_arg++) {
2✔
1159

1160
        int64_t num_writ    = 0;
1✔
1161
        int     saved_errno = 0;
1✔
1162

1163
        arg.o = KOS_array_read(ctx, args.o, i_arg);
1✔
1164
        TRY_OBJID(arg.o);
1✔
1165

1166
        if (GET_OBJ_TYPE(arg.o) == OBJ_BUFFER) {
1✔
1167

1168
            const size_t to_write = (size_t)KOS_get_buffer_size(arg.o);
×
1169

1170
            if (to_write > 0) {
×
1171

1172
                const uint8_t *data = KOS_buffer_data_const(arg.o);
×
1173

1174
                if (kos_is_heap_object(KOS_atomic_read_relaxed_obj(OBJPTR(BUFFER, arg.o)->data))) {
×
1175

1176
                    if (KOS_vector_resize(&cstr, to_write)) {
×
1177
                        KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
×
1178
                        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1179
                    }
1180

1181
                    memcpy(cstr.buffer, data, to_write);
×
1182
                    data = (uint8_t *)cstr.buffer;
×
1183
                }
1184
                else {
1185
                    assert(kos_is_tracked_object(KOS_atomic_read_relaxed_obj(OBJPTR(BUFFER, arg.o)->data)));
×
1186
                }
1187

1188
                KOS_suspend_context(ctx);
×
1189

1190
                reset_last_error();
×
1191

1192
                num_writ = send(get_socket(socket_holder), (const char *)data, (DATA_LEN)to_write, 0);
×
1193

1194
                if (num_writ < 0)
×
1195
                    saved_errno = get_error();
×
1196

1197
                KOS_resume_context(ctx);
×
1198
            }
1199
        }
1200
        else if (GET_OBJ_TYPE(arg.o) == OBJ_STRING) {
1✔
1201

1202
            if (IS_BAD_PTR(print_args.o)) {
1✔
1203
                print_args.o = KOS_new_array(ctx, 1);
1✔
1204
                TRY_OBJID(print_args.o);
1✔
1205
            }
1206

1207
            TRY(KOS_array_write(ctx, print_args.o, 0, arg.o));
1✔
1208

1209
            TRY(KOS_print_to_cstr_vec(ctx, print_args.o, KOS_DONT_QUOTE, &cstr, " ", 1));
1✔
1210

1211
            if (cstr.size) {
1✔
1212
                KOS_suspend_context(ctx);
1✔
1213

1214
                reset_last_error();
1✔
1215

1216
                num_writ = send(get_socket(socket_holder), cstr.buffer, (DATA_LEN)(cstr.size - 1), 0);
1✔
1217

1218
                if (num_writ < 0)
1✔
1219
                    saved_errno = get_error();
×
1220

1221
                KOS_resume_context(ctx);
1✔
1222
            }
1223

1224
            cstr.size = 0;
1✔
1225
        }
1226
        else
1227
            RAISE_EXCEPTION_STR(str_err_not_buffer_or_str);
×
1228

1229
        if (saved_errno) {
1✔
1230
            KOS_raise_errno_value(ctx, "send", saved_errno);
×
1231
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1232
        }
1233
    }
1234

1235
cleanup:
1✔
1236
    release_socket(socket_holder);
1✔
1237

1238
    KOS_vector_destroy(&cstr);
1✔
1239

1240
    this_.o = KOS_destroy_top_locals(ctx, &print_args, &this_);
1✔
1241

1242
    return error ? KOS_BADPTR : this_.o;
1✔
1243
}
1244

1245
/* @item net socket.prototype.sendto()
1246
 *
1247
 *     socket.prototype.sendto()
1248
 */
1249
static KOS_OBJ_ID kos_sendto(KOS_CONTEXT ctx,
×
1250
                             KOS_OBJ_ID  this_obj,
1251
                             KOS_OBJ_ID  args_obj)
1252
{
1253
    /* TODO */
1254
    return KOS_VOID;
×
1255
}
1256

1257
#ifdef _WIN32
1258
typedef BOOL SOCK_OPT_BOOL;
1259
#else
1260
typedef int SOCK_OPT_BOOL;
1261
#endif
1262

1263
static KOS_OBJ_ID getsockopt_bool(KOS_CONTEXT        ctx,
×
1264
                                  KOS_SOCKET_HOLDER *socket_holder,
1265
                                  int                option)
1266
{
1267
    SOCK_OPT_BOOL bool_value  = 0;
×
1268
    ADDR_LEN      opt_size    = (ADDR_LEN)sizeof(bool_value);
×
1269
    int           error       = 0;
×
1270
    int           saved_errno = 0;
×
1271

1272
    KOS_suspend_context(ctx);
×
1273

1274
    reset_last_error();
×
1275

1276
    error = getsockopt(get_socket(socket_holder),
×
1277
                       SOL_SOCKET,
1278
                       option,
1279
                       (char *)&bool_value,
1280
                       &opt_size);
1281

1282
    if (error)
×
1283
        saved_errno = get_error();
×
1284

1285
    KOS_resume_context(ctx);
×
1286

1287
    if (error) {
×
1288
        KOS_raise_errno_value(ctx, "setsockopt", saved_errno);
×
1289
        return KOS_BADPTR;
×
1290
    }
1291

1292
    return KOS_BOOL(bool_value);
×
1293
}
1294

1295
static KOS_OBJ_ID getsockopt_int(KOS_CONTEXT        ctx,
×
1296
                                 KOS_SOCKET_HOLDER *socket_holder,
1297
                                 int                option)
1298
{
1299
    ADDR_LEN opt_size    = (ADDR_LEN)sizeof(int);
×
1300
    int      int_value   = 0;
×
1301
    int      error       = 0;
×
1302
    int      saved_errno = 0;
×
1303

1304
    KOS_suspend_context(ctx);
×
1305

1306
    reset_last_error();
×
1307

1308
    error = getsockopt(get_socket(socket_holder),
×
1309
                       SOL_SOCKET,
1310
                       option,
1311
                       (char *)&int_value,
1312
                       &opt_size);
1313

1314
    if (error)
×
1315
        saved_errno = get_error();
×
1316

1317
    KOS_resume_context(ctx);
×
1318

1319
    if (error) {
×
1320
        KOS_raise_errno_value(ctx, "setsockopt", saved_errno);
×
1321
        return KOS_BADPTR;
×
1322
    }
1323

1324
    return KOS_new_int(ctx, int_value);
×
1325
}
1326

1327
static KOS_OBJ_ID getsockopt_time(KOS_CONTEXT        ctx,
×
1328
                                  KOS_SOCKET_HOLDER *socket_holder,
1329
                                  int                option)
1330
{
1331
#ifdef _WIN32
1332
    DWORD          time_value;
1333
#else
1334
    struct timeval time_value;
1335
#endif
1336
    ADDR_LEN       opt_size    = (ADDR_LEN)sizeof(time_value);
×
1337
    int            error       = 0;
×
1338
    int            saved_errno = 0;
×
1339

1340
    memset(&time_value, 0, sizeof(time_value));
×
1341

1342
    KOS_suspend_context(ctx);
×
1343

1344
    reset_last_error();
×
1345

1346
    error = getsockopt(get_socket(socket_holder),
×
1347
                       SOL_SOCKET,
1348
                       option,
1349
                       (char *)&time_value,
1350
                       &opt_size);
1351

1352
    if (error)
×
1353
        saved_errno = get_error();
×
1354

1355
    KOS_resume_context(ctx);
×
1356

1357
    if (error) {
×
1358
        KOS_raise_errno_value(ctx, "setsockopt", saved_errno);
×
1359
        return KOS_BADPTR;
×
1360
    }
1361

1362
#ifdef _WIN32
1363
    return KOS_new_float(ctx, (double)time_value / 1000.0);
1364
#else
1365
    return KOS_new_float(ctx, (double)time_value.tv_sec + (double)time_value.tv_usec / 1000000.0);
×
1366
#endif
1367
}
1368

1369
KOS_DECLARE_STATIC_CONST_STRING(str_option, "option");
1370

1371
static const KOS_CONVERT getsockopt_args[2] = {
1372
    KOS_DEFINE_MANDATORY_ARG(str_option),
1373
    KOS_DEFINE_TAIL_ARG()
1374
};
1375

1376
/* @item net socket.prototype.getsockopt()
1377
 *
1378
 *     socket.prototype.getsockopt(option)
1379
 *
1380
 * Returns value of a socket option.
1381
 *
1382
 * `option` is an integer specifying the option to retrieve.
1383
 */
1384
static KOS_OBJ_ID kos_getsockopt(KOS_CONTEXT ctx,
×
1385
                                 KOS_OBJ_ID  this_obj,
1386
                                 KOS_OBJ_ID  args_obj)
1387
{
1388
    KOS_LOCAL          args;
1389
    KOS_LOCAL          this_;
1390
    KOS_LOCAL          value;
1391
    KOS_SOCKET_HOLDER *socket_holder;
1392
    int64_t            option;
1393
    int                error;
1394

1395
    assert(KOS_get_array_size(args_obj) > 0);
×
1396

1397
    KOS_init_local(     ctx, &value);
×
1398
    KOS_init_local_with(ctx, &this_, this_obj);
×
1399
    KOS_init_local_with(ctx, &args,  args_obj);
×
1400

1401
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
×
1402

1403
    value.o = KOS_array_read(ctx, args.o, 0);
×
1404
    TRY_OBJID(value.o);
×
1405

1406
    if (GET_OBJ_TYPE(value.o) > OBJ_INTEGER) {
×
1407
        KOS_raise_printf(ctx, "option argument is %s but expected integer",
×
1408
                         KOS_get_type_name(GET_OBJ_TYPE(value.o)));
×
1409
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1410
    }
1411

1412
    TRY(KOS_get_integer(ctx, value.o, &option));
×
1413

1414
    switch (option) {
×
1415
        case SO_BROADCAST:
×
1416
            value.o = getsockopt_bool(ctx, socket_holder, (int)option);
×
1417
            break;
×
1418

1419
        case SO_DEBUG:
×
1420
            value.o = getsockopt_bool(ctx, socket_holder, (int)option);
×
1421
            break;
×
1422

1423
        case SO_DONTROUTE:
×
1424
            value.o = getsockopt_bool(ctx, socket_holder, (int)option);
×
1425
            break;
×
1426

1427
        case SO_KEEPALIVE:
×
1428
            value.o = getsockopt_bool(ctx, socket_holder, (int)option);
×
1429
            break;
×
1430

1431
        /*
1432
        case SO_LINGER:
1433
        */
1434

1435
        case SO_OOBINLINE:
×
1436
            value.o = getsockopt_bool(ctx, socket_holder, (int)option);
×
1437
            break;
×
1438

1439
        case SO_RCVBUF:
×
1440
            value.o = getsockopt_int(ctx, socket_holder, (int)option);
×
1441
            break;
×
1442

1443
        case SO_RCVTIMEO:
×
1444
            value.o = getsockopt_time(ctx, socket_holder, (int)option);
×
1445
            break;
×
1446

1447
        case SO_REUSEADDR:
×
1448
            value.o = getsockopt_bool(ctx, socket_holder, (int)option);
×
1449
            break;
×
1450

1451
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1452
        case SO_REUSEPORT:
1453
            value.o = getsockopt_bool(ctx, socket_holder, SO_REUSEPORT);
1454
            break;
1455
#endif
1456

1457
        case SO_SNDBUF:
×
1458
            value.o = getsockopt_int(ctx, socket_holder, (int)option);
×
1459
            break;
×
1460

1461
        case SO_SNDTIMEO:
×
1462
            value.o = getsockopt_time(ctx, socket_holder, (int)option);
×
1463
            break;
×
1464

1465
        default:
×
1466
            KOS_raise_printf(ctx, "unknown option %" PRId64, option);
×
1467
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1468
    }
1469

1470
    if (value.o == KOS_BADPTR)
×
1471
        error = KOS_ERROR_EXCEPTION;
×
1472

1473
cleanup:
×
1474
    release_socket(socket_holder);
×
1475

1476
    value.o = KOS_destroy_top_locals(ctx, &args, &value);
×
1477

1478
    return error ? KOS_BADPTR : value.o;
×
1479
}
1480

1481
static int setsockopt_bool(KOS_CONTEXT        ctx,
6✔
1482
                           KOS_SOCKET_HOLDER *socket_holder,
1483
                           int                option,
1484
                           KOS_OBJ_ID         value)
1485
{
1486
    int64_t       val64;
1487
    SOCK_OPT_BOOL bool_value;
1488
    int           error       = 0;
6✔
1489
    int           saved_errno = 0;
6✔
1490

1491
    if (GET_OBJ_TYPE(value) == OBJ_BOOLEAN)
6✔
1492
        val64 = (int)KOS_get_bool(value);
6✔
1493
    else if ( ! IS_NUMERIC_OBJ(value)) {
×
1494
        KOS_raise_printf(ctx, "value argument is %s but expected integer",
×
1495
                         KOS_get_type_name(GET_OBJ_TYPE(value)));
×
1496
        return KOS_ERROR_EXCEPTION;
×
1497
    }
1498
    else {
1499
        error = KOS_get_integer(ctx, value, &val64);
×
1500
        if (error)
×
1501
            return error;
×
1502
    }
1503

1504
    bool_value = (SOCK_OPT_BOOL)(val64 != 0);
6✔
1505

1506
    KOS_suspend_context(ctx);
6✔
1507

1508
    reset_last_error();
6✔
1509

1510
    error = setsockopt(get_socket(socket_holder),
6✔
1511
                       SOL_SOCKET,
1512
                       option,
1513
                       (const char *)&bool_value,
1514
                       (ADDR_LEN)sizeof(bool_value));
1515

1516
    if (error)
6✔
1517
        saved_errno = get_error();
×
1518

1519
    KOS_resume_context(ctx);
6✔
1520

1521
    if (error) {
6✔
1522
        KOS_raise_errno_value(ctx, "setsockopt", saved_errno);
×
1523
        return KOS_ERROR_EXCEPTION;
×
1524
    }
1525

1526
    return KOS_SUCCESS;
6✔
1527
}
1528

1529
static int setsockopt_int(KOS_CONTEXT        ctx,
×
1530
                          KOS_SOCKET_HOLDER *socket_holder,
1531
                          int                option,
1532
                          KOS_OBJ_ID         value)
1533
{
1534
    int64_t val64;
1535
    int     int_value;
1536
    int     error       = 0;
×
1537
    int     saved_errno = 0;
×
1538

1539
    if ( ! IS_NUMERIC_OBJ(value)) {
×
1540
        KOS_raise_printf(ctx, "value argument is %s but expected integer",
×
1541
                         KOS_get_type_name(GET_OBJ_TYPE(value)));
×
1542
        return KOS_ERROR_EXCEPTION;
×
1543
    }
1544

1545
    error = KOS_get_integer(ctx, value, &val64);
×
1546
    if (error)
×
1547
        return error;
×
1548

1549
    int_value = (int)val64;
×
1550

1551
    KOS_suspend_context(ctx);
×
1552

1553
    reset_last_error();
×
1554

1555
    error = setsockopt(get_socket(socket_holder),
×
1556
                       SOL_SOCKET,
1557
                       option,
1558
                       (const char *)&int_value,
1559
                       (ADDR_LEN)sizeof(int_value));
1560

1561
    if (error)
×
1562
        saved_errno = get_error();
×
1563

1564
    KOS_resume_context(ctx);
×
1565

1566
    if (error) {
×
1567
        KOS_raise_errno_value(ctx, "setsockopt", saved_errno);
×
1568
        return KOS_ERROR_EXCEPTION;
×
1569
    }
1570

1571
    return KOS_SUCCESS;
×
1572
}
1573

1574
static int setsockopt_time(KOS_CONTEXT        ctx,
×
1575
                           KOS_SOCKET_HOLDER *socket_holder,
1576
                           int                option,
1577
                           KOS_OBJ_ID         value)
1578
{
1579
    KOS_NUMERIC    numeric = KOS_get_numeric(value);
×
1580
    uint64_t       tv_usec;
1581
#ifdef _WIN32
1582
    DWORD          time_value;
1583
#else
1584
    struct timeval time_value;
1585
#endif
1586
    int            positive    = 0;
×
1587
    int            error       = 0;
×
1588
    int            saved_errno = 0;
×
1589

1590
    switch (numeric.type) {
×
1591

1592
        case KOS_INTEGER_VALUE:
×
1593
            tv_usec  = (uint64_t)numeric.u.i * 1000000U;
×
1594
            positive = numeric.u.i >= 0;
×
1595
            break;
×
1596

1597
        case KOS_FLOAT_VALUE:
×
1598
            tv_usec  = (uint64_t)floor(numeric.u.d * 1000000.0);
×
1599
            positive = numeric.u.d >= 0;
×
1600
            break;
×
1601

1602
        default:
×
1603
            KOS_raise_printf(ctx, "value argument is %s but expected integer",
×
1604
                             KOS_get_type_name(GET_OBJ_TYPE(value)));
×
1605
            return KOS_ERROR_EXCEPTION;
×
1606
    }
1607

1608
    if ( ! positive || (tv_usec / 1000U) > 0x7FFFFFFFU) {
×
1609
        KOS_raise_printf(ctx, "value argument %" PRIu64 " us is out of range", tv_usec);
×
1610
        return KOS_ERROR_EXCEPTION;
×
1611
    }
1612

1613
#ifdef _WIN32
1614
    time_value = (DWORD)(tv_usec + 999U / 1000U);
1615
#else
1616
    time_value.tv_sec  = (TIME_FRAGMENT)(tv_usec / 1000000U);
×
1617
    time_value.tv_usec = (TIME_FRAGMENT)(tv_usec % 1000000U);
×
1618
#endif
1619

1620
    KOS_suspend_context(ctx);
×
1621

1622
    reset_last_error();
×
1623

1624
    error = setsockopt(get_socket(socket_holder),
×
1625
                       SOL_SOCKET,
1626
                       option,
1627
                       (const char *)&time_value,
1628
                       (ADDR_LEN)sizeof(time_value));
1629

1630
    if (error)
×
1631
        saved_errno = get_error();
×
1632

1633
    KOS_resume_context(ctx);
×
1634

1635
    if (error) {
×
1636
        KOS_raise_errno_value(ctx, "setsockopt", saved_errno);
×
1637
        return KOS_ERROR_EXCEPTION;
×
1638
    }
1639

1640
    return KOS_SUCCESS;
×
1641
}
1642

1643
KOS_DECLARE_STATIC_CONST_STRING(str_value, "value");
1644

1645
static const KOS_CONVERT setsockopt_args[3] = {
1646
    KOS_DEFINE_MANDATORY_ARG(str_option),
1647
    KOS_DEFINE_MANDATORY_ARG(str_value),
1648
    KOS_DEFINE_TAIL_ARG()
1649
};
1650

1651
/* @item net socket.prototype.setsockopt()
1652
 *
1653
 *     socket.prototype.setsockopt(option, value)
1654
 *
1655
 * Sets a socket option.
1656
 *
1657
 * `option` is an integer specifying the option to set and
1658
 * `value` is the value to set for this option.
1659
 *
1660
 * Returns the socket itself (`this`).
1661
 *
1662
 * On error throws an exception.
1663
 */
1664
static KOS_OBJ_ID kos_setsockopt(KOS_CONTEXT ctx,
6✔
1665
                                 KOS_OBJ_ID  this_obj,
1666
                                 KOS_OBJ_ID  args_obj)
1667
{
1668
    KOS_LOCAL          args;
1669
    KOS_LOCAL          value;
1670
    KOS_LOCAL          this_;
1671
    KOS_SOCKET_HOLDER *socket_holder;
1672
    int64_t            option;
1673
    int                error;
1674

1675
    assert(KOS_get_array_size(args_obj) > 1);
6✔
1676

1677
    KOS_init_local_with(ctx, &this_, this_obj);
6✔
1678
    KOS_init_local(     ctx, &value);
6✔
1679
    KOS_init_local_with(ctx, &args,  args_obj);
6✔
1680

1681
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
6✔
1682

1683
    value.o = KOS_array_read(ctx, args.o, 0);
6✔
1684
    TRY_OBJID(value.o);
6✔
1685

1686
    if (GET_OBJ_TYPE(value.o) > OBJ_INTEGER) {
6✔
1687
        KOS_raise_printf(ctx, "option argument is %s but expected integer",
×
1688
                         KOS_get_type_name(GET_OBJ_TYPE(value.o)));
×
1689
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1690
    }
1691

1692
    TRY(KOS_get_integer(ctx, value.o, &option));
6✔
1693

1694
    value.o = KOS_array_read(ctx, args.o, 1);
6✔
1695
    TRY_OBJID(value.o);
6✔
1696

1697
    switch (option) {
6✔
1698
        case SO_BROADCAST:
×
1699
            TRY(setsockopt_bool(ctx, socket_holder, (int)option, value.o));
×
1700
            break;
×
1701

1702
        case SO_DEBUG:
×
1703
            TRY(setsockopt_bool(ctx, socket_holder, (int)option, value.o));
×
1704
            break;
×
1705

1706
        case SO_DONTROUTE:
×
1707
            TRY(setsockopt_bool(ctx, socket_holder, (int)option, value.o));
×
1708
            break;
×
1709

1710
        case SO_KEEPALIVE:
×
1711
            TRY(setsockopt_bool(ctx, socket_holder, (int)option, value.o));
×
1712
            break;
×
1713

1714
        /*
1715
        case SO_LINGER:
1716
        */
1717

1718
        case SO_OOBINLINE:
×
1719
            TRY(setsockopt_bool(ctx, socket_holder, (int)option, value.o));
×
1720
            break;
×
1721

1722
        case SO_RCVBUF:
×
1723
            TRY(setsockopt_int(ctx, socket_holder, (int)option, value.o));
×
1724
            break;
×
1725

1726
        case SO_RCVTIMEO:
×
1727
            TRY(setsockopt_time(ctx, socket_holder, (int)option, value.o));
×
1728
            break;
×
1729

1730
        case SO_REUSEADDR:
6✔
1731
            TRY(setsockopt_bool(ctx, socket_holder, (int)option, value.o));
6✔
1732
            break;
6✔
1733

1734
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1735
        case SO_REUSEPORT:
1736
            TRY(setsockopt_bool(ctx, socket_holder, SO_REUSEPORT, value.o));
1737
            break;
1738
#endif
1739

1740
        case SO_SNDBUF:
×
1741
            TRY(setsockopt_int(ctx, socket_holder, (int)option, value.o));
×
1742
            break;
×
1743

1744
        case SO_SNDTIMEO:
×
1745
            TRY(setsockopt_time(ctx, socket_holder, (int)option, value.o));
×
1746
            break;
×
1747

1748
        default:
×
1749
            KOS_raise_printf(ctx, "unknown option %" PRId64, option);
×
1750
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1751
    }
1752

1753
cleanup:
6✔
1754
    release_socket(socket_holder);
6✔
1755

1756
    this_.o = KOS_destroy_top_locals(ctx, &args, &this_);
6✔
1757

1758
    return error ? KOS_BADPTR : this_.o;
6✔
1759
}
1760

1761
KOS_DECLARE_STATIC_CONST_STRING(str_how, "how");
1762

1763
static const KOS_CONVERT shutdown_args[2] = {
1764
    { KOS_CONST_ID(str_how), TO_SMALL_INT(SHUT_RDWR), 0, 0, KOS_NATIVE_INT32 },
1765
    KOS_DEFINE_TAIL_ARG()
1766
};
1767

1768
/* @item net socket.prototype.shutdown()
1769
 *
1770
 *     socket.prototype.shutdown(how = SHUT_RDWR)
1771
 *
1772
 * Shuts down one or two directions of the connection.
1773
 *
1774
 * `how` specifies if only one direction of the connection is closed
1775
 * (`SHUT_RD` or `SHUT_WR`) or both (`SHUT_RDWR`).
1776
 *
1777
 * Returns the socket itself (`this`).
1778
 *
1779
 * On error throws an exception.
1780
 */
1781
static KOS_OBJ_ID kos_shutdown(KOS_CONTEXT ctx,
4✔
1782
                               KOS_OBJ_ID  this_obj,
1783
                               KOS_OBJ_ID  args_obj)
1784
{
1785
    KOS_LOCAL          this_;
1786
    KOS_SOCKET_HOLDER *socket_holder = KOS_NULL;
4✔
1787
    int                saved_errno;
1788
    int                error;
1789
    int32_t            how           = 0;
4✔
1790

1791
    KOS_init_local_with(ctx, &this_, this_obj);
4✔
1792

1793
    TRY(KOS_extract_native_from_array(ctx, args_obj, "argument", shutdown_args, KOS_NULL, &how));
4✔
1794

1795
    TRY(acquire_socket_object(ctx, this_.o, &socket_holder));
4✔
1796

1797
    KOS_suspend_context(ctx);
4✔
1798

1799
    reset_last_error();
4✔
1800

1801
    error = shutdown(get_socket(socket_holder), how);
4✔
1802

1803
    saved_errno = get_error();
4✔
1804

1805
    KOS_resume_context(ctx);
4✔
1806

1807
    if (error) {
4✔
1808
        KOS_raise_errno_value(ctx, "shutdown", saved_errno);
×
1809
        RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
1810
    }
1811

1812
cleanup:
4✔
1813
    release_socket(socket_holder);
4✔
1814

1815
    this_.o = KOS_destroy_top_local(ctx, &this_);
4✔
1816

1817
    return error ? KOS_BADPTR : this_.o;
4✔
1818
}
1819

1820
KOS_INIT_MODULE(net, 0)(KOS_CONTEXT ctx, KOS_OBJ_ID module_obj)
1✔
1821
{
1822
    int       error = KOS_SUCCESS;
1✔
1823
    KOS_LOCAL module;
1824
    KOS_LOCAL socket_proto;
1825

1826
    const KOS_CONVERT recv_args[3] = {
1✔
1827
        KOS_DEFINE_OPTIONAL_ARG(str_size,   TO_SMALL_INT(4096)),
1✔
1828
        KOS_DEFINE_OPTIONAL_ARG(str_buffer, KOS_VOID          ),
1✔
1829
        KOS_DEFINE_TAIL_ARG()
1830
    };
1831

1832
    const KOS_CONVERT wait_args[2] = {
1✔
1833
        KOS_DEFINE_OPTIONAL_ARG(str_timeout_sec, KOS_VOID),
1✔
1834
        KOS_DEFINE_TAIL_ARG()
1835
    };
1836

1837
    KOS_init_debug_output();
1838

1839
    KOS_init_local_with(ctx, &module, module_obj);
1✔
1840
    KOS_init_local(     ctx, &socket_proto);
1✔
1841

1842
#ifdef _WIN32
1843
    {
1844
        WSADATA info;
1845

1846
        KOS_suspend_context(ctx);
1847

1848
        error = WSAStartup(MAKEWORD(2, 2), &info);
1849

1850
        KOS_resume_context(ctx);
1851

1852
        if (error) {
1853
            KOS_raise_last_error(ctx, "WSAStartup", (unsigned)error);
1854
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
1855
        }
1856
    }
1857
#endif
1858

1859
    TRY_ADD_CONSTRUCTOR(    ctx, module.o,                 "socket",     kos_socket,     socket_args, &socket_proto.o);
1✔
1860
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "accept",     kos_accept,     KOS_NULL);
1✔
1861
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "bind",       kos_bind,       bind_args);
1✔
1862
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "close",      kos_close,      KOS_NULL);
1✔
1863
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "connect",    kos_connect,    connect_args);
1✔
1864
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "getsockopt", kos_getsockopt, getsockopt_args);
1✔
1865
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "listen",     kos_listen,     listen_args);
1✔
1866
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "read",       kos_recv,       recv_args);
1✔
1867
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "recv",       kos_recv,       recv_args);
1✔
1868
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "recvfrom",   kos_recvfrom,   KOS_NULL);
1✔
1869
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "release",    kos_close,      KOS_NULL);
1✔
1870
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "wait",       kos_wait,       wait_args);
1✔
1871
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "send",       kos_send,       KOS_NULL);
1✔
1872
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "sendto",     kos_sendto,     KOS_NULL);
1✔
1873
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "setsockopt", kos_setsockopt, setsockopt_args);
1✔
1874
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "shutdown",   kos_shutdown,   shutdown_args);
1✔
1875
    TRY_ADD_MEMBER_FUNCTION(ctx, module.o, socket_proto.o, "write",      kos_send,       KOS_NULL);
1✔
1876

1877
    TRY_ADD_MEMBER_PROPERTY(ctx, module.o, socket_proto.o, "blocking",   get_blocking,   KOS_NULL);
1✔
1878

1879
#ifndef _WIN32
1880
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "AF_LOCAL",     AF_LOCAL);
1✔
1881
#endif
1882
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "AF_INET",      AF_INET);
1✔
1883
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "AF_INET6",     AF_INET6);
1✔
1884

1885
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SOCK_STREAM",  SOCK_STREAM);
1✔
1886
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SOCK_DGRAM",   SOCK_DGRAM);
1✔
1887
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SOCK_RAW",     SOCK_RAW);
1✔
1888

1889
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SHUT_RD",      SHUT_RD);
1✔
1890
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SHUT_RDWR",    SHUT_RDWR);
1✔
1891
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SHUT_WR",      SHUT_WR);
1✔
1892

1893
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_BROADCAST", SO_BROADCAST);
1✔
1894
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_DEBUG",     SO_DEBUG);
1✔
1895
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_DONTROUTE", SO_DONTROUTE);
1✔
1896
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_KEEPALIVE", SO_KEEPALIVE);
1✔
1897
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_LINGER",    SO_LINGER);
1✔
1898
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_OOBINLINE", SO_OOBINLINE);
1✔
1899
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_RCVBUF",    SO_RCVBUF);
1✔
1900
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_RCVTIMEO",  SO_RCVTIMEO);
1✔
1901
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_REUSEADDR", SO_REUSEADDR);
1✔
1902
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1903
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_REUSEPORT", SO_REUSEPORT);
1904
#endif
1905
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_SNDBUF",    SO_SNDBUF);
1✔
1906
    TRY_ADD_INTEGER_CONSTANT(ctx, module.o, "SO_SNDTIMEO",  SO_SNDTIMEO);
1✔
1907

1908
cleanup:
1✔
1909
    KOS_destroy_top_locals(ctx, &socket_proto, &module);
1✔
1910

1911
    return error;
1✔
1912
}
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

© 2026 Coveralls, Inc