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