1 /***********************************************************************
2 *
3 * event_tcp.c -- implementation of event-driven socket I/O.
4 *
5 * Copyright (C) 2001 Roaring Penguin Software Inc.
6 * Copyright (C) 2018-2021 Dianne Skoll
7 *
8 * This program may be distributed according to the terms of the GNU
9 * General Public License, version 2 or (at your option) any later version.
10 *
11 * LIC: GPL
12 *
13 ***********************************************************************/
14
15 #include "event_tcp.h"
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <sys/types.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <string.h>
22
23 static void free_state(EventTcpState *state);
24
25 typedef struct EventTcpConnectState_t {
26 int fd;
27 EventHandler *conn;
28 EventTcpConnectFunc f;
29 void *data;
30 } EventTcpConnectState;
31
32 /**********************************************************************
33 * %FUNCTION: handle_accept
34 * %ARGUMENTS:
35 * es -- event selector
36 * fd -- socket
37 * flags -- ignored
38 * data -- the accept callback function
39 * %RETURNS:
40 * Nothing
41 * %DESCRIPTION:
42 * Calls accept; if a connection arrives, calls the accept callback
43 * function with connected descriptor
44 ***********************************************************************/
45 static void
handle_accept(EventSelector * es,int fd,unsigned int flags,void * data)46 handle_accept(EventSelector *es,
47 int fd,
48 unsigned int flags,
49 void *data)
50 {
51 int conn;
52 EventTcpAcceptFunc f;
53
54 EVENT_DEBUG(("tcp_handle_accept(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
55 conn = accept(fd, NULL, NULL);
56 if (conn < 0) return;
57 f = (EventTcpAcceptFunc) data;
58
59 f(es, conn);
60 }
61
62 /**********************************************************************
63 * %FUNCTION: handle_connect
64 * %ARGUMENTS:
65 * es -- event selector
66 * fd -- socket
67 * flags -- ignored
68 * data -- the accept callback function
69 * %RETURNS:
70 * Nothing
71 * %DESCRIPTION:
72 * Calls accept; if a connection arrives, calls the accept callback
73 * function with connected descriptor
74 ***********************************************************************/
75 static void
handle_connect(EventSelector * es,int fd,unsigned int flags,void * data)76 handle_connect(EventSelector *es,
77 int fd,
78 unsigned int flags,
79 void *data)
80 {
81 int error = 0;
82 socklen_t len = sizeof(error);
83 EventTcpConnectState *state = (EventTcpConnectState *) data;
84
85 EVENT_DEBUG(("tcp_handle_connect(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
86
87 /* Cancel writable event */
88 Event_DelHandler(es, state->conn);
89 state->conn = NULL;
90
91 /* Timeout? */
92 if (flags & EVENT_FLAG_TIMEOUT) {
93 errno = ETIMEDOUT;
94 state->f(es, fd, EVENT_TCP_FLAG_TIMEOUT, state->data);
95 free(state);
96 return;
97 }
98
99 /* Check for pending error */
100 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
101 state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data);
102 free(state);
103 return;
104 }
105 if (error) {
106 errno = error;
107 state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data);
108 free(state);
109 return;
110 }
111
112 /* It looks cool! */
113 state->f(es, fd, EVENT_TCP_FLAG_COMPLETE, state->data);
114 free(state);
115 }
116
117 /**********************************************************************
118 * %FUNCTION: EventTcp_CreateAcceptor
119 * %ARGUMENTS:
120 * es -- event selector
121 * socket -- listening socket
122 * f -- function to call when a connection is accepted
123 * data -- extra data to pass to f.
124 * %RETURNS:
125 * An event handler on success, NULL on failure.
126 * %DESCRIPTION:
127 * Sets up an accepting socket and calls "f" whenever a new
128 * connection arrives.
129 ***********************************************************************/
130 EventHandler *
EventTcp_CreateAcceptor(EventSelector * es,int socket,EventTcpAcceptFunc f)131 EventTcp_CreateAcceptor(EventSelector *es,
132 int socket,
133 EventTcpAcceptFunc f)
134 {
135 int flags;
136
137 EVENT_DEBUG(("EventTcp_CreateAcceptor(es=%p, socket=%d)\n", es, socket));
138 /* Make sure socket is non-blocking */
139 flags = fcntl(socket, F_GETFL, 0);
140 if (flags == -1) {
141 return NULL;
142 }
143 if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
144 return NULL;
145 }
146
147 return Event_AddHandler(es, socket, EVENT_FLAG_READABLE,
148 handle_accept, (void *) f);
149
150 }
151
152 /**********************************************************************
153 * %FUNCTION: free_state
154 * %ARGUMENTS:
155 * state -- EventTcpState to free
156 * %RETURNS:
157 * Nothing
158 * %DESCRIPTION:
159 * Frees all state associated with the TcpEvent.
160 ***********************************************************************/
161 static void
free_state(EventTcpState * state)162 free_state(EventTcpState *state)
163 {
164 if (!state) return;
165 EVENT_DEBUG(("tcp_free_state(state=%p)\n", state));
166 if (state->buf) free(state->buf);
167 if (state->eh) Event_DelHandler(state->es, state->eh);
168 free(state);
169 }
170
171 /**********************************************************************
172 * %FUNCTION: handle_readable
173 * %ARGUMENTS:
174 * es -- event selector
175 * fd -- the readable socket
176 * flags -- ignored
177 * data -- the EventTcpState object
178 * %RETURNS:
179 * Nothing
180 * %DESCRIPTION:
181 * Continues to fill buffer. Calls callback when done.
182 ***********************************************************************/
183 static void
handle_readable(EventSelector * es,int fd,unsigned int flags,void * data)184 handle_readable(EventSelector *es,
185 int fd,
186 unsigned int flags,
187 void *data)
188 {
189 EventTcpState *state = (EventTcpState *) data;
190 int done = state->cur - state->buf;
191 int togo = state->len - done;
192 int nread = 0;
193 int flag;
194
195 EVENT_DEBUG(("tcp_handle_readable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
196
197 /* Timed out? */
198 if (flags & EVENT_FLAG_TIMEOUT) {
199 errno = ETIMEDOUT;
200 (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT,
201 state->data);
202 free_state(state);
203 return;
204 }
205 if (state->delim < 0) {
206 /* Not looking for a delimiter */
207 /* togo had better not be zero here! */
208 nread = read(fd, state->cur, togo);
209 if (nread <= 0) {
210 /* Change connection reset to EOF if we have read at least
211 one char */
212 if (nread < 0 && errno == ECONNRESET && done > 0) {
213 nread = 0;
214 }
215 flag = (nread) ? EVENT_TCP_FLAG_IOERROR : EVENT_TCP_FLAG_EOF;
216 /* error or EOF */
217 (state->f)(es, state->socket, state->buf, done, flag, state->data);
218 free_state(state);
219 return;
220 }
221 state->cur += nread;
222 done += nread;
223 if (done >= state->len) {
224 /* Read enough! */
225 (state->f)(es, state->socket, state->buf, done,
226 EVENT_TCP_FLAG_COMPLETE, state->data);
227 free_state(state);
228 return;
229 }
230 } else {
231 /* Looking for a delimiter */
232 while ( (togo > 0) && (nread = read(fd, state->cur, 1)) == 1) {
233 togo--;
234 done++;
235 state->cur++;
236 if (*(state->cur - 1) == state->delim) break;
237 }
238
239 if (nread <= 0) {
240 /* Error or EOF -- check for EAGAIN */
241 if (nread < 0 && errno == EAGAIN) return;
242 }
243
244 /* Some other error, or EOF, or delimiter, or read enough */
245 if (nread < 0) {
246 flag = EVENT_TCP_FLAG_IOERROR;
247 } else if (nread == 0) {
248 flag = EVENT_TCP_FLAG_EOF;
249 } else {
250 flag = EVENT_TCP_FLAG_COMPLETE;
251 }
252 (state->f)(es, state->socket, state->buf, done, flag, state->data);
253 free_state(state);
254 return;
255 }
256 }
257
258 /**********************************************************************
259 * %FUNCTION: handle_writeable
260 * %ARGUMENTS:
261 * es -- event selector
262 * fd -- the writeable socket
263 * flags -- ignored
264 * data -- the EventTcpState object
265 * %RETURNS:
266 * Nothing
267 * %DESCRIPTION:
268 * Continues to fill buffer. Calls callback when done.
269 ***********************************************************************/
270 static void
handle_writeable(EventSelector * es,int fd,unsigned int flags,void * data)271 handle_writeable(EventSelector *es,
272 int fd,
273 unsigned int flags,
274 void *data)
275 {
276 EventTcpState *state = (EventTcpState *) data;
277 int done = state->cur - state->buf;
278 int togo = state->len - done;
279 int n;
280
281 /* Timed out? */
282 if (flags & EVENT_FLAG_TIMEOUT) {
283 errno = ETIMEDOUT;
284 (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT,
285 state->data);
286 free_state(state);
287 return;
288 }
289
290 /* togo had better not be zero here! */
291 n = write(fd, state->cur, togo);
292
293 EVENT_DEBUG(("tcp_handle_writeable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
294 if (n <= 0) {
295 /* error */
296 if (state->f) {
297 (state->f)(es, state->socket, state->buf, done,
298 EVENT_TCP_FLAG_IOERROR,
299 state->data);
300 } else {
301 close(fd);
302 }
303 free_state(state);
304 return;
305 }
306 state->cur += n;
307 done += n;
308 if (done >= state->len) {
309 /* Written enough! */
310 if (state->f) {
311 (state->f)(es, state->socket, state->buf, done,
312 EVENT_TCP_FLAG_COMPLETE, state->data);
313 } else {
314 close(fd);
315 }
316 free_state(state);
317 return;
318 }
319
320 }
321
322 /**********************************************************************
323 * %FUNCTION: EventTcp_ReadBuf
324 * %ARGUMENTS:
325 * es -- event selector
326 * socket -- socket to read from
327 * len -- maximum number of bytes to read
328 * delim -- delimiter at which to stop reading, or -1 if we should
329 * read exactly len bytes
330 * f -- function to call on EOF or when all bytes have been read
331 * timeout -- if non-zero, timeout in seconds after which we cancel
332 * operation.
333 * data -- extra data to pass to function f.
334 * %RETURNS:
335 * A new EventTcpState token or NULL on error
336 * %DESCRIPTION:
337 * Sets up a handler to fill a buffer from a socket.
338 ***********************************************************************/
339 EventTcpState *
EventTcp_ReadBuf(EventSelector * es,int socket,int len,int delim,EventTcpIOFinishedFunc f,int timeout,void * data)340 EventTcp_ReadBuf(EventSelector *es,
341 int socket,
342 int len,
343 int delim,
344 EventTcpIOFinishedFunc f,
345 int timeout,
346 void *data)
347 {
348 EventTcpState *state;
349 int flags;
350 struct timeval t;
351
352 EVENT_DEBUG(("EventTcp_ReadBuf(es=%p, socket=%d, len=%d, delim=%d, timeout=%d)\n", es, socket, len, delim, timeout));
353 if (len <= 0) return NULL;
354 if (socket < 0) return NULL;
355
356 /* Make sure socket is non-blocking */
357 flags = fcntl(socket, F_GETFL, 0);
358 if (flags == -1) {
359 return NULL;
360 }
361 if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
362 return NULL;
363 }
364
365 state = malloc(sizeof(EventTcpState));
366 if (!state) return NULL;
367
368 memset(state, 0, sizeof(EventTcpState));
369
370 state->socket = socket;
371
372 state->buf = malloc(len);
373 if (!state->buf) {
374 free_state(state);
375 return NULL;
376 }
377
378 state->cur = state->buf;
379 state->len = len;
380 state->f = f;
381 state->es = es;
382
383 if (timeout <= 0) {
384 t.tv_sec = -1;
385 t.tv_usec = -1;
386 } else {
387 t.tv_sec = timeout;
388 t.tv_usec = 0;
389 }
390
391 state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_READABLE,
392 t, handle_readable,
393 (void *) state);
394 if (!state->eh) {
395 free_state(state);
396 return NULL;
397 }
398 state->data = data;
399 state->delim = delim;
400 EVENT_DEBUG(("EventTcp_ReadBuf() -> %p\n", state));
401
402 return state;
403 }
404
405 /**********************************************************************
406 * %FUNCTION: EventTcp_WriteBuf
407 * %ARGUMENTS:
408 * es -- event selector
409 * socket -- socket to read from
410 * buf -- buffer to write
411 * len -- number of bytes to write
412 * f -- function to call on EOF or when all bytes have been read
413 * timeout -- timeout after which to cancel operation
414 * data -- extra data to pass to function f.
415 * %RETURNS:
416 * A new EventTcpState token or NULL on error
417 * %DESCRIPTION:
418 * Sets up a handler to fill a buffer from a socket.
419 ***********************************************************************/
420 EventTcpState *
EventTcp_WriteBuf(EventSelector * es,int socket,char * buf,int len,EventTcpIOFinishedFunc f,int timeout,void * data)421 EventTcp_WriteBuf(EventSelector *es,
422 int socket,
423 char *buf,
424 int len,
425 EventTcpIOFinishedFunc f,
426 int timeout,
427 void *data)
428 {
429 EventTcpState *state;
430 int flags;
431 struct timeval t;
432
433 EVENT_DEBUG(("EventTcp_WriteBuf(es=%p, socket=%d, len=%d, timeout=%d)\n", es, socket, len, timeout));
434 if (len <= 0) return NULL;
435 if (socket < 0) return NULL;
436
437 /* Make sure socket is non-blocking */
438 flags = fcntl(socket, F_GETFL, 0);
439 if (flags == -1) {
440 return NULL;
441 }
442 if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
443 return NULL;
444 }
445
446 state = malloc(sizeof(EventTcpState));
447 if (!state) return NULL;
448
449 memset(state, 0, sizeof(EventTcpState));
450
451 state->socket = socket;
452
453 state->buf = malloc(len);
454 if (!state->buf) {
455 free_state(state);
456 return NULL;
457 }
458 memcpy(state->buf, buf, len);
459
460 state->cur = state->buf;
461 state->len = len;
462 state->f = f;
463 state->es = es;
464
465 if (timeout <= 0) {
466 t.tv_sec = -1;
467 t.tv_usec = -1;
468 } else {
469 t.tv_sec = timeout;
470 t.tv_usec = 0;
471 }
472
473 state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_WRITEABLE,
474 t, handle_writeable,
475 (void *) state);
476 if (!state->eh) {
477 free_state(state);
478 return NULL;
479 }
480
481 state->data = data;
482 state->delim = -1;
483 EVENT_DEBUG(("EventTcp_WriteBuf() -> %p\n", state));
484 return state;
485 }
486
487 /**********************************************************************
488 * %FUNCTION: EventTcp_Connect
489 * %ARGUMENTS:
490 * es -- event selector
491 * fd -- descriptor to connect
492 * addr -- address to connect to
493 * addrlen -- length of address
494 * f -- function to call with connected socket
495 * data -- extra data to pass to f
496 * %RETURNS:
497 * Nothing
498 * %DESCRIPTION:
499 * Does a non-blocking connect on fd
500 ***********************************************************************/
501 void
EventTcp_Connect(EventSelector * es,int fd,struct sockaddr const * addr,socklen_t addrlen,EventTcpConnectFunc f,int timeout,void * data)502 EventTcp_Connect(EventSelector *es,
503 int fd,
504 struct sockaddr const *addr,
505 socklen_t addrlen,
506 EventTcpConnectFunc f,
507 int timeout,
508 void *data)
509 {
510 int flags;
511 int n;
512 EventTcpConnectState *state;
513 struct timeval t;
514
515 /* Make sure socket is non-blocking */
516 flags = fcntl(fd, F_GETFL, 0);
517 if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
518 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
519 return;
520 }
521
522 n = connect(fd, addr, addrlen);
523 if (n < 0) {
524 if (errno != EINPROGRESS) {
525 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
526 return;
527 }
528 }
529
530 if (n == 0) { /* Connect succeeded immediately */
531 f(es, fd, EVENT_TCP_FLAG_COMPLETE, data);
532 return;
533 }
534
535 state = malloc(sizeof(*state));
536 if (!state) {
537 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
538 return;
539 }
540 state->f = f;
541 state->fd = fd;
542 state->data = data;
543
544 if (timeout <= 0) {
545 t.tv_sec = -1;
546 t.tv_usec = -1;
547 } else {
548 t.tv_sec = timeout;
549 t.tv_usec = 0;
550 }
551
552 state->conn = Event_AddHandlerWithTimeout(es, fd, EVENT_FLAG_WRITEABLE,
553 t, handle_connect,
554 (void *) state);
555 if (!state->conn) {
556 free(state);
557 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
558 return;
559 }
560 }
561
562 /**********************************************************************
563 * %FUNCTION: EventTcp_CancelPending
564 * %ARGUMENTS:
565 * s -- an EventTcpState
566 * %RETURNS:
567 * Nothing
568 * %DESCRIPTION:
569 * Cancels the pending event handler
570 ***********************************************************************/
571 void
EventTcp_CancelPending(EventTcpState * s)572 EventTcp_CancelPending(EventTcpState *s)
573 {
574 free_state(s);
575 }
576