HomePort
tcpd.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011 Aalborg University. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this list of
8  * conditions and the following disclaimer.
9  *
10  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11  * of conditions and the following disclaimer in the documentation and/or other materials
12  * provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY Aalborg University ''AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Aalborg University OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  */
27 
28 #include "tcpd_intern.h"
29 #include "hpd/hpd_shared_api.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <netdb.h>
35 #include <unistd.h>
36 #include <arpa/inet.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 
52 static void *tcpd_get_in_addr(struct sockaddr *sa)
53 {
54  if (sa->sa_family == AF_INET) {
55  // IPv4
56  return &(((struct sockaddr_in*)sa)->sin_addr.s_addr);
57  } else {
58  // IPv6
59  return &(((struct sockaddr_in6*)sa)->sin6_addr.s6_addr);
60  }
61 }
62 
74 static void tcpd_on_ev_recv(hpd_ev_loop_t *loop, struct ev_io *watcher, int revents)
75 {
76  ssize_t received;
77  hpd_tcpd_conn_t *conn = watcher->data;
79  size_t max_data_size = settings->max_data_size;
80  char buffer[max_data_size];
81  const hpd_module_t *context = conn->tcpd->context;
82 
83  HPD_LOG_VERBOSE(context, "Receiving data from %s", conn->ip);
84  if ((received = recv(watcher->fd, buffer, max_data_size-1, 0)) < 0) {
85  int err = errno;
86  if (received == -1 && errno == EWOULDBLOCK) {
87  HPD_LOG_WARN(context, "libev callback called without data to receive (conn: %s)", conn->ip);
88  return;
89  }
90  HPD_LOG_ERROR(context, "recv(): %s", strerror(err));
91  if (hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
92  return;
93  } else if (received == 0) {
94  HPD_LOG_INFO(context, "Connection closed by %s\n", conn->ip);
95  if (hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
96  return;
97  }
98 
99  if (settings->on_receive) {
100  if (settings->on_receive(conn->tcpd, conn,
101  settings->tcpd_ctx, &conn->ctx,
102  buffer, (size_t) received)) {
103  HPD_LOG_ERROR(context, "Failed to handle new data, killing it.");
104  if (hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
105  return;
106  }
107  }
108 
109  // Reset timeout
110  if (conn->timeout) {
111  conn->timeout_watcher.repeat = conn->tcpd->settings.timeout;
112  ev_timer_again(loop, &conn->timeout_watcher);
113  }
114 }
115 
128 static void tcpd_on_ev_send(hpd_ev_loop_t *loop, struct ev_io *watcher, int revents)
129 {
130  hpd_tcpd_conn_t *conn = watcher->data;
131  ssize_t sent;
132  const hpd_module_t *context = conn->tcpd->context;
133 
134  sent = send(watcher->fd, conn->send_msg, conn->send_len, 0);
135  if (sent < 0) {
136  int err = errno;
137  HPD_LOG_ERROR(context, "send(): %s", strerror(err));
138  } else if (sent == conn->send_len) {
139  free(conn->send_msg);
140  conn->send_msg = NULL;
141  conn->send_len = 0;
142  } else {
143  conn->send_len -= sent;
144  char *s = malloc(conn->send_len*sizeof(char));
145  if (!s) {
146  HPD_LOG_ERROR(context, "Cannot allocate enough memory.");
147  free(conn->send_msg);
148  conn->send_msg = NULL;
149  conn->send_len = 0;
150  } else {
151  strcpy(s, &conn->send_msg[sent]);
152  free(conn->send_msg);
153  conn->send_msg = s;
154  return;
155  }
156  }
157 
158  ev_io_stop(conn->tcpd->loop, &conn->send_watcher);
159  if (conn->send_close && hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
160 }
161 
171 static void tcpd_on_ev_timeout(hpd_ev_loop_t *loop, struct ev_timer *watcher, int revents)
172 {
173  hpd_tcpd_conn_t *conn = watcher->data;
174  const hpd_module_t *context = conn->tcpd->context;
175 
176  HPD_LOG_INFO(context, "Timeout on %s [%ld].", conn->ip, (long)conn);
177  if (hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
178 }
179 
193 static void tcpd_on_ev_conn(hpd_ev_loop_t *loop, struct ev_io *watcher, int revents)
194 {
195  char ip_string[INET6_ADDRSTRLEN];
196  int in_fd;
197  socklen_t in_size;
198  struct sockaddr_storage in_addr_storage;
199  struct sockaddr *in_addr = (struct sockaddr *)&in_addr_storage;
200  hpd_tcpd_conn_t *conn;
201  hpd_tcpd_t *tcpd = (hpd_tcpd_t *)watcher->data;
203  const hpd_module_t *context = tcpd->context;
204 
205  // Accept connection
206  in_size = sizeof *in_addr;
207  if ((in_fd = accept(watcher->fd, in_addr, &in_size)) < 0) {
208  int err = errno;
209  HPD_LOG_ERROR(context, "accept(): %s", strerror(err));
210  return;
211  }
212 
213  // Print a nice message
214  inet_ntop(in_addr_storage.ss_family, tcpd_get_in_addr(in_addr), ip_string, sizeof ip_string);
215  HPD_LOG_INFO(context, "Got connection from %s.", ip_string);
216 
217  // Create conn and parser
218  conn = malloc(sizeof(hpd_tcpd_conn_t));
219  if (conn == NULL) {
220  HPD_LOG_ERROR(context, "Cannot allocation memory for connection.");
221  close(in_fd);
222  return;
223  }
224  conn->tcpd = watcher->data;
225  strcpy(conn->ip, ip_string);
226  conn->timeout_watcher.data = conn;
227  conn->recv_watcher.data = conn;
228  conn->send_watcher.data = conn;
229  conn->ctx = NULL;
230  conn->send_msg = NULL;
231  conn->send_len = 0;
232  conn->send_close = 0;
233  conn->timeout = 1;
234 
235  // Add connection to list
236  TAILQ_INSERT_TAIL(&conn->tcpd->conns, conn, HPD_TAILQ_FIELD);
237 
238  // Call back
239  if (settings->on_connect) {
240  if (settings->on_connect(conn->tcpd, conn, settings->tcpd_ctx, &conn->ctx)) {
241  HPD_LOG_ERROR(context, "Failed to handle new connection, killing it.");
242  if (hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
243  return;
244  }
245  }
246 
247  // Start timeout and io watcher
248  ev_io_init(&conn->recv_watcher, tcpd_on_ev_recv, in_fd, EV_READ);
249  ev_io_init(&conn->send_watcher, tcpd_on_ev_send, in_fd, EV_WRITE);
250  ev_io_start(loop, &conn->recv_watcher);
251  ev_init(&conn->timeout_watcher, tcpd_on_ev_timeout);
252  conn->timeout_watcher.repeat = settings->timeout;
253  if (conn->timeout)
254  ev_timer_again(loop, &conn->timeout_watcher);
255 }
256 
271 {
272  if (settings == NULL || context == NULL || loop == NULL) return HPD_E_NULL;
273  if (settings->port <= HPD_TCPD_P_SYSTEM_PORTS_START || settings->port > HPD_TCPD_P_DYNAMIC_PORTS_END) return HPD_E_ARGUMENT;
274 
275  (*tcpd) = malloc(sizeof(hpd_tcpd_t));
276  if (!(*tcpd)) return HPD_E_ALLOC;
277 
278  memcpy(&(*tcpd)->settings, settings, sizeof(hpd_tcpd_settings_t));
279  sprintf((*tcpd)->port_str, "%i", settings->port);
280 
281  (*tcpd)->context = context;
282  (*tcpd)->loop = loop;
283  TAILQ_INIT(&(*tcpd)->conns);
284 
285  return HPD_E_SUCCESS;
286 }
287 
298 {
299  if (!tcpd) return HPD_E_NULL;
300  free(tcpd);
301  return HPD_E_SUCCESS;
302 }
303 
319 {
320  if (!tcpd) return HPD_E_NULL;
321 
322  int rc;
323  struct addrinfo hints;
324  struct addrinfo *servinfo, *p;
325 
326  // Clear struct and set requirements for socket
327  memset(&hints, 0, sizeof hints);
328  hints.ai_family = AF_UNSPEC; // IPv4/IPv6
329  hints.ai_socktype = SOCK_STREAM; // TCP Stream
330  hints.ai_flags = AI_PASSIVE; // Wildcard address
331 
332  // Get address infos we later use this to open socket
333  if ((rc = getaddrinfo(NULL, tcpd->port_str, &hints, &servinfo)) != 0) {
334  switch (rc) {
335  case EAI_SYSTEM: {
336  int err = errno;
337  HPD_LOG_ERROR(tcpd->context, "getaddrinfo() failed with: %s", gai_strerror(rc));
338  HPD_LOG_ERROR(tcpd->context, "System error message: %s", strerror(err));
339  freeaddrinfo(servinfo);
340  return HPD_E_UNKNOWN;
341  }
342  default: {
343  HPD_LOG_ERROR(tcpd->context, "getaddrinfo() failed with: %s", gai_strerror(rc));
344  freeaddrinfo(servinfo);
345  return HPD_E_UNKNOWN;
346  }
347  }
348  }
349 
350  // Loop through results and bind to first
351  for (p = servinfo; p != NULL; p=p->ai_next) {
352 
353  // Create socket
354  if ((tcpd->sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
355  int err = errno;
356  HPD_LOG_DEBUG(tcpd->context, "socket() failed with: %s", strerror(err));
357  continue;
358  }
359 
360  // Change to non-blocking sockets
361  if (fcntl(tcpd->sockfd, F_SETFL, O_NONBLOCK)) {
362  int err = errno;
363  HPD_LOG_DEBUG(tcpd->context, "fcntl() failed with: %s", strerror(err));
364  close(tcpd->sockfd);
365  continue;
366  }
367 
368  // Reuse addr for testing purposes
369 #ifdef DEBUG
370  int yes = 1;
371  if (setsockopt(tcpd->sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
372  int err = errno;
373  HPD_LOG_DEBUG(tcpd->context, "setsockopt() failed with: %s", strerror(err));
374  close(tcpd->sockfd);
375  continue;
376  }
377 #endif
378 
379  // Bind to socket
380  if (bind(tcpd->sockfd, p->ai_addr, p->ai_addrlen) != 0) {
381  int err = errno;
382  HPD_LOG_DEBUG(tcpd->context, "bind() failed with: %s", strerror(err));
383  close(tcpd->sockfd);
384  continue;
385  }
386 
387  break;
388  }
389 
390  // Check if we binded to anything
391  if (p == NULL) {
392  close(tcpd->sockfd);
393  freeaddrinfo(servinfo);
394  return HPD_E_UNKNOWN;
395  }
396 
397  // Clean up
398  freeaddrinfo(servinfo);
399 
400  // Listen on socket
401  if (listen(tcpd->sockfd, SOMAXCONN) < 0) {
402  int err = errno;
403  HPD_LOG_DEBUG(tcpd->context, "listen() failed with: %s", strerror(err));
404  close(tcpd->sockfd);
405  return HPD_E_UNKNOWN;
406  }
407 
408  // Set listener on libev
409  tcpd->watcher.data = tcpd;
410  ev_io_init(&tcpd->watcher, tcpd_on_ev_conn, tcpd->sockfd, EV_READ);
411  ev_io_start(tcpd->loop, &tcpd->watcher);
412 
413  return HPD_E_SUCCESS;
414 }
415 
429 {
430  if (!tcpd) return HPD_E_NULL;
431 
432  hpd_tcpd_conn_t *conn, *tmp;
433  const hpd_module_t *context = tcpd->context;
434 
435  // Stop accept watcher
436  ev_io_stop(tcpd->loop, &tcpd->watcher);
437 
438  // Kill all connections
439  TAILQ_FOREACH_SAFE(conn, &tcpd->conns, HPD_TAILQ_FIELD, tmp)
440  if (hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
441 
442  // Close socket
443  if (close(tcpd->sockfd) != 0) {
444  int err = errno;
445  HPD_LOG_ERROR(context, "close(): %s", strerror(err));
446  return HPD_E_UNKNOWN;
447  }
448 
449  return HPD_E_SUCCESS;
450 }
451 
460 {
461  if (!conn || !ip) return HPD_E_NULL;
462  (*ip) = conn->ip;
463  return HPD_E_SUCCESS;
464 }
465 
466 
480 {
481  if (!conn) return HPD_E_NULL;
482  conn->timeout = 0;
483  ev_timer_stop(conn->tcpd->loop, &conn->timeout_watcher);
484  return HPD_E_SUCCESS;
485 }
486 
487 
503 hpd_error_t hpd_tcpd_conn_sendf(hpd_tcpd_conn_t *conn, const char *fmt, ...)
504 {
505  if (!conn) return HPD_E_NULL;
506  va_list arg;
507  va_start(arg, fmt);
508  hpd_error_t rc = hpd_tcpd_conn_vsendf(conn, fmt, arg);
509  va_end(arg);
510  return rc;
511 }
512 
513 
534 hpd_error_t hpd_tcpd_conn_vsendf(hpd_tcpd_conn_t *conn, const char *fmt, va_list vp)
535 {
536  if (!conn) return HPD_E_NULL;
537 
538  char *new_msg;
539  int new_len;
540  va_list vp2;
541 
542  // Copy arg to avoid errors on 64bit
543  va_copy(vp2, vp);
544 
545  // Get the length to expand with
546  new_len = vsnprintf("", 0, fmt, vp);
547  if (new_len < 0) {
548  int err = errno;
549  HPD_LOG_DEBUG(conn->tcpd->context, "vsnprintf(): %s", strerror(err));
550  return HPD_E_UNKNOWN;
551  }
552 
553  // Expand message to send
554  new_msg = realloc(conn->send_msg,
555  (conn->send_len + new_len + 1)*sizeof(char));
556  if (new_msg == NULL) HPD_LOG_RETURN_E_ALLOC(conn->tcpd->context);
557  conn->send_msg = new_msg;
558 
559  // Concatenate strings
560  vsprintf(&(conn->send_msg[conn->send_len]), fmt, vp2);
561 
562  // Start send watcher
563  if (conn->send_len == 0 && conn->tcpd != NULL)
564  ev_io_start(conn->tcpd->loop, &conn->send_watcher);
565 
566  // Update length
567  conn->send_len += new_len;
568 
569  return HPD_E_SUCCESS;
570 }
571 
582 {
583  if (!conn) return HPD_E_NULL;
584 
585  const hpd_module_t *context = conn->tcpd->context;
586 
587  conn->send_close = 1;
588 
589  if (conn->send_msg == NULL) {
590  ev_io_stop(conn->tcpd->loop, &conn->send_watcher);
591  if (conn->send_close && hpd_tcpd_conn_kill(conn)) HPD_LOG_ERROR(context, "Failed to kill connection.");
592  }
593 
594  return HPD_E_SUCCESS;
595 }
596 
609 {
610  if (!conn) return HPD_E_NULL;
611 
613 
614  // Print messange
615  HPD_LOG_INFO(conn->tcpd->context, "Killing connection from %s.", conn->ip);
616 
617  // Stop circular calls and only kill this connection once
618  if (conn->recv_watcher.fd < 0) return HPD_E_SUCCESS;
619 
620  // Stop watchers
621  ev_io_stop(conn->tcpd->loop, &conn->recv_watcher);
622  ev_io_stop(conn->tcpd->loop, &conn->send_watcher);
623  ev_timer_stop(conn->tcpd->loop, &conn->timeout_watcher);
624 
625  // Close socket
626  if (close(conn->recv_watcher.fd) != 0) {
627  int err = errno;
628  HPD_LOG_ERROR(conn->tcpd->context, "close(): %s", strerror(err));
629  }
630  conn->recv_watcher.fd = -1;
631 
632  // Remove from list
633  TAILQ_REMOVE(&conn->tcpd->conns, conn, HPD_TAILQ_FIELD);
634 
635  // Call back
636  if (settings->on_disconnect && settings->on_disconnect(conn->tcpd, conn, settings->tcpd_ctx, &conn->ctx))
637  HPD_LOG_ERROR(conn->tcpd->context, "Failed to disconnect.");
638 
639  // Cleanup
640  free(conn->send_msg);
641  free(conn);
642 
643  return HPD_E_SUCCESS;
644 }
Instance of a tcpd.
Definition: tcpd_intern.h:45
hpd_error_t hpd_tcpd_destroy(hpd_tcpd_t *tcpd)
Destroy tcpd and free used memory.
Definition: tcpd.c:297
int timeout
Restart timeout watcher ?
Definition: tcpd_intern.h:61
hpd_error_t hpd_tcpd_stop(hpd_tcpd_t *tcpd)
Stop an already running tcpd.
Definition: tcpd.c:428
ev_io send_watcher
Send watcher.
Definition: tcpd_intern.h:63
static void tcpd_on_ev_send(hpd_ev_loop_t *loop, struct ev_io *watcher, int revents)
Send callback for io-watcher.
Definition: tcpd.c:128
hpd_tcpd_port_t port
Port number.
Definition: hpd_tcpd.h:95
ev_timer timeout_watcher
Timeout watcher.
Definition: tcpd_intern.h:60
free(data.url)
hpd_error_t hpd_tcpd_conn_keep_open(hpd_tcpd_conn_t *conn)
Disable timeout on connection.
Definition: tcpd.c:479
#define HPD_TAILQ_FIELD
Definition: hpd_queue.h:37
struct hp_settings settings
#define HPD_LOG_INFO(CONTEXT, FMT,...)
char * send_msg
Data to send.
Definition: tcpd_intern.h:64
hpd_error_t hpd_tcpd_start(hpd_tcpd_t *tcpd)
Start the tcpd.
Definition: tcpd.c:318
hpd_tcpd_t * tcpd
Webserver instance.
Definition: tcpd_intern.h:58
static void tcpd_on_ev_timeout(hpd_ev_loop_t *loop, struct ev_timer *watcher, int revents)
Timeout callback for timeout watcher.
Definition: tcpd.c:171
hpd_tcpd_nodata_f on_disconnect
Definition: hpd_tcpd.h:101
size_t max_data_size
Definition: hpd_tcpd.h:98
hpd_error_t hpd_tcpd_conn_get_ip(hpd_tcpd_conn_t *conn, const char **ip)
Get the IP address of the client.
Definition: tcpd.c:459
#define HPD_LOG_VERBOSE(CONTEXT, FMT,...)
hpd_error_t hpd_tcpd_conn_sendf(hpd_tcpd_conn_t *conn, const char *fmt,...)
Send message on connection.
Definition: tcpd.c:503
int send_close
Close socket after send ?
Definition: tcpd_intern.h:66
int sockfd
Socket file descriptor.
Definition: tcpd_intern.h:50
enum hpd_error hpd_error_t
Definition: hpd_types.h:167
#define HPD_LOG_RETURN_E_ALLOC(CONTEXT)
char ip[INET6_ADDRSTRLEN]
IP address of client.
Definition: tcpd_intern.h:59
hpd_error_t hpd_tcpd_conn_kill(hpd_tcpd_conn_t *conn)
Kill and clean up after a connection.
Definition: tcpd.c:608
hpd_tcpd_settings_t settings
Settings.
Definition: tcpd_intern.h:46
const hpd_module_t * context
Definition: tcpd_intern.h:52
hpd_ev_loop_t * loop
Event loop.
Definition: tcpd_intern.h:48
static void * tcpd_get_in_addr(struct sockaddr *sa)
Get the in_addr from a sockaddr (IPv4 or IPv6)
Definition: tcpd.c:52
hpd_tcpd_conns_t conns
Linked List of connections.
Definition: tcpd_intern.h:49
All data to represent a connection.
Definition: tcpd_intern.h:56
static struct ev_loop * loop
hpd_error_t hpd_tcpd_conn_close(hpd_tcpd_conn_t *conn)
Close a connection, after the remaining data has been sent.
Definition: tcpd.c:581
hpd_error_t hpd_tcpd_conn_vsendf(hpd_tcpd_conn_t *conn, const char *fmt, va_list vp)
Send message on connection.
Definition: tcpd.c:534
hpd_tcpd_data_f on_receive
Definition: hpd_tcpd.h:100
size_t send_len
Length of data to send.
Definition: tcpd_intern.h:65
#define HPD_LOG_WARN(CONTEXT, FMT,...)
hpd_error_t hpd_tcpd_create(hpd_tcpd_t **tcpd, hpd_tcpd_settings_t *settings, const hpd_module_t *context, hpd_ev_loop_t *loop)
Create new tcpd instance.
Definition: tcpd.c:269
void * ctx
Connection context.
Definition: tcpd_intern.h:67
struct ev_loop hpd_ev_loop_t
Definition: hpd_types.h:51
char port_str[6]
Port number - as a string.
Definition: tcpd_intern.h:47
ev_io watcher
New connection watcher.
Definition: tcpd_intern.h:51
Settings struct for tcpd.
Definition: hpd_tcpd.h:94
#define HPD_LOG_ERROR(CONTEXT, FMT,...)
ev_io recv_watcher
Recieve watcher.
Definition: tcpd_intern.h:62
static void tcpd_on_ev_conn(hpd_ev_loop_t *loop, struct ev_io *watcher, int revents)
Initialise and accept connection.
Definition: tcpd.c:193
#define HPD_LOG_DEBUG(CONTEXT, FMT,...)
static void tcpd_on_ev_recv(hpd_ev_loop_t *loop, struct ev_io *watcher, int revents)
Recieve callback for io-watcher.
Definition: tcpd.c:74