Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/wapython
Path: blob/main/core/posix-node/src/socket.zig
1067 views
1
const c = @import("c.zig");
2
const node = @import("node.zig");
3
const std = @import("std");
4
const clib = @cImport({
5
@cInclude("sys/socket.h");
6
@cInclude("errno.h");
7
@cInclude("netdb.h");
8
@cInclude("poll.h");
9
});
10
const builtin = @import("builtin");
11
12
pub const constants = .{
13
.c_import = clib,
14
.names = [_][:0]const u8{ "EADDRINUSE", "EADDRNOTAVAIL", "EAFNOSUPPORT", "EAGAIN", "EALREADY", "ECONNREFUSED", "EFAULT", "EHOSTUNREACH", "EINPROGRESS", "EISCONN", "ENETDOWN", "ENETUNREACH", "ENOBUFS", "ENOTSOCK", "ENOPROTOOPT", "EOPNOTSUPP", "EPROTOTYPE", "ETIMEDOUT", "ECONNRESET", "ELOOP", "ENAMETOOLONG", "SHUT_RD", "SHUT_WR", "SHUT_RDWR", "MSG_OOB", "MSG_PEEK", "MSG_WAITALL", "MSG_DONTROUTE", "SO_ACCEPTCONN", "SO_BROADCAST", "SO_DEBUG", "SO_DONTROUTE", "SO_ERROR", "SO_KEEPALIVE", "SO_LINGER", "SO_OOBINLINE", "SO_RCVBUF", "SO_RCVLOWAT", "SO_RCVTIMEO", "SO_REUSEADDR", "SO_REUSEPORT", "SO_SNDBUF", "SO_SNDLOWAT", "SO_SNDTIMEO", "SO_TIMESTAMP", "SO_TYPE", "SOL_SOCKET", "ENOTCONN" },
15
};
16
17
pub fn register(env: c.napi_env, exports: c.napi_value) !void {
18
try node.registerFunction(env, exports, "accept", accept);
19
try node.registerFunction(env, exports, "_bind", bind);
20
try node.registerFunction(env, exports, "_connect", connect);
21
try node.registerFunction(env, exports, "getsockname", getsockname);
22
try node.registerFunction(env, exports, "getpeername", getpeername);
23
try node.registerFunction(env, exports, "listen", listen);
24
try node.registerFunction(env, exports, "recv", recv);
25
try node.registerFunction(env, exports, "send", send);
26
try node.registerFunction(env, exports, "shutdown", shutdown);
27
try node.registerFunction(env, exports, "socket", socket);
28
try node.registerFunction(env, exports, "getsockopt", getsockopt);
29
try node.registerFunction(env, exports, "setsockopt", setsockopt);
30
try node.registerFunction(env, exports, "pollSocket", pollSocket);
31
}
32
33
fn socket(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
34
const argv = node.getArgv(env, info, 3) catch return null;
35
const family = node.i32FromValue(env, argv[0], "family") catch return null;
36
const socktype = node.i32FromValue(env, argv[1], "socktype") catch return null;
37
const protocol = node.i32FromValue(env, argv[2], "protocol") catch return null;
38
39
const fd = clib.socket(family, socktype, protocol);
40
if (fd == -1) {
41
node.throwErrno(env, "error creating socket");
42
return null;
43
}
44
return node.create_i32(env, fd, "fd") catch return null;
45
}
46
47
// int bind(int socket, const struct sockaddr *address, socklen_t address_len);
48
// bind: (socket: number, sa_len:number, sa_family:number, sa_data:Buffer) => void;
49
50
fn bind(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
51
const argv = node.getArgv(env, info, 4) catch return null;
52
const socket_fd = node.i32FromValue(env, argv[0], "socket") catch return null;
53
const sa_len = node.i32FromValue(env, argv[1], "sa_len") catch return null;
54
const sa_family = node.i32FromValue(env, argv[2], "sa_family") catch return null;
55
56
var sa_data: [*]u8 = undefined;
57
var dummy: usize = undefined;
58
if (c.napi_get_buffer_info(env, argv[3], @ptrCast([*c]?*anyopaque, &sa_data), &dummy) != c.napi_ok) {
59
node.throwErrno(env, "error reading sa_data");
60
return null;
61
}
62
63
var sockaddr: clib.sockaddr = undefined;
64
sockaddr.sa_data = sa_data[0..14].*; // TODO: I'm dubious! Maybe just for ipv4?
65
if (builtin.target.os.tag != .linux) {
66
sockaddr.sa_len = @intCast(u8, sa_len);
67
}
68
sockaddr.sa_family = @intCast(u8, sa_family);
69
70
// std.debug.print("calling bind(socket_fd={}, sockaddr={}, sa_length={})\n", .{ socket_fd, sockaddr, @sizeOf(clib.sockaddr) });
71
const fd = clib.bind(socket_fd, &sockaddr, @sizeOf(clib.sockaddr));
72
if (fd == -1) {
73
node.throwErrno(env, "error calling bind");
74
return null;
75
}
76
return null;
77
}
78
79
fn connect(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
80
const argv = node.getArgv(env, info, 4) catch return null;
81
const socket_fd = node.i32FromValue(env, argv[0], "socket") catch return null;
82
const sa_len = node.i32FromValue(env, argv[1], "sa_len") catch return null;
83
const sa_family = node.i32FromValue(env, argv[2], "sa_family") catch return null;
84
85
var sa_data: [*]u8 = undefined;
86
var dummy: usize = undefined;
87
if (c.napi_get_buffer_info(env, argv[3], @ptrCast([*c]?*anyopaque, &sa_data), &dummy) != c.napi_ok) {
88
node.throwErrno(env, "error reading sa_data");
89
return null;
90
}
91
92
// std.debug.print("dummy = {}\n", .{dummy});
93
var sockaddr: clib.sockaddr = undefined;
94
// TODO: This is only for ipv4. I haven't been able to do this properly for ipv6, where
95
// dummy is 30 instead, because it violates what the C header says, and zig really doesn't
96
// like this. ipv6 painfully abuses C in a way that zig doesn't agree with. I may
97
// need some separate C code for this or maybe a #define in cInclude?
98
sockaddr.sa_data = sa_data[0..14].*;
99
if (builtin.target.os.tag != .linux) {
100
sockaddr.sa_len = @intCast(u8, sa_len);
101
}
102
sockaddr.sa_family = @intCast(u8, sa_family);
103
104
// std.debug.print("calling connect(socket_fd={}, sockaddr={}, sa_length={})\n", .{ socket_fd, sockaddr, @sizeOf(clib.sockaddr) });
105
const fd = clib.connect(socket_fd, &sockaddr, @sizeOf(clib.sockaddr));
106
if (fd == -1) {
107
node.throwErrno(env, "error calling connect");
108
return null;
109
}
110
return null;
111
}
112
113
// I found this useful during debugging and development, just to see what's in these structs on a native system:
114
115
// fn testit() void {
116
// var hints: clib.addrinfo = clib.addrinfo{ .ai_flags = 0, .ai_family = clib.AF_UNSPEC, .ai_socktype = clib.SOCK_STREAM, .ai_protocol = 0, .ai_addrlen = 0, .ai_addr = 0, .ai_canonname = 0, .ai_next = 0 };
117
// var res: ?*clib.addrinfo = null;
118
// //clib.memset(&hints, 0, @sizeOf(clib.addrinfo));
119
120
// if (clib.getaddrinfo("127.0.0.1", "2000", &hints, &res) != 0) {
121
// std.debug.print("Error getting addr info\n", .{});
122
// } else {
123
// if (res) |res2| {
124
// std.debug.print("Got res = {}\n", .{res2.ai_addr.*});
125
// }
126
// }
127
// }
128
129
// int getsockname(int sockfd, void* addr, socklen_t* addrlen);
130
131
fn getsockname(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
132
const argv = node.getArgv(env, info, 1) catch return null;
133
const socket_fd = node.i32FromValue(env, argv[0], "socket") catch return null;
134
135
var sockaddr: clib.sockaddr = undefined;
136
var addrlen: clib.socklen_t = @sizeOf(clib.sockaddr);
137
const r = clib.getsockname(socket_fd, &sockaddr, &addrlen);
138
if (r != 0) {
139
node.throwErrno(env, "error calling getsockname");
140
return null;
141
}
142
// std.debug.print("getsockname: sockaddr = {}, addrlen = {}\n", .{ sockaddr, addrlen });
143
return createSockaddr(env, &sockaddr, addrlen);
144
}
145
146
// Extract everything out of sockaddr and addrlen to make a Sockaddr Javascript object
147
fn createSockaddr(env: c.napi_env, sockaddr: *clib.sockaddr, addrlen: clib.socklen_t) c.napi_value {
148
var object = node.createObject(env, "") catch return null;
149
// sa_len
150
const sa_len = node.create_u32(env, addrlen, "sa_len") catch return null;
151
node.setNamedProperty(env, object, "sa_len", sa_len, "setting sa_len") catch return null;
152
153
const sa_family = node.create_i32(env, sockaddr.sa_family, "sa_family") catch return null;
154
node.setNamedProperty(env, object, "sa_family", sa_family, "setting sa_family") catch return null;
155
156
const sa_data = node.createBufferCopy(env, &sockaddr.sa_data, addrlen - 2, "sa_data") catch return null;
157
node.setNamedProperty(env, object, "sa_data", sa_data, "setting sa_data") catch return null;
158
return object;
159
}
160
161
// int getpeername(int sockfd, void* addr, socklen_t* addrlen);
162
163
fn getpeername(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
164
const argv = node.getArgv(env, info, 1) catch return null;
165
const socket_fd = node.i32FromValue(env, argv[0], "socket") catch return null;
166
167
var sockaddr: clib.sockaddr = undefined;
168
var addrlen: clib.socklen_t = @sizeOf(clib.sockaddr);
169
const r = clib.getpeername(socket_fd, &sockaddr, &addrlen);
170
if (r != 0) {
171
node.throwErrno(env, "error calling getpeername");
172
return null;
173
}
174
// std.debug.print("getpeername: sockaddr = {}, addrlen = {}\n", .{ sockaddr, addrlen });
175
return createSockaddr(env, &sockaddr, addrlen);
176
}
177
178
// NOTE: for recv, the buffer get writtens to directly, so the client can
179
// make things efficient (or not) however they want, and we don't malloc or free.
180
181
// ssize_t recv(int socket, void *buffer, size_t length, int flags);
182
// recv: (socket: number, buffer:Buffer, flags: number) => void;
183
fn recv(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
184
const argv = node.getArgv(env, info, 3) catch return null;
185
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
186
const flags = node.i32FromValue(env, argv[2], "flags") catch return null;
187
188
var buffer: [*]u8 = undefined;
189
var length: usize = undefined;
190
if (c.napi_get_buffer_info(env, argv[1], @ptrCast([*c]?*anyopaque, &buffer), &length) != c.napi_ok) {
191
node.throwErrno(env, "error reading buffer");
192
return null;
193
}
194
// Receive the data.
195
// std.debug.print("socket_fd={}, buffer={*}, length={}, flags={}\n", .{ socket_fd, buffer, length, flags });
196
const bytes_received = clib.recv(socket_fd, buffer, length, flags);
197
if (bytes_received < 0) {
198
node.throwErrno(env, "error receiving data from socket");
199
return null;
200
}
201
return node.create_i32(env, @intCast(i32, bytes_received), "bytes received") catch return null;
202
}
203
204
// ssize_t send(int socket, void *buffer, size_t length, int flags);
205
// send: (socket: number, buffer: Buffer, flags: number) => number;
206
fn send(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
207
const argv = node.getArgv(env, info, 3) catch return null;
208
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
209
const flags = node.i32FromValue(env, argv[2], "flags") catch return null;
210
211
var buffer: [*]u8 = undefined;
212
var length: usize = undefined;
213
if (c.napi_get_buffer_info(env, argv[1], @ptrCast([*c]?*anyopaque, &buffer), &length) != c.napi_ok) {
214
node.throwErrno(env, "error reading buffer");
215
return null;
216
}
217
// Send the data.
218
const bytes_sent = clib.send(socket_fd, buffer, length, flags);
219
if (bytes_sent < 0) {
220
node.throwErrno(env, "error sending data to socket");
221
return null;
222
}
223
224
return node.create_i32(env, @intCast(i32, bytes_sent), "bytes sent") catch return null;
225
}
226
227
// int shutdown(int socket, int how);
228
fn shutdown(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
229
const argv = node.getArgv(env, info, 2) catch return null;
230
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
231
const how = node.i32FromValue(env, argv[1], "how") catch return null;
232
const r = clib.shutdown(socket_fd, how);
233
if (r != 0) {
234
node.throwErrno(env, "error calling shutdown on network socket");
235
}
236
return null;
237
}
238
239
// int listen(int socket, int backlog);
240
fn listen(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
241
const argv = node.getArgv(env, info, 2) catch return null;
242
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
243
const backlog = node.i32FromValue(env, argv[1], "backlog") catch return null;
244
const r = clib.listen(socket_fd, backlog);
245
if (r != 0) {
246
node.throwErrno(env, "error calling listen on network socket");
247
}
248
return null;
249
}
250
251
// int accept(int socket, struct sockaddr *address, socklen_t *address_len);
252
// accept: (socket: number) => { fd: number; sockaddr: Sockaddr };
253
fn accept(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
254
const argv = node.getArgv(env, info, 1) catch return null;
255
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
256
257
var sockaddr: clib.sockaddr = undefined;
258
// NOTE: On linux it's required that addrlen be initialized with
259
// the size of sockaddr, but on macOS it isn't.
260
var addrlen: clib.socklen_t = @sizeOf(clib.sockaddr);
261
// std.debug.print("accept(socket_fd={}, sockaddr={*}, addrlen={*})\n", .{ socket_fd, &sockaddr, &addrlen });
262
const fd = clib.accept(socket_fd, &sockaddr, &addrlen);
263
if (fd == -1) {
264
// std.debug.print("calling accept on socket_fd failed -- errno={}\n", .{@import("util.zig").getErrno()});
265
node.throwErrno(env, "error calling accept on network socket");
266
return null;
267
}
268
269
var object = node.createObject(env, "") catch return null;
270
271
const sockaddrObj = createSockaddr(env, &sockaddr, addrlen);
272
if (sockaddrObj == null) return null;
273
node.setNamedProperty(env, object, "sockaddr", sockaddrObj, "setting sockaddr") catch return null;
274
const new_fd = node.create_i32(env, fd, "fd") catch return null;
275
node.setNamedProperty(env, object, "fd", new_fd, "setting fd") catch return null;
276
return object;
277
}
278
279
// getsockopt: (
280
// socket: number,
281
// level: number,
282
// option_name: number,
283
// max_len: number
284
// ) => Buffer;
285
//
286
// int getsockopt(int socket, int level, int option_name, void *option_value,
287
// socklen_t *option_len);
288
289
fn getsockopt(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
290
const argv = node.getArgv(env, info, 4) catch return null;
291
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
292
const level = node.i32FromValue(env, argv[1], "option_name") catch return null;
293
const option_name = node.i32FromValue(env, argv[2], "option_name") catch return null;
294
var option_len = @intCast(c_uint, node.u32FromValue(env, argv[3], "option_len") catch return null);
295
296
const option_value = std.c.malloc(option_len) orelse {
297
node.throwError(env, "error allocating memory");
298
return null;
299
};
300
defer std.c.free(option_value);
301
302
const r = clib.getsockopt(socket_fd, level, option_name, option_value, &option_len);
303
if (r == -1) {
304
node.throwErrno(env, "error calling getsockopt on network socket");
305
return null;
306
}
307
const buf = node.createBufferCopy(env, option_value, option_len, "return option Buffer") catch return null;
308
return buf;
309
}
310
311
// setsockopt: (
312
// socket: number,
313
// level: number,
314
// option_name: number,
315
// option_value: Buffer
316
// ) => void;
317
//
318
// int setsockopt(int socket, int level, int option_name, const void *option_value,
319
// socklen_t option_len);
320
fn setsockopt(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
321
const argv = node.getArgv(env, info, 4) catch return null;
322
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
323
const level = node.i32FromValue(env, argv[1], "option_name") catch return null;
324
const option_name = node.i32FromValue(env, argv[2], "option_name") catch return null;
325
326
var option_value: [*]u8 = undefined;
327
var option_len: usize = undefined;
328
if (c.napi_get_buffer_info(env, argv[3], @ptrCast([*c]?*anyopaque, &option_value), &option_len) != c.napi_ok) {
329
node.throwErrno(env, "error reading buffer");
330
return null;
331
}
332
const r = clib.setsockopt(socket_fd, level, option_name, option_value, @intCast(c_uint, option_len));
333
if (r == -1) {
334
node.throwErrno(env, "error calling setsockopt on network socket");
335
return null;
336
}
337
return null;
338
}
339
340
// pollSocket: (fd: number, events: number, timeout_ms: number) => void;
341
fn pollSocket(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_value {
342
const argv = node.getArgv(env, info, 3) catch return null;
343
const socket_fd = node.i32FromValue(env, argv[0], "socket_fd") catch return null;
344
const events = node.i32FromValue(env, argv[1], "events") catch return null;
345
const timeout_ms = node.i32FromValue(env, argv[2], "timeout_ms") catch return null;
346
347
var fds: [1]clib.pollfd = undefined;
348
fds[0].fd = socket_fd;
349
fds[0].events = @intCast(c_short, events);
350
fds[0].revents = 0;
351
// std.debug.print("waiting {}ms for fd={}\n", .{ timeout_ms, fds[0].fd });
352
const r = clib.poll(&fds, 1, timeout_ms);
353
// std.debug.print("done waiting; r={}\n", .{r});
354
if (r == -1) {
355
node.throwErrno(env, "error polling for a socket");
356
return null;
357
}
358
return null;
359
}
360
361