My Project
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros
connection-handler.c
Go to the documentation of this file.
1 /*
2 START OF LICENSE STUB
3  DeDOS: Declarative Dispersion-Oriented Software
4  Copyright (C) 2017 University of Pennsylvania, Georgetown University
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 END OF LICENSE STUB
19 */
21 #include "logging.h"
22 #include "webserver/sslops.h"
23 #include "webserver/socketops.h"
24 #include "webserver/dbops.h"
25 #include "webserver/regex.h"
27 #include "connection-handler.h"
28 #include <unistd.h>
29 
30 #ifdef __GNUC__
31 #define UNUSED __attribute__ ((unused))
32 #else
33 #define UNUSED
34 #endif
35 
36 void init_connection(struct connection *conn, int fd) {
37  conn->fd = fd;
38  conn->ssl = NULL;
39  conn->status = CON_ACCEPTED;
40 }
41 
42 void init_read_state(struct read_state *state, struct connection *conn) {
43  state->conn = *conn;
44  state->req[0] = '\0';
45  state->req_len = 0;
46 }
47 
48 void init_http_state(struct http_state *state, struct connection *conn) {
49  state->conn = *conn;
50  init_parser_state(&state->parser);
51  bzero(&state->db, sizeof(state->db));
52 }
53 
54 void init_response_state(struct response_state *state, struct connection *conn) {
55  state->conn = *conn;
56  state->url[0] = '\0';
57  state->path[0] = '\0';
58  state->header[0] = '\0';
59  state->header_len = 0;
60  state->body[0] = '\0';
61  state->body_len = 0;
62 }
63 
64 int accept_connection(struct connection *conn, int use_ssl) {
65  if (!use_ssl) {
66  log(LOG_CONNECTION_INFO,"Not implemented, assuming connection is already accepted");
67  return WS_COMPLETE;
68  }
69 
70  if (conn->ssl == NULL) {
71  conn->ssl = init_ssl_connection(conn->fd);
72  }
73 
74  int rtn = accept_ssl(conn->ssl);
75  if (rtn == WS_COMPLETE) {
76  log(LOG_CONNECTION_INFO, "Accepted SSL connection on fd %d", conn->fd);
77  return WS_COMPLETE;
78  } else if (rtn == -1) {
79  //log_error("Error accepting SSL (fd: %d)", conn->fd);
80  return WS_ERROR;
81  } else {
82  log(LOG_CONNECTION_INFO, "SSL accept incomplete (fd: %d)", conn->fd);
83  return rtn;
84  }
85 }
86 
88  int use_ssl = (state->conn.ssl != NULL);
89 
90  int rtn;
91  int bytes = MAX_RECV_LEN;
92  if (use_ssl) {
93  rtn = read_ssl(state->conn.ssl, state->req, &bytes);
94  } else {
95  rtn = read_socket(state->conn.fd, state->req, &bytes);
96  }
97 
98  switch (rtn) {
99  case WS_INCOMPLETE_READ:
100  case WS_INCOMPLETE_WRITE:
101  log(LOG_CONNECTION_INFO, "Read incomplete (fd: %d)", state->conn.fd);
102  return rtn;
103  case WS_COMPLETE:
104  state->req_len = bytes;
105  log(LOG_CONNECTION_INFO, "Completed reading %d bytes (fd: %d)",
106  bytes, state->conn.fd);
107  return WS_COMPLETE;
108  case WS_ERROR:
109  log(LOG_WS_ERRORS, "Error reading (fd: %d)", state->conn.fd);
110  return WS_ERROR;
111  default:
112  log_error("Unknown return code: %d (fd: %d)", rtn, state->conn.fd);
113  return WS_ERROR;
114  }
115 }
116 
117 int parse_request(char *req, int req_len, struct http_state *state) {
118  int status = parse_http(&state->parser, req, req_len);
119 
120  switch (status) {
121  case WS_COMPLETE:
122  log(LOG_CONNECTION_INFO, "Request complete (fd: %d)", state->conn.fd);
123  return WS_COMPLETE;
124  case WS_INCOMPLETE_READ:
125  log(LOG_CONNECTION_INFO, "Partial request received (fd: %d)", state->conn.fd);
126  return WS_INCOMPLETE_READ;
127  case WS_ERROR:
128  log_error("Error parsing request (fd: %d)", state->conn.fd);
129  return WS_ERROR;
130  default:
131  log_error("Unknown status %d returned from parrser (fd: %d)", status, state->conn.fd);
132  return WS_ERROR;
133  }
134 }
135 
136 #define REGEX_KEY "regex="
137 
138 int has_regex(char *url) {
139  char *regex_loc = strstr(url, REGEX_KEY);
140  if (regex_loc == NULL) {
141  log(LOG_CONNECTION_INFO, "Found no %s in %s", REGEX_KEY, url);
142  return 0;
143  } else {
144  log(LOG_CONNECTION_INFO, "Found regex");
145  return 1;
146  }
147 }
148 #define MAX_REGEX_VALUE_LEN 64
149 
150 static int get_regex_value(char *url, char *regex) {
151  char *regex_start = strstr(url, REGEX_KEY);
152  if (regex_start == NULL)
153  return -1;
154  regex_start += strlen(REGEX_KEY);
155  int start_i = (regex_start - url);
156  for (int i=start_i; (i-start_i)<MAX_REGEX_VALUE_LEN; i++) {
157  switch (url[i]) {
158  case '?':
159  case '&':
160  case '\0':
161  case ' ':
162  strncpy(regex, &url[start_i], i - start_i);
163  regex[i-start_i] = '\0';
164  return 0;
165  default:
166  continue;
167  }
168  }
169  log_error("Requested regex value (%s) too long", regex_start);
170  return -1;
171 }
172 
173 int access_database(char *url, struct db_state *state) {
174  if ( strstr(url, "database") == NULL ) {
175  return WS_COMPLETE;
176  }
177  int rtn = query_db(state);
178  switch (rtn) {
179  case WS_COMPLETE:
180  log(LOG_CONNECTION_INFO, "Successfully queried db");
181  return WS_COMPLETE;
182  case WS_INCOMPLETE_READ:
183  case WS_INCOMPLETE_WRITE:
184  log(LOG_CONNECTION_INFO, "Partial DB query");
185  return rtn;
186  case WS_ERROR:
187  log_error("Error querying database");
188  return WS_ERROR;
189  default:
190  log_error("Unknown return %d from database query", rtn);
191  return WS_ERROR;
192  }
193 }
194 
195 #define DEFAULT_HTTP_HEADER\
196  "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n" \
197 
198 #define DEFAULT_HTTP_BODY\
199  "<!DOCTYPE html>\n<html>\n<body>\n<h1>Dedos New Runtime</h1>\n</body>\n</html>"
200 
201 #define ERROR_HTTP_HEADER\
202  "HTTP/1.1 418 IM A TEAPOT\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n"
203 
204 #define ERROR_HTTP_BODY\
205  "<!DOCTYPE html>\n<html>\n<body>\n<h1>418 I am a teapot</h1>\n</body>\n</html>"
206 
207 int craft_error_response(char UNUSED *url, char *response) {
208  int n = sprintf(response, ERROR_HTTP_HEADER ERROR_HTTP_BODY,
209  (int)strlen(ERROR_HTTP_BODY));
210  return n;
211 }
212 
213 int craft_nonregex_response(char UNUSED *url, char *response) {
214  int n = sprintf(response, DEFAULT_HTTP_HEADER DEFAULT_HTTP_BODY,
215  (int)strlen(DEFAULT_HTTP_BODY));
216  log(LOG_CONNECTION_INFO, "Crafted non-regex response");
217  return n;
218 }
219 
220 int craft_regex_response(char *url, char *response) {
221  char regex_value[MAX_REGEX_VALUE_LEN];
222  int rtn = get_regex_value(url, regex_value);
223  if (rtn != 0) {
224  log_error("Non-regex URL passed to craft_regex_response!");
225  return -1;
226  } else {
227  char html[4096];
228  rtn = regex_html(regex_value, html);
229  if (rtn < 0) {
230  log_error("Error crafting regex response");
231  return -1;
232  }
233  sprintf(response, DEFAULT_HTTP_HEADER "%s", (int)strlen(html), html);
234  log(LOG_CONNECTION_INFO, "Crafted regex response");
235  return strlen(response);
236  }
237 }
238 
240  if (state->body[0] == '\0' && state->header[0] == '\0') {
241  log_error("Attempted to write empty response");
242  return WS_ERROR;
243  }
244 
245  int use_ssl = (state->conn.ssl != NULL);
246  int bytes = state->header_len;
247  int rtn;
248  if (use_ssl) {
249  rtn = write_ssl(state->conn.ssl, state->header, &bytes);
250  } else {
251  rtn = write_socket(state->conn.fd, state->header, &bytes);
252  }
253 
254  switch (rtn) {
255  case WS_INCOMPLETE_READ:
256  case WS_INCOMPLETE_WRITE:
257  log(LOG_CONNECTION_INFO, "Header write incomplete (fd: %d)", state->conn.fd);
258  return rtn;
259  case WS_COMPLETE:
260  log(LOG_CONNECTION_INFO, "Completed writing headers (fd: %d)", state->conn.fd);
261  break;
262  case WS_ERROR:
263  log_error("Error writing headers (fd: %d)", state->conn.fd);
264  return WS_ERROR;
265  default:
266  log_error("Unknown return %d from call to write (fd: %d)", rtn, state->conn.fd);
267  return WS_ERROR;
268  }
269 
270  if (state->body_len > 0) {
271  bytes = state->body_len;
272  if (use_ssl) {
273  rtn = write_ssl(state->conn.ssl, state->body, &bytes);
274  } else {
275  rtn = write_socket(state->conn.fd, state->body, &bytes);
276  }
277 
278  switch (rtn) {
279  case WS_INCOMPLETE_READ:
280  case WS_INCOMPLETE_WRITE:
281  log(LOG_CONNECTION_INFO, "Write incomplete (fd: %d)", state->conn.fd);
282  return rtn;
283  case WS_COMPLETE:
284  log(LOG_CONNECTION_INFO, "Completed writing response (fd: %d)", state->conn.fd);
285  return WS_COMPLETE;
286  case WS_ERROR:
287  log_error("Error writing response (fd: %d)", state->conn.fd);
288  return WS_ERROR;
289  default:
290  log_error("Unknown return %d from call to write (fd: %d)", rtn, state->conn.fd);
291  return WS_ERROR;
292  }
293  } else {
294  return rtn;
295  }
296 }
297 
298 int close_connection(struct connection *conn) {
299  log(LOG_CONNECTION_INFO, "Closing connection (fd: %d)", conn->fd);
300  if (conn->ssl != NULL) {
301  close_ssl(conn->ssl);
302  }
303 
304  int rtn = close(conn->fd);
305  if (rtn != 0) {
306  log_perror("Error closing connection (fd: %d)", conn->fd);
307  return WS_ERROR;
308  }
309  log(LOG_CONNECTION_INFO, "Closed connection (fd: %d)", conn->fd);
310  return WS_COMPLETE;
311 }
void init_http_state(struct http_state *state, struct connection *conn)
#define ERROR_HTTP_BODY
int regex_html(char *to_match, char *htmlDoc)
struct connection conn
int has_regex(char *url)
struct parser_state parser
int parse_http(struct parser_state *state, char *buf, ssize_t bytes)
void init_connection(struct connection *conn, int fd)
struct connection conn
int write_socket(int fd, char *buf, int *buf_size)
Definition: socketops.c:99
SSL * init_ssl_connection(int fd)
Definition: sslops.c:176
#define REGEX_KEY
int accept_ssl(SSL *ssl)
Definition: sslops.c:195
int read_ssl(SSL *ssl, char *buf, int *buf_size)
Definition: sslops.c:211
int craft_error_response(char *url, char *response)
int craft_regex_response(char *url, char *response)
#define WS_INCOMPLETE_READ
Definition: webserver.h:25
#define log_perror(fmt,...)
Definition: logging.h:102
#define DEFAULT_HTTP_BODY
#define MAX_RECV_LEN
Logging of status messages to the terminal.
int write_response(struct response_state *state)
#define MAX_REGEX_VALUE_LEN
#define WS_INCOMPLETE_WRITE
Definition: webserver.h:26
int close_connection(struct connection *conn)
static int get_regex_value(char *url, char *regex)
void init_read_state(struct read_state *state, struct connection *conn)
int accept_connection(struct connection *conn, int use_ssl)
struct db_state db
#define log_error(fmt,...)
Definition: logging.h:101
int parse_request(char *req, int req_len, struct http_state *state)
char req[4096]
int read_socket(int fd, char *buf, int *buf_size)
Definition: socketops.c:84
#define DEFAULT_HTTP_HEADER
int access_database(char *url, struct db_state *state)
void init_parser_state(struct parser_state *state)
Definition: dbops.h:32
int query_db(struct db_state *state)
Definition: dbops.c:182
#define UNUSED
int craft_nonregex_response(char *url, char *response)
int write_ssl(SSL *ssl, char *buf, int *buf_size)
Definition: sslops.c:224
struct connection conn
#define WS_COMPLETE
Definition: webserver.h:24
#define WS_ERROR
Definition: webserver.h:27
#define log(level, fmt,...)
Log at a custom level.
Definition: logging.h:147
void init_response_state(struct response_state *state, struct connection *conn)
int read_request(struct read_state *state)
#define ERROR_HTTP_HEADER
state
Definition: http_parser.c:298
enum webserver_status status
int close_ssl(SSL *ssl)
Definition: sslops.c:241