BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file socket.c |
||
3 | * @author Ambroz Bizjak <ambrop7@gmail.com> |
||
4 | * |
||
5 | * @section LICENSE |
||
6 | * |
||
7 | * Redistribution and use in source and binary forms, with or without |
||
8 | * modification, are permitted provided that the following conditions are met: |
||
9 | * 1. Redistributions of source code must retain the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer. |
||
11 | * 2. Redistributions in binary form must reproduce the above copyright |
||
12 | * notice, this list of conditions and the following disclaimer in the |
||
13 | * documentation and/or other materials provided with the distribution. |
||
14 | * 3. Neither the name of the author nor the |
||
15 | * names of its contributors may be used to endorse or promote products |
||
16 | * derived from this software without specific prior written permission. |
||
17 | * |
||
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
||
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
21 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||
22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
28 | * |
||
29 | * @section DESCRIPTION |
||
30 | * |
||
31 | * Synopsis: |
||
32 | * sys.socket sys.connect(string addr [, map options]) |
||
33 | * |
||
34 | * Options: |
||
35 | * "read_size" - the maximum number of bytes that can be read by a single |
||
36 | * read() call. Must be greater than zero. Greater values may improve |
||
37 | * performance, but will increase memory usage. Default: 8192. |
||
38 | * |
||
39 | * Variables: |
||
40 | * string is_error - "true" if there was an error with the connection, |
||
41 | * "false" if not |
||
42 | * |
||
43 | * Description: |
||
44 | * Attempts to establish a connection to a server. The address should be |
||
45 | * in one of the following forms: |
||
46 | * - {"tcp", {"ipv4", ipv4_address, port_number}}, |
||
47 | * - {"tcp", {"ipv6", ipv6_address, port_number}}, |
||
48 | * - {"unix", socket_path}, |
||
49 | * - {"device", device_path}. |
||
50 | * When the connection attempt is finished, the sys.connect() statement goes |
||
51 | * up, and the 'is_error' variable should be used to check for connection |
||
52 | * failure. If there was no error, the read(), write() and close() methods |
||
53 | * can be used to work with the connection. |
||
54 | * If an error occurs after the connection has been established, the |
||
55 | * sys.connect() statement will automatically trigger backtracking, and the |
||
56 | * 'is_error' variable will be changed to "true". This means that all |
||
57 | * errors with the connection can be handled at the place of sys.connect(), |
||
58 | * and no special care is normally needed to handle error in read() and |
||
59 | * write(). |
||
60 | * The special "device" address type may be used to connect to a serial |
||
61 | * port. But you need to configure the port yourself first (stty). |
||
62 | * WARNING: when you're not trying to either send or receive data, the |
||
63 | * connection may be unable to detect any events with the connection. |
||
64 | * You should never be neither sending nor receiving for an indefinite time. |
||
65 | * |
||
66 | * Synopsis: |
||
67 | * sys.socket::read() |
||
68 | * |
||
69 | * Variables: |
||
70 | * string (empty) - some data received from the socket, or empty on EOF |
||
71 | * string eof - "true" if EOF was encountered, "false" if not |
||
72 | * string not_eof - (deprecated) "true" if EOF was not encountered, |
||
73 | * "false" if it was |
||
74 | * |
||
75 | * Description: |
||
76 | * Receives data from the connection. If EOF was encountered (remote host |
||
77 | * has closed the connection), this returns no data. Otherwise it returns |
||
78 | * at least one byte. |
||
79 | * WARNING: after you receive EOF from a sys.listen() type socket, is is |
||
80 | * your responsibility to call close() eventually, else the cline process |
||
81 | * may remain alive indefinitely. |
||
82 | * WARNING: this may return an arbitrarily small chunk of data. There is |
||
83 | * no significance to the size of the chunks. Correct code will behave |
||
84 | * the same no matter how the incoming data stream is split up. |
||
85 | * WARNING: if a read() is terminated while it is still in progress, i.e. |
||
86 | * has not gone up yet, then the connection is automatically closed, as |
||
87 | * if close() was called. |
||
88 | * |
||
89 | * Synopsis: |
||
90 | * sys.socket::write(string data) |
||
91 | * |
||
92 | * Description: |
||
93 | * Sends data to the connection. |
||
94 | * WARNING: this may block if the operating system's internal send buffer |
||
95 | * is full. Be careful not to enter a deadlock where both ends of the |
||
96 | * connection are trying to send data to the other, but neither is trying |
||
97 | * to receive any data. |
||
98 | * WARNING: if a write() is terminated while it is still in progress, i.e. |
||
99 | * has not gone up yet, then the connection is automatically closed, as |
||
100 | * if close() was called. |
||
101 | * |
||
102 | * Synopsis: |
||
103 | * sys.socket::close() |
||
104 | * |
||
105 | * Description: |
||
106 | * Closes the connection. After this, any further read(), write() or close() |
||
107 | * will trigger an error with the interpreter. For client sockets created |
||
108 | * via sys.listen(), this will immediately trigger termination of the client |
||
109 | * process. |
||
110 | * |
||
111 | * Synopsis: |
||
112 | * sys.listen(string address, string client_template, list args [, map options]) |
||
113 | * |
||
114 | * Options: |
||
115 | * "read_size" - the maximum number of bytes that can be read by a single |
||
116 | * read() call. Must be greater than zero. Greater values may improve |
||
117 | * performance, but will increase memory usage. Default: 8192. |
||
118 | * |
||
119 | * Variables: |
||
120 | * string is_error - "true" if listening failed to inittialize, "false" if |
||
121 | * not |
||
122 | * |
||
123 | * Special objects and variables in client_template: |
||
124 | * sys.socket _socket - the socket object for the client |
||
125 | * string _socket.client_addr - the address of the client. The form is |
||
126 | * like the second part of the sys.connect() address format, e.g. |
||
127 | * {"ipv4", "1.2.3.4", "4000"}. |
||
128 | * |
||
129 | * Description: |
||
130 | * Starts listening on the specified address. The format of the device is |
||
131 | * as described for sys.connect, but without the "device" address type. |
||
132 | * The 'is_error' variable |
||
133 | * reflects the success of listening initiation. If listening succeeds, |
||
134 | * then for every client that connects, a process is automatically created |
||
135 | * from the template specified by 'client_template', and the 'args' list |
||
136 | * is used as template arguments. Inside such processes, a special object |
||
137 | * '_socket' is available, which represents the connection, and supports |
||
138 | * the same methods as sys.connect(), i.e. read(), write() and close(). |
||
139 | * When an error occurs with the connection, the socket is automatically |
||
140 | * closed, triggering process termination. |
||
141 | */ |
||
142 | |||
143 | #include <stdlib.h> |
||
144 | #include <limits.h> |
||
145 | #include <stdarg.h> |
||
146 | #include <sys/types.h> |
||
147 | #include <sys/stat.h> |
||
148 | #include <fcntl.h> |
||
149 | |||
150 | #include <misc/offset.h> |
||
151 | #include <misc/debug.h> |
||
152 | #include <structure/LinkedList0.h> |
||
153 | #include <system/BConnection.h> |
||
154 | #include <system/BConnectionGeneric.h> |
||
155 | #include <ncd/extra/address_utils.h> |
||
156 | #include <ncd/extra/NCDBuf.h> |
||
157 | |||
158 | #include <ncd/module_common.h> |
||
159 | |||
160 | #include <generated/blog_channel_ncd_socket.h> |
||
161 | |||
162 | #define CONNECTION_TYPE_CONNECT 1 |
||
163 | #define CONNECTION_TYPE_LISTEN 2 |
||
164 | |||
165 | #define CONNECTION_STATE_CONNECTING 1 |
||
166 | #define CONNECTION_STATE_ESTABLISHED 2 |
||
167 | #define CONNECTION_STATE_ERROR 3 |
||
168 | #define CONNECTION_STATE_ABORTED 4 |
||
169 | |||
170 | #define DEFAULT_READ_BUF_SIZE 8192 |
||
171 | |||
172 | struct connection { |
||
173 | union { |
||
174 | struct { |
||
175 | NCDModuleInst *i; |
||
176 | BConnector connector; |
||
177 | size_t read_buf_size; |
||
178 | } connect; |
||
179 | struct { |
||
180 | struct listen_instance *listen_inst; |
||
181 | LinkedList0Node clients_list_node; |
||
182 | BAddr addr; |
||
183 | NCDModuleProcess process; |
||
184 | } listen; |
||
185 | }; |
||
186 | |||
187 | unsigned int type:2; |
||
188 | unsigned int state:3; |
||
189 | unsigned int recv_closed:1; |
||
190 | BConnection connection; |
||
191 | NCDBufStore store; |
||
192 | struct read_instance *read_inst; |
||
193 | struct write_instance *write_inst; |
||
194 | }; |
||
195 | |||
196 | struct read_instance { |
||
197 | NCDModuleInst *i; |
||
198 | struct connection *con_inst; |
||
199 | NCDBuf *buf; |
||
200 | size_t read_size; |
||
201 | }; |
||
202 | |||
203 | struct write_instance { |
||
204 | NCDModuleInst *i; |
||
205 | struct connection *con_inst; |
||
206 | MemRef data; |
||
207 | }; |
||
208 | |||
209 | struct listen_instance { |
||
210 | NCDModuleInst *i; |
||
211 | unsigned int have_error:1; |
||
212 | unsigned int dying:1; |
||
213 | size_t read_buf_size; |
||
214 | NCDValRef client_template; |
||
215 | NCDValRef client_template_args; |
||
216 | BListener listener; |
||
217 | LinkedList0 clients_list; |
||
218 | }; |
||
219 | |||
220 | enum {STRING_SOCKET, STRING_SYS_SOCKET, STRING_CLIENT_ADDR}; |
||
221 | |||
222 | static const char *strings[] = { |
||
223 | "_socket", "sys.socket", "client_addr", NULL |
||
224 | }; |
||
225 | |||
226 | static int parse_options (NCDModuleInst *i, NCDValRef options, size_t *out_read_size); |
||
227 | static void connection_log (struct connection *o, int level, const char *fmt, ...); |
||
228 | static void connection_free_connection (struct connection *o); |
||
229 | static void connection_error (struct connection *o); |
||
230 | static void connection_abort (struct connection *o); |
||
231 | static void connection_connector_handler (void *user, int is_error); |
||
232 | static void connection_complete_establish (struct connection *o); |
||
233 | static void connection_connection_handler (void *user, int event); |
||
234 | static void connection_send_handler_done (void *user, int data_len); |
||
235 | static void connection_recv_handler_done (void *user, int data_len); |
||
236 | static void connection_process_handler (struct NCDModuleProcess_s *process, int event); |
||
237 | static int connection_process_func_getspecialobj (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object); |
||
238 | static int connection_process_socket_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); |
||
239 | static int connection_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); |
||
240 | static void listen_listener_handler (void *user); |
||
241 | |||
242 | static int parse_options (NCDModuleInst *i, NCDValRef options, size_t *out_read_size) |
||
243 | { |
||
244 | ASSERT(out_read_size) |
||
245 | |||
246 | *out_read_size = DEFAULT_READ_BUF_SIZE; |
||
247 | |||
248 | if (!NCDVal_IsInvalid(options)) { |
||
249 | if (!NCDVal_IsMap(options)) { |
||
250 | ModuleLog(i, BLOG_ERROR, "options argument is not a map"); |
||
251 | return 0; |
||
252 | } |
||
253 | |||
254 | int num_recognized = 0; |
||
255 | NCDValRef value; |
||
256 | |||
257 | if (!NCDVal_IsInvalid(value = NCDVal_MapGetValue(options, "read_size"))) { |
||
258 | uintmax_t read_size; |
||
259 | if (!ncd_read_uintmax(value, &read_size) || read_size > SIZE_MAX || read_size == 0) { |
||
260 | ModuleLog(i, BLOG_ERROR, "wrong read_size"); |
||
261 | return 0; |
||
262 | } |
||
263 | num_recognized++; |
||
264 | *out_read_size = read_size; |
||
265 | } |
||
266 | |||
267 | if (NCDVal_MapCount(options) > num_recognized) { |
||
268 | ModuleLog(i, BLOG_ERROR, "unrecognized options present"); |
||
269 | return 0; |
||
270 | } |
||
271 | } |
||
272 | |||
273 | return 1; |
||
274 | } |
||
275 | |||
276 | static void connection_log (struct connection *o, int level, const char *fmt, ...) |
||
277 | { |
||
278 | va_list vl; |
||
279 | va_start(vl, fmt); |
||
280 | |||
281 | switch (o->type) { |
||
282 | case CONNECTION_TYPE_CONNECT: { |
||
283 | NCDModuleInst_Backend_LogVarArg(o->connect.i, BLOG_CURRENT_CHANNEL, level, fmt, vl); |
||
284 | } break; |
||
285 | |||
286 | case CONNECTION_TYPE_LISTEN: { |
||
287 | if (BLog_WouldLog(BLOG_CURRENT_CHANNEL, level)) { |
||
288 | BLog_Begin(); |
||
289 | o->listen.listen_inst->i->params->logfunc(o->listen.listen_inst->i); |
||
290 | char addr_str[BADDR_MAX_PRINT_LEN]; |
||
291 | BAddr_Print(&o->listen.addr, addr_str); |
||
292 | BLog_Append("client %s: ", addr_str); |
||
293 | BLog_AppendVarArg(fmt, vl); |
||
294 | BLog_Finish(BLOG_CURRENT_CHANNEL, level); |
||
295 | } |
||
296 | } break; |
||
297 | |||
298 | default: ASSERT(0); |
||
299 | } |
||
300 | |||
301 | va_end(vl); |
||
302 | } |
||
303 | |||
304 | static void connection_free_connection (struct connection *o) |
||
305 | { |
||
306 | // disconnect read instance |
||
307 | if (o->read_inst) { |
||
308 | ASSERT(o->read_inst->con_inst == o) |
||
309 | o->read_inst->con_inst = NULL; |
||
310 | } |
||
311 | |||
312 | // disconnect write instance |
||
313 | if (o->write_inst) { |
||
314 | ASSERT(o->write_inst->con_inst == o) |
||
315 | o->write_inst->con_inst = NULL; |
||
316 | } |
||
317 | |||
318 | // free connection interfaces |
||
319 | BConnection_RecvAsync_Free(&o->connection); |
||
320 | BConnection_SendAsync_Free(&o->connection); |
||
321 | |||
322 | // free connection |
||
323 | BConnection_Free(&o->connection); |
||
324 | |||
325 | // free store |
||
326 | NCDBufStore_Free(&o->store); |
||
327 | } |
||
328 | |||
329 | static void connection_error (struct connection *o) |
||
330 | { |
||
331 | ASSERT(o->state == CONNECTION_STATE_CONNECTING || |
||
332 | o->state == CONNECTION_STATE_ESTABLISHED) |
||
333 | |||
334 | // for listen clients, we don't report errors directly, |
||
335 | // we just terminate the client process |
||
336 | if (o->type == CONNECTION_TYPE_LISTEN) { |
||
337 | ASSERT(o->state != CONNECTION_STATE_CONNECTING) |
||
338 | connection_abort(o); |
||
339 | return; |
||
340 | } |
||
341 | |||
342 | // free connector |
||
343 | if (o->state == CONNECTION_STATE_CONNECTING) { |
||
344 | BConnector_Free(&o->connect.connector); |
||
345 | } |
||
346 | |||
347 | // free connection resources |
||
348 | if (o->state == CONNECTION_STATE_ESTABLISHED) { |
||
349 | connection_free_connection(o); |
||
350 | } |
||
351 | |||
352 | // trigger reporting of failure |
||
353 | if (o->state == CONNECTION_STATE_ESTABLISHED) { |
||
354 | NCDModuleInst_Backend_Down(o->connect.i); |
||
355 | } |
||
356 | NCDModuleInst_Backend_Up(o->connect.i); |
||
357 | |||
358 | // set state |
||
359 | o->state = CONNECTION_STATE_ERROR; |
||
360 | } |
||
361 | |||
362 | static void connection_abort (struct connection *o) |
||
363 | { |
||
364 | ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) |
||
365 | |||
366 | // free connection resources |
||
367 | connection_free_connection(o); |
||
368 | |||
369 | // if this is a listen connection, terminate client process |
||
370 | if (o->type == CONNECTION_TYPE_LISTEN) { |
||
371 | NCDModuleProcess_Terminate(&o->listen.process); |
||
372 | } |
||
373 | |||
374 | // set state |
||
375 | o->state = CONNECTION_STATE_ABORTED; |
||
376 | } |
||
377 | |||
378 | static void connection_connector_handler (void *user, int is_error) |
||
379 | { |
||
380 | struct connection *o = user; |
||
381 | ASSERT(o->type == CONNECTION_TYPE_CONNECT) |
||
382 | ASSERT(o->state == CONNECTION_STATE_CONNECTING) |
||
383 | |||
384 | // check error |
||
385 | if (is_error) { |
||
386 | connection_log(o, BLOG_ERROR, "connection failed"); |
||
387 | goto fail; |
||
388 | } |
||
389 | |||
390 | // init connection |
||
391 | if (!BConnection_Init(&o->connection, BConnection_source_connector(&o->connect.connector), o->connect.i->params->iparams->reactor, o, connection_connection_handler)) { |
||
392 | connection_log(o, BLOG_ERROR, "BConnection_Init failed"); |
||
393 | goto fail; |
||
394 | } |
||
395 | |||
396 | // free connector |
||
397 | BConnector_Free(&o->connect.connector); |
||
398 | |||
399 | return connection_complete_establish(o); |
||
400 | |||
401 | fail: |
||
402 | connection_error(o); |
||
403 | } |
||
404 | |||
405 | static void connection_complete_establish (struct connection *o) |
||
406 | { |
||
407 | // init connection interfaces |
||
408 | BConnection_SendAsync_Init(&o->connection); |
||
409 | BConnection_RecvAsync_Init(&o->connection); |
||
410 | |||
411 | // setup send/recv done callbacks |
||
412 | StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&o->connection), connection_send_handler_done, o); |
||
413 | StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&o->connection), connection_recv_handler_done, o); |
||
414 | |||
415 | // init store |
||
416 | NCDBufStore_Init(&o->store, o->connect.read_buf_size); |
||
417 | |||
418 | // set not reading, not writing, recv not closed |
||
419 | o->read_inst = NULL; |
||
420 | o->write_inst = NULL; |
||
421 | o->recv_closed = 0; |
||
422 | |||
423 | // set state |
||
424 | o->state = CONNECTION_STATE_ESTABLISHED; |
||
425 | |||
426 | // go up |
||
427 | NCDModuleInst_Backend_Up(o->connect.i); |
||
428 | } |
||
429 | |||
430 | static void connection_connection_handler (void *user, int event) |
||
431 | { |
||
432 | struct connection *o = user; |
||
433 | ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) |
||
434 | ASSERT(event == BCONNECTION_EVENT_RECVCLOSED || event == BCONNECTION_EVENT_ERROR) |
||
435 | ASSERT(event != BCONNECTION_EVENT_RECVCLOSED || !o->recv_closed) |
||
436 | |||
437 | if (event == BCONNECTION_EVENT_RECVCLOSED) { |
||
438 | // if we have read operation, make it finish with eof |
||
439 | if (o->read_inst) { |
||
440 | ASSERT(o->read_inst->con_inst == o) |
||
441 | o->read_inst->con_inst = NULL; |
||
442 | o->read_inst->read_size = 0; |
||
443 | NCDModuleInst_Backend_Up(o->read_inst->i); |
||
444 | o->read_inst = NULL; |
||
445 | } |
||
446 | |||
447 | // set recv closed |
||
448 | o->recv_closed = 1; |
||
449 | return; |
||
450 | } |
||
451 | |||
452 | connection_log(o, BLOG_ERROR, "connection error"); |
||
453 | |||
454 | // handle error |
||
455 | connection_error(o); |
||
456 | } |
||
457 | |||
458 | static void connection_send_handler_done (void *user, int data_len) |
||
459 | { |
||
460 | struct connection *o = user; |
||
461 | ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) |
||
462 | ASSERT(o->write_inst) |
||
463 | ASSERT(o->write_inst->con_inst == o) |
||
464 | ASSERT(data_len > 0) |
||
465 | ASSERT(data_len <= o->write_inst->data.len) |
||
466 | |||
467 | struct write_instance *wr = o->write_inst; |
||
468 | |||
469 | // update send state |
||
470 | wr->data = MemRef_SubFrom(wr->data, data_len); |
||
471 | |||
472 | // if there's more to send, send again |
||
473 | if (wr->data.len > 0) { |
||
474 | size_t to_send = (wr->data.len > INT_MAX) ? INT_MAX : wr->data.len; |
||
475 | StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&o->connection), (uint8_t *)wr->data.ptr, to_send); |
||
476 | return; |
||
477 | } |
||
478 | |||
479 | // finish write operation |
||
480 | wr->con_inst = NULL; |
||
481 | NCDModuleInst_Backend_Up(wr->i); |
||
482 | o->write_inst = NULL; |
||
483 | } |
||
484 | |||
485 | static void connection_recv_handler_done (void *user, int data_len) |
||
486 | { |
||
487 | struct connection *o = user; |
||
488 | ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) |
||
489 | ASSERT(o->read_inst) |
||
490 | ASSERT(o->read_inst->con_inst == o) |
||
491 | ASSERT(!o->recv_closed) |
||
492 | ASSERT(data_len > 0) |
||
493 | ASSERT(data_len <= NCDBufStore_BufSize(&o->store)) |
||
494 | |||
495 | struct read_instance *re = o->read_inst; |
||
496 | |||
497 | // finish read operation |
||
498 | re->con_inst = NULL; |
||
499 | re->read_size = data_len; |
||
500 | NCDModuleInst_Backend_Up(re->i); |
||
501 | o->read_inst = NULL; |
||
502 | } |
||
503 | |||
504 | static void connection_process_handler (struct NCDModuleProcess_s *process, int event) |
||
505 | { |
||
506 | struct connection *o = UPPER_OBJECT(process, struct connection, listen.process); |
||
507 | ASSERT(o->type == CONNECTION_TYPE_LISTEN) |
||
508 | |||
509 | switch (event) { |
||
510 | case NCDMODULEPROCESS_EVENT_UP: { |
||
511 | ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) |
||
512 | } break; |
||
513 | |||
514 | case NCDMODULEPROCESS_EVENT_DOWN: { |
||
515 | ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) |
||
516 | NCDModuleProcess_Continue(&o->listen.process); |
||
517 | } break; |
||
518 | |||
519 | case NCDMODULEPROCESS_EVENT_TERMINATED: { |
||
520 | ASSERT(o->state == CONNECTION_STATE_ABORTED) |
||
521 | |||
522 | struct listen_instance *li = o->listen.listen_inst; |
||
523 | ASSERT(!li->have_error) |
||
524 | |||
525 | // remove from clients list |
||
526 | LinkedList0_Remove(&li->clients_list, &o->listen.clients_list_node); |
||
527 | |||
528 | // free process |
||
529 | NCDModuleProcess_Free(&o->listen.process); |
||
530 | |||
531 | // free connection structure |
||
532 | free(o); |
||
533 | |||
534 | // if listener is dying and this was the last process, have it die |
||
535 | if (li->dying && LinkedList0_IsEmpty(&li->clients_list)) { |
||
536 | NCDModuleInst_Backend_Dead(li->i); |
||
537 | } |
||
538 | } break; |
||
539 | |||
540 | default: ASSERT(0); |
||
541 | } |
||
542 | } |
||
543 | |||
544 | static int connection_process_func_getspecialobj (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object) |
||
545 | { |
||
546 | struct connection *o = UPPER_OBJECT(process, struct connection, listen.process); |
||
547 | ASSERT(o->type == CONNECTION_TYPE_LISTEN) |
||
548 | |||
549 | if (name == ModuleString(o->listen.listen_inst->i, STRING_SOCKET)) { |
||
550 | *out_object = NCDObject_Build(ModuleString(o->listen.listen_inst->i, STRING_SYS_SOCKET), o, connection_process_socket_obj_func_getvar, NCDObject_no_getobj); |
||
551 | return 1; |
||
552 | } |
||
553 | |||
554 | if (name == NCD_STRING_CALLER) { |
||
555 | *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, connection_process_caller_obj_func_getobj); |
||
556 | return 1; |
||
557 | } |
||
558 | |||
559 | return 0; |
||
560 | } |
||
561 | |||
562 | static int connection_process_socket_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) |
||
563 | { |
||
564 | struct connection *o = NCDObject_DataPtr(obj); |
||
565 | ASSERT(o->type == CONNECTION_TYPE_LISTEN) |
||
566 | |||
567 | if (name == ModuleString(o->listen.listen_inst->i, STRING_CLIENT_ADDR)) { |
||
568 | *out_value = ncd_make_baddr(o->listen.addr, mem); |
||
569 | if (NCDVal_IsInvalid(*out_value)) { |
||
570 | connection_log(o, BLOG_ERROR, "ncd_make_baddr failed"); |
||
571 | } |
||
572 | return 1; |
||
573 | } |
||
574 | |||
575 | return 0; |
||
576 | } |
||
577 | |||
578 | static int connection_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) |
||
579 | { |
||
580 | struct connection *o = NCDObject_DataPtr(obj); |
||
581 | ASSERT(o->type == CONNECTION_TYPE_LISTEN) |
||
582 | |||
583 | return NCDModuleInst_Backend_GetObj(o->listen.listen_inst->i, name, out_object); |
||
584 | } |
||
585 | |||
586 | static void listen_listener_handler (void *user) |
||
587 | { |
||
588 | struct listen_instance *o = user; |
||
589 | ASSERT(!o->have_error) |
||
590 | ASSERT(!o->dying) |
||
591 | |||
592 | // allocate connection structure |
||
593 | struct connection *con = malloc(sizeof(*con)); |
||
594 | if (!con) { |
||
595 | ModuleLog(o->i, BLOG_ERROR, "malloc failed"); |
||
596 | goto fail0; |
||
597 | } |
||
598 | |||
599 | // set connection type and listen instance |
||
600 | con->type = CONNECTION_TYPE_LISTEN; |
||
601 | con->listen.listen_inst = o; |
||
602 | |||
603 | // init connection |
||
604 | if (!BConnection_Init(&con->connection, BConnection_source_listener(&o->listener, &con->listen.addr), o->i->params->iparams->reactor, con, connection_connection_handler)) { |
||
605 | ModuleLog(o->i, BLOG_ERROR, "BConnection_Init failed"); |
||
606 | goto fail1; |
||
607 | } |
||
608 | |||
609 | // init connection interfaces |
||
610 | BConnection_SendAsync_Init(&con->connection); |
||
611 | BConnection_RecvAsync_Init(&con->connection); |
||
612 | |||
613 | // setup send/recv done callbacks |
||
614 | StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&con->connection), connection_send_handler_done, con); |
||
615 | StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&con->connection), connection_recv_handler_done, con); |
||
616 | |||
617 | // init process |
||
618 | if (!NCDModuleProcess_InitValue(&con->listen.process, o->i, o->client_template, o->client_template_args, connection_process_handler)) { |
||
619 | ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_InitValue failed"); |
||
620 | goto fail2; |
||
621 | } |
||
622 | |||
623 | // set special objects callback |
||
624 | NCDModuleProcess_SetSpecialFuncs(&con->listen.process, connection_process_func_getspecialobj); |
||
625 | |||
626 | // insert to clients list |
||
627 | LinkedList0_Prepend(&o->clients_list, &con->listen.clients_list_node); |
||
628 | |||
629 | // init store |
||
630 | NCDBufStore_Init(&con->store, o->read_buf_size); |
||
631 | |||
632 | // set not reading, not writing, recv not closed |
||
633 | con->read_inst = NULL; |
||
634 | con->write_inst = NULL; |
||
635 | con->recv_closed = 0; |
||
636 | |||
637 | // set state |
||
638 | con->state = CONNECTION_STATE_ESTABLISHED; |
||
639 | return; |
||
640 | |||
641 | fail2: |
||
642 | BConnection_RecvAsync_Free(&con->connection); |
||
643 | BConnection_SendAsync_Free(&con->connection); |
||
644 | BConnection_Free(&con->connection); |
||
645 | fail1: |
||
646 | free(con); |
||
647 | fail0: |
||
648 | return; |
||
649 | } |
||
650 | |||
651 | static int connect_custom_addr_handler (void *user, NCDValRef protocol, NCDValRef data) |
||
652 | { |
||
653 | NCDValRef *device_path = user; |
||
654 | if (NCDVal_StringEquals(protocol, "device")) { |
||
655 | if (!NCDVal_IsStringNoNulls(data)) { |
||
656 | return 0; |
||
657 | } |
||
658 | *device_path = data; |
||
659 | return 1; |
||
660 | } |
||
661 | return 0; |
||
662 | } |
||
663 | |||
664 | static void connect_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) |
||
665 | { |
||
666 | struct connection *o = vo; |
||
667 | o->type = CONNECTION_TYPE_CONNECT; |
||
668 | o->connect.i = i; |
||
669 | |||
670 | // pass connection pointer to methods so the same methods can work for |
||
671 | // listen type connections |
||
672 | NCDModuleInst_Backend_PassMemToMethods(i); |
||
673 | |||
674 | // read arguments |
||
675 | NCDValRef address_arg; |
||
676 | NCDValRef options_arg = NCDVal_NewInvalid(); |
||
677 | if (!NCDVal_ListRead(params->args, 1, &address_arg) && |
||
678 | !NCDVal_ListRead(params->args, 2, &address_arg, &options_arg) |
||
679 | ) { |
||
680 | ModuleLog(i, BLOG_ERROR, "wrong arity"); |
||
681 | goto fail0; |
||
682 | } |
||
683 | |||
684 | // parse options |
||
685 | if (!parse_options(i, options_arg, &o->connect.read_buf_size)) { |
||
686 | goto fail0; |
||
687 | } |
||
688 | |||
689 | // read address |
||
690 | struct BConnection_addr address; |
||
691 | NCDValRef device_path; |
||
692 | if (!ncd_read_bconnection_addr_ext(address_arg, connect_custom_addr_handler, &device_path, &address)) { |
||
693 | ModuleLog(i, BLOG_ERROR, "wrong address"); |
||
694 | goto error; |
||
695 | } |
||
696 | |||
697 | // Did the custom handler handle the address as a device? |
||
698 | if (address.type == -1) { |
||
699 | // get null terminated device path |
||
700 | NCDValNullTermString device_path_nts; |
||
701 | if (!NCDVal_StringNullTerminate(device_path, &device_path_nts)) { |
||
702 | ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); |
||
703 | goto error; |
||
704 | } |
||
705 | |||
706 | // open the device |
||
707 | int devfd = open(device_path_nts.data, O_RDWR|O_NONBLOCK); |
||
708 | NCDValNullTermString_Free(&device_path_nts); |
||
709 | if (devfd < 0) { |
||
710 | ModuleLog(i, BLOG_ERROR, "open failed"); |
||
711 | goto error; |
||
712 | } |
||
713 | |||
714 | // init connection |
||
715 | if (!BConnection_Init(&o->connection, BConnection_source_pipe(devfd, 1), i->params->iparams->reactor, o, connection_connection_handler)) { |
||
716 | ModuleLog(i, BLOG_ERROR, "BConnection_Init failed"); |
||
717 | goto error; |
||
718 | } |
||
719 | |||
720 | connection_complete_establish(o); |
||
721 | } else { |
||
722 | // init connector |
||
723 | if (!BConnector_InitGeneric(&o->connect.connector, address, i->params->iparams->reactor, o, connection_connector_handler)) { |
||
724 | ModuleLog(i, BLOG_ERROR, "BConnector_InitGeneric failed"); |
||
725 | goto error; |
||
726 | } |
||
727 | |||
728 | // set state |
||
729 | o->state = CONNECTION_STATE_CONNECTING; |
||
730 | } |
||
731 | |||
732 | return; |
||
733 | |||
734 | error: |
||
735 | // go up in error state |
||
736 | o->state = CONNECTION_STATE_ERROR; |
||
737 | NCDModuleInst_Backend_Up(i); |
||
738 | return; |
||
739 | |||
740 | fail0: |
||
741 | NCDModuleInst_Backend_DeadError(i); |
||
742 | } |
||
743 | |||
744 | static void connect_func_die (void *vo) |
||
745 | { |
||
746 | struct connection *o = vo; |
||
747 | ASSERT(o->type == CONNECTION_TYPE_CONNECT) |
||
748 | |||
749 | // free connector |
||
750 | if (o->state == CONNECTION_STATE_CONNECTING) { |
||
751 | BConnector_Free(&o->connect.connector); |
||
752 | } |
||
753 | |||
754 | // free connection resources |
||
755 | if (o->state == CONNECTION_STATE_ESTABLISHED) { |
||
756 | connection_free_connection(o); |
||
757 | } |
||
758 | |||
759 | NCDModuleInst_Backend_Dead(o->connect.i); |
||
760 | } |
||
761 | |||
762 | static int connect_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) |
||
763 | { |
||
764 | struct connection *o = vo; |
||
765 | ASSERT(o->type == CONNECTION_TYPE_CONNECT) |
||
766 | ASSERT(o->state != CONNECTION_STATE_CONNECTING) |
||
767 | |||
768 | if (name == NCD_STRING_IS_ERROR) { |
||
769 | int is_error = (o->state == CONNECTION_STATE_ERROR); |
||
770 | *out = ncd_make_boolean(mem, is_error); |
||
771 | return 1; |
||
772 | } |
||
773 | |||
774 | return 0; |
||
775 | } |
||
776 | |||
777 | static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) |
||
778 | { |
||
779 | struct read_instance *o = vo; |
||
780 | o->i = i; |
||
781 | |||
782 | // read arguments |
||
783 | if (!NCDVal_ListRead(params->args, 0)) { |
||
784 | ModuleLog(i, BLOG_ERROR, "wrong arity"); |
||
785 | goto fail0; |
||
786 | } |
||
787 | |||
788 | // get connection |
||
789 | struct connection *con_inst = params->method_user; |
||
790 | |||
791 | // check connection state |
||
792 | if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { |
||
793 | ModuleLog(i, BLOG_ERROR, "connection is not established"); |
||
794 | goto fail0; |
||
795 | } |
||
796 | |||
797 | // check if there's already a read in progress |
||
798 | if (con_inst->read_inst) { |
||
799 | ModuleLog(i, BLOG_ERROR, "read is already in progress"); |
||
800 | goto fail0; |
||
801 | } |
||
802 | |||
803 | // get buffer |
||
804 | o->buf = NCDBufStore_GetBuf(&con_inst->store); |
||
805 | if (!o->buf) { |
||
806 | ModuleLog(i, BLOG_ERROR, "NCDBufStore_GetBuf failed"); |
||
807 | goto fail0; |
||
808 | } |
||
809 | |||
810 | // if eof was reached, go up immediately |
||
811 | if (con_inst->recv_closed) { |
||
812 | o->con_inst = NULL; |
||
813 | o->read_size = 0; |
||
814 | NCDModuleInst_Backend_Up(i); |
||
815 | return; |
||
816 | } |
||
817 | |||
818 | // set connection |
||
819 | o->con_inst = con_inst; |
||
820 | |||
821 | // register read operation in connection |
||
822 | con_inst->read_inst = o; |
||
823 | |||
824 | // receive |
||
825 | size_t buf_size = NCDBufStore_BufSize(&con_inst->store); |
||
826 | int to_read = (buf_size > INT_MAX ? INT_MAX : buf_size); |
||
827 | StreamRecvInterface_Receiver_Recv(BConnection_RecvAsync_GetIf(&con_inst->connection), (uint8_t *)NCDBuf_Data(o->buf), to_read); |
||
828 | return; |
||
829 | |||
830 | fail0: |
||
831 | NCDModuleInst_Backend_DeadError(i); |
||
832 | } |
||
833 | |||
834 | static void read_func_die (void *vo) |
||
835 | { |
||
836 | struct read_instance *o = vo; |
||
837 | |||
838 | // if we're receiving, abort connection |
||
839 | if (o->con_inst) { |
||
840 | ASSERT(o->con_inst->state == CONNECTION_STATE_ESTABLISHED) |
||
841 | ASSERT(o->con_inst->read_inst == o) |
||
842 | connection_abort(o->con_inst); |
||
843 | } |
||
844 | |||
845 | // release buffer |
||
846 | BRefTarget_Deref(NCDBuf_RefTarget(o->buf)); |
||
847 | |||
848 | NCDModuleInst_Backend_Dead(o->i); |
||
849 | } |
||
850 | |||
851 | static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) |
||
852 | { |
||
853 | struct read_instance *o = vo; |
||
854 | ASSERT(!o->con_inst) |
||
855 | |||
856 | if (name == NCD_STRING_EMPTY) { |
||
857 | *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->read_size, NCDBuf_RefTarget(o->buf)); |
||
858 | return 1; |
||
859 | } |
||
860 | |||
861 | if (name == NCD_STRING_EOF || name == NCD_STRING_NOT_EOF) { |
||
862 | *out = ncd_make_boolean(mem, (o->read_size == 0) == (name == NCD_STRING_EOF)); |
||
863 | return 1; |
||
864 | } |
||
865 | |||
866 | return 0; |
||
867 | } |
||
868 | |||
869 | static void write_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) |
||
870 | { |
||
871 | struct write_instance *o = vo; |
||
872 | o->i = i; |
||
873 | |||
874 | // read arguments |
||
875 | NCDValRef data_arg; |
||
876 | if (!NCDVal_ListRead(params->args, 1, &data_arg)) { |
||
877 | ModuleLog(i, BLOG_ERROR, "wrong arity"); |
||
878 | goto fail0; |
||
879 | } |
||
880 | if (!NCDVal_IsString(data_arg)) { |
||
881 | ModuleLog(i, BLOG_ERROR, "wrong type"); |
||
882 | goto fail0; |
||
883 | } |
||
884 | |||
885 | // get connection |
||
886 | struct connection *con_inst = params->method_user; |
||
887 | |||
888 | // check connection state |
||
889 | if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { |
||
890 | ModuleLog(i, BLOG_ERROR, "connection is not established"); |
||
891 | goto fail0; |
||
892 | } |
||
893 | |||
894 | // check if there's already a write in progress |
||
895 | if (con_inst->write_inst) { |
||
896 | ModuleLog(i, BLOG_ERROR, "write is already in progress"); |
||
897 | goto fail0; |
||
898 | } |
||
899 | |||
900 | // set send state |
||
901 | o->data = NCDVal_StringMemRef(data_arg); |
||
902 | |||
903 | // if there's nothing to send, go up immediately |
||
904 | if (o->data.len == 0) { |
||
905 | o->con_inst = NULL; |
||
906 | NCDModuleInst_Backend_Up(i); |
||
907 | return; |
||
908 | } |
||
909 | |||
910 | // set connection |
||
911 | o->con_inst = con_inst; |
||
912 | |||
913 | // register write operation in connection |
||
914 | con_inst->write_inst = o; |
||
915 | |||
916 | // send |
||
917 | size_t to_send = (o->data.len > INT_MAX) ? INT_MAX : o->data.len; |
||
918 | StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&con_inst->connection), (uint8_t *)o->data.ptr, to_send); |
||
919 | return; |
||
920 | |||
921 | fail0: |
||
922 | NCDModuleInst_Backend_DeadError(i); |
||
923 | } |
||
924 | |||
925 | static void write_func_die (void *vo) |
||
926 | { |
||
927 | struct write_instance *o = vo; |
||
928 | |||
929 | // if we're sending, abort connection |
||
930 | if (o->con_inst) { |
||
931 | ASSERT(o->con_inst->state == CONNECTION_STATE_ESTABLISHED) |
||
932 | ASSERT(o->con_inst->write_inst == o) |
||
933 | connection_abort(o->con_inst); |
||
934 | } |
||
935 | |||
936 | NCDModuleInst_Backend_Dead(o->i); |
||
937 | } |
||
938 | |||
939 | static void close_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) |
||
940 | { |
||
941 | // read arguments |
||
942 | if (!NCDVal_ListRead(params->args, 0)) { |
||
943 | ModuleLog(i, BLOG_ERROR, "wrong arity"); |
||
944 | goto fail0; |
||
945 | } |
||
946 | |||
947 | // get connection |
||
948 | struct connection *con_inst = params->method_user; |
||
949 | |||
950 | // check connection state |
||
951 | if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { |
||
952 | ModuleLog(i, BLOG_ERROR, "connection is not established"); |
||
953 | goto fail0; |
||
954 | } |
||
955 | |||
956 | // go up |
||
957 | NCDModuleInst_Backend_Up(i); |
||
958 | |||
959 | // abort |
||
960 | connection_abort(con_inst); |
||
961 | return; |
||
962 | |||
963 | fail0: |
||
964 | NCDModuleInst_Backend_DeadError(i); |
||
965 | } |
||
966 | |||
967 | static void listen_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) |
||
968 | { |
||
969 | struct listen_instance *o = vo; |
||
970 | o->i = i; |
||
971 | |||
972 | // read arguments |
||
973 | NCDValRef address_arg; |
||
974 | NCDValRef client_template_arg; |
||
975 | NCDValRef args_arg; |
||
976 | NCDValRef options_arg = NCDVal_NewInvalid(); |
||
977 | if (!NCDVal_ListRead(params->args, 3, &address_arg, &client_template_arg, &args_arg) && |
||
978 | !NCDVal_ListRead(params->args, 4, &address_arg, &client_template_arg, &args_arg, &options_arg) |
||
979 | ) { |
||
980 | ModuleLog(i, BLOG_ERROR, "wrong arity"); |
||
981 | goto fail0; |
||
982 | } |
||
983 | if (!NCDVal_IsString(client_template_arg) || !NCDVal_IsList(args_arg)) { |
||
984 | ModuleLog(i, BLOG_ERROR, "wrong type"); |
||
985 | goto fail0; |
||
986 | } |
||
987 | |||
988 | // parse options |
||
989 | if (!parse_options(i, options_arg, &o->read_buf_size)) { |
||
990 | goto fail0; |
||
991 | } |
||
992 | |||
993 | // remember client template and arguments |
||
994 | o->client_template = client_template_arg; |
||
995 | o->client_template_args = args_arg; |
||
996 | |||
997 | // set no error, not dying |
||
998 | o->have_error = 0; |
||
999 | o->dying = 0; |
||
1000 | |||
1001 | // read address |
||
1002 | struct BConnection_addr address; |
||
1003 | if (!ncd_read_bconnection_addr(address_arg, &address)) { |
||
1004 | ModuleLog(i, BLOG_ERROR, "wrong address"); |
||
1005 | goto error; |
||
1006 | } |
||
1007 | |||
1008 | // init listener |
||
1009 | if (!BListener_InitGeneric(&o->listener, address, i->params->iparams->reactor, o, listen_listener_handler)) { |
||
1010 | ModuleLog(i, BLOG_ERROR, "BListener_InitGeneric failed"); |
||
1011 | goto error; |
||
1012 | } |
||
1013 | |||
1014 | // init clients list |
||
1015 | LinkedList0_Init(&o->clients_list); |
||
1016 | |||
1017 | // go up |
||
1018 | NCDModuleInst_Backend_Up(i); |
||
1019 | return; |
||
1020 | |||
1021 | error: |
||
1022 | // go up with error |
||
1023 | o->have_error = 1; |
||
1024 | NCDModuleInst_Backend_Up(i); |
||
1025 | return; |
||
1026 | |||
1027 | fail0: |
||
1028 | NCDModuleInst_Backend_DeadError(i); |
||
1029 | } |
||
1030 | |||
1031 | static void listen_func_die (void *vo) |
||
1032 | { |
||
1033 | struct listen_instance *o = vo; |
||
1034 | ASSERT(!o->dying) |
||
1035 | |||
1036 | // free listener |
||
1037 | if (!o->have_error) { |
||
1038 | BListener_Free(&o->listener); |
||
1039 | } |
||
1040 | |||
1041 | // if we have no clients, die right away |
||
1042 | if (o->have_error || LinkedList0_IsEmpty(&o->clients_list)) { |
||
1043 | NCDModuleInst_Backend_Dead(o->i); |
||
1044 | return; |
||
1045 | } |
||
1046 | |||
1047 | // set dying |
||
1048 | o->dying = 1; |
||
1049 | |||
1050 | // abort all clients and wait for them |
||
1051 | for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->clients_list); ln; ln = LinkedList0Node_Next(ln)) { |
||
1052 | struct connection *con = UPPER_OBJECT(ln, struct connection, listen.clients_list_node); |
||
1053 | ASSERT(con->type == CONNECTION_TYPE_LISTEN) |
||
1054 | ASSERT(con->listen.listen_inst == o) |
||
1055 | ASSERT(con->state == CONNECTION_STATE_ESTABLISHED || con->state == CONNECTION_STATE_ABORTED) |
||
1056 | |||
1057 | if (con->state != CONNECTION_STATE_ABORTED) { |
||
1058 | connection_abort(con); |
||
1059 | } |
||
1060 | } |
||
1061 | } |
||
1062 | |||
1063 | static int listen_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) |
||
1064 | { |
||
1065 | struct listen_instance *o = vo; |
||
1066 | |||
1067 | if (name == NCD_STRING_IS_ERROR) { |
||
1068 | *out = ncd_make_boolean(mem, o->have_error); |
||
1069 | return 1; |
||
1070 | } |
||
1071 | |||
1072 | return 0; |
||
1073 | } |
||
1074 | |||
1075 | static struct NCDModule modules[] = { |
||
1076 | { |
||
1077 | .type = "sys.connect", |
||
1078 | .base_type = "sys.socket", |
||
1079 | .func_new2 = connect_func_new, |
||
1080 | .func_die = connect_func_die, |
||
1081 | .func_getvar2 = connect_func_getvar, |
||
1082 | .alloc_size = sizeof(struct connection) |
||
1083 | }, { |
||
1084 | .type = "sys.socket::read", |
||
1085 | .func_new2 = read_func_new, |
||
1086 | .func_die = read_func_die, |
||
1087 | .func_getvar2 = read_func_getvar, |
||
1088 | .alloc_size = sizeof(struct read_instance) |
||
1089 | }, { |
||
1090 | .type = "sys.socket::write", |
||
1091 | .func_new2 = write_func_new, |
||
1092 | .func_die = write_func_die, |
||
1093 | .alloc_size = sizeof(struct write_instance) |
||
1094 | }, { |
||
1095 | .type = "sys.socket::close", |
||
1096 | .func_new2 = close_func_new |
||
1097 | }, { |
||
1098 | .type = "sys.listen", |
||
1099 | .func_new2 = listen_func_new, |
||
1100 | .func_die = listen_func_die, |
||
1101 | .func_getvar2 = listen_func_getvar, |
||
1102 | .alloc_size = sizeof(struct listen_instance) |
||
1103 | }, { |
||
1104 | .type = NULL |
||
1105 | } |
||
1106 | }; |
||
1107 | |||
1108 | const struct NCDModuleGroup ncdmodule_socket = { |
||
1109 | .modules = modules, |
||
1110 | .strings = strings |
||
1111 | }; |