kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

proxy.c (7534B)



      1 /*
      2  * Copyright (c) 2015 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/param.h>
     18 #include <sys/socket.h>
     19 
     20 #include <kore/kore.h>
     21 
     22 /*
     23  * In this example Kore acts as a TLS proxy shuffling data between
     24  * an encrypted connection and a plain text backend.
     25  *
     26  * It will look at the TLS SNI extension to figure out what backend
     27  * to use for the connection when it comes in.
     28  *
     29  * Add your backends to the data structure below.
     30  */
     31 
     32 /* Default timeouts, 5 seconds for connecting, 15 seconds otherwise. */
     33 #define PROXY_TIMEOUT		(15 * 1000)
     34 #define PROXY_CONNECT_TIMEOUT	(5 * 1000)
     35 
     36 /* All domains and their backends. */
     37 struct {
     38 	const char		*name;
     39 	const char		*ip;
     40 	const u_int16_t		port;
     41 } backends[] = {
     42 	{ "localhost",	"127.0.0.1",	8080 },
     43 	{ NULL,		NULL,		0 }
     44 };
     45 
     46 int	client_handle(struct connection *);
     47 void	client_setup(struct connection *);
     48 
     49 void	disconnect(struct connection *);
     50 int	pipe_data(struct netbuf *);
     51 
     52 int	backend_handle_connect(struct connection *);
     53 int	backend_handle_default(struct connection *);
     54 
     55 /*
     56  * Called for every new connection on a certain ip/port. Which one is
     57  * configured in the TLS proxy its configuration file.
     58  */
     59 void
     60 client_setup(struct connection *c)
     61 {
     62 	int			i, fd;
     63 	const char		*name;
     64 	struct connection	*backend;
     65 
     66 	/* Paranoia. */
     67 	name = SSL_get_servername(c->ssl, TLSEXT_NAMETYPE_host_name);
     68 	if (name == NULL) {
     69 		kore_connection_disconnect(c);
     70 		return;
     71 	}
     72 
     73 	/* Figure out what backend to use. */
     74 	for (i = 0; backends[i].name != NULL; i++) {
     75 		if (!strcasecmp(backends[i].name, name))
     76 			break;
     77 	}
     78 
     79 	/* If we don't have any backends, we just disconnect the client. */
     80 	if (backends[i].name == NULL) {
     81 		kore_connection_disconnect(c);
     82 		return;
     83 	}
     84 
     85 	/* Create new socket for the backend connection. */
     86 	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
     87 		kore_log(LOG_ERR, "socket(): %s", errno_s);
     88 		kore_connection_disconnect(c);
     89 		return;
     90 	}
     91 
     92 	/* Set it to non blocking as well. */
     93 	if (!kore_connection_nonblock(fd, 1)) {
     94 		close(fd);
     95 		kore_connection_disconnect(c);
     96 		return;
     97 	}
     98 
     99 	/* Grab a new connection from Kore to hook backend into. */
    100 	backend = kore_connection_new(NULL);
    101 
    102 	/* Prepare our connection. */
    103 	backend->family = AF_INET;
    104 	backend->addr.ipv4.sin_family = AF_INET;
    105 	backend->addr.ipv4.sin_port = htons(backends[i].port);
    106 	backend->addr.ipv4.sin_addr.s_addr = inet_addr(backends[i].ip);
    107 
    108 	/* Set the file descriptor for the backend. */
    109 	backend->fd = fd;
    110 
    111 	/* Default write/read callbacks for backend. */
    112 	backend->read = net_read;
    113 	backend->write = net_write;
    114 
    115 	/* Connection type (unknown to Kore). */
    116 	backend->proto = CONN_PROTO_UNKNOWN;
    117 	backend->state = CONN_STATE_ESTABLISHED;
    118 
    119 	/* The backend idle timer is set first to connection timeout. */
    120 	backend->idle_timer.length = PROXY_CONNECT_TIMEOUT;
    121 
    122 	/* The client idle timer is set to default idle time. */
    123 	c->idle_timer.length = PROXY_TIMEOUT;
    124 
    125 	/* Now link both the client and the backend connection together. */
    126 	c->hdlr_extra = backend;
    127 	backend->hdlr_extra = c;
    128 
    129 	/*
    130 	 * The handle function pointer for the backend is set to the
    131 	 * backend_handle_connect() while connecting.
    132 	 */
    133 	c->handle = client_handle;
    134 	backend->handle = backend_handle_connect;
    135 
    136 	/* Set the disconnect method for both connections. */
    137 	c->disconnect = disconnect;
    138 	backend->disconnect = disconnect;
    139 
    140 	/* Queue write events for the backend connection for now. */
    141 	kore_platform_schedule_write(backend->fd, backend);
    142 
    143 	/* Start idle timer for the backend. */
    144 	kore_connection_start_idletimer(backend);
    145 
    146 	/* Set our client connection to established. */
    147 	c->state = CONN_STATE_ESTABLISHED;
    148 
    149 	/* Insert the backend into the list of Kore connections. */
    150 	TAILQ_INSERT_TAIL(&connections, backend, list);
    151 
    152 	/* Kick off connecting. */
    153 	backend->evt.flags |= KORE_EVENT_WRITE;
    154 	backend->handle(backend);
    155 }
    156 
    157 /*
    158  * This function is called for backends while they are connecting.
    159  * In here we check for write events and attempt to connect() to the
    160  * backend.
    161  *
    162  * Once a connection is established we set the backend handle function
    163  * pointer to the backend_handle_default() callback and setup the reads
    164  * for both the backend and the client connection we received.
    165  */
    166 int
    167 backend_handle_connect(struct connection *c)
    168 {
    169 	int			ret;
    170 	struct connection	*src;
    171 
    172 	/* We will get a write notification when we can progress. */
    173 	if (!(c->evt.flags & KORE_EVENT_WRITE))
    174 		return (KORE_RESULT_OK);
    175 
    176 	kore_connection_stop_idletimer(c);
    177 
    178 	/* Attempt connecting. */
    179 	ret = connect(c->fd, (struct sockaddr *)&c->addr.ipv4,
    180 	    sizeof(c->addr.ipv4));
    181 
    182 	/* If we failed check why, we are non blocking. */
    183 	if (ret == -1) {
    184 		/* If we got a real error, disconnect. */
    185 		if (errno != EALREADY && errno != EINPROGRESS &&
    186 		    errno != EISCONN) {
    187 			kore_log(LOG_ERR, "connect(): %s", errno_s);
    188 			return (KORE_RESULT_ERROR);
    189 		}
    190 
    191 		/* Clean the write flag, we'll be called later. */
    192 		if (errno != EISCONN) {
    193 			c->evt.flags &= ~KORE_EVENT_WRITE;
    194 			kore_connection_start_idletimer(c);
    195 			return (KORE_RESULT_OK);
    196 		}
    197 	}
    198 
    199 	/* The connection to the backend succeeded. */
    200 	c->handle = backend_handle_default;
    201 
    202 	/* Setup read calls for both backend and its client. */
    203 	net_recv_queue(c, NETBUF_SEND_PAYLOAD_MAX,
    204 	    NETBUF_CALL_CB_ALWAYS, pipe_data);
    205 	net_recv_queue(c->hdlr_extra, NETBUF_SEND_PAYLOAD_MAX,
    206 	    NETBUF_CALL_CB_ALWAYS, pipe_data);
    207 
    208 	/* Allow for all events now. */
    209 	kore_connection_start_idletimer(c);
    210 	kore_platform_event_all(c->fd, c);
    211 
    212 	/* Allow events from source now. */
    213 	src = c->hdlr_extra;
    214 	kore_platform_event_all(src->fd, src);
    215 
    216 	/* Now lets start. */
    217 	return (c->handle(c));
    218 }
    219 
    220 /*
    221  * Called for connection activity on a backend, just forwards
    222  * to the default Kore connection handling for now.
    223  */
    224 int
    225 backend_handle_default(struct connection *c)
    226 {
    227 	return (kore_connection_handle(c));
    228 }
    229 
    230 /*
    231  * Called for connection activity on a client, just forwards
    232  * to the default Kore connection handling for now.
    233  */
    234 int
    235 client_handle(struct connection *c)
    236 {
    237 	return (kore_connection_handle(c));
    238 }
    239 
    240 /*
    241  * Called whenever a client or its backend have disconnected.
    242  * This will disconnect the matching paired connection as well.
    243  */
    244 void
    245 disconnect(struct connection *c)
    246 {
    247 	struct connection	*pair = c->hdlr_extra;
    248 
    249 	c->hdlr_extra = NULL;
    250 
    251 	if (pair != NULL) {
    252 		pair->hdlr_extra = NULL;
    253 		kore_connection_disconnect(pair);
    254 	}
    255 }
    256 
    257 /*
    258  * Called whenever data is available that must be piped through
    259  * to the paired connection. (client<>backend or backend<>client).
    260  */
    261 int
    262 pipe_data(struct netbuf *nb)
    263 {
    264 	struct connection	*src = nb->owner;
    265 	struct connection	*dst = src->hdlr_extra;
    266 
    267 	/* Flush data out towards destination. */
    268 	net_send_queue(dst, nb->buf, nb->s_off);
    269 	net_send_flush(dst);
    270 
    271 	/* Reset read for source. */
    272 	net_recv_reset(src, NETBUF_SEND_PAYLOAD_MAX, pipe_data);
    273 
    274 	return (KORE_RESULT_OK);
    275 }